From c1b5540332ac2fd65215366f9ea10940b86d5fe7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 19:31:27 +0000 Subject: [PATCH 01/88] Replace custom template substitution with yt-dlp's evaluate_outtmpl Replace the hand-rolled _outtmpl_substitute_field() / _compile_outtmpl_pattern() with a new _resolve_outtmpl_fields() that delegates to yt-dlp's YoutubeDL.evaluate_outtmpl(). This gives playlist/channel output templates access to yt-dlp's full template syntax: defaults (%(field|fallback)s), conditional formatting (%(field&prefix {})s), math (%(field+N)d), datetime formatting (%(field>%Y-%m-%d)s), and more. Only field references whose root name matches the targeted prefix (e.g. "playlist" or "channel") are resolved; all other references remain as template placeholders for yt-dlp to fill during the actual download. Agent-Logs-Url: https://github.com/alexta69/metube/sessions/0ae5ff34-540f-4fc8-a81c-358fb92b7c15 Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com> --- app/tests/test_ytdl_utils.py | 44 +++++++++++++++++---- app/ytdl.py | 77 +++++++++++++++++------------------- 2 files changed, 73 insertions(+), 48 deletions(-) diff --git a/app/tests/test_ytdl_utils.py b/app/tests/test_ytdl_utils.py index 54c9400..06d6d50 100644 --- a/app/tests/test_ytdl_utils.py +++ b/app/tests/test_ytdl_utils.py @@ -24,7 +24,7 @@ class _ImpersonateTarget: fake_impersonate.ImpersonateTarget = _ImpersonateTarget fake_networking.impersonate = fake_impersonate -fake_utils.STR_FORMAT_RE_TMPL = r"(?P)%\((?P{})\)(?P[-0-9.]*{})" +fake_utils.STR_FORMAT_RE_TMPL = r"(?P)%\((?P(?P{}))\)(?P[-0-9.]*{})" fake_utils.STR_FORMAT_TYPES = "diouxXeEfFgGcrsa" fake_yt_dlp.networking = fake_networking fake_yt_dlp.utils = fake_utils @@ -37,11 +37,15 @@ from ytdl import ( DownloadInfo, _compact_persisted_entry, _convert_srt_to_txt_file, - _outtmpl_substitute_field, + _resolve_outtmpl_fields, _sanitize_entry_for_pickle, _sanitize_path_component, ) +# Detect whether the real yt-dlp is loaded (as opposed to the minimal fake +# shim above). _resolve_outtmpl_fields needs YoutubeDL at runtime. +_has_real_ytdlp = hasattr(sys.modules.get("yt_dlp"), "YoutubeDL") + class SanitizePathComponentTests(unittest.TestCase): def test_replaces_windows_invalid_chars(self): @@ -52,15 +56,39 @@ class SanitizePathComponentTests(unittest.TestCase): self.assertEqual(_sanitize_path_component(42), 42) -class OuttmplSubstituteFieldTests(unittest.TestCase): - def test_simple_substitution(self): - self.assertEqual(_outtmpl_substitute_field("%(title)s", "title", "Hello"), "Hello") +@unittest.skipUnless(_has_real_ytdlp, "requires real yt-dlp") +class ResolveOuttmplFieldsTests(unittest.TestCase): + """Tests for _resolve_outtmpl_fields (delegates to yt-dlp's template engine).""" + + def test_simple_playlist_substitution(self): + info = {"playlist_title": "My PL", "playlist_index": "03"} + result = _resolve_outtmpl_fields("%(playlist_title)s/%(title)s.%(ext)s", info, ("playlist",)) + self.assertEqual(result, "My PL/%(title)s.%(ext)s") def test_format_spec_int(self): - self.assertEqual(_outtmpl_substitute_field("%(idx)02d", "idx", 3), "03") + info = {"playlist_index": "3"} + result = _resolve_outtmpl_fields("%(playlist_index)02d-%(title)s", info, ("playlist",)) + self.assertEqual(result, "03-%(title)s") - def test_missing_field_unchanged(self): - self.assertEqual(_outtmpl_substitute_field("%(other)s", "title", "x"), "%(other)s") + def test_non_targeted_fields_unchanged(self): + info = {"playlist_title": "PL"} + result = _resolve_outtmpl_fields("%(title)s/%(ext)s", info, ("playlist",)) + self.assertEqual(result, "%(title)s/%(ext)s") + + def test_default_value(self): + info = {"playlist_index": "1"} + result = _resolve_outtmpl_fields("%(playlist_title|Unknown)s/%(playlist_index)s", info, ("playlist",)) + self.assertEqual(result, "Unknown/1") + + def test_channel_prefix(self): + info = {"channel": "MyChan", "channel_index": "05"} + result = _resolve_outtmpl_fields("%(channel)s/%(channel_index)02d-%(title)s", info, ("channel",)) + self.assertEqual(result, "MyChan/05-%(title)s") + + def test_math_operation(self): + info = {"playlist_index": "3"} + result = _resolve_outtmpl_fields("%(playlist_index+100)d", info, ("playlist",)) + self.assertEqual(result, "103") class SanitizeEntryForPickleTests(unittest.TestCase): diff --git a/app/ytdl.py b/app/ytdl.py index 6aac00d..15f42f1 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -13,7 +13,6 @@ import logging import re import types from typing import Any, Optional -from functools import lru_cache import yt_dlp.networking.impersonate from yt_dlp.utils import STR_FORMAT_RE_TMPL, STR_FORMAT_TYPES @@ -24,13 +23,6 @@ from state_store import AtomicJsonStore, from_json_compatible, read_legacy_shelf log = logging.getLogger('ytdl') -@lru_cache(maxsize=None) -def _compile_outtmpl_pattern(field: str) -> re.Pattern: - """Compile a regex pattern to match a specific field in an output template, including optional format specifiers.""" - conversion_types = f"[{re.escape(STR_FORMAT_TYPES)}]" - return re.compile(STR_FORMAT_RE_TMPL.format(re.escape(field), conversion_types)) - - # Characters that are invalid in Windows/NTFS path components. These are pre- # sanitised when substituting playlist/channel titles into output templates so # that downloads do not fail on NTFS-mounted volumes or Windows Docker hosts. @@ -41,44 +33,51 @@ def _sanitize_path_component(value: Any) -> Any: """Replace characters that are invalid in Windows path components with '_'. Non-string values (int, float, None, …) are passed through unchanged so - that ``_outtmpl_substitute_field`` can still coerce them with format specs - (e.g. ``%(playlist_index)02d``). Only string values are sanitised because - Windows-invalid characters are only a concern for human-readable strings - (titles, channel names, etc.) that may end up as directory names. + that numeric format specs (e.g. ``%(playlist_index)02d``) still work. + Only string values are sanitised because Windows-invalid characters are + only a concern for human-readable strings (titles, channel names, etc.) + that may end up as directory names. """ if not isinstance(value, str): return value return _WINDOWS_INVALID_PATH_CHARS.sub('_', value) -def _outtmpl_substitute_field(template: str, field: str, value: Any) -> str: - """Substitute a single field in an output template, applying any format specifiers to the value.""" - pattern = _compile_outtmpl_pattern(field) +# Regex matching yt-dlp output-template field references, e.g. ``%(title)s`` +# or ``%(playlist_index)03d``. Built from yt-dlp's own ``STR_FORMAT_RE_TMPL`` +# so that it stays in sync with upstream changes to the template syntax. +_OUTTMPL_FIELD_RE = re.compile( + STR_FORMAT_RE_TMPL.format('[^)]+', f'[{STR_FORMAT_TYPES}ljhqBUDS]') +) - def replacement(match: re.Match) -> str: - if match.group("has_key") is None: - return match.group(0) - prefix = match.group("prefix") or "" - format_spec = match.group("format") +def _resolve_outtmpl_fields(template: str, info_dict: dict, prefixes: tuple[str, ...]) -> str: + """Resolve specific fields in an output template using yt-dlp's template engine. - if not format_spec: - return f"{prefix}{value}" + Only field references whose root name starts with one of *prefixes* are + evaluated. All other references are left untouched so that yt-dlp can + resolve them later during the actual download. - conversion_type = format_spec[-1] - try: - if conversion_type in "diouxX": - coerced_value = int(value) - elif conversion_type in "eEfFgG": - coerced_value = float(value) - else: - coerced_value = value + This delegates to ``YoutubeDL.evaluate_outtmpl`` for each targeted field + reference, giving access to the full yt-dlp template syntax (defaults, + conditional formatting, math operations, datetime formatting, etc.). + """ + matches = list(_OUTTMPL_FIELD_RE.finditer(template)) + if not matches: + return template - return f"{prefix}{('%' + format_spec) % coerced_value}" - except (ValueError, TypeError): - return f"{prefix}{value}" + with yt_dlp.YoutubeDL({'quiet': True}) as ydl: + for match in reversed(matches): + key = match.group('key') + if key is None: + continue + root = re.match(r'\w+', key) + if root is None or not root.group(0).startswith(prefixes): + continue + resolved = ydl.evaluate_outtmpl(match.group(0), info_dict) + template = template[:match.start()] + resolved + template[match.end():] - return pattern.sub(replacement, template) + return template _MAX_ENTRY_SANITIZE_DEPTH = 64 @@ -818,15 +817,13 @@ class DownloadQueue: if entry is not None and entry.get('playlist_index') is not None: if len(self.config.OUTPUT_TEMPLATE_PLAYLIST): output = self.config.OUTPUT_TEMPLATE_PLAYLIST - for property, value in entry.items(): - if property.startswith("playlist"): - output = _outtmpl_substitute_field(output, property, _sanitize_path_component(value)) + sanitized = {k: _sanitize_path_component(v) for k, v in entry.items()} + output = _resolve_outtmpl_fields(output, sanitized, ('playlist',)) if entry is not None and entry.get('channel_index') is not None: if len(self.config.OUTPUT_TEMPLATE_CHANNEL): output = self.config.OUTPUT_TEMPLATE_CHANNEL - for property, value in entry.items(): - if property.startswith("channel"): - output = _outtmpl_substitute_field(output, property, _sanitize_path_component(value)) + sanitized = {k: _sanitize_path_component(v) for k, v in entry.items()} + output = _resolve_outtmpl_fields(output, sanitized, ('channel',)) ytdl_options = dict(self.config.YTDL_OPTIONS) playlist_item_limit = getattr(dl, 'playlist_item_limit', 0) if playlist_item_limit > 0: From b17e1e5668f677e821dc541da44412d736c23f0a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 19:34:09 +0000 Subject: [PATCH 02/88] Add explanatory comment for fake STR_FORMAT_RE_TMPL key group in tests Agent-Logs-Url: https://github.com/alexta69/metube/sessions/0ae5ff34-540f-4fc8-a81c-358fb92b7c15 Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com> --- app/tests/test_ytdl_utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/tests/test_ytdl_utils.py b/app/tests/test_ytdl_utils.py index 06d6d50..a577339 100644 --- a/app/tests/test_ytdl_utils.py +++ b/app/tests/test_ytdl_utils.py @@ -24,6 +24,9 @@ class _ImpersonateTarget: fake_impersonate.ImpersonateTarget = _ImpersonateTarget fake_networking.impersonate = fake_impersonate +# The inner ``key`` group mirrors the real ``STR_FORMAT_RE_TMPL`` so that +# ``_OUTTMPL_FIELD_RE`` (compiled at import time) has the named group that +# ``_resolve_outtmpl_fields`` reads via ``match.group('key')``. fake_utils.STR_FORMAT_RE_TMPL = r"(?P)%\((?P(?P{}))\)(?P[-0-9.]*{})" fake_utils.STR_FORMAT_TYPES = "diouxXeEfFgGcrsa" fake_yt_dlp.networking = fake_networking From 981e6c10035f5bd270b39a70d51dbb6802c0f548 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 19:59:32 +0000 Subject: [PATCH 03/88] Propagate missing playlist context fields (playlist_count, playlist_autonumber, n_entries, __last_playlist_index) The playlist/channel processing loop now sets playlist_count, playlist_autonumber, n_entries, and __last_playlist_index on each video entry so that templates like %(playlist_autonumber)s, %(playlist_count)s, and %(playlist_index&{} - |)s resolve correctly instead of showing NA. Also updates _compact_persisted_entry to preserve n_entries and __last_playlist_index across restarts. Fixes #692 Agent-Logs-Url: https://github.com/alexta69/metube/sessions/b5aeb55a-3197-4a14-b8b4-96c9a67796e8 Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com> --- app/tests/test_ytdl_utils.py | 37 ++++++++++++++++++++++++++++++++++++ app/ytdl.py | 14 +++++++++++--- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/app/tests/test_ytdl_utils.py b/app/tests/test_ytdl_utils.py index a577339..0f2e66d 100644 --- a/app/tests/test_ytdl_utils.py +++ b/app/tests/test_ytdl_utils.py @@ -93,6 +93,35 @@ class ResolveOuttmplFieldsTests(unittest.TestCase): result = _resolve_outtmpl_fields("%(playlist_index+100)d", info, ("playlist",)) self.assertEqual(result, "103") + def test_playlist_count_and_autonumber(self): + info = { + "playlist_title": "My PL", + "playlist_index": "03", + "playlist_count": 10, + "playlist_autonumber": 3, + "n_entries": 10, + "__last_playlist_index": 10, + } + result = _resolve_outtmpl_fields( + "%(playlist_title)s/%(playlist_autonumber)s of %(playlist_count)s - %(title)s.%(ext)s", + info, + ("playlist",), + ) + # playlist_autonumber is auto-padded by yt-dlp using __last_playlist_index + self.assertEqual(result, "My PL/03 of 10 - %(title)s.%(ext)s") + + def test_conditional_playlist_index(self): + info = { + "playlist_index": "5", + "playlist_count": 10, + } + result = _resolve_outtmpl_fields( + "%(playlist_index&{} - |)s%(title)s.%(ext)s", + info, + ("playlist",), + ) + self.assertEqual(result, "5 - %(title)s.%(ext)s") + class SanitizeEntryForPickleTests(unittest.TestCase): def test_nested(self): @@ -250,8 +279,12 @@ class CompactPersistedEntryTests(unittest.TestCase): entry = { "playlist_index": "01", "playlist_title": "Playlist", + "playlist_count": 10, + "playlist_autonumber": 1, "channel_index": "02", "channel_title": "Channel", + "n_entries": 10, + "__last_playlist_index": 10, "formats": [{"id": "huge"}], "description": "big blob", } @@ -263,8 +296,12 @@ class CompactPersistedEntryTests(unittest.TestCase): { "playlist_index": "01", "playlist_title": "Playlist", + "playlist_count": 10, + "playlist_autonumber": 1, "channel_index": "02", "channel_title": "Channel", + "n_entries": 10, + "__last_playlist_index": 10, }, ) diff --git a/app/ytdl.py b/app/ytdl.py index 15f42f1..65b5ed4 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -295,13 +295,16 @@ _PERSISTED_DOWNLOAD_FIELDS = ( ) +_COMPACT_ENTRY_EXTRA_KEYS = frozenset(("n_entries", "__last_playlist_index")) + + def _compact_persisted_entry(entry: Any) -> Optional[dict[str, Any]]: if not isinstance(entry, dict): return None compact = { key: value for key, value in entry.items() - if key.startswith("playlist") or key.startswith("channel") + if key.startswith("playlist") or key.startswith("channel") or key in _COMPACT_ENTRY_EXTRA_KEYS } return compact or None @@ -893,8 +896,9 @@ class DownloadQueue: # Convert generator to list if needed (for len() and slicing operations) if isinstance(entries, types.GeneratorType): entries = list(entries) - log.info(f'{etype} detected with {len(entries)} entries') - index_digits = len(str(len(entries))) + total_entries = len(entries) + log.info(f'{etype} detected with {total_entries} entries') + index_digits = len(str(total_entries)) results = [] if playlist_item_limit > 0: log.info(f'Item limit is set. Processing only first {playlist_item_limit} entries') @@ -906,6 +910,10 @@ class DownloadQueue: etr["_type"] = "video" etr[etype] = entry.get("id") or entry.get("channel_id") or entry.get("channel") etr[f"{etype}_index"] = '{{0:0{0:d}d}}'.format(index_digits).format(index) + etr[f"{etype}_count"] = total_entries + etr[f"{etype}_autonumber"] = index + etr["n_entries"] = total_entries + etr["__last_playlist_index"] = total_entries for property in ("id", "title", "uploader", "uploader_id"): if property in entry: etr[f"{etype}_{property}"] = entry[property] From 1cc27d3f55fdc32327b89c85b845dc13d20b19b8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 07:23:14 +0000 Subject: [PATCH 04/88] Initial plan From 771ba52d5344a4983f17acfbabe8bfd53d58fc84 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 07:25:14 +0000 Subject: [PATCH 05/88] Use PORT env variable in Dockerfile HEALTHCHECK instead of hardcoded 8081 Agent-Logs-Url: https://github.com/alexta69/metube/sessions/899e7074-fd3d-4538-8bad-8ee6804d5052 Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com> --- Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 8d17cd5..f9896bc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -63,9 +63,10 @@ ENV UMASK=022 ENV DOWNLOAD_DIR=/downloads ENV STATE_DIR=/downloads/.metube ENV TEMP_DIR=/downloads +ENV PORT=8081 VOLUME /downloads EXPOSE 8081 -HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 CMD curl -fsS "http://localhost:8081/" || exit 1 +HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 CMD curl -fsS "http://localhost:${PORT}/" || exit 1 # Add build-time argument for version ARG VERSION=dev From d7eaaaa94baeb557b49dde9fb7a4b29b8a4e2d2b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 20:02:25 +0000 Subject: [PATCH 06/88] Add clarifying comments for n_entries and __last_playlist_index fields (closes #692) Agent-Logs-Url: https://github.com/alexta69/metube/sessions/b5aeb55a-3197-4a14-b8b4-96c9a67796e8 Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com> --- app/ytdl.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/ytdl.py b/app/ytdl.py index 65b5ed4..889c0fb 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -912,6 +912,8 @@ class DownloadQueue: etr[f"{etype}_index"] = '{{0:0{0:d}d}}'.format(index_digits).format(index) etr[f"{etype}_count"] = total_entries etr[f"{etype}_autonumber"] = index + # n_entries: standard yt-dlp field for total count (used by template engine) + # __last_playlist_index: yt-dlp internal field for auto-padding autonumber etr["n_entries"] = total_entries etr["__last_playlist_index"] = total_entries for property in ("id", "title", "uploader", "uploader_id"): From 0cba61c9a4a80a1f2e5f8e091e26d2094736e830 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Thu, 2 Apr 2026 10:52:56 +0300 Subject: [PATCH 07/88] update README --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ad47702..e588b5e 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,12 @@ ![Build Status](https://github.com/alexta69/metube/actions/workflows/main.yml/badge.svg) ![Docker Pulls](https://img.shields.io/docker/pulls/alexta69/metube.svg) -Web GUI for youtube-dl (using the [yt-dlp](https://github.com/yt-dlp/yt-dlp) fork) with playlist support. Allows you to download videos from YouTube and [dozens of other sites](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md). +MeTube is a self-hosted web UI for `yt-dlp`, for downloading media from YouTube and [dozens of other sites](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md). + +Key capabilities: +* Download videos, audio, captions, and thumbnails from a browser UI. +* Download playlists and channels, with configurable output and download options. +* Subscribe to channels and playlists, periodically check for new items, and queue new uploads automatically. ![screenshot1](https://github.com/alexta69/metube/raw/master/screenshot.gif) From 565a715037eb41df0c10ef179d690bce6a88da51 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 06:16:12 +0000 Subject: [PATCH 08/88] feat: add per-download yt-dlp presets and overrides Agent-Logs-Url: https://github.com/alexta69/metube/sessions/8a3119fc-63d1-4508-a196-8c50ff248812 Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com> --- README.md | 2 + app/main.py | 107 ++++++++++++++++++ app/subscriptions.py | 16 +++ app/tests/test_api.py | 49 ++++++++ app/tests/test_config.py | 25 ++++ app/tests/test_download_queue.py | 40 +++++++ app/tests/test_main_helpers.py | 44 +++++++ app/ytdl.py | 32 +++++- ui/src/app/app.html | 29 +++++ ui/src/app/app.ts | 57 ++++++++++ ui/src/app/interfaces/download.ts | 2 + ui/src/app/services/downloads.service.spec.ts | 13 +++ ui/src/app/services/downloads.service.ts | 10 ++ ui/src/app/services/subscriptions.service.ts | 2 + 14 files changed, 427 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e588b5e..bfa709a 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,8 @@ Certain values can be set via environment variables, using the `-e` parameter on * __OUTPUT_TEMPLATE_CHANNEL__: The template for the filenames of the downloaded videos when downloaded as a channel. Defaults to `%(channel)s/%(title)s.%(ext)s`. Set to empty to use `OUTPUT_TEMPLATE` instead. * __YTDL_OPTIONS__: Additional options to pass to yt-dlp in JSON format. [See available options here](https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/YoutubeDL.py#L222). They roughly correspond to command-line options, though some do not have exact equivalents here. For example, `--recode-video` has to be specified via `postprocessors`. Also note that dashes are replaced with underscores. You may find [this script](https://github.com/yt-dlp/yt-dlp/blob/master/devscripts/cli_to_api.py) helpful for converting from command-line options to `YTDL_OPTIONS`. * __YTDL_OPTIONS_FILE__: A path to a JSON file that will be loaded and used for populating `YTDL_OPTIONS` above. Please note that if both `YTDL_OPTIONS_FILE` and `YTDL_OPTIONS` are specified, the options in `YTDL_OPTIONS` take precedence. The file will be monitored for changes and reloaded automatically when changes are detected. +* __YTDL_OPTIONS_PRESETS__: A JSON object mapping preset names to yt-dlp option objects. These preset names are exposed in the web UI's Advanced Options panel so users can pick per-download overrides without changing the global `YTDL_OPTIONS`. +* __YTDL_OPTIONS_PRESETS_FILE__: A path to a JSON file containing `YTDL_OPTIONS_PRESETS`. If both are specified, values from `YTDL_OPTIONS_PRESETS_FILE` are merged into `YTDL_OPTIONS_PRESETS`. ### 🌐 Web Server & URLs diff --git a/app/main.py b/app/main.py index 284198d..4ffe2cb 100644 --- a/app/main.py +++ b/app/main.py @@ -57,6 +57,8 @@ class Config: 'CLEAR_COMPLETED_AFTER': '0', 'YTDL_OPTIONS': '{}', 'YTDL_OPTIONS_FILE': '', + 'YTDL_OPTIONS_PRESETS': '{}', + 'YTDL_OPTIONS_PRESETS_FILE': '', 'ROBOTS_TXT': '', 'HOST': '0.0.0.0', 'PORT': '8081', @@ -91,12 +93,17 @@ class Config: # Convert relative addresses to absolute addresses to prevent the failure of file address comparison if self.YTDL_OPTIONS_FILE and self.YTDL_OPTIONS_FILE.startswith('.'): self.YTDL_OPTIONS_FILE = str(Path(self.YTDL_OPTIONS_FILE).resolve()) + if self.YTDL_OPTIONS_PRESETS_FILE and self.YTDL_OPTIONS_PRESETS_FILE.startswith('.'): + self.YTDL_OPTIONS_PRESETS_FILE = str(Path(self.YTDL_OPTIONS_PRESETS_FILE).resolve()) self._runtime_overrides = {} success,_ = self.load_ytdl_options() if not success: sys.exit(1) + success,_ = self.load_ytdl_option_presets() + if not success: + sys.exit(1) def set_runtime_override(self, key, value): self._runtime_overrides[key] = value @@ -160,6 +167,37 @@ class Config: self._apply_runtime_overrides() return (True, '') + def load_ytdl_option_presets(self) -> tuple[bool, str]: + try: + self.YTDL_OPTIONS_PRESETS = json.loads(os.environ.get('YTDL_OPTIONS_PRESETS', '{}')) + assert isinstance(self.YTDL_OPTIONS_PRESETS, dict) + assert all(isinstance(name, str) and isinstance(options, dict) for name, options in self.YTDL_OPTIONS_PRESETS.items()) + except (json.decoder.JSONDecodeError, AssertionError): + msg = 'Environment variable YTDL_OPTIONS_PRESETS is invalid' + log.error(msg) + return (False, msg) + + if not self.YTDL_OPTIONS_PRESETS_FILE: + return (True, '') + + log.info(f'Loading yt-dlp option presets from "{self.YTDL_OPTIONS_PRESETS_FILE}"') + if not os.path.exists(self.YTDL_OPTIONS_PRESETS_FILE): + msg = f'File "{self.YTDL_OPTIONS_PRESETS_FILE}" not found' + log.error(msg) + return (False, msg) + try: + with open(self.YTDL_OPTIONS_PRESETS_FILE) as json_data: + opts = json.load(json_data) + assert isinstance(opts, dict) + assert all(isinstance(name, str) and isinstance(options, dict) for name, options in opts.items()) + except (json.decoder.JSONDecodeError, AssertionError): + msg = 'YTDL_OPTIONS_PRESETS_FILE contents is invalid' + log.error(msg) + return (False, msg) + + self.YTDL_OPTIONS_PRESETS.update(opts) + return (True, '') + config = Config() # Align root logger level with Config (keeps a single source of truth). # This re-applies the log level after Config loads, in case LOGLEVEL was @@ -194,6 +232,53 @@ VALID_VIDEO_CODECS = {'auto', 'h264', 'h265', 'av1', 'vp9'} VALID_VIDEO_FORMATS = {'any', 'mp4', 'ios'} VALID_AUDIO_FORMATS = {'m4a', 'mp3', 'opus', 'wav', 'flac'} VALID_THUMBNAIL_FORMATS = {'jpg'} +BLOCKED_YTDL_OVERRIDE_KEYS = frozenset({ + 'exec', + 'exec_before_dl', + 'exec_cmd', + 'external_downloader', + 'external_downloader_args', + 'format', + 'ignore_no_formats_error', + 'no_color', + 'outtmpl', + 'paths', + 'postprocessor_hooks', + 'progress_hooks', + 'quiet', + 'socket_timeout', + 'verbose', +}) + + +def _iter_nested_keys(value): + if isinstance(value, dict): + for key, nested in value.items(): + yield str(key) + yield from _iter_nested_keys(nested) + elif isinstance(value, list): + for item in value: + yield from _iter_nested_keys(item) + + +def _parse_ytdl_options_overrides(value) -> dict: + if value is None or value == '': + return {} + + if isinstance(value, str): + try: + value = json.loads(value) + except json.JSONDecodeError as exc: + raise web.HTTPBadRequest(reason='ytdl_options_overrides must be valid JSON') from exc + + if not isinstance(value, dict): + raise web.HTTPBadRequest(reason='ytdl_options_overrides must be a JSON object') + + blocked_keys = sorted({key for key in _iter_nested_keys(value) if key in BLOCKED_YTDL_OVERRIDE_KEYS}) + if blocked_keys: + raise web.HTTPBadRequest(reason=f'ytdl_options_overrides contains disallowed keys: {blocked_keys}') + + return value def _migrate_legacy_request(post: dict) -> dict: @@ -384,6 +469,8 @@ def parse_download_options(post: dict) -> dict: chapter_template = post.get('chapter_template') subtitle_language = post.get('subtitle_language') subtitle_mode = post.get('subtitle_mode') + ytdl_options_preset = post.get('ytdl_options_preset') + ytdl_options_overrides = post.get('ytdl_options_overrides') if custom_name_prefix is None: custom_name_prefix = '' @@ -401,12 +488,16 @@ def parse_download_options(post: dict) -> dict: subtitle_language = 'en' if subtitle_mode is None: subtitle_mode = 'prefer_manual' + if ytdl_options_preset is None: + ytdl_options_preset = '' download_type = str(download_type).strip().lower() codec = str(codec or 'auto').strip().lower() format = str(format or '').strip().lower() quality = str(quality).strip().lower() subtitle_language = str(subtitle_language).strip() subtitle_mode = str(subtitle_mode).strip() + ytdl_options_preset = str(ytdl_options_preset).strip() + ytdl_options_overrides = _parse_ytdl_options_overrides(ytdl_options_overrides) if chapter_template and ('..' in chapter_template or chapter_template.startswith('/') or chapter_template.startswith('\\')): raise web.HTTPBadRequest(reason='chapter_template must not contain ".." or start with a path separator') @@ -414,6 +505,8 @@ def parse_download_options(post: dict) -> dict: raise web.HTTPBadRequest(reason='subtitle_language must match pattern [A-Za-z0-9-] and be at most 35 characters') if subtitle_mode not in VALID_SUBTITLE_MODES: raise web.HTTPBadRequest(reason=f'subtitle_mode must be one of {sorted(VALID_SUBTITLE_MODES)}') + if ytdl_options_preset and ytdl_options_preset not in config.YTDL_OPTIONS_PRESETS: + raise web.HTTPBadRequest(reason='ytdl_options_preset must match a configured preset') if download_type not in VALID_DOWNLOAD_TYPES: raise web.HTTPBadRequest(reason=f'download_type must be one of {sorted(VALID_DOWNLOAD_TYPES)}') @@ -466,6 +559,8 @@ def parse_download_options(post: dict) -> dict: 'chapter_template': chapter_template, 'subtitle_language': subtitle_language, 'subtitle_mode': subtitle_mode, + 'ytdl_options_preset': ytdl_options_preset, + 'ytdl_options_overrides': ytdl_options_overrides, } @@ -500,9 +595,19 @@ async def add(request): o['chapter_template'], o['subtitle_language'], o['subtitle_mode'], + o['ytdl_options_preset'], + o['ytdl_options_overrides'], ) return web.Response(text=serializer.encode(status)) + +@routes.get(config.URL_PREFIX + 'presets') +async def presets(request): + return web.Response( + text=serializer.encode({'presets': sorted(config.YTDL_OPTIONS_PRESETS.keys())}), + content_type='application/json', + ) + @routes.post(config.URL_PREFIX + 'cancel-add') async def cancel_add(request): dqueue.cancel_add() @@ -541,6 +646,8 @@ async def subscribe(request): chapter_template=o['chapter_template'], subtitle_language=o['subtitle_language'], subtitle_mode=o['subtitle_mode'], + ytdl_options_preset=o['ytdl_options_preset'], + ytdl_options_overrides=o['ytdl_options_overrides'], ) return web.Response(text=serializer.encode(result)) diff --git a/app/subscriptions.py b/app/subscriptions.py index 901ff59..94a1d3b 100644 --- a/app/subscriptions.py +++ b/app/subscriptions.py @@ -145,6 +145,8 @@ class SubscriptionInfo: chapter_template: str = "" subtitle_language: str = "en" subtitle_mode: str = "prefer_manual" + ytdl_options_preset: str = "" + ytdl_options_overrides: dict[str, Any] = field(default_factory=dict) last_checked: Optional[float] = None seen_ids: list[str] = field(default_factory=list) error: Optional[str] = None @@ -190,6 +192,8 @@ def _subscription_to_record(sub: SubscriptionInfo) -> dict[str, Any]: "chapter_template": sub.chapter_template, "subtitle_language": sub.subtitle_language, "subtitle_mode": sub.subtitle_mode, + "ytdl_options_preset": sub.ytdl_options_preset, + "ytdl_options_overrides": sub.ytdl_options_overrides, "last_checked": sub.last_checked, "seen_ids": list(sub.seen_ids), "error": sub.error, @@ -311,6 +315,8 @@ class SubscriptionManager: chapter_template: str, subtitle_language: str, subtitle_mode: str, + ytdl_options_preset: str = "", + ytdl_options_overrides: Optional[dict[str, Any]] = None, ) -> tuple[list[str], list[str]]: queued_ids: list[str] = [] queue_errors: list[str] = [] @@ -336,6 +342,8 @@ class SubscriptionManager: chapter_template or None, subtitle_language, subtitle_mode, + ytdl_options_preset, + ytdl_options_overrides, ) if isinstance(result, dict) and result.get("status") == "error": msg = str(result.get("msg") or f"Queueing failed for {vurl}") @@ -403,6 +411,8 @@ class SubscriptionManager: chapter_template: str, subtitle_language: str, subtitle_mode: str, + ytdl_options_preset: str = "", + ytdl_options_overrides: Optional[dict[str, Any]] = None, ) -> dict: url = self._normalize_url(url) if not url: @@ -460,6 +470,8 @@ class SubscriptionManager: chapter_template=chapter_template or "", subtitle_language=subtitle_language, subtitle_mode=subtitle_mode, + ytdl_options_preset=ytdl_options_preset, + ytdl_options_overrides=dict(ytdl_options_overrides or {}), last_checked=time.time(), seen_ids=list(dict.fromkeys(all_ids)), error=None, @@ -608,6 +620,8 @@ class SubscriptionManager: dl_chapter = cur.chapter_template dl_sublang = cur.subtitle_language dl_submode = cur.subtitle_mode + dl_ytdl_preset = cur.ytdl_options_preset + dl_ytdl_overrides = dict(cur.ytdl_options_overrides) new_entries: list[dict] = [] new_ids: list[str] = [] @@ -632,6 +646,8 @@ class SubscriptionManager: chapter_template=dl_chapter or "", subtitle_language=dl_sublang, subtitle_mode=dl_submode, + ytdl_options_preset=dl_ytdl_preset, + ytdl_options_overrides=dl_ytdl_overrides, ) log.info( "Subscription check finished for %s: %d new, %d queued, %d failed", diff --git a/app/tests/test_api.py b/app/tests/test_api.py index 4aa18e8..5af4e25 100644 --- a/app/tests/test_api.py +++ b/app/tests/test_api.py @@ -37,6 +37,8 @@ def _valid_video_add_body(**kwargs): "codec": "auto", "format": "any", "quality": "best", + "ytdl_options_preset": "", + "ytdl_options_overrides": "", } base.update(kwargs) return base @@ -59,6 +61,23 @@ async def test_add_ok(mock_dqueue): mock_dqueue.add.assert_awaited_once() +@pytest.mark.asyncio +async def test_add_passes_preset_and_overrides(mock_dqueue, monkeypatch): + monkeypatch.setattr(main.config, "YTDL_OPTIONS_PRESETS", {"Preset A": {"writesubtitles": True}}) + req = _json_request( + _valid_video_add_body( + ytdl_options_preset="Preset A", + ytdl_options_overrides='{"writesubtitles": true}', + ) + ) + resp = await main.add(req) + assert resp.status == 200 + call = mock_dqueue.add.await_args + assert call is not None + assert call.args[13] == "Preset A" + assert call.args[14] == {"writesubtitles": True} + + @pytest.mark.asyncio async def test_add_missing_url_returns_400(mock_dqueue): req = _json_request({"download_type": "video", "quality": "best", "format": "any"}) @@ -124,6 +143,27 @@ async def test_add_invalid_json_body(mock_dqueue): await main.add(req) +@pytest.mark.asyncio +async def test_add_invalid_ytdl_options_override_json(mock_dqueue): + req = _json_request(_valid_video_add_body(ytdl_options_overrides="{bad json}")) + with pytest.raises(web.HTTPBadRequest): + await main.add(req) + + +@pytest.mark.asyncio +async def test_add_blocked_ytdl_options_override_key(mock_dqueue): + req = _json_request(_valid_video_add_body(ytdl_options_overrides='{"exec": "rm -rf /"}')) + with pytest.raises(web.HTTPBadRequest): + await main.add(req) + + +@pytest.mark.asyncio +async def test_add_unknown_ytdl_preset(mock_dqueue): + req = _json_request(_valid_video_add_body(ytdl_options_preset="Missing")) + with pytest.raises(web.HTTPBadRequest): + await main.add(req) + + @pytest.mark.asyncio async def test_delete_missing_ids(mock_dqueue): req = _json_request({"where": "queue"}) @@ -168,6 +208,15 @@ async def test_version_json(mock_dqueue): assert "yt-dlp" in body and "version" in body +@pytest.mark.asyncio +async def test_presets_endpoint_returns_names(mock_dqueue, monkeypatch): + monkeypatch.setattr(main.config, "YTDL_OPTIONS_PRESETS", {"Preset B": {}, "Preset A": {}}) + req = MagicMock(spec=web.Request) + resp = await main.presets(req) + assert resp.status == 200 + assert json.loads(resp.text) == {"presets": ["Preset A", "Preset B"]} + + @pytest.mark.asyncio async def test_cookie_status(mock_dqueue): req = MagicMock(spec=web.Request) diff --git a/app/tests/test_config.py b/app/tests/test_config.py index 0461ba1..558f998 100644 --- a/app/tests/test_config.py +++ b/app/tests/test_config.py @@ -33,6 +33,16 @@ class ConfigTests(unittest.TestCase): c = Config() self.assertEqual(c.YTDL_OPTIONS["quiet"], True) + def test_ytdl_option_presets_json_loaded(self): + presets = {"Audio extras": {"embed_thumbnail": True}} + with patch.dict( + os.environ, + _base_env(YTDL_OPTIONS_PRESETS=json.dumps(presets)), + clear=False, + ): + c = Config() + self.assertEqual(c.YTDL_OPTIONS_PRESETS["Audio extras"]["embed_thumbnail"], True) + def test_invalid_ytdl_options_exits(self): with patch.dict(os.environ, _base_env(YTDL_OPTIONS="not-json"), clear=False): with self.assertRaises(SystemExit): @@ -73,6 +83,21 @@ class ConfigTests(unittest.TestCase): finally: os.unlink(path) + def test_ytdl_option_presets_file_merges(self): + with tempfile.NamedTemporaryFile("w", suffix=".json", delete=False) as f: + json.dump({"With subtitles": {"writesubtitles": True}}, f) + path = f.name + try: + with patch.dict( + os.environ, + _base_env(YTDL_OPTIONS_PRESETS="{}", YTDL_OPTIONS_PRESETS_FILE=path), + clear=False, + ): + c = Config() + self.assertIn("With subtitles", c.YTDL_OPTIONS_PRESETS) + finally: + os.unlink(path) + if __name__ == "__main__": unittest.main() diff --git a/app/tests/test_download_queue.py b/app/tests/test_download_queue.py index dfa21c7..5397c15 100644 --- a/app/tests/test_download_queue.py +++ b/app/tests/test_download_queue.py @@ -25,6 +25,7 @@ def dq_env(): cfg.TEMP_DIR = dl cfg.MAX_CONCURRENT_DOWNLOADS = "3" cfg.YTDL_OPTIONS = {} + cfg.YTDL_OPTIONS_PRESETS = {} cfg.CUSTOM_DIRS = True cfg.CREATE_CUSTOM_DIRS = True cfg.CLEAR_COMPLETED_AFTER = "0" @@ -175,3 +176,42 @@ async def test_add_entry_queues_single_video_without_reextracting(dq_env): assert result["status"] == "ok" assert dq.pending.exists("https://example.com/watch?v=1") + + +@pytest.mark.asyncio +async def test_add_merges_global_preset_and_override_options(dq_env): + notifier = AsyncMock() + dq_env.YTDL_OPTIONS = {"writesubtitles": False, "cookiefile": "/tmp/global.txt"} + dq_env.YTDL_OPTIONS_PRESETS = {"Preset A": {"writesubtitles": True, "proxy": "http://preset"}} + + def fake_extract(self, url): + return { + "_type": "video", + "id": "vid2", + "title": "Preset Video", + "url": url, + "webpage_url": url, + } + + dq = DownloadQueue(dq_env, notifier) + with patch.object(DownloadQueue, "_DownloadQueue__extract_info", fake_extract): + result = await dq.add( + "https://example.com/preset", + "video", + "auto", + "any", + "best", + "", + "", + 0, + auto_start=False, + ytdl_options_preset="Preset A", + ytdl_options_overrides={"proxy": "http://override", "embed_thumbnail": True}, + ) + + assert result["status"] == "ok" + queued = dq.pending.get("https://example.com/preset") + assert queued.ytdl_opts["cookiefile"] == "/tmp/global.txt" + assert queued.ytdl_opts["writesubtitles"] is True + assert queued.ytdl_opts["proxy"] == "http://override" + assert queued.ytdl_opts["embed_thumbnail"] is True diff --git a/app/tests/test_main_helpers.py b/app/tests/test_main_helpers.py index 4258b79..e6cfcfd 100644 --- a/app/tests/test_main_helpers.py +++ b/app/tests/test_main_helpers.py @@ -101,5 +101,49 @@ class FrontendSafeTests(unittest.TestCase): self.assertNotIn("DOWNLOAD_DIR", safe) +class ParseYtdlOverridesTests(unittest.TestCase): + def test_empty_override_string_returns_empty_dict(self): + self.assertEqual(main._parse_ytdl_options_overrides(""), {}) + + def test_rejects_non_object_json(self): + with self.assertRaises(main.web.HTTPBadRequest): + main._parse_ytdl_options_overrides('["bad"]') + + def test_rejects_blocked_keys(self): + with self.assertRaises(main.web.HTTPBadRequest): + main._parse_ytdl_options_overrides('{"exec": "rm -rf /"}') + + +class ParseDownloadOptionsTests(unittest.TestCase): + def test_accepts_known_preset_and_overrides(self): + previous = dict(main.config.YTDL_OPTIONS_PRESETS) + main.config.YTDL_OPTIONS_PRESETS = {"With subtitles": {"writesubtitles": True}} + try: + parsed = main.parse_download_options({ + "url": "https://example.com/v", + "download_type": "video", + "codec": "auto", + "format": "any", + "quality": "best", + "ytdl_options_preset": "With subtitles", + "ytdl_options_overrides": '{"writesubtitles": true}', + }) + finally: + main.config.YTDL_OPTIONS_PRESETS = previous + self.assertEqual(parsed["ytdl_options_preset"], "With subtitles") + self.assertEqual(parsed["ytdl_options_overrides"], {"writesubtitles": True}) + + def test_rejects_unknown_preset(self): + with self.assertRaises(main.web.HTTPBadRequest): + main.parse_download_options({ + "url": "https://example.com/v", + "download_type": "video", + "codec": "auto", + "format": "any", + "quality": "best", + "ytdl_options_preset": "Missing preset", + }) + + if __name__ == "__main__": unittest.main() diff --git a/app/ytdl.py b/app/ytdl.py index 889c0fb..61d514e 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -188,6 +188,8 @@ class DownloadInfo: chapter_template, subtitle_language="en", subtitle_mode="prefer_manual", + ytdl_options_preset="", + ytdl_options_overrides=None, ): self.id = id if len(custom_name_prefix) == 0 else f'{custom_name_prefix}.{id}' self.title = title if len(custom_name_prefix) == 0 else f'{custom_name_prefix}.{title}' @@ -210,6 +212,8 @@ class DownloadInfo: self.chapter_template = chapter_template self.subtitle_language = subtitle_language self.subtitle_mode = subtitle_mode + self.ytdl_options_preset = ytdl_options_preset + self.ytdl_options_overrides = dict(ytdl_options_overrides or {}) self.subtitle_files = [] def __setstate__(self, state): @@ -262,6 +266,10 @@ class DownloadInfo: self.subtitle_language = "en" if not hasattr(self, "subtitle_mode"): self.subtitle_mode = "prefer_manual" + if not hasattr(self, "ytdl_options_preset"): + self.ytdl_options_preset = "" + if not hasattr(self, "ytdl_options_overrides"): + self.ytdl_options_overrides = {} if not hasattr(self, "entry"): self.entry = None if not hasattr(self, "subtitle_files"): @@ -285,6 +293,8 @@ _PERSISTED_DOWNLOAD_FIELDS = ( "chapter_template", "subtitle_language", "subtitle_mode", + "ytdl_options_preset", + "ytdl_options_overrides", "status", "timestamp", "error", @@ -828,6 +838,10 @@ class DownloadQueue: sanitized = {k: _sanitize_path_component(v) for k, v in entry.items()} output = _resolve_outtmpl_fields(output, sanitized, ('channel',)) ytdl_options = dict(self.config.YTDL_OPTIONS) + preset_name = getattr(dl, 'ytdl_options_preset', '') + if preset_name: + ytdl_options.update(self.config.YTDL_OPTIONS_PRESETS.get(preset_name, {})) + ytdl_options.update(getattr(dl, 'ytdl_options_overrides', {}) or {}) playlist_item_limit = getattr(dl, 'playlist_item_limit', 0) if playlist_item_limit > 0: log.info(f'playlist limit is set. Processing only first {playlist_item_limit} entries') @@ -855,6 +869,8 @@ class DownloadQueue: chapter_template, subtitle_language, subtitle_mode, + ytdl_options_preset, + ytdl_options_overrides, already, _add_gen=None, ): @@ -887,6 +903,8 @@ class DownloadQueue: chapter_template, subtitle_language, subtitle_mode, + ytdl_options_preset, + ytdl_options_overrides, already, _add_gen, ) @@ -934,6 +952,8 @@ class DownloadQueue: chapter_template, subtitle_language, subtitle_mode, + ytdl_options_preset, + ytdl_options_overrides, already, _add_gen, ) @@ -965,6 +985,8 @@ class DownloadQueue: chapter_template=chapter_template, subtitle_language=subtitle_language, subtitle_mode=subtitle_mode, + ytdl_options_preset=ytdl_options_preset, + ytdl_options_overrides=ytdl_options_overrides, ) await self.__add_download(dl, auto_start) return {'status': 'ok'} @@ -985,13 +1007,15 @@ class DownloadQueue: chapter_template=None, subtitle_language="en", subtitle_mode="prefer_manual", + ytdl_options_preset="", + ytdl_options_overrides=None, already=None, _add_gen=None, ): log.info( f'adding {url}: {download_type=} {codec=} {format=} {quality=} {already=} {folder=} {custom_name_prefix=} ' f'{playlist_item_limit=} {auto_start=} {split_by_chapters=} {chapter_template=} ' - f'{subtitle_language=} {subtitle_mode=}' + f'{subtitle_language=} {subtitle_mode=} {ytdl_options_preset=}' ) if already is None: _add_gen = self._add_generation @@ -1020,6 +1044,8 @@ class DownloadQueue: chapter_template, subtitle_language, subtitle_mode, + ytdl_options_preset, + ytdl_options_overrides, already, _add_gen, ) @@ -1039,6 +1065,8 @@ class DownloadQueue: chapter_template=None, subtitle_language="en", subtitle_mode="prefer_manual", + ytdl_options_preset="", + ytdl_options_overrides=None, ): normalized_entry = copy.deepcopy(entry) if isinstance(entry, dict) else entry already = set() @@ -1056,6 +1084,8 @@ class DownloadQueue: chapter_template, subtitle_language, subtitle_mode, + ytdl_options_preset, + ytdl_options_overrides, already, None, ) diff --git a/ui/src/app/app.html b/ui/src/app/app.html index 303bdd1..73d4f77 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -447,6 +447,35 @@ ngbTooltip="How often to poll subscriptions for new videos"> +
+
+ Option Preset + +
+
+
+
+ Custom yt-dlp Options + +
+
diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index e0c9a67..4786b7e 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -83,6 +83,9 @@ export class App implements AfterViewInit, OnInit, OnDestroy { chapterTemplate: string; subtitleLanguage: string; subtitleMode: string; + ytdlOptionsPreset: string; + ytdlOptionsOverrides: string; + ytdlOptionPresetNames: string[] = []; addInProgress = false; cancelRequested = false; subscribeInProgress = false; @@ -231,6 +234,8 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.chapterTemplate = this.cookieService.get('metube_chapter_template') || ''; this.subtitleLanguage = this.cookieService.get('metube_subtitle_language') || 'en'; this.subtitleMode = this.cookieService.get('metube_subtitle_mode') || 'prefer_manual'; + this.ytdlOptionsPreset = this.cookieService.get('metube_ytdl_options_preset') || ''; + this.ytdlOptionsOverrides = this.cookieService.get('metube_ytdl_options_overrides') || ''; const allowedDownloadTypes = new Set(this.downloadTypes.map(t => t.id)); const allowedVideoCodecs = new Set(this.videoCodecs.map(c => c.id)); if (!allowedDownloadTypes.has(this.downloadType)) { @@ -287,6 +292,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { }); this.getConfiguration(); this.getYtdlOptionsUpdateTime(); + this.getYtdlOptionPresets(); this.customDirs$ = this.getMatchingCustomDir(); this.setTheme(this.activeTheme!); @@ -415,6 +421,39 @@ export class App implements AfterViewInit, OnInit, OnDestroy { }); } + getYtdlOptionPresets() { + this.downloads.getPresets().pipe(takeUntilDestroyed(this.destroyRef)).subscribe({ + next: (data) => { + this.ytdlOptionPresetNames = Array.isArray(data?.presets) + ? data.presets.filter((preset): preset is string => typeof preset === 'string') + : []; + if (this.ytdlOptionsPreset && !this.ytdlOptionPresetNames.includes(this.ytdlOptionsPreset)) { + this.ytdlOptionsPreset = ''; + this.ytdlOptionsPresetChanged(); + } + this.cdr.markForCheck(); + }, + }); + } + + private validateYtdlOptionsOverrides(value: string): boolean { + const trimmed = value?.trim() || ''; + if (!trimmed) { + return true; + } + try { + const parsed = JSON.parse(trimmed); + if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object') { + alert('Custom yt-dlp options must be a JSON object'); + return false; + } + } catch { + alert('Custom yt-dlp options must be valid JSON'); + return false; + } + return true; + } + private rebuildCachedSubs() { this.cachedSubs = Array.from(this.subscriptionsSvc.subscriptions.entries()); const validIds = new Set(this.cachedSubs.map(([id]) => id)); @@ -491,6 +530,9 @@ export class App implements AfterViewInit, OnInit, OnDestroy { alert('Chapter template must include %(section_number)'); return; } + if (!this.validateYtdlOptionsOverrides(payload.ytdlOptionsOverrides)) { + return; + } this.subscribeInProgress = true; this.subscriptionsSvc .subscribe({ @@ -695,6 +737,14 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.saveSelection(this.downloadType); } + ytdlOptionsPresetChanged() { + this.cookieService.set('metube_ytdl_options_preset', this.ytdlOptionsPreset, { expires: this.settingsCookieExpiryDays }); + } + + ytdlOptionsOverridesChanged() { + this.cookieService.set('metube_ytdl_options_overrides', this.ytdlOptionsOverrides, { expires: this.settingsCookieExpiryDays }); + } + isVideoType() { return this.downloadType === 'video'; } @@ -894,6 +944,8 @@ export class App implements AfterViewInit, OnInit, OnDestroy { chapterTemplate: overrides.chapterTemplate ?? this.chapterTemplate, subtitleLanguage: overrides.subtitleLanguage ?? this.subtitleLanguage, subtitleMode: overrides.subtitleMode ?? this.subtitleMode, + ytdlOptionsPreset: overrides.ytdlOptionsPreset ?? this.ytdlOptionsPreset, + ytdlOptionsOverrides: overrides.ytdlOptionsOverrides ?? this.ytdlOptionsOverrides, }; } @@ -905,6 +957,9 @@ export class App implements AfterViewInit, OnInit, OnDestroy { alert('Chapter template must include %(section_number)'); return; } + if (!this.validateYtdlOptionsOverrides(payload.ytdlOptionsOverrides)) { + return; + } console.debug('Downloading:', payload); this.addInProgress = true; @@ -960,6 +1015,8 @@ export class App implements AfterViewInit, OnInit, OnDestroy { chapterTemplate: download.chapter_template, subtitleLanguage: download.subtitle_language, subtitleMode: download.subtitle_mode, + ytdlOptionsPreset: download.ytdl_options_preset || '', + ytdlOptionsOverrides: download.ytdl_options_overrides ? JSON.stringify(download.ytdl_options_overrides) : '', }); this.downloads.delById('done', [key]).subscribe(); } diff --git a/ui/src/app/interfaces/download.ts b/ui/src/app/interfaces/download.ts index c988f71..d06af7f 100644 --- a/ui/src/app/interfaces/download.ts +++ b/ui/src/app/interfaces/download.ts @@ -14,6 +14,8 @@ export interface Download { chapter_template?: string; subtitle_language?: string; subtitle_mode?: string; + ytdl_options_preset?: string; + ytdl_options_overrides?: Record; status: string; msg: string; percent: number; diff --git a/ui/src/app/services/downloads.service.spec.ts b/ui/src/app/services/downloads.service.spec.ts index 29e4406..5d4723c 100644 --- a/ui/src/app/services/downloads.service.spec.ts +++ b/ui/src/app/services/downloads.service.spec.ts @@ -39,6 +39,8 @@ function basePayload(): AddDownloadPayload { chapterTemplate: '', subtitleLanguage: 'en', subtitleMode: 'prefer_manual', + ytdlOptionsPreset: '', + ytdlOptionsOverrides: '', }; } @@ -79,11 +81,22 @@ describe('DownloadsService', () => { chapter_template: '', subtitle_language: 'en', subtitle_mode: 'prefer_manual', + ytdl_options_preset: '', + ytdl_options_overrides: '', }), ); req.flush({ status: 'ok' }); }); + it('getPresets() fetches configured preset names', () => { + service.getPresets().subscribe((result) => { + expect(result).toEqual({ presets: ['Preset A'] }); + }); + const req = httpMock.expectOne('presets'); + expect(req.request.method).toBe('GET'); + req.flush({ presets: ['Preset A'] }); + }); + it('cancelAdd posts to cancel-add', () => { service.cancelAdd().subscribe(); const req = httpMock.expectOne('cancel-add'); diff --git a/ui/src/app/services/downloads.service.ts b/ui/src/app/services/downloads.service.ts index 0a2b6f4..66792f7 100644 --- a/ui/src/app/services/downloads.service.ts +++ b/ui/src/app/services/downloads.service.ts @@ -20,6 +20,8 @@ export interface AddDownloadPayload { chapterTemplate: string; subtitleLanguage: string; subtitleMode: string; + ytdlOptionsPreset: string; + ytdlOptionsOverrides: string; } @Injectable({ providedIn: 'root' @@ -141,11 +143,19 @@ export class DownloadsService { chapter_template: payload.chapterTemplate, subtitle_language: payload.subtitleLanguage, subtitle_mode: payload.subtitleMode, + ytdl_options_preset: payload.ytdlOptionsPreset, + ytdl_options_overrides: payload.ytdlOptionsOverrides, }).pipe( catchError(this.handleHTTPError) ); } + public getPresets() { + return this.http.get<{ presets: string[] }>('presets').pipe( + catchError(() => of({ presets: [] })) + ); + } + public startById(ids: string[]) { return this.http.post('start', {ids: ids}); } diff --git a/ui/src/app/services/subscriptions.service.ts b/ui/src/app/services/subscriptions.service.ts index ae35f3c..57fbcf6 100644 --- a/ui/src/app/services/subscriptions.service.ts +++ b/ui/src/app/services/subscriptions.service.ts @@ -94,6 +94,8 @@ export class SubscriptionsService { chapter_template: payload.chapterTemplate, subtitle_language: payload.subtitleLanguage, subtitle_mode: payload.subtitleMode, + ytdl_options_preset: payload.ytdlOptionsPreset, + ytdl_options_overrides: payload.ytdlOptionsOverrides, check_interval_minutes: payload.checkIntervalMinutes, }) .pipe(catchError((err) => this.handleHTTPError(err))); From 6e9b2dd7b3281719480d65ec22f2b3fb983b15d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 09:05:19 +0000 Subject: [PATCH 09/88] Gate manual yt-dlp overrides behind flag Agent-Logs-Url: https://github.com/alexta69/metube/sessions/31b4274d-cf48-4260-b73b-633cbcd2bb09 Co-authored-by: alexta69 <7450369+alexta69@users.noreply.github.com> --- README.md | 1 + app/main.py | 45 +++-------- app/tests/test_api.py | 14 +++- app/tests/test_config.py | 6 ++ app/tests/test_main_helpers.py | 18 ++++- ui/src/app/app.html | 4 +- ui/src/app/app.spec.ts | 143 +++++++++++++++++++++++++++++++++ ui/src/app/app.ts | 12 ++- 8 files changed, 201 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index bfa709a..bb1845a 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ Certain values can be set via environment variables, using the `-e` parameter on * __YTDL_OPTIONS_FILE__: A path to a JSON file that will be loaded and used for populating `YTDL_OPTIONS` above. Please note that if both `YTDL_OPTIONS_FILE` and `YTDL_OPTIONS` are specified, the options in `YTDL_OPTIONS` take precedence. The file will be monitored for changes and reloaded automatically when changes are detected. * __YTDL_OPTIONS_PRESETS__: A JSON object mapping preset names to yt-dlp option objects. These preset names are exposed in the web UI's Advanced Options panel so users can pick per-download overrides without changing the global `YTDL_OPTIONS`. * __YTDL_OPTIONS_PRESETS_FILE__: A path to a JSON file containing `YTDL_OPTIONS_PRESETS`. If both are specified, values from `YTDL_OPTIONS_PRESETS_FILE` are merged into `YTDL_OPTIONS_PRESETS`. +* __ALLOW_YTDL_OPTIONS_OVERRIDES__: Whether to show the web UI field for manual per-download `ytdl_options_overrides`. Defaults to `false`. Enabling this allows arbitrary yt-dlp API options to be supplied by UI users, which may enable arbitrary command execution inside the container depending on the options used. Enable only if you understand and accept that risk. ### 🌐 Web Server & URLs diff --git a/app/main.py b/app/main.py index 4ffe2cb..2a01d77 100644 --- a/app/main.py +++ b/app/main.py @@ -59,6 +59,7 @@ class Config: 'YTDL_OPTIONS_FILE': '', 'YTDL_OPTIONS_PRESETS': '{}', 'YTDL_OPTIONS_PRESETS_FILE': '', + 'ALLOW_YTDL_OPTIONS_OVERRIDES': 'false', 'ROBOTS_TXT': '', 'HOST': '0.0.0.0', 'PORT': '8081', @@ -72,7 +73,7 @@ class Config: 'ENABLE_ACCESSLOG': 'false', } - _BOOLEAN = ('DOWNLOAD_DIRS_INDEXABLE', 'CUSTOM_DIRS', 'CREATE_CUSTOM_DIRS', 'DELETE_FILE_ON_TRASHCAN', 'HTTPS', 'ENABLE_ACCESSLOG') + _BOOLEAN = ('DOWNLOAD_DIRS_INDEXABLE', 'CUSTOM_DIRS', 'CREATE_CUSTOM_DIRS', 'DELETE_FILE_ON_TRASHCAN', 'HTTPS', 'ENABLE_ACCESSLOG', 'ALLOW_YTDL_OPTIONS_OVERRIDES') def __init__(self): for k, v in self._DEFAULTS.items(): @@ -126,6 +127,7 @@ class Config: 'PUBLIC_HOST_AUDIO_URL', 'DEFAULT_OPTION_PLAYLIST_ITEM_LIMIT', 'SUBSCRIPTION_DEFAULT_CHECK_INTERVAL', + 'ALLOW_YTDL_OPTIONS_OVERRIDES', ) def frontend_safe(self) -> dict: @@ -232,36 +234,7 @@ VALID_VIDEO_CODECS = {'auto', 'h264', 'h265', 'av1', 'vp9'} VALID_VIDEO_FORMATS = {'any', 'mp4', 'ios'} VALID_AUDIO_FORMATS = {'m4a', 'mp3', 'opus', 'wav', 'flac'} VALID_THUMBNAIL_FORMATS = {'jpg'} -BLOCKED_YTDL_OVERRIDE_KEYS = frozenset({ - 'exec', - 'exec_before_dl', - 'exec_cmd', - 'external_downloader', - 'external_downloader_args', - 'format', - 'ignore_no_formats_error', - 'no_color', - 'outtmpl', - 'paths', - 'postprocessor_hooks', - 'progress_hooks', - 'quiet', - 'socket_timeout', - 'verbose', -}) - - -def _iter_nested_keys(value): - if isinstance(value, dict): - for key, nested in value.items(): - yield str(key) - yield from _iter_nested_keys(nested) - elif isinstance(value, list): - for item in value: - yield from _iter_nested_keys(item) - - -def _parse_ytdl_options_overrides(value) -> dict: +def _parse_ytdl_options_overrides(value, *, enabled: bool) -> dict: if value is None or value == '': return {} @@ -274,9 +247,8 @@ def _parse_ytdl_options_overrides(value) -> dict: if not isinstance(value, dict): raise web.HTTPBadRequest(reason='ytdl_options_overrides must be a JSON object') - blocked_keys = sorted({key for key in _iter_nested_keys(value) if key in BLOCKED_YTDL_OVERRIDE_KEYS}) - if blocked_keys: - raise web.HTTPBadRequest(reason=f'ytdl_options_overrides contains disallowed keys: {blocked_keys}') + if value and not enabled: + raise web.HTTPBadRequest(reason='ytdl_options_overrides are disabled') return value @@ -497,7 +469,10 @@ def parse_download_options(post: dict) -> dict: subtitle_language = str(subtitle_language).strip() subtitle_mode = str(subtitle_mode).strip() ytdl_options_preset = str(ytdl_options_preset).strip() - ytdl_options_overrides = _parse_ytdl_options_overrides(ytdl_options_overrides) + ytdl_options_overrides = _parse_ytdl_options_overrides( + ytdl_options_overrides, + enabled=config.ALLOW_YTDL_OPTIONS_OVERRIDES, + ) if chapter_template and ('..' in chapter_template or chapter_template.startswith('/') or chapter_template.startswith('\\')): raise web.HTTPBadRequest(reason='chapter_template must not contain ".." or start with a path separator') diff --git a/app/tests/test_api.py b/app/tests/test_api.py index 5af4e25..b8d1f2d 100644 --- a/app/tests/test_api.py +++ b/app/tests/test_api.py @@ -64,6 +64,7 @@ async def test_add_ok(mock_dqueue): @pytest.mark.asyncio async def test_add_passes_preset_and_overrides(mock_dqueue, monkeypatch): monkeypatch.setattr(main.config, "YTDL_OPTIONS_PRESETS", {"Preset A": {"writesubtitles": True}}) + monkeypatch.setattr(main.config, "ALLOW_YTDL_OPTIONS_OVERRIDES", True) req = _json_request( _valid_video_add_body( ytdl_options_preset="Preset A", @@ -151,12 +152,23 @@ async def test_add_invalid_ytdl_options_override_json(mock_dqueue): @pytest.mark.asyncio -async def test_add_blocked_ytdl_options_override_key(mock_dqueue): +async def test_add_rejects_ytdl_options_overrides_when_disabled(mock_dqueue): req = _json_request(_valid_video_add_body(ytdl_options_overrides='{"exec": "rm -rf /"}')) with pytest.raises(web.HTTPBadRequest): await main.add(req) +@pytest.mark.asyncio +async def test_add_allows_any_ytdl_options_override_key_when_enabled(mock_dqueue, monkeypatch): + monkeypatch.setattr(main.config, "ALLOW_YTDL_OPTIONS_OVERRIDES", True) + req = _json_request(_valid_video_add_body(ytdl_options_overrides='{"exec": "echo hi"}')) + resp = await main.add(req) + assert resp.status == 200 + call = mock_dqueue.add.await_args + assert call is not None + assert call.args[14] == {"exec": "echo hi"} + + @pytest.mark.asyncio async def test_add_unknown_ytdl_preset(mock_dqueue): req = _json_request(_valid_video_add_body(ytdl_options_preset="Missing")) diff --git a/app/tests/test_config.py b/app/tests/test_config.py index 558f998..d97c1a9 100644 --- a/app/tests/test_config.py +++ b/app/tests/test_config.py @@ -59,6 +59,12 @@ class ConfigTests(unittest.TestCase): safe = c.frontend_safe() self.assertNotIn("YTDL_OPTIONS", safe) self.assertNotIn("HOST", safe) + self.assertEqual(safe["ALLOW_YTDL_OPTIONS_OVERRIDES"], False) + + def test_allow_ytdl_options_overrides_boolean_loaded(self): + with patch.dict(os.environ, _base_env(ALLOW_YTDL_OPTIONS_OVERRIDES="true"), clear=False): + c = Config() + self.assertTrue(c.ALLOW_YTDL_OPTIONS_OVERRIDES) def test_runtime_override_roundtrip(self): with patch.dict(os.environ, _base_env(), clear=False): diff --git a/app/tests/test_main_helpers.py b/app/tests/test_main_helpers.py index e6cfcfd..37dd6fd 100644 --- a/app/tests/test_main_helpers.py +++ b/app/tests/test_main_helpers.py @@ -99,25 +99,34 @@ class FrontendSafeTests(unittest.TestCase): self.assertIn(key, safe) self.assertNotIn("YTDL_OPTIONS", safe) self.assertNotIn("DOWNLOAD_DIR", safe) + self.assertIn("ALLOW_YTDL_OPTIONS_OVERRIDES", safe) class ParseYtdlOverridesTests(unittest.TestCase): def test_empty_override_string_returns_empty_dict(self): - self.assertEqual(main._parse_ytdl_options_overrides(""), {}) + self.assertEqual(main._parse_ytdl_options_overrides("", enabled=False), {}) def test_rejects_non_object_json(self): with self.assertRaises(main.web.HTTPBadRequest): - main._parse_ytdl_options_overrides('["bad"]') + main._parse_ytdl_options_overrides('["bad"]', enabled=True) - def test_rejects_blocked_keys(self): + def test_rejects_non_empty_overrides_when_disabled(self): with self.assertRaises(main.web.HTTPBadRequest): - main._parse_ytdl_options_overrides('{"exec": "rm -rf /"}') + main._parse_ytdl_options_overrides('{"exec": "rm -rf /"}', enabled=False) + + def test_allows_any_keys_when_enabled(self): + self.assertEqual( + main._parse_ytdl_options_overrides('{"exec": "rm -rf /"}', enabled=True), + {"exec": "rm -rf /"}, + ) class ParseDownloadOptionsTests(unittest.TestCase): def test_accepts_known_preset_and_overrides(self): previous = dict(main.config.YTDL_OPTIONS_PRESETS) + previous_allow = main.config.ALLOW_YTDL_OPTIONS_OVERRIDES main.config.YTDL_OPTIONS_PRESETS = {"With subtitles": {"writesubtitles": True}} + main.config.ALLOW_YTDL_OPTIONS_OVERRIDES = True try: parsed = main.parse_download_options({ "url": "https://example.com/v", @@ -130,6 +139,7 @@ class ParseDownloadOptionsTests(unittest.TestCase): }) finally: main.config.YTDL_OPTIONS_PRESETS = previous + main.config.ALLOW_YTDL_OPTIONS_OVERRIDES = previous_allow self.assertEqual(parsed["ytdl_options_preset"], "With subtitles") self.assertEqual(parsed["ytdl_options_overrides"], {"writesubtitles": True}) diff --git a/ui/src/app/app.html b/ui/src/app/app.html index 73d4f77..453d4d9 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -447,7 +447,7 @@ ngbTooltip="How often to poll subscriptions for new videos">
-
+
Option Preset - - - -
-
+
+ + +
Download Folder @@ -405,7 +392,6 @@ ngbTooltip="Choose where to save downloads. Type to create a new folder." /> }
-
@@ -419,6 +405,48 @@ ngbTooltip="Add a prefix to downloaded filenames">
+
+
+
+
+ + +
+
+ @if (splitByChapters) { +
+
+ Template + +
+
+ } +
+
+
+ + + +
+
+
+ Auto Start + +
+
Items Limit @@ -448,7 +476,10 @@
-
+ + + +
Option Preset @@ -481,101 +512,72 @@
}
-
-
-
-
-
- - -
-
- @if (splitByChapters) { -
-
- Template - -
-
+ + + +
+
+
Cookies
+ +
+ + @if (hasCookies) { + + } +
+
-
- - -
-
-
-
-
-
Cookies
- -
- - @if (hasCookies) { - - } -
- +
+
Bulk Actions
+
+
+
-
-
Bulk Actions
-
-
- -
-
- -
-
- -
-
+
+ +
+
+
diff --git a/ui/src/app/app.sass b/ui/src/app/app.sass index 9368c48..7232aaf 100644 --- a/ui/src/app/app.sass +++ b/ui/src/app/app.sass @@ -182,6 +182,18 @@ main opacity: 0.65 pointer-events: none +.settings-section-label + font-size: 0.8rem + text-transform: uppercase + letter-spacing: 0.1em + font-weight: 600 + color: var(--bs-body-color) + margin-top: 1.75rem + margin-bottom: 0.75rem + + &:first-child + margin-top: 0 + .action-group-label font-size: 0.7rem text-transform: uppercase diff --git a/uv.lock b/uv.lock index d439471..44a77db 100644 --- a/uv.lock +++ b/uv.lock @@ -235,59 +235,59 @@ wheels = [ [[package]] name = "charset-normalizer" -version = "3.4.6" +version = "3.4.7" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7b/60/e3bec1881450851b087e301bedc3daa9377a4d45f1c26aa90b0b235e38aa/charset_normalizer-3.4.6.tar.gz", hash = "sha256:1ae6b62897110aa7c79ea2f5dd38d1abca6db663687c0b1ad9aed6f6bae3d9d6", size = 143363, upload-time = "2026-03-15T18:53:25.478Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/1d/4fdabeef4e231153b6ed7567602f3b68265ec4e5b76d6024cf647d43d981/charset_normalizer-3.4.6-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:11afb56037cbc4b1555a34dd69151e8e069bee82e613a73bef6e714ce733585f", size = 294823, upload-time = "2026-03-15T18:51:15.755Z" }, - { url = "https://files.pythonhosted.org/packages/47/7b/20e809b89c69d37be748d98e84dce6820bf663cf19cf6b942c951a3e8f41/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:423fb7e748a08f854a08a222b983f4df1912b1daedce51a72bd24fe8f26a1843", size = 198527, upload-time = "2026-03-15T18:51:17.177Z" }, - { url = "https://files.pythonhosted.org/packages/37/a6/4f8d27527d59c039dce6f7622593cdcd3d70a8504d87d09eb11e9fdc6062/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d73beaac5e90173ac3deb9928a74763a6d230f494e4bfb422c217a0ad8e629bf", size = 218388, upload-time = "2026-03-15T18:51:18.934Z" }, - { url = "https://files.pythonhosted.org/packages/f6/9b/4770ccb3e491a9bacf1c46cc8b812214fe367c86a96353ccc6daf87b01ec/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d60377dce4511655582e300dc1e5a5f24ba0cb229005a1d5c8d0cb72bb758ab8", size = 214563, upload-time = "2026-03-15T18:51:20.374Z" }, - { url = "https://files.pythonhosted.org/packages/2b/58/a199d245894b12db0b957d627516c78e055adc3a0d978bc7f65ddaf7c399/charset_normalizer-3.4.6-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:530e8cebeea0d76bdcf93357aa5e41336f48c3dc709ac52da2bb167c5b8271d9", size = 206587, upload-time = "2026-03-15T18:51:21.807Z" }, - { url = "https://files.pythonhosted.org/packages/7e/70/3def227f1ec56f5c69dfc8392b8bd63b11a18ca8178d9211d7cc5e5e4f27/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:a26611d9987b230566f24a0a125f17fe0de6a6aff9f25c9f564aaa2721a5fb88", size = 194724, upload-time = "2026-03-15T18:51:23.508Z" }, - { url = "https://files.pythonhosted.org/packages/58/ab/9318352e220c05efd31c2779a23b50969dc94b985a2efa643ed9077bfca5/charset_normalizer-3.4.6-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:34315ff4fc374b285ad7f4a0bf7dcbfe769e1b104230d40f49f700d4ab6bbd84", size = 202956, upload-time = "2026-03-15T18:51:25.239Z" }, - { url = "https://files.pythonhosted.org/packages/75/13/f3550a3ac25b70f87ac98c40d3199a8503676c2f1620efbf8d42095cfc40/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5f8ddd609f9e1af8c7bd6e2aca279c931aefecd148a14402d4e368f3171769fd", size = 201923, upload-time = "2026-03-15T18:51:26.682Z" }, - { url = "https://files.pythonhosted.org/packages/1b/db/c5c643b912740b45e8eec21de1bbab8e7fc085944d37e1e709d3dcd9d72f/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:80d0a5615143c0b3225e5e3ef22c8d5d51f3f72ce0ea6fb84c943546c7b25b6c", size = 195366, upload-time = "2026-03-15T18:51:28.129Z" }, - { url = "https://files.pythonhosted.org/packages/5a/67/3b1c62744f9b2448443e0eb160d8b001c849ec3fef591e012eda6484787c/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:92734d4d8d187a354a556626c221cd1a892a4e0802ccb2af432a1d85ec012194", size = 219752, upload-time = "2026-03-15T18:51:29.556Z" }, - { url = "https://files.pythonhosted.org/packages/f6/98/32ffbaf7f0366ffb0445930b87d103f6b406bc2c271563644bde8a2b1093/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:613f19aa6e082cf96e17e3ffd89383343d0d589abda756b7764cf78361fd41dc", size = 203296, upload-time = "2026-03-15T18:51:30.921Z" }, - { url = "https://files.pythonhosted.org/packages/41/12/5d308c1bbe60cabb0c5ef511574a647067e2a1f631bc8634fcafaccd8293/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2b1a63e8224e401cafe7739f77efd3f9e7f5f2026bda4aead8e59afab537784f", size = 215956, upload-time = "2026-03-15T18:51:32.399Z" }, - { url = "https://files.pythonhosted.org/packages/53/e9/5f85f6c5e20669dbe56b165c67b0260547dea97dba7e187938833d791687/charset_normalizer-3.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6cceb5473417d28edd20c6c984ab6fee6c6267d38d906823ebfe20b03d607dc2", size = 208652, upload-time = "2026-03-15T18:51:34.214Z" }, - { url = "https://files.pythonhosted.org/packages/f1/11/897052ea6af56df3eef3ca94edafee410ca699ca0c7b87960ad19932c55e/charset_normalizer-3.4.6-cp313-cp313-win32.whl", hash = "sha256:d7de2637729c67d67cf87614b566626057e95c303bc0a55ffe391f5205e7003d", size = 143940, upload-time = "2026-03-15T18:51:36.15Z" }, - { url = "https://files.pythonhosted.org/packages/a1/5c/724b6b363603e419829f561c854b87ed7c7e31231a7908708ac086cdf3e2/charset_normalizer-3.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:572d7c822caf521f0525ba1bce1a622a0b85cf47ffbdae6c9c19e3b5ac3c4389", size = 154101, upload-time = "2026-03-15T18:51:37.876Z" }, - { url = "https://files.pythonhosted.org/packages/01/a5/7abf15b4c0968e47020f9ca0935fb3274deb87cb288cd187cad92e8cdffd/charset_normalizer-3.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a4474d924a47185a06411e0064b803c68be044be2d60e50e8bddcc2649957c1f", size = 143109, upload-time = "2026-03-15T18:51:39.565Z" }, - { url = "https://files.pythonhosted.org/packages/25/6f/ffe1e1259f384594063ea1869bfb6be5cdb8bc81020fc36c3636bc8302a1/charset_normalizer-3.4.6-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9cc6e6d9e571d2f863fa77700701dae73ed5f78881efc8b3f9a4398772ff53e8", size = 294458, upload-time = "2026-03-15T18:51:41.134Z" }, - { url = "https://files.pythonhosted.org/packages/56/60/09bb6c13a8c1016c2ed5c6a6488e4ffef506461aa5161662bd7636936fb1/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef5960d965e67165d75b7c7ffc60a83ec5abfc5c11b764ec13ea54fbef8b4421", size = 199277, upload-time = "2026-03-15T18:51:42.953Z" }, - { url = "https://files.pythonhosted.org/packages/00/50/dcfbb72a5138bbefdc3332e8d81a23494bf67998b4b100703fd15fa52d81/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b3694e3f87f8ac7ce279d4355645b3c878d24d1424581b46282f24b92f5a4ae2", size = 218758, upload-time = "2026-03-15T18:51:44.339Z" }, - { url = "https://files.pythonhosted.org/packages/03/b3/d79a9a191bb75f5aa81f3aaaa387ef29ce7cb7a9e5074ba8ea095cc073c2/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5d11595abf8dd942a77883a39d81433739b287b6aa71620f15164f8096221b30", size = 215299, upload-time = "2026-03-15T18:51:45.871Z" }, - { url = "https://files.pythonhosted.org/packages/76/7e/bc8911719f7084f72fd545f647601ea3532363927f807d296a8c88a62c0d/charset_normalizer-3.4.6-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7bda6eebafd42133efdca535b04ccb338ab29467b3f7bf79569883676fc628db", size = 206811, upload-time = "2026-03-15T18:51:47.308Z" }, - { url = "https://files.pythonhosted.org/packages/e2/40/c430b969d41dda0c465aa36cc7c2c068afb67177bef50905ac371b28ccc7/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:bbc8c8650c6e51041ad1be191742b8b421d05bbd3410f43fa2a00c8db87678e8", size = 193706, upload-time = "2026-03-15T18:51:48.849Z" }, - { url = "https://files.pythonhosted.org/packages/48/15/e35e0590af254f7df984de1323640ef375df5761f615b6225ba8deb9799a/charset_normalizer-3.4.6-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:22c6f0c2fbc31e76c3b8a86fba1a56eda6166e238c29cdd3d14befdb4a4e4815", size = 202706, upload-time = "2026-03-15T18:51:50.257Z" }, - { url = "https://files.pythonhosted.org/packages/5e/bd/f736f7b9cc5e93a18b794a50346bb16fbfd6b37f99e8f306f7951d27c17c/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7edbed096e4a4798710ed6bc75dcaa2a21b68b6c356553ac4823c3658d53743a", size = 202497, upload-time = "2026-03-15T18:51:52.012Z" }, - { url = "https://files.pythonhosted.org/packages/9d/ba/2cc9e3e7dfdf7760a6ed8da7446d22536f3d0ce114ac63dee2a5a3599e62/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:7f9019c9cb613f084481bd6a100b12e1547cf2efe362d873c2e31e4035a6fa43", size = 193511, upload-time = "2026-03-15T18:51:53.723Z" }, - { url = "https://files.pythonhosted.org/packages/9e/cb/5be49b5f776e5613be07298c80e1b02a2d900f7a7de807230595c85a8b2e/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:58c948d0d086229efc484fe2f30c2d382c86720f55cd9bc33591774348ad44e0", size = 220133, upload-time = "2026-03-15T18:51:55.333Z" }, - { url = "https://files.pythonhosted.org/packages/83/43/99f1b5dad345accb322c80c7821071554f791a95ee50c1c90041c157ae99/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:419a9d91bd238052642a51938af8ac05da5b3343becde08d5cdeab9046df9ee1", size = 203035, upload-time = "2026-03-15T18:51:56.736Z" }, - { url = "https://files.pythonhosted.org/packages/87/9a/62c2cb6a531483b55dddff1a68b3d891a8b498f3ca555fbcf2978e804d9d/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5273b9f0b5835ff0350c0828faea623c68bfa65b792720c453e22b25cc72930f", size = 216321, upload-time = "2026-03-15T18:51:58.17Z" }, - { url = "https://files.pythonhosted.org/packages/6e/79/94a010ff81e3aec7c293eb82c28f930918e517bc144c9906a060844462eb/charset_normalizer-3.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0e901eb1049fdb80f5bd11ed5ea1e498ec423102f7a9b9e4645d5b8204ff2815", size = 208973, upload-time = "2026-03-15T18:51:59.998Z" }, - { url = "https://files.pythonhosted.org/packages/2a/57/4ecff6d4ec8585342f0c71bc03efaa99cb7468f7c91a57b105bcd561cea8/charset_normalizer-3.4.6-cp314-cp314-win32.whl", hash = "sha256:b4ff1d35e8c5bd078be89349b6f3a845128e685e751b6ea1169cf2160b344c4d", size = 144610, upload-time = "2026-03-15T18:52:02.213Z" }, - { url = "https://files.pythonhosted.org/packages/80/94/8434a02d9d7f168c25767c64671fead8d599744a05d6a6c877144c754246/charset_normalizer-3.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:74119174722c4349af9708993118581686f343adc1c8c9c007d59be90d077f3f", size = 154962, upload-time = "2026-03-15T18:52:03.658Z" }, - { url = "https://files.pythonhosted.org/packages/46/4c/48f2cdbfd923026503dfd67ccea45c94fd8fe988d9056b468579c66ed62b/charset_normalizer-3.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:e5bcc1a1ae744e0bb59641171ae53743760130600da8db48cbb6e4918e186e4e", size = 143595, upload-time = "2026-03-15T18:52:05.123Z" }, - { url = "https://files.pythonhosted.org/packages/31/93/8878be7569f87b14f1d52032946131bcb6ebbd8af3e20446bc04053dc3f1/charset_normalizer-3.4.6-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:ad8faf8df23f0378c6d527d8b0b15ea4a2e23c89376877c598c4870d1b2c7866", size = 314828, upload-time = "2026-03-15T18:52:06.831Z" }, - { url = "https://files.pythonhosted.org/packages/06/b6/fae511ca98aac69ecc35cde828b0a3d146325dd03d99655ad38fc2cc3293/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f5ea69428fa1b49573eef0cc44a1d43bebd45ad0c611eb7d7eac760c7ae771bc", size = 208138, upload-time = "2026-03-15T18:52:08.239Z" }, - { url = "https://files.pythonhosted.org/packages/54/57/64caf6e1bf07274a1e0b7c160a55ee9e8c9ec32c46846ce59b9c333f7008/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:06a7e86163334edfc5d20fe104db92fcd666e5a5df0977cb5680a506fe26cc8e", size = 224679, upload-time = "2026-03-15T18:52:10.043Z" }, - { url = "https://files.pythonhosted.org/packages/aa/cb/9ff5a25b9273ef160861b41f6937f86fae18b0792fe0a8e75e06acb08f1d/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e1f6e2f00a6b8edb562826e4632e26d063ac10307e80f7461f7de3ad8ef3f077", size = 223475, upload-time = "2026-03-15T18:52:11.854Z" }, - { url = "https://files.pythonhosted.org/packages/fc/97/440635fc093b8d7347502a377031f9605a1039c958f3cd18dcacffb37743/charset_normalizer-3.4.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:95b52c68d64c1878818687a473a10547b3292e82b6f6fe483808fb1468e2f52f", size = 215230, upload-time = "2026-03-15T18:52:13.325Z" }, - { url = "https://files.pythonhosted.org/packages/cd/24/afff630feb571a13f07c8539fbb502d2ab494019492aaffc78ef41f1d1d0/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:7504e9b7dc05f99a9bbb4525c67a2c155073b44d720470a148b34166a69c054e", size = 199045, upload-time = "2026-03-15T18:52:14.752Z" }, - { url = "https://files.pythonhosted.org/packages/e5/17/d1399ecdaf7e0498c327433e7eefdd862b41236a7e484355b8e0e5ebd64b/charset_normalizer-3.4.6-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:172985e4ff804a7ad08eebec0a1640ece87ba5041d565fff23c8f99c1f389484", size = 211658, upload-time = "2026-03-15T18:52:16.278Z" }, - { url = "https://files.pythonhosted.org/packages/b5/38/16baa0affb957b3d880e5ac2144caf3f9d7de7bc4a91842e447fbb5e8b67/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:4be9f4830ba8741527693848403e2c457c16e499100963ec711b1c6f2049b7c7", size = 210769, upload-time = "2026-03-15T18:52:17.782Z" }, - { url = "https://files.pythonhosted.org/packages/05/34/c531bc6ac4c21da9ddfddb3107be2287188b3ea4b53b70fc58f2a77ac8d8/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79090741d842f564b1b2827c0b82d846405b744d31e84f18d7a7b41c20e473ff", size = 201328, upload-time = "2026-03-15T18:52:19.553Z" }, - { url = "https://files.pythonhosted.org/packages/fa/73/a5a1e9ca5f234519c1953608a03fe109c306b97fdfb25f09182babad51a7/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:87725cfb1a4f1f8c2fc9890ae2f42094120f4b44db9360be5d99a4c6b0e03a9e", size = 225302, upload-time = "2026-03-15T18:52:21.043Z" }, - { url = "https://files.pythonhosted.org/packages/ba/f6/cd782923d112d296294dea4bcc7af5a7ae0f86ab79f8fefbda5526b6cfc0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:fcce033e4021347d80ed9c66dcf1e7b1546319834b74445f561d2e2221de5659", size = 211127, upload-time = "2026-03-15T18:52:22.491Z" }, - { url = "https://files.pythonhosted.org/packages/0e/c5/0b6898950627af7d6103a449b22320372c24c6feda91aa24e201a478d161/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ca0276464d148c72defa8bb4390cce01b4a0e425f3b50d1435aa6d7a18107602", size = 222840, upload-time = "2026-03-15T18:52:24.113Z" }, - { url = "https://files.pythonhosted.org/packages/7d/25/c4bba773bef442cbdc06111d40daa3de5050a676fa26e85090fc54dd12f0/charset_normalizer-3.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:197c1a244a274bb016dd8b79204850144ef77fe81c5b797dc389327adb552407", size = 216890, upload-time = "2026-03-15T18:52:25.541Z" }, - { url = "https://files.pythonhosted.org/packages/35/1a/05dacadb0978da72ee287b0143097db12f2e7e8d3ffc4647da07a383b0b7/charset_normalizer-3.4.6-cp314-cp314t-win32.whl", hash = "sha256:2a24157fa36980478dd1770b585c0f30d19e18f4fb0c47c13aa568f871718579", size = 155379, upload-time = "2026-03-15T18:52:27.05Z" }, - { url = "https://files.pythonhosted.org/packages/5d/7a/d269d834cb3a76291651256f3b9a5945e81d0a49ab9f4a498964e83c0416/charset_normalizer-3.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:cd5e2801c89992ed8c0a3f0293ae83c159a60d9a5d685005383ef4caca77f2c4", size = 169043, upload-time = "2026-03-15T18:52:28.502Z" }, - { url = "https://files.pythonhosted.org/packages/23/06/28b29fba521a37a8932c6a84192175c34d49f84a6d4773fa63d05f9aff22/charset_normalizer-3.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:47955475ac79cc504ef2704b192364e51d0d473ad452caedd0002605f780101c", size = 148523, upload-time = "2026-03-15T18:52:29.956Z" }, - { url = "https://files.pythonhosted.org/packages/2a/68/687187c7e26cb24ccbd88e5069f5ef00eba804d36dde11d99aad0838ab45/charset_normalizer-3.4.6-py3-none-any.whl", hash = "sha256:947cf925bc916d90adba35a64c82aace04fa39b46b52d4630ece166655905a69", size = 61455, upload-time = "2026-03-15T18:53:23.833Z" }, + { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, + { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, + { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, + { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, + { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, + { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, + { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, + { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, + { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, + { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, + { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, + { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, + { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, + { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, + { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, + { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, + { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, + { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, + { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, + { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, + { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, + { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, + { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, + { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, + { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, + { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, + { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, + { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, + { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, + { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, + { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, ] [[package]] @@ -324,15 +324,15 @@ wheels = [ [[package]] name = "deno" -version = "2.7.10" +version = "2.7.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e8/01/c03ed7db9adbd02a45de56037e2b685adc730775e8878229881ed907458d/deno-2.7.10.tar.gz", hash = "sha256:ea30a61f98c9a57b80f80a525a1d4687e36b7fcca133f813439c8431489e703b", size = 8165, upload-time = "2026-03-31T15:12:00.299Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/3f/a0477c72b847c0082ceb8885261eb14fb4addde22e8fb0146c011636979b/deno-2.7.11.tar.gz", hash = "sha256:342a656fca446fadc261ed22af35693b6c34e79129fa2bd387a1e5d39f496a99", size = 8167, upload-time = "2026-04-01T12:48:16.673Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/4b/a28d8c7ff5d797f52098dcbce91b3ff8394bdbd0dd07cb4c87b032ead539/deno-2.7.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4e361633c1ce6ec439d312911ec230e4e060c4e5ca957e8f58823129af511b13", size = 47849281, upload-time = "2026-03-31T15:11:43.879Z" }, - { url = "https://files.pythonhosted.org/packages/b5/da/d572cf9f195aaf317a0e222af10f8adf3f3acfd114f80fec78c110fb66e6/deno-2.7.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6c4c03e583c4c4d5647ec97690038a4b7c00a7ad076949b5ce26203857b1c85a", size = 44608218, upload-time = "2026-03-31T15:11:47.664Z" }, - { url = "https://files.pythonhosted.org/packages/e6/da/f77fd4852d84063728d618b9f4c088b31d27a999b6dfc3dd1f9623dd56a9/deno-2.7.10-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b415a36b63e3c5c478180a5cc0e9f517095005086144dbe52b34268b397c404b", size = 48384632, upload-time = "2026-03-31T15:11:50.816Z" }, - { url = "https://files.pythonhosted.org/packages/dc/13/a41c3aba09103cd31ddbe2fc8fc98df5118df7b23d4cee248926367c6469/deno-2.7.10-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:1f4aab0be642692205df91e39eff1db774d6b4c5d8dfcff0669d014fd0c80cba", size = 50420236, upload-time = "2026-03-31T15:11:54.139Z" }, - { url = "https://files.pythonhosted.org/packages/c7/a9/423f671846107bed51c405bbd1e32782f0b39edd5d075e4f806b8eea77f5/deno-2.7.10-py3-none-win_amd64.whl", hash = "sha256:3c2ee1773cf48b0fe9e74d23da3b6f9b685240e90db81ce6f5c8c0922c08b992", size = 49403842, upload-time = "2026-03-31T15:11:57.728Z" }, + { url = "https://files.pythonhosted.org/packages/d1/64/c8cb5a9c50135ada59b412b7511852d551d2618e169f48a8e7b8e90a382a/deno-2.7.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:76aa633656b07f64cfda3aa94a1e16bc123034ba2fe676d23b179f8326728534", size = 47857669, upload-time = "2026-04-01T12:48:01.947Z" }, + { url = "https://files.pythonhosted.org/packages/27/f3/250216e71e21cfc291ff6eb6503a479410ff927c9f97ebb644463c620692/deno-2.7.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a1dd1ceb0c2109b54334a621503e5297d473f5c3e6702992dfa68d9b7cdf1177", size = 44607869, upload-time = "2026-04-01T12:48:05.23Z" }, + { url = "https://files.pythonhosted.org/packages/01/51/b8fcb7d6882d659abd678f48fc5bc89aa0aa10f5a399fca3823680f277ca/deno-2.7.11-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:56578808c891d1daeadc279ac73445a24a83c2c721b37016fd92b1aa9224979b", size = 48394760, upload-time = "2026-04-01T12:48:08.049Z" }, + { url = "https://files.pythonhosted.org/packages/a0/18/91d1ceb2e15b4446fcecdf4be17ed59b02f5b35d8c98c94e7eaadf10591e/deno-2.7.11-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:58eea29d18fe8f80f2a4354d96225a31961ed3fa68336a7fcdd6add331df6acd", size = 50418090, upload-time = "2026-04-01T12:48:10.84Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8d/7a801d85a0a8233d00ea1a381fd32e76c81be9db78ada162e211e7619221/deno-2.7.11-py3-none-win_amd64.whl", hash = "sha256:e2b69676e52543153b82e926f558983f9251db2251474f3225301cf7aca05a2b", size = 49417475, upload-time = "2026-04-01T12:48:14.158Z" }, ] [[package]] From dd0f98d12f502b4a15b1b9fb4ffaa873324e2d1c Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Sat, 4 Apr 2026 10:25:46 +0300 Subject: [PATCH 13/88] change option presets to be multi-select --- .vscode/launch.json | 2 + README.md | 22 +++++++- app/main.py | 33 ++++++++--- app/subscriptions.py | 38 ++++++++++--- app/tests/test_api.py | 21 +++++-- app/tests/test_download_queue.py | 10 +++- app/tests/test_main_helpers.py | 55 ++++++++++++++++++- app/ytdl.py | 41 ++++++++------ ui/src/app/app.html | 22 ++++---- ui/src/app/app.spec.ts | 8 +-- ui/src/app/app.ts | 44 ++++++++++++--- ui/src/app/interfaces/download.ts | 2 +- ui/src/app/services/downloads.service.spec.ts | 4 +- ui/src/app/services/downloads.service.ts | 4 +- ui/src/app/services/subscriptions.service.ts | 2 +- 15 files changed, 234 insertions(+), 74 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 5d88c55..5c31505 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,6 +14,7 @@ "DOWNLOAD_DIR": "${env:USERPROFILE}/Downloads", "STATE_DIR": "${env:TEMP}", "ALLOW_YTDL_OPTIONS_OVERRIDES": "true", + "YTDL_OPTIONS_PRESETS": "{\"sponsorblock\": {\"postprocessors\": [{\"key\": \"SponsorBlock\", \"categories\": [\"sponsor\", \"selfpromo\", \"interaction\"]}, {\"key\": \"ModifyChapters\", \"remove_sponsor_segments\": [\"sponsor\", \"selfpromo\", \"interaction\"]}]}, \"embed-subs\": {\"writesubtitles\": true, \"writeautomaticsub\": true, \"subtitleslangs\": [\"en\", \"de\"], \"postprocessors\": [{\"key\": \"FFmpegEmbedSubtitle\"}]}, \"limit-rate\": {\"ratelimit\": 5000000}}", } }, "osx": { @@ -21,6 +22,7 @@ "DOWNLOAD_DIR": "${env:HOME}/Downloads", "STATE_DIR": "${env:TMPDIR}", "ALLOW_YTDL_OPTIONS_OVERRIDES": "true", + "YTDL_OPTIONS_PRESETS": "{\"sponsorblock\": {\"postprocessors\": [{\"key\": \"SponsorBlock\", \"categories\": [\"sponsor\", \"selfpromo\", \"interaction\"]}, {\"key\": \"ModifyChapters\", \"remove_sponsor_segments\": [\"sponsor\", \"selfpromo\", \"interaction\"]}]}, \"embed-subs\": {\"writesubtitles\": true, \"writeautomaticsub\": true, \"subtitleslangs\": [\"en\", \"de\"], \"postprocessors\": [{\"key\": \"FFmpegEmbedSubtitle\"}]}, \"limit-rate\": {\"ratelimit\": 5000000}}", } }, "console": "integratedTerminal" diff --git a/README.md b/README.md index bb1845a..87d897f 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,27 @@ Certain values can be set via environment variables, using the `-e` parameter on * __OUTPUT_TEMPLATE_CHANNEL__: The template for the filenames of the downloaded videos when downloaded as a channel. Defaults to `%(channel)s/%(title)s.%(ext)s`. Set to empty to use `OUTPUT_TEMPLATE` instead. * __YTDL_OPTIONS__: Additional options to pass to yt-dlp in JSON format. [See available options here](https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/YoutubeDL.py#L222). They roughly correspond to command-line options, though some do not have exact equivalents here. For example, `--recode-video` has to be specified via `postprocessors`. Also note that dashes are replaced with underscores. You may find [this script](https://github.com/yt-dlp/yt-dlp/blob/master/devscripts/cli_to_api.py) helpful for converting from command-line options to `YTDL_OPTIONS`. * __YTDL_OPTIONS_FILE__: A path to a JSON file that will be loaded and used for populating `YTDL_OPTIONS` above. Please note that if both `YTDL_OPTIONS_FILE` and `YTDL_OPTIONS` are specified, the options in `YTDL_OPTIONS` take precedence. The file will be monitored for changes and reloaded automatically when changes are detected. -* __YTDL_OPTIONS_PRESETS__: A JSON object mapping preset names to yt-dlp option objects. These preset names are exposed in the web UI's Advanced Options panel so users can pick per-download overrides without changing the global `YTDL_OPTIONS`. +* __YTDL_OPTIONS_PRESETS__: Lets you define **named presets**β€”each preset is a bundle of extra download settings. In the web UI (Advanced Options), you can pick **one or more** presets for a download instead of changing the server-wide defaults. If several presets are selected, they are applied **in order**; if two presets touch the same setting, the **later** one wins. If you turn on the optional β€œcustom yt-dlp options” field, those choices override presets. Define each preset the same way as global `YTDL_OPTIONS` (see above). Example: + ```json + { + "sponsorblock": { + "postprocessors": [ + { "key": "SponsorBlock", "categories": ["sponsor", "selfpromo", "interaction"] }, + { "key": "ModifyChapters", "remove_sponsor_segments": ["sponsor", "selfpromo", "interaction"] } + ] + }, + "embed-subs": { + "writesubtitles": true, + "writeautomaticsub": true, + "subtitleslangs": ["en", "de"], + "postprocessors": [{ "key": "FFmpegEmbedSubtitle" }] + }, + "limit-rate": { + "ratelimit": 5000000 + } + } + ``` + With this file, the UI would offer **sponsorblock** (remove sponsor/self-promo/interaction segments), **embed-subs** (add English and German subtitles into the video file), and **limit-rate** (slow downloads to about 5 MB/s)β€”things the normal quality/format controls do not cover. * __YTDL_OPTIONS_PRESETS_FILE__: A path to a JSON file containing `YTDL_OPTIONS_PRESETS`. If both are specified, values from `YTDL_OPTIONS_PRESETS_FILE` are merged into `YTDL_OPTIONS_PRESETS`. * __ALLOW_YTDL_OPTIONS_OVERRIDES__: Whether to show the web UI field for manual per-download `ytdl_options_overrides`. Defaults to `false`. Enabling this allows arbitrary yt-dlp API options to be supplied by UI users, which may enable arbitrary command execution inside the container depending on the options used. Enable only if you understand and accept that risk. diff --git a/app/main.py b/app/main.py index 2a01d77..5853163 100644 --- a/app/main.py +++ b/app/main.py @@ -253,6 +253,23 @@ def _parse_ytdl_options_overrides(value, *, enabled: bool) -> dict: return value +def _parse_ytdl_options_presets(post: dict) -> list[str]: + """Normalize preset names from add/subscribe body; supports list or legacy singular string.""" + raw = post.get('ytdl_options_presets') + if raw is None: + raw = post.get('ytdl_options_preset') + if raw is None: + return [] + if isinstance(raw, list): + return [str(x).strip() for x in raw if str(x).strip()] + if isinstance(raw, str): + s = raw.strip() + return [s] if s else [] + raise web.HTTPBadRequest( + reason='ytdl_options_presets must be a JSON array of strings (or legacy ytdl_options_preset string)', + ) + + def _migrate_legacy_request(post: dict) -> dict: """ BACKWARD COMPATIBILITY: Translate old API request schema into the new one. @@ -441,7 +458,6 @@ def parse_download_options(post: dict) -> dict: chapter_template = post.get('chapter_template') subtitle_language = post.get('subtitle_language') subtitle_mode = post.get('subtitle_mode') - ytdl_options_preset = post.get('ytdl_options_preset') ytdl_options_overrides = post.get('ytdl_options_overrides') if custom_name_prefix is None: @@ -460,15 +476,13 @@ def parse_download_options(post: dict) -> dict: subtitle_language = 'en' if subtitle_mode is None: subtitle_mode = 'prefer_manual' - if ytdl_options_preset is None: - ytdl_options_preset = '' download_type = str(download_type).strip().lower() codec = str(codec or 'auto').strip().lower() format = str(format or '').strip().lower() quality = str(quality).strip().lower() subtitle_language = str(subtitle_language).strip() subtitle_mode = str(subtitle_mode).strip() - ytdl_options_preset = str(ytdl_options_preset).strip() + ytdl_options_presets = _parse_ytdl_options_presets(post) ytdl_options_overrides = _parse_ytdl_options_overrides( ytdl_options_overrides, enabled=config.ALLOW_YTDL_OPTIONS_OVERRIDES, @@ -480,8 +494,9 @@ def parse_download_options(post: dict) -> dict: raise web.HTTPBadRequest(reason='subtitle_language must match pattern [A-Za-z0-9-] and be at most 35 characters') if subtitle_mode not in VALID_SUBTITLE_MODES: raise web.HTTPBadRequest(reason=f'subtitle_mode must be one of {sorted(VALID_SUBTITLE_MODES)}') - if ytdl_options_preset and ytdl_options_preset not in config.YTDL_OPTIONS_PRESETS: - raise web.HTTPBadRequest(reason='ytdl_options_preset must match a configured preset') + for preset_name in ytdl_options_presets: + if preset_name not in config.YTDL_OPTIONS_PRESETS: + raise web.HTTPBadRequest(reason='ytdl_options_presets must only contain configured preset names') if download_type not in VALID_DOWNLOAD_TYPES: raise web.HTTPBadRequest(reason=f'download_type must be one of {sorted(VALID_DOWNLOAD_TYPES)}') @@ -534,7 +549,7 @@ def parse_download_options(post: dict) -> dict: 'chapter_template': chapter_template, 'subtitle_language': subtitle_language, 'subtitle_mode': subtitle_mode, - 'ytdl_options_preset': ytdl_options_preset, + 'ytdl_options_presets': ytdl_options_presets, 'ytdl_options_overrides': ytdl_options_overrides, } @@ -570,7 +585,7 @@ async def add(request): o['chapter_template'], o['subtitle_language'], o['subtitle_mode'], - o['ytdl_options_preset'], + o['ytdl_options_presets'], o['ytdl_options_overrides'], ) return web.Response(text=serializer.encode(status)) @@ -621,7 +636,7 @@ async def subscribe(request): chapter_template=o['chapter_template'], subtitle_language=o['subtitle_language'], subtitle_mode=o['subtitle_mode'], - ytdl_options_preset=o['ytdl_options_preset'], + ytdl_options_presets=o['ytdl_options_presets'], ytdl_options_overrides=o['ytdl_options_overrides'], ) return web.Response(text=serializer.encode(result)) diff --git a/app/subscriptions.py b/app/subscriptions.py index 94a1d3b..d2b3521 100644 --- a/app/subscriptions.py +++ b/app/subscriptions.py @@ -145,7 +145,7 @@ class SubscriptionInfo: chapter_template: str = "" subtitle_language: str = "en" subtitle_mode: str = "prefer_manual" - ytdl_options_preset: str = "" + ytdl_options_presets: list[str] = field(default_factory=list) ytdl_options_overrides: dict[str, Any] = field(default_factory=dict) last_checked: Optional[float] = None seen_ids: list[str] = field(default_factory=list) @@ -192,7 +192,7 @@ def _subscription_to_record(sub: SubscriptionInfo) -> dict[str, Any]: "chapter_template": sub.chapter_template, "subtitle_language": sub.subtitle_language, "subtitle_mode": sub.subtitle_mode, - "ytdl_options_preset": sub.ytdl_options_preset, + "ytdl_options_presets": list(sub.ytdl_options_presets), "ytdl_options_overrides": sub.ytdl_options_overrides, "last_checked": sub.last_checked, "seen_ids": list(sub.seen_ids), @@ -200,13 +200,32 @@ def _subscription_to_record(sub: SubscriptionInfo) -> dict[str, Any]: } +def _normalize_subscription_record(rec: dict[str, Any]) -> dict[str, Any]: + """Migrate legacy ytdl_options_preset (str) to ytdl_options_presets (list).""" + out = dict(rec) + if "ytdl_options_presets" not in out: + old = out.pop("ytdl_options_preset", None) + if old is None: + out["ytdl_options_presets"] = [] + elif isinstance(old, list): + out["ytdl_options_presets"] = [str(x).strip() for x in old if str(x).strip()] + elif isinstance(old, str): + out["ytdl_options_presets"] = [old.strip()] if old.strip() else [] + else: + out["ytdl_options_presets"] = [] + else: + out.pop("ytdl_options_preset", None) + return out + + def _subscription_from_record(record: Any) -> Optional[SubscriptionInfo]: field_names = {f.name for f in fields(SubscriptionInfo)} if isinstance(record, SubscriptionInfo): return record if isinstance(record, dict): try: - return SubscriptionInfo(**{k: v for k, v in record.items() if k in field_names}) + normalized = _normalize_subscription_record(dict(record)) + return SubscriptionInfo(**{k: v for k, v in normalized.items() if k in field_names}) except TypeError: return None return None @@ -315,11 +334,12 @@ class SubscriptionManager: chapter_template: str, subtitle_language: str, subtitle_mode: str, - ytdl_options_preset: str = "", + ytdl_options_presets: Optional[list[str]] = None, ytdl_options_overrides: Optional[dict[str, Any]] = None, ) -> tuple[list[str], list[str]]: queued_ids: list[str] = [] queue_errors: list[str] = [] + presets = list(ytdl_options_presets or []) for ent in entries: eid = _entry_id(ent) vurl = _entry_video_url(ent) @@ -342,7 +362,7 @@ class SubscriptionManager: chapter_template or None, subtitle_language, subtitle_mode, - ytdl_options_preset, + presets, ytdl_options_overrides, ) if isinstance(result, dict) and result.get("status") == "error": @@ -411,7 +431,7 @@ class SubscriptionManager: chapter_template: str, subtitle_language: str, subtitle_mode: str, - ytdl_options_preset: str = "", + ytdl_options_presets: Optional[list[str]] = None, ytdl_options_overrides: Optional[dict[str, Any]] = None, ) -> dict: url = self._normalize_url(url) @@ -470,7 +490,7 @@ class SubscriptionManager: chapter_template=chapter_template or "", subtitle_language=subtitle_language, subtitle_mode=subtitle_mode, - ytdl_options_preset=ytdl_options_preset, + ytdl_options_presets=list(ytdl_options_presets or []), ytdl_options_overrides=dict(ytdl_options_overrides or {}), last_checked=time.time(), seen_ids=list(dict.fromkeys(all_ids)), @@ -620,7 +640,7 @@ class SubscriptionManager: dl_chapter = cur.chapter_template dl_sublang = cur.subtitle_language dl_submode = cur.subtitle_mode - dl_ytdl_preset = cur.ytdl_options_preset + dl_ytdl_presets = list(cur.ytdl_options_presets) dl_ytdl_overrides = dict(cur.ytdl_options_overrides) new_entries: list[dict] = [] @@ -646,7 +666,7 @@ class SubscriptionManager: chapter_template=dl_chapter or "", subtitle_language=dl_sublang, subtitle_mode=dl_submode, - ytdl_options_preset=dl_ytdl_preset, + ytdl_options_presets=dl_ytdl_presets, ytdl_options_overrides=dl_ytdl_overrides, ) log.info( diff --git a/app/tests/test_api.py b/app/tests/test_api.py index b8d1f2d..e7fb8ae 100644 --- a/app/tests/test_api.py +++ b/app/tests/test_api.py @@ -37,7 +37,7 @@ def _valid_video_add_body(**kwargs): "codec": "auto", "format": "any", "quality": "best", - "ytdl_options_preset": "", + "ytdl_options_presets": [], "ytdl_options_overrides": "", } base.update(kwargs) @@ -67,7 +67,7 @@ async def test_add_passes_preset_and_overrides(mock_dqueue, monkeypatch): monkeypatch.setattr(main.config, "ALLOW_YTDL_OPTIONS_OVERRIDES", True) req = _json_request( _valid_video_add_body( - ytdl_options_preset="Preset A", + ytdl_options_presets=["Preset A"], ytdl_options_overrides='{"writesubtitles": true}', ) ) @@ -75,10 +75,23 @@ async def test_add_passes_preset_and_overrides(mock_dqueue, monkeypatch): assert resp.status == 200 call = mock_dqueue.add.await_args assert call is not None - assert call.args[13] == "Preset A" + assert call.args[13] == ["Preset A"] assert call.args[14] == {"writesubtitles": True} +@pytest.mark.asyncio +async def test_add_legacy_string_preset_normalized(mock_dqueue, monkeypatch): + monkeypatch.setattr(main.config, "YTDL_OPTIONS_PRESETS", {"Legacy": {}}) + body = _valid_video_add_body() + del body["ytdl_options_presets"] + body["ytdl_options_preset"] = "Legacy" + req = _json_request(body) + resp = await main.add(req) + assert resp.status == 200 + call = mock_dqueue.add.await_args + assert call.args[13] == ["Legacy"] + + @pytest.mark.asyncio async def test_add_missing_url_returns_400(mock_dqueue): req = _json_request({"download_type": "video", "quality": "best", "format": "any"}) @@ -171,7 +184,7 @@ async def test_add_allows_any_ytdl_options_override_key_when_enabled(mock_dqueue @pytest.mark.asyncio async def test_add_unknown_ytdl_preset(mock_dqueue): - req = _json_request(_valid_video_add_body(ytdl_options_preset="Missing")) + req = _json_request(_valid_video_add_body(ytdl_options_presets=["Missing"])) with pytest.raises(web.HTTPBadRequest): await main.add(req) diff --git a/app/tests/test_download_queue.py b/app/tests/test_download_queue.py index 5397c15..b4c7da2 100644 --- a/app/tests/test_download_queue.py +++ b/app/tests/test_download_queue.py @@ -182,7 +182,10 @@ async def test_add_entry_queues_single_video_without_reextracting(dq_env): async def test_add_merges_global_preset_and_override_options(dq_env): notifier = AsyncMock() dq_env.YTDL_OPTIONS = {"writesubtitles": False, "cookiefile": "/tmp/global.txt"} - dq_env.YTDL_OPTIONS_PRESETS = {"Preset A": {"writesubtitles": True, "proxy": "http://preset"}} + dq_env.YTDL_OPTIONS_PRESETS = { + "Preset A": {"writesubtitles": True, "proxy": "http://preset-a"}, + "Preset B": {"writesubtitles": False, "ratelimit": 1000}, + } def fake_extract(self, url): return { @@ -205,13 +208,14 @@ async def test_add_merges_global_preset_and_override_options(dq_env): "", 0, auto_start=False, - ytdl_options_preset="Preset A", + ytdl_options_presets=["Preset A", "Preset B"], ytdl_options_overrides={"proxy": "http://override", "embed_thumbnail": True}, ) assert result["status"] == "ok" queued = dq.pending.get("https://example.com/preset") assert queued.ytdl_opts["cookiefile"] == "/tmp/global.txt" - assert queued.ytdl_opts["writesubtitles"] is True + assert queued.ytdl_opts["writesubtitles"] is False + assert queued.ytdl_opts["ratelimit"] == 1000 assert queued.ytdl_opts["proxy"] == "http://override" assert queued.ytdl_opts["embed_thumbnail"] is True diff --git a/app/tests/test_main_helpers.py b/app/tests/test_main_helpers.py index 37dd6fd..5b9fb2c 100644 --- a/app/tests/test_main_helpers.py +++ b/app/tests/test_main_helpers.py @@ -140,9 +140,44 @@ class ParseDownloadOptionsTests(unittest.TestCase): finally: main.config.YTDL_OPTIONS_PRESETS = previous main.config.ALLOW_YTDL_OPTIONS_OVERRIDES = previous_allow - self.assertEqual(parsed["ytdl_options_preset"], "With subtitles") + self.assertEqual(parsed["ytdl_options_presets"], ["With subtitles"]) self.assertEqual(parsed["ytdl_options_overrides"], {"writesubtitles": True}) + def test_accepts_multiple_presets_in_order(self): + previous = dict(main.config.YTDL_OPTIONS_PRESETS) + main.config.YTDL_OPTIONS_PRESETS = { + "A": {"writesubtitles": True}, + "B": {"writesubtitles": False}, + } + try: + parsed = main.parse_download_options({ + "url": "https://example.com/v", + "download_type": "video", + "codec": "auto", + "format": "any", + "quality": "best", + "ytdl_options_presets": ["A", "B"], + }) + finally: + main.config.YTDL_OPTIONS_PRESETS = previous + self.assertEqual(parsed["ytdl_options_presets"], ["A", "B"]) + + def test_legacy_singular_preset_string_normalized_to_list(self): + previous = dict(main.config.YTDL_OPTIONS_PRESETS) + main.config.YTDL_OPTIONS_PRESETS = {"Solo": {}} + try: + parsed = main.parse_download_options({ + "url": "https://example.com/v", + "download_type": "video", + "codec": "auto", + "format": "any", + "quality": "best", + "ytdl_options_preset": "Solo", + }) + finally: + main.config.YTDL_OPTIONS_PRESETS = previous + self.assertEqual(parsed["ytdl_options_presets"], ["Solo"]) + def test_rejects_unknown_preset(self): with self.assertRaises(main.web.HTTPBadRequest): main.parse_download_options({ @@ -151,9 +186,25 @@ class ParseDownloadOptionsTests(unittest.TestCase): "codec": "auto", "format": "any", "quality": "best", - "ytdl_options_preset": "Missing preset", + "ytdl_options_presets": ["Missing preset"], }) + def test_rejects_unknown_preset_in_list(self): + previous = dict(main.config.YTDL_OPTIONS_PRESETS) + main.config.YTDL_OPTIONS_PRESETS = {"Known": {}} + try: + with self.assertRaises(main.web.HTTPBadRequest): + main.parse_download_options({ + "url": "https://example.com/v", + "download_type": "video", + "codec": "auto", + "format": "any", + "quality": "best", + "ytdl_options_presets": ["Known", "Nope"], + }) + finally: + main.config.YTDL_OPTIONS_PRESETS = previous + if __name__ == "__main__": unittest.main() diff --git a/app/ytdl.py b/app/ytdl.py index 61d514e..21d44f9 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -188,7 +188,7 @@ class DownloadInfo: chapter_template, subtitle_language="en", subtitle_mode="prefer_manual", - ytdl_options_preset="", + ytdl_options_presets=None, ytdl_options_overrides=None, ): self.id = id if len(custom_name_prefix) == 0 else f'{custom_name_prefix}.{id}' @@ -212,7 +212,7 @@ class DownloadInfo: self.chapter_template = chapter_template self.subtitle_language = subtitle_language self.subtitle_mode = subtitle_mode - self.ytdl_options_preset = ytdl_options_preset + self.ytdl_options_presets = list(ytdl_options_presets or []) self.ytdl_options_overrides = dict(ytdl_options_overrides or {}) self.subtitle_files = [] @@ -266,8 +266,14 @@ class DownloadInfo: self.subtitle_language = "en" if not hasattr(self, "subtitle_mode"): self.subtitle_mode = "prefer_manual" - if not hasattr(self, "ytdl_options_preset"): - self.ytdl_options_preset = "" + legacy_preset = self.__dict__.pop("ytdl_options_preset", None) + if "ytdl_options_presets" not in self.__dict__: + if isinstance(legacy_preset, str) and legacy_preset.strip(): + self.ytdl_options_presets = [legacy_preset.strip()] + elif isinstance(legacy_preset, list): + self.ytdl_options_presets = [str(x).strip() for x in legacy_preset if str(x).strip()] + else: + self.ytdl_options_presets = [] if not hasattr(self, "ytdl_options_overrides"): self.ytdl_options_overrides = {} if not hasattr(self, "entry"): @@ -293,7 +299,7 @@ _PERSISTED_DOWNLOAD_FIELDS = ( "chapter_template", "subtitle_language", "subtitle_mode", - "ytdl_options_preset", + "ytdl_options_presets", "ytdl_options_overrides", "status", "timestamp", @@ -838,8 +844,7 @@ class DownloadQueue: sanitized = {k: _sanitize_path_component(v) for k, v in entry.items()} output = _resolve_outtmpl_fields(output, sanitized, ('channel',)) ytdl_options = dict(self.config.YTDL_OPTIONS) - preset_name = getattr(dl, 'ytdl_options_preset', '') - if preset_name: + for preset_name in getattr(dl, 'ytdl_options_presets', None) or []: ytdl_options.update(self.config.YTDL_OPTIONS_PRESETS.get(preset_name, {})) ytdl_options.update(getattr(dl, 'ytdl_options_overrides', {}) or {}) playlist_item_limit = getattr(dl, 'playlist_item_limit', 0) @@ -869,7 +874,7 @@ class DownloadQueue: chapter_template, subtitle_language, subtitle_mode, - ytdl_options_preset, + ytdl_options_presets, ytdl_options_overrides, already, _add_gen=None, @@ -903,7 +908,7 @@ class DownloadQueue: chapter_template, subtitle_language, subtitle_mode, - ytdl_options_preset, + ytdl_options_presets, ytdl_options_overrides, already, _add_gen, @@ -952,7 +957,7 @@ class DownloadQueue: chapter_template, subtitle_language, subtitle_mode, - ytdl_options_preset, + ytdl_options_presets, ytdl_options_overrides, already, _add_gen, @@ -985,7 +990,7 @@ class DownloadQueue: chapter_template=chapter_template, subtitle_language=subtitle_language, subtitle_mode=subtitle_mode, - ytdl_options_preset=ytdl_options_preset, + ytdl_options_presets=ytdl_options_presets, ytdl_options_overrides=ytdl_options_overrides, ) await self.__add_download(dl, auto_start) @@ -1007,15 +1012,17 @@ class DownloadQueue: chapter_template=None, subtitle_language="en", subtitle_mode="prefer_manual", - ytdl_options_preset="", + ytdl_options_presets=None, ytdl_options_overrides=None, already=None, _add_gen=None, ): + if ytdl_options_presets is None: + ytdl_options_presets = [] log.info( f'adding {url}: {download_type=} {codec=} {format=} {quality=} {already=} {folder=} {custom_name_prefix=} ' f'{playlist_item_limit=} {auto_start=} {split_by_chapters=} {chapter_template=} ' - f'{subtitle_language=} {subtitle_mode=} {ytdl_options_preset=}' + f'{subtitle_language=} {subtitle_mode=} {ytdl_options_presets=}' ) if already is None: _add_gen = self._add_generation @@ -1044,7 +1051,7 @@ class DownloadQueue: chapter_template, subtitle_language, subtitle_mode, - ytdl_options_preset, + ytdl_options_presets, ytdl_options_overrides, already, _add_gen, @@ -1065,9 +1072,11 @@ class DownloadQueue: chapter_template=None, subtitle_language="en", subtitle_mode="prefer_manual", - ytdl_options_preset="", + ytdl_options_presets=None, ytdl_options_overrides=None, ): + if ytdl_options_presets is None: + ytdl_options_presets = [] normalized_entry = copy.deepcopy(entry) if isinstance(entry, dict) else entry already = set() return await self.__add_entry( @@ -1084,7 +1093,7 @@ class DownloadQueue: chapter_template, subtitle_language, subtitle_mode, - ytdl_options_preset, + ytdl_options_presets, ytdl_options_overrides, already, None, diff --git a/ui/src/app/app.html b/ui/src/app/app.html index 14a3a1b..9ec0472 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -482,18 +482,18 @@
- Option Preset - + ngbTooltip="Choose one or more yt-dlp option presets configured on the server (applied in order)" />
@if (allowYtdlOptionsOverrides()) { diff --git a/ui/src/app/app.spec.ts b/ui/src/app/app.spec.ts index c9700e6..3b83aeb 100644 --- a/ui/src/app/app.spec.ts +++ b/ui/src/app/app.spec.ts @@ -140,10 +140,10 @@ describe('App', () => { const root = fixture.nativeElement as HTMLElement; expect(root.querySelector('input[name="ytdlOptionsOverrides"]')).toBeNull(); - const presetWrapper = root.querySelector('select[name="ytdlOptionsPreset"]')?.closest('.col-12'); + const presetWrapper = root.querySelector('ng-select[name="ytdlOptionsPresets"]')?.closest('.col-12'); expect(presetWrapper?.classList.contains('col-md-6')).toBe(false); - const presetRow = root.querySelector('select[name="ytdlOptionsPreset"]')?.closest('.row'); + const presetRow = root.querySelector('ng-select[name="ytdlOptionsPresets"]')?.closest('.row'); expect(presetRow?.querySelector('input[name="checkIntervalMinutes"]')).toBeNull(); }); @@ -157,10 +157,10 @@ describe('App', () => { const root = fixture.nativeElement as HTMLElement; expect(root.querySelector('input[name="ytdlOptionsOverrides"]')).not.toBeNull(); - const presetWrapper = root.querySelector('select[name="ytdlOptionsPreset"]')?.closest('.col-12'); + const presetWrapper = root.querySelector('ng-select[name="ytdlOptionsPresets"]')?.closest('.col-12'); expect(presetWrapper?.classList.contains('col-md-6')).toBe(true); - const presetRow = root.querySelector('select[name="ytdlOptionsPreset"]')?.closest('.row'); + const presetRow = root.querySelector('ng-select[name="ytdlOptionsPresets"]')?.closest('.row'); expect(presetRow?.querySelector('input[name="checkIntervalMinutes"]')).toBeNull(); expect(presetRow?.querySelector('input[name="ytdlOptionsOverrides"]')).not.toBeNull(); }); diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index ed93912..d2a5110 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -83,7 +83,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { chapterTemplate: string; subtitleLanguage: string; subtitleMode: string; - ytdlOptionsPreset: string; + ytdlOptionsPresets: string[] = []; ytdlOptionsOverrides: string; ytdlOptionPresetNames: string[] = []; addInProgress = false; @@ -234,7 +234,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.chapterTemplate = this.cookieService.get('metube_chapter_template') || ''; this.subtitleLanguage = this.cookieService.get('metube_subtitle_language') || 'en'; this.subtitleMode = this.cookieService.get('metube_subtitle_mode') || 'prefer_manual'; - this.ytdlOptionsPreset = this.cookieService.get('metube_ytdl_options_preset') || ''; + this.ytdlOptionsPresets = this.loadYtdlOptionsPresetsFromCookie(); this.ytdlOptionsOverrides = this.cookieService.get('metube_ytdl_options_overrides') || ''; const allowedDownloadTypes = new Set(this.downloadTypes.map(t => t.id)); const allowedVideoCodecs = new Set(this.videoCodecs.map(c => c.id)); @@ -431,15 +431,35 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.ytdlOptionPresetNames = Array.isArray(data?.presets) ? data.presets.filter((preset): preset is string => typeof preset === 'string') : []; - if (this.ytdlOptionsPreset && !this.ytdlOptionPresetNames.includes(this.ytdlOptionsPreset)) { - this.ytdlOptionsPreset = ''; - this.ytdlOptionsPresetChanged(); + if (this.ytdlOptionsPresets?.length) { + const valid = new Set(this.ytdlOptionPresetNames); + const filtered = this.ytdlOptionsPresets.filter((p) => valid.has(p)); + if (filtered.length !== this.ytdlOptionsPresets.length) { + this.ytdlOptionsPresets = filtered; + this.ytdlOptionsPresetsChanged(); + } } this.cdr.markForCheck(); }, }); } + private loadYtdlOptionsPresetsFromCookie(): string[] { + const jsonCookie = this.cookieService.get('metube_ytdl_options_presets'); + if (jsonCookie) { + try { + const parsed = JSON.parse(jsonCookie) as unknown; + if (Array.isArray(parsed)) { + return parsed.filter((p): p is string => typeof p === 'string' && p.length > 0); + } + } catch { + // fall through to legacy cookie + } + } + const legacy = this.cookieService.get('metube_ytdl_options_preset')?.trim(); + return legacy ? [legacy] : []; + } + private validateYtdlOptionsOverrides(value: string): boolean { if (!this.allowYtdlOptionsOverrides()) { return true; @@ -744,8 +764,12 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.saveSelection(this.downloadType); } - ytdlOptionsPresetChanged() { - this.cookieService.set('metube_ytdl_options_preset', this.ytdlOptionsPreset, { expires: this.settingsCookieExpiryDays }); + ytdlOptionsPresetsChanged() { + this.cookieService.set( + 'metube_ytdl_options_presets', + JSON.stringify(this.ytdlOptionsPresets ?? []), + { expires: this.settingsCookieExpiryDays }, + ); } ytdlOptionsOverridesChanged() { @@ -952,7 +976,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { chapterTemplate: overrides.chapterTemplate ?? this.chapterTemplate, subtitleLanguage: overrides.subtitleLanguage ?? this.subtitleLanguage, subtitleMode: overrides.subtitleMode ?? this.subtitleMode, - ytdlOptionsPreset: overrides.ytdlOptionsPreset ?? this.ytdlOptionsPreset, + ytdlOptionsPresets: overrides.ytdlOptionsPresets ?? [...this.ytdlOptionsPresets], ytdlOptionsOverrides: allowYtdlOptionsOverrides ? (overrides.ytdlOptionsOverrides ?? this.ytdlOptionsOverrides) : '', @@ -1025,7 +1049,9 @@ export class App implements AfterViewInit, OnInit, OnDestroy { chapterTemplate: download.chapter_template, subtitleLanguage: download.subtitle_language, subtitleMode: download.subtitle_mode, - ytdlOptionsPreset: download.ytdl_options_preset || '', + ytdlOptionsPresets: download.ytdl_options_presets?.length + ? [...download.ytdl_options_presets] + : [], ytdlOptionsOverrides: download.ytdl_options_overrides ? JSON.stringify(download.ytdl_options_overrides) : '', }); this.downloads.delById('done', [key]).subscribe(); diff --git a/ui/src/app/interfaces/download.ts b/ui/src/app/interfaces/download.ts index d06af7f..5705a81 100644 --- a/ui/src/app/interfaces/download.ts +++ b/ui/src/app/interfaces/download.ts @@ -14,7 +14,7 @@ export interface Download { chapter_template?: string; subtitle_language?: string; subtitle_mode?: string; - ytdl_options_preset?: string; + ytdl_options_presets?: string[]; ytdl_options_overrides?: Record; status: string; msg: string; diff --git a/ui/src/app/services/downloads.service.spec.ts b/ui/src/app/services/downloads.service.spec.ts index 5d4723c..f07b768 100644 --- a/ui/src/app/services/downloads.service.spec.ts +++ b/ui/src/app/services/downloads.service.spec.ts @@ -39,7 +39,7 @@ function basePayload(): AddDownloadPayload { chapterTemplate: '', subtitleLanguage: 'en', subtitleMode: 'prefer_manual', - ytdlOptionsPreset: '', + ytdlOptionsPresets: [], ytdlOptionsOverrides: '', }; } @@ -81,7 +81,7 @@ describe('DownloadsService', () => { chapter_template: '', subtitle_language: 'en', subtitle_mode: 'prefer_manual', - ytdl_options_preset: '', + ytdl_options_presets: [], ytdl_options_overrides: '', }), ); diff --git a/ui/src/app/services/downloads.service.ts b/ui/src/app/services/downloads.service.ts index 66792f7..5a0f1e8 100644 --- a/ui/src/app/services/downloads.service.ts +++ b/ui/src/app/services/downloads.service.ts @@ -20,7 +20,7 @@ export interface AddDownloadPayload { chapterTemplate: string; subtitleLanguage: string; subtitleMode: string; - ytdlOptionsPreset: string; + ytdlOptionsPresets: string[]; ytdlOptionsOverrides: string; } @Injectable({ @@ -143,7 +143,7 @@ export class DownloadsService { chapter_template: payload.chapterTemplate, subtitle_language: payload.subtitleLanguage, subtitle_mode: payload.subtitleMode, - ytdl_options_preset: payload.ytdlOptionsPreset, + ytdl_options_presets: payload.ytdlOptionsPresets, ytdl_options_overrides: payload.ytdlOptionsOverrides, }).pipe( catchError(this.handleHTTPError) diff --git a/ui/src/app/services/subscriptions.service.ts b/ui/src/app/services/subscriptions.service.ts index 57fbcf6..2a95fb2 100644 --- a/ui/src/app/services/subscriptions.service.ts +++ b/ui/src/app/services/subscriptions.service.ts @@ -94,7 +94,7 @@ export class SubscriptionsService { chapter_template: payload.chapterTemplate, subtitle_language: payload.subtitleLanguage, subtitle_mode: payload.subtitleMode, - ytdl_options_preset: payload.ytdlOptionsPreset, + ytdl_options_presets: payload.ytdlOptionsPresets, ytdl_options_overrides: payload.ytdlOptionsOverrides, check_interval_minutes: payload.checkIntervalMinutes, }) From 54680c405c7e13b803c9ab8575342e8c1a9956ea Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Sat, 4 Apr 2026 12:54:59 +0300 Subject: [PATCH 14/88] explain yt-dlp configuration in detail --- README.md | 155 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 119 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 87d897f..3d40314 100644 --- a/README.md +++ b/README.md @@ -66,31 +66,11 @@ Certain values can be set via environment variables, using the `-e` parameter on * __OUTPUT_TEMPLATE_CHAPTER__: The template for the filenames of the downloaded videos when split into chapters via postprocessors. Defaults to `%(title)s - %(section_number)s %(section_title)s.%(ext)s`. * __OUTPUT_TEMPLATE_PLAYLIST__: The template for the filenames of the downloaded videos when downloaded as a playlist. Defaults to `%(playlist_title)s/%(title)s.%(ext)s`. Set to empty to use `OUTPUT_TEMPLATE` instead. * __OUTPUT_TEMPLATE_CHANNEL__: The template for the filenames of the downloaded videos when downloaded as a channel. Defaults to `%(channel)s/%(title)s.%(ext)s`. Set to empty to use `OUTPUT_TEMPLATE` instead. -* __YTDL_OPTIONS__: Additional options to pass to yt-dlp in JSON format. [See available options here](https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/YoutubeDL.py#L222). They roughly correspond to command-line options, though some do not have exact equivalents here. For example, `--recode-video` has to be specified via `postprocessors`. Also note that dashes are replaced with underscores. You may find [this script](https://github.com/yt-dlp/yt-dlp/blob/master/devscripts/cli_to_api.py) helpful for converting from command-line options to `YTDL_OPTIONS`. -* __YTDL_OPTIONS_FILE__: A path to a JSON file that will be loaded and used for populating `YTDL_OPTIONS` above. Please note that if both `YTDL_OPTIONS_FILE` and `YTDL_OPTIONS` are specified, the options in `YTDL_OPTIONS` take precedence. The file will be monitored for changes and reloaded automatically when changes are detected. -* __YTDL_OPTIONS_PRESETS__: Lets you define **named presets**β€”each preset is a bundle of extra download settings. In the web UI (Advanced Options), you can pick **one or more** presets for a download instead of changing the server-wide defaults. If several presets are selected, they are applied **in order**; if two presets touch the same setting, the **later** one wins. If you turn on the optional β€œcustom yt-dlp options” field, those choices override presets. Define each preset the same way as global `YTDL_OPTIONS` (see above). Example: - ```json - { - "sponsorblock": { - "postprocessors": [ - { "key": "SponsorBlock", "categories": ["sponsor", "selfpromo", "interaction"] }, - { "key": "ModifyChapters", "remove_sponsor_segments": ["sponsor", "selfpromo", "interaction"] } - ] - }, - "embed-subs": { - "writesubtitles": true, - "writeautomaticsub": true, - "subtitleslangs": ["en", "de"], - "postprocessors": [{ "key": "FFmpegEmbedSubtitle" }] - }, - "limit-rate": { - "ratelimit": 5000000 - } - } - ``` - With this file, the UI would offer **sponsorblock** (remove sponsor/self-promo/interaction segments), **embed-subs** (add English and German subtitles into the video file), and **limit-rate** (slow downloads to about 5 MB/s)β€”things the normal quality/format controls do not cover. -* __YTDL_OPTIONS_PRESETS_FILE__: A path to a JSON file containing `YTDL_OPTIONS_PRESETS`. If both are specified, values from `YTDL_OPTIONS_PRESETS_FILE` are merged into `YTDL_OPTIONS_PRESETS`. -* __ALLOW_YTDL_OPTIONS_OVERRIDES__: Whether to show the web UI field for manual per-download `ytdl_options_overrides`. Defaults to `false`. Enabling this allows arbitrary yt-dlp API options to be supplied by UI users, which may enable arbitrary command execution inside the container depending on the options used. Enable only if you understand and accept that risk. +* __YTDL_OPTIONS__: Additional options to pass to yt-dlp, as a JSON object. See [Configuring yt-dlp options](#%EF%B8%8F-configuring-yt-dlp-options) for details, examples, and available options reference. +* __YTDL_OPTIONS_FILE__: Path to a JSON file containing yt-dlp options. Monitored and reloaded automatically on changes. See [Configuring yt-dlp options](#%EF%B8%8F-configuring-yt-dlp-options). +* __YTDL_OPTIONS_PRESETS__: Named bundles of yt-dlp options, selectable per download in the UI. See [Configuring yt-dlp options](#%EF%B8%8F-configuring-yt-dlp-options) for format and examples. +* __YTDL_OPTIONS_PRESETS_FILE__: Path to a JSON file containing presets. Monitored and reloaded automatically on changes. See [Configuring yt-dlp options](#%EF%B8%8F-configuring-yt-dlp-options). +* __ALLOW_YTDL_OPTIONS_OVERRIDES__: Whether to show a free-text field in the UI for per-download yt-dlp option overrides. Defaults to `false`. See [Configuring yt-dlp options](#%EF%B8%8F-configuring-yt-dlp-options) for details and security considerations. ### 🌐 Web Server & URLs @@ -113,6 +93,120 @@ Certain values can be set via environment variables, using the `-e` parameter on * __LOGLEVEL__: Log level, can be set to `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`, or `NONE`. Defaults to `INFO`. * __ENABLE_ACCESSLOG__: Whether to enable access log. Defaults to `false`. +## πŸŽ›οΈ Configuring yt-dlp options + +MeTube lets you customize how [yt-dlp](https://github.com/yt-dlp/yt-dlp) behaves at three levels, from broadest to most specific: + +1. **Global options** β€” apply to every download by default. +2. **Presets** β€” named bundles of options that users can pick per download from the UI. +3. **Per-download overrides** β€” free-form options entered in the UI for a single download. + +When a download starts, these layers are combined in order. If the same option appears in more than one layer, the more specific one wins: per-download overrides beat presets, and presets beat global options. + +### Option format + +yt-dlp options in MeTube are expressed as JSON objects. The keys are yt-dlp API option names, which roughly correspond to command-line flags with dashes replaced by underscores. For example, the command-line flag `--embed-thumbnail` becomes `"embed_thumbnail": true` in JSON. + +> **Tip:** Some command-line flags don't have a direct single-key equivalent β€” for instance, `--recode-video` must be expressed via `"postprocessors"`. A full list of available API options can be found [in the yt-dlp source](https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/YoutubeDL.py#L224), and [this conversion script](https://github.com/yt-dlp/yt-dlp/blob/master/devscripts/cli_to_api.py) can help translate command-line flags to their API equivalents. + +### Global options + +Global options form the baseline for every download. There are two ways to define them, and you can use either or both: + +**Inline via environment variable** (`YTDL_OPTIONS`) β€” pass a JSON object directly: + +```yaml +environment: + - 'YTDL_OPTIONS={"writesubtitles": true, "subtitleslangs": ["en", "de"], "updatetime": false, "embed_thumbnail": true}' +``` + +**Via a JSON file** (`YTDL_OPTIONS_FILE`) β€” mount a file into the container and point to it: + +```yaml +volumes: + - /path/to/ytdl-options.json:/config/ytdl-options.json +environment: + - YTDL_OPTIONS_FILE=/config/ytdl-options.json +``` + +where `ytdl-options.json` contains: + +```json +{ + "writesubtitles": true, + "subtitleslangs": ["en", "de"], + "updatetime": false, + "embed_thumbnail": true +} +``` + +The file is monitored for changes and reloaded automatically β€” no container restart needed. If you use both methods and they define the same key, the **file takes precedence**. + +### Presets + +Presets let you define named bundles of options that appear in the web UI under **Advanced Options** as "Option Presets". Users can select one or more presets per download, making it easy to apply common option combinations without editing global settings. + +Like global options, presets can be set inline or via a file: + +* `YTDL_OPTIONS_PRESETS` β€” a JSON object where each key is a preset name and its value is a set of yt-dlp options. +* `YTDL_OPTIONS_PRESETS_FILE` β€” path to a JSON file containing presets, monitored and reloaded on changes. + +If both are used and they define a preset with the same name, the **file's version takes precedence**. + +**Example** β€” a presets file defining three presets: + +```json +{ + "sponsorblock": { + "postprocessors": [ + { "key": "SponsorBlock", "categories": ["sponsor", "selfpromo", "interaction"] }, + { "key": "ModifyChapters", "remove_sponsor_segments": ["sponsor", "selfpromo", "interaction"] } + ] + }, + "embed-subs": { + "writesubtitles": true, + "writeautomaticsub": true, + "subtitleslangs": ["en", "de"], + "postprocessors": [{ "key": "FFmpegEmbedSubtitle" }] + }, + "limit-rate": { + "ratelimit": 5000000 + } +} +``` + +This makes three presets available in the UI: +* **sponsorblock** β€” strips sponsor, self-promo, and interaction segments from videos. +* **embed-subs** β€” downloads English and German subtitles and embeds them into the video file. +* **limit-rate** β€” caps download speed to ~5 MB/s. + +When multiple presets are selected for a download, they are applied in order. If two presets set the same option, the later one wins. + +### Per-download overrides + +For one-off tweaks, MeTube can expose a free-text JSON field in the UI ("Custom yt-dlp Options") where users type yt-dlp options that apply only to that single download. This is disabled by default: + +```yaml +environment: + - ALLOW_YTDL_OPTIONS_OVERRIDES=true +``` + +Once enabled, the field appears under **Advanced Options**. Any options entered there take the highest priority, overriding both global options and selected presets. + +> **⚠️ Security note:** Enabling this allows arbitrary yt-dlp API options to be supplied by anyone with access to the UI. Depending on the options used, this may enable arbitrary command execution inside the container. Enable only in trusted environments. + +### How the layers combine + +When a download starts, the final set of yt-dlp options is built in this order: + +1. Start with **global options** (`YTDL_OPTIONS` / `YTDL_OPTIONS_FILE`). +2. Apply each selected **preset** in order (later presets overwrite earlier ones for conflicting keys). +3. Apply any **per-download overrides** on top (overwrite everything else for conflicting keys). + +**Example:** Suppose your global options set `"writesubtitles": false`, but you select a preset that sets `"writesubtitles": true`. Subtitles will be written for that download because the preset overrides the global setting. If you additionally enter `{"writesubtitles": false}` in the per-download overrides field, that value wins and subtitles will not be written. + +### Configuration cookbooks + The project's Wiki contains examples of useful configurations contributed by users of MeTube: * [YTDL_OPTIONS Cookbook](https://github.com/alexta69/metube/wiki/YTDL_OPTIONS-Cookbook) * [OUTPUT_TEMPLATE Cookbook](https://github.com/alexta69/metube/wiki/OUTPUT_TEMPLATE-Cookbook) @@ -141,17 +235,6 @@ __Firefox:__ contributed by [nanocortex](https://github.com/nanocortex). You can [rithask](https://github.com/rithask) created an iOS shortcut to send URLs to MeTube from Safari. Enter the MeTube instance address when prompted which will be saved for later use. You can run the shortcut from Safari’s share menu. The shortcut can be downloaded from [this iCloud link](https://www.icloud.com/shortcuts/66627a9f334c467baabdb2769763a1a6). -## πŸ“± iOS Compatibility - -iOS has strict requirements for video files, requiring h264 or h265 video codec and aac audio codec in MP4 container. This can sometimes be a lower quality than the best quality available. To accommodate iOS requirements, when downloading a MP4 format you can choose "Best (iOS)" to get the best quality formats as compatible as possible with iOS requirements. - -To force all downloads to be converted to an iOS-compatible codec, insert this as an environment variable: - -```yaml - environment: - - 'YTDL_OPTIONS={"format": "best", "exec": "ffmpeg -i %(filepath)q -c:v libx264 -c:a aac %(filepath)q.h264.mp4"}' -``` - ## πŸ”– Bookmarklet [kushfest](https://github.com/kushfest) has created a Chrome bookmarklet for sending the currently open webpage to MeTube. Please note that if you're on an HTTPS page, your MeTube instance must be configured with `HTTPS` as `true` in the environment, or be behind an HTTPS reverse proxy (see below) for the bookmarklet to work. From 373692ac657eb416435463f08f527975bfc72dff Mon Sep 17 00:00:00 2001 From: jacinli Date: Sun, 5 Apr 2026 14:05:59 +0800 Subject: [PATCH 15/88] fix: parse string boolean values when updating subscriptions --- app/subscriptions.py | 15 +++++++- app/tests/test_subscriptions.py | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) diff --git a/app/subscriptions.py b/app/subscriptions.py index d2b3521..4db28c8 100644 --- a/app/subscriptions.py +++ b/app/subscriptions.py @@ -231,6 +231,19 @@ def _subscription_from_record(record: Any) -> Optional[SubscriptionInfo]: return None +def _coerce_bool(value: Any) -> bool: + """Accept JSON booleans and common string forms used by API clients.""" + if isinstance(value, bool): + return value + if isinstance(value, str): + lowered = value.strip().lower() + if lowered in {"true", "1", "on"}: + return True + if lowered in {"false", "0", "off"}: + return False + raise ValueError("enabled must be a boolean") + + class SubscriptionNotifier: """Hook for Socket.IO / UI updates.""" @@ -546,7 +559,7 @@ class SubscriptionManager: old_enabled = sub.enabled if "enabled" in changes: - sub.enabled = bool(changes["enabled"]) + sub.enabled = _coerce_bool(changes["enabled"]) if "check_interval_minutes" in changes: sub.check_interval_minutes = max(1, int(changes["check_interval_minutes"])) if "name" in changes and changes["name"]: diff --git a/app/tests/test_subscriptions.py b/app/tests/test_subscriptions.py index 9fc465b..e3f6229 100644 --- a/app/tests/test_subscriptions.py +++ b/app/tests/test_subscriptions.py @@ -386,6 +386,73 @@ class SubscriptionPersistenceTests(unittest.IsolatedAsyncioTestCase): self.assertEqual(sub.seen_ids[:2], ["v2", "v1"]) self.assertEqual([entry["webpage_url"] for entry, _, _ in queue.entries], ["https://example.com/v2"]) + async def test_update_subscription_parses_string_false_enabled(self): + with tempfile.TemporaryDirectory() as tmp: + queue = _Queue() + mgr = SubscriptionManager(_Config(tmp), queue, _Notifier()) + + with patch( + "subscriptions.extract_flat_playlist", + return_value=( + {"_type": "channel", "title": "Channel"}, + [{"id": "v1", "title": "One", "webpage_url": "https://example.com/v1"}], + ), + ): + result = await mgr.add_subscription( + "https://example.com/channel", + check_interval_minutes=60, + download_type="video", + codec="auto", + format="any", + quality="best", + folder="", + custom_name_prefix="", + auto_start=True, + playlist_item_limit=0, + split_by_chapters=False, + chapter_template="", + subtitle_language="en", + subtitle_mode="prefer_manual", + ) + + sub_id = result["subscription"]["id"] + update = await mgr.update_subscription(sub_id, {"enabled": "false"}) + self.assertEqual(update["status"], "ok") + self.assertFalse(mgr.list_all()[0].enabled) + + async def test_update_subscription_rejects_invalid_enabled_value(self): + with tempfile.TemporaryDirectory() as tmp: + queue = _Queue() + mgr = SubscriptionManager(_Config(tmp), queue, _Notifier()) + + with patch( + "subscriptions.extract_flat_playlist", + return_value=( + {"_type": "channel", "title": "Channel"}, + [{"id": "v1", "title": "One", "webpage_url": "https://example.com/v1"}], + ), + ): + result = await mgr.add_subscription( + "https://example.com/channel", + check_interval_minutes=60, + download_type="video", + codec="auto", + format="any", + quality="best", + folder="", + custom_name_prefix="", + auto_start=True, + playlist_item_limit=0, + split_by_chapters=False, + chapter_template="", + subtitle_language="en", + subtitle_mode="prefer_manual", + ) + + sub_id = result["subscription"]["id"] + with self.assertRaises(ValueError): + await mgr.update_subscription(sub_id, {"enabled": "maybe"}) + class ExtractFlatPlaylistTests(unittest.TestCase): def test_descends_one_level_when_root_entries_are_nested_collections(self): responses = iter( From b7709d3536cb7275626d996e9a55baa0c28e6183 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 Apr 2026 16:12:43 +0000 Subject: [PATCH 16/88] Bump astral-sh/setup-uv from 6 to 7 in the github-actions group Bumps the github-actions group with 1 update: [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv). Updates `astral-sh/setup-uv` from 6 to 7 - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/v6...v7) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ce31a92..9ac9d87 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,7 +32,7 @@ jobs: env: CI: true - name: Install uv - uses: astral-sh/setup-uv@v6 + uses: astral-sh/setup-uv@v7 - name: Install Python dependencies run: uv sync --frozen --group dev - name: Run backend smoke checks From d38d7bd1b10cb0a93826f7210257d9b5e94548e0 Mon Sep 17 00:00:00 2001 From: McSwindler Date: Thu, 9 Apr 2026 10:15:11 -0500 Subject: [PATCH 17/88] fix: handle playlists that don't supply video ids --- app/subscriptions.py | 2 ++ app/ytdl.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/app/subscriptions.py b/app/subscriptions.py index 4db28c8..031ede0 100644 --- a/app/subscriptions.py +++ b/app/subscriptions.py @@ -359,6 +359,8 @@ class SubscriptionManager: if not eid or not vurl: continue queue_entry = dict(ent) + if "id" not in queue_entry: + queue_entry["id"] = eid queue_entry["_type"] = "video" queue_entry["webpage_url"] = vurl result = await self.dqueue.add_entry( diff --git a/app/ytdl.py b/app/ytdl.py index 21d44f9..509ebb7 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -19,6 +19,7 @@ from yt_dlp.utils import STR_FORMAT_RE_TMPL, STR_FORMAT_TYPES from dl_formats import get_format, get_opts, AUDIO_FORMATS from datetime import datetime from state_store import AtomicJsonStore, from_json_compatible, read_legacy_shelf, to_json_compatible +from subscriptions import _entry_id log = logging.getLogger('ytdl') @@ -930,6 +931,8 @@ class DownloadQueue: if _add_gen is not None and self._add_generation != _add_gen: log.info(f'Playlist add canceled after processing {len(already)} entries') return {'status': 'ok', 'msg': f'Canceled - added {len(already)} items before cancel'} + if "id" not in etr: + etr["id"] = _entry_id(etr) etr["_type"] = "video" etr[etype] = entry.get("id") or entry.get("channel_id") or entry.get("channel") etr[f"{etype}_index"] = '{{0:0{0:d}d}}'.format(index_digits).format(index) From 0b3645aea1546f74fc6b26e7936e51863248079e Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Thu, 9 Apr 2026 21:00:26 +0300 Subject: [PATCH 18/88] upgrade dependencies --- ui/package.json | 28 +- ui/pnpm-lock.yaml | 857 +++++++++++++++++++++++----------------------- uv.lock | 12 +- 3 files changed, 456 insertions(+), 441 deletions(-) diff --git a/ui/package.json b/ui/package.json index fd820b2..383dca2 100644 --- a/ui/package.json +++ b/ui/package.json @@ -23,21 +23,21 @@ }, "private": true, "dependencies": { - "@angular/animations": "^21.2.7", - "@angular/common": "^21.2.7", - "@angular/compiler": "^21.2.7", - "@angular/core": "^21.2.7", - "@angular/forms": "^21.2.7", - "@angular/platform-browser": "^21.2.7", - "@angular/platform-browser-dynamic": "^21.2.7", - "@angular/service-worker": "^21.2.7", + "@angular/animations": "^21.2.8", + "@angular/common": "^21.2.8", + "@angular/compiler": "^21.2.8", + "@angular/core": "^21.2.8", + "@angular/forms": "^21.2.8", + "@angular/platform-browser": "^21.2.8", + "@angular/platform-browser-dynamic": "^21.2.8", + "@angular/service-worker": "^21.2.8", "@fortawesome/angular-fontawesome": "~4.0.0", "@fortawesome/fontawesome-svg-core": "^7.2.0", "@fortawesome/free-brands-svg-icons": "^7.2.0", "@fortawesome/free-regular-svg-icons": "^7.2.0", "@fortawesome/free-solid-svg-icons": "^7.2.0", "@ng-bootstrap/ng-bootstrap": "^20.0.0", - "@ng-select/ng-select": "^21.7.0", + "@ng-select/ng-select": "^21.8.0", "@popperjs/core": "^2.11.8", "bootstrap": "^5.3.8", "ngx-cookie-service": "^21.3.1", @@ -48,16 +48,16 @@ }, "devDependencies": { "@angular-eslint/builder": "21.1.0", - "@angular/build": "^21.2.6", - "@angular/cli": "^21.2.6", - "@angular/compiler-cli": "^21.2.7", - "@angular/localize": "^21.2.7", + "@angular/build": "^21.2.7", + "@angular/cli": "^21.2.7", + "@angular/compiler-cli": "^21.2.8", + "@angular/localize": "^21.2.8", "@eslint/js": "^9.39.4", "angular-eslint": "21.1.0", "eslint": "^9.39.4", "jsdom": "^27.4.0", "typescript": "~5.9.3", "typescript-eslint": "8.47.0", - "vitest": "^4.1.2" + "vitest": "^4.1.4" } } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 7c493ca..4f53e48 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -9,32 +9,32 @@ importers: .: dependencies: '@angular/animations': - specifier: ^21.2.7 - version: 21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^21.2.8 + version: 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/common': - specifier: ^21.2.7 - version: 21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^21.2.8 + version: 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@angular/compiler': - specifier: ^21.2.7 - version: 21.2.7 + specifier: ^21.2.8 + version: 21.2.8 '@angular/core': - specifier: ^21.2.7 - version: 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^21.2.8 + version: 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) '@angular/forms': - specifier: ^21.2.7 - version: 21.2.7(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + specifier: ^21.2.8 + version: 21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) '@angular/platform-browser': - specifier: ^21.2.7 - version: 21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^21.2.8 + version: 21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/platform-browser-dynamic': - specifier: ^21.2.7 - version: 21.2.7(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.7)(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))) + specifier: ^21.2.8 + version: 21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.8)(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))) '@angular/service-worker': - specifier: ^21.2.7 - version: 21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^21.2.8 + version: 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@fortawesome/angular-fontawesome': specifier: ~4.0.0 - version: 4.0.0(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)) + version: 4.0.0(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) '@fortawesome/fontawesome-svg-core': specifier: ^7.2.0 version: 7.2.0 @@ -49,10 +49,10 @@ importers: version: 7.2.0 '@ng-bootstrap/ng-bootstrap': specifier: ^20.0.0 - version: 20.0.0(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.7(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.7(@angular/compiler-cli@21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3))(@angular/compiler@21.2.7))(@popperjs/core@2.11.8)(rxjs@7.8.2) + version: 20.0.0(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8))(@popperjs/core@2.11.8)(rxjs@7.8.2) '@ng-select/ng-select': - specifier: ^21.7.0 - version: 21.7.0(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.7(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) + specifier: ^21.8.0 + version: 21.8.0(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) '@popperjs/core': specifier: ^2.11.8 version: 2.11.8 @@ -61,10 +61,10 @@ importers: version: 5.3.8(@popperjs/core@2.11.8) ngx-cookie-service: specifier: ^21.3.1 - version: 21.3.1(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)) + version: 21.3.1(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) ngx-socket-io: specifier: ~4.10.0 - version: 4.10.0(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + version: 4.10.0(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) rxjs: specifier: ~7.8.2 version: 7.8.2 @@ -77,25 +77,25 @@ importers: devDependencies: '@angular-eslint/builder': specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.6(@types/node@25.5.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular/build': - specifier: ^21.2.6 - version: 21.2.6(@angular/compiler-cli@21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3))(@angular/compiler@21.2.7)(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.7(@angular/compiler-cli@21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3))(@angular/compiler@21.2.7))(@angular/platform-browser@21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.1)(chokidar@5.0.0)(postcss@8.5.8)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.2(@types/node@25.5.1)(jsdom@27.4.0)(vite@7.3.1(@types/node@25.5.1)(sass@1.97.3))) + specifier: ^21.2.7 + version: 21.2.7(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8)(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(chokidar@5.0.0)(postcss@8.5.9)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.4(@types/node@25.5.2)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3))) '@angular/cli': - specifier: ^21.2.6 - version: 21.2.6(@types/node@25.5.1)(chokidar@5.0.0) + specifier: ^21.2.7 + version: 21.2.7(@types/node@25.5.2)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^21.2.7 - version: 21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3) + specifier: ^21.2.8 + version: 21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3) '@angular/localize': - specifier: ^21.2.7 - version: 21.2.7(@angular/compiler-cli@21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3))(@angular/compiler@21.2.7) + specifier: ^21.2.8 + version: 21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8) '@eslint/js': specifier: ^9.39.4 version: 9.39.4 angular-eslint: specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.6(@types/node@25.5.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) eslint: specifier: ^9.39.4 version: 9.39.4 @@ -109,8 +109,8 @@ importers: specifier: 8.47.0 version: 8.47.0(eslint@9.39.4)(typescript@5.9.3) vitest: - specifier: ^4.1.2 - version: 4.1.2(@types/node@25.5.1)(jsdom@27.4.0)(vite@7.3.1(@types/node@25.5.1)(sass@1.97.3)) + specifier: ^4.1.4 + version: 4.1.4(@types/node@25.5.2)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3)) packages: @@ -177,13 +177,13 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.2102.6': - resolution: {integrity: sha512-h4qybKypR7OuwcTHPQI1zRm7abXgmPiV49vI2UeMtVVY/GKzru9gMexcYmWabzEyBY8w6VSfWjV2X+eit2EhDQ==} + '@angular-devkit/architect@0.2102.7': + resolution: {integrity: sha512-4K/5hln9iaPEt3F/NyYqncNLvYpzSjRslEkHl2xIgZwQsIFHEvhnDRBYj2/oatURQhBqO/Yu15z/icVOYLxuTg==} 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'} hasBin: true - '@angular-devkit/core@21.2.6': - resolution: {integrity: sha512-u5gPTAY7MC02uACQE39xxiFcm1hslF+ih/f2borMWnhER0JNTpHjLiLRXFkq7or7+VVHU30zfhK4XNAuO4WTIg==} + '@angular-devkit/core@21.2.7': + resolution: {integrity: sha512-DONYY5u4IENO2qpd23mODaE4JI2EIohWV1kuJnsU9HIcm5wN714QB2z9WY/s4gLfUiAMIUu/8lpnW/0kOQZAnQ==} 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: ^5.0.0 @@ -191,8 +191,8 @@ packages: chokidar: optional: true - '@angular-devkit/schematics@21.2.6': - resolution: {integrity: sha512-hk2duJlPJyiMaI9MVWA5XpmlpD9C4n8qgquV/MJ7/n+ZRSwW3w1ndL5qUmA1ki+4Da54v/Rc8Wt5tUS955+93w==} + '@angular-devkit/schematics@21.2.7': + resolution: {integrity: sha512-LYAjjUI1qM7pR/sd0yYt8OLA6ljOOXjcfzV40I5XQNmhAxq90YYS5xwMcixOmWX+z5zvCYGvPXvJGWjzio6SUg==} 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'} '@angular-eslint/builder@21.1.0': @@ -239,14 +239,14 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '*' - '@angular/animations@21.2.7': - resolution: {integrity: sha512-h8tUjQVSWfi2fohzxXeDDTjCfWABioYlPMrV1j98wCcFJad3FSnKCY0/gq8B4X6V81NGV29nEnhPyV0GinUBpQ==} + '@angular/animations@21.2.8': + resolution: {integrity: sha512-RIqfVmfretQ0x/mXgMXe7Bw0Tpe8+zBV/Mm2OaNVyrmNG+9gYItEn5t/ZnQGcPD5nMNqckgp3+4/ZMc/qkS5ww==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.2.7 + '@angular/core': 21.2.8 - '@angular/build@21.2.6': - resolution: {integrity: sha512-PJltYl9/INfz8nZ/KHf39nqlmt3c9PR0jJaZt6hhCPENyAf4PwQpm28erkJmbOYO864goIuws41lduYXyDqQ0Q==} + '@angular/build@21.2.7': + resolution: {integrity: sha512-FpSkFqpsJtdN1cROekVYkmeV1QepdP+/d7fyYQEuNmlOlyqXSDh9qJmy4iL9VNbAU0rk+vFCtYM86rO7Pt9cSw==} 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: '@angular/compiler': ^21.0.0 @@ -256,7 +256,7 @@ packages: '@angular/platform-browser': ^21.0.0 '@angular/platform-server': ^21.0.0 '@angular/service-worker': ^21.0.0 - '@angular/ssr': ^21.2.6 + '@angular/ssr': ^21.2.7 karma: ^6.4.0 less: ^4.2.0 ng-packagr: ^21.0.0 @@ -291,38 +291,38 @@ packages: vitest: optional: true - '@angular/cli@21.2.6': - resolution: {integrity: sha512-I5DOFcIT1HKymyy2f78fjgD0Iv6jG46GbBZ/VxejcnhjubFpuN4CwPdugXf9rIDs8KZQqBzDBFUbq11vnk8h0A==} + '@angular/cli@21.2.7': + resolution: {integrity: sha512-N/wj8fFRB718efIFYpwnYfy+MecZREZXsUNMTVndFLH6T0jCheb9PVetR6jsyZp6h46USNPOmJYJ/9255lME+Q==} 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'} hasBin: true - '@angular/common@21.2.7': - resolution: {integrity: sha512-YFdnU5z8JloJjLYa52OyCOULQhqEE/ym7vKfABySWDsiVXZr9FNmKMeZi/lUcg7ZO22UbBihqW9a9D6VSHOo+g==} + '@angular/common@21.2.8': + resolution: {integrity: sha512-ZvgcxsLPkSG0B1jc2ZXshAWIFBoQ0U9uwIX/zG/RGcfMpoKyEDNAebli6FTIpxIlz/35rtBNV7EGPhinjPTJFQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.2.7 + '@angular/core': 21.2.8 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@21.2.7': - resolution: {integrity: sha512-r76vKBM7Wu0N8PTeec7340Gtv1wC7IBQGJOQnukshPgzaabgNKxmUiChGxi+RJNo/Tsdiw9ZfddcBgBjq79ZIg==} + '@angular/compiler-cli@21.2.8': + resolution: {integrity: sha512-S0W+6QazCsn/4xWZu0V5VmU9zmKIlqFR2FJSsAQUPReVmpA40SuQSP6A/cyMVIMYaHvO/cAXSHJVgpxBzBSL/Q==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.7 + '@angular/compiler': 21.2.8 typescript: '>=5.9 <6.1' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@21.2.7': - resolution: {integrity: sha512-4J0Nl5gGmr5SKgR3FHK4J6rdG0aP5zAsY3AJU8YXH+D98CeNTjQUD8XHsdD2cTwo08V5mDdFa5VCsREpMPJ5gQ==} + '@angular/compiler@21.2.8': + resolution: {integrity: sha512-Il9KlT6qX8rWmun5jY6wMLx56bCQZpOVIFEyHM4ai2wmxvbqyxgRFKDs4iMRNn1h04Tgupl6cKSqP9lecIvH6w==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - '@angular/core@21.2.7': - resolution: {integrity: sha512-4bnskeRNNOZMn3buVw47Zz9Py4B8AZgYHe5xBEMOY5/yrldb7OFje5gWCWls23P18FKwhl+Xx1hgnOEPSs29gw==} + '@angular/core@21.2.8': + resolution: {integrity: sha512-hI7n4t8qgFJaVV55LIaNuzcdP+/IeuqQRu3huSLo47Gf6uZAD0Acj4Ye9SC8YNmhUu5/RiImngm9NOlcI2oCJA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/compiler': 21.2.7 + '@angular/compiler': 21.2.8 rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 || ~0.16.0 peerDependenciesMeta: @@ -331,49 +331,49 @@ packages: zone.js: optional: true - '@angular/forms@21.2.7': - resolution: {integrity: sha512-YD/h07cdEeAUs41ysTk6820T0lG/XiQmFiq02d3IsiHYI5Vaj2pg9Ti1wWZYEBM//hVAPTzV0dwdV7Q1Gxju1w==} + '@angular/forms@21.2.8': + resolution: {integrity: sha512-tyQAHjfMHcqETRkKQaZHjYqIK9W8uRenPpY2DF/Jl+S7CwcaX4T8t8TKgzvTynNzQW9QGiLg0pqVosVMKzBXJg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.2.7 - '@angular/core': 21.2.7 - '@angular/platform-browser': 21.2.7 + '@angular/common': 21.2.8 + '@angular/core': 21.2.8 + '@angular/platform-browser': 21.2.8 rxjs: ^6.5.3 || ^7.4.0 - '@angular/localize@21.2.7': - resolution: {integrity: sha512-EcZHeEuEy9Fr4cWOwdJudPVbjfqso82bAXmnAE0f+Rt5LB3Le8O5RDGgV52EuLo8gzTiWzhVUI7Zg1tYkBqnSQ==} + '@angular/localize@21.2.8': + resolution: {integrity: sha512-wt1ZIE2c7IL1KOgfJyr7pN5SNoQXy02dB/dswZKQlJWduVWkzG83uftPFPjhfASQeqrlpW7tXhb6PiRkJGlCkg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.7 - '@angular/compiler-cli': 21.2.7 + '@angular/compiler': 21.2.8 + '@angular/compiler-cli': 21.2.8 - '@angular/platform-browser-dynamic@21.2.7': - resolution: {integrity: sha512-P7s9ABgJqRyUS5HNRJFRr15xaXbWfSH9KjSxkBYYJbXIA986uGb6qjnHepu+2xMyZDlt2nd9Ms+t4M60/GM+FQ==} + '@angular/platform-browser-dynamic@21.2.8': + resolution: {integrity: sha512-9XeplSHsKnLDm14dvwXG00Ox6WbDrhf7ub7MxxcJ6gCgRm/yqJ3Vrz4a+NBpYnelapqiCCGEdHeyx2xt8vG1qA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.2.7 - '@angular/compiler': 21.2.7 - '@angular/core': 21.2.7 - '@angular/platform-browser': 21.2.7 + '@angular/common': 21.2.8 + '@angular/compiler': 21.2.8 + '@angular/core': 21.2.8 + '@angular/platform-browser': 21.2.8 - '@angular/platform-browser@21.2.7': - resolution: {integrity: sha512-nklVhstRZL4wpYg9Cyae/Eyfa7LMpgb0TyD/F//qCuohhM8nM7F+O0ekykGD6H+I34jsvqx6yLS7MicndWVz7Q==} + '@angular/platform-browser@21.2.8': + resolution: {integrity: sha512-4fwmGf7GCuIsjFqx1gqqWC92YjlN9SmGJO17TPPsOm5zUOnDx+h3Bj9XjdXxlcBtugTb2xHk6Auqyv3lzWGlkw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/animations': 21.2.7 - '@angular/common': 21.2.7 - '@angular/core': 21.2.7 + '@angular/animations': 21.2.8 + '@angular/common': 21.2.8 + '@angular/core': 21.2.8 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/service-worker@21.2.7': - resolution: {integrity: sha512-vTZvoV/Tp/cetrsvlsLBkisTUr5RISLmJPo+2outfhfOM6nJNZ1Flc66I46HpAYCAYgrcuyDxCtivjF4SP+yUQ==} + '@angular/service-worker@21.2.8': + resolution: {integrity: sha512-r0hE9Xm50HhIQUXfpi6Bl8Omp5uluKoj04EgY8uvK5cVO7wWlC9PJ52Gmc59K3e1Qj6pBPMX9AxykQeIlTZH+g==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/core': 21.2.7 + '@angular/core': 21.2.8 rxjs: ^6.5.3 || ^7.4.0 '@asamuzakjp/css-color@4.1.2': @@ -740,8 +740,8 @@ packages: '@harperfast/extended-iterable@1.0.3': resolution: {integrity: sha512-sSAYhQca3rDWtQUHSAPeO7axFIUJOI6hn1gjRC5APVE1a90tuyT8f5WIgRsFhhWA7htNkju2veB9eWL6YHi/Lw==} - '@hono/node-server@1.19.12': - resolution: {integrity: sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==} + '@hono/node-server@1.19.13': + resolution: {integrity: sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 @@ -1115,8 +1115,8 @@ packages: resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} engines: {node: '>= 10'} - '@napi-rs/wasm-runtime@1.1.2': - resolution: {integrity: sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==} + '@napi-rs/wasm-runtime@1.1.3': + resolution: {integrity: sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 @@ -1131,8 +1131,8 @@ packages: '@popperjs/core': ^2.11.8 rxjs: ^6.5.3 || ^7.4.0 - '@ng-select/ng-select@21.7.0': - resolution: {integrity: sha512-YHeayf5hzl8FmbCXsJDoAeTchxO1mxCQdIVbM8sarJE0K+Dvtxeiec59l4rnaj0ZW/Vb97wD5K7vaCQfvuXhoQ==} + '@ng-select/ng-select@21.8.0': + resolution: {integrity: sha512-PXJFfitM7VO/ynqjJ3wKGoRETU6y0ARjcz5bA5V420fX0H6WyU3O91Ex5uFDNx08DzT4fU1GHPs0KXxI+LYOVg==} engines: {node: ^20.19.0 || ^22.12.0 || ^24.0.0} peerDependencies: '@angular/common': ^21.0.0 @@ -1504,8 +1504,8 @@ packages: cpu: [x64] os: [win32] - '@schematics/angular@21.2.6': - resolution: {integrity: sha512-KpLD8R2S762jbLdNEepE+b7KjhVOKPFHHdgNqhPv0NiGLdsvXSOx1e63JvFacoCZdmP7n3/gwmyT/utcVvnsag==} + '@schematics/angular@21.2.7': + resolution: {integrity: sha512-aqEj3RyBtmH+41HZvrbfrpCo0e+0NzwyQyNSC/wLDShVqoidBtPbEdHU1FZ4+ni41da7rI3F12gUuAHws27kMA==} 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'} '@sigstore/bundle@4.0.0': @@ -1516,8 +1516,8 @@ packages: resolution: {integrity: sha512-kxHrDQ9YgfrWUSXU0cjsQGv8JykOFZQ9ErNKbFPWzk3Hgpwu8x2hHrQ9IdA8yl+j9RTLTC3sAF3Tdq1IQCP4oA==} engines: {node: ^20.17.0 || >=22.9.0} - '@sigstore/protobuf-specs@0.5.0': - resolution: {integrity: sha512-MM8XIwUjN2bwvCg1QvrMtbBmpcSHrkhFSCu1D11NyPvDQ25HEc4oG5/OcQfd/Tlf/OxmKWERDj0zGE23jQaMwA==} + '@sigstore/protobuf-specs@0.5.1': + resolution: {integrity: sha512-/ScWUhhoFasJsSRGTVBwId1loQjjnjAfE4djL6ZhrXRpNCmPTnUKF5Jokd58ILseOMjzET3UrMOtJPS9sYeI0g==} engines: {node: ^18.17.0 || >=20.5.0} '@sigstore/sign@4.1.1': @@ -1579,8 +1579,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@25.5.1': - resolution: {integrity: sha512-lgrR3HRNQdTEeeXBnLURFO4JIIbpcVcMlLM9IG0jsNRTRNSbMkm9S2hyhxhnokke1NM25Dr9QghgeB5PQKolrw==} + '@types/node@25.5.2': + resolution: {integrity: sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==} '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -1606,8 +1606,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.58.0': - resolution: {integrity: sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==} + '@typescript-eslint/project-service@8.58.1': + resolution: {integrity: sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1616,8 +1616,8 @@ packages: resolution: {integrity: sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.58.0': - resolution: {integrity: sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==} + '@typescript-eslint/scope-manager@8.58.1': + resolution: {integrity: sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.47.0': @@ -1626,8 +1626,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/tsconfig-utils@8.58.0': - resolution: {integrity: sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==} + '@typescript-eslint/tsconfig-utils@8.58.1': + resolution: {integrity: sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1643,8 +1643,8 @@ packages: resolution: {integrity: sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.58.0': - resolution: {integrity: sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==} + '@typescript-eslint/types@8.58.1': + resolution: {integrity: sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.47.0': @@ -1653,8 +1653,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.58.0': - resolution: {integrity: sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==} + '@typescript-eslint/typescript-estree@8.58.1': + resolution: {integrity: sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1666,8 +1666,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.58.0': - resolution: {integrity: sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==} + '@typescript-eslint/utils@8.58.1': + resolution: {integrity: sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -1677,8 +1677,8 @@ packages: resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.58.0': - resolution: {integrity: sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==} + '@typescript-eslint/visitor-keys@8.58.1': + resolution: {integrity: sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-basic-ssl@2.1.4': @@ -1687,11 +1687,11 @@ packages: peerDependencies: vite: ^6.0.0 || ^7.0.0 - '@vitest/expect@4.1.2': - resolution: {integrity: sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==} + '@vitest/expect@4.1.4': + resolution: {integrity: sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==} - '@vitest/mocker@4.1.2': - resolution: {integrity: sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==} + '@vitest/mocker@4.1.4': + resolution: {integrity: sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1701,20 +1701,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.2': - resolution: {integrity: sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==} + '@vitest/pretty-format@4.1.4': + resolution: {integrity: sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==} - '@vitest/runner@4.1.2': - resolution: {integrity: sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==} + '@vitest/runner@4.1.4': + resolution: {integrity: sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==} - '@vitest/snapshot@4.1.2': - resolution: {integrity: sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==} + '@vitest/snapshot@4.1.4': + resolution: {integrity: sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw==} - '@vitest/spy@4.1.2': - resolution: {integrity: sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==} + '@vitest/spy@4.1.4': + resolution: {integrity: sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==} - '@vitest/utils@4.1.2': - resolution: {integrity: sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==} + '@vitest/utils@4.1.4': + resolution: {integrity: sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==} '@yarnpkg/lockfile@1.1.0': resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} @@ -1817,8 +1817,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.10.13: - resolution: {integrity: sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw==} + baseline-browser-mapping@2.10.17: + resolution: {integrity: sha512-HdrkN8eVG2CXxeifv/VdJ4A4RSra1DTW8dc/hdxzhGHN8QePs6gKaWM9pHPcpCoxYZJuOZ8drHmbdpLHjCYjLA==} engines: {node: '>=6.0.0'} hasBin: true @@ -1883,8 +1883,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001784: - resolution: {integrity: sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==} + caniuse-lite@1.0.30001787: + resolution: {integrity: sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -1946,8 +1946,8 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - content-disposition@1.0.1: - resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} engines: {node: '>=18'} content-type@1.0.5: @@ -2041,8 +2041,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.331: - resolution: {integrity: sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==} + electron-to-chromium@1.5.334: + resolution: {integrity: sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2336,8 +2336,8 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hono@4.12.10: - resolution: {integrity: sha512-mx/p18PLy5og9ufies2GOSUqep98Td9q4i/EF6X7yJgAiIopxqdfIO3jbqsi3jRgTgw88jMDEzVKi+V2EF+27w==} + hono@4.12.12: + resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==} engines: {node: '>=16.9.0'} hosted-git-info@9.0.2: @@ -2542,8 +2542,8 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} - lru-cache@11.2.7: - resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} + lru-cache@11.3.3: + resolution: {integrity: sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -2856,8 +2856,8 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss@8.5.8: - resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + postcss@8.5.9: + resolution: {integrity: sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -2880,8 +2880,8 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qs@6.15.0: - resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} + qs@6.15.1: + resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} engines: {node: '>=0.6'} queue-microtask@1.2.3: @@ -2994,8 +2994,8 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} side-channel-map@1.0.1: @@ -3136,23 +3136,27 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.0.4: - resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} + tinyexec@1.1.1: + resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==} engines: {node: '>=18'} tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + tinyrainbow@3.1.0: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tldts-core@7.0.27: - resolution: {integrity: sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==} + tldts-core@7.0.28: + resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==} - tldts@7.0.27: - resolution: {integrity: sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==} + tldts@7.0.28: + resolution: {integrity: sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==} hasBin: true to-regex-range@5.0.1: @@ -3232,8 +3236,8 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite@7.3.1: - resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + vite@7.3.2: + resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3272,18 +3276,20 @@ packages: yaml: optional: true - vitest@4.1.2: - resolution: {integrity: sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==} + vitest@4.1.4: + resolution: {integrity: sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.2 - '@vitest/browser-preview': 4.1.2 - '@vitest/browser-webdriverio': 4.1.2 - '@vitest/ui': 4.1.2 + '@vitest/browser-playwright': 4.1.4 + '@vitest/browser-preview': 4.1.4 + '@vitest/browser-webdriverio': 4.1.4 + '@vitest/coverage-istanbul': 4.1.4 + '@vitest/coverage-v8': 4.1.4 + '@vitest/ui': 4.1.4 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3300,6 +3306,10 @@ packages: optional: true '@vitest/browser-webdriverio': optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true '@vitest/ui': optional: true happy-dom: @@ -3537,14 +3547,14 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.2102.6(chokidar@5.0.0)': + '@angular-devkit/architect@0.2102.7(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.6(chokidar@5.0.0) + '@angular-devkit/core': 21.2.7(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/core@21.2.6(chokidar@5.0.0)': + '@angular-devkit/core@21.2.7(chokidar@5.0.0)': dependencies: ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) @@ -3555,9 +3565,9 @@ snapshots: optionalDependencies: chokidar: 5.0.0 - '@angular-devkit/schematics@21.2.6(chokidar@5.0.0)': + '@angular-devkit/schematics@21.2.7(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.6(chokidar@5.0.0) + '@angular-devkit/core': 21.2.7(chokidar@5.0.0) jsonc-parser: 3.3.1 magic-string: 0.30.21 ora: 9.3.0 @@ -3565,11 +3575,11 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-eslint/builder@21.1.0(@angular/cli@21.2.6(@types/node@25.5.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/builder@21.1.0(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@angular-devkit/architect': 0.2102.6(chokidar@5.0.0) - '@angular-devkit/core': 21.2.6(chokidar@5.0.0) - '@angular/cli': 21.2.6(@types/node@25.5.1)(chokidar@5.0.0) + '@angular-devkit/architect': 0.2102.7(chokidar@5.0.0) + '@angular-devkit/core': 21.2.7(chokidar@5.0.0) + '@angular/cli': 21.2.7(@types/node@25.5.2)(chokidar@5.0.0) eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: @@ -3577,34 +3587,34 @@ snapshots: '@angular-eslint/bundled-angular-compiler@21.1.0': {} - '@angular-eslint/eslint-plugin-template@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.58.0)(@typescript-eslint/utils@8.58.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin-template@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.58.1)(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.58.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/types': 8.58.0 - '@typescript-eslint/utils': 8.58.0(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4)(typescript@5.9.3) aria-query: 5.3.2 axobject-query: 4.1.0 eslint: 9.39.4 typescript: 5.9.3 - '@angular-eslint/eslint-plugin@21.1.0(@typescript-eslint/utils@8.58.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin@21.1.0(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.58.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/utils': 8.58.0(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 - '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.6(@types/node@25.5.1)(chokidar@5.0.0))(@typescript-eslint/types@8.58.0)(@typescript-eslint/utils@8.58.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(@typescript-eslint/types@8.58.1)(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@angular-devkit/core': 21.2.6(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.6(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.58.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.58.0)(@typescript-eslint/utils@8.58.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.6(@types/node@25.5.1)(chokidar@5.0.0) + '@angular-devkit/core': 21.2.7(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.7(chokidar@5.0.0) + '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.58.1)(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular/cli': 21.2.7(@types/node@25.5.2)(chokidar@5.0.0) ignore: 7.0.5 semver: 7.7.3 strip-json-comments: 3.1.1 @@ -3623,29 +3633,29 @@ snapshots: eslint-scope: 9.1.2 typescript: 5.9.3 - '@angular-eslint/utils@21.1.0(@typescript-eslint/utils@8.58.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/utils@21.1.0(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@typescript-eslint/utils': 8.58.0(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 - '@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - '@angular/build@21.2.6(@angular/compiler-cli@21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3))(@angular/compiler@21.2.7)(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.7(@angular/compiler-cli@21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3))(@angular/compiler@21.2.7))(@angular/platform-browser@21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.1)(chokidar@5.0.0)(postcss@8.5.8)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.2(@types/node@25.5.1)(jsdom@27.4.0)(vite@7.3.1(@types/node@25.5.1)(sass@1.97.3)))': + '@angular/build@21.2.7(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8)(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(chokidar@5.0.0)(postcss@8.5.9)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.4(@types/node@25.5.2)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3)))': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2102.6(chokidar@5.0.0) - '@angular/compiler': 21.2.7 - '@angular/compiler-cli': 21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3) + '@angular-devkit/architect': 0.2102.7(chokidar@5.0.0) + '@angular/compiler': 21.2.8 + '@angular/compiler-cli': 21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3) '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@25.5.1) - '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.1(@types/node@25.5.1)(sass@1.97.3)) + '@inquirer/confirm': 5.1.21(@types/node@25.5.2) + '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3)) beasties: 0.4.1 browserslist: 4.28.2 esbuild: 0.27.3 @@ -3666,16 +3676,16 @@ snapshots: tslib: 2.8.1 typescript: 5.9.3 undici: 7.24.4 - vite: 7.3.1(@types/node@25.5.1)(sass@1.97.3) + vite: 7.3.2(@types/node@25.5.2)(sass@1.97.3) watchpack: 2.5.1 optionalDependencies: - '@angular/core': 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/localize': 21.2.7(@angular/compiler-cli@21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3))(@angular/compiler@21.2.7) - '@angular/platform-browser': 21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker': 21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/localize': 21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8) + '@angular/platform-browser': 21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/service-worker': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) lmdb: 3.5.1 - postcss: 8.5.8 - vitest: 4.1.2(@types/node@25.5.1)(jsdom@27.4.0)(vite@7.3.1(@types/node@25.5.1)(sass@1.97.3)) + postcss: 8.5.9 + vitest: 4.1.4(@types/node@25.5.2)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3)) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -3691,15 +3701,15 @@ snapshots: - tsx - yaml - '@angular/cli@21.2.6(@types/node@25.5.1)(chokidar@5.0.0)': + '@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2102.6(chokidar@5.0.0) - '@angular-devkit/core': 21.2.6(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.6(chokidar@5.0.0) - '@inquirer/prompts': 7.10.1(@types/node@25.5.1) - '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.5.1))(@types/node@25.5.1)(listr2@9.0.5) + '@angular-devkit/architect': 0.2102.7(chokidar@5.0.0) + '@angular-devkit/core': 21.2.7(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.7(chokidar@5.0.0) + '@inquirer/prompts': 7.10.1(@types/node@25.5.2) + '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.5.2))(@types/node@25.5.2)(listr2@9.0.5) '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) - '@schematics/angular': 21.2.6(chokidar@5.0.0) + '@schematics/angular': 21.2.7(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 algoliasearch: 5.48.1 ini: 6.0.0 @@ -3717,15 +3727,15 @@ snapshots: - chokidar - supports-color - '@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3)': + '@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3)': dependencies: - '@angular/compiler': 21.2.7 + '@angular/compiler': 21.2.8 '@babel/core': 7.29.0 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 5.0.0 @@ -3739,57 +3749,57 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/compiler@21.2.7': + '@angular/compiler@21.2.8': dependencies: tslib: 2.8.1 - '@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)': + '@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 21.2.7 + '@angular/compiler': 21.2.8 zone.js: 0.15.0 - '@angular/forms@21.2.7(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': + '@angular/forms@21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) '@standard-schema/spec': 1.1.0 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/localize@21.2.7(@angular/compiler-cli@21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3))(@angular/compiler@21.2.7)': + '@angular/localize@21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8)': dependencies: - '@angular/compiler': 21.2.7 - '@angular/compiler-cli': 21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3) + '@angular/compiler': 21.2.8 + '@angular/compiler-cli': 21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3) '@babel/core': 7.29.0 '@types/babel__core': 7.20.5 - tinyglobby: 0.2.15 + tinyglobby: 0.2.16 yargs: 18.0.0 transitivePeerDependencies: - supports-color - '@angular/platform-browser-dynamic@21.2.7(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.7)(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))': + '@angular/platform-browser-dynamic@21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.8)(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))': dependencies: - '@angular/common': 21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/compiler': 21.2.7 - '@angular/core': 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/compiler': 21.2.8 + '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) tslib: 2.8.1 - '@angular/platform-browser@21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/common': 21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 optionalDependencies: - '@angular/animations': 21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/animations': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/service-worker@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 @@ -3799,7 +3809,7 @@ snapshots: '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - lru-cache: 11.2.7 + lru-cache: 11.3.3 '@asamuzakjp/dom-selector@6.8.1': dependencies: @@ -3807,7 +3817,7 @@ snapshots: bidi-js: 1.0.3 css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 - lru-cache: 11.2.7 + lru-cache: 11.3.3 '@asamuzakjp/nwsapi@2.3.9': {} @@ -4085,9 +4095,9 @@ snapshots: '@exodus/bytes@1.15.0': {} - '@fortawesome/angular-fontawesome@4.0.0(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))': + '@fortawesome/angular-fontawesome@4.0.0(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) '@fortawesome/fontawesome-svg-core': 7.2.0 tslib: 2.8.1 @@ -4114,9 +4124,9 @@ snapshots: '@harperfast/extended-iterable@1.0.3': optional: true - '@hono/node-server@1.19.12(hono@4.12.10)': + '@hono/node-server@1.19.13(hono@4.12.12)': dependencies: - hono: 4.12.10 + hono: 4.12.12 '@humanfs/core@0.19.1': {} @@ -4131,128 +4141,128 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.2(@types/node@25.5.1)': + '@inquirer/checkbox@4.3.2(@types/node@25.5.2)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.5.1) + '@inquirer/core': 10.3.2(@types/node@25.5.2) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.5.1) + '@inquirer/type': 3.0.10(@types/node@25.5.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 - '@inquirer/confirm@5.1.21(@types/node@25.5.1)': + '@inquirer/confirm@5.1.21(@types/node@25.5.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.1) - '@inquirer/type': 3.0.10(@types/node@25.5.1) + '@inquirer/core': 10.3.2(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.5.2) optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 - '@inquirer/core@10.3.2(@types/node@25.5.1)': + '@inquirer/core@10.3.2(@types/node@25.5.2)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.5.1) + '@inquirer/type': 3.0.10(@types/node@25.5.2) 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 optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 - '@inquirer/editor@4.2.23(@types/node@25.5.1)': + '@inquirer/editor@4.2.23(@types/node@25.5.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.1) - '@inquirer/external-editor': 1.0.3(@types/node@25.5.1) - '@inquirer/type': 3.0.10(@types/node@25.5.1) + '@inquirer/core': 10.3.2(@types/node@25.5.2) + '@inquirer/external-editor': 1.0.3(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.5.2) optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 - '@inquirer/expand@4.0.23(@types/node@25.5.1)': + '@inquirer/expand@4.0.23(@types/node@25.5.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.1) - '@inquirer/type': 3.0.10(@types/node@25.5.1) + '@inquirer/core': 10.3.2(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.5.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 - '@inquirer/external-editor@1.0.3(@types/node@25.5.1)': + '@inquirer/external-editor@1.0.3(@types/node@25.5.2)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.3.1(@types/node@25.5.1)': + '@inquirer/input@4.3.1(@types/node@25.5.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.1) - '@inquirer/type': 3.0.10(@types/node@25.5.1) + '@inquirer/core': 10.3.2(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.5.2) optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 - '@inquirer/number@3.0.23(@types/node@25.5.1)': + '@inquirer/number@3.0.23(@types/node@25.5.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.1) - '@inquirer/type': 3.0.10(@types/node@25.5.1) + '@inquirer/core': 10.3.2(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.5.2) optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 - '@inquirer/password@4.0.23(@types/node@25.5.1)': + '@inquirer/password@4.0.23(@types/node@25.5.2)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.5.1) - '@inquirer/type': 3.0.10(@types/node@25.5.1) + '@inquirer/core': 10.3.2(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.5.2) optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 - '@inquirer/prompts@7.10.1(@types/node@25.5.1)': + '@inquirer/prompts@7.10.1(@types/node@25.5.2)': dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@25.5.1) - '@inquirer/confirm': 5.1.21(@types/node@25.5.1) - '@inquirer/editor': 4.2.23(@types/node@25.5.1) - '@inquirer/expand': 4.0.23(@types/node@25.5.1) - '@inquirer/input': 4.3.1(@types/node@25.5.1) - '@inquirer/number': 3.0.23(@types/node@25.5.1) - '@inquirer/password': 4.0.23(@types/node@25.5.1) - '@inquirer/rawlist': 4.1.11(@types/node@25.5.1) - '@inquirer/search': 3.2.2(@types/node@25.5.1) - '@inquirer/select': 4.4.2(@types/node@25.5.1) + '@inquirer/checkbox': 4.3.2(@types/node@25.5.2) + '@inquirer/confirm': 5.1.21(@types/node@25.5.2) + '@inquirer/editor': 4.2.23(@types/node@25.5.2) + '@inquirer/expand': 4.0.23(@types/node@25.5.2) + '@inquirer/input': 4.3.1(@types/node@25.5.2) + '@inquirer/number': 3.0.23(@types/node@25.5.2) + '@inquirer/password': 4.0.23(@types/node@25.5.2) + '@inquirer/rawlist': 4.1.11(@types/node@25.5.2) + '@inquirer/search': 3.2.2(@types/node@25.5.2) + '@inquirer/select': 4.4.2(@types/node@25.5.2) optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 - '@inquirer/rawlist@4.1.11(@types/node@25.5.1)': + '@inquirer/rawlist@4.1.11(@types/node@25.5.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.1) - '@inquirer/type': 3.0.10(@types/node@25.5.1) + '@inquirer/core': 10.3.2(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.5.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 - '@inquirer/search@3.2.2(@types/node@25.5.1)': + '@inquirer/search@3.2.2(@types/node@25.5.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.1) + '@inquirer/core': 10.3.2(@types/node@25.5.2) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.5.1) + '@inquirer/type': 3.0.10(@types/node@25.5.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 - '@inquirer/select@4.4.2(@types/node@25.5.1)': + '@inquirer/select@4.4.2(@types/node@25.5.2)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.5.1) + '@inquirer/core': 10.3.2(@types/node@25.5.2) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.5.1) + '@inquirer/type': 3.0.10(@types/node@25.5.2) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 - '@inquirer/type@3.0.10(@types/node@25.5.1)': + '@inquirer/type@3.0.10(@types/node@25.5.2)': optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 '@isaacs/fs-minipass@4.0.1': dependencies: @@ -4279,10 +4289,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@25.5.1))(@types/node@25.5.1)(listr2@9.0.5)': + '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@25.5.2))(@types/node@25.5.2)(listr2@9.0.5)': dependencies: - '@inquirer/prompts': 7.10.1(@types/node@25.5.1) - '@inquirer/type': 3.0.10(@types/node@25.5.1) + '@inquirer/prompts': 7.10.1(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.5.2) listr2: 9.0.5 transitivePeerDependencies: - '@types/node' @@ -4310,7 +4320,7 @@ snapshots: '@modelcontextprotocol/sdk@1.26.0(zod@4.3.6)': dependencies: - '@hono/node-server': 1.19.12(hono@4.12.10) + '@hono/node-server': 1.19.13(hono@4.12.12) ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 @@ -4320,7 +4330,7 @@ snapshots: eventsource-parser: 3.0.6 express: 5.2.1 express-rate-limit: 8.3.2(express@5.2.1) - hono: 4.12.10 + hono: 4.12.12 jose: 6.2.2 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -4420,28 +4430,28 @@ snapshots: '@napi-rs/nice-win32-x64-msvc': 1.1.1 optional: true - '@napi-rs/wasm-runtime@1.1.2(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)': + '@napi-rs/wasm-runtime@1.1.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)': dependencies: '@emnapi/core': 1.9.1 '@emnapi/runtime': 1.9.1 '@tybys/wasm-util': 0.10.1 optional: true - '@ng-bootstrap/ng-bootstrap@20.0.0(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.7(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.7(@angular/compiler-cli@21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3))(@angular/compiler@21.2.7))(@popperjs/core@2.11.8)(rxjs@7.8.2)': + '@ng-bootstrap/ng-bootstrap@20.0.0(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8))(@popperjs/core@2.11.8)(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 21.2.7(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) - '@angular/localize': 21.2.7(@angular/compiler-cli@21.2.7(@angular/compiler@21.2.7)(typescript@5.9.3))(@angular/compiler@21.2.7) + '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/localize': 21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8) '@popperjs/core': 2.11.8 rxjs: 7.8.2 tslib: 2.8.1 - '@ng-select/ng-select@21.7.0(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.7(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': + '@ng-select/ng-select@21.8.0(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': dependencies: - '@angular/common': 21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 21.2.7(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.7(@angular/animations@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) tslib: 2.8.1 '@nodelib/fs.scandir@2.1.5': @@ -4461,7 +4471,7 @@ snapshots: agent-base: 7.1.4 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 - lru-cache: 11.2.7 + lru-cache: 11.3.3 socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color @@ -4475,7 +4485,7 @@ snapshots: '@gar/promise-retry': 1.0.3 '@npmcli/promise-spawn': 9.0.1 ini: 6.0.0 - lru-cache: 11.2.7 + lru-cache: 11.3.3 npm-pick-manifest: 11.0.3 proc-log: 6.1.0 semver: 7.7.4 @@ -4611,7 +4621,7 @@ snapshots: '@rolldown/binding-wasm32-wasi@1.0.0-rc.4(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.2(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1) + '@napi-rs/wasm-runtime': 1.1.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -4700,28 +4710,28 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.60.1': optional: true - '@schematics/angular@21.2.6(chokidar@5.0.0)': + '@schematics/angular@21.2.7(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.6(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.6(chokidar@5.0.0) + '@angular-devkit/core': 21.2.7(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.7(chokidar@5.0.0) jsonc-parser: 3.3.1 transitivePeerDependencies: - chokidar '@sigstore/bundle@4.0.0': dependencies: - '@sigstore/protobuf-specs': 0.5.0 + '@sigstore/protobuf-specs': 0.5.1 '@sigstore/core@3.2.0': {} - '@sigstore/protobuf-specs@0.5.0': {} + '@sigstore/protobuf-specs@0.5.1': {} '@sigstore/sign@4.1.1': dependencies: '@gar/promise-retry': 1.0.3 '@sigstore/bundle': 4.0.0 '@sigstore/core': 3.2.0 - '@sigstore/protobuf-specs': 0.5.0 + '@sigstore/protobuf-specs': 0.5.1 make-fetch-happen: 15.0.5 proc-log: 6.1.0 transitivePeerDependencies: @@ -4729,7 +4739,7 @@ snapshots: '@sigstore/tuf@4.0.2': dependencies: - '@sigstore/protobuf-specs': 0.5.0 + '@sigstore/protobuf-specs': 0.5.1 tuf-js: 4.1.0 transitivePeerDependencies: - supports-color @@ -4738,7 +4748,7 @@ snapshots: dependencies: '@sigstore/bundle': 4.0.0 '@sigstore/core': 3.2.0 - '@sigstore/protobuf-specs': 0.5.0 + '@sigstore/protobuf-specs': 0.5.1 '@socket.io/component-emitter@3.1.2': {} @@ -4784,7 +4794,7 @@ snapshots: '@types/cors@2.8.19': dependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 '@types/deep-eql@4.0.2': {} @@ -4794,13 +4804,13 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@25.5.1': + '@types/node@25.5.2': dependencies: undici-types: 7.18.2 '@types/ws@8.18.1': dependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 '@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: @@ -4840,10 +4850,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.58.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.58.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.58.0(typescript@5.9.3) - '@typescript-eslint/types': 8.58.0 + '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@5.9.3) + '@typescript-eslint/types': 8.58.1 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -4854,16 +4864,16 @@ snapshots: '@typescript-eslint/types': 8.47.0 '@typescript-eslint/visitor-keys': 8.47.0 - '@typescript-eslint/scope-manager@8.58.0': + '@typescript-eslint/scope-manager@8.58.1': dependencies: - '@typescript-eslint/types': 8.58.0 - '@typescript-eslint/visitor-keys': 8.58.0 + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/visitor-keys': 8.58.1 '@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.58.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.58.1(typescript@5.9.3)': dependencies: typescript: 5.9.3 @@ -4881,7 +4891,7 @@ snapshots: '@typescript-eslint/types@8.47.0': {} - '@typescript-eslint/types@8.58.0': {} + '@typescript-eslint/types@8.58.1': {} '@typescript-eslint/typescript-estree@8.47.0(typescript@5.9.3)': dependencies: @@ -4899,16 +4909,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.58.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.58.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.58.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.58.0(typescript@5.9.3) - '@typescript-eslint/types': 8.58.0 - '@typescript-eslint/visitor-keys': 8.58.0 + '@typescript-eslint/project-service': 8.58.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@5.9.3) + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/visitor-keys': 8.58.1 debug: 4.4.3 minimatch: 10.2.5 semver: 7.7.4 - tinyglobby: 0.2.15 + tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -4925,12 +4935,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.58.0(eslint@9.39.4)(typescript@5.9.3)': + '@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) - '@typescript-eslint/scope-manager': 8.58.0 - '@typescript-eslint/types': 8.58.0 - '@typescript-eslint/typescript-estree': 8.58.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.58.1 + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: @@ -4941,53 +4951,53 @@ snapshots: '@typescript-eslint/types': 8.47.0 eslint-visitor-keys: 4.2.1 - '@typescript-eslint/visitor-keys@8.58.0': + '@typescript-eslint/visitor-keys@8.58.1': dependencies: - '@typescript-eslint/types': 8.58.0 + '@typescript-eslint/types': 8.58.1 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.1(@types/node@25.5.1)(sass@1.97.3))': + '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3))': dependencies: - vite: 7.3.1(@types/node@25.5.1)(sass@1.97.3) + vite: 7.3.2(@types/node@25.5.2)(sass@1.97.3) - '@vitest/expect@4.1.2': + '@vitest/expect@4.1.4': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.2 - '@vitest/utils': 4.1.2 + '@vitest/spy': 4.1.4 + '@vitest/utils': 4.1.4 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.2(vite@7.3.1(@types/node@25.5.1)(sass@1.97.3))': + '@vitest/mocker@4.1.4(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3))': dependencies: - '@vitest/spy': 4.1.2 + '@vitest/spy': 4.1.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.5.1)(sass@1.97.3) + vite: 7.3.2(@types/node@25.5.2)(sass@1.97.3) - '@vitest/pretty-format@4.1.2': + '@vitest/pretty-format@4.1.4': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.2': + '@vitest/runner@4.1.4': dependencies: - '@vitest/utils': 4.1.2 + '@vitest/utils': 4.1.4 pathe: 2.0.3 - '@vitest/snapshot@4.1.2': + '@vitest/snapshot@4.1.4': dependencies: - '@vitest/pretty-format': 4.1.2 - '@vitest/utils': 4.1.2 + '@vitest/pretty-format': 4.1.4 + '@vitest/utils': 4.1.4 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.2': {} + '@vitest/spy@4.1.4': {} - '@vitest/utils@4.1.2': + '@vitest/utils@4.1.4': dependencies: - '@vitest/pretty-format': 4.1.2 + '@vitest/pretty-format': 4.1.4 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -5048,18 +5058,18 @@ snapshots: '@algolia/requester-fetch': 5.48.1 '@algolia/requester-node-http': 5.48.1 - angular-eslint@21.1.0(@angular/cli@21.2.6(@types/node@25.5.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): + angular-eslint@21.1.0(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): dependencies: - '@angular-devkit/core': 21.2.6(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.6(chokidar@5.0.0) - '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.6(@types/node@25.5.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.58.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.58.0)(@typescript-eslint/utils@8.58.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.6(@types/node@25.5.1)(chokidar@5.0.0))(@typescript-eslint/types@8.58.0)(@typescript-eslint/utils@8.58.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-devkit/core': 21.2.7(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.7(chokidar@5.0.0) + '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.58.1)(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(@typescript-eslint/types@8.58.1)(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.6(@types/node@25.5.1)(chokidar@5.0.0) - '@typescript-eslint/types': 8.58.0 - '@typescript-eslint/utils': 8.58.0(eslint@9.39.4)(typescript@5.9.3) + '@angular/cli': 21.2.7(@types/node@25.5.2)(chokidar@5.0.0) + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 typescript-eslint: 8.47.0(eslint@9.39.4)(typescript@5.9.3) @@ -5095,7 +5105,7 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.10.13: {} + baseline-browser-mapping@2.10.17: {} beasties@0.4.1: dependencies: @@ -5105,9 +5115,9 @@ snapshots: domhandler: 5.0.3 htmlparser2: 10.1.0 picocolors: 1.1.1 - postcss: 8.5.8 + postcss: 8.5.9 postcss-media-query-parser: 0.2.3 - postcss-safe-parser: 7.0.1(postcss@8.5.8) + postcss-safe-parser: 7.0.1(postcss@8.5.9) bidi-js@1.0.3: dependencies: @@ -5121,7 +5131,7 @@ snapshots: http-errors: 2.0.1 iconv-lite: 0.7.2 on-finished: 2.4.1 - qs: 6.15.0 + qs: 6.15.1 raw-body: 3.0.2 type-is: 2.0.1 transitivePeerDependencies: @@ -5152,9 +5162,9 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.13 - caniuse-lite: 1.0.30001784 - electron-to-chromium: 1.5.331 + baseline-browser-mapping: 2.10.17 + caniuse-lite: 1.0.30001787 + electron-to-chromium: 1.5.334 node-releases: 2.0.37 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -5167,7 +5177,7 @@ snapshots: '@npmcli/fs': 5.0.0 fs-minipass: 3.0.3 glob: 13.0.6 - lru-cache: 11.2.7 + lru-cache: 11.3.3 minipass: 7.1.3 minipass-collect: 2.0.1 minipass-flush: 1.0.7 @@ -5187,7 +5197,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001784: {} + caniuse-lite@1.0.30001787: {} chai@6.2.2: {} @@ -5239,7 +5249,7 @@ snapshots: concat-map@0.0.1: {} - content-disposition@1.0.1: {} + content-disposition@1.1.0: {} content-type@1.0.5: {} @@ -5284,7 +5294,7 @@ snapshots: '@asamuzakjp/css-color': 4.1.2 '@csstools/css-syntax-patches-for-csstree': 1.1.2(css-tree@3.2.1) css-tree: 3.2.1 - lru-cache: 11.2.7 + lru-cache: 11.3.3 data-urls@6.0.1: dependencies: @@ -5330,7 +5340,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.331: {} + electron-to-chromium@1.5.334: {} emoji-regex@10.6.0: {} @@ -5355,7 +5365,7 @@ snapshots: engine.io@6.6.6: dependencies: '@types/cors': 2.8.19 - '@types/node': 25.5.1 + '@types/node': 25.5.2 '@types/ws': 8.18.1 accepts: 1.3.8 base64id: 2.0.0 @@ -5528,7 +5538,7 @@ snapshots: dependencies: accepts: 2.0.0 body-parser: 2.2.2 - content-disposition: 1.0.1 + content-disposition: 1.1.0 content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 @@ -5546,7 +5556,7 @@ snapshots: once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.15.0 + qs: 6.15.1 range-parser: 1.2.1 router: 2.2.0 send: 1.2.1 @@ -5681,11 +5691,11 @@ snapshots: dependencies: function-bind: 1.1.2 - hono@4.12.10: {} + hono@4.12.12: {} hosted-git-info@9.0.2: dependencies: - lru-cache: 11.2.7 + lru-cache: 11.3.3 html-encoding-sniffer@6.0.0: dependencies: @@ -5902,7 +5912,7 @@ snapshots: strip-ansi: 7.2.0 wrap-ansi: 9.0.2 - lru-cache@11.2.7: {} + lru-cache@11.3.3: {} lru-cache@5.1.1: dependencies: @@ -6035,16 +6045,16 @@ snapshots: negotiator@1.0.0: {} - ngx-cookie-service@21.3.1(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0)): + ngx-cookie-service@21.3.1(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)): dependencies: - '@angular/common': 21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - ngx-socket-io@4.10.0(@angular/common@21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): + ngx-socket-io@4.10.0(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): dependencies: - '@angular/common': 21.2.7(@angular/core@21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.7(@angular/compiler@21.2.7)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) core-js: 3.49.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 @@ -6077,7 +6087,7 @@ snapshots: proc-log: 6.1.0 semver: 7.7.4 tar: 7.5.13 - tinyglobby: 0.2.15 + tinyglobby: 0.2.16 which: 6.0.1 transitivePeerDependencies: - supports-color @@ -6233,7 +6243,7 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.2.7 + lru-cache: 11.3.3 minipass: 7.1.3 path-to-regexp@8.4.2: {} @@ -6254,11 +6264,11 @@ snapshots: postcss-media-query-parser@0.2.3: {} - postcss-safe-parser@7.0.1(postcss@8.5.8): + postcss-safe-parser@7.0.1(postcss@8.5.9): dependencies: - postcss: 8.5.8 + postcss: 8.5.9 - postcss@8.5.8: + postcss@8.5.9: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 @@ -6280,7 +6290,7 @@ snapshots: punycode@2.3.1: {} - qs@6.15.0: + qs@6.15.1: dependencies: side-channel: 1.1.0 @@ -6440,7 +6450,7 @@ snapshots: shebang-regex@3.0.0: {} - side-channel-list@1.0.0: + side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 @@ -6464,7 +6474,7 @@ snapshots: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 - side-channel-list: 1.0.0 + side-channel-list: 1.0.1 side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 @@ -6476,7 +6486,7 @@ snapshots: dependencies: '@sigstore/bundle': 4.0.0 '@sigstore/core': 3.2.0 - '@sigstore/protobuf-specs': 0.5.0 + '@sigstore/protobuf-specs': 0.5.1 '@sigstore/sign': 4.1.1 '@sigstore/tuf': 4.0.2 '@sigstore/verify': 3.1.0 @@ -6624,20 +6634,25 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.0.4: {} + tinyexec@1.1.1: {} tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + tinyrainbow@3.1.0: {} - tldts-core@7.0.27: {} + tldts-core@7.0.28: {} - tldts@7.0.27: + tldts@7.0.28: dependencies: - tldts-core: 7.0.27 + tldts-core: 7.0.28 to-regex-range@5.0.1: dependencies: @@ -6647,7 +6662,7 @@ snapshots: tough-cookie@6.0.1: dependencies: - tldts: 7.0.27 + tldts: 7.0.28 tr46@6.0.0: dependencies: @@ -6710,28 +6725,28 @@ snapshots: vary@1.1.2: {} - vite@7.3.1(@types/node@25.5.1)(sass@1.97.3): + vite@7.3.2(@types/node@25.5.2)(sass@1.97.3): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.8 + postcss: 8.5.9 rollup: 4.60.1 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 fsevents: 2.3.3 sass: 1.97.3 - vitest@4.1.2(@types/node@25.5.1)(jsdom@27.4.0)(vite@7.3.1(@types/node@25.5.1)(sass@1.97.3)): + vitest@4.1.4(@types/node@25.5.2)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3)): dependencies: - '@vitest/expect': 4.1.2 - '@vitest/mocker': 4.1.2(vite@7.3.1(@types/node@25.5.1)(sass@1.97.3)) - '@vitest/pretty-format': 4.1.2 - '@vitest/runner': 4.1.2 - '@vitest/snapshot': 4.1.2 - '@vitest/spy': 4.1.2 - '@vitest/utils': 4.1.2 + '@vitest/expect': 4.1.4 + '@vitest/mocker': 4.1.4(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3)) + '@vitest/pretty-format': 4.1.4 + '@vitest/runner': 4.1.4 + '@vitest/snapshot': 4.1.4 + '@vitest/spy': 4.1.4 + '@vitest/utils': 4.1.4 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -6740,13 +6755,13 @@ snapshots: picomatch: 4.0.4 std-env: 4.0.0 tinybench: 2.9.0 - tinyexec: 1.0.4 - tinyglobby: 0.2.15 + tinyexec: 1.1.1 + tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 7.3.1(@types/node@25.5.1)(sass@1.97.3) + vite: 7.3.2(@types/node@25.5.2)(sass@1.97.3) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.5.1 + '@types/node': 25.5.2 jsdom: 27.4.0 transitivePeerDependencies: - msw diff --git a/uv.lock b/uv.lock index 44a77db..a9bd172 100644 --- a/uv.lock +++ b/uv.lock @@ -602,11 +602,11 @@ wheels = [ [[package]] name = "platformdirs" -version = "4.9.4" +version = "4.9.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/19/56/8d4c30c8a1d07013911a8fdbd8f89440ef9f08d07a1b50ab8ca8be5a20f9/platformdirs-4.9.4.tar.gz", hash = "sha256:1ec356301b7dc906d83f371c8f487070e99d3ccf9e501686456394622a01a934", size = 28737, upload-time = "2026-03-05T18:34:13.271Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/63/d7/97f7e3a6abb67d8080dd406fd4df842c2be0efaf712d1c899c32a075027c/platformdirs-4.9.4-py3-none-any.whl", hash = "sha256:68a9a4619a666ea6439f2ff250c12a853cd1cbd5158d258bd824a7df6be2f868", size = 21216, upload-time = "2026-03-05T18:34:12.172Z" }, + { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, ] [[package]] @@ -755,7 +755,7 @@ wheels = [ [[package]] name = "pytest" -version = "9.0.2" +version = "9.0.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -764,9 +764,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/d1/db/7ef3487e0fb0049ddb5ce41d3a49c235bf9ad299b6a25d5780a89f19230f/pytest-9.0.2.tar.gz", hash = "sha256:75186651a92bd89611d1d9fc20f0b4345fd827c41ccd5c299a868a05d70edf11", size = 1568901, upload-time = "2025-12-06T21:30:51.014Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/ab/b3226f0bd7cdcf710fbede2b3548584366da3b19b5021e74f5bde2a8fa3f/pytest-9.0.2-py3-none-any.whl", hash = "sha256:711ffd45bf766d5264d487b917733b453d917afd2b0ad65223959f59089f875b", size = 374801, upload-time = "2025-12-06T21:30:49.154Z" }, + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, ] [[package]] From 0072d3488ae5b8d922d3ee87458d829993742a32 Mon Sep 17 00:00:00 2001 From: az10b Date: Thu, 9 Apr 2026 19:45:51 -0500 Subject: [PATCH 19/88] Fix permissive CORS policy that allows cross-origin attacks The on_prepare handler unconditionally reflected the Origin request header into Access-Control-Allow-Origin, and Socket.IO was configured with cors_allowed_origins='*'. This allowed any website to make authenticated cross-origin requests to all API endpoints, enabling cross-origin download initiation, cookie overwrite, and data deletion. Replace the blanket origin reflection with an explicit allowlist via the CORS_ALLOWED_ORIGINS environment variable. When unset, cross-origin requests are denied by default. Users who need cross-origin access can set CORS_ALLOWED_ORIGINS to a comma-separated list of trusted origins. --- app/main.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/main.py b/app/main.py index 5853163..7269c8c 100644 --- a/app/main.py +++ b/app/main.py @@ -60,6 +60,7 @@ class Config: 'YTDL_OPTIONS_PRESETS': '{}', 'YTDL_OPTIONS_PRESETS_FILE': '', 'ALLOW_YTDL_OPTIONS_OVERRIDES': 'false', + 'CORS_ALLOWED_ORIGINS': '', 'ROBOTS_TXT': '', 'HOST': '0.0.0.0', 'PORT': '8081', @@ -223,7 +224,8 @@ class ObjectSerializer(json.JSONEncoder): serializer = ObjectSerializer() app = web.Application() -sio = socketio.AsyncServer(cors_allowed_origins='*') +_cors_origins = [o.strip() for o in config.CORS_ALLOWED_ORIGINS.split(',') if o.strip()] if config.CORS_ALLOWED_ORIGINS else [] +sio = socketio.AsyncServer(cors_allowed_origins=_cors_origins if _cors_origins else []) sio.attach(app, socketio_path=config.URL_PREFIX + 'socket.io') routes = web.RouteTableDef() VALID_SUBTITLE_FORMATS = {'srt', 'txt', 'vtt', 'ttml', 'sbv', 'scc', 'dfxp'} @@ -912,8 +914,9 @@ app.router.add_route('OPTIONS', config.URL_PREFIX + 'upload-cookies', add_cors) app.router.add_route('OPTIONS', config.URL_PREFIX + 'delete-cookies', add_cors) async def on_prepare(request, response): - if 'Origin' in request.headers: - response.headers['Access-Control-Allow-Origin'] = request.headers['Origin'] + origin = request.headers.get('Origin') + if origin and _cors_origins and origin in _cors_origins: + response.headers['Access-Control-Allow-Origin'] = origin response.headers['Access-Control-Allow-Headers'] = 'Content-Type' app.on_response_prepare.append(on_prepare) From a6e8617ad8892ac1d5e7c4228275805cb47ce8de Mon Sep 17 00:00:00 2001 From: Benjamin Gervan Date: Fri, 10 Apr 2026 06:41:45 +0200 Subject: [PATCH 20/88] Don't mark live streams as seen --- app/subscriptions.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/subscriptions.py b/app/subscriptions.py index 031ede0..08d76c6 100644 --- a/app/subscriptions.py +++ b/app/subscriptions.py @@ -483,6 +483,8 @@ class SubscriptionManager: seen_entries = [ent for ent in entries if _is_media_entry(ent)] all_ids: list[str] = [] for ent in seen_entries: + if ent.get("live_status") == "is_upcoming": + continue # Don't mark scheduled streams as seen; queue them when they go live eid = _entry_id(ent) if eid: all_ids.append(eid) @@ -662,7 +664,9 @@ class SubscriptionManager: new_ids: list[str] = [] for ent in entries: eid = _entry_id(ent) - if not eid or eid in seen: + if not eid: + continue + if eid in seen and ent.get("live_status") != "is_live": continue new_entries.append(ent) new_ids.append(eid) From aa60420ead5105c06d34bd225327dba50b56e241 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 10 Apr 2026 08:09:20 +0300 Subject: [PATCH 21/88] document CORS_ALLOWED_ORIGINS variable --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 3d40314..e5e3e8c 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ Certain values can be set via environment variables, using the `-e` parameter on * __HTTPS__: Use `https` instead of `http` (__CERTFILE__ and __KEYFILE__ required). Defaults to `false`. * __CERTFILE__: HTTPS certificate file path. * __KEYFILE__: HTTPS key file path. +* __CORS_ALLOWED_ORIGINS__: Comma-separated list of origins permitted to make cross-origin requests to the MeTube API. When unset or empty, all cross-origin requests are denied. This must be configured for [bookmarklets](#-bookmarklet) and any other browser-based tools that contact MeTube from a different origin. Example: `https://www.youtube.com,https://www.vimeo.com`. * __ROBOTS_TXT__: A path to a `robots.txt` file mounted in the container. ### 🏠 Basic Setup @@ -239,6 +240,8 @@ __Firefox:__ contributed by [nanocortex](https://github.com/nanocortex). You can [kushfest](https://github.com/kushfest) has created a Chrome bookmarklet for sending the currently open webpage to MeTube. Please note that if you're on an HTTPS page, your MeTube instance must be configured with `HTTPS` as `true` in the environment, or be behind an HTTPS reverse proxy (see below) for the bookmarklet to work. +Since bookmarklets run in the context of the current page (e.g. youtube.com), the requests they make to MeTube are cross-origin. You must add the origins of sites where you use the bookmarklet to the __CORS_ALLOWED_ORIGINS__ environment variable, otherwise the browser will block the requests. For example, to use the bookmarklet on YouTube and Vimeo: `CORS_ALLOWED_ORIGINS=https://www.youtube.com,https://www.vimeo.com`. + GitHub doesn't allow embedding JavaScript as a link, so the bookmarklet has to be created manually by copying the following code to a new bookmark you create on your bookmarks bar. Change the hostname in the URL below to point to your MeTube instance. ```javascript From 06c4a2c4a84dec3562f576a062de101ff51054e1 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 10 Apr 2026 08:38:32 +0300 Subject: [PATCH 22/88] update documentation --- AGENTS.md | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 34 ++++++++----------------------- 2 files changed, 68 insertions(+), 26 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..35bae0d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,60 @@ +# Agent Guidelines + +## README.md size constraint + +The README.md is synced to Docker Hub, which has a **25,000 character limit**. +Any change to README.md **must** keep the file under 25,000 characters (`wc -c README.md`). +If an addition would exceed the limit, trim existing prose elsewhere β€” prefer tightening verbose descriptions over removing sections. + +## Tech stack + +- **Backend:** Python 3.13+, aiohttp, python-socketio 5.x, yt-dlp +- **Frontend:** Angular 21, TypeScript, Bootstrap 5, SASS, ngx-socket-io +- **Package managers:** uv (Python), pnpm (frontend) +- **Container:** Multi-stage Docker (Node builder + Python runtime), multi-arch (amd64/arm64) + +## Build & test commands + +```bash +# Frontend (run from ui/) +pnpm install --frozen-lockfile +pnpm run lint +pnpm run build +pnpm exec ng test --watch=false + +# Backend (run from repo root) +uv sync --frozen --group dev +python -m compileall app +uv run pytest app/tests/ +``` + +All of these run in CI (`.github/workflows/main.yml`) on every push to master and must pass. + +## Code style + +Follow `.editorconfig`: +- Python: 4-space indent +- Everything else (TypeScript, YAML, JSON, HTML): 2-space indent +- UTF-8, LF line endings, trim trailing whitespace, final newline + +Frontend additionally uses ESLint (`ui/eslint.config.js`) and Prettier (config in `ui/package.json`: `printWidth=100`, `singleQuote=true`). + +## Project structure + +``` +app/main.py β€” HTTP server, Socket.IO events, REST API routes, Config class +app/ytdl.py β€” Download queue logic, yt-dlp integration +app/subscriptions.py β€” Channel/playlist subscription manager +app/state_store.py β€” JSON-based persistent storage with atomic writes +app/dl_formats.py β€” Video/audio codec/quality mapping +app/tests/ β€” pytest tests (asyncio_mode=auto) +ui/src/app/ β€” Angular standalone components (no NgModules) +``` + +## Key conventions + +- Backend configuration lives in the `Config` class in `app/main.py` with env-var defaults in `_DEFAULTS`. New env vars go there. +- Real-time communication uses Socket.IO events, not REST polling. +- Frontend uses standalone Angular components with `inject()` for DI, RxJS Subjects for state, and `takeUntilDestroyed()` for cleanup. +- State is persisted as JSON files via `AtomicJsonStore` in `app/state_store.py`. +- No pre-commit hooks β€” linting and tests are enforced in CI only. diff --git a/README.md b/README.md index e5e3e8c..0e1c864 100644 --- a/README.md +++ b/README.md @@ -226,7 +226,7 @@ In case you need to use your browser's cookies with MeTube, for example to downl ## πŸ”Œ Browser extensions -Browser extensions allow right-clicking videos and sending them directly to MeTube. Please note that if you're on an HTTPS page, your MeTube instance must be behind an HTTPS reverse proxy (see below) for the extensions to work. +Browser extensions allow right-clicking videos and sending them directly to MeTube. If you're on an HTTPS page, your MeTube instance must be behind an HTTPS reverse proxy (see below) for extensions to work. __Chrome:__ contributed by [Rpsl](https://github.com/rpsl). You can install it from [Google Chrome Webstore](https://chrome.google.com/webstore/detail/metube-downloader/fbmkmdnlhacefjljljlbhkodfmfkijdh) or use developer mode and install [from sources](https://github.com/Rpsl/metube-browser-extension). @@ -254,23 +254,15 @@ javascript:!function(){xhr=new XMLHttpRequest();xhr.open("POST","https://metube. javascript:(function(){xhr=new XMLHttpRequest();xhr.open("POST","https://metube.domain.com/add");xhr.send(JSON.stringify({"url":document.location.href,"quality":"best"}));xhr.onload=function(){if(xhr.status==200){alert("Sent to metube!")}else{alert("Send to metube failed. Check the javascript console for clues.")}}})(); ``` -The above bookmarklets use `alert()` as a success/failure notification. The following will show a toast message instead: - -Chrome: +The above bookmarklets use `alert()` for notifications. This variant shows a toast instead (Chrome β€” for Firefox, replace the `!function(){...}()` wrapper with `(function(){...})()`): ```javascript javascript:!function(){function notify(msg) {var sc = document.scrollingElement.scrollTop; var text = document.createElement('span');text.innerHTML=msg;var ts = text.style;ts.all = 'revert';ts.color = '#000';ts.fontFamily = 'Verdana, sans-serif';ts.fontSize = '15px';ts.backgroundColor = 'white';ts.padding = '15px';ts.border = '1px solid gainsboro';ts.boxShadow = '3px 3px 10px';ts.zIndex = '100';document.body.appendChild(text);ts.position = 'absolute'; ts.top = 50 + sc + 'px'; ts.left = (window.innerWidth / 2)-(text.offsetWidth / 2) + 'px'; setTimeout(function () { text.style.visibility = "hidden"; }, 1500);}xhr=new XMLHttpRequest();xhr.open("POST","https://metube.domain.com/add");xhr.send(JSON.stringify({"url":document.location.href,"quality":"best"}));xhr.onload=function() { if(xhr.status==200){notify("Sent to metube!")}else {notify("Send to metube failed. Check the javascript console for clues.")}}}(); ``` -Firefox: - -```javascript -javascript:(function(){function notify(msg) {var sc = document.scrollingElement.scrollTop; var text = document.createElement('span');text.innerHTML=msg;var ts = text.style;ts.all = 'revert';ts.color = '#000';ts.fontFamily = 'Verdana, sans-serif';ts.fontSize = '15px';ts.backgroundColor = 'white';ts.padding = '15px';ts.border = '1px solid gainsboro';ts.boxShadow = '3px 3px 10px';ts.zIndex = '100';document.body.appendChild(text);ts.position = 'absolute'; ts.top = 50 + sc + 'px'; ts.left = (window.innerWidth / 2)-(text.offsetWidth / 2) + 'px'; setTimeout(function () { text.style.visibility = "hidden"; }, 1500);}xhr=new XMLHttpRequest();xhr.open("POST","https://metube.domain.com/add");xhr.send(JSON.stringify({"url":document.location.href,"quality":"best"}));xhr.onload=function() { if(xhr.status==200){notify("Sent to metube!")}else {notify("Send to metube failed. Check the javascript console for clues.")}}})(); -``` - ## ⚑ Raycast extension -[dotvhs](https://github.com/dotvhs) has created an [extension for Raycast](https://www.raycast.com/dot/metube) that allows adding videos to MeTube directly from Raycast. +[dotvhs](https://github.com/dotvhs) has created an [extension for Raycast](https://www.raycast.com/dot/metube) for adding videos to MeTube directly from Raycast. ## πŸ”’ HTTPS support, and running behind a reverse proxy @@ -294,11 +286,9 @@ services: - KEYFILE=/ssl/key.pem ``` -It's also possible to run MeTube behind a reverse proxy, in order to support authentication. HTTPS support can also be added in this way. +MeTube can also run behind a reverse proxy for HTTPS termination or authentication. When serving under a subdirectory, set `URL_PREFIX` accordingly. -When running behind a reverse proxy which remaps the URL (i.e. serves MeTube under a subdirectory and not under root), don't forget to set the URL_PREFIX environment variable to the correct value. - -If you're using the [linuxserver/swag](https://docs.linuxserver.io/general/swag) image for your reverse proxying needs (which I can heartily recommend), it already includes ready snippets for proxying MeTube both in [subfolder](https://github.com/linuxserver/reverse-proxy-confs/blob/master/metube.subfolder.conf.sample) and [subdomain](https://github.com/linuxserver/reverse-proxy-confs/blob/master/metube.subdomain.conf.sample) modes under the `nginx/proxy-confs` directory in the configuration volume. It also includes Authelia which can be used for authentication. +The [linuxserver/swag](https://docs.linuxserver.io/general/swag) image includes ready-made snippets for MeTube in [subfolder](https://github.com/linuxserver/reverse-proxy-confs/blob/master/metube.subfolder.conf.sample) and [subdomain](https://github.com/linuxserver/reverse-proxy-confs/blob/master/metube.subdomain.conf.sample) modes, plus Authelia for authentication. ### 🌐 NGINX @@ -350,28 +340,20 @@ example.com { ## πŸ”„ Updating yt-dlp -The engine which powers the actual video downloads in MeTube is [yt-dlp](https://github.com/yt-dlp/yt-dlp). Since video sites regularly change their layouts, frequent updates of yt-dlp are required to keep up. - -There's an automatic nightly build of MeTube which looks for a new version of yt-dlp, and if one exists, the build pulls it and publishes an updated docker image. Therefore, in order to keep up with the changes, it's recommended that you update your MeTube container regularly with the latest image. - -I recommend installing and setting up [watchtower](https://github.com/nicholas-fedor/watchtower) for this purpose. +MeTube is powered by [yt-dlp](https://github.com/yt-dlp/yt-dlp), which requires frequent updates as video sites change their layouts. A nightly build automatically publishes a new Docker image whenever a new yt-dlp version is available, so keep your container up to date β€” [watchtower](https://github.com/nicholas-fedor/watchtower) works well for this. ## πŸ”§ Troubleshooting and submitting issues -Before asking a question or submitting an issue for MeTube, please remember that MeTube is only a UI for [yt-dlp](https://github.com/yt-dlp/yt-dlp). Any issues you might be experiencing with authentication to video websites, postprocessing, permissions, other `YTDL_OPTIONS` configurations which seem not to work, or anything else that concerns the workings of the underlying yt-dlp library, need not be opened on the MeTube project. In order to debug and troubleshoot them, it's advised to try using the yt-dlp binary directly first, bypassing the UI, and once that is working, importing the options that worked for you into `YTDL_OPTIONS`. - -In order to test with the yt-dlp command directly, you can either download it and run it locally, or for a better simulation of its actual conditions, you can run it within the MeTube container itself. Assuming your MeTube container is called `metube`, run the following on your Docker host to get a shell inside the container: +MeTube is only a UI for [yt-dlp](https://github.com/yt-dlp/yt-dlp). Issues with authentication, postprocessing, permissions, or `YTDL_OPTIONS` should be debugged with yt-dlp directly first β€” once working, import those options into MeTube. To test inside the container: ```bash docker exec -ti metube sh cd /downloads ``` -Once there, you can use the yt-dlp command freely. - ## πŸ’‘ Submitting feature requests -MeTube development relies on code contributions by the community. The program as it currently stands fits my own use cases, and is therefore feature-complete as far as I'm concerned. If your use cases are different and require additional features, please feel free to submit PRs that implement those features. It's advisable to create an issue first to discuss the planned implementation, because in an effort to reduce bloat, some PRs may not be accepted. However, note that opening a feature request when you don't intend to implement the feature will rarely result in the request being fulfilled. +MeTube development relies on community contributions. If you need additional features, please submit a PR. Create an issue first to discuss the implementation β€” some PRs may not be accepted to reduce bloat. Feature requests without an accompanying PR are unlikely to be fulfilled. ## πŸ› οΈ Building and running locally From 4330d3b6c61c2a6bd5427b5796b6288881070ea6 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 10 Apr 2026 14:06:08 +0300 Subject: [PATCH 23/88] fix yt-dlp options examples --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0e1c864..6c5c7f4 100644 --- a/README.md +++ b/README.md @@ -106,9 +106,9 @@ When a download starts, these layers are combined in order. If the same option a ### Option format -yt-dlp options in MeTube are expressed as JSON objects. The keys are yt-dlp API option names, which roughly correspond to command-line flags with dashes replaced by underscores. For example, the command-line flag `--embed-thumbnail` becomes `"embed_thumbnail": true` in JSON. +yt-dlp options in MeTube are expressed as JSON objects. The keys are yt-dlp API option names, which roughly correspond to command-line flags with dashes replaced by underscores. For example, the command-line flag `--write-subs` becomes `"writesubtitles": true` in JSON. -> **Tip:** Some command-line flags don't have a direct single-key equivalent β€” for instance, `--recode-video` must be expressed via `"postprocessors"`. A full list of available API options can be found [in the yt-dlp source](https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/YoutubeDL.py#L224), and [this conversion script](https://github.com/yt-dlp/yt-dlp/blob/master/devscripts/cli_to_api.py) can help translate command-line flags to their API equivalents. +> **Tip:** Some command-line flags don't have a direct single-key equivalent β€” for instance, `--embed-thumbnail` and `--recode-video` must be expressed via `"postprocessors"`. A full list of available API options can be found [in the yt-dlp source](https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/YoutubeDL.py#L224), and [this conversion script](https://github.com/yt-dlp/yt-dlp/blob/master/devscripts/cli_to_api.py) can help translate command-line flags to their API equivalents. ### Global options @@ -118,7 +118,7 @@ Global options form the baseline for every download. There are two ways to defin ```yaml environment: - - 'YTDL_OPTIONS={"writesubtitles": true, "subtitleslangs": ["en", "de"], "updatetime": false, "embed_thumbnail": true}' + - 'YTDL_OPTIONS={"writesubtitles": true, "subtitleslangs": ["en", "de"], "updatetime": false, "writethumbnail": true}' ``` **Via a JSON file** (`YTDL_OPTIONS_FILE`) β€” mount a file into the container and point to it: @@ -137,7 +137,7 @@ where `ytdl-options.json` contains: "writesubtitles": true, "subtitleslangs": ["en", "de"], "updatetime": false, - "embed_thumbnail": true + "writethumbnail": true } ``` From 381896901ae90120730b76ef4a557975f20ee481 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 16:12:38 +0000 Subject: [PATCH 24/88] Bump softprops/action-gh-release from 2 to 3 in the github-actions group Bumps the github-actions group with 1 update: [softprops/action-gh-release](https://github.com/softprops/action-gh-release). Updates `softprops/action-gh-release` from 2 to 3 - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v2...v3) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9ac9d87..03b9911 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -209,7 +209,7 @@ jobs: git push origin ":refs/tags/$TAG_NAME" || true fi - name: Create GitHub Release - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@v3 with: tag_name: ${{ steps.date.outputs.date }} name: Release ${{ steps.date.outputs.date }} From 210c607c53eaabd0dc6fafcff608c642791d10a8 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Sun, 12 Apr 2026 22:58:27 +0300 Subject: [PATCH 25/88] fix pnpm build --- ui/pnpm-workspace.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ui/pnpm-workspace.yaml diff --git a/ui/pnpm-workspace.yaml b/ui/pnpm-workspace.yaml new file mode 100644 index 0000000..fde66ed --- /dev/null +++ b/ui/pnpm-workspace.yaml @@ -0,0 +1,6 @@ +allowBuilds: + '@parcel/watcher': true + core-js: true + esbuild: true + lmdb: true + msgpackr-extract: true From 03f71fd2572ca2122574ccc61e8fa5a963bb76e1 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Mon, 13 Apr 2026 19:02:27 +0300 Subject: [PATCH 26/88] fix asterisk CORS_ALLOWED_ORIGINS, mentioned in #955 --- app/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/main.py b/app/main.py index 7269c8c..62084c8 100644 --- a/app/main.py +++ b/app/main.py @@ -915,7 +915,7 @@ app.router.add_route('OPTIONS', config.URL_PREFIX + 'delete-cookies', add_cors) async def on_prepare(request, response): origin = request.headers.get('Origin') - if origin and _cors_origins and origin in _cors_origins: + if origin and _cors_origins and ('*' in _cors_origins or origin in _cors_origins): response.headers['Access-Control-Allow-Origin'] = origin response.headers['Access-Control-Allow-Headers'] = 'Content-Type' From f2d71cbe2ed302ef523fdd4868f56189a231a3e9 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Mon, 13 Apr 2026 20:45:20 +0300 Subject: [PATCH 27/88] add more CORS details --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c5c7f4..ada5f84 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ Certain values can be set via environment variables, using the `-e` parameter on * __HTTPS__: Use `https` instead of `http` (__CERTFILE__ and __KEYFILE__ required). Defaults to `false`. * __CERTFILE__: HTTPS certificate file path. * __KEYFILE__: HTTPS key file path. -* __CORS_ALLOWED_ORIGINS__: Comma-separated list of origins permitted to make cross-origin requests to the MeTube API. When unset or empty, all cross-origin requests are denied. This must be configured for [bookmarklets](#-bookmarklet) and any other browser-based tools that contact MeTube from a different origin. Example: `https://www.youtube.com,https://www.vimeo.com`. +* __CORS_ALLOWED_ORIGINS__: Comma-separated list of origins permitted to make cross-origin requests to the MeTube API. When unset or empty, all cross-origin requests are denied. Set to `*` to allow all origins. This must be configured for [browser extensions](#-browser-extensions), [bookmarklets](#-bookmarklet), and any other browser-based tools that contact MeTube from a different origin. For browser extensions use `*` (see below); for bookmarklets you can list specific sites, e.g. `https://www.youtube.com,https://www.vimeo.com`. * __ROBOTS_TXT__: A path to a `robots.txt` file mounted in the container. ### 🏠 Basic Setup @@ -228,6 +228,8 @@ In case you need to use your browser's cookies with MeTube, for example to downl Browser extensions allow right-clicking videos and sending them directly to MeTube. If you're on an HTTPS page, your MeTube instance must be behind an HTTPS reverse proxy (see below) for extensions to work. +Since browser extensions make requests from their own origin (`chrome-extension://...` or `moz-extension://...`), you must set `CORS_ALLOWED_ORIGINS=*` for them to work. + __Chrome:__ contributed by [Rpsl](https://github.com/rpsl). You can install it from [Google Chrome Webstore](https://chrome.google.com/webstore/detail/metube-downloader/fbmkmdnlhacefjljljlbhkodfmfkijdh) or use developer mode and install [from sources](https://github.com/Rpsl/metube-browser-extension). __Firefox:__ contributed by [nanocortex](https://github.com/nanocortex). You can install it from [Firefox Addons](https://addons.mozilla.org/en-US/firefox/addon/metube-downloader) or get sources from [here](https://github.com/nanocortex/metube-firefox-addon). From 29ccc42409d626dfaefc1c1b3c244911308db13b Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Mon, 13 Apr 2026 20:49:10 +0300 Subject: [PATCH 28/88] don't run workflow on README changes --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 03b9911..4e24e06 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,6 +4,8 @@ on: push: branches: - 'master' + paths-ignore: + - '**.md' jobs: quality-checks: From 1a32eba4746121def6f44c042b665d48f85888b8 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Thu, 16 Apr 2026 22:06:34 +0300 Subject: [PATCH 29/88] fix PUBLIC_HOST_URL without a trailing slash (closes #959) --- app/main.py | 5 +++++ app/tests/test_config.py | 41 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/app/main.py b/app/main.py index 62084c8..283de01 100644 --- a/app/main.py +++ b/app/main.py @@ -92,6 +92,11 @@ class Config: if not self.URL_PREFIX.endswith('/'): self.URL_PREFIX += '/' + for attr in ('PUBLIC_HOST_URL', 'PUBLIC_HOST_AUDIO_URL'): + val = getattr(self, attr) + if val and not val.endswith('/'): + setattr(self, attr, val + '/') + # Convert relative addresses to absolute addresses to prevent the failure of file address comparison if self.YTDL_OPTIONS_FILE and self.YTDL_OPTIONS_FILE.startswith('.'): self.YTDL_OPTIONS_FILE = str(Path(self.YTDL_OPTIONS_FILE).resolve()) diff --git a/app/tests/test_config.py b/app/tests/test_config.py index d97c1a9..6b096bf 100644 --- a/app/tests/test_config.py +++ b/app/tests/test_config.py @@ -23,6 +23,47 @@ class ConfigTests(unittest.TestCase): c = Config() self.assertEqual(c.URL_PREFIX, "foo/") + def test_public_host_url_gets_trailing_slash(self): + with patch.dict( + os.environ, + _base_env(PUBLIC_HOST_URL="https://ytdl.example.com"), + clear=False, + ): + c = Config() + self.assertEqual(c.PUBLIC_HOST_URL, "https://ytdl.example.com/") + + def test_public_host_audio_url_gets_trailing_slash(self): + with patch.dict( + os.environ, + _base_env(PUBLIC_HOST_AUDIO_URL="https://audio.example.com"), + clear=False, + ): + c = Config() + self.assertEqual(c.PUBLIC_HOST_AUDIO_URL, "https://audio.example.com/") + + def test_public_host_url_empty_stays_empty(self): + with patch.dict( + os.environ, + _base_env(PUBLIC_HOST_URL="", PUBLIC_HOST_AUDIO_URL=""), + clear=False, + ): + c = Config() + self.assertEqual(c.PUBLIC_HOST_URL, "") + self.assertEqual(c.PUBLIC_HOST_AUDIO_URL, "") + + def test_public_host_url_already_slashed_unchanged(self): + with patch.dict( + os.environ, + _base_env( + PUBLIC_HOST_URL="https://ytdl.example.com/", + PUBLIC_HOST_AUDIO_URL="https://audio.example.com/", + ), + clear=False, + ): + c = Config() + self.assertEqual(c.PUBLIC_HOST_URL, "https://ytdl.example.com/") + self.assertEqual(c.PUBLIC_HOST_AUDIO_URL, "https://audio.example.com/") + def test_ytdl_options_json_loaded(self): opts = {"quiet": True, "no_warnings": True} with patch.dict( From ab42325db56788ffdded8187f427e228ab5e10e5 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Thu, 16 Apr 2026 22:30:42 +0300 Subject: [PATCH 30/88] upgrade dependencies --- ui/package.json | 20 +- ui/pnpm-lock.yaml | 684 +++++++++++++++++++++++----------------------- uv.lock | 20 +- 3 files changed, 362 insertions(+), 362 deletions(-) diff --git a/ui/package.json b/ui/package.json index 383dca2..8bc4c42 100644 --- a/ui/package.json +++ b/ui/package.json @@ -23,14 +23,14 @@ }, "private": true, "dependencies": { - "@angular/animations": "^21.2.8", - "@angular/common": "^21.2.8", - "@angular/compiler": "^21.2.8", - "@angular/core": "^21.2.8", - "@angular/forms": "^21.2.8", - "@angular/platform-browser": "^21.2.8", - "@angular/platform-browser-dynamic": "^21.2.8", - "@angular/service-worker": "^21.2.8", + "@angular/animations": "^21.2.9", + "@angular/common": "^21.2.9", + "@angular/compiler": "^21.2.9", + "@angular/core": "^21.2.9", + "@angular/forms": "^21.2.9", + "@angular/platform-browser": "^21.2.9", + "@angular/platform-browser-dynamic": "^21.2.9", + "@angular/service-worker": "^21.2.9", "@fortawesome/angular-fontawesome": "~4.0.0", "@fortawesome/fontawesome-svg-core": "^7.2.0", "@fortawesome/free-brands-svg-icons": "^7.2.0", @@ -50,8 +50,8 @@ "@angular-eslint/builder": "21.1.0", "@angular/build": "^21.2.7", "@angular/cli": "^21.2.7", - "@angular/compiler-cli": "^21.2.8", - "@angular/localize": "^21.2.8", + "@angular/compiler-cli": "^21.2.9", + "@angular/localize": "^21.2.9", "@eslint/js": "^9.39.4", "angular-eslint": "21.1.0", "eslint": "^9.39.4", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 4f53e48..4ddb04a 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -9,32 +9,32 @@ importers: .: dependencies: '@angular/animations': - specifier: ^21.2.8 - version: 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^21.2.9 + version: 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/common': - specifier: ^21.2.8 - version: 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^21.2.9 + version: 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@angular/compiler': - specifier: ^21.2.8 - version: 21.2.8 + specifier: ^21.2.9 + version: 21.2.9 '@angular/core': - specifier: ^21.2.8 - version: 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^21.2.9 + version: 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) '@angular/forms': - specifier: ^21.2.8 - version: 21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + specifier: ^21.2.9 + version: 21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) '@angular/platform-browser': - specifier: ^21.2.8 - version: 21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^21.2.9 + version: 21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/platform-browser-dynamic': - specifier: ^21.2.8 - version: 21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.8)(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))) + specifier: ^21.2.9 + version: 21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.9)(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))) '@angular/service-worker': - specifier: ^21.2.8 - version: 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^21.2.9 + version: 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@fortawesome/angular-fontawesome': specifier: ~4.0.0 - version: 4.0.0(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) + version: 4.0.0(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) '@fortawesome/fontawesome-svg-core': specifier: ^7.2.0 version: 7.2.0 @@ -49,10 +49,10 @@ importers: version: 7.2.0 '@ng-bootstrap/ng-bootstrap': specifier: ^20.0.0 - version: 20.0.0(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8))(@popperjs/core@2.11.8)(rxjs@7.8.2) + version: 20.0.0(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9))(@popperjs/core@2.11.8)(rxjs@7.8.2) '@ng-select/ng-select': specifier: ^21.8.0 - version: 21.8.0(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) + version: 21.8.0(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) '@popperjs/core': specifier: ^2.11.8 version: 2.11.8 @@ -61,10 +61,10 @@ importers: version: 5.3.8(@popperjs/core@2.11.8) ngx-cookie-service: specifier: ^21.3.1 - version: 21.3.1(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) + version: 21.3.1(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) ngx-socket-io: specifier: ~4.10.0 - version: 4.10.0(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + version: 4.10.0(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) rxjs: specifier: ~7.8.2 version: 7.8.2 @@ -77,25 +77,25 @@ importers: devDependencies: '@angular-eslint/builder': specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular/build': specifier: ^21.2.7 - version: 21.2.7(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8)(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(chokidar@5.0.0)(postcss@8.5.9)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.4(@types/node@25.5.2)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3))) + version: 21.2.7(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9)(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(chokidar@5.0.0)(postcss@8.5.10)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.4(@types/node@25.6.0)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3))) '@angular/cli': specifier: ^21.2.7 - version: 21.2.7(@types/node@25.5.2)(chokidar@5.0.0) + version: 21.2.7(@types/node@25.6.0)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^21.2.8 - version: 21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3) + specifier: ^21.2.9 + version: 21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3) '@angular/localize': - specifier: ^21.2.8 - version: 21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8) + specifier: ^21.2.9 + version: 21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9) '@eslint/js': specifier: ^9.39.4 version: 9.39.4 angular-eslint: specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) eslint: specifier: ^9.39.4 version: 9.39.4 @@ -110,7 +110,7 @@ importers: version: 8.47.0(eslint@9.39.4)(typescript@5.9.3) vitest: specifier: ^4.1.4 - version: 4.1.4(@types/node@25.5.2)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3)) + version: 4.1.4(@types/node@25.6.0)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3)) packages: @@ -239,11 +239,11 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '*' - '@angular/animations@21.2.8': - resolution: {integrity: sha512-RIqfVmfretQ0x/mXgMXe7Bw0Tpe8+zBV/Mm2OaNVyrmNG+9gYItEn5t/ZnQGcPD5nMNqckgp3+4/ZMc/qkS5ww==} + '@angular/animations@21.2.9': + resolution: {integrity: sha512-wOWbrneivpTYx3xhiPygoNFNC8ZZ1shpgwBe1hYvfky1fkiz1c92XeHIW1V4rPqYw6d3U55aUX5InyTsVe2Xng==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.2.8 + '@angular/core': 21.2.9 '@angular/build@21.2.7': resolution: {integrity: sha512-FpSkFqpsJtdN1cROekVYkmeV1QepdP+/d7fyYQEuNmlOlyqXSDh9qJmy4iL9VNbAU0rk+vFCtYM86rO7Pt9cSw==} @@ -296,33 +296,33 @@ packages: 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'} hasBin: true - '@angular/common@21.2.8': - resolution: {integrity: sha512-ZvgcxsLPkSG0B1jc2ZXshAWIFBoQ0U9uwIX/zG/RGcfMpoKyEDNAebli6FTIpxIlz/35rtBNV7EGPhinjPTJFQ==} + '@angular/common@21.2.9': + resolution: {integrity: sha512-7spQcF3hPN/fjTx6Pwa32KRRdO0NcixnRuPV4lo50ejtXesjiLVR+fkaX38sawAyGoq89IuuYvUDrbLwCMypmQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.2.8 + '@angular/core': 21.2.9 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@21.2.8': - resolution: {integrity: sha512-S0W+6QazCsn/4xWZu0V5VmU9zmKIlqFR2FJSsAQUPReVmpA40SuQSP6A/cyMVIMYaHvO/cAXSHJVgpxBzBSL/Q==} + '@angular/compiler-cli@21.2.9': + resolution: {integrity: sha512-hTTW/OiqTXrwTneS18CMp47OX0XSbLYl2rIomLS3nXVJniSETH6S/k+LqQtGWWgLbzsd3PzUOOckHnvzpTBTsA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.8 + '@angular/compiler': 21.2.9 typescript: '>=5.9 <6.1' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@21.2.8': - resolution: {integrity: sha512-Il9KlT6qX8rWmun5jY6wMLx56bCQZpOVIFEyHM4ai2wmxvbqyxgRFKDs4iMRNn1h04Tgupl6cKSqP9lecIvH6w==} + '@angular/compiler@21.2.9': + resolution: {integrity: sha512-clsK1EsSPtAuqlRl4CciA/gsvsW7xe0eWcvHxtrMW6DYaUJ6X4AAuDxEEJ5cf/3Mpw4s8KssjIUPPtbrUIGLSQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - '@angular/core@21.2.8': - resolution: {integrity: sha512-hI7n4t8qgFJaVV55LIaNuzcdP+/IeuqQRu3huSLo47Gf6uZAD0Acj4Ye9SC8YNmhUu5/RiImngm9NOlcI2oCJA==} + '@angular/core@21.2.9': + resolution: {integrity: sha512-uZLq2aedJ+0uEZxyf6a1Nc7y1aZ7akAW7K1Kon8JUDZOvI2IDbk0i00MzkELt8q9uSmSSqg9zNKuhjspFf0Pyw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/compiler': 21.2.8 + '@angular/compiler': 21.2.9 rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 || ~0.16.0 peerDependenciesMeta: @@ -331,49 +331,49 @@ packages: zone.js: optional: true - '@angular/forms@21.2.8': - resolution: {integrity: sha512-tyQAHjfMHcqETRkKQaZHjYqIK9W8uRenPpY2DF/Jl+S7CwcaX4T8t8TKgzvTynNzQW9QGiLg0pqVosVMKzBXJg==} + '@angular/forms@21.2.9': + resolution: {integrity: sha512-qXLnzmsJoHMgV/gDU7AZgsKBhUH7k6im6V9YuY5UpHHl+nGKCWxtePAZRB0OH2AsqzLwER3Fv2S6+mtmb7651w==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.2.8 - '@angular/core': 21.2.8 - '@angular/platform-browser': 21.2.8 + '@angular/common': 21.2.9 + '@angular/core': 21.2.9 + '@angular/platform-browser': 21.2.9 rxjs: ^6.5.3 || ^7.4.0 - '@angular/localize@21.2.8': - resolution: {integrity: sha512-wt1ZIE2c7IL1KOgfJyr7pN5SNoQXy02dB/dswZKQlJWduVWkzG83uftPFPjhfASQeqrlpW7tXhb6PiRkJGlCkg==} + '@angular/localize@21.2.9': + resolution: {integrity: sha512-xf6C1XPm4JU2KlCQR8/XdsJPPmienTVGM71bCgVRu9dT3UiJbO7UKSA8Jc4XoDio5/mZ8VYFXBA/ozlQj22+Dw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.8 - '@angular/compiler-cli': 21.2.8 + '@angular/compiler': 21.2.9 + '@angular/compiler-cli': 21.2.9 - '@angular/platform-browser-dynamic@21.2.8': - resolution: {integrity: sha512-9XeplSHsKnLDm14dvwXG00Ox6WbDrhf7ub7MxxcJ6gCgRm/yqJ3Vrz4a+NBpYnelapqiCCGEdHeyx2xt8vG1qA==} + '@angular/platform-browser-dynamic@21.2.9': + resolution: {integrity: sha512-Z+2vefW4GUSuTC4BOKNiyftqecLSjxOKwe1ZNljBsjesLzywIXi+v+tyEm8ODHHlf7bz/0HwXvc9OYZmfjt95A==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.2.8 - '@angular/compiler': 21.2.8 - '@angular/core': 21.2.8 - '@angular/platform-browser': 21.2.8 + '@angular/common': 21.2.9 + '@angular/compiler': 21.2.9 + '@angular/core': 21.2.9 + '@angular/platform-browser': 21.2.9 - '@angular/platform-browser@21.2.8': - resolution: {integrity: sha512-4fwmGf7GCuIsjFqx1gqqWC92YjlN9SmGJO17TPPsOm5zUOnDx+h3Bj9XjdXxlcBtugTb2xHk6Auqyv3lzWGlkw==} + '@angular/platform-browser@21.2.9': + resolution: {integrity: sha512-MjEtFvoFtsjsAeu2yzauqGgwwEHV4ml25c9vGFmw4OmSoNme4yp41f2DegwOkn1TTHL3OF3GE65ng2U2feJU4Q==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/animations': 21.2.8 - '@angular/common': 21.2.8 - '@angular/core': 21.2.8 + '@angular/animations': 21.2.9 + '@angular/common': 21.2.9 + '@angular/core': 21.2.9 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/service-worker@21.2.8': - resolution: {integrity: sha512-r0hE9Xm50HhIQUXfpi6Bl8Omp5uluKoj04EgY8uvK5cVO7wWlC9PJ52Gmc59K3e1Qj6pBPMX9AxykQeIlTZH+g==} + '@angular/service-worker@21.2.9': + resolution: {integrity: sha512-rAjUo9T9tV/nbcAOjFVAjn6mbHlh358/KLCKX7WzXl/fXVUB0lvlpzi+NnZbc9zHIP3IEdrg5HYsuaSIWSbiUQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/core': 21.2.8 + '@angular/core': 21.2.9 rxjs: ^6.5.3 || ^7.4.0 '@asamuzakjp/css-color@4.1.2': @@ -464,15 +464,15 @@ packages: resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} engines: {node: '>=20.19.0'} - '@csstools/css-calc@3.1.1': - resolution: {integrity: sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==} + '@csstools/css-calc@3.2.0': + resolution: {integrity: sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-color-parser@4.0.2': - resolution: {integrity: sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==} + '@csstools/css-color-parser@4.1.0': + resolution: {integrity: sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -484,8 +484,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.2': - resolution: {integrity: sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==} + '@csstools/css-syntax-patches-for-csstree@1.1.3': + resolution: {integrity: sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -740,8 +740,8 @@ packages: '@harperfast/extended-iterable@1.0.3': resolution: {integrity: sha512-sSAYhQca3rDWtQUHSAPeO7axFIUJOI6hn1gjRC5APVE1a90tuyT8f5WIgRsFhhWA7htNkju2veB9eWL6YHi/Lw==} - '@hono/node-server@1.19.13': - resolution: {integrity: sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==} + '@hono/node-server@1.19.14': + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 @@ -900,8 +900,8 @@ packages: resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} engines: {node: '>=18.0.0'} - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + '@istanbuljs/schema@0.1.6': + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} engines: {node: '>=8'} '@jridgewell/gen-mapping@0.3.13': @@ -1115,8 +1115,8 @@ packages: resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} engines: {node: '>= 10'} - '@napi-rs/wasm-runtime@1.1.3': - resolution: {integrity: sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==} + '@napi-rs/wasm-runtime@1.1.4': + resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 @@ -1579,8 +1579,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@25.5.2': - resolution: {integrity: sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==} + '@types/node@25.6.0': + resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -1606,8 +1606,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.58.1': - resolution: {integrity: sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==} + '@typescript-eslint/project-service@8.58.2': + resolution: {integrity: sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1616,8 +1616,8 @@ packages: resolution: {integrity: sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.58.1': - resolution: {integrity: sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==} + '@typescript-eslint/scope-manager@8.58.2': + resolution: {integrity: sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.47.0': @@ -1626,8 +1626,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/tsconfig-utils@8.58.1': - resolution: {integrity: sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==} + '@typescript-eslint/tsconfig-utils@8.58.2': + resolution: {integrity: sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1643,8 +1643,8 @@ packages: resolution: {integrity: sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.58.1': - resolution: {integrity: sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==} + '@typescript-eslint/types@8.58.2': + resolution: {integrity: sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.47.0': @@ -1653,8 +1653,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.58.1': - resolution: {integrity: sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==} + '@typescript-eslint/typescript-estree@8.58.2': + resolution: {integrity: sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1666,8 +1666,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.58.1': - resolution: {integrity: sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==} + '@typescript-eslint/utils@8.58.2': + resolution: {integrity: sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -1677,8 +1677,8 @@ packages: resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.58.1': - resolution: {integrity: sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==} + '@typescript-eslint/visitor-keys@8.58.2': + resolution: {integrity: sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-basic-ssl@2.1.4': @@ -1817,8 +1817,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.10.17: - resolution: {integrity: sha512-HdrkN8eVG2CXxeifv/VdJ4A4RSra1DTW8dc/hdxzhGHN8QePs6gKaWM9pHPcpCoxYZJuOZ8drHmbdpLHjCYjLA==} + baseline-browser-mapping@2.10.19: + resolution: {integrity: sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==} engines: {node: '>=6.0.0'} hasBin: true @@ -1841,11 +1841,11 @@ packages: peerDependencies: '@popperjs/core': ^2.11.8 - brace-expansion@1.1.13: - resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==} + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} - brace-expansion@2.0.3: - resolution: {integrity: sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==} + brace-expansion@2.1.0: + resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} brace-expansion@5.0.5: resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} @@ -1883,8 +1883,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001787: - resolution: {integrity: sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==} + caniuse-lite@1.0.30001788: + resolution: {integrity: sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -2041,8 +2041,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.334: - resolution: {integrity: sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog==} + electron-to-chromium@1.5.339: + resolution: {integrity: sha512-Is+0BBHJ4NrdpAYiperrmp53pLywG/yV/6lIMTAnhxvzj/Cmn5Q/ogSHC6AKe7X+8kPLxxFk0cs5oc/3j/fxIg==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2336,8 +2336,8 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hono@4.12.12: - resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==} + hono@4.12.14: + resolution: {integrity: sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==} engines: {node: '>=16.9.0'} hosted-git-info@9.0.2: @@ -2542,8 +2542,8 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} - lru-cache@11.3.3: - resolution: {integrity: sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==} + lru-cache@11.3.5: + resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -2856,8 +2856,8 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss@8.5.9: - resolution: {integrity: sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==} + postcss@8.5.10: + resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -3091,11 +3091,11 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@4.0.0: - resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} - stdin-discarder@0.3.1: - resolution: {integrity: sha512-reExS1kSGoElkextOcPkel4NE99S0BWxjUHQeDFnR8S993JxpPX7KU4MNmO19NXhlJp+8dmdCbKQVNgLJh2teA==} + stdin-discarder@0.3.2: + resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} engines: {node: '>=18'} string-width@4.2.3: @@ -3208,8 +3208,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - undici-types@7.18.2: - resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici-types@7.19.2: + resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} undici@7.24.4: resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==} @@ -3575,11 +3575,11 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-eslint/builder@21.1.0(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/builder@21.1.0(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-devkit/architect': 0.2102.7(chokidar@5.0.0) '@angular-devkit/core': 21.2.7(chokidar@5.0.0) - '@angular/cli': 21.2.7(@types/node@25.5.2)(chokidar@5.0.0) + '@angular/cli': 21.2.7(@types/node@25.6.0)(chokidar@5.0.0) eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: @@ -3587,34 +3587,34 @@ snapshots: '@angular-eslint/bundled-angular-compiler@21.1.0': {} - '@angular-eslint/eslint-plugin-template@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.58.1)(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin-template@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.58.2)(@typescript-eslint/utils@8.58.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/utils': 8.58.1(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.58.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/utils': 8.58.2(eslint@9.39.4)(typescript@5.9.3) aria-query: 5.3.2 axobject-query: 4.1.0 eslint: 9.39.4 typescript: 5.9.3 - '@angular-eslint/eslint-plugin@21.1.0(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin@21.1.0(@typescript-eslint/utils@8.58.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/utils': 8.58.1(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.58.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.58.2(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 - '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(@typescript-eslint/types@8.58.1)(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(@typescript-eslint/types@8.58.2)(@typescript-eslint/utils@8.58.2(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-devkit/core': 21.2.7(chokidar@5.0.0) '@angular-devkit/schematics': 21.2.7(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.58.1)(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.7(@types/node@25.5.2)(chokidar@5.0.0) + '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.58.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.58.2)(@typescript-eslint/utils@8.58.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular/cli': 21.2.7(@types/node@25.6.0)(chokidar@5.0.0) ignore: 7.0.5 semver: 7.7.3 strip-json-comments: 3.1.1 @@ -3633,29 +3633,29 @@ snapshots: eslint-scope: 9.1.2 typescript: 5.9.3 - '@angular-eslint/utils@21.1.0(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/utils@21.1.0(@typescript-eslint/utils@8.58.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@typescript-eslint/utils': 8.58.1(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.58.2(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 - '@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - '@angular/build@21.2.7(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8)(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.5.2)(chokidar@5.0.0)(postcss@8.5.9)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.4(@types/node@25.5.2)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3)))': + '@angular/build@21.2.7(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9)(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(chokidar@5.0.0)(postcss@8.5.10)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.4(@types/node@25.6.0)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3)))': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2102.7(chokidar@5.0.0) - '@angular/compiler': 21.2.8 - '@angular/compiler-cli': 21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3) + '@angular/compiler': 21.2.9 + '@angular/compiler-cli': 21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3) '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@25.5.2) - '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3)) + '@inquirer/confirm': 5.1.21(@types/node@25.6.0) + '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3)) beasties: 0.4.1 browserslist: 4.28.2 esbuild: 0.27.3 @@ -3676,16 +3676,16 @@ snapshots: tslib: 2.8.1 typescript: 5.9.3 undici: 7.24.4 - vite: 7.3.2(@types/node@25.5.2)(sass@1.97.3) + vite: 7.3.2(@types/node@25.6.0)(sass@1.97.3) watchpack: 2.5.1 optionalDependencies: - '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/localize': 21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8) - '@angular/platform-browser': 21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/localize': 21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9) + '@angular/platform-browser': 21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/service-worker': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) lmdb: 3.5.1 - postcss: 8.5.9 - vitest: 4.1.4(@types/node@25.5.2)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3)) + postcss: 8.5.10 + vitest: 4.1.4(@types/node@25.6.0)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3)) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -3701,13 +3701,13 @@ snapshots: - tsx - yaml - '@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0)': + '@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0)': dependencies: '@angular-devkit/architect': 0.2102.7(chokidar@5.0.0) '@angular-devkit/core': 21.2.7(chokidar@5.0.0) '@angular-devkit/schematics': 21.2.7(chokidar@5.0.0) - '@inquirer/prompts': 7.10.1(@types/node@25.5.2) - '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.5.2))(@types/node@25.5.2)(listr2@9.0.5) + '@inquirer/prompts': 7.10.1(@types/node@25.6.0) + '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.6.0))(@types/node@25.6.0)(listr2@9.0.5) '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) '@schematics/angular': 21.2.7(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 @@ -3727,15 +3727,15 @@ snapshots: - chokidar - supports-color - '@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3)': + '@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3)': dependencies: - '@angular/compiler': 21.2.8 + '@angular/compiler': 21.2.9 '@babel/core': 7.29.0 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 5.0.0 @@ -3749,31 +3749,31 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/compiler@21.2.8': + '@angular/compiler@21.2.9': dependencies: tslib: 2.8.1 - '@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)': + '@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 21.2.8 + '@angular/compiler': 21.2.9 zone.js: 0.15.0 - '@angular/forms@21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': + '@angular/forms@21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) '@standard-schema/spec': 1.1.0 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/localize@21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8)': + '@angular/localize@21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9)': dependencies: - '@angular/compiler': 21.2.8 - '@angular/compiler-cli': 21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3) + '@angular/compiler': 21.2.9 + '@angular/compiler-cli': 21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3) '@babel/core': 7.29.0 '@types/babel__core': 7.20.5 tinyglobby: 0.2.16 @@ -3781,35 +3781,35 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/platform-browser-dynamic@21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.8)(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))': + '@angular/platform-browser-dynamic@21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.9)(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))': dependencies: - '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/compiler': 21.2.8 - '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/compiler': 21.2.9 + '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) tslib: 2.8.1 - '@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 optionalDependencies: - '@angular/animations': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/animations': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/service-worker@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 '@asamuzakjp/css-color@4.1.2': dependencies: - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - lru-cache: 11.3.3 + lru-cache: 11.3.5 '@asamuzakjp/dom-selector@6.8.1': dependencies: @@ -3817,7 +3817,7 @@ snapshots: bidi-js: 1.0.3 css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 - lru-cache: 11.3.3 + lru-cache: 11.3.5 '@asamuzakjp/nwsapi@2.3.9': {} @@ -3931,15 +3931,15 @@ snapshots: '@csstools/color-helpers@6.0.2': {} - '@csstools/css-calc@3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-calc@3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-color-parser@4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/color-helpers': 6.0.2 - '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -3947,7 +3947,7 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.2(css-tree@3.2.1)': + '@csstools/css-syntax-patches-for-csstree@1.1.3(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 @@ -4095,9 +4095,9 @@ snapshots: '@exodus/bytes@1.15.0': {} - '@fortawesome/angular-fontawesome@4.0.0(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))': + '@fortawesome/angular-fontawesome@4.0.0(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) '@fortawesome/fontawesome-svg-core': 7.2.0 tslib: 2.8.1 @@ -4124,9 +4124,9 @@ snapshots: '@harperfast/extended-iterable@1.0.3': optional: true - '@hono/node-server@1.19.13(hono@4.12.12)': + '@hono/node-server@1.19.14(hono@4.12.14)': dependencies: - hono: 4.12.12 + hono: 4.12.14 '@humanfs/core@0.19.1': {} @@ -4141,134 +4141,134 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.2(@types/node@25.5.2)': + '@inquirer/checkbox@4.3.2(@types/node@25.6.0)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.5.2) + '@inquirer/core': 10.3.2(@types/node@25.6.0) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.6.0) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 - '@inquirer/confirm@5.1.21(@types/node@25.5.2)': + '@inquirer/confirm@5.1.21(@types/node@25.6.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.2) - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/core': 10.3.2(@types/node@25.6.0) + '@inquirer/type': 3.0.10(@types/node@25.6.0) optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 - '@inquirer/core@10.3.2(@types/node@25.5.2)': + '@inquirer/core@10.3.2(@types/node@25.6.0)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.6.0) 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 optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 - '@inquirer/editor@4.2.23(@types/node@25.5.2)': + '@inquirer/editor@4.2.23(@types/node@25.6.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.2) - '@inquirer/external-editor': 1.0.3(@types/node@25.5.2) - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/core': 10.3.2(@types/node@25.6.0) + '@inquirer/external-editor': 1.0.3(@types/node@25.6.0) + '@inquirer/type': 3.0.10(@types/node@25.6.0) optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 - '@inquirer/expand@4.0.23(@types/node@25.5.2)': + '@inquirer/expand@4.0.23(@types/node@25.6.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.2) - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/core': 10.3.2(@types/node@25.6.0) + '@inquirer/type': 3.0.10(@types/node@25.6.0) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 - '@inquirer/external-editor@1.0.3(@types/node@25.5.2)': + '@inquirer/external-editor@1.0.3(@types/node@25.6.0)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.3.1(@types/node@25.5.2)': + '@inquirer/input@4.3.1(@types/node@25.6.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.2) - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/core': 10.3.2(@types/node@25.6.0) + '@inquirer/type': 3.0.10(@types/node@25.6.0) optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 - '@inquirer/number@3.0.23(@types/node@25.5.2)': + '@inquirer/number@3.0.23(@types/node@25.6.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.2) - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/core': 10.3.2(@types/node@25.6.0) + '@inquirer/type': 3.0.10(@types/node@25.6.0) optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 - '@inquirer/password@4.0.23(@types/node@25.5.2)': + '@inquirer/password@4.0.23(@types/node@25.6.0)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.5.2) - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/core': 10.3.2(@types/node@25.6.0) + '@inquirer/type': 3.0.10(@types/node@25.6.0) optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 - '@inquirer/prompts@7.10.1(@types/node@25.5.2)': + '@inquirer/prompts@7.10.1(@types/node@25.6.0)': dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@25.5.2) - '@inquirer/confirm': 5.1.21(@types/node@25.5.2) - '@inquirer/editor': 4.2.23(@types/node@25.5.2) - '@inquirer/expand': 4.0.23(@types/node@25.5.2) - '@inquirer/input': 4.3.1(@types/node@25.5.2) - '@inquirer/number': 3.0.23(@types/node@25.5.2) - '@inquirer/password': 4.0.23(@types/node@25.5.2) - '@inquirer/rawlist': 4.1.11(@types/node@25.5.2) - '@inquirer/search': 3.2.2(@types/node@25.5.2) - '@inquirer/select': 4.4.2(@types/node@25.5.2) + '@inquirer/checkbox': 4.3.2(@types/node@25.6.0) + '@inquirer/confirm': 5.1.21(@types/node@25.6.0) + '@inquirer/editor': 4.2.23(@types/node@25.6.0) + '@inquirer/expand': 4.0.23(@types/node@25.6.0) + '@inquirer/input': 4.3.1(@types/node@25.6.0) + '@inquirer/number': 3.0.23(@types/node@25.6.0) + '@inquirer/password': 4.0.23(@types/node@25.6.0) + '@inquirer/rawlist': 4.1.11(@types/node@25.6.0) + '@inquirer/search': 3.2.2(@types/node@25.6.0) + '@inquirer/select': 4.4.2(@types/node@25.6.0) optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 - '@inquirer/rawlist@4.1.11(@types/node@25.5.2)': + '@inquirer/rawlist@4.1.11(@types/node@25.6.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.2) - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/core': 10.3.2(@types/node@25.6.0) + '@inquirer/type': 3.0.10(@types/node@25.6.0) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 - '@inquirer/search@3.2.2(@types/node@25.5.2)': + '@inquirer/search@3.2.2(@types/node@25.6.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.2) + '@inquirer/core': 10.3.2(@types/node@25.6.0) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.6.0) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 - '@inquirer/select@4.4.2(@types/node@25.5.2)': + '@inquirer/select@4.4.2(@types/node@25.6.0)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.5.2) + '@inquirer/core': 10.3.2(@types/node@25.6.0) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.6.0) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 - '@inquirer/type@3.0.10(@types/node@25.5.2)': + '@inquirer/type@3.0.10(@types/node@25.6.0)': optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 '@isaacs/fs-minipass@4.0.1': dependencies: minipass: 7.1.3 - '@istanbuljs/schema@0.1.3': {} + '@istanbuljs/schema@0.1.6': {} '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -4289,10 +4289,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@25.5.2))(@types/node@25.5.2)(listr2@9.0.5)': + '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@25.6.0))(@types/node@25.6.0)(listr2@9.0.5)': dependencies: - '@inquirer/prompts': 7.10.1(@types/node@25.5.2) - '@inquirer/type': 3.0.10(@types/node@25.5.2) + '@inquirer/prompts': 7.10.1(@types/node@25.6.0) + '@inquirer/type': 3.0.10(@types/node@25.6.0) listr2: 9.0.5 transitivePeerDependencies: - '@types/node' @@ -4320,7 +4320,7 @@ snapshots: '@modelcontextprotocol/sdk@1.26.0(zod@4.3.6)': dependencies: - '@hono/node-server': 1.19.13(hono@4.12.12) + '@hono/node-server': 1.19.14(hono@4.12.14) ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 @@ -4330,7 +4330,7 @@ snapshots: eventsource-parser: 3.0.6 express: 5.2.1 express-rate-limit: 8.3.2(express@5.2.1) - hono: 4.12.12 + hono: 4.12.14 jose: 6.2.2 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -4430,28 +4430,28 @@ snapshots: '@napi-rs/nice-win32-x64-msvc': 1.1.1 optional: true - '@napi-rs/wasm-runtime@1.1.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)': + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)': dependencies: '@emnapi/core': 1.9.1 '@emnapi/runtime': 1.9.1 '@tybys/wasm-util': 0.10.1 optional: true - '@ng-bootstrap/ng-bootstrap@20.0.0(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8))(@popperjs/core@2.11.8)(rxjs@7.8.2)': + '@ng-bootstrap/ng-bootstrap@20.0.0(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9))(@popperjs/core@2.11.8)(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) - '@angular/localize': 21.2.8(@angular/compiler-cli@21.2.8(@angular/compiler@21.2.8)(typescript@5.9.3))(@angular/compiler@21.2.8) + '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/localize': 21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9) '@popperjs/core': 2.11.8 rxjs: 7.8.2 tslib: 2.8.1 - '@ng-select/ng-select@21.8.0(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': + '@ng-select/ng-select@21.8.0(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': dependencies: - '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 21.2.8(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.8(@angular/animations@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) tslib: 2.8.1 '@nodelib/fs.scandir@2.1.5': @@ -4471,7 +4471,7 @@ snapshots: agent-base: 7.1.4 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 - lru-cache: 11.3.3 + lru-cache: 11.3.5 socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color @@ -4485,7 +4485,7 @@ snapshots: '@gar/promise-retry': 1.0.3 '@npmcli/promise-spawn': 9.0.1 ini: 6.0.0 - lru-cache: 11.3.3 + lru-cache: 11.3.5 npm-pick-manifest: 11.0.3 proc-log: 6.1.0 semver: 7.7.4 @@ -4621,7 +4621,7 @@ snapshots: '@rolldown/binding-wasm32-wasi@1.0.0-rc.4(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.3(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1) + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -4794,7 +4794,7 @@ snapshots: '@types/cors@2.8.19': dependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 '@types/deep-eql@4.0.2': {} @@ -4804,13 +4804,13 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@25.5.2': + '@types/node@25.6.0': dependencies: - undici-types: 7.18.2 + undici-types: 7.19.2 '@types/ws@8.18.1': dependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 '@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: @@ -4850,10 +4850,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.58.1(typescript@5.9.3)': + '@typescript-eslint/project-service@8.58.2(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@5.9.3) - '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/tsconfig-utils': 8.58.2(typescript@5.9.3) + '@typescript-eslint/types': 8.58.2 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -4864,16 +4864,16 @@ snapshots: '@typescript-eslint/types': 8.47.0 '@typescript-eslint/visitor-keys': 8.47.0 - '@typescript-eslint/scope-manager@8.58.1': + '@typescript-eslint/scope-manager@8.58.2': dependencies: - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/visitor-keys': 8.58.1 + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/visitor-keys': 8.58.2 '@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.58.1(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.58.2(typescript@5.9.3)': dependencies: typescript: 5.9.3 @@ -4891,7 +4891,7 @@ snapshots: '@typescript-eslint/types@8.47.0': {} - '@typescript-eslint/types@8.58.1': {} + '@typescript-eslint/types@8.58.2': {} '@typescript-eslint/typescript-estree@8.47.0(typescript@5.9.3)': dependencies: @@ -4909,12 +4909,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.58.1(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.58.2(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.58.1(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@5.9.3) - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/visitor-keys': 8.58.1 + '@typescript-eslint/project-service': 8.58.2(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.58.2(typescript@5.9.3) + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/visitor-keys': 8.58.2 debug: 4.4.3 minimatch: 10.2.5 semver: 7.7.4 @@ -4935,12 +4935,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3)': + '@typescript-eslint/utils@8.58.2(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) - '@typescript-eslint/scope-manager': 8.58.1 - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.58.2 + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/typescript-estree': 8.58.2(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: @@ -4951,14 +4951,14 @@ snapshots: '@typescript-eslint/types': 8.47.0 eslint-visitor-keys: 4.2.1 - '@typescript-eslint/visitor-keys@8.58.1': + '@typescript-eslint/visitor-keys@8.58.2': dependencies: - '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/types': 8.58.2 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3))': + '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3))': dependencies: - vite: 7.3.2(@types/node@25.5.2)(sass@1.97.3) + vite: 7.3.2(@types/node@25.6.0)(sass@1.97.3) '@vitest/expect@4.1.4': dependencies: @@ -4969,13 +4969,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.4(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3))': + '@vitest/mocker@4.1.4(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3))': dependencies: '@vitest/spy': 4.1.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.2(@types/node@25.5.2)(sass@1.97.3) + vite: 7.3.2(@types/node@25.6.0)(sass@1.97.3) '@vitest/pretty-format@4.1.4': dependencies: @@ -5058,18 +5058,18 @@ snapshots: '@algolia/requester-fetch': 5.48.1 '@algolia/requester-node-http': 5.48.1 - angular-eslint@21.1.0(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): + angular-eslint@21.1.0(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): dependencies: '@angular-devkit/core': 21.2.7(chokidar@5.0.0) '@angular-devkit/schematics': 21.2.7(chokidar@5.0.0) - '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.58.1)(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.7(@types/node@25.5.2)(chokidar@5.0.0))(@typescript-eslint/types@8.58.1)(@typescript-eslint/utils@8.58.1(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.58.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.58.2)(@typescript-eslint/utils@8.58.2(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(@typescript-eslint/types@8.58.2)(@typescript-eslint/utils@8.58.2(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.7(@types/node@25.5.2)(chokidar@5.0.0) - '@typescript-eslint/types': 8.58.1 - '@typescript-eslint/utils': 8.58.1(eslint@9.39.4)(typescript@5.9.3) + '@angular/cli': 21.2.7(@types/node@25.6.0)(chokidar@5.0.0) + '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/utils': 8.58.2(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 typescript-eslint: 8.47.0(eslint@9.39.4)(typescript@5.9.3) @@ -5105,7 +5105,7 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.10.17: {} + baseline-browser-mapping@2.10.19: {} beasties@0.4.1: dependencies: @@ -5115,9 +5115,9 @@ snapshots: domhandler: 5.0.3 htmlparser2: 10.1.0 picocolors: 1.1.1 - postcss: 8.5.9 + postcss: 8.5.10 postcss-media-query-parser: 0.2.3 - postcss-safe-parser: 7.0.1(postcss@8.5.9) + postcss-safe-parser: 7.0.1(postcss@8.5.10) bidi-js@1.0.3: dependencies: @@ -5143,12 +5143,12 @@ snapshots: dependencies: '@popperjs/core': 2.11.8 - brace-expansion@1.1.13: + brace-expansion@1.1.14: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.3: + brace-expansion@2.1.0: dependencies: balanced-match: 1.0.2 @@ -5162,9 +5162,9 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.17 - caniuse-lite: 1.0.30001787 - electron-to-chromium: 1.5.334 + baseline-browser-mapping: 2.10.19 + caniuse-lite: 1.0.30001788 + electron-to-chromium: 1.5.339 node-releases: 2.0.37 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -5177,7 +5177,7 @@ snapshots: '@npmcli/fs': 5.0.0 fs-minipass: 3.0.3 glob: 13.0.6 - lru-cache: 11.3.3 + lru-cache: 11.3.5 minipass: 7.1.3 minipass-collect: 2.0.1 minipass-flush: 1.0.7 @@ -5197,7 +5197,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001787: {} + caniuse-lite@1.0.30001788: {} chai@6.2.2: {} @@ -5292,9 +5292,9 @@ snapshots: cssstyle@5.3.7: dependencies: '@asamuzakjp/css-color': 4.1.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.2(css-tree@3.2.1) + '@csstools/css-syntax-patches-for-csstree': 1.1.3(css-tree@3.2.1) css-tree: 3.2.1 - lru-cache: 11.3.3 + lru-cache: 11.3.5 data-urls@6.0.1: dependencies: @@ -5340,7 +5340,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.334: {} + electron-to-chromium@1.5.339: {} emoji-regex@10.6.0: {} @@ -5365,7 +5365,7 @@ snapshots: engine.io@6.6.6: dependencies: '@types/cors': 2.8.19 - '@types/node': 25.5.2 + '@types/node': 25.6.0 '@types/ws': 8.18.1 accepts: 1.3.8 base64id: 2.0.0 @@ -5691,11 +5691,11 @@ snapshots: dependencies: function-bind: 1.1.2 - hono@4.12.12: {} + hono@4.12.14: {} hosted-git-info@9.0.2: dependencies: - lru-cache: 11.3.3 + lru-cache: 11.3.5 html-encoding-sniffer@6.0.0: dependencies: @@ -5795,7 +5795,7 @@ snapshots: dependencies: '@babel/core': 7.29.0 '@babel/parser': 7.29.2 - '@istanbuljs/schema': 0.1.3 + '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 semver: 7.7.4 transitivePeerDependencies: @@ -5912,7 +5912,7 @@ snapshots: strip-ansi: 7.2.0 wrap-ansi: 9.0.2 - lru-cache@11.3.3: {} + lru-cache@11.3.5: {} lru-cache@5.1.1: dependencies: @@ -5974,11 +5974,11 @@ snapshots: minimatch@3.1.5: dependencies: - brace-expansion: 1.1.13 + brace-expansion: 1.1.14 minimatch@9.0.9: dependencies: - brace-expansion: 2.0.3 + brace-expansion: 2.1.0 minipass-collect@2.0.1: dependencies: @@ -6045,16 +6045,16 @@ snapshots: negotiator@1.0.0: {} - ngx-cookie-service@21.3.1(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0)): + ngx-cookie-service@21.3.1(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)): dependencies: - '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - ngx-socket-io@4.10.0(@angular/common@21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): + ngx-socket-io@4.10.0(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): dependencies: - '@angular/common': 21.2.8(@angular/core@21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.8(@angular/compiler@21.2.8)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) core-js: 3.49.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 @@ -6179,7 +6179,7 @@ snapshots: is-interactive: 2.0.0 is-unicode-supported: 2.1.0 log-symbols: 7.0.1 - stdin-discarder: 0.3.1 + stdin-discarder: 0.3.2 string-width: 8.2.0 ordered-binary@1.6.1: @@ -6243,7 +6243,7 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.3.3 + lru-cache: 11.3.5 minipass: 7.1.3 path-to-regexp@8.4.2: {} @@ -6264,11 +6264,11 @@ snapshots: postcss-media-query-parser@0.2.3: {} - postcss-safe-parser@7.0.1(postcss@8.5.9): + postcss-safe-parser@7.0.1(postcss@8.5.10): dependencies: - postcss: 8.5.9 + postcss: 8.5.10 - postcss@8.5.9: + postcss@8.5.10: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 @@ -6587,9 +6587,9 @@ snapshots: statuses@2.0.2: {} - std-env@4.0.0: {} + std-env@4.1.0: {} - stdin-discarder@0.3.1: {} + stdin-discarder@0.3.2: {} string-width@4.2.3: dependencies: @@ -6705,7 +6705,7 @@ snapshots: typescript@5.9.3: {} - undici-types@7.18.2: {} + undici-types@7.19.2: {} undici@7.24.4: {} @@ -6725,23 +6725,23 @@ snapshots: vary@1.1.2: {} - vite@7.3.2(@types/node@25.5.2)(sass@1.97.3): + vite@7.3.2(@types/node@25.6.0)(sass@1.97.3): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.9 + postcss: 8.5.10 rollup: 4.60.1 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 fsevents: 2.3.3 sass: 1.97.3 - vitest@4.1.4(@types/node@25.5.2)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3)): + vitest@4.1.4(@types/node@25.6.0)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3)): dependencies: '@vitest/expect': 4.1.4 - '@vitest/mocker': 4.1.4(vite@7.3.2(@types/node@25.5.2)(sass@1.97.3)) + '@vitest/mocker': 4.1.4(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3)) '@vitest/pretty-format': 4.1.4 '@vitest/runner': 4.1.4 '@vitest/snapshot': 4.1.4 @@ -6753,15 +6753,15 @@ snapshots: obug: 2.1.1 pathe: 2.0.3 picomatch: 4.0.4 - std-env: 4.0.0 + std-env: 4.1.0 tinybench: 2.9.0 tinyexec: 1.1.1 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 7.3.2(@types/node@25.5.2)(sass@1.97.3) + vite: 7.3.2(@types/node@25.6.0)(sass@1.97.3) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.5.2 + '@types/node': 25.6.0 jsdom: 27.4.0 transitivePeerDependencies: - msw diff --git a/uv.lock b/uv.lock index a9bd172..67ebfb6 100644 --- a/uv.lock +++ b/uv.lock @@ -324,15 +324,15 @@ wheels = [ [[package]] name = "deno" -version = "2.7.11" +version = "2.7.12" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/d7/3f/a0477c72b847c0082ceb8885261eb14fb4addde22e8fb0146c011636979b/deno-2.7.11.tar.gz", hash = "sha256:342a656fca446fadc261ed22af35693b6c34e79129fa2bd387a1e5d39f496a99", size = 8167, upload-time = "2026-04-01T12:48:16.673Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/ac/39b45b51e7aaf9cad1de36f91bd58677818a7d7a867fa0721faff43b67c4/deno-2.7.12.tar.gz", hash = "sha256:7ef413693b4a9d86837a6f4991738429908f6f42f6f3ed85254ab0e679438049", size = 8167, upload-time = "2026-04-09T20:39:50.505Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/64/c8cb5a9c50135ada59b412b7511852d551d2618e169f48a8e7b8e90a382a/deno-2.7.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:76aa633656b07f64cfda3aa94a1e16bc123034ba2fe676d23b179f8326728534", size = 47857669, upload-time = "2026-04-01T12:48:01.947Z" }, - { url = "https://files.pythonhosted.org/packages/27/f3/250216e71e21cfc291ff6eb6503a479410ff927c9f97ebb644463c620692/deno-2.7.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a1dd1ceb0c2109b54334a621503e5297d473f5c3e6702992dfa68d9b7cdf1177", size = 44607869, upload-time = "2026-04-01T12:48:05.23Z" }, - { url = "https://files.pythonhosted.org/packages/01/51/b8fcb7d6882d659abd678f48fc5bc89aa0aa10f5a399fca3823680f277ca/deno-2.7.11-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:56578808c891d1daeadc279ac73445a24a83c2c721b37016fd92b1aa9224979b", size = 48394760, upload-time = "2026-04-01T12:48:08.049Z" }, - { url = "https://files.pythonhosted.org/packages/a0/18/91d1ceb2e15b4446fcecdf4be17ed59b02f5b35d8c98c94e7eaadf10591e/deno-2.7.11-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:58eea29d18fe8f80f2a4354d96225a31961ed3fa68336a7fcdd6add331df6acd", size = 50418090, upload-time = "2026-04-01T12:48:10.84Z" }, - { url = "https://files.pythonhosted.org/packages/b6/8d/7a801d85a0a8233d00ea1a381fd32e76c81be9db78ada162e211e7619221/deno-2.7.11-py3-none-win_amd64.whl", hash = "sha256:e2b69676e52543153b82e926f558983f9251db2251474f3225301cf7aca05a2b", size = 49417475, upload-time = "2026-04-01T12:48:14.158Z" }, + { url = "https://files.pythonhosted.org/packages/77/f0/0be717182294e9b61e58cd89b88d1b711c7e9c134e53890474b8d1e95704/deno-2.7.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:74677d1be23426b1f16ce33c1a96868bc7554e1d41a12fb4d446017bb1955d1d", size = 47683364, upload-time = "2026-04-09T20:39:35.086Z" }, + { url = "https://files.pythonhosted.org/packages/8d/44/ca1de5368b193c9819c57d505ff8ceb86dc38e2e87856b23c23b58abd234/deno-2.7.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:acf94fa815ac84a948b196f53a7e088ed126ec2e390a25d92cdfdc8c5d8a851e", size = 44460697, upload-time = "2026-04-09T20:39:38.5Z" }, + { url = "https://files.pythonhosted.org/packages/ce/68/ce111604f5877220e53fb10c5180ffaa484f829f20fcbc7f7115a90afadd/deno-2.7.12-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:ab7c12ee0e10cd232dbb8f7cf5802bfdbcc3462c0b1453726b158d8a27d4185f", size = 48237362, upload-time = "2026-04-09T20:39:41.555Z" }, + { url = "https://files.pythonhosted.org/packages/d7/07/d8ba3284e098128076cd9f84e85d2e62d12fa1e5b8e5c47681983017af1a/deno-2.7.12-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b396675fef296530c386387990d26d118398452bac47541e739b353b9ff8809f", size = 50247653, upload-time = "2026-04-09T20:39:44.956Z" }, + { url = "https://files.pythonhosted.org/packages/07/b0/c6c215eca2aa54b700a95867901832245518b2f217edfc78e733401bf8b3/deno-2.7.12-py3-none-win_amd64.whl", hash = "sha256:ddeeb03427d7ce0979a395ae2d1aa5ebcddeb97ab0fd9e7dfa56a08e0e5b817c", size = 49200604, upload-time = "2026-04-09T20:39:48.218Z" }, ] [[package]] @@ -593,11 +593,11 @@ wheels = [ [[package]] name = "packaging" -version = "26.0" +version = "26.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, + { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, ] [[package]] From e9f979b34907ec203dd9d52129beddd38a9e1cb0 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Sat, 18 Apr 2026 08:46:29 +0300 Subject: [PATCH 31/88] fix yt-dlp options overrides (closes #958) --- README.md | 4 ++ app/tests/test_download_queue.py | 97 ++++++++++++++++++++++++++++++-- app/ytdl.py | 36 ++++++++---- 3 files changed, 123 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ada5f84..d6f80aa 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,8 @@ MeTube lets you customize how [yt-dlp](https://github.com/yt-dlp/yt-dlp) behaves When a download starts, these layers are combined in order. If the same option appears in more than one layer, the more specific one wins: per-download overrides beat presets, and presets beat global options. +In JSON presets and overrides, setting an option to **`null`** clears that option for that download (for example, `"download_archive": null` overrides a global archive path so the archive is not used). This follows yt-dlp’s usual meaning of `None` for that option. + ### Option format yt-dlp options in MeTube are expressed as JSON objects. The keys are yt-dlp API option names, which roughly correspond to command-line flags with dashes replaced by underscores. For example, the command-line flag `--write-subs` becomes `"writesubtitles": true` in JSON. @@ -204,6 +206,8 @@ When a download starts, the final set of yt-dlp options is built in this order: 2. Apply each selected **preset** in order (later presets overwrite earlier ones for conflicting keys). 3. Apply any **per-download overrides** on top (overwrite everything else for conflicting keys). +MeTube always forces its own flat-extract behaviour during the initial metadata fetch (`extract_flat`, `noplaylist`, etc.); presets cannot override those keys for that phase. + **Example:** Suppose your global options set `"writesubtitles": false`, but you select a preset that sets `"writesubtitles": true`. Subtitles will be written for that download because the preset overrides the global setting. If you additionally enter `{"writesubtitles": false}` in the per-download overrides field, that value wins and subtitles will not be written. ### Configuration cookbooks diff --git a/app/tests/test_download_queue.py b/app/tests/test_download_queue.py index b4c7da2..a078403 100644 --- a/app/tests/test_download_queue.py +++ b/app/tests/test_download_queue.py @@ -56,7 +56,7 @@ def test_get_returns_tuple_of_lists(dq_env): async def test_add_single_video_goes_to_pending_when_auto_start_false(dq_env): notifier = AsyncMock() - def fake_extract(self, url): + def fake_extract(self, url, ytdl_options_presets=None, ytdl_options_overrides=None): return { "_type": "video", "id": "vid1", @@ -86,7 +86,7 @@ async def test_add_single_video_goes_to_pending_when_auto_start_false(dq_env): async def test_cancel_removes_from_pending(dq_env): notifier = AsyncMock() - def fake_extract(self, url): + def fake_extract(self, url, ytdl_options_presets=None, ytdl_options_overrides=None): return { "_type": "video", "id": "vid1", @@ -118,7 +118,7 @@ async def test_cancel_removes_from_pending(dq_env): async def test_start_pending_moves_to_queue(dq_env): notifier = AsyncMock() - def fake_extract(self, url): + def fake_extract(self, url, ytdl_options_presets=None, ytdl_options_overrides=None): return { "_type": "video", "id": "vid1", @@ -187,7 +187,7 @@ async def test_add_merges_global_preset_and_override_options(dq_env): "Preset B": {"writesubtitles": False, "ratelimit": 1000}, } - def fake_extract(self, url): + def fake_extract(self, url, ytdl_options_presets=None, ytdl_options_overrides=None): return { "_type": "video", "id": "vid2", @@ -219,3 +219,92 @@ async def test_add_merges_global_preset_and_override_options(dq_env): assert queued.ytdl_opts["ratelimit"] == 1000 assert queued.ytdl_opts["proxy"] == "http://override" assert queued.ytdl_opts["embed_thumbnail"] is True + + +@pytest.mark.asyncio +async def test_extract_info_preset_null_download_archive_overrides_global(dq_env): + """Preset download_archive:null must apply during extract_info (global archive otherwise wins first).""" + dq_env.YTDL_OPTIONS = {"download_archive": "/tmp/archive.txt"} + dq_env.YTDL_OPTIONS_PRESETS = {"NoArchive": {"download_archive": None}} + + captured_params: list = [] + + class FakeYoutubeDL: + def __init__(self, params=None): + captured_params.append(params) + + def extract_info(self, url, download=False): + return { + "_type": "video", + "id": "vid-archive", + "title": "Archive Test", + "url": url, + "webpage_url": url, + } + + notifier = AsyncMock() + dq = DownloadQueue(dq_env, notifier) + with patch("ytdl.yt_dlp.YoutubeDL", FakeYoutubeDL): + result = await dq.add( + "https://example.com/archive-test", + "video", + "auto", + "any", + "best", + "", + "", + 0, + auto_start=False, + ytdl_options_presets=["NoArchive"], + ) + + assert result["status"] == "ok" + assert len(captured_params) == 1 + extract_params = captured_params[0] + assert extract_params.get("download_archive") is None + assert extract_params["extract_flat"] is True + assert extract_params["noplaylist"] is True + + +@pytest.mark.asyncio +async def test_extract_info_metube_extract_keys_win_over_preset(dq_env): + """MeTube's flat-extract settings must not be overridden by presets.""" + dq_env.YTDL_OPTIONS = {} + dq_env.YTDL_OPTIONS_PRESETS = { + "TryOverride": {"extract_flat": False, "noplaylist": False}, + } + + captured_params: list = [] + + class FakeYoutubeDL: + def __init__(self, params=None): + captured_params.append(params) + + def extract_info(self, url, download=False): + return { + "_type": "video", + "id": "vid-flat", + "title": "Flat Test", + "url": url, + "webpage_url": url, + } + + notifier = AsyncMock() + dq = DownloadQueue(dq_env, notifier) + with patch("ytdl.yt_dlp.YoutubeDL", FakeYoutubeDL): + result = await dq.add( + "https://example.com/flat-test", + "video", + "auto", + "any", + "best", + "", + "", + 0, + auto_start=False, + ytdl_options_presets=["TryOverride"], + ) + + assert result["status"] == "ok" + assert captured_params[0]["extract_flat"] is True + assert captured_params[0]["noplaylist"] is True diff --git a/app/ytdl.py b/app/ytdl.py index 509ebb7..2738447 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -9,6 +9,7 @@ from collections import OrderedDict import time import asyncio import multiprocessing +from functools import partial import logging import re import types @@ -796,9 +797,19 @@ class DownloadQueue: log.debug(f'Auto-clearing completed download: {url}') await self.clear([url]) - def __extract_info(self, url): + def _build_ytdl_options(self, ytdl_options_presets=None, ytdl_options_overrides=None): + """Merge global options, presets (in order), and per-download overrides.""" + opts = dict(self.config.YTDL_OPTIONS) + for preset_name in ytdl_options_presets or []: + opts.update(self.config.YTDL_OPTIONS_PRESETS.get(preset_name, {})) + opts.update(ytdl_options_overrides or {}) + return opts + + def __extract_info(self, url, ytdl_options_presets=None, ytdl_options_overrides=None): debug_logging = logging.getLogger().isEnabledFor(logging.DEBUG) - return yt_dlp.YoutubeDL(params={ + user_opts = self._build_ytdl_options(ytdl_options_presets, ytdl_options_overrides) + params = { + **user_opts, 'quiet': not debug_logging, 'verbose': debug_logging, 'no_color': True, @@ -806,9 +817,11 @@ class DownloadQueue: 'ignore_no_formats_error': True, 'noplaylist': True, 'paths': {"home": self.config.DOWNLOAD_DIR, "temp": self.config.TEMP_DIR}, - **self.config.YTDL_OPTIONS, - **({'impersonate': yt_dlp.networking.impersonate.ImpersonateTarget.from_str(self.config.YTDL_OPTIONS['impersonate'])} if 'impersonate' in self.config.YTDL_OPTIONS else {}), - }).extract_info(url, download=False) + } + imp = user_opts.get('impersonate') + if imp is not None: + params['impersonate'] = yt_dlp.networking.impersonate.ImpersonateTarget.from_str(imp) + return yt_dlp.YoutubeDL(params=params).extract_info(url, download=False) def __calc_download_path(self, download_type, folder): base_directory = self.config.AUDIO_DOWNLOAD_DIR if download_type == 'audio' else self.config.DOWNLOAD_DIR @@ -844,10 +857,10 @@ class DownloadQueue: output = self.config.OUTPUT_TEMPLATE_CHANNEL sanitized = {k: _sanitize_path_component(v) for k, v in entry.items()} output = _resolve_outtmpl_fields(output, sanitized, ('channel',)) - ytdl_options = dict(self.config.YTDL_OPTIONS) - for preset_name in getattr(dl, 'ytdl_options_presets', None) or []: - ytdl_options.update(self.config.YTDL_OPTIONS_PRESETS.get(preset_name, {})) - ytdl_options.update(getattr(dl, 'ytdl_options_overrides', {}) or {}) + ytdl_options = self._build_ytdl_options( + getattr(dl, 'ytdl_options_presets', None), + getattr(dl, 'ytdl_options_overrides', {}) or {}, + ) playlist_item_limit = getattr(dl, 'playlist_item_limit', 0) if playlist_item_limit > 0: log.info(f'playlist limit is set. Processing only first {playlist_item_limit} entries') @@ -1037,7 +1050,10 @@ class DownloadQueue: else: already.add(url) try: - entry = await asyncio.get_running_loop().run_in_executor(None, self.__extract_info, url) + entry = await asyncio.get_running_loop().run_in_executor( + None, + partial(self.__extract_info, url, ytdl_options_presets, ytdl_options_overrides), + ) except yt_dlp.utils.YoutubeDLError as exc: return {'status': 'error', 'msg': str(exc)} return await self.__add_entry( From 0ea934c08f124a52fb4b69ef60b3e0a054afe041 Mon Sep 17 00:00:00 2001 From: rdiaz738 Date: Mon, 20 Apr 2026 17:24:16 -0700 Subject: [PATCH 32/88] Updated import and fixed race condition --- app/ytdl.py | 6 +++-- ui/src/app/app.html | 11 ++++++++ ui/src/app/app.ts | 62 ++++++++++++++++++++------------------------- 3 files changed, 43 insertions(+), 36 deletions(-) diff --git a/app/ytdl.py b/app/ytdl.py index 2738447..d6df963 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -1140,9 +1140,11 @@ class DownloadQueue: if not self.queue.exists(id): log.warning(f'requested cancel for non-existent download {id}') continue - if self.queue.get(id).started(): - self.queue.get(id).cancel() + dl = self.queue.get(id) + if dl.started(): + dl.cancel() else: + dl.canceled = True self.queue.delete(id) await self.notifier.canceled(id) return {'status': 'ok'} diff --git a/ui/src/app/app.html b/ui/src/app/app.html index 9ec0472..2b294fd 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -608,6 +608,17 @@ @if (batchImportStatus) { {{ batchImportStatus }} } + @if (importInProgress) { +
+
+ {{ batchImportCount }} / {{ batchImportTotal }} +
+
+ }
@@ -887,6 +899,8 @@ Name URL + Sub. title filter Interval (min) Last checked Status @@ -905,6 +919,32 @@ {{ entry[1].name }} {{ entry[1].url }} + + @if (editingTitleRegexId === entry[0]) { +
+ + + +
+ } @else { +
+ {{ entry[1].title_regex || 'β€”' }} + +
+ } + {{ entry[1].check_interval_minutes }} @if (entry[1].last_checked !== null) { diff --git a/ui/src/app/app.spec.ts b/ui/src/app/app.spec.ts index 3b83aeb..0e09bd7 100644 --- a/ui/src/app/app.spec.ts +++ b/ui/src/app/app.spec.ts @@ -63,8 +63,10 @@ class DownloadsServiceStub { class SubscriptionsServiceStub { subscriptions = new Map(); subscriptionsChanged = new Subject(); + subscribeCalls: unknown[] = []; - subscribe() { + subscribe(payload: unknown) { + this.subscribeCalls.push(payload); return of({ status: 'ok' as const }); } @@ -72,6 +74,10 @@ class SubscriptionsServiceStub { return of({}); } + update() { + return of({ status: 'ok' as const }); + } + refreshList() { return of([]); } @@ -175,4 +181,29 @@ describe('App', () => { expect(payload.ytdlOptionsOverrides).toBe(''); }); + + it('includes titleRegex in subscribe payload', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + const subs = TestBed.inject(SubscriptionsService) as unknown as SubscriptionsServiceStub; + app.addUrl = 'https://example.com/channel'; + app.titleRegex = 'EPISODE'; + app.addSubscription(); + expect(subs.subscribeCalls.length).toBe(1); + const payload = subs.subscribeCalls[0] as { titleRegex: string }; + expect(payload.titleRegex).toBe('EPISODE'); + }); + + it('blocks subscribe with invalid title regex', () => { + const alertSpy = vi.spyOn(window, 'alert').mockImplementation(() => undefined); + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + const subs = TestBed.inject(SubscriptionsService) as unknown as SubscriptionsServiceStub; + app.addUrl = 'https://example.com/channel'; + app.titleRegex = '['; + app.addSubscription(); + expect(subs.subscribeCalls.length).toBe(0); + expect(alertSpy).toHaveBeenCalledWith('Invalid subscription title filter (regex)'); + alertSpy.mockRestore(); + }); }); diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index cd88283..b7f1f6b 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -90,6 +90,9 @@ export class App implements AfterViewInit, OnInit, OnDestroy { cancelRequested = false; subscribeInProgress = false; checkIntervalMinutes = 60; + titleRegex = ''; + editingTitleRegexId: string | null = null; + titleRegexEditDraft = ''; cachedSubs: [string, SubscriptionRow][] = []; selectedSubscriptionIds = new Set(); checkingSubscriptionIds = new Set(); @@ -560,6 +563,15 @@ export class App implements AfterViewInit, OnInit, OnDestroy { alert('Please enter a URL'); return; } + const tr = (this.titleRegex || '').trim(); + if (tr) { + try { + void RegExp(tr); + } catch { + alert('Invalid subscription title filter (regex)'); + return; + } + } if (payload.splitByChapters && !payload.chapterTemplate.includes('%(section_number)')) { alert('Chapter template must include %(section_number)'); return; @@ -572,6 +584,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { .subscribe({ ...payload, checkIntervalMinutes: this.checkIntervalMinutes, + titleRegex: tr, }) .pipe( takeUntilDestroyed(this.destroyRef), @@ -587,11 +600,44 @@ export class App implements AfterViewInit, OnInit, OnDestroy { alert(r.msg || 'Subscribe failed'); } else { this.addUrl = ''; + this.titleRegex = ''; } }, }); } + beginEditTitleRegex(id: string, current: string | undefined) { + this.editingTitleRegexId = id; + this.titleRegexEditDraft = current ?? ''; + this.cdr.markForCheck(); + } + + cancelEditTitleRegex() { + this.editingTitleRegexId = null; + this.titleRegexEditDraft = ''; + this.cdr.markForCheck(); + } + + saveTitleRegex(id: string) { + const raw = (this.titleRegexEditDraft || '').trim(); + if (raw) { + try { + void RegExp(raw); + } catch { + alert('Invalid subscription title filter (regex)'); + return; + } + } + this.subscriptionsSvc.update(id, { title_regex: raw }).subscribe((res) => { + const error = this.getStatusError(res); + if (error) { + alert(error || 'Update subscription failed'); + return; + } + this.cancelEditTitleRegex(); + }); + } + deleteSubscription(id: string) { this.subscriptionsSvc.delete([id]).subscribe((res) => { const error = this.getStatusError(res); diff --git a/ui/src/app/interfaces/subscription.ts b/ui/src/app/interfaces/subscription.ts index 85bc794..f5ab68e 100644 --- a/ui/src/app/interfaces/subscription.ts +++ b/ui/src/app/interfaces/subscription.ts @@ -9,6 +9,7 @@ export interface SubscriptionRow { format: string; quality: string; folder: string; + title_regex?: string; last_checked: number | null; seen_count: number; error: string | null; diff --git a/ui/src/app/services/subscriptions.service.ts b/ui/src/app/services/subscriptions.service.ts index 2a95fb2..4db08cf 100644 --- a/ui/src/app/services/subscriptions.service.ts +++ b/ui/src/app/services/subscriptions.service.ts @@ -10,6 +10,7 @@ import { AddDownloadPayload } from './downloads.service'; export interface SubscribePayload extends AddDownloadPayload { checkIntervalMinutes: number; + titleRegex: string; } @Injectable({ @@ -97,6 +98,7 @@ export class SubscriptionsService { ytdl_options_presets: payload.ytdlOptionsPresets, ytdl_options_overrides: payload.ytdlOptionsOverrides, check_interval_minutes: payload.checkIntervalMinutes, + title_regex: payload.titleRegex, }) .pipe(catchError((err) => this.handleHTTPError(err))); } @@ -105,7 +107,10 @@ export class SubscriptionsService { return this.http.post('subscriptions/delete', { ids }).pipe(catchError((err) => this.handleHTTPError(err))); } - update(id: string, changes: Partial>) { + update( + id: string, + changes: Partial>, + ) { return this.http .post('subscriptions/update', { id, ...changes }) .pipe(catchError((err) => this.handleHTTPError(err))); From 4f83174d052deb3940d90e3a97b02a6c0498a289 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Sun, 26 Apr 2026 23:07:50 +0300 Subject: [PATCH 37/88] implement time-clipped downloads (closes #969, replaces #907) --- app/main.py | 149 ++++++++++++++++++ app/tests/test_api.py | 27 ++++ app/tests/test_download_queue.py | 35 ++++ app/tests/test_main_helpers.py | 74 +++++++++ app/ytdl.py | 38 ++++- ui/src/app/app.html | 28 ++++ ui/src/app/app.spec.ts | 24 +++ ui/src/app/app.ts | 22 ++- ui/src/app/interfaces/download.ts | 2 + ui/src/app/services/downloads.service.spec.ts | 20 +++ ui/src/app/services/downloads.service.ts | 11 +- 11 files changed, 426 insertions(+), 4 deletions(-) diff --git a/app/main.py b/app/main.py index e566bab..889ee7e 100644 --- a/app/main.py +++ b/app/main.py @@ -14,6 +14,7 @@ import logging import json import pathlib import re +from urllib.parse import parse_qs, urlencode, urlparse, urlunparse from watchfiles import DefaultFilter, Change, awatch from ytdl import DownloadQueueNotifier, DownloadQueue, Download @@ -260,6 +261,115 @@ def _parse_ytdl_options_overrides(value, *, enabled: bool) -> dict: return value +_YOUTUBE_T_COMPACT_RE = re.compile( + r'^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)(?:s)?)?$', + re.IGNORECASE, +) + + +def _parse_youtube_t_compact(value: str) -> float | None: + """Parse YouTube-style ``t`` values: ``885``, ``885s``, ``14m45s``, ``1h2m3s``.""" + v = value.strip() + if not v: + return None + if re.fullmatch(r'-?\d+(\.\d+)?', v): + sec = float(v) + return sec if sec >= 0 else None + m = _YOUTUBE_T_COMPACT_RE.match(v) + if m and any(m.groups()): + hours = int(m.group(1) or 0) + minutes = int(m.group(2) or 0) + seconds = int(m.group(3) or 0) + total = hours * 3600 + minutes * 60 + seconds + return float(total) if total >= 0 else None + return None + + +def _parse_clock_timestamp(s: str) -> float: + """Parse ``MM:SS``, ``H:MM:SS``, or single segment as seconds (with optional decimals).""" + part = s.strip() + if not part: + raise ValueError('empty timestamp') + segments = part.split(':') + if len(segments) > 3: + raise ValueError('too many segments') + try: + nums = [float(x) for x in segments] + except ValueError as exc: + raise ValueError('invalid number') from exc + if any(x < 0 for x in nums): + raise ValueError('negative segment') + if len(segments) == 1: + return nums[0] + if len(segments) == 2: + return nums[0] * 60 + nums[1] + return nums[0] * 3600 + nums[1] * 60 + nums[2] + + +def _parse_clip_timestamp_value(value) -> float: + """Coerce a clip boundary from JSON to seconds (non-negative).""" + if isinstance(value, bool): + raise web.HTTPBadRequest(reason='clip timestamp must be a number or string') + if isinstance(value, (int, float)): + if value < 0: + raise web.HTTPBadRequest(reason='clip timestamp must be non-negative') + return float(value) + s = str(value).strip() + if not s: + raise web.HTTPBadRequest(reason='clip timestamp cannot be empty') + if ':' in s: + try: + return _parse_clock_timestamp(s) + except ValueError as exc: + raise web.HTTPBadRequest(reason='invalid clip timestamp format') from exc + compact = _parse_youtube_t_compact(s) + if compact is not None: + return compact + raise web.HTTPBadRequest(reason='invalid clip timestamp format') + + +def _optional_clip_field(raw) -> float | None: + if raw is None: + return None + if isinstance(raw, str) and not raw.strip(): + return None + return _parse_clip_timestamp_value(raw) + + +def _clip_field_provided_in_post(raw) -> bool: + if raw is None: + return False + if isinstance(raw, str) and not raw.strip(): + return False + return True + + +def _extract_t_query_from_url(url: str) -> tuple[str, float | None]: + """If ``t=`` is present and parseable, return URL without ``t`` and start seconds.""" + try: + parsed = urlparse(url) + params = parse_qs(parsed.query) + except Exception: + return url, None + t_values = params.get('t') + if not t_values: + return url, None + start = _parse_youtube_t_compact(t_values[0]) + if start is None: + return url, None + filtered = {k: v for k, v in params.items() if k != 't'} + new_query = urlencode(filtered, doseq=True) + cleaned = urlunparse(( + parsed.scheme, + parsed.netloc, + parsed.path, + parsed.params, + new_query, + parsed.fragment, + )) + return cleaned, float(start) + + def _parse_ytdl_options_presets(post: dict) -> list[str]: """Normalize preset names from add/subscribe body; supports list or legacy singular string.""" raw = post.get('ytdl_options_presets') @@ -542,6 +652,39 @@ def parse_download_options(post: dict) -> dict: except (TypeError, ValueError) as exc: raise web.HTTPBadRequest(reason='playlist_item_limit must be an integer') from exc + clip_start_raw = post.get('clip_start') + clip_end_raw = post.get('clip_end') + clip_start: float | None + clip_end: float | None + if download_type in ('captions', 'thumbnail'): + if _clip_field_provided_in_post(clip_start_raw) or _clip_field_provided_in_post(clip_end_raw): + raise web.HTTPBadRequest( + reason='clip_start and clip_end are only supported for video and audio downloads', + ) + clip_start = None + clip_end = None + else: + cleaned_url, url_t = _extract_t_query_from_url(url) + if url_t is not None: + url = cleaned_url + explicit_start = _optional_clip_field(clip_start_raw) + explicit_end = _optional_clip_field(clip_end_raw) + explicit_start_provided = _clip_field_provided_in_post(clip_start_raw) + explicit_end_provided = _clip_field_provided_in_post(clip_end_raw) + if explicit_start_provided: + clip_start = explicit_start + elif explicit_end_provided: + clip_start = 0.0 + elif url_t is not None: + clip_start = url_t + else: + clip_start = None + clip_end = explicit_end + if clip_end is not None and clip_start is None: + clip_start = 0.0 + if clip_start is not None and clip_end is not None and clip_end <= clip_start: + raise web.HTTPBadRequest(reason='clip_end must be greater than clip_start') + return { 'url': url, 'download_type': download_type, @@ -558,6 +701,8 @@ def parse_download_options(post: dict) -> dict: 'subtitle_mode': subtitle_mode, 'ytdl_options_presets': ytdl_options_presets, 'ytdl_options_overrides': ytdl_options_overrides, + 'clip_start': clip_start, + 'clip_end': clip_end, } @@ -594,6 +739,8 @@ async def add(request): o['subtitle_mode'], o['ytdl_options_presets'], o['ytdl_options_overrides'], + o['clip_start'], + o['clip_end'], ) return web.Response(text=serializer.encode(status)) @@ -627,6 +774,8 @@ async def subscribe(request): raise web.HTTPBadRequest(reason='check_interval_minutes must be an integer') from exc if cic < 1: raise web.HTTPBadRequest(reason='check_interval_minutes must be at least 1') + if o.get('clip_start') is not None or o.get('clip_end') is not None: + raise web.HTTPBadRequest(reason='clip options are not supported for subscriptions') result = await submgr.add_subscription( o['url'], diff --git a/app/tests/test_api.py b/app/tests/test_api.py index e7fb8ae..9776856 100644 --- a/app/tests/test_api.py +++ b/app/tests/test_api.py @@ -279,3 +279,30 @@ async def test_add_legacy_format_migrated(mock_dqueue): call = mock_dqueue.add.await_args assert call is not None assert call.args[1] == "audio" + + +@pytest.mark.asyncio +async def test_add_passes_clip_bounds_to_queue(mock_dqueue): + req = _json_request( + _valid_video_add_body(clip_start="2:26", clip_end="3:24"), + ) + resp = await main.add(req) + assert resp.status == 200 + call = mock_dqueue.add.await_args + assert call is not None + assert call.args[15] == pytest.approx(146.0) + assert call.args[16] == pytest.approx(204.0) + + +@pytest.mark.asyncio +async def test_subscribe_rejects_clip_options(mock_dqueue, monkeypatch): + monkeypatch.setattr(main.submgr, "add_subscription", AsyncMock()) + req = _json_request( + { + **_valid_video_add_body(clip_start="10"), + "check_interval_minutes": 60, + } + ) + with pytest.raises(web.HTTPBadRequest): + await main.subscribe(req) + main.submgr.add_subscription.assert_not_awaited() diff --git a/app/tests/test_download_queue.py b/app/tests/test_download_queue.py index 71632c8..20bf863 100644 --- a/app/tests/test_download_queue.py +++ b/app/tests/test_download_queue.py @@ -351,3 +351,38 @@ async def test_extract_info_metube_extract_keys_win_over_preset(dq_env): assert result["status"] == "ok" assert captured_params[0]["extract_flat"] is True assert captured_params[0]["noplaylist"] is True + + +@pytest.mark.asyncio +async def test_add_sets_clip_bounds_on_download_info(dq_env): + notifier = AsyncMock() + + def fake_extract(self, url, ytdl_options_presets=None, ytdl_options_overrides=None): + return { + "_type": "video", + "id": "vid1", + "title": "Test Video", + "url": url, + "webpage_url": url, + } + + dq = DownloadQueue(dq_env, notifier) + with patch.object(DownloadQueue, "_DownloadQueue__extract_info", fake_extract): + result = await dq.add( + "https://example.com/clip", + "video", + "auto", + "any", + "best", + "", + "", + 0, + auto_start=False, + clip_start=10.0, + clip_end=99.5, + ) + + assert result["status"] == "ok" + download = dq.pending.get("https://example.com/clip") + assert download.info.clip_start == 10.0 + assert download.info.clip_end == 99.5 diff --git a/app/tests/test_main_helpers.py b/app/tests/test_main_helpers.py index 5b9fb2c..d39e54b 100644 --- a/app/tests/test_main_helpers.py +++ b/app/tests/test_main_helpers.py @@ -205,6 +205,80 @@ class ParseDownloadOptionsTests(unittest.TestCase): finally: main.config.YTDL_OPTIONS_PRESETS = previous + def test_clip_start_end_seconds_and_clock(self): + parsed = main.parse_download_options({ + "url": "https://example.com/watch?v=1", + "download_type": "video", + "codec": "auto", + "format": "any", + "quality": "best", + "clip_start": "2:26", + "clip_end": "3:24", + }) + self.assertEqual(parsed["clip_start"], 146.0) + self.assertEqual(parsed["clip_end"], 204.0) + + def test_clip_url_t_param_strips_query_and_sets_start(self): + parsed = main.parse_download_options({ + "url": "https://example.com/watch?v=1&t=855s", + "download_type": "video", + "codec": "auto", + "format": "any", + "quality": "best", + }) + self.assertEqual(parsed["url"], "https://example.com/watch?v=1") + self.assertEqual(parsed["clip_start"], 855.0) + self.assertIsNone(parsed["clip_end"]) + + def test_clip_explicit_start_wins_over_url_t(self): + parsed = main.parse_download_options({ + "url": "https://example.com/watch?v=1&t=100", + "download_type": "video", + "codec": "auto", + "format": "any", + "quality": "best", + "clip_start": "50", + }) + self.assertEqual(parsed["url"], "https://example.com/watch?v=1") + self.assertEqual(parsed["clip_start"], 50.0) + self.assertIsNone(parsed["clip_end"]) + + def test_clip_end_only_sets_start_zero_and_strips_url_t(self): + parsed = main.parse_download_options({ + "url": "https://example.com/watch?v=1&t=999", + "download_type": "video", + "codec": "auto", + "format": "any", + "quality": "best", + "clip_end": "60", + }) + self.assertEqual(parsed["url"], "https://example.com/watch?v=1") + self.assertEqual(parsed["clip_start"], 0.0) + self.assertEqual(parsed["clip_end"], 60.0) + + def test_clip_rejects_end_before_start(self): + with self.assertRaises(main.web.HTTPBadRequest): + main.parse_download_options({ + "url": "https://example.com/watch?v=1", + "download_type": "video", + "codec": "auto", + "format": "any", + "quality": "best", + "clip_start": "100", + "clip_end": "50", + }) + + def test_clip_rejected_for_captions(self): + with self.assertRaises(main.web.HTTPBadRequest): + main.parse_download_options({ + "url": "https://example.com/watch?v=1", + "download_type": "captions", + "codec": "auto", + "format": "srt", + "quality": "best", + "clip_start": "1", + }) + if __name__ == "__main__": unittest.main() diff --git a/app/ytdl.py b/app/ytdl.py index d6df963..d928c0f 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -192,6 +192,8 @@ class DownloadInfo: subtitle_mode="prefer_manual", ytdl_options_presets=None, ytdl_options_overrides=None, + clip_start=None, + clip_end=None, ): self.id = id if len(custom_name_prefix) == 0 else f'{custom_name_prefix}.{id}' self.title = title if len(custom_name_prefix) == 0 else f'{custom_name_prefix}.{title}' @@ -216,6 +218,8 @@ class DownloadInfo: self.subtitle_mode = subtitle_mode self.ytdl_options_presets = list(ytdl_options_presets or []) self.ytdl_options_overrides = dict(ytdl_options_overrides or {}) + self.clip_start = clip_start + self.clip_end = clip_end self.subtitle_files = [] def __setstate__(self, state): @@ -284,6 +288,10 @@ class DownloadInfo: self.subtitle_files = [] if not hasattr(self, "chapter_files"): self.chapter_files = [] + if not hasattr(self, "clip_start"): + self.clip_start = None + if not hasattr(self, "clip_end"): + self.clip_end = None _PERSISTED_DOWNLOAD_FIELDS = ( @@ -303,6 +311,8 @@ _PERSISTED_DOWNLOAD_FIELDS = ( "subtitle_mode", "ytdl_options_presets", "ytdl_options_overrides", + "clip_start", + "clip_end", "status", "timestamp", "error", @@ -473,6 +483,16 @@ class Download: 'force_keyframes': False }) + clip_start = getattr(self.info, 'clip_start', None) + clip_end = getattr(self.info, 'clip_end', None) + if clip_start is not None or clip_end is not None: + start = float(clip_start) if clip_start is not None else 0.0 + end = float(clip_end) if clip_end is not None else float('inf') + ytdl_params['download_ranges'] = yt_dlp.utils.download_range_func( + None, + [(start, end)], + ) + ret = yt_dlp.YoutubeDL(params=ytdl_params).download([self.info.url]) self.status_queue.put({'status': 'finished' if ret == 0 else 'error'}) log.info(f"Finished download for: {self.info.title}") @@ -890,6 +910,8 @@ class DownloadQueue: subtitle_mode, ytdl_options_presets, ytdl_options_overrides, + clip_start, + clip_end, already, _add_gen=None, ): @@ -924,6 +946,8 @@ class DownloadQueue: subtitle_mode, ytdl_options_presets, ytdl_options_overrides, + clip_start, + clip_end, already, _add_gen, ) @@ -975,6 +999,8 @@ class DownloadQueue: subtitle_mode, ytdl_options_presets, ytdl_options_overrides, + clip_start, + clip_end, already, _add_gen, ) @@ -1008,6 +1034,8 @@ class DownloadQueue: subtitle_mode=subtitle_mode, ytdl_options_presets=ytdl_options_presets, ytdl_options_overrides=ytdl_options_overrides, + clip_start=clip_start, + clip_end=clip_end, ) await self.__add_download(dl, auto_start) return {'status': 'ok'} @@ -1030,6 +1058,8 @@ class DownloadQueue: subtitle_mode="prefer_manual", ytdl_options_presets=None, ytdl_options_overrides=None, + clip_start=None, + clip_end=None, already=None, _add_gen=None, ): @@ -1038,7 +1068,7 @@ class DownloadQueue: log.info( f'adding {url}: {download_type=} {codec=} {format=} {quality=} {already=} {folder=} {custom_name_prefix=} ' f'{playlist_item_limit=} {auto_start=} {split_by_chapters=} {chapter_template=} ' - f'{subtitle_language=} {subtitle_mode=} {ytdl_options_presets=}' + f'{subtitle_language=} {subtitle_mode=} {ytdl_options_presets=} {clip_start=} {clip_end=}' ) if already is None: _add_gen = self._add_generation @@ -1072,6 +1102,8 @@ class DownloadQueue: subtitle_mode, ytdl_options_presets, ytdl_options_overrides, + clip_start, + clip_end, already, _add_gen, ) @@ -1093,6 +1125,8 @@ class DownloadQueue: subtitle_mode="prefer_manual", ytdl_options_presets=None, ytdl_options_overrides=None, + clip_start=None, + clip_end=None, ): if ytdl_options_presets is None: ytdl_options_presets = [] @@ -1114,6 +1148,8 @@ class DownloadQueue: subtitle_mode, ytdl_options_presets, ytdl_options_overrides, + clip_start, + clip_end, already, None, ) diff --git a/ui/src/app/app.html b/ui/src/app/app.html index cf4986c..06d6476 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -428,6 +428,34 @@ }
+ @if (downloadType === 'video' || downloadType === 'audio') { +
+
+ Clip start + +
+
+
+
+ Clip end + +
+
+ }
diff --git a/ui/src/app/app.spec.ts b/ui/src/app/app.spec.ts index 0e09bd7..2ef3185 100644 --- a/ui/src/app/app.spec.ts +++ b/ui/src/app/app.spec.ts @@ -194,6 +194,30 @@ describe('App', () => { expect(payload.titleRegex).toBe('EPISODE'); }); + it('omits clip fields from subscribe payload', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + const subs = TestBed.inject(SubscriptionsService) as unknown as SubscriptionsServiceStub; + app.addUrl = 'https://example.com/channel'; + app.clipStart = '1:00'; + app.clipEnd = '2:00'; + app.addSubscription(); + expect(subs.subscribeCalls.length).toBe(1); + const payload = subs.subscribeCalls[0] as Record; + expect('clipStart' in payload).toBe(false); + expect('clipEnd' in payload).toBe(false); + }); + + it('buildAddPayload includes clip times', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + app.clipStart = '0:10'; + app.clipEnd = '1:20'; + const payload = app['buildAddPayload'](); + expect(payload.clipStart).toBe('0:10'); + expect(payload.clipEnd).toBe('1:20'); + }); + it('blocks subscribe with invalid title regex', () => { const alertSpy = vi.spyOn(window, 'alert').mockImplementation(() => undefined); const fixture = TestBed.createComponent(App); diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index b7f1f6b..8caf48f 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -81,6 +81,8 @@ export class App implements AfterViewInit, OnInit, OnDestroy { playlistItemLimit!: number; splitByChapters: boolean; chapterTemplate: string; + clipStart = ''; + clipEnd = ''; subtitleLanguage: string; subtitleMode: string; ytdlOptionsPresets: string[] = []; @@ -242,6 +244,8 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.splitByChapters = this.cookieService.get('metube_split_chapters') === 'true'; // Will be set from backend configuration, use empty string as placeholder this.chapterTemplate = this.cookieService.get('metube_chapter_template') || ''; + this.clipStart = this.cookieService.get('metube_clip_start') || ''; + this.clipEnd = this.cookieService.get('metube_clip_end') || ''; this.subtitleLanguage = this.cookieService.get('metube_subtitle_language') || 'en'; this.subtitleMode = this.cookieService.get('metube_subtitle_mode') || 'prefer_manual'; this.ytdlOptionsPresets = this.loadYtdlOptionsPresetsFromCookie(); @@ -579,10 +583,14 @@ export class App implements AfterViewInit, OnInit, OnDestroy { if (!this.validateYtdlOptionsOverrides(payload.ytdlOptionsOverrides)) { return; } + // Subscriptions do not support clip ranges (backend rejects clip fields). + const { clipStart: _clipStart, clipEnd: _clipEnd, ...subscribeBase } = payload; + void _clipStart; + void _clipEnd; this.subscribeInProgress = true; this.subscriptionsSvc .subscribe({ - ...payload, + ...subscribeBase, checkIntervalMinutes: this.checkIntervalMinutes, titleRegex: tr, }) @@ -807,6 +815,14 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.cookieService.set('metube_chapter_template', this.chapterTemplate, { expires: this.settingsCookieExpiryDays }); } + clipStartChanged() { + this.cookieService.set('metube_clip_start', this.clipStart, { expires: this.settingsCookieExpiryDays }); + } + + clipEndChanged() { + this.cookieService.set('metube_clip_end', this.clipEnd, { expires: this.settingsCookieExpiryDays }); + } + subtitleLanguageChanged() { this.cookieService.set('metube_subtitle_language', this.subtitleLanguage, { expires: this.settingsCookieExpiryDays }); this.saveSelection(this.downloadType); @@ -1033,6 +1049,8 @@ export class App implements AfterViewInit, OnInit, OnDestroy { ytdlOptionsOverrides: allowYtdlOptionsOverrides ? (overrides.ytdlOptionsOverrides ?? this.ytdlOptionsOverrides) : '', + clipStart: overrides.clipStart ?? this.clipStart, + clipEnd: overrides.clipEnd ?? this.clipEnd, }; } @@ -1106,6 +1124,8 @@ export class App implements AfterViewInit, OnInit, OnDestroy { ? [...download.ytdl_options_presets] : [], ytdlOptionsOverrides: download.ytdl_options_overrides ? JSON.stringify(download.ytdl_options_overrides) : '', + clipStart: download.clip_start != null ? String(download.clip_start) : '', + clipEnd: download.clip_end != null ? String(download.clip_end) : '', }); this.downloads.delById('done', [key]).subscribe(); } diff --git a/ui/src/app/interfaces/download.ts b/ui/src/app/interfaces/download.ts index 5705a81..935d4da 100644 --- a/ui/src/app/interfaces/download.ts +++ b/ui/src/app/interfaces/download.ts @@ -16,6 +16,8 @@ export interface Download { subtitle_mode?: string; ytdl_options_presets?: string[]; ytdl_options_overrides?: Record; + clip_start?: number; + clip_end?: number; status: string; msg: string; percent: number; diff --git a/ui/src/app/services/downloads.service.spec.ts b/ui/src/app/services/downloads.service.spec.ts index f07b768..2fbc4be 100644 --- a/ui/src/app/services/downloads.service.spec.ts +++ b/ui/src/app/services/downloads.service.spec.ts @@ -41,6 +41,8 @@ function basePayload(): AddDownloadPayload { subtitleMode: 'prefer_manual', ytdlOptionsPresets: [], ytdlOptionsOverrides: '', + clipStart: '', + clipEnd: '', }; } @@ -88,6 +90,24 @@ describe('DownloadsService', () => { req.flush({ status: 'ok' }); }); + it('add() sends clip_start and clip_end when set', () => { + service + .add({ + ...basePayload(), + clipStart: '1:00', + clipEnd: '2:00', + }) + .subscribe(); + const req = httpMock.expectOne('add'); + expect(req.request.body).toEqual( + expect.objectContaining({ + clip_start: '1:00', + clip_end: '2:00', + }), + ); + req.flush({ status: 'ok' }); + }); + it('getPresets() fetches configured preset names', () => { service.getPresets().subscribe((result) => { expect(result).toEqual({ presets: ['Preset A'] }); diff --git a/ui/src/app/services/downloads.service.ts b/ui/src/app/services/downloads.service.ts index 5a0f1e8..60c3df6 100644 --- a/ui/src/app/services/downloads.service.ts +++ b/ui/src/app/services/downloads.service.ts @@ -22,6 +22,8 @@ export interface AddDownloadPayload { subtitleMode: string; ytdlOptionsPresets: string[]; ytdlOptionsOverrides: string; + clipStart?: string; + clipEnd?: string; } @Injectable({ providedIn: 'root' @@ -129,7 +131,7 @@ export class DownloadsService { } public add(payload: AddDownloadPayload) { - return this.http.post('add', { + const body: Record = { url: payload.url, download_type: payload.downloadType, codec: payload.codec, @@ -145,7 +147,12 @@ export class DownloadsService { subtitle_mode: payload.subtitleMode, ytdl_options_presets: payload.ytdlOptionsPresets, ytdl_options_overrides: payload.ytdlOptionsOverrides, - }).pipe( + }; + const cs = payload.clipStart?.trim(); + const ce = payload.clipEnd?.trim(); + if (cs) body['clip_start'] = cs; + if (ce) body['clip_end'] = ce; + return this.http.post('add', body).pipe( catchError(this.handleHTTPError) ); } From 5d96a581b9d161575ed63b57a10aec8823e4cb1d Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Tue, 28 Apr 2026 22:02:05 +0300 Subject: [PATCH 38/88] allow filtering out members-only videos in subscriptions (closes #971) --- app/main.py | 15 +- app/subscriptions.py | 64 ++++- app/tests/test_subscriptions.py | 232 ++++++++++++++++++- ui/src/app/app.html | 9 + ui/src/app/app.spec.ts | 15 +- ui/src/app/app.ts | 3 + ui/src/app/interfaces/subscription.ts | 1 + ui/src/app/services/subscriptions.service.ts | 9 +- 8 files changed, 341 insertions(+), 7 deletions(-) diff --git a/app/main.py b/app/main.py index 889ee7e..61d7e6e 100644 --- a/app/main.py +++ b/app/main.py @@ -18,7 +18,7 @@ from urllib.parse import parse_qs, urlencode, urlparse, urlunparse from watchfiles import DefaultFilter, Change, awatch from ytdl import DownloadQueueNotifier, DownloadQueue, Download -from subscriptions import SubscriptionManager, SubscriptionNotifier, SubscriptionInfo +from subscriptions import SubscriptionManager, SubscriptionNotifier, SubscriptionInfo, coerce_optional_bool from yt_dlp.version import __version__ as yt_dlp_version log = logging.getLogger('main') @@ -777,6 +777,15 @@ async def subscribe(request): if o.get('clip_start') is not None or o.get('clip_end') is not None: raise web.HTTPBadRequest(reason='clip options are not supported for subscriptions') + try: + skip_subscriber_only = coerce_optional_bool( + post.get('skip_subscriber_only'), + default=False, + field_name='skip_subscriber_only', + ) + except ValueError as exc: + raise web.HTTPBadRequest(reason=str(exc)) from exc + result = await submgr.add_subscription( o['url'], check_interval_minutes=cic, @@ -795,6 +804,7 @@ async def subscribe(request): ytdl_options_presets=o['ytdl_options_presets'], ytdl_options_overrides=o['ytdl_options_overrides'], title_regex=post.get('title_regex'), + skip_subscriber_only=skip_subscriber_only, ) return web.Response(text=serializer.encode(result)) @@ -813,7 +823,8 @@ async def subscriptions_update(request): changes = { k: v for k, v in post.items() - if k != 'id' and k in ('enabled', 'check_interval_minutes', 'name', 'title_regex') + if k != 'id' + and k in ('enabled', 'check_interval_minutes', 'name', 'title_regex', 'skip_subscriber_only') } if not changes: raise web.HTTPBadRequest(reason='no valid fields to update') diff --git a/app/subscriptions.py b/app/subscriptions.py index 170859c..0b5774a 100644 --- a/app/subscriptions.py +++ b/app/subscriptions.py @@ -127,6 +127,21 @@ def _entry_id(entry: dict) -> Optional[str]: return url +def _is_subscriber_only_entry(entry: dict) -> bool: + """True when yt-dlp marks the entry as channel member-only (subscriber_only availability).""" + return str(entry.get("availability") or "") == "subscriber_only" + + +def coerce_optional_bool(value: Any, *, default: bool = False, field_name: str = "value") -> bool: + """Parse optional JSON booleans for subscription settings.""" + if value is None: + return default + try: + return _coerce_bool(value) + except ValueError as exc: + raise ValueError(f"{field_name} must be a boolean") from exc + + @dataclass class SubscriptionInfo: id: str @@ -149,6 +164,7 @@ class SubscriptionInfo: ytdl_options_presets: list[str] = field(default_factory=list) ytdl_options_overrides: dict[str, Any] = field(default_factory=dict) title_regex: str = "" + skip_subscriber_only: bool = False last_checked: Optional[float] = None seen_ids: list[str] = field(default_factory=list) error: Optional[str] = None @@ -170,6 +186,7 @@ class SubscriptionInfo: "quality": self.quality, "folder": self.folder, "title_regex": self.title_regex, + "skip_subscriber_only": self.skip_subscriber_only, "last_checked": self.last_checked, "seen_count": len(self.seen_ids), "error": self.error, @@ -198,6 +215,7 @@ def _subscription_to_record(sub: SubscriptionInfo) -> dict[str, Any]: "ytdl_options_presets": list(sub.ytdl_options_presets), "ytdl_options_overrides": sub.ytdl_options_overrides, "title_regex": sub.title_regex, + "skip_subscriber_only": sub.skip_subscriber_only, "last_checked": sub.last_checked, "seen_ids": list(sub.seen_ids), "error": sub.error, @@ -469,6 +487,7 @@ class SubscriptionManager: ytdl_options_presets: Optional[list[str]] = None, ytdl_options_overrides: Optional[dict[str, Any]] = None, title_regex: Any = None, + skip_subscriber_only: Any = None, ) -> dict: url = self._normalize_url(url) if not url: @@ -477,6 +496,14 @@ class SubscriptionManager: title_regex_stored = validate_title_regex(title_regex) except re.error as exc: return {"status": "error", "msg": f"Invalid title_regex: {exc}"} + try: + skip_so = coerce_optional_bool( + skip_subscriber_only, + default=False, + field_name="skip_subscriber_only", + ) + except ValueError as exc: + return {"status": "error", "msg": str(exc)} async with self._lock: if url in self._url_index or url in self._pending_urls: @@ -535,6 +562,7 @@ class SubscriptionManager: ytdl_options_presets=list(ytdl_options_presets or []), ytdl_options_overrides=dict(ytdl_options_overrides or {}), title_regex=title_regex_stored, + skip_subscriber_only=skip_so, last_checked=time.time(), seen_ids=list(dict.fromkeys(all_ids)), error=None, @@ -588,6 +616,18 @@ class SubscriptionManager: except re.error as exc: return {"status": "error", "msg": f"Invalid title_regex: {exc}"} + skip_so_set = False + validated_skip_so = False + if "skip_subscriber_only" in changes: + try: + validated_skip_so = coerce_optional_bool( + changes["skip_subscriber_only"], + field_name="skip_subscriber_only", + ) + skip_so_set = True + except ValueError as exc: + return {"status": "error", "msg": str(exc)} + async with self._lock: sub = self._subs.get(sub_id) if not sub: @@ -603,6 +643,8 @@ class SubscriptionManager: sub.name = str(changes["name"]) if validated_tr is not None: sub.title_regex = validated_tr + if skip_so_set: + sub.skip_subscriber_only = validated_skip_so try: self._save_locked() @@ -695,6 +737,7 @@ class SubscriptionManager: dl_ytdl_presets = list(cur.ytdl_options_presets) dl_ytdl_overrides = dict(cur.ytdl_options_overrides) dl_title_regex = cur.title_regex or "" + dl_skip_subscriber_only = bool(cur.skip_subscriber_only) new_entries: list[dict] = [] for ent in entries: @@ -727,6 +770,18 @@ class SubscriptionManager: continue queue_entries.append(ent) + subscriber_filtered_ids: list[str] = [] + if dl_skip_subscriber_only: + kept_entries: list[dict] = [] + for ent in queue_entries: + eid = _entry_id(ent) + if _is_subscriber_only_entry(ent): + if eid: + subscriber_filtered_ids.append(eid) + continue + kept_entries.append(ent) + queue_entries = kept_entries + queued_ids, queue_errors = await self._queue_subscription_entries( queue_entries, download_type=dl_type, @@ -745,15 +800,20 @@ class SubscriptionManager: ytdl_options_overrides=dl_ytdl_overrides, ) log.info( - "Subscription check finished for %s: %d new, %d filtered, %d queued, %d failed", + "Subscription check finished for %s: %d new, %d filtered, %d subscriber_skipped, %d queued, %d failed", sub.name, len(new_entries), len(filtered_ids), + len(subscriber_filtered_ids), len(queued_ids), len(queue_errors), ) - merged = list(dict.fromkeys(queued_ids + filtered_ids + seen_ids_snapshot)) + merged = list( + dict.fromkeys( + queued_ids + filtered_ids + subscriber_filtered_ids + seen_ids_snapshot + ) + ) max_seen = int(getattr(self.config, "SUBSCRIPTION_MAX_SEEN_IDS", 50000)) if len(merged) > max_seen: merged = merged[:max_seen] diff --git a/app/tests/test_subscriptions.py b/app/tests/test_subscriptions.py index a5bde09..525db11 100644 --- a/app/tests/test_subscriptions.py +++ b/app/tests/test_subscriptions.py @@ -28,7 +28,12 @@ sys.modules.setdefault("yt_dlp", fake_yt_dlp) sys.modules.setdefault("yt_dlp.networking", fake_networking) sys.modules.setdefault("yt_dlp.networking.impersonate", fake_impersonate) -from subscriptions import SubscriptionManager, extract_flat_playlist +from subscriptions import ( + SubscriptionManager, + _is_subscriber_only_entry, + coerce_optional_bool, + extract_flat_playlist, +) class _Config: @@ -75,6 +80,20 @@ def _create_legacy_shelf(path: str, record) -> None: shelf["sub-1"] = record +class SubscriberOnlyHelperTests(unittest.TestCase): + def test_is_subscriber_only_detects_availability(self): + self.assertTrue(_is_subscriber_only_entry({"availability": "subscriber_only"})) + self.assertFalse(_is_subscriber_only_entry({"availability": None})) + self.assertFalse(_is_subscriber_only_entry({})) + + def test_coerce_optional_bool_defaults_and_fields(self): + self.assertFalse(coerce_optional_bool(None, default=False)) + self.assertTrue(coerce_optional_bool(True)) + self.assertFalse(coerce_optional_bool(False)) + with self.assertRaises(ValueError): + coerce_optional_bool("maybe", field_name="skip_subscriber_only") + + class SubscriptionPersistenceTests(unittest.IsolatedAsyncioTestCase): def test_load_imports_legacy_subscription_shelf(self): with tempfile.TemporaryDirectory() as tmp: @@ -386,6 +405,108 @@ class SubscriptionPersistenceTests(unittest.IsolatedAsyncioTestCase): self.assertEqual(sub.seen_ids[:2], ["v2", "v1"]) self.assertEqual([entry["webpage_url"] for entry, _, _ in queue.entries], ["https://example.com/v2"]) + async def test_check_now_queues_subscriber_only_when_skip_disabled(self): + with tempfile.TemporaryDirectory() as tmp: + queue = _Queue() + mgr = SubscriptionManager(_Config(tmp), queue, _Notifier()) + + with patch( + "subscriptions.extract_flat_playlist", + side_effect=[ + ( + {"_type": "channel", "title": "Channel"}, + [{"id": "v1", "title": "One", "webpage_url": "https://example.com/v1"}], + ), + ( + {"_type": "channel", "title": "Channel"}, + [ + { + "id": "v2", + "title": "Members", + "webpage_url": "https://example.com/v2", + "availability": "subscriber_only", + }, + {"id": "v1", "title": "One", "webpage_url": "https://example.com/v1"}, + ], + ), + ], + ): + result = await mgr.add_subscription( + "https://example.com/channel", + check_interval_minutes=60, + download_type="video", + codec="auto", + format="any", + quality="best", + folder="", + custom_name_prefix="", + auto_start=True, + playlist_item_limit=0, + split_by_chapters=False, + chapter_template="", + subtitle_language="en", + subtitle_mode="prefer_manual", + skip_subscriber_only=False, + ) + self.assertFalse(mgr.list_all()[0].skip_subscriber_only) + await mgr.check_now([result["subscription"]["id"]]) + + sub = mgr.list_all()[0] + self.assertIsNone(sub.error) + self.assertEqual(sub.seen_ids[:2], ["v2", "v1"]) + self.assertEqual([entry["webpage_url"] for entry, _, _ in queue.entries], ["https://example.com/v2"]) + + async def test_check_now_skips_subscriber_only_when_skip_enabled(self): + with tempfile.TemporaryDirectory() as tmp: + queue = _Queue() + mgr = SubscriptionManager(_Config(tmp), queue, _Notifier()) + + with patch( + "subscriptions.extract_flat_playlist", + side_effect=[ + ( + {"_type": "channel", "title": "Channel"}, + [{"id": "v1", "title": "One", "webpage_url": "https://example.com/v1"}], + ), + ( + {"_type": "channel", "title": "Channel"}, + [ + { + "id": "v2", + "title": "Members", + "webpage_url": "https://example.com/v2", + "availability": "subscriber_only", + }, + {"id": "v1", "title": "One", "webpage_url": "https://example.com/v1"}, + ], + ), + ], + ): + result = await mgr.add_subscription( + "https://example.com/channel", + check_interval_minutes=60, + download_type="video", + codec="auto", + format="any", + quality="best", + folder="", + custom_name_prefix="", + auto_start=True, + playlist_item_limit=0, + split_by_chapters=False, + chapter_template="", + subtitle_language="en", + subtitle_mode="prefer_manual", + skip_subscriber_only=True, + ) + self.assertTrue(mgr.list_all()[0].skip_subscriber_only) + await mgr.check_now([result["subscription"]["id"]]) + + sub = mgr.list_all()[0] + self.assertIsNone(sub.error) + self.assertEqual(sub.seen_ids[:2], ["v2", "v1"]) + self.assertEqual(queue.entries, []) + async def test_update_subscription_parses_string_false_enabled(self): with tempfile.TemporaryDirectory() as tmp: queue = _Queue() @@ -688,6 +809,72 @@ class SubscriptionPersistenceTests(unittest.IsolatedAsyncioTestCase): self.assertEqual(upd["subscription"]["title_regex"], "foo|bar") self.assertEqual(mgr.list_all()[0].title_regex, "foo|bar") + async def test_update_subscription_skip_subscriber_only(self): + with tempfile.TemporaryDirectory() as tmp: + queue = _Queue() + mgr = SubscriptionManager(_Config(tmp), queue, _Notifier()) + with patch( + "subscriptions.extract_flat_playlist", + return_value=( + {"_type": "channel", "title": "Channel"}, + [{"id": "v1", "title": "One", "webpage_url": "https://example.com/v1"}], + ), + ): + result = await mgr.add_subscription( + "https://example.com/channel", + check_interval_minutes=60, + download_type="video", + codec="auto", + format="any", + quality="best", + folder="", + custom_name_prefix="", + auto_start=True, + playlist_item_limit=0, + split_by_chapters=False, + chapter_template="", + subtitle_language="en", + subtitle_mode="prefer_manual", + ) + sub_id = result["subscription"]["id"] + self.assertFalse(mgr.list_all()[0].skip_subscriber_only) + upd = await mgr.update_subscription(sub_id, {"skip_subscriber_only": True}) + self.assertEqual(upd["status"], "ok") + self.assertTrue(upd["subscription"]["skip_subscriber_only"]) + self.assertTrue(mgr.list_all()[0].skip_subscriber_only) + + async def test_update_subscription_rejects_invalid_skip_subscriber_only(self): + with tempfile.TemporaryDirectory() as tmp: + queue = _Queue() + mgr = SubscriptionManager(_Config(tmp), queue, _Notifier()) + with patch( + "subscriptions.extract_flat_playlist", + return_value=( + {"_type": "channel", "title": "Channel"}, + [{"id": "v1", "title": "One", "webpage_url": "https://example.com/v1"}], + ), + ): + result = await mgr.add_subscription( + "https://example.com/channel", + check_interval_minutes=60, + download_type="video", + codec="auto", + format="any", + quality="best", + folder="", + custom_name_prefix="", + auto_start=True, + playlist_item_limit=0, + split_by_chapters=False, + chapter_template="", + subtitle_language="en", + subtitle_mode="prefer_manual", + ) + sub_id = result["subscription"]["id"] + upd = await mgr.update_subscription(sub_id, {"skip_subscriber_only": "maybe"}) + self.assertEqual(upd["status"], "error") + self.assertFalse(mgr.list_all()[0].skip_subscriber_only) + def test_persistence_includes_title_regex(self): with tempfile.TemporaryDirectory() as tmp: json_path = os.path.join(tmp, "subscriptions.json") @@ -728,6 +915,49 @@ class SubscriptionPersistenceTests(unittest.IsolatedAsyncioTestCase): ) mgr = SubscriptionManager(_Config(tmp), _Queue(), _Notifier()) self.assertEqual(mgr.list_all()[0].title_regex, "EPISODE") + self.assertFalse(mgr.list_all()[0].skip_subscriber_only) + + def test_persistence_includes_skip_subscriber_only(self): + with tempfile.TemporaryDirectory() as tmp: + json_path = os.path.join(tmp, "subscriptions.json") + with open(json_path, "w", encoding="utf-8") as f: + json.dump( + { + "schema_version": 2, + "kind": "subscriptions", + "items": [ + { + "id": "sub-1", + "name": "Channel", + "url": "https://example.com/channel", + "enabled": True, + "check_interval_minutes": 60, + "download_type": "video", + "codec": "auto", + "format": "any", + "quality": "best", + "folder": "", + "custom_name_prefix": "", + "auto_start": True, + "playlist_item_limit": 0, + "split_by_chapters": False, + "chapter_template": "", + "subtitle_language": "en", + "subtitle_mode": "prefer_manual", + "ytdl_options_presets": [], + "ytdl_options_overrides": {}, + "title_regex": "", + "skip_subscriber_only": True, + "last_checked": None, + "seen_ids": [], + "error": None, + } + ], + }, + f, + ) + mgr = SubscriptionManager(_Config(tmp), _Queue(), _Notifier()) + self.assertTrue(mgr.list_all()[0].skip_subscriber_only) class ExtractFlatPlaylistTests(unittest.TestCase): diff --git a/ui/src/app/app.html b/ui/src/app/app.html index 06d6476..b2ae0db 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -515,6 +515,15 @@ ngbTooltip="In subscriptions, only titles matching this Python-style regex are queued. Empty = all. Case-sensitive; use (?i) in the pattern for case-insensitive.">
+
+
+ + +
+
diff --git a/ui/src/app/app.spec.ts b/ui/src/app/app.spec.ts index 2ef3185..cdaf356 100644 --- a/ui/src/app/app.spec.ts +++ b/ui/src/app/app.spec.ts @@ -190,8 +190,21 @@ describe('App', () => { app.titleRegex = 'EPISODE'; app.addSubscription(); expect(subs.subscribeCalls.length).toBe(1); - const payload = subs.subscribeCalls[0] as { titleRegex: string }; + const payload = subs.subscribeCalls[0] as { titleRegex: string; skipSubscriberOnly: boolean }; expect(payload.titleRegex).toBe('EPISODE'); + expect(payload.skipSubscriberOnly).toBe(false); + }); + + it('includes skipSubscriberOnly true when checked', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + const subs = TestBed.inject(SubscriptionsService) as unknown as SubscriptionsServiceStub; + app.addUrl = 'https://example.com/channel'; + app.skipSubscriberOnly = true; + app.addSubscription(); + expect(subs.subscribeCalls.length).toBe(1); + const payload = subs.subscribeCalls[0] as { skipSubscriberOnly: boolean }; + expect(payload.skipSubscriberOnly).toBe(true); }); it('omits clip fields from subscribe payload', () => { diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index 8caf48f..a73f879 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -93,6 +93,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { subscribeInProgress = false; checkIntervalMinutes = 60; titleRegex = ''; + skipSubscriberOnly = false; editingTitleRegexId: string | null = null; titleRegexEditDraft = ''; cachedSubs: [string, SubscriptionRow][] = []; @@ -593,6 +594,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { ...subscribeBase, checkIntervalMinutes: this.checkIntervalMinutes, titleRegex: tr, + skipSubscriberOnly: this.skipSubscriberOnly, }) .pipe( takeUntilDestroyed(this.destroyRef), @@ -609,6 +611,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { } else { this.addUrl = ''; this.titleRegex = ''; + this.skipSubscriberOnly = false; } }, }); diff --git a/ui/src/app/interfaces/subscription.ts b/ui/src/app/interfaces/subscription.ts index f5ab68e..99b012e 100644 --- a/ui/src/app/interfaces/subscription.ts +++ b/ui/src/app/interfaces/subscription.ts @@ -10,6 +10,7 @@ export interface SubscriptionRow { quality: string; folder: string; title_regex?: string; + skip_subscriber_only?: boolean; last_checked: number | null; seen_count: number; error: string | null; diff --git a/ui/src/app/services/subscriptions.service.ts b/ui/src/app/services/subscriptions.service.ts index 4db08cf..a50be8e 100644 --- a/ui/src/app/services/subscriptions.service.ts +++ b/ui/src/app/services/subscriptions.service.ts @@ -11,6 +11,7 @@ import { AddDownloadPayload } from './downloads.service'; export interface SubscribePayload extends AddDownloadPayload { checkIntervalMinutes: number; titleRegex: string; + skipSubscriberOnly: boolean; } @Injectable({ @@ -99,6 +100,7 @@ export class SubscriptionsService { ytdl_options_overrides: payload.ytdlOptionsOverrides, check_interval_minutes: payload.checkIntervalMinutes, title_regex: payload.titleRegex, + skip_subscriber_only: payload.skipSubscriberOnly, }) .pipe(catchError((err) => this.handleHTTPError(err))); } @@ -109,7 +111,12 @@ export class SubscriptionsService { update( id: string, - changes: Partial>, + changes: Partial< + Pick< + SubscriptionRow, + 'enabled' | 'check_interval_minutes' | 'name' | 'title_regex' | 'skip_subscriber_only' + > + >, ) { return this.http .post('subscriptions/update', { id, ...changes }) From e2773db65a4bf976273c434102b7e868eb0a90d8 Mon Sep 17 00:00:00 2001 From: Sean McCollum Date: Mon, 4 May 2026 10:42:17 -0700 Subject: [PATCH 39/88] make ui more mobile mobile-friendly Remove popovers and replace with question icons you have to click Replace combobox in output > Download Folder with an autocomplete --- ui/package.json | 2 +- ui/src/app/app.html | 120 ++++++++++++++++++-------------------------- ui/src/app/app.sass | 12 +++++ ui/src/app/app.ts | 41 +++++++++++---- 4 files changed, 92 insertions(+), 83 deletions(-) diff --git a/ui/package.json b/ui/package.json index 6a48bde..7e9cfef 100644 --- a/ui/package.json +++ b/ui/package.json @@ -5,7 +5,7 @@ "ng": "ng", "start": "ng serve", "build": "ng build", - "build:watch": "ng build --watch", + "build:watch": "ng build --watch --configuration development", "test": "ng test", "lint": "ng lint" }, diff --git a/ui/src/app/app.html b/ui/src/app/app.html index b2ae0db..d0ec2db 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -279,13 +279,12 @@
- Format + Format + placeholder="e.g. en, es, zh-Hans"> @for (lang of subtitleLanguages; track lang.id) { @@ -313,13 +311,12 @@
- Subtitle Source + Subtitle Source - } + [ngbTypeahead]="searchFolder" + [editable]="!!downloads.configuration['CREATE_CUSTOM_DIRS']" + (focus)="folderFocus$.next($any($event.target).value)" + (click)="folderClick$.next($any($event.target).value)" + #folderTypeahead="ngbTypeahead" + [disabled]="addInProgress || subscribeInProgress || downloads.loading"> +
- Custom Name Prefix + Custom Name Prefix + [disabled]="addInProgress || subscribeInProgress || downloads.loading">
@@ -411,18 +404,17 @@
+ [disabled]="addInProgress || subscribeInProgress || downloads.loading"> +
@if (splitByChapters) {
- Template + Template + (change)="chapterTemplateChanged()" [disabled]="addInProgress || subscribeInProgress || downloads.loading">
} @@ -431,28 +423,26 @@ @if (downloadType === 'video' || downloadType === 'audio') {
- Clip start + Clip start + [disabled]="addInProgress || subscribeInProgress || downloads.loading">
- Clip end + Clip end + [disabled]="addInProgress || subscribeInProgress || downloads.loading">
} @@ -461,23 +451,18 @@
-
-
- Auto Start - +
+
+ + +
- Items Limit + Items Limit + [disabled]="addInProgress || subscribeInProgress || downloads.loading">
- Subscription Check (min) + Subscription Check (min) + [disabled]="addInProgress || subscribeInProgress || downloads.loading">
- Subscription Title Filter + Subscription Title Filter + placeholder="Optional regex">
+ [disabled]="addInProgress || subscribeInProgress || downloads.loading" /> +
@@ -531,7 +513,7 @@
- Option Presets + Option Presets + [disabled]="addInProgress || subscribeInProgress || downloads.loading" />
@if (allowYtdlOptionsOverrides()) {
- Custom yt-dlp Options + Custom yt-dlp Options + [disabled]="addInProgress || subscribeInProgress || downloads.loading">
} @@ -566,7 +546,7 @@
-
Cookies
+
Cookies
@@ -574,8 +554,7 @@
- Format + Format
- Subtitle Source + Subtitle Source
- Custom Name Prefix + Custom Name Prefix -
@if (splitByChapters) {
- Template + Template
@@ -423,7 +422,7 @@ @if (downloadType === 'video' || downloadType === 'audio') {
- Clip start + Clip start
- Clip end + Clip end - - +
- Items Limit + Items Limit
- Subscription Check (min) + Subscription Check (min)
- Subscription Title Filter + Subscription Title Filter - - +
@@ -513,7 +510,7 @@
- Option Presets + Option Presets
- Custom yt-dlp Options + Custom yt-dlp Options Tools
-
Cookies
+
Cookies
@@ -915,7 +912,7 @@ Name URL - Sub. title filter + Sub. title filter Interval (min) Last checked Status diff --git a/ui/src/app/app.sass b/ui/src/app/app.sass index 7f4240f..c84d122 100644 --- a/ui/src/app/app.sass +++ b/ui/src/app/app.sass @@ -201,16 +201,12 @@ main color: var(--bs-secondary-color) margin-bottom: 0.4rem -.help-icon - cursor: pointer - opacity: 0.55 - padding: 0.25rem - margin-left: 0.25rem - font-size: 0.85em - line-height: 1 +.help-title + text-decoration: underline dotted + text-underline-offset: 0.2em + cursor: help - &:hover, &:focus - opacity: 1 + &:focus outline: none .cookie-status diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index 06c881a..d145db7 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -7,7 +7,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { NgbModule, NgbTypeahead } from '@ng-bootstrap/ng-bootstrap'; import { NgSelectModule } from '@ng-select/ng-select'; -import { faTrashAlt, faCheckCircle, faTimesCircle, faRedoAlt, faSun, faMoon, faCheck, faCircleHalfStroke, faCircleQuestion, faDownload, faExternalLinkAlt, faFileImport, faFileExport, faCopy, faClock, faTachometerAlt, faSortAmountDown, faSortAmountUp, faChevronRight, faChevronDown, faUpload, faPause, faPlay } from '@fortawesome/free-solid-svg-icons'; +import { faTrashAlt, faCheckCircle, faTimesCircle, faRedoAlt, faSun, faMoon, faCheck, faCircleHalfStroke, faDownload, faExternalLinkAlt, faFileImport, faFileExport, faCopy, faClock, faTachometerAlt, faSortAmountDown, faSortAmountUp, faChevronRight, faChevronDown, faUpload, faPause, faPlay } from '@fortawesome/free-solid-svg-icons'; import { faGithub } from '@fortawesome/free-brands-svg-icons'; import { CookieService } from 'ngx-cookie-service'; import { AddDownloadPayload, DownloadsService } from './services/downloads.service'; @@ -172,7 +172,6 @@ export class App implements AfterViewInit, OnInit, OnDestroy { faMoon = faMoon; faCheck = faCheck; faCircleHalfStroke = faCircleHalfStroke; - faCircleQuestion = faCircleQuestion; faDownload = faDownload; faExternalLinkAlt = faExternalLinkAlt; faFileImport = faFileImport; From 39a8948976d7d9e113c8578d4298c5926af4ddac Mon Sep 17 00:00:00 2001 From: Helmut Date: Thu, 28 May 2026 07:00:40 +0200 Subject: [PATCH 41/88] feat(ui): add iOS Web Share button next to download link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a share button to the completed-list action row that hands the downloaded file off to the platform share sheet via navigator.share(). On iOS Safari/Chrome this surfaces the native Save-to-Photos / Save-to- Files / AirDrop options for videos and images, and Files / 3rd-party app targets for audio. On platforms without Web Share support (Desktop Firefox/Chrome/Safari) the button hides itself; the existing download link remains the universal fallback. Implementation notes: - canShareDownloads() requires both navigator.share AND navigator.canShare (Desktop Safari has the former without the latter; we always intend to share a file, not a URL) - shareDownload() fetches the file via the existing buildDownloadLink() helper, wraps it in a File, then runs canShare() before share() so we can bail out cleanly on platforms that reject the MIME type - AbortError (user dismisses sheet) is silenced; other errors logged - Tooltip on the button explains the iOS behaviour briefly Refs alexta69/metube#582 β€” addresses the 'add to Photos.app' request without depending on the iOS Shortcut, which has had reliability issues (cf #763). --- ui/src/app/app.html | 3 +++ ui/src/app/app.ts | 48 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/ui/src/app/app.html b/ui/src/app/app.html index b2ae0db..26f496a 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -854,6 +854,9 @@ @if (entry[1].filename) { } + @if (entry[1].filename && canShareDownloads()) { + + }
diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index a73f879..03448ee 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -7,7 +7,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { NgSelectModule } from '@ng-select/ng-select'; -import { faTrashAlt, faCheckCircle, faTimesCircle, faRedoAlt, faSun, faMoon, faCheck, faCircleHalfStroke, faDownload, faExternalLinkAlt, faFileImport, faFileExport, faCopy, faClock, faTachometerAlt, faSortAmountDown, faSortAmountUp, faChevronRight, faChevronDown, faUpload, faPause, faPlay } from '@fortawesome/free-solid-svg-icons'; +import { faTrashAlt, faCheckCircle, faTimesCircle, faRedoAlt, faSun, faMoon, faCheck, faCircleHalfStroke, faDownload, faExternalLinkAlt, faFileImport, faFileExport, faCopy, faClock, faTachometerAlt, faSortAmountDown, faSortAmountUp, faChevronRight, faChevronDown, faUpload, faPause, faPlay, faShareNodes } from '@fortawesome/free-solid-svg-icons'; import { faGithub } from '@fortawesome/free-brands-svg-icons'; import { CookieService } from 'ngx-cookie-service'; import { AddDownloadPayload, DownloadsService } from './services/downloads.service'; @@ -185,6 +185,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { faUpload = faUpload; faPause = faPause; faPlay = faPlay; + faShareNodes = faShareNodes; subtitleLanguages = [ { id: 'en', text: 'English' }, { id: 'ar', text: 'Arabic' }, @@ -1189,6 +1190,51 @@ export class App implements AfterViewInit, OnInit, OnDestroy { return baseDir + encodeURIComponent(download.filename); } + // Web Share API support β€” primarily for iOS Safari / Chrome, lets the user + // hand the downloaded file off to the platform share sheet (Photos.app, + // Files, third-party apps, AirDrop). Falls back silently to the standard + // download flow on platforms without navigator.share / canShare. + canShareDownloads(): boolean { + // navigator.share alone is not enough β€” Desktop Safari implements + // navigator.share but not canShare with files. We explicitly require + // both, since we always intend to share a file (not a URL). + return typeof navigator !== 'undefined' + && typeof navigator.share === 'function' + && typeof navigator.canShare === 'function'; + } + + async shareDownload(download: Download): Promise { + if (!this.canShareDownloads()) { + return; + } + try { + const response = await fetch(this.buildDownloadLink(download)); + if (!response.ok) { + throw new Error(`HTTP ${response.status} fetching file for share`); + } + const blob = await response.blob(); + const file = new File([blob], download.filename, { + type: blob.type || 'application/octet-stream', + }); + const payload: ShareData = { files: [file], title: download.title }; + if (!navigator.canShare(payload)) { + // File type not shareable on this platform (e.g. desktop browsers, + // or some MIME types iOS refuses). Bail out so the user can still + // use the regular download button right next to this one. + console.warn('navigator.canShare rejected payload for', download.filename); + return; + } + await navigator.share(payload); + } catch (err: any) { + // AbortError = user dismissed the share sheet β†’ silent no-op. + // Other errors (network, file too big, …) get logged but we don't + // surface a UI error: the regular download link remains a fallback. + if (err?.name !== 'AbortError') { + console.error('Share failed:', err); + } + } + } + buildResultItemTooltip(download: Download) { const parts = []; if (download.msg) { From 6ff364aacf8e993adf0ec56d4cec6ad508a09ab0 Mon Sep 17 00:00:00 2001 From: Helmut Date: Fri, 29 May 2026 04:56:52 +0200 Subject: [PATCH 42/88] feat(ui): warn before share + surface failures for large files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Web Share fails silently when iOS' share sheet refuses the payload, typically because the file exceeds the platform's soft size limit (~50–100 MB depending on iOS version). The previous patch logged to the console but the user saw nothing β€” staring at a button that 'does nothing' is poor UX. Adds two layers of feedback: 1. Pre-flight size check (SHARE_SIZE_WARN_BYTES = 80 MB, conservative relative to iOS' actual limit) with a confirm() dialog before the fetch. Avoids spending bandwidth pulling a 150 MB blob into the browser only for navigator.canShare to reject it. 2. Surfaces canShare-rejection AND share()-failure as a visible alert() suggesting the user fall back to the download link next to the share button. Tested locally with files from 0.7 MB up to 150.7 MB: small files share unchanged, the 150 MB file now produces a pre-flight warning the user can dismiss, and any subsequent rejection produces a clear alert instead of silently no-op'ing. --- ui/src/app/app.ts | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index 03448ee..089b78f 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -1203,10 +1203,28 @@ export class App implements AfterViewInit, OnInit, OnDestroy { && typeof navigator.canShare === 'function'; } + // Conservative warning threshold for the share sheet β€” iOS' actual + // refusal limit varies between ~50 MB (older versions) and ~150 MB + // (recent ones). 80 MB warns the user before the time-wasting fetch+ + // copy of a too-large file that the platform will then reject. + private static readonly SHARE_SIZE_WARN_BYTES = 80 * 1024 * 1024; + async shareDownload(download: Download): Promise { if (!this.canShareDownloads()) { return; } + // Pre-flight size check: warn the user about the iOS share-sheet + // soft-fail on large files, before we spend time fetching the whole + // file into memory only to have navigator.canShare reject it. + if (download.size && download.size > App.SHARE_SIZE_WARN_BYTES) { + const sizeMb = Math.round(download.size / 1024 / 1024); + const proceed = window.confirm( + `This file is ${sizeMb} MB. iOS' share sheet often refuses files ` + + `larger than ~100 MB and the share will silently fail. ` + + `Try anyway? (Use the download button instead if it fails.)` + ); + if (!proceed) return; + } try { const response = await fetch(this.buildDownloadLink(download)); if (!response.ok) { @@ -1218,20 +1236,28 @@ export class App implements AfterViewInit, OnInit, OnDestroy { }); const payload: ShareData = { files: [file], title: download.title }; if (!navigator.canShare(payload)) { - // File type not shareable on this platform (e.g. desktop browsers, - // or some MIME types iOS refuses). Bail out so the user can still - // use the regular download button right next to this one. + // The platform refused the payload β€” most commonly because the + // file is too large for the iOS share sheet, or the MIME type + // isn't accepted. Tell the user so they can fall back to the + // download button right next to this one instead of staring at + // a button that quietly did nothing. console.warn('navigator.canShare rejected payload for', download.filename); + window.alert( + `Your device's share sheet doesn't accept this file ` + + `(most likely because it's too large). ` + + `Please use the download button instead.` + ); return; } await navigator.share(payload); } catch (err: any) { // AbortError = user dismissed the share sheet β†’ silent no-op. - // Other errors (network, file too big, …) get logged but we don't - // surface a UI error: the regular download link remains a fallback. - if (err?.name !== 'AbortError') { - console.error('Share failed:', err); - } + if (err?.name === 'AbortError') return; + console.error('Share failed:', err); + window.alert( + `Share failed: ${err?.message || 'unknown error'}. ` + + `Please use the download button instead.` + ); } } From ad92607a21bc58fe4ca985f2382e293f609da0a4 Mon Sep 17 00:00:00 2001 From: Helmut Date: Fri, 29 May 2026 05:24:49 +0200 Subject: [PATCH 43/88] fix(ui): drop redundant tooltip on share button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iOS doesn't have hover, so the tooltip only ever showed on desktop β€” where the share-arrow glyph is universally recognised anyway. Aria- label stays for screen readers. --- ui/src/app/app.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/app/app.html b/ui/src/app/app.html index 26f496a..847a628 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -855,7 +855,7 @@ } @if (entry[1].filename && canShareDownloads()) { - + } From 4478d1394ee799e549f334439f76fea71de38ace Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 29 May 2026 13:20:04 +0300 Subject: [PATCH 44/88] upgrade dependencies --- ui/package.json | 28 +- ui/pnpm-lock.yaml | 1629 +++++++++++++++++++++++---------------------- uv.lock | 454 ++++++------- 3 files changed, 1073 insertions(+), 1038 deletions(-) diff --git a/ui/package.json b/ui/package.json index 6a48bde..2608c00 100644 --- a/ui/package.json +++ b/ui/package.json @@ -23,21 +23,21 @@ }, "private": true, "dependencies": { - "@angular/animations": "^21.2.9", - "@angular/common": "^21.2.9", - "@angular/compiler": "^21.2.9", - "@angular/core": "^21.2.9", - "@angular/forms": "^21.2.9", - "@angular/platform-browser": "^21.2.9", - "@angular/platform-browser-dynamic": "^21.2.9", - "@angular/service-worker": "^21.2.9", + "@angular/animations": "^21.2.15", + "@angular/common": "^21.2.15", + "@angular/compiler": "^21.2.15", + "@angular/core": "^21.2.15", + "@angular/forms": "^21.2.15", + "@angular/platform-browser": "^21.2.15", + "@angular/platform-browser-dynamic": "^21.2.15", + "@angular/service-worker": "^21.2.15", "@fortawesome/angular-fontawesome": "~4.0.0", "@fortawesome/fontawesome-svg-core": "^7.2.0", "@fortawesome/free-brands-svg-icons": "^7.2.0", "@fortawesome/free-regular-svg-icons": "^7.2.0", "@fortawesome/free-solid-svg-icons": "^7.2.0", "@ng-bootstrap/ng-bootstrap": "^20.0.0", - "@ng-select/ng-select": "^21.8.0", + "@ng-select/ng-select": "^21.8.2", "@popperjs/core": "^2.11.8", "bootstrap": "^5.3.8", "ngx-cookie-service": "^21.3.1", @@ -48,16 +48,16 @@ }, "devDependencies": { "@angular-eslint/builder": "21.1.0", - "@angular/build": "^21.2.7", - "@angular/cli": "^21.2.7", - "@angular/compiler-cli": "^21.2.9", - "@angular/localize": "^21.2.9", + "@angular/build": "^21.2.13", + "@angular/cli": "^21.2.13", + "@angular/compiler-cli": "^21.2.15", + "@angular/localize": "^21.2.15", "@eslint/js": "^9.39.4", "angular-eslint": "21.1.0", "eslint": "^9.39.4", "jsdom": "^27.4.0", "typescript": "~5.9.3", "typescript-eslint": "8.47.0", - "vitest": "^4.1.5" + "vitest": "^4.1.7" } } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index beb791e..9c0d5c8 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -9,32 +9,32 @@ importers: .: dependencies: '@angular/animations': - specifier: ^21.2.9 - version: 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^21.2.15 + version: 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/common': - specifier: ^21.2.9 - version: 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^21.2.15 + version: 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@angular/compiler': - specifier: ^21.2.9 - version: 21.2.9 + specifier: ^21.2.15 + version: 21.2.15 '@angular/core': - specifier: ^21.2.9 - version: 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^21.2.15 + version: 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) '@angular/forms': - specifier: ^21.2.9 - version: 21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + specifier: ^21.2.15 + version: 21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) '@angular/platform-browser': - specifier: ^21.2.9 - version: 21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^21.2.15 + version: 21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/platform-browser-dynamic': - specifier: ^21.2.9 - version: 21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.9)(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))) + specifier: ^21.2.15 + version: 21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.15)(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))) '@angular/service-worker': - specifier: ^21.2.9 - version: 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^21.2.15 + version: 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@fortawesome/angular-fontawesome': specifier: ~4.0.0 - version: 4.0.0(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) + version: 4.0.0(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) '@fortawesome/fontawesome-svg-core': specifier: ^7.2.0 version: 7.2.0 @@ -49,10 +49,10 @@ importers: version: 7.2.0 '@ng-bootstrap/ng-bootstrap': specifier: ^20.0.0 - version: 20.0.0(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9))(@popperjs/core@2.11.8)(rxjs@7.8.2) + version: 20.0.0(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15))(@popperjs/core@2.11.8)(rxjs@7.8.2) '@ng-select/ng-select': - specifier: ^21.8.0 - version: 21.8.0(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) + specifier: ^21.8.2 + version: 21.8.2(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) '@popperjs/core': specifier: ^2.11.8 version: 2.11.8 @@ -61,10 +61,10 @@ importers: version: 5.3.8(@popperjs/core@2.11.8) ngx-cookie-service: specifier: ^21.3.1 - version: 21.3.1(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) + version: 21.3.1(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) ngx-socket-io: specifier: ~4.10.0 - version: 4.10.0(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + version: 4.10.0(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) rxjs: specifier: ~7.8.2 version: 7.8.2 @@ -77,25 +77,25 @@ importers: devDependencies: '@angular-eslint/builder': specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular/build': - specifier: ^21.2.7 - version: 21.2.7(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9)(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(chokidar@5.0.0)(postcss@8.5.10)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.5(@types/node@25.6.0)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3))) + specifier: ^21.2.13 + version: 21.2.13(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15)(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.9.1)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3))) '@angular/cli': - specifier: ^21.2.7 - version: 21.2.7(@types/node@25.6.0)(chokidar@5.0.0) + specifier: ^21.2.13 + version: 21.2.13(@types/node@25.9.1)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^21.2.9 - version: 21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3) + specifier: ^21.2.15 + version: 21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3) '@angular/localize': - specifier: ^21.2.9 - version: 21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9) + specifier: ^21.2.15 + version: 21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15) '@eslint/js': specifier: ^9.39.4 version: 9.39.4 angular-eslint: specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) eslint: specifier: ^9.39.4 version: 9.39.4 @@ -109,8 +109,8 @@ importers: specifier: 8.47.0 version: 8.47.0(eslint@9.39.4)(typescript@5.9.3) vitest: - specifier: ^4.1.5 - version: 4.1.5(@types/node@25.6.0)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3)) + specifier: ^4.1.7 + version: 4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)) packages: @@ -177,13 +177,13 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.2102.7': - resolution: {integrity: sha512-4K/5hln9iaPEt3F/NyYqncNLvYpzSjRslEkHl2xIgZwQsIFHEvhnDRBYj2/oatURQhBqO/Yu15z/icVOYLxuTg==} + '@angular-devkit/architect@0.2102.13': + resolution: {integrity: sha512-fheyi0gPx6b7tT+WQ+ePlzdGqKjPLUK72wg5Z9pkVtQ5+VN/8yB9mlRlmoivngd2FeNG9wMeNynWZGYycnOWVw==} 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'} hasBin: true - '@angular-devkit/core@21.2.7': - resolution: {integrity: sha512-DONYY5u4IENO2qpd23mODaE4JI2EIohWV1kuJnsU9HIcm5wN714QB2z9WY/s4gLfUiAMIUu/8lpnW/0kOQZAnQ==} + '@angular-devkit/core@21.2.13': + resolution: {integrity: sha512-9jLaHcUr6BumIY9nCsBib1q62p259nf++gd2igYJ7mLm1w/0wEacsZ1cC8wCGEe6vx8a+DrD+EVCQ6zivePG2A==} 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: ^5.0.0 @@ -191,8 +191,8 @@ packages: chokidar: optional: true - '@angular-devkit/schematics@21.2.7': - resolution: {integrity: sha512-LYAjjUI1qM7pR/sd0yYt8OLA6ljOOXjcfzV40I5XQNmhAxq90YYS5xwMcixOmWX+z5zvCYGvPXvJGWjzio6SUg==} + '@angular-devkit/schematics@21.2.13': + resolution: {integrity: sha512-gifpOcMNiAy49lQmQKhzpxoSfS3qJQSEdJSF5m7RVFkAcmllfcCD76GPN4dhho3wdAnbZ3qr54LtDqrGY4xNjw==} 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'} '@angular-eslint/builder@21.1.0': @@ -239,14 +239,14 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '*' - '@angular/animations@21.2.9': - resolution: {integrity: sha512-wOWbrneivpTYx3xhiPygoNFNC8ZZ1shpgwBe1hYvfky1fkiz1c92XeHIW1V4rPqYw6d3U55aUX5InyTsVe2Xng==} + '@angular/animations@21.2.15': + resolution: {integrity: sha512-Z8AsLTwc++Fcu0fJnclAF9zMfumAd5KXrwtSdyECqLpqd+lEmmsOpeOl6P7loqdDz99KYh/8UF4eJxdMvnsaKw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.2.9 + '@angular/core': 21.2.15 - '@angular/build@21.2.7': - resolution: {integrity: sha512-FpSkFqpsJtdN1cROekVYkmeV1QepdP+/d7fyYQEuNmlOlyqXSDh9qJmy4iL9VNbAU0rk+vFCtYM86rO7Pt9cSw==} + '@angular/build@21.2.13': + resolution: {integrity: sha512-Y9TDAaTQ+E5LScCKA/hPZmns/7Mpu6J2BiPj2cETA1xNjvgRpeb5Mh32KuhZb20NSFLvjpdnLuBTTtbym7hevw==} 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: '@angular/compiler': ^21.0.0 @@ -256,7 +256,7 @@ packages: '@angular/platform-browser': ^21.0.0 '@angular/platform-server': ^21.0.0 '@angular/service-worker': ^21.0.0 - '@angular/ssr': ^21.2.7 + '@angular/ssr': ^21.2.13 karma: ^6.4.0 less: ^4.2.0 ng-packagr: ^21.0.0 @@ -291,38 +291,38 @@ packages: vitest: optional: true - '@angular/cli@21.2.7': - resolution: {integrity: sha512-N/wj8fFRB718efIFYpwnYfy+MecZREZXsUNMTVndFLH6T0jCheb9PVetR6jsyZp6h46USNPOmJYJ/9255lME+Q==} + '@angular/cli@21.2.13': + resolution: {integrity: sha512-j1kOV/f0og/3xCwG7Y8RyPd6V7uYfX2NuvXbvN1mzgxLLN2mu6CTsvPg5l/9Pu9SJI3KOPRgDxWyuP3k8KuzMg==} 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'} hasBin: true - '@angular/common@21.2.9': - resolution: {integrity: sha512-7spQcF3hPN/fjTx6Pwa32KRRdO0NcixnRuPV4lo50ejtXesjiLVR+fkaX38sawAyGoq89IuuYvUDrbLwCMypmQ==} + '@angular/common@21.2.15': + resolution: {integrity: sha512-PHbICQe4YCXnax2FcmKUpiffs8XPW9A0KlZF35qgJoQyBMBZx5F8c8geCh25jxtq77n3eBTmOa/WIAdSqiitkQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.2.9 + '@angular/core': 21.2.15 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@21.2.9': - resolution: {integrity: sha512-hTTW/OiqTXrwTneS18CMp47OX0XSbLYl2rIomLS3nXVJniSETH6S/k+LqQtGWWgLbzsd3PzUOOckHnvzpTBTsA==} + '@angular/compiler-cli@21.2.15': + resolution: {integrity: sha512-/MU7OA9d/e9P5SthR+N6JJObBmzcGsgNQaeQ2YfSUnU0lCRVQweTWwxLFDbfU6UX8MZFWB6pdI57zod8r5kXUw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.9 + '@angular/compiler': 21.2.15 typescript: '>=5.9 <6.1' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@21.2.9': - resolution: {integrity: sha512-clsK1EsSPtAuqlRl4CciA/gsvsW7xe0eWcvHxtrMW6DYaUJ6X4AAuDxEEJ5cf/3Mpw4s8KssjIUPPtbrUIGLSQ==} + '@angular/compiler@21.2.15': + resolution: {integrity: sha512-nwpNb+NbVUNzR3cck0QXbU/oFK7BpmXOXVnN/w7+P4+TsFUYeTtO1Ojbc15jkqe6mSM0lBvGlcoztVblHQkqcw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - '@angular/core@21.2.9': - resolution: {integrity: sha512-uZLq2aedJ+0uEZxyf6a1Nc7y1aZ7akAW7K1Kon8JUDZOvI2IDbk0i00MzkELt8q9uSmSSqg9zNKuhjspFf0Pyw==} + '@angular/core@21.2.15': + resolution: {integrity: sha512-J5JsUnNtQURdeA7EA3DoCsMBizW3l01gfqM326Al72Ou3woFWmRb5P3LOXpIOzAeMQhO6Z5tW+B1t+4qmoq7uw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/compiler': 21.2.9 + '@angular/compiler': 21.2.15 rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 || ~0.16.0 peerDependenciesMeta: @@ -331,49 +331,49 @@ packages: zone.js: optional: true - '@angular/forms@21.2.9': - resolution: {integrity: sha512-qXLnzmsJoHMgV/gDU7AZgsKBhUH7k6im6V9YuY5UpHHl+nGKCWxtePAZRB0OH2AsqzLwER3Fv2S6+mtmb7651w==} + '@angular/forms@21.2.15': + resolution: {integrity: sha512-swGUHgbBrPNvODPR9qBP6+vT2EHiyW361iEgS3HpTmvDhF/kD4l8NE0vh3P5N0DnEtGh4umOCKfQ1w6hPJ7lqA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.2.9 - '@angular/core': 21.2.9 - '@angular/platform-browser': 21.2.9 + '@angular/common': 21.2.15 + '@angular/core': 21.2.15 + '@angular/platform-browser': 21.2.15 rxjs: ^6.5.3 || ^7.4.0 - '@angular/localize@21.2.9': - resolution: {integrity: sha512-xf6C1XPm4JU2KlCQR8/XdsJPPmienTVGM71bCgVRu9dT3UiJbO7UKSA8Jc4XoDio5/mZ8VYFXBA/ozlQj22+Dw==} + '@angular/localize@21.2.15': + resolution: {integrity: sha512-zZWw3zidZDURx8iTXR8W5XqE8t6adWAVyE/R0954l6p1JdC5RJuBCs9XxHL8yZEjWZye7inpI/ypJ1+vNHCycg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.9 - '@angular/compiler-cli': 21.2.9 + '@angular/compiler': 21.2.15 + '@angular/compiler-cli': 21.2.15 - '@angular/platform-browser-dynamic@21.2.9': - resolution: {integrity: sha512-Z+2vefW4GUSuTC4BOKNiyftqecLSjxOKwe1ZNljBsjesLzywIXi+v+tyEm8ODHHlf7bz/0HwXvc9OYZmfjt95A==} + '@angular/platform-browser-dynamic@21.2.15': + resolution: {integrity: sha512-3xvlWLZlsWjPyJFGatOOsod/f5AFjmSUDoOXo0zsr2ckHc4TxbDTnkLULhRSWv6m68fKOdQb8Si8rI15gC5yqA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.2.9 - '@angular/compiler': 21.2.9 - '@angular/core': 21.2.9 - '@angular/platform-browser': 21.2.9 + '@angular/common': 21.2.15 + '@angular/compiler': 21.2.15 + '@angular/core': 21.2.15 + '@angular/platform-browser': 21.2.15 - '@angular/platform-browser@21.2.9': - resolution: {integrity: sha512-MjEtFvoFtsjsAeu2yzauqGgwwEHV4ml25c9vGFmw4OmSoNme4yp41f2DegwOkn1TTHL3OF3GE65ng2U2feJU4Q==} + '@angular/platform-browser@21.2.15': + resolution: {integrity: sha512-O4ZHVV/rxkK1AuiD9M3UssL/HkoQvBcZy2+U421IMNibclGhwH9aRwc/0ZlQ7zpseS9+KPZ23FebvN4/92IbPg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/animations': 21.2.9 - '@angular/common': 21.2.9 - '@angular/core': 21.2.9 + '@angular/animations': 21.2.15 + '@angular/common': 21.2.15 + '@angular/core': 21.2.15 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/service-worker@21.2.9': - resolution: {integrity: sha512-rAjUo9T9tV/nbcAOjFVAjn6mbHlh358/KLCKX7WzXl/fXVUB0lvlpzi+NnZbc9zHIP3IEdrg5HYsuaSIWSbiUQ==} + '@angular/service-worker@21.2.15': + resolution: {integrity: sha512-fnt5gFip6ZIz5D4yOipCfGTafTsg8fqbRIumCelfHFmPy1RtdK9Ij3ogos+yS1WpkNDWmP+OdLqC2JB+0LfSeQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/core': 21.2.9 + '@angular/core': 21.2.15 rxjs: ^6.5.3 || ^7.4.0 '@asamuzakjp/css-color@4.1.2': @@ -385,40 +385,40 @@ packages: '@asamuzakjp/nwsapi@2.3.9': resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} - '@babel/code-frame@7.29.0': - resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.0': - resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} '@babel/core@7.29.0': resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.29.1': - resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -427,52 +427,52 @@ packages: resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.29.2': - resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.2': - resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.29.0': - resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} engines: {node: '>=6.9.0'} - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} '@csstools/color-helpers@6.0.2': resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} engines: {node: '>=20.19.0'} - '@csstools/css-calc@3.2.0': - resolution: {integrity: sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==} + '@csstools/css-calc@3.2.1': + resolution: {integrity: sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-color-parser@4.1.0': - resolution: {integrity: sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==} + '@csstools/css-color-parser@4.1.1': + resolution: {integrity: sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -484,8 +484,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.3': - resolution: {integrity: sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==} + '@csstools/css-syntax-patches-for-csstree@1.1.4': + resolution: {integrity: sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -699,8 +699,8 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@exodus/bytes@1.15.0': - resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==} + '@exodus/bytes@1.15.1': + resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: '@noble/hashes': ^1.8.0 || ^2.0.0 @@ -976,33 +976,33 @@ packages: '@cfworker/json-schema': optional: true - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': - resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': + resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==} cpu: [arm64] os: [darwin] - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': - resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4': + resolution: {integrity: sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==} cpu: [x64] os: [darwin] - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': - resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4': + resolution: {integrity: sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==} cpu: [arm64] os: [linux] - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': - resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4': + resolution: {integrity: sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==} cpu: [arm] os: [linux] - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': - resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4': + resolution: {integrity: sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==} cpu: [x64] os: [linux] - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': - resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': + resolution: {integrity: sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==} cpu: [x64] os: [win32] @@ -1135,8 +1135,8 @@ packages: '@popperjs/core': ^2.11.8 rxjs: ^6.5.3 || ^7.4.0 - '@ng-select/ng-select@21.8.0': - resolution: {integrity: sha512-PXJFfitM7VO/ynqjJ3wKGoRETU6y0ARjcz5bA5V420fX0H6WyU3O91Ex5uFDNx08DzT4fU1GHPs0KXxI+LYOVg==} + '@ng-select/ng-select@21.8.2': + resolution: {integrity: sha512-Gk5xlVKLOG3WTIgOteduwlKBZTt0njkoUXheKgUSKn+h+poYQAf61uENCK08Tj0XWxes9BQUc5J+R2GC+86jgA==} engines: {node: ^20.19.0 || ^22.12.0 || ^24.0.0} peerDependencies: '@angular/common': ^21.0.0 @@ -1155,8 +1155,8 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@npmcli/agent@4.0.0': - resolution: {integrity: sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==} + '@npmcli/agent@4.0.2': + resolution: {integrity: sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg==} engines: {node: ^20.17.0 || >=22.9.0} '@npmcli/fs@5.0.0': @@ -1370,154 +1370,154 @@ packages: '@rolldown/pluginutils@1.0.0-rc.4': resolution: {integrity: sha512-1BrrmTu0TWfOP1riA8uakjFc9bpIUGzVKETsOtzY39pPga8zELGDl8eu1Dx7/gjM5CAz14UknsUMpBO8L+YntQ==} - '@rollup/rollup-android-arm-eabi@4.60.2': - resolution: {integrity: sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==} + '@rollup/rollup-android-arm-eabi@4.60.4': + resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.60.2': - resolution: {integrity: sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==} + '@rollup/rollup-android-arm64@4.60.4': + resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.60.2': - resolution: {integrity: sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==} + '@rollup/rollup-darwin-arm64@4.60.4': + resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.60.2': - resolution: {integrity: sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==} + '@rollup/rollup-darwin-x64@4.60.4': + resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.60.2': - resolution: {integrity: sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==} + '@rollup/rollup-freebsd-arm64@4.60.4': + resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.60.2': - resolution: {integrity: sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==} + '@rollup/rollup-freebsd-x64@4.60.4': + resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.60.2': - resolution: {integrity: sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==} + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.60.2': - resolution: {integrity: sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==} + '@rollup/rollup-linux-arm-musleabihf@4.60.4': + resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.60.2': - resolution: {integrity: sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==} + '@rollup/rollup-linux-arm64-gnu@4.60.4': + resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.60.2': - resolution: {integrity: sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==} + '@rollup/rollup-linux-arm64-musl@4.60.4': + resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.60.2': - resolution: {integrity: sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==} + '@rollup/rollup-linux-loong64-gnu@4.60.4': + resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.60.2': - resolution: {integrity: sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==} + '@rollup/rollup-linux-loong64-musl@4.60.4': + resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.60.2': - resolution: {integrity: sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==} + '@rollup/rollup-linux-ppc64-gnu@4.60.4': + resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.60.2': - resolution: {integrity: sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==} + '@rollup/rollup-linux-ppc64-musl@4.60.4': + resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.60.2': - resolution: {integrity: sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==} + '@rollup/rollup-linux-riscv64-gnu@4.60.4': + resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.60.2': - resolution: {integrity: sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==} + '@rollup/rollup-linux-riscv64-musl@4.60.4': + resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.60.2': - resolution: {integrity: sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==} + '@rollup/rollup-linux-s390x-gnu@4.60.4': + resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.60.2': - resolution: {integrity: sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==} + '@rollup/rollup-linux-x64-gnu@4.60.4': + resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.60.2': - resolution: {integrity: sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==} + '@rollup/rollup-linux-x64-musl@4.60.4': + resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.60.2': - resolution: {integrity: sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==} + '@rollup/rollup-openbsd-x64@4.60.4': + resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.60.2': - resolution: {integrity: sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==} + '@rollup/rollup-openharmony-arm64@4.60.4': + resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.60.2': - resolution: {integrity: sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==} + '@rollup/rollup-win32-arm64-msvc@4.60.4': + resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.2': - resolution: {integrity: sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==} + '@rollup/rollup-win32-ia32-msvc@4.60.4': + resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.60.2': - resolution: {integrity: sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==} + '@rollup/rollup-win32-x64-gnu@4.60.4': + resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.2': - resolution: {integrity: sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==} + '@rollup/rollup-win32-x64-msvc@4.60.4': + resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} cpu: [x64] os: [win32] - '@schematics/angular@21.2.7': - resolution: {integrity: sha512-aqEj3RyBtmH+41HZvrbfrpCo0e+0NzwyQyNSC/wLDShVqoidBtPbEdHU1FZ4+ni41da7rI3F12gUuAHws27kMA==} + '@schematics/angular@21.2.13': + resolution: {integrity: sha512-e5guslSLKbb3PJ6gUuVqM+V9xgn68cJkG1IyBohho34shbpOeoWW2eYdWQQjxvn0KUdgEhYSRBluBamCHngaUA==} 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'} '@sigstore/bundle@4.0.0': resolution: {integrity: sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==} engines: {node: ^20.17.0 || >=22.9.0} - '@sigstore/core@3.2.0': - resolution: {integrity: sha512-kxHrDQ9YgfrWUSXU0cjsQGv8JykOFZQ9ErNKbFPWzk3Hgpwu8x2hHrQ9IdA8yl+j9RTLTC3sAF3Tdq1IQCP4oA==} + '@sigstore/core@3.2.1': + resolution: {integrity: sha512-qRsxPnCrbC/puegGxKuynfnxgLiHqWStrSjxkoB4YKqq3Z3s4cyZyj42ZdWFAEblNP65C+rBH8EuREHIXoi83g==} engines: {node: ^20.17.0 || >=22.9.0} '@sigstore/protobuf-specs@0.5.1': @@ -1532,8 +1532,8 @@ packages: resolution: {integrity: sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ==} engines: {node: ^20.17.0 || >=22.9.0} - '@sigstore/verify@3.1.0': - resolution: {integrity: sha512-mNe0Iigql08YupSOGv197YdHpPPr+EzDZmfCgMc7RPNaZTw5aLN01nBl6CHJOh3BGtnMIj83EeN4butBchc8Ag==} + '@sigstore/verify@3.1.1': + resolution: {integrity: sha512-qv7+G3J2cc6wwFj3yKvXOamzqhMwSk1ogPGmhpS8iXllcPrJaIIBA+4HbttlHVu1pqWTdmaCH/WE7UOC51kdoA==} engines: {node: ^20.17.0 || >=22.9.0} '@socket.io/component-emitter@3.1.2': @@ -1550,8 +1550,8 @@ packages: resolution: {integrity: sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==} engines: {node: ^20.17.0 || >=22.9.0} - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1580,11 +1580,14 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@25.6.0': - resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} + '@types/node@25.9.1': + resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -1610,8 +1613,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.59.0': - resolution: {integrity: sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==} + '@typescript-eslint/project-service@8.60.0': + resolution: {integrity: sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1620,8 +1623,8 @@ packages: resolution: {integrity: sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.59.0': - resolution: {integrity: sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==} + '@typescript-eslint/scope-manager@8.60.0': + resolution: {integrity: sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.47.0': @@ -1630,8 +1633,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/tsconfig-utils@8.59.0': - resolution: {integrity: sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==} + '@typescript-eslint/tsconfig-utils@8.60.0': + resolution: {integrity: sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1647,8 +1650,8 @@ packages: resolution: {integrity: sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.59.0': - resolution: {integrity: sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==} + '@typescript-eslint/types@8.60.0': + resolution: {integrity: sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.47.0': @@ -1657,8 +1660,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.59.0': - resolution: {integrity: sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==} + '@typescript-eslint/typescript-estree@8.60.0': + resolution: {integrity: sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1670,8 +1673,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.59.0': - resolution: {integrity: sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==} + '@typescript-eslint/utils@8.60.0': + resolution: {integrity: sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -1681,8 +1684,8 @@ packages: resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.59.0': - resolution: {integrity: sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==} + '@typescript-eslint/visitor-keys@8.60.0': + resolution: {integrity: sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-basic-ssl@2.1.4': @@ -1691,11 +1694,11 @@ packages: peerDependencies: vite: ^6.0.0 || ^7.0.0 - '@vitest/expect@4.1.5': - resolution: {integrity: sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==} + '@vitest/expect@4.1.7': + resolution: {integrity: sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==} - '@vitest/mocker@4.1.5': - resolution: {integrity: sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==} + '@vitest/mocker@4.1.7': + resolution: {integrity: sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1705,20 +1708,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.5': - resolution: {integrity: sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==} + '@vitest/pretty-format@4.1.7': + resolution: {integrity: sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==} - '@vitest/runner@4.1.5': - resolution: {integrity: sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==} + '@vitest/runner@4.1.7': + resolution: {integrity: sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==} - '@vitest/snapshot@4.1.5': - resolution: {integrity: sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==} + '@vitest/snapshot@4.1.7': + resolution: {integrity: sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==} - '@vitest/spy@4.1.5': - resolution: {integrity: sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==} + '@vitest/spy@4.1.7': + resolution: {integrity: sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==} - '@vitest/utils@4.1.5': - resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==} + '@vitest/utils@4.1.7': + resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==} '@yarnpkg/lockfile@1.1.0': resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} @@ -1757,12 +1760,15 @@ packages: ajv: optional: true - ajv@6.14.0: - resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + algoliasearch@5.48.1: resolution: {integrity: sha512-Rf7xmeuIo7nb6S4mp4abW2faW8DauZyE2faBIKFaUfP3wnpOvNSbiI5AwVhqBNj0jPgBWEvhyCu0sLjN2q77Rg==} engines: {node: '>= 14.0.0'} @@ -1821,8 +1827,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.10.20: - resolution: {integrity: sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==} + baseline-browser-mapping@2.10.32: + resolution: {integrity: sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==} engines: {node: '>=6.0.0'} hasBin: true @@ -1845,14 +1851,14 @@ packages: peerDependencies: '@popperjs/core': ^2.11.8 - brace-expansion@1.1.14: - resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + brace-expansion@1.1.15: + resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} - brace-expansion@2.1.0: - resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} + brace-expansion@2.1.1: + resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} - brace-expansion@5.0.5: - resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} braces@3.0.3: @@ -1887,8 +1893,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001788: - resolution: {integrity: sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==} + caniuse-lite@1.0.30001793: + resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -1958,6 +1964,10 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} + convert-source-map@1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -2045,8 +2055,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.340: - resolution: {integrity: sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==} + electron-to-chromium@1.5.364: + resolution: {integrity: sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2058,15 +2068,15 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - engine.io-client@6.6.4: - resolution: {integrity: sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==} + engine.io-client@6.6.5: + resolution: {integrity: sha512-QCwxUDULPlXv8F6tqMMKx5dNkTe6OaBYRMPYeXKBlyOoKvAmE0ac6pW7fFhSscJ/5SI7666/U/B+MElbsrJlIg==} engine.io-parser@5.2.3: resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} engines: {node: '>=10.0.0'} - engine.io@6.6.6: - resolution: {integrity: sha512-U2SN0w3OpjFRVlrc17E6TMDmH58Xl9rai1MblNjAdwWp07Kk+llmzX0hjDpQdrDGzwmvOtgM5yI+meYX6iZ2xA==} + engine.io@6.6.8: + resolution: {integrity: sha512-2agL3ueZhqxoVrfmntO8yuVj+uNSlIOnhykYHk3Cq0ShYPdUjjUiSJrQvXjq01I9jAuI0Zl2YO8Evv5Mqytm5g==} engines: {node: '>=10.2.0'} entities@4.5.0: @@ -2104,11 +2114,11 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@2.0.0: - resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} esbuild@0.27.3: @@ -2187,8 +2197,8 @@ packages: eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} - eventsource-parser@3.0.8: - resolution: {integrity: sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==} + eventsource-parser@3.1.0: + resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==} engines: {node: '>=18.0.0'} eventsource@3.0.7: @@ -2202,8 +2212,8 @@ packages: exponential-backoff@3.1.3: resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} - express-rate-limit@8.3.2: - resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==} + express-rate-limit@8.5.2: + resolution: {integrity: sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A==} engines: {node: '>= 16'} peerDependencies: express: '>= 4.11' @@ -2225,8 +2235,8 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fast-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -2291,8 +2301,8 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-east-asian-width@1.5.0: - resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} + get-east-asian-width@1.6.0: + resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} engines: {node: '>=18'} get-intrinsic@1.3.0: @@ -2340,16 +2350,16 @@ packages: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} - hasown@2.0.3: - resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} - hono@4.12.14: - resolution: {integrity: sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==} + hono@4.12.23: + resolution: {integrity: sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==} engines: {node: '>=16.9.0'} - hosted-git-info@9.0.2: - resolution: {integrity: sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==} + hosted-git-info@9.0.3: + resolution: {integrity: sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==} engines: {node: ^20.17.0 || >=22.9.0} html-encoding-sniffer@6.0.0: @@ -2390,8 +2400,8 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - immutable@5.1.5: - resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==} + immutable@5.1.6: + resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -2408,8 +2418,8 @@ packages: resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} engines: {node: ^20.17.0 || >=22.9.0} - ip-address@10.1.0: - resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} engines: {node: '>= 12'} ipaddr.js@1.9.1: @@ -2465,8 +2475,8 @@ packages: resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} engines: {node: '>=10'} - jose@6.2.2: - resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -2550,8 +2560,8 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} - lru-cache@11.3.5: - resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==} + lru-cache@11.5.1: + resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -2560,8 +2570,8 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - make-fetch-happen@15.0.5: - resolution: {integrity: sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg==} + make-fetch-happen@15.0.6: + resolution: {integrity: sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw==} engines: {node: ^20.17.0 || >=22.9.0} math-intrinsics@1.1.0: @@ -2657,19 +2667,19 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - msgpackr-extract@3.0.3: - resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + msgpackr-extract@3.0.4: + resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==} hasBin: true - msgpackr@1.11.10: - resolution: {integrity: sha512-iCZNq+HszvF+fC3anCm4nBmWEnbeIAfpDs6IStAEKhQ2YSgkjzVG2FF9XJqwwQh5bH3N9OUTUt4QwVN6MLMLtA==} + msgpackr@1.11.12: + resolution: {integrity: sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg==} mute-stream@2.0.0: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -2712,8 +2722,9 @@ packages: engines: {node: ^20.17.0 || >=22.9.0} hasBin: true - node-releases@2.0.37: - resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==} + node-releases@2.0.46: + resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==} + engines: {node: '>=18'} nopt@9.0.0: resolution: {integrity: sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==} @@ -2864,8 +2875,8 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss@8.5.10: - resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==} + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -2888,8 +2899,8 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qs@6.15.1: - resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} + qs@6.15.2: + resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} engines: {node: '>=0.6'} queue-microtask@1.2.3: @@ -2942,8 +2953,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.60.2: - resolution: {integrity: sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==} + rollup@4.60.4: + resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2983,6 +2994,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.1: + resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} + engines: {node: '>=10'} + hasBin: true + send@1.2.1: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} @@ -3025,8 +3041,8 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sigstore@4.1.0: - resolution: {integrity: sha512-/fUgUhYghuLzVT/gaJoeVehLCgZiUxPCPMcyVNY0lIf/cTCz58K/WTI7PefDarXxp9nUKpEwg1yyz3eSBMTtgA==} + sigstore@4.1.1: + resolution: {integrity: sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w==} engines: {node: ^20.17.0 || >=22.9.0} slice-ansi@7.1.2: @@ -3041,8 +3057,8 @@ packages: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - socket.io-adapter@2.5.6: - resolution: {integrity: sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==} + socket.io-adapter@2.5.7: + resolution: {integrity: sha512-e0LyK91f3cUxTmv95/KzoLg47+zF+s/sbxRGDNsyG4dmIP8ZSX8ax6byOxfJXeNNtS/8AZlfD+uP7gBeR7DLlg==} socket.io-client@4.8.3: resolution: {integrity: sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==} @@ -3060,8 +3076,8 @@ packages: resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} - socks@2.8.7: - resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} + socks@2.8.9: + resolution: {integrity: sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} source-map-js@1.2.1: @@ -3114,8 +3130,8 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} - string-width@8.2.0: - resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} + string-width@8.2.1: + resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} engines: {node: '>=20'} strip-ansi@6.0.1: @@ -3137,15 +3153,15 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tar@7.5.13: - resolution: {integrity: sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==} + tar@7.5.15: + resolution: {integrity: sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==} engines: {node: '>=18'} tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.1.1: - resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==} + tinyexec@1.2.3: + resolution: {integrity: sha512-g62dB+w1/OEFnPvmX0yd/HnetYITOL+1nJW7kitOycOeAvmbWC/nu0fwmmQ/kupNojqExzyC/T++pST/jRJ2mQ==} engines: {node: '>=18'} tinyglobby@0.2.15: @@ -3160,11 +3176,11 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tldts-core@7.0.28: - resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==} + tldts-core@7.4.1: + resolution: {integrity: sha512-sc2nGvGbixlJRHwTh/qQdPXTxJU1UDJboGPQm4d/01YUJ9r/u6aeIulQvEaxUlvKDN7hb1qCLjax+jhVAPLa/g==} - tldts@7.0.28: - resolution: {integrity: sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==} + tldts@7.4.0: + resolution: {integrity: sha512-yHBe+zVfzNZ3QfTPW/Z6KK1G2t340gFjMHqI/4KKSt/abzYydzuCnpqdaF5gCCABby+9Yfbj59oR5F2Fd5CBzg==} hasBin: true to-regex-range@5.0.1: @@ -3200,9 +3216,9 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} - engines: {node: '>= 0.6'} + type-is@2.1.0: + resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} + engines: {node: '>= 18'} typescript-eslint@8.47.0: resolution: {integrity: sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q==} @@ -3216,11 +3232,11 @@ packages: engines: {node: '>=14.17'} hasBin: true - undici-types@7.19.2: - resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - undici@6.25.0: - resolution: {integrity: sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==} + undici@6.26.0: + resolution: {integrity: sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==} engines: {node: '>=18.17'} undici@7.24.4: @@ -3288,20 +3304,20 @@ packages: yaml: optional: true - vitest@4.1.5: - resolution: {integrity: sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==} + vitest@4.1.7: + resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.5 - '@vitest/browser-preview': 4.1.5 - '@vitest/browser-webdriverio': 4.1.5 - '@vitest/coverage-istanbul': 4.1.5 - '@vitest/coverage-v8': 4.1.5 - '@vitest/ui': 4.1.5 + '@vitest/browser-playwright': 4.1.7 + '@vitest/browser-preview': 4.1.7 + '@vitest/browser-webdriverio': 4.1.7 + '@vitest/coverage-istanbul': 4.1.7 + '@vitest/coverage-v8': 4.1.7 + '@vitest/ui': 4.1.7 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3386,8 +3402,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.3: - resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + ws@8.20.1: + resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -3398,8 +3414,8 @@ packages: utf-8-validate: optional: true - ws@8.20.0: - resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -3559,14 +3575,14 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.2102.7(chokidar@5.0.0)': + '@angular-devkit/architect@0.2102.13(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.7(chokidar@5.0.0) + '@angular-devkit/core': 21.2.13(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/core@21.2.7(chokidar@5.0.0)': + '@angular-devkit/core@21.2.13(chokidar@5.0.0)': dependencies: ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) @@ -3577,9 +3593,9 @@ snapshots: optionalDependencies: chokidar: 5.0.0 - '@angular-devkit/schematics@21.2.7(chokidar@5.0.0)': + '@angular-devkit/schematics@21.2.13(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.7(chokidar@5.0.0) + '@angular-devkit/core': 21.2.13(chokidar@5.0.0) jsonc-parser: 3.3.1 magic-string: 0.30.21 ora: 9.3.0 @@ -3587,11 +3603,11 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-eslint/builder@21.1.0(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/builder@21.1.0(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@angular-devkit/architect': 0.2102.7(chokidar@5.0.0) - '@angular-devkit/core': 21.2.7(chokidar@5.0.0) - '@angular/cli': 21.2.7(@types/node@25.6.0)(chokidar@5.0.0) + '@angular-devkit/architect': 0.2102.13(chokidar@5.0.0) + '@angular-devkit/core': 21.2.13(chokidar@5.0.0) + '@angular/cli': 21.2.13(@types/node@25.9.1)(chokidar@5.0.0) eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: @@ -3599,34 +3615,34 @@ snapshots: '@angular-eslint/bundled-angular-compiler@21.1.0': {} - '@angular-eslint/eslint-plugin-template@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.59.0)(@typescript-eslint/utils@8.59.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin-template@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.59.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/utils': 8.59.0(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/utils': 8.60.0(eslint@9.39.4)(typescript@5.9.3) aria-query: 5.3.2 axobject-query: 4.1.0 eslint: 9.39.4 typescript: 5.9.3 - '@angular-eslint/eslint-plugin@21.1.0(@typescript-eslint/utils@8.59.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin@21.1.0(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.59.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/utils': 8.59.0(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.60.0(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 - '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(@typescript-eslint/types@8.59.0)(@typescript-eslint/utils@8.59.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@angular-devkit/core': 21.2.7(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.7(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.59.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.59.0)(@typescript-eslint/utils@8.59.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.7(@types/node@25.6.0)(chokidar@5.0.0) + '@angular-devkit/core': 21.2.13(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.13(chokidar@5.0.0) + '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular/cli': 21.2.13(@types/node@25.9.1)(chokidar@5.0.0) ignore: 7.0.5 semver: 7.7.3 strip-json-comments: 3.1.1 @@ -3645,29 +3661,29 @@ snapshots: eslint-scope: 9.1.2 typescript: 5.9.3 - '@angular-eslint/utils@21.1.0(@typescript-eslint/utils@8.59.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/utils@21.1.0(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@typescript-eslint/utils': 8.59.0(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.60.0(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 - '@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - '@angular/build@21.2.7(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9)(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.6.0)(chokidar@5.0.0)(postcss@8.5.10)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.5(@types/node@25.6.0)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3)))': + '@angular/build@21.2.13(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15)(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.9.1)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)))': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2102.7(chokidar@5.0.0) - '@angular/compiler': 21.2.9 - '@angular/compiler-cli': 21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3) + '@angular-devkit/architect': 0.2102.13(chokidar@5.0.0) + '@angular/compiler': 21.2.15 + '@angular/compiler-cli': 21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3) '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@25.6.0) - '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3)) + '@inquirer/confirm': 5.1.21(@types/node@25.9.1) + '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)) beasties: 0.4.1 browserslist: 4.28.2 esbuild: 0.27.3 @@ -3688,16 +3704,16 @@ snapshots: tslib: 2.8.1 typescript: 5.9.3 undici: 7.24.4 - vite: 7.3.2(@types/node@25.6.0)(sass@1.97.3) + vite: 7.3.2(@types/node@25.9.1)(sass@1.97.3) watchpack: 2.5.1 optionalDependencies: - '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/localize': 21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9) - '@angular/platform-browser': 21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/localize': 21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15) + '@angular/platform-browser': 21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/service-worker': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) lmdb: 3.5.1 - postcss: 8.5.10 - vitest: 4.1.5(@types/node@25.6.0)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3)) + postcss: 8.5.15 + vitest: 4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -3713,15 +3729,15 @@ snapshots: - tsx - yaml - '@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0)': + '@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2102.7(chokidar@5.0.0) - '@angular-devkit/core': 21.2.7(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.7(chokidar@5.0.0) - '@inquirer/prompts': 7.10.1(@types/node@25.6.0) - '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.6.0))(@types/node@25.6.0)(listr2@9.0.5) + '@angular-devkit/architect': 0.2102.13(chokidar@5.0.0) + '@angular-devkit/core': 21.2.13(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.13(chokidar@5.0.0) + '@inquirer/prompts': 7.10.1(@types/node@25.9.1) + '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.1))(@types/node@25.9.1)(listr2@9.0.5) '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) - '@schematics/angular': 21.2.7(chokidar@5.0.0) + '@schematics/angular': 21.2.13(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 algoliasearch: 5.48.1 ini: 6.0.0 @@ -3739,21 +3755,21 @@ snapshots: - chokidar - supports-color - '@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3)': + '@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3)': dependencies: - '@angular/compiler': 21.2.9 + '@angular/compiler': 21.2.15 '@babel/core': 7.29.0 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 5.0.0 convert-source-map: 1.9.0 reflect-metadata: 0.2.2 - semver: 7.7.4 + semver: 7.8.1 tslib: 2.8.1 yargs: 18.0.0 optionalDependencies: @@ -3761,31 +3777,31 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/compiler@21.2.9': + '@angular/compiler@21.2.15': dependencies: tslib: 2.8.1 - '@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)': + '@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 21.2.9 + '@angular/compiler': 21.2.15 zone.js: 0.15.0 - '@angular/forms@21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': + '@angular/forms@21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) '@standard-schema/spec': 1.1.0 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/localize@21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9)': + '@angular/localize@21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15)': dependencies: - '@angular/compiler': 21.2.9 - '@angular/compiler-cli': 21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3) + '@angular/compiler': 21.2.15 + '@angular/compiler-cli': 21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3) '@babel/core': 7.29.0 '@types/babel__core': 7.20.5 tinyglobby: 0.2.16 @@ -3793,35 +3809,35 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/platform-browser-dynamic@21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.9)(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))': + '@angular/platform-browser-dynamic@21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.15)(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))': dependencies: - '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/compiler': 21.2.9 - '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/compiler': 21.2.15 + '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) tslib: 2.8.1 - '@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 optionalDependencies: - '@angular/animations': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/animations': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/service-worker@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 '@asamuzakjp/css-color@4.1.2': dependencies: - '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - lru-cache: 11.3.5 + lru-cache: 11.5.1 '@asamuzakjp/dom-selector@6.8.1': dependencies: @@ -3829,29 +3845,29 @@ snapshots: bidi-js: 1.0.3 css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 - lru-cache: 11.3.5 + lru-cache: 11.5.1 '@asamuzakjp/nwsapi@2.3.9': {} - '@babel/code-frame@7.29.0': + '@babel/code-frame@7.29.7': dependencies: - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.29.0': {} + '@babel/compat-data@7.29.7': {} '@babel/core@7.29.0': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.29.2 - '@babel/parser': 7.29.2 - '@babel/template': 7.28.6 - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.0) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -3861,97 +3877,97 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.29.1': + '@babel/generator@7.29.7': dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 - '@babel/helper-compilation-targets@7.28.6': + '@babel/helper-compilation-targets@7.29.7': dependencies: - '@babel/compat-data': 7.29.0 - '@babel/helper-validator-option': 7.27.1 + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 browserslist: 4.28.2 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-globals@7.28.0': {} + '@babel/helper-globals@7.29.7': {} - '@babel/helper-module-imports@7.28.6': + '@babel/helper-module-imports@7.29.7': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color '@babel/helper-split-export-declaration@7.24.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 - '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@7.29.7': {} - '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@7.29.7': {} - '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-validator-option@7.29.7': {} - '@babel/helpers@7.29.2': + '@babel/helpers@7.29.7': dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 - '@babel/parser@7.29.2': + '@babel/parser@7.29.7': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 - '@babel/template@7.28.6': + '@babel/template@7.29.7': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 - '@babel/traverse@7.29.0': + '@babel/traverse@7.29.7': dependencies: - '@babel/code-frame': 7.29.0 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.2 - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.29.0': + '@babel/types@7.29.7': dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 '@csstools/color-helpers@6.0.2': {} - '@csstools/css-calc@3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-color-parser@4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/color-helpers': 6.0.2 - '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -3959,7 +3975,7 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.3(css-tree@3.2.1)': + '@csstools/css-syntax-patches-for-csstree@1.1.4(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 @@ -4084,7 +4100,7 @@ snapshots: '@eslint/eslintrc@3.3.5': dependencies: - ajv: 6.14.0 + ajv: 6.15.0 debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 @@ -4105,11 +4121,11 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 - '@exodus/bytes@1.15.0': {} + '@exodus/bytes@1.15.1': {} - '@fortawesome/angular-fontawesome@4.0.0(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))': + '@fortawesome/angular-fontawesome@4.0.0(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) '@fortawesome/fontawesome-svg-core': 7.2.0 tslib: 2.8.1 @@ -4136,9 +4152,9 @@ snapshots: '@harperfast/extended-iterable@1.0.3': optional: true - '@hono/node-server@1.19.14(hono@4.12.14)': + '@hono/node-server@1.19.14(hono@4.12.23)': dependencies: - hono: 4.12.14 + hono: 4.12.23 '@humanfs/core@0.19.2': dependencies: @@ -4158,128 +4174,128 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.2(@types/node@25.6.0)': + '@inquirer/checkbox@4.3.2(@types/node@25.9.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.6.0) + '@inquirer/core': 10.3.2(@types/node@25.9.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.6.0) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 - '@inquirer/confirm@5.1.21(@types/node@25.6.0)': + '@inquirer/confirm@5.1.21(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.6.0) - '@inquirer/type': 3.0.10(@types/node@25.6.0) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 - '@inquirer/core@10.3.2(@types/node@25.6.0)': + '@inquirer/core@10.3.2(@types/node@25.9.1)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.6.0) + '@inquirer/type': 3.0.10(@types/node@25.9.1) 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 optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 - '@inquirer/editor@4.2.23(@types/node@25.6.0)': + '@inquirer/editor@4.2.23(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.6.0) - '@inquirer/external-editor': 1.0.3(@types/node@25.6.0) - '@inquirer/type': 3.0.10(@types/node@25.6.0) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/external-editor': 1.0.3(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 - '@inquirer/expand@4.0.23(@types/node@25.6.0)': + '@inquirer/expand@4.0.23(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.6.0) - '@inquirer/type': 3.0.10(@types/node@25.6.0) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 - '@inquirer/external-editor@1.0.3(@types/node@25.6.0)': + '@inquirer/external-editor@1.0.3(@types/node@25.9.1)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.3.1(@types/node@25.6.0)': + '@inquirer/input@4.3.1(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.6.0) - '@inquirer/type': 3.0.10(@types/node@25.6.0) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 - '@inquirer/number@3.0.23(@types/node@25.6.0)': + '@inquirer/number@3.0.23(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.6.0) - '@inquirer/type': 3.0.10(@types/node@25.6.0) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 - '@inquirer/password@4.0.23(@types/node@25.6.0)': + '@inquirer/password@4.0.23(@types/node@25.9.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.6.0) - '@inquirer/type': 3.0.10(@types/node@25.6.0) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 - '@inquirer/prompts@7.10.1(@types/node@25.6.0)': + '@inquirer/prompts@7.10.1(@types/node@25.9.1)': dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@25.6.0) - '@inquirer/confirm': 5.1.21(@types/node@25.6.0) - '@inquirer/editor': 4.2.23(@types/node@25.6.0) - '@inquirer/expand': 4.0.23(@types/node@25.6.0) - '@inquirer/input': 4.3.1(@types/node@25.6.0) - '@inquirer/number': 3.0.23(@types/node@25.6.0) - '@inquirer/password': 4.0.23(@types/node@25.6.0) - '@inquirer/rawlist': 4.1.11(@types/node@25.6.0) - '@inquirer/search': 3.2.2(@types/node@25.6.0) - '@inquirer/select': 4.4.2(@types/node@25.6.0) + '@inquirer/checkbox': 4.3.2(@types/node@25.9.1) + '@inquirer/confirm': 5.1.21(@types/node@25.9.1) + '@inquirer/editor': 4.2.23(@types/node@25.9.1) + '@inquirer/expand': 4.0.23(@types/node@25.9.1) + '@inquirer/input': 4.3.1(@types/node@25.9.1) + '@inquirer/number': 3.0.23(@types/node@25.9.1) + '@inquirer/password': 4.0.23(@types/node@25.9.1) + '@inquirer/rawlist': 4.1.11(@types/node@25.9.1) + '@inquirer/search': 3.2.2(@types/node@25.9.1) + '@inquirer/select': 4.4.2(@types/node@25.9.1) optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 - '@inquirer/rawlist@4.1.11(@types/node@25.6.0)': + '@inquirer/rawlist@4.1.11(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.6.0) - '@inquirer/type': 3.0.10(@types/node@25.6.0) + '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 - '@inquirer/search@3.2.2(@types/node@25.6.0)': + '@inquirer/search@3.2.2(@types/node@25.9.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.6.0) + '@inquirer/core': 10.3.2(@types/node@25.9.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.6.0) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 - '@inquirer/select@4.4.2(@types/node@25.6.0)': + '@inquirer/select@4.4.2(@types/node@25.9.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.6.0) + '@inquirer/core': 10.3.2(@types/node@25.9.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.6.0) + '@inquirer/type': 3.0.10(@types/node@25.9.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 - '@inquirer/type@3.0.10(@types/node@25.6.0)': + '@inquirer/type@3.0.10(@types/node@25.9.1)': optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 '@isaacs/fs-minipass@4.0.1': dependencies: @@ -4306,10 +4322,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@25.6.0))(@types/node@25.6.0)(listr2@9.0.5)': + '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.1))(@types/node@25.9.1)(listr2@9.0.5)': dependencies: - '@inquirer/prompts': 7.10.1(@types/node@25.6.0) - '@inquirer/type': 3.0.10(@types/node@25.6.0) + '@inquirer/prompts': 7.10.1(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.1) listr2: 9.0.5 transitivePeerDependencies: - '@types/node' @@ -4337,18 +4353,18 @@ snapshots: '@modelcontextprotocol/sdk@1.26.0(zod@4.3.6)': dependencies: - '@hono/node-server': 1.19.14(hono@4.12.14) - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) + '@hono/node-server': 1.19.14(hono@4.12.23) + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) content-type: 1.0.5 cors: 2.8.6 cross-spawn: 7.0.6 eventsource: 3.0.7 - eventsource-parser: 3.0.8 + eventsource-parser: 3.1.0 express: 5.2.1 - express-rate-limit: 8.3.2(express@5.2.1) - hono: 4.12.14 - jose: 6.2.2 + express-rate-limit: 8.5.2(express@5.2.1) + hono: 4.12.23 + jose: 6.2.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 @@ -4357,22 +4373,22 @@ snapshots: transitivePeerDependencies: - supports-color - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': optional: true - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4': optional: true - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4': optional: true - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4': optional: true - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4': optional: true - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': optional: true '@napi-rs/nice-android-arm-eabi@1.1.1': @@ -4451,24 +4467,24 @@ snapshots: dependencies: '@emnapi/core': 1.9.1 '@emnapi/runtime': 1.9.1 - '@tybys/wasm-util': 0.10.1 + '@tybys/wasm-util': 0.10.2 optional: true - '@ng-bootstrap/ng-bootstrap@20.0.0(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9))(@popperjs/core@2.11.8)(rxjs@7.8.2)': + '@ng-bootstrap/ng-bootstrap@20.0.0(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15))(@popperjs/core@2.11.8)(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) - '@angular/localize': 21.2.9(@angular/compiler-cli@21.2.9(@angular/compiler@21.2.9)(typescript@5.9.3))(@angular/compiler@21.2.9) + '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/localize': 21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15) '@popperjs/core': 2.11.8 rxjs: 7.8.2 tslib: 2.8.1 - '@ng-select/ng-select@21.8.0(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': + '@ng-select/ng-select@21.8.2(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': dependencies: - '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 21.2.9(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.9(@angular/animations@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) tslib: 2.8.1 '@nodelib/fs.scandir@2.1.5': @@ -4483,12 +4499,12 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@npmcli/agent@4.0.0': + '@npmcli/agent@4.0.2': dependencies: agent-base: 7.1.4 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 - lru-cache: 11.3.5 + lru-cache: 11.5.1 socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color @@ -4502,7 +4518,7 @@ snapshots: '@gar/promise-retry': 1.0.3 '@npmcli/promise-spawn': 9.0.1 ini: 6.0.0 - lru-cache: 11.3.5 + lru-cache: 11.5.1 npm-pick-manifest: 11.0.3 proc-log: 6.1.0 semver: 7.7.4 @@ -4519,7 +4535,7 @@ snapshots: dependencies: '@npmcli/git': 7.0.2 glob: 13.0.6 - hosted-git-info: 9.0.2 + hosted-git-info: 9.0.3 json-parse-even-better-errors: 5.0.0 proc-log: 6.1.0 semver: 7.7.4 @@ -4650,85 +4666,85 @@ snapshots: '@rolldown/pluginutils@1.0.0-rc.4': {} - '@rollup/rollup-android-arm-eabi@4.60.2': + '@rollup/rollup-android-arm-eabi@4.60.4': optional: true - '@rollup/rollup-android-arm64@4.60.2': + '@rollup/rollup-android-arm64@4.60.4': optional: true - '@rollup/rollup-darwin-arm64@4.60.2': + '@rollup/rollup-darwin-arm64@4.60.4': optional: true - '@rollup/rollup-darwin-x64@4.60.2': + '@rollup/rollup-darwin-x64@4.60.4': optional: true - '@rollup/rollup-freebsd-arm64@4.60.2': + '@rollup/rollup-freebsd-arm64@4.60.4': optional: true - '@rollup/rollup-freebsd-x64@4.60.2': + '@rollup/rollup-freebsd-x64@4.60.4': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.2': + '@rollup/rollup-linux-arm-gnueabihf@4.60.4': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.2': + '@rollup/rollup-linux-arm-musleabihf@4.60.4': optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.2': + '@rollup/rollup-linux-arm64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-arm64-musl@4.60.2': + '@rollup/rollup-linux-arm64-musl@4.60.4': optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.2': + '@rollup/rollup-linux-loong64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-loong64-musl@4.60.2': + '@rollup/rollup-linux-loong64-musl@4.60.4': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.2': + '@rollup/rollup-linux-ppc64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.2': + '@rollup/rollup-linux-ppc64-musl@4.60.4': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.2': + '@rollup/rollup-linux-riscv64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.2': + '@rollup/rollup-linux-riscv64-musl@4.60.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.2': + '@rollup/rollup-linux-s390x-gnu@4.60.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.60.2': + '@rollup/rollup-linux-x64-gnu@4.60.4': optional: true - '@rollup/rollup-linux-x64-musl@4.60.2': + '@rollup/rollup-linux-x64-musl@4.60.4': optional: true - '@rollup/rollup-openbsd-x64@4.60.2': + '@rollup/rollup-openbsd-x64@4.60.4': optional: true - '@rollup/rollup-openharmony-arm64@4.60.2': + '@rollup/rollup-openharmony-arm64@4.60.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.2': + '@rollup/rollup-win32-arm64-msvc@4.60.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.2': + '@rollup/rollup-win32-ia32-msvc@4.60.4': optional: true - '@rollup/rollup-win32-x64-gnu@4.60.2': + '@rollup/rollup-win32-x64-gnu@4.60.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.60.2': + '@rollup/rollup-win32-x64-msvc@4.60.4': optional: true - '@schematics/angular@21.2.7(chokidar@5.0.0)': + '@schematics/angular@21.2.13(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.7(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.7(chokidar@5.0.0) + '@angular-devkit/core': 21.2.13(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.13(chokidar@5.0.0) jsonc-parser: 3.3.1 transitivePeerDependencies: - chokidar @@ -4737,7 +4753,7 @@ snapshots: dependencies: '@sigstore/protobuf-specs': 0.5.1 - '@sigstore/core@3.2.0': {} + '@sigstore/core@3.2.1': {} '@sigstore/protobuf-specs@0.5.1': {} @@ -4745,9 +4761,9 @@ snapshots: dependencies: '@gar/promise-retry': 1.0.3 '@sigstore/bundle': 4.0.0 - '@sigstore/core': 3.2.0 + '@sigstore/core': 3.2.1 '@sigstore/protobuf-specs': 0.5.1 - make-fetch-happen: 15.0.5 + make-fetch-happen: 15.0.6 proc-log: 6.1.0 transitivePeerDependencies: - supports-color @@ -4759,10 +4775,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@sigstore/verify@3.1.0': + '@sigstore/verify@3.1.1': dependencies: '@sigstore/bundle': 4.0.0 - '@sigstore/core': 3.2.0 + '@sigstore/core': 3.2.1 '@sigstore/protobuf-specs': 0.5.1 '@socket.io/component-emitter@3.1.2': {} @@ -4776,31 +4792,31 @@ snapshots: '@tufjs/canonical-json': 2.0.0 minimatch: 10.2.5 - '@tybys/wasm-util@0.10.1': + '@tybys/wasm-util@0.10.2': dependencies: tslib: 2.8.1 optional: true '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.29.7 '@types/chai@5.2.3': dependencies: @@ -4809,7 +4825,7 @@ snapshots: '@types/cors@2.8.19': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 '@types/deep-eql@4.0.2': {} @@ -4817,15 +4833,17 @@ snapshots: '@types/estree@1.0.8': {} + '@types/estree@1.0.9': {} + '@types/json-schema@7.0.15': {} - '@types/node@25.6.0': + '@types/node@25.9.1': dependencies: - undici-types: 7.19.2 + undici-types: 7.24.6 '@types/ws@8.18.1': dependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 '@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: @@ -4865,10 +4883,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.59.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.60.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.59.0(typescript@5.9.3) - '@typescript-eslint/types': 8.59.0 + '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.9.3) + '@typescript-eslint/types': 8.60.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -4879,16 +4897,16 @@ snapshots: '@typescript-eslint/types': 8.47.0 '@typescript-eslint/visitor-keys': 8.47.0 - '@typescript-eslint/scope-manager@8.59.0': + '@typescript-eslint/scope-manager@8.60.0': dependencies: - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/visitor-keys': 8.59.0 + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/visitor-keys': 8.60.0 '@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.59.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.60.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 @@ -4906,7 +4924,7 @@ snapshots: '@typescript-eslint/types@8.47.0': {} - '@typescript-eslint/types@8.59.0': {} + '@typescript-eslint/types@8.60.0': {} '@typescript-eslint/typescript-estree@8.47.0(typescript@5.9.3)': dependencies: @@ -4918,21 +4936,21 @@ snapshots: fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.9 - semver: 7.7.4 + semver: 7.8.1 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.59.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.60.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.59.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.59.0(typescript@5.9.3) - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/visitor-keys': 8.59.0 + '@typescript-eslint/project-service': 8.60.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.9.3) + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/visitor-keys': 8.60.0 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.7.4 + semver: 7.8.1 tinyglobby: 0.2.16 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 @@ -4950,12 +4968,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.59.0(eslint@9.39.4)(typescript@5.9.3)': + '@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) - '@typescript-eslint/scope-manager': 8.59.0 - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/typescript-estree': 8.59.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.60.0 + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: @@ -4966,53 +4984,53 @@ snapshots: '@typescript-eslint/types': 8.47.0 eslint-visitor-keys: 4.2.1 - '@typescript-eslint/visitor-keys@8.59.0': + '@typescript-eslint/visitor-keys@8.60.0': dependencies: - '@typescript-eslint/types': 8.59.0 + '@typescript-eslint/types': 8.60.0 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3))': + '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3))': dependencies: - vite: 7.3.2(@types/node@25.6.0)(sass@1.97.3) + vite: 7.3.2(@types/node@25.9.1)(sass@1.97.3) - '@vitest/expect@4.1.5': + '@vitest/expect@4.1.7': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.5 - '@vitest/utils': 4.1.5 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.5(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3))': + '@vitest/mocker@4.1.7(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3))': dependencies: - '@vitest/spy': 4.1.5 + '@vitest/spy': 4.1.7 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.2(@types/node@25.6.0)(sass@1.97.3) + vite: 7.3.2(@types/node@25.9.1)(sass@1.97.3) - '@vitest/pretty-format@4.1.5': + '@vitest/pretty-format@4.1.7': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.5': + '@vitest/runner@4.1.7': dependencies: - '@vitest/utils': 4.1.5 + '@vitest/utils': 4.1.7 pathe: 2.0.3 - '@vitest/snapshot@4.1.5': + '@vitest/snapshot@4.1.7': dependencies: - '@vitest/pretty-format': 4.1.5 - '@vitest/utils': 4.1.5 + '@vitest/pretty-format': 4.1.7 + '@vitest/utils': 4.1.7 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.5': {} + '@vitest/spy@4.1.7': {} - '@vitest/utils@4.1.5': + '@vitest/utils@4.1.7': dependencies: - '@vitest/pretty-format': 4.1.5 + '@vitest/pretty-format': 4.1.7 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -5042,7 +5060,11 @@ snapshots: optionalDependencies: ajv: 8.18.0 - ajv@6.14.0: + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + + ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 @@ -5052,7 +5074,14 @@ snapshots: ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 + fast-uri: 3.1.2 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.2 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -5073,18 +5102,18 @@ snapshots: '@algolia/requester-fetch': 5.48.1 '@algolia/requester-node-http': 5.48.1 - angular-eslint@21.1.0(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): + angular-eslint@21.1.0(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): dependencies: - '@angular-devkit/core': 21.2.7(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.7(chokidar@5.0.0) - '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.59.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.59.0)(@typescript-eslint/utils@8.59.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.7(@types/node@25.6.0)(chokidar@5.0.0))(@typescript-eslint/types@8.59.0)(@typescript-eslint/utils@8.59.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-devkit/core': 21.2.13(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.13(chokidar@5.0.0) + '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.7(@types/node@25.6.0)(chokidar@5.0.0) - '@typescript-eslint/types': 8.59.0 - '@typescript-eslint/utils': 8.59.0(eslint@9.39.4)(typescript@5.9.3) + '@angular/cli': 21.2.13(@types/node@25.9.1)(chokidar@5.0.0) + '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/utils': 8.60.0(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 typescript-eslint: 8.47.0(eslint@9.39.4)(typescript@5.9.3) @@ -5120,7 +5149,7 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.10.20: {} + baseline-browser-mapping@2.10.32: {} beasties@0.4.1: dependencies: @@ -5130,9 +5159,9 @@ snapshots: domhandler: 5.0.3 htmlparser2: 10.1.0 picocolors: 1.1.1 - postcss: 8.5.10 + postcss: 8.5.15 postcss-media-query-parser: 0.2.3 - postcss-safe-parser: 7.0.1(postcss@8.5.10) + postcss-safe-parser: 7.0.1(postcss@8.5.15) bidi-js@1.0.3: dependencies: @@ -5146,9 +5175,9 @@ snapshots: http-errors: 2.0.1 iconv-lite: 0.7.2 on-finished: 2.4.1 - qs: 6.15.1 + qs: 6.15.2 raw-body: 3.0.2 - type-is: 2.0.1 + type-is: 2.1.0 transitivePeerDependencies: - supports-color @@ -5158,16 +5187,16 @@ snapshots: dependencies: '@popperjs/core': 2.11.8 - brace-expansion@1.1.14: + brace-expansion@1.1.15: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.1.0: + brace-expansion@2.1.1: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.5: + brace-expansion@5.0.6: dependencies: balanced-match: 4.0.4 @@ -5177,10 +5206,10 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.20 - caniuse-lite: 1.0.30001788 - electron-to-chromium: 1.5.340 - node-releases: 2.0.37 + baseline-browser-mapping: 2.10.32 + caniuse-lite: 1.0.30001793 + electron-to-chromium: 1.5.364 + node-releases: 2.0.46 update-browserslist-db: 1.2.3(browserslist@4.28.2) buffer-from@1.1.2: {} @@ -5192,7 +5221,7 @@ snapshots: '@npmcli/fs': 5.0.0 fs-minipass: 3.0.3 glob: 13.0.6 - lru-cache: 11.3.5 + lru-cache: 11.5.1 minipass: 7.1.3 minipass-collect: 2.0.1 minipass-flush: 1.0.7 @@ -5212,7 +5241,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001788: {} + caniuse-lite@1.0.30001793: {} chai@6.2.2: {} @@ -5244,7 +5273,7 @@ snapshots: cli-truncate@5.2.0: dependencies: slice-ansi: 8.0.0 - string-width: 8.2.0 + string-width: 8.2.1 cli-width@4.1.0: {} @@ -5268,6 +5297,8 @@ snapshots: content-type@1.0.5: {} + content-type@2.0.0: {} + convert-source-map@1.9.0: {} convert-source-map@2.0.0: {} @@ -5307,9 +5338,9 @@ snapshots: cssstyle@5.3.7: dependencies: '@asamuzakjp/css-color': 4.1.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.3(css-tree@3.2.1) + '@csstools/css-syntax-patches-for-csstree': 1.1.4(css-tree@3.2.1) css-tree: 3.2.1 - lru-cache: 11.3.5 + lru-cache: 11.5.1 data-urls@6.0.1: dependencies: @@ -5355,7 +5386,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.340: {} + electron-to-chromium@1.5.364: {} emoji-regex@10.6.0: {} @@ -5363,12 +5394,12 @@ snapshots: encodeurl@2.0.0: {} - engine.io-client@6.6.4: + engine.io-client@6.6.5: dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.4.3 engine.io-parser: 5.2.3 - ws: 8.18.3 + ws: 8.20.1 xmlhttprequest-ssl: 2.1.2 transitivePeerDependencies: - bufferutil @@ -5377,10 +5408,10 @@ snapshots: engine.io-parser@5.2.3: {} - engine.io@6.6.6: + engine.io@6.6.8: dependencies: '@types/cors': 2.8.19 - '@types/node': 25.6.0 + '@types/node': 25.9.1 '@types/ws': 8.18.1 accepts: 1.3.8 base64id: 2.0.0 @@ -5388,7 +5419,7 @@ snapshots: cors: 2.8.6 debug: 4.4.3 engine.io-parser: 5.2.3 - ws: 8.18.3 + ws: 8.20.1 transitivePeerDependencies: - bufferutil - supports-color @@ -5412,9 +5443,9 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@2.0.0: {} + es-module-lexer@2.1.0: {} - es-object-atoms@1.1.1: + es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 @@ -5461,7 +5492,7 @@ snapshots: eslint-scope@9.1.2: dependencies: '@types/esrecurse': 4.3.1 - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 esrecurse: 4.3.0 estraverse: 5.3.0 @@ -5484,8 +5515,8 @@ snapshots: '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 - '@types/estree': 1.0.8 - ajv: 6.14.0 + '@types/estree': 1.0.9 + ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3 @@ -5528,7 +5559,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 esutils@2.0.3: {} @@ -5536,20 +5567,20 @@ snapshots: eventemitter3@5.0.4: {} - eventsource-parser@3.0.8: {} + eventsource-parser@3.1.0: {} eventsource@3.0.7: dependencies: - eventsource-parser: 3.0.8 + eventsource-parser: 3.1.0 expect-type@1.3.0: {} exponential-backoff@3.1.3: {} - express-rate-limit@8.3.2(express@5.2.1): + express-rate-limit@8.5.2(express@5.2.1): dependencies: express: 5.2.1 - ip-address: 10.1.0 + ip-address: 10.2.0 express@5.2.1: dependencies: @@ -5573,13 +5604,13 @@ snapshots: once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.15.1 + qs: 6.15.2 range-parser: 1.2.1 router: 2.2.0 send: 1.2.1 serve-static: 2.2.1 statuses: 2.0.2 - type-is: 2.0.1 + type-is: 2.1.0 vary: 1.1.2 transitivePeerDependencies: - supports-color @@ -5598,7 +5629,7 @@ snapshots: fast-levenshtein@2.0.6: {} - fast-uri@3.1.0: {} + fast-uri@3.1.2: {} fastq@1.20.1: dependencies: @@ -5656,25 +5687,25 @@ snapshots: get-caller-file@2.0.5: {} - get-east-asian-width@1.5.0: {} + get-east-asian-width@1.6.0: {} get-intrinsic@1.3.0: 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 + es-object-atoms: 1.1.2 function-bind: 1.1.2 get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.3 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 glob-parent@5.1.2: dependencies: @@ -5704,19 +5735,19 @@ snapshots: has-symbols@1.1.0: {} - hasown@2.0.3: + hasown@2.0.4: dependencies: function-bind: 1.1.2 - hono@4.12.14: {} + hono@4.12.23: {} - hosted-git-info@9.0.2: + hosted-git-info@9.0.3: dependencies: - lru-cache: 11.3.5 + lru-cache: 11.5.1 html-encoding-sniffer@6.0.0: dependencies: - '@exodus/bytes': 1.15.0 + '@exodus/bytes': 1.15.1 transitivePeerDependencies: - '@noble/hashes' @@ -5763,7 +5794,7 @@ snapshots: ignore@7.0.5: {} - immutable@5.1.5: {} + immutable@5.1.6: {} import-fresh@3.3.1: dependencies: @@ -5776,7 +5807,7 @@ snapshots: ini@6.0.0: {} - ip-address@10.1.0: {} + ip-address@10.2.0: {} ipaddr.js@1.9.1: {} @@ -5786,7 +5817,7 @@ snapshots: is-fullwidth-code-point@5.1.0: dependencies: - get-east-asian-width: 1.5.0 + get-east-asian-width: 1.6.0 is-glob@4.0.3: dependencies: @@ -5811,14 +5842,14 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.29.0 - '@babel/parser': 7.29.2 + '@babel/parser': 7.29.7 '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 semver: 7.7.4 transitivePeerDependencies: - supports-color - jose@6.2.2: {} + jose@6.2.3: {} js-tokens@4.0.0: {} @@ -5830,7 +5861,7 @@ snapshots: dependencies: '@acemir/cssom': 0.9.31 '@asamuzakjp/dom-selector': 6.8.1 - '@exodus/bytes': 1.15.0 + '@exodus/bytes': 1.15.1 cssstyle: 5.3.7 data-urls: 6.0.1 decimal.js: 10.6.0 @@ -5846,7 +5877,7 @@ snapshots: webidl-conversions: 8.0.1 whatwg-mimetype: 4.0.0 whatwg-url: 15.1.0 - ws: 8.20.0 + ws: 8.21.0 xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' @@ -5895,7 +5926,7 @@ snapshots: lmdb@3.5.1: dependencies: '@harperfast/extended-iterable': 1.0.3 - msgpackr: 1.11.10 + msgpackr: 1.11.12 node-addon-api: 6.1.0 node-gyp-build-optional-packages: 5.2.2 ordered-binary: 1.6.1 @@ -5929,7 +5960,7 @@ snapshots: strip-ansi: 7.2.0 wrap-ansi: 9.0.2 - lru-cache@11.3.5: {} + lru-cache@11.5.1: {} lru-cache@5.1.1: dependencies: @@ -5939,10 +5970,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - make-fetch-happen@15.0.5: + make-fetch-happen@15.0.6: dependencies: '@gar/promise-retry': 1.0.3 - '@npmcli/agent': 4.0.0 + '@npmcli/agent': 4.0.2 '@npmcli/redact': 4.0.0 cacache: 20.0.4 http-cache-semantics: 4.2.0 @@ -5987,15 +6018,15 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.5 + brace-expansion: 5.0.6 minimatch@3.1.5: dependencies: - brace-expansion: 1.1.14 + brace-expansion: 1.1.15 minimatch@9.0.9: dependencies: - brace-expansion: 2.1.0 + brace-expansion: 2.1.1 minipass-collect@2.0.1: dependencies: @@ -6035,26 +6066,26 @@ snapshots: ms@2.1.3: {} - msgpackr-extract@3.0.3: + msgpackr-extract@3.0.4: dependencies: node-gyp-build-optional-packages: 5.2.2 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 + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.4 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.4 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.4 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.4 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.4 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.4 optional: true - msgpackr@1.11.10: + msgpackr@1.11.12: optionalDependencies: - msgpackr-extract: 3.0.3 + msgpackr-extract: 3.0.4 optional: true mute-stream@2.0.0: {} - nanoid@3.3.11: {} + nanoid@3.3.12: {} natural-compare@1.4.0: {} @@ -6062,16 +6093,16 @@ snapshots: negotiator@1.0.0: {} - ngx-cookie-service@21.3.1(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0)): + ngx-cookie-service@21.3.1(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)): dependencies: - '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - ngx-socket-io@4.10.0(@angular/common@21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): + ngx-socket-io@4.10.0(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): dependencies: - '@angular/common': 21.2.9(@angular/core@21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.9(@angular/compiler@21.2.9)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) core-js: 3.49.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 @@ -6102,12 +6133,12 @@ snapshots: nopt: 9.0.0 proc-log: 6.1.0 semver: 7.7.4 - tar: 7.5.13 + tar: 7.5.15 tinyglobby: 0.2.16 - undici: 6.25.0 + undici: 6.26.0 which: 6.0.1 - node-releases@2.0.37: {} + node-releases@2.0.46: {} nopt@9.0.0: dependencies: @@ -6125,7 +6156,7 @@ snapshots: npm-package-arg@13.0.2: dependencies: - hosted-git-info: 9.0.2 + hosted-git-info: 9.0.3 proc-log: 6.1.0 semver: 7.7.4 validate-npm-package-name: 7.0.2 @@ -6146,7 +6177,7 @@ snapshots: dependencies: '@npmcli/redact': 4.0.0 jsonparse: 1.3.1 - make-fetch-happen: 15.0.5 + make-fetch-happen: 15.0.6 minipass: 7.1.3 minipass-fetch: 5.0.2 minizlib: 3.1.0 @@ -6195,7 +6226,7 @@ snapshots: is-unicode-supported: 2.1.0 log-symbols: 7.0.1 stdin-discarder: 0.3.2 - string-width: 8.2.0 + string-width: 8.2.1 ordered-binary@1.6.1: optional: true @@ -6226,9 +6257,9 @@ snapshots: npm-registry-fetch: 19.1.1 proc-log: 6.1.0 promise-retry: 2.0.1 - sigstore: 4.1.0 + sigstore: 4.1.1 ssri: 13.0.1 - tar: 7.5.13 + tar: 7.5.15 transitivePeerDependencies: - supports-color @@ -6258,7 +6289,7 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.3.5 + lru-cache: 11.5.1 minipass: 7.1.3 path-to-regexp@8.4.2: {} @@ -6279,13 +6310,13 @@ snapshots: postcss-media-query-parser@0.2.3: {} - postcss-safe-parser@7.0.1(postcss@8.5.10): + postcss-safe-parser@7.0.1(postcss@8.5.15): dependencies: - postcss: 8.5.10 + postcss: 8.5.15 - postcss@8.5.10: + postcss@8.5.15: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -6305,7 +6336,7 @@ snapshots: punycode@2.3.1: {} - qs@6.15.1: + qs@6.15.2: dependencies: side-channel: 1.1.0 @@ -6363,35 +6394,35 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - rollup@4.60.2: + rollup@4.60.4: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.2 - '@rollup/rollup-android-arm64': 4.60.2 - '@rollup/rollup-darwin-arm64': 4.60.2 - '@rollup/rollup-darwin-x64': 4.60.2 - '@rollup/rollup-freebsd-arm64': 4.60.2 - '@rollup/rollup-freebsd-x64': 4.60.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.2 - '@rollup/rollup-linux-arm-musleabihf': 4.60.2 - '@rollup/rollup-linux-arm64-gnu': 4.60.2 - '@rollup/rollup-linux-arm64-musl': 4.60.2 - '@rollup/rollup-linux-loong64-gnu': 4.60.2 - '@rollup/rollup-linux-loong64-musl': 4.60.2 - '@rollup/rollup-linux-ppc64-gnu': 4.60.2 - '@rollup/rollup-linux-ppc64-musl': 4.60.2 - '@rollup/rollup-linux-riscv64-gnu': 4.60.2 - '@rollup/rollup-linux-riscv64-musl': 4.60.2 - '@rollup/rollup-linux-s390x-gnu': 4.60.2 - '@rollup/rollup-linux-x64-gnu': 4.60.2 - '@rollup/rollup-linux-x64-musl': 4.60.2 - '@rollup/rollup-openbsd-x64': 4.60.2 - '@rollup/rollup-openharmony-arm64': 4.60.2 - '@rollup/rollup-win32-arm64-msvc': 4.60.2 - '@rollup/rollup-win32-ia32-msvc': 4.60.2 - '@rollup/rollup-win32-x64-gnu': 4.60.2 - '@rollup/rollup-win32-x64-msvc': 4.60.2 + '@rollup/rollup-android-arm-eabi': 4.60.4 + '@rollup/rollup-android-arm64': 4.60.4 + '@rollup/rollup-darwin-arm64': 4.60.4 + '@rollup/rollup-darwin-x64': 4.60.4 + '@rollup/rollup-freebsd-arm64': 4.60.4 + '@rollup/rollup-freebsd-x64': 4.60.4 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 + '@rollup/rollup-linux-arm-musleabihf': 4.60.4 + '@rollup/rollup-linux-arm64-gnu': 4.60.4 + '@rollup/rollup-linux-arm64-musl': 4.60.4 + '@rollup/rollup-linux-loong64-gnu': 4.60.4 + '@rollup/rollup-linux-loong64-musl': 4.60.4 + '@rollup/rollup-linux-ppc64-gnu': 4.60.4 + '@rollup/rollup-linux-ppc64-musl': 4.60.4 + '@rollup/rollup-linux-riscv64-gnu': 4.60.4 + '@rollup/rollup-linux-riscv64-musl': 4.60.4 + '@rollup/rollup-linux-s390x-gnu': 4.60.4 + '@rollup/rollup-linux-x64-gnu': 4.60.4 + '@rollup/rollup-linux-x64-musl': 4.60.4 + '@rollup/rollup-openbsd-x64': 4.60.4 + '@rollup/rollup-openharmony-arm64': 4.60.4 + '@rollup/rollup-win32-arm64-msvc': 4.60.4 + '@rollup/rollup-win32-ia32-msvc': 4.60.4 + '@rollup/rollup-win32-x64-gnu': 4.60.4 + '@rollup/rollup-win32-x64-msvc': 4.60.4 fsevents: 2.3.3 router@2.2.0: @@ -6417,7 +6448,7 @@ snapshots: sass@1.97.3: dependencies: chokidar: 4.0.3 - immutable: 5.1.5 + immutable: 5.1.6 source-map-js: 1.2.1 optionalDependencies: '@parcel/watcher': 2.5.6 @@ -6432,6 +6463,8 @@ snapshots: semver@7.7.4: {} + semver@7.8.1: {} + send@1.2.1: dependencies: debug: 4.4.3 @@ -6497,14 +6530,14 @@ snapshots: signal-exit@4.1.0: {} - sigstore@4.1.0: + sigstore@4.1.1: dependencies: '@sigstore/bundle': 4.0.0 - '@sigstore/core': 3.2.0 + '@sigstore/core': 3.2.1 '@sigstore/protobuf-specs': 0.5.1 '@sigstore/sign': 4.1.1 '@sigstore/tuf': 4.0.2 - '@sigstore/verify': 3.1.0 + '@sigstore/verify': 3.1.1 transitivePeerDependencies: - supports-color @@ -6520,10 +6553,10 @@ snapshots: smart-buffer@4.2.0: {} - socket.io-adapter@2.5.6: + socket.io-adapter@2.5.7: dependencies: debug: 4.4.3 - ws: 8.18.3 + ws: 8.20.1 transitivePeerDependencies: - bufferutil - supports-color @@ -6533,7 +6566,7 @@ snapshots: dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.4.3 - engine.io-client: 6.6.4 + engine.io-client: 6.6.5 socket.io-parser: 4.2.6 transitivePeerDependencies: - bufferutil @@ -6553,8 +6586,8 @@ snapshots: base64id: 2.0.0 cors: 2.8.6 debug: 4.4.3 - engine.io: 6.6.6 - socket.io-adapter: 2.5.6 + engine.io: 6.6.8 + socket.io-adapter: 2.5.7 socket.io-parser: 4.2.6 transitivePeerDependencies: - bufferutil @@ -6565,13 +6598,13 @@ snapshots: dependencies: agent-base: 7.1.4 debug: 4.4.3 - socks: 2.8.7 + socks: 2.8.9 transitivePeerDependencies: - supports-color - socks@2.8.7: + socks@2.8.9: dependencies: - ip-address: 10.1.0 + ip-address: 10.2.0 smart-buffer: 4.2.0 source-map-js@1.2.1: {} @@ -6615,12 +6648,12 @@ snapshots: string-width@7.2.0: dependencies: emoji-regex: 10.6.0 - get-east-asian-width: 1.5.0 + get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 - string-width@8.2.0: + string-width@8.2.1: dependencies: - get-east-asian-width: 1.5.0 + get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 strip-ansi@6.0.1: @@ -6639,7 +6672,7 @@ snapshots: symbol-tree@3.2.4: {} - tar@7.5.13: + tar@7.5.15: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 @@ -6649,7 +6682,7 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.1.1: {} + tinyexec@1.2.3: {} tinyglobby@0.2.15: dependencies: @@ -6663,11 +6696,11 @@ snapshots: tinyrainbow@3.1.0: {} - tldts-core@7.0.28: {} + tldts-core@7.4.1: {} - tldts@7.0.28: + tldts@7.4.0: dependencies: - tldts-core: 7.0.28 + tldts-core: 7.4.1 to-regex-range@5.0.1: dependencies: @@ -6677,7 +6710,7 @@ snapshots: tough-cookie@6.0.1: dependencies: - tldts: 7.0.28 + tldts: 7.4.0 tr46@6.0.0: dependencies: @@ -6693,7 +6726,7 @@ snapshots: dependencies: '@tufjs/models': 4.1.0 debug: 4.4.3 - make-fetch-happen: 15.0.5 + make-fetch-happen: 15.0.6 transitivePeerDependencies: - supports-color @@ -6701,9 +6734,9 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-is@2.0.1: + type-is@2.1.0: dependencies: - content-type: 1.0.5 + content-type: 2.0.0 media-typer: 1.1.0 mime-types: 3.0.2 @@ -6720,9 +6753,9 @@ snapshots: typescript@5.9.3: {} - undici-types@7.19.2: {} + undici-types@7.24.6: {} - undici@6.25.0: {} + undici@6.26.0: {} undici@7.24.4: {} @@ -6742,29 +6775,29 @@ snapshots: vary@1.1.2: {} - vite@7.3.2(@types/node@25.6.0)(sass@1.97.3): + vite@7.3.2(@types/node@25.9.1)(sass@1.97.3): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.10 - rollup: 4.60.2 + postcss: 8.5.15 + rollup: 4.60.4 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 fsevents: 2.3.3 sass: 1.97.3 - vitest@4.1.5(@types/node@25.6.0)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3)): + vitest@4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)): dependencies: - '@vitest/expect': 4.1.5 - '@vitest/mocker': 4.1.5(vite@7.3.2(@types/node@25.6.0)(sass@1.97.3)) - '@vitest/pretty-format': 4.1.5 - '@vitest/runner': 4.1.5 - '@vitest/snapshot': 4.1.5 - '@vitest/spy': 4.1.5 - '@vitest/utils': 4.1.5 - es-module-lexer: 2.0.0 + '@vitest/expect': 4.1.7 + '@vitest/mocker': 4.1.7(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)) + '@vitest/pretty-format': 4.1.7 + '@vitest/runner': 4.1.7 + '@vitest/snapshot': 4.1.7 + '@vitest/spy': 4.1.7 + '@vitest/utils': 4.1.7 + es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 obug: 2.1.1 @@ -6772,13 +6805,13 @@ snapshots: picomatch: 4.0.4 std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 1.1.1 + tinyexec: 1.2.3 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 7.3.2(@types/node@25.6.0)(sass@1.97.3) + vite: 7.3.2(@types/node@25.9.1)(sass@1.97.3) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.6.0 + '@types/node': 25.9.1 jsdom: 27.4.0 transitivePeerDependencies: - msw @@ -6835,9 +6868,9 @@ snapshots: wrappy@1.0.2: {} - ws@8.18.3: {} + ws@8.20.1: {} - ws@8.20.0: {} + ws@8.21.0: {} xml-name-validator@5.0.0: {} diff --git a/uv.lock b/uv.lock index 67ebfb6..7d2c324 100644 --- a/uv.lock +++ b/uv.lock @@ -4,11 +4,11 @@ requires-python = ">=3.13" [[package]] name = "aiohappyeyeballs" -version = "2.6.1" +version = "2.6.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591, upload-time = "2026-05-20T15:12:24.631Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, + { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062, upload-time = "2026-05-20T15:12:23.328Z" }, ] [[package]] @@ -181,11 +181,11 @@ wheels = [ [[package]] name = "certifi" -version = "2026.2.25" +version = "2026.5.20" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, + { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, ] [[package]] @@ -324,15 +324,15 @@ wheels = [ [[package]] name = "deno" -version = "2.7.12" +version = "2.8.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5c/ac/39b45b51e7aaf9cad1de36f91bd58677818a7d7a867fa0721faff43b67c4/deno-2.7.12.tar.gz", hash = "sha256:7ef413693b4a9d86837a6f4991738429908f6f42f6f3ed85254ab0e679438049", size = 8167, upload-time = "2026-04-09T20:39:50.505Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/eb/b743a520cdd668e070a4535296123f1c62d054b00699f58e49c32ab5925c/deno-2.8.1.tar.gz", hash = "sha256:fb65e568bef30b1a7e63f033713f1a6792a8456e339febdb7d638c6bb2c4c008", size = 8167, upload-time = "2026-05-27T13:01:06.508Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/f0/0be717182294e9b61e58cd89b88d1b711c7e9c134e53890474b8d1e95704/deno-2.7.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:74677d1be23426b1f16ce33c1a96868bc7554e1d41a12fb4d446017bb1955d1d", size = 47683364, upload-time = "2026-04-09T20:39:35.086Z" }, - { url = "https://files.pythonhosted.org/packages/8d/44/ca1de5368b193c9819c57d505ff8ceb86dc38e2e87856b23c23b58abd234/deno-2.7.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:acf94fa815ac84a948b196f53a7e088ed126ec2e390a25d92cdfdc8c5d8a851e", size = 44460697, upload-time = "2026-04-09T20:39:38.5Z" }, - { url = "https://files.pythonhosted.org/packages/ce/68/ce111604f5877220e53fb10c5180ffaa484f829f20fcbc7f7115a90afadd/deno-2.7.12-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:ab7c12ee0e10cd232dbb8f7cf5802bfdbcc3462c0b1453726b158d8a27d4185f", size = 48237362, upload-time = "2026-04-09T20:39:41.555Z" }, - { url = "https://files.pythonhosted.org/packages/d7/07/d8ba3284e098128076cd9f84e85d2e62d12fa1e5b8e5c47681983017af1a/deno-2.7.12-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b396675fef296530c386387990d26d118398452bac47541e739b353b9ff8809f", size = 50247653, upload-time = "2026-04-09T20:39:44.956Z" }, - { url = "https://files.pythonhosted.org/packages/07/b0/c6c215eca2aa54b700a95867901832245518b2f217edfc78e733401bf8b3/deno-2.7.12-py3-none-win_amd64.whl", hash = "sha256:ddeeb03427d7ce0979a395ae2d1aa5ebcddeb97ab0fd9e7dfa56a08e0e5b817c", size = 49200604, upload-time = "2026-04-09T20:39:48.218Z" }, + { url = "https://files.pythonhosted.org/packages/d1/71/f9dc8ad874dcc26cf1a154c8f89d77e1155ced1f6a64be9d21127bd555ce/deno-2.8.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:733e24e4883c9516534cae7a10277048ffea7cc034f9f726e8c145d48ba75d19", size = 42749443, upload-time = "2026-05-27T13:00:49.439Z" }, + { url = "https://files.pythonhosted.org/packages/f5/db/1721aca1a9bd132a3f721bd547022534fcdd6221701561c5e33705cdfb6d/deno-2.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7b4638504f3730f9b25229db08d1bce87bc64f52498f9f8f5aeba702c7ff2115", size = 39460813, upload-time = "2026-05-27T13:00:52.578Z" }, + { url = "https://files.pythonhosted.org/packages/1a/20/b50646f865562b8f21532cad6f9804b126efd4030cfd0c5e1d11217b15ca/deno-2.8.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:91f29a2df4cb6135872d68f38005c08fe0c389c84cb7349d7399eedbfbe19829", size = 43893026, upload-time = "2026-05-27T13:00:55.939Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1e/75b84e7096b53f077ac01f9b5c979b7b42b0a6497f56d7c4ae072381e059/deno-2.8.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:f0958910ffa88f6b6e142129ab34b7a3aec361fc63904ff803ce1594beca230a", size = 46038250, upload-time = "2026-05-27T13:00:59.662Z" }, + { url = "https://files.pythonhosted.org/packages/14/d6/14f6cf25025644bb8b7d9b4606780b5ec6ee429a55c0d1f05681718c7fc0/deno-2.8.1-py3-none-win_amd64.whl", hash = "sha256:71ec55c0a0944beee376aa824722734cf3e617661bca5e143caa83991921e4f5", size = 41962337, upload-time = "2026-05-27T13:01:03.453Z" }, ] [[package]] @@ -428,11 +428,11 @@ wheels = [ [[package]] name = "idna" -version = "3.11" +version = "3.17" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/28/99c51f664567218d824af024c0251650fb27e4ca066df188dab0769c5b91/idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f", size = 196048, upload-time = "2026-05-28T14:32:38.55Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/de/a7/f76514cc40ad6234098ecdebda08732d75964776c51a42845b7da10649e2/idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c", size = 65316, upload-time = "2026-05-28T14:32:37.035Z" }, ] [[package]] @@ -593,20 +593,20 @@ wheels = [ [[package]] name = "packaging" -version = "26.1" +version = "26.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/df/de/0d2b39fb4af88a0258f3bac87dfcbb48e73fbdea4a2ed0e2213f9a4c2f9a/packaging-26.1.tar.gz", hash = "sha256:f042152b681c4bfac5cae2742a55e103d27ab2ec0f3d88037136b6bfe7c9c5de", size = 215519, upload-time = "2026-04-14T21:12:49.362Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/c2/920ef838e2f0028c8262f16101ec09ebd5969864e5a64c4c05fad0617c56/packaging-26.1-py3-none-any.whl", hash = "sha256:5d9c0669c6285e491e0ced2eee587eaf67b670d94a19e94e3984a481aba6802f", size = 95831, upload-time = "2026-04-14T21:12:47.56Z" }, + { url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" }, ] [[package]] name = "platformdirs" -version = "4.9.6" +version = "4.10.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9f/4a/0883b8e3802965322523f0b200ecf33d31f10991d0401162f4b23c698b42/platformdirs-4.9.6.tar.gz", hash = "sha256:3bfa75b0ad0db84096ae777218481852c0ebc6c727b3168c1b9e0118e458cf0a", size = 29400, upload-time = "2026-04-09T00:04:10.812Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/47/e4501f49c178ae1d9f4a75073fda4204f52647993f075a9db4d14930e0c5/platformdirs-4.10.0.tar.gz", hash = "sha256:31e761a6a0ca04faf7353ea759bdba55652be214725111e5aac52dfa29d4bef7", size = 31224, upload-time = "2026-05-28T03:32:53.587Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/75/a6/a0a304dc33b49145b21f4808d763822111e67d1c3a32b524a1baf947b6e1/platformdirs-4.9.6-py3-none-any.whl", hash = "sha256:e61adb1d5e5cb3441b4b7710bea7e4c12250ca49439228cc1021c00dcfac0917", size = 21348, upload-time = "2026-04-09T00:04:09.463Z" }, + { url = "https://files.pythonhosted.org/packages/81/e6/cd9575ac904136b3cbf7aa7ee819ef86eedb7274e46f230e94ea4342e729/platformdirs-4.10.0-py3-none-any.whl", hash = "sha256:fb516cdb12eb0d857d0cd85a7c57cea4d060bee4578d6cf5a14dfdf8cbf8784a", size = 22743, upload-time = "2026-05-28T03:32:52.175Z" }, ] [[package]] @@ -620,71 +620,79 @@ wheels = [ [[package]] name = "propcache" -version = "0.4.1" +version = "0.5.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, - { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, - { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, - { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, - { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, - { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, - { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, - { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, - { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, - { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, - { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, - { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, - { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, - { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, - { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, - { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, - { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, - { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, - { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, - { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, - { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, - { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, - { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, - { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, - { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, - { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, - { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, - { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, - { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, - { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, - { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, - { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, - { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, - { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, - { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, - { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, - { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, - { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, - { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, - { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, - { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, - { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, - { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, - { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, - { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, - { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, - { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, - { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, - { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, - { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, - { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, - { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, - { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, - { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, - { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, - { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, - { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, + { url = "https://files.pythonhosted.org/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457, upload-time = "2026-05-08T21:00:36.355Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835, upload-time = "2026-05-08T21:00:38.072Z" }, + { url = "https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545, upload-time = "2026-05-08T21:00:39.319Z" }, + { url = "https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886, upload-time = "2026-05-08T21:00:40.621Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261, upload-time = "2026-05-08T21:00:41.775Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184, upload-time = "2026-05-08T21:00:43.018Z" }, + { url = "https://files.pythonhosted.org/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534, upload-time = "2026-05-08T21:00:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500, upload-time = "2026-05-08T21:00:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994, upload-time = "2026-05-08T21:00:47.093Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884, upload-time = "2026-05-08T21:00:48.376Z" }, + { url = "https://files.pythonhosted.org/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464, upload-time = "2026-05-08T21:00:49.954Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588, upload-time = "2026-05-08T21:00:51.281Z" }, + { url = "https://files.pythonhosted.org/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667, upload-time = "2026-05-08T21:00:52.602Z" }, + { url = "https://files.pythonhosted.org/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463, upload-time = "2026-05-08T21:00:54.303Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621, upload-time = "2026-05-08T21:00:55.808Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649, upload-time = "2026-05-08T21:00:57.061Z" }, + { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636, upload-time = "2026-05-08T21:00:58.646Z" }, + { url = "https://files.pythonhosted.org/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872, upload-time = "2026-05-08T21:00:59.889Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257, upload-time = "2026-05-08T21:01:01.195Z" }, + { url = "https://files.pythonhosted.org/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696, upload-time = "2026-05-08T21:01:02.941Z" }, + { url = "https://files.pythonhosted.org/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378, upload-time = "2026-05-08T21:01:04.475Z" }, + { url = "https://files.pythonhosted.org/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283, upload-time = "2026-05-08T21:01:05.959Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616, upload-time = "2026-05-08T21:01:07.228Z" }, + { url = "https://files.pythonhosted.org/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773, upload-time = "2026-05-08T21:01:08.514Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664, upload-time = "2026-05-08T21:01:09.874Z" }, + { url = "https://files.pythonhosted.org/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643, upload-time = "2026-05-08T21:01:11.132Z" }, + { url = "https://files.pythonhosted.org/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595, upload-time = "2026-05-08T21:01:12.387Z" }, + { url = "https://files.pythonhosted.org/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711, upload-time = "2026-05-08T21:01:13.676Z" }, + { url = "https://files.pythonhosted.org/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247, upload-time = "2026-05-08T21:01:14.936Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102, upload-time = "2026-05-08T21:01:16.281Z" }, + { url = "https://files.pythonhosted.org/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964, upload-time = "2026-05-08T21:01:17.57Z" }, + { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546, upload-time = "2026-05-08T21:01:18.946Z" }, + { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330, upload-time = "2026-05-08T21:01:20.162Z" }, + { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521, upload-time = "2026-05-08T21:01:21.399Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ea/23ee535d90ce8bcc465a3028eb3cc0ce3bd1005f4bb27710b30587de798d/propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999", size = 94662, upload-time = "2026-05-08T21:01:22.683Z" }, + { url = "https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e", size = 53928, upload-time = "2026-05-08T21:01:23.986Z" }, + { url = "https://files.pythonhosted.org/packages/63/b1/4260d67d6bd85e58a66b72d54ce15d5de789b6f3870cc6bedf8ff9667401/propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539", size = 54650, upload-time = "2026-05-08T21:01:25.305Z" }, + { url = "https://files.pythonhosted.org/packages/70/06/2f46c318e3307cd7a6a7481def374ce838c0fe20084b39dd54b0879d0e99/propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e", size = 59912, upload-time = "2026-05-08T21:01:26.545Z" }, + { url = "https://files.pythonhosted.org/packages/4c/29/fe1aebec2ce57ab985a9c382bded1124431f85078113aa222c5d278430d4/propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979", size = 63300, upload-time = "2026-05-08T21:01:27.937Z" }, + { url = "https://files.pythonhosted.org/packages/b4/18/2334b26768b6c82be8c69e83671b767d5ef426aa09b0cba6c2ea47816774/propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80", size = 64208, upload-time = "2026-05-08T21:01:29.484Z" }, + { url = "https://files.pythonhosted.org/packages/2b/76/7f1bfd6afff4c5e38e36a3c6d68eb5f4b7311ea80baf693db78d95b603c4/propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825", size = 61633, upload-time = "2026-05-08T21:01:31.068Z" }, + { url = "https://files.pythonhosted.org/packages/c4/46/b3ff8aba2b4953a3e50de2cf72f1b5748b8eca93b15f3dc2c84339084c09/propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39", size = 61724, upload-time = "2026-05-08T21:01:32.374Z" }, + { url = "https://files.pythonhosted.org/packages/c5/01/814cfcafbcff954f94c01cf30e097ddc88a076b5440fbcf4570753437d40/propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4", size = 60069, upload-time = "2026-05-08T21:01:33.67Z" }, + { url = "https://files.pythonhosted.org/packages/da/68/5c6f7622d510cc666a300687e06fd060c1a43361c0c9b20d284f06d8096a/propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5", size = 57099, upload-time = "2026-05-08T21:01:34.915Z" }, + { url = "https://files.pythonhosted.org/packages/55/27/9cb0b4c679124085327957d42521c99dba04c88c90c3e55a6f0b633ebccc/propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702", size = 63391, upload-time = "2026-05-08T21:01:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9d/7258aaa5bdf60fc6f27591eef6fe52768cb0beda7140be477c8b12c9794a/propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3", size = 61626, upload-time = "2026-05-08T21:01:37.545Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0d/41c602003e8a9b16fe1e7eadf62c7bfba9d5474370b24200bf48b315f45f/propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5", size = 64781, upload-time = "2026-05-08T21:01:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f3/38e66b1856e9bd079deea015bc4a55f7767c0e4db2f7dcf69e7e680ba4ce/propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4", size = 62570, upload-time = "2026-05-08T21:01:40.415Z" }, + { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436, upload-time = "2026-05-08T21:01:41.654Z" }, + { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373, upload-time = "2026-05-08T21:01:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554, upload-time = "2026-05-08T21:01:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/8a/81/fadf555f42d3b762eea8a53950b0489fdc0aa9da5f8ed9e10ce0a4e01b48/propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb", size = 99395, upload-time = "2026-05-08T21:01:45.883Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c9/c61e134a686949cf7971af3a390148b1156f7be81c73bc0cd12c873e2d48/propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078", size = 56653, upload-time = "2026-05-08T21:01:47.307Z" }, + { url = "https://files.pythonhosted.org/packages/cb/73/daf935ea7048ddd7ec8eec5345b4a40b619d2d178b3c0a0900796bc3c794/propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa", size = 56914, upload-time = "2026-05-08T21:01:48.573Z" }, + { url = "https://files.pythonhosted.org/packages/79/9f/aba959b435ea18617edd7cf0a7ad0b9c574b8fc7e3d2cd55fb59cb255d33/propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917", size = 62567, upload-time = "2026-05-08T21:01:49.903Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a1/859942de9a791ff42f6141736f5b37749b8f53e65edfa49638c67dd67e6a/propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe", size = 65542, upload-time = "2026-05-08T21:01:51.204Z" }, + { url = "https://files.pythonhosted.org/packages/b5/61/315bc0fd6c0fc7f80a528b8afd209e5fc4a875ea79571b91b8f50f442907/propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03", size = 66845, upload-time = "2026-05-08T21:01:52.539Z" }, + { url = "https://files.pythonhosted.org/packages/47/f7/9f8122e3132e8e354ac41975ef8f1099be7d5a16bc7ae562734e993665c0/propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335", size = 63985, upload-time = "2026-05-08T21:01:53.847Z" }, + { url = "https://files.pythonhosted.org/packages/c8/54/c317819ec157cbf6f35df9df9657a6f82daf34d5faf15948b2f639c2192e/propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285", size = 63999, upload-time = "2026-05-08T21:01:55.179Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/387e3f7dfce0a9233df41fb888aa1c30222cb4bbbf09537c02dd9bd85fe2/propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837", size = 62779, upload-time = "2026-05-08T21:01:57.489Z" }, + { url = "https://files.pythonhosted.org/packages/a1/9c/596784cb5824ed61ee960d3f8655a3f0993e107c6e98ab6c818b7fb92ccb/propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8", size = 59796, upload-time = "2026-05-08T21:01:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3d/1a6cfa1726a48542c1e8784a0761421476a5b68e09b7f36bf95eb954aaba/propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366", size = 66023, upload-time = "2026-05-08T21:02:00.228Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0e/05fd6990369477076e4e280bcb970de760fddf0161a46e988bc95f7940ec/propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56", size = 64448, upload-time = "2026-05-08T21:02:01.888Z" }, + { url = "https://files.pythonhosted.org/packages/cd/86/5f8da315a4309c62c10c0b2516b17492d5d3bbe1bb862b96604db67e2a37/propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d", size = 67329, upload-time = "2026-05-08T21:02:03.484Z" }, + { url = "https://files.pythonhosted.org/packages/da/d3/3368efe79ab21f0cdf86ef49895811c9cc933131d4cde1f28a624e22e712/propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2", size = 65172, upload-time = "2026-05-08T21:02:04.745Z" }, + { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813, upload-time = "2026-05-08T21:02:06.025Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764, upload-time = "2026-05-08T21:02:07.353Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140, upload-time = "2026-05-08T21:02:09.065Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, ] [[package]] @@ -785,44 +793,44 @@ wheels = [ [[package]] name = "pytest-asyncio" -version = "1.3.0" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pytest" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/7c/d36d04db312ecf4298932ef77e6e4a9e8ad017906e24e34f0b0c361a2473/pytest_asyncio-1.4.0.tar.gz", hash = "sha256:c6c0d2259945122819f171a32ecea2c349ead889ee28176caaf492143424be42", size = 58514, upload-time = "2026-05-26T09:56:04.083Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, + { url = "https://files.pythonhosted.org/packages/03/e2/08a497ef684b88559c9cc5f4ad53a37e7b99e727094a86d6ea32536d5d3c/pytest_asyncio-1.4.0-py3-none-any.whl", hash = "sha256:933ca923a23075a87fb7070c0ec272a6848489824d887c85c812670932835aa1", size = 16930, upload-time = "2026-05-26T09:56:02.576Z" }, ] [[package]] name = "python-engineio" -version = "4.13.1" +version = "4.13.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "simple-websocket" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/34/12/bdef9dbeedbe2cdeba2a2056ad27b1fb081557d34b69a97f574843462cae/python_engineio-4.13.1.tar.gz", hash = "sha256:0a853fcef52f5b345425d8c2b921ac85023a04dfcf75d7b74696c61e940fd066", size = 92348, upload-time = "2026-02-06T23:38:06.12Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/6d/4384c2723adad93a3d6de4297e6d9c8b93be7f778a407f34f6ee0b2bea3e/python_engineio-4.13.2.tar.gz", hash = "sha256:a7732e99cfb7db6ed1aee31f18d7f73bbae086a92f31dee019bc646155d9684e", size = 79639, upload-time = "2026-05-21T21:45:07.578Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/54/0cce26da03a981f949bb8449c9778537f75f5917c172e1d2992ff25cb57d/python_engineio-4.13.1-py3-none-any.whl", hash = "sha256:f32ad10589859c11053ad7d9bb3c9695cdf862113bfb0d20bc4d890198287399", size = 59847, upload-time = "2026-02-06T23:38:04.861Z" }, + { url = "https://files.pythonhosted.org/packages/b7/28/180bfc5c95e83d40cb2abce512684ccad44e4819ec899fc36cb404a19061/python_engineio-4.13.2-py3-none-any.whl", hash = "sha256:8c101cd170e400dc4e970cd523325cde22df8fc25140953f379327055d701a6b", size = 59993, upload-time = "2026-05-21T21:45:06.162Z" }, ] [[package]] name = "python-socketio" -version = "5.16.1" +version = "5.16.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bidict" }, { name = "python-engineio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/59/81/cf8284f45e32efa18d3848ed82cdd4dcc1b657b082458fbe01ad3e1f2f8d/python_socketio-5.16.1.tar.gz", hash = "sha256:f863f98eacce81ceea2e742f6388e10ca3cdd0764be21d30d5196470edf5ea89", size = 128508, upload-time = "2026-02-06T23:42:07Z" } +sdist = { url = "https://files.pythonhosted.org/packages/07/dd/6fd4112b941f7d39b8171b6ba17902609bd8fa2059c3812a3c29dade13e7/python_socketio-5.16.2.tar.gz", hash = "sha256:ad88c228d921646efa436c0a0df217e364ef30ec072df4041484e54d49c15989", size = 128011, upload-time = "2026-05-21T22:03:44.418Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/07/c7/deb8c5e604404dbf10a3808a858946ca3547692ff6316b698945bb72177e/python_socketio-5.16.1-py3-none-any.whl", hash = "sha256:a3eb1702e92aa2f2b5d3ba00261b61f062cce51f1cfb6900bf3ab4d1934d2d35", size = 82054, upload-time = "2026-02-06T23:42:05.772Z" }, + { url = "https://files.pythonhosted.org/packages/72/dc/0decaf5da92a7a969374474025787102d811d42aed1d32191fa338620e15/python_socketio-5.16.2-py3-none-any.whl", hash = "sha256:bef2da3374fd533aed4297f57b4f6512b52aa51604cb0da2165f401291c5ca20", size = 82137, upload-time = "2026-05-21T22:03:42.616Z" }, ] [[package]] name = "requests" -version = "2.33.1" +version = "2.34.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, @@ -830,9 +838,9 @@ dependencies = [ { name = "idna" }, { name = "urllib3" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5f/a4/98b9c7c6428a668bf7e42ebb7c79d576a1c3c1e3ae2d47e674b468388871/requests-2.33.1.tar.gz", hash = "sha256:18817f8c57c6263968bc123d237e3b8b08ac046f5456bd1e307ee8f4250d3517", size = 134120, upload-time = "2026-03-30T16:09:15.531Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/8e/7540e8a2036f79a125c1d2ebadf69ed7901608859186c856fa0388ef4197/requests-2.33.1-py3-none-any.whl", hash = "sha256:4e6d1ef462f3626a1f0a0a9c42dd93c63bad33f9f1c1937509b8c5c8718ab56a", size = 64947, upload-time = "2026-03-30T16:09:13.83Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, ] [[package]] @@ -849,77 +857,92 @@ wheels = [ [[package]] name = "tomlkit" -version = "0.14.0" +version = "0.15.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167, upload-time = "2026-01-13T01:14:53.304Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/db/03eaf4331631ef6b27d6e3c9b68c54dc6f0d63d87201fed600cc409307fd/tomlkit-0.15.0.tar.gz", hash = "sha256:7d1a9ecba3086638211b13814ea79c90dd54dd11993564376f3aa92271f5c7a3", size = 161875, upload-time = "2026-05-10T07:38:22.245Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, + { url = "https://files.pythonhosted.org/packages/6a/43/8bd850ee71a191bf072e31302c73a66be413fecdd98fdcd111ecbcce13ca/tomlkit-0.15.0-py3-none-any.whl", hash = "sha256:4dbc8f0fc024412b57ced8757ac7461305126a648ff8c2c807fcb8e133a78738", size = 41328, upload-time = "2026-05-10T07:38:23.517Z" }, ] [[package]] name = "urllib3" -version = "2.6.3" +version = "2.7.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, ] [[package]] name = "watchfiles" -version = "1.1.1" +version = "1.2.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c2/c9/8869df9b2a2d6c59d79220a4db37679e74f807c559ffe5265e08b227a210/watchfiles-1.1.1.tar.gz", hash = "sha256:a173cb5c16c4f40ab19cecf48a534c409f7ea983ab8fed0741304a1c0a31b3f2", size = 94440, upload-time = "2025-10-14T15:06:21.08Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/41/5e1a4bb12aac5f1493fa1bdc11154eca3b258ca4eba65d39c473fe19d8e9/watchfiles-1.2.0.tar.gz", hash = "sha256:c995fba777f1ea992f090f9236e9284cf7a5d1a0130dd5a3d82c598cacd76838", size = 108252, upload-time = "2026-05-18T04:32:04.251Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/bb/f4/f750b29225fe77139f7ae5de89d4949f5a99f934c65a1f1c0b248f26f747/watchfiles-1.1.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:130e4876309e8686a5e37dba7d5e9bc77e6ed908266996ca26572437a5271e18", size = 404321, upload-time = "2025-10-14T15:05:02.063Z" }, - { url = "https://files.pythonhosted.org/packages/2b/f9/f07a295cde762644aa4c4bb0f88921d2d141af45e735b965fb2e87858328/watchfiles-1.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5f3bde70f157f84ece3765b42b4a52c6ac1a50334903c6eaf765362f6ccca88a", size = 391783, upload-time = "2025-10-14T15:05:03.052Z" }, - { url = "https://files.pythonhosted.org/packages/bc/11/fc2502457e0bea39a5c958d86d2cb69e407a4d00b85735ca724bfa6e0d1a/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14e0b1fe858430fc0251737ef3824c54027bedb8c37c38114488b8e131cf8219", size = 449279, upload-time = "2025-10-14T15:05:04.004Z" }, - { url = "https://files.pythonhosted.org/packages/e3/1f/d66bc15ea0b728df3ed96a539c777acfcad0eb78555ad9efcaa1274688f0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f27db948078f3823a6bb3b465180db8ebecf26dd5dae6f6180bd87383b6b4428", size = 459405, upload-time = "2025-10-14T15:05:04.942Z" }, - { url = "https://files.pythonhosted.org/packages/be/90/9f4a65c0aec3ccf032703e6db02d89a157462fbb2cf20dd415128251cac0/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059098c3a429f62fc98e8ec62b982230ef2c8df68c79e826e37b895bc359a9c0", size = 488976, upload-time = "2025-10-14T15:05:05.905Z" }, - { url = "https://files.pythonhosted.org/packages/37/57/ee347af605d867f712be7029bb94c8c071732a4b44792e3176fa3c612d39/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfb5862016acc9b869bb57284e6cb35fdf8e22fe59f7548858e2f971d045f150", size = 595506, upload-time = "2025-10-14T15:05:06.906Z" }, - { url = "https://files.pythonhosted.org/packages/a8/78/cc5ab0b86c122047f75e8fc471c67a04dee395daf847d3e59381996c8707/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:319b27255aacd9923b8a276bb14d21a5f7ff82564c744235fc5eae58d95422ae", size = 474936, upload-time = "2025-10-14T15:05:07.906Z" }, - { url = "https://files.pythonhosted.org/packages/62/da/def65b170a3815af7bd40a3e7010bf6ab53089ef1b75d05dd5385b87cf08/watchfiles-1.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d", size = 456147, upload-time = "2025-10-14T15:05:09.138Z" }, - { url = "https://files.pythonhosted.org/packages/57/99/da6573ba71166e82d288d4df0839128004c67d2778d3b566c138695f5c0b/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c22c776292a23bfc7237a98f791b9ad3144b02116ff10d820829ce62dff46d0b", size = 630007, upload-time = "2025-10-14T15:05:10.117Z" }, - { url = "https://files.pythonhosted.org/packages/a8/51/7439c4dd39511368849eb1e53279cd3454b4a4dbace80bab88feeb83c6b5/watchfiles-1.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3a476189be23c3686bc2f4321dd501cb329c0a0469e77b7b534ee10129ae6374", size = 622280, upload-time = "2025-10-14T15:05:11.146Z" }, - { url = "https://files.pythonhosted.org/packages/95/9c/8ed97d4bba5db6fdcdb2b298d3898f2dd5c20f6b73aee04eabe56c59677e/watchfiles-1.1.1-cp313-cp313-win32.whl", hash = "sha256:bf0a91bfb5574a2f7fc223cf95eeea79abfefa404bf1ea5e339c0c1560ae99a0", size = 272056, upload-time = "2025-10-14T15:05:12.156Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f3/c14e28429f744a260d8ceae18bf58c1d5fa56b50d006a7a9f80e1882cb0d/watchfiles-1.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:52e06553899e11e8074503c8e716d574adeeb7e68913115c4b3653c53f9bae42", size = 288162, upload-time = "2025-10-14T15:05:13.208Z" }, - { url = "https://files.pythonhosted.org/packages/dc/61/fe0e56c40d5cd29523e398d31153218718c5786b5e636d9ae8ae79453d27/watchfiles-1.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:ac3cc5759570cd02662b15fbcd9d917f7ecd47efe0d6b40474eafd246f91ea18", size = 277909, upload-time = "2025-10-14T15:05:14.49Z" }, - { url = "https://files.pythonhosted.org/packages/79/42/e0a7d749626f1e28c7108a99fb9bf524b501bbbeb9b261ceecde644d5a07/watchfiles-1.1.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:563b116874a9a7ce6f96f87cd0b94f7faf92d08d0021e837796f0a14318ef8da", size = 403389, upload-time = "2025-10-14T15:05:15.777Z" }, - { url = "https://files.pythonhosted.org/packages/15/49/08732f90ce0fbbc13913f9f215c689cfc9ced345fb1bcd8829a50007cc8d/watchfiles-1.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3ad9fe1dae4ab4212d8c91e80b832425e24f421703b5a42ef2e4a1e215aff051", size = 389964, upload-time = "2025-10-14T15:05:16.85Z" }, - { url = "https://files.pythonhosted.org/packages/27/0d/7c315d4bd5f2538910491a0393c56bf70d333d51bc5b34bee8e68e8cea19/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce70f96a46b894b36eba678f153f052967a0d06d5b5a19b336ab0dbbd029f73e", size = 448114, upload-time = "2025-10-14T15:05:17.876Z" }, - { url = "https://files.pythonhosted.org/packages/c3/24/9e096de47a4d11bc4df41e9d1e61776393eac4cb6eb11b3e23315b78b2cc/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb467c999c2eff23a6417e58d75e5828716f42ed8289fe6b77a7e5a91036ca70", size = 460264, upload-time = "2025-10-14T15:05:18.962Z" }, - { url = "https://files.pythonhosted.org/packages/cc/0f/e8dea6375f1d3ba5fcb0b3583e2b493e77379834c74fd5a22d66d85d6540/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:836398932192dae4146c8f6f737d74baeac8b70ce14831a239bdb1ca882fc261", size = 487877, upload-time = "2025-10-14T15:05:20.094Z" }, - { url = "https://files.pythonhosted.org/packages/ac/5b/df24cfc6424a12deb41503b64d42fbea6b8cb357ec62ca84a5a3476f654a/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:743185e7372b7bc7c389e1badcc606931a827112fbbd37f14c537320fca08620", size = 595176, upload-time = "2025-10-14T15:05:21.134Z" }, - { url = "https://files.pythonhosted.org/packages/8f/b5/853b6757f7347de4e9b37e8cc3289283fb983cba1ab4d2d7144694871d9c/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:afaeff7696e0ad9f02cbb8f56365ff4686ab205fcf9c4c5b6fdfaaa16549dd04", size = 473577, upload-time = "2025-10-14T15:05:22.306Z" }, - { url = "https://files.pythonhosted.org/packages/e1/f7/0a4467be0a56e80447c8529c9fce5b38eab4f513cb3d9bf82e7392a5696b/watchfiles-1.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7eb7da0eb23aa2ba036d4f616d46906013a68caf61b7fdbe42fc8b25132e77", size = 455425, upload-time = "2025-10-14T15:05:23.348Z" }, - { url = "https://files.pythonhosted.org/packages/8e/e0/82583485ea00137ddf69bc84a2db88bd92ab4a6e3c405e5fb878ead8d0e7/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:831a62658609f0e5c64178211c942ace999517f5770fe9436be4c2faeba0c0ef", size = 628826, upload-time = "2025-10-14T15:05:24.398Z" }, - { url = "https://files.pythonhosted.org/packages/28/9a/a785356fccf9fae84c0cc90570f11702ae9571036fb25932f1242c82191c/watchfiles-1.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f9a2ae5c91cecc9edd47e041a930490c31c3afb1f5e6d71de3dc671bfaca02bf", size = 622208, upload-time = "2025-10-14T15:05:25.45Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f4/0872229324ef69b2c3edec35e84bd57a1289e7d3fe74588048ed8947a323/watchfiles-1.1.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:d1715143123baeeaeadec0528bb7441103979a1d5f6fd0e1f915383fea7ea6d5", size = 404315, upload-time = "2025-10-14T15:05:26.501Z" }, - { url = "https://files.pythonhosted.org/packages/7b/22/16d5331eaed1cb107b873f6ae1b69e9ced582fcf0c59a50cd84f403b1c32/watchfiles-1.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd", size = 390869, upload-time = "2025-10-14T15:05:27.649Z" }, - { url = "https://files.pythonhosted.org/packages/b2/7e/5643bfff5acb6539b18483128fdc0ef2cccc94a5b8fbda130c823e8ed636/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb", size = 449919, upload-time = "2025-10-14T15:05:28.701Z" }, - { url = "https://files.pythonhosted.org/packages/51/2e/c410993ba5025a9f9357c376f48976ef0e1b1aefb73b97a5ae01a5972755/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bfff9740c69c0e4ed32416f013f3c45e2ae42ccedd1167ef2d805c000b6c71a5", size = 460845, upload-time = "2025-10-14T15:05:30.064Z" }, - { url = "https://files.pythonhosted.org/packages/8e/a4/2df3b404469122e8680f0fcd06079317e48db58a2da2950fb45020947734/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b27cf2eb1dda37b2089e3907d8ea92922b673c0c427886d4edc6b94d8dfe5db3", size = 489027, upload-time = "2025-10-14T15:05:31.064Z" }, - { url = "https://files.pythonhosted.org/packages/ea/84/4587ba5b1f267167ee715b7f66e6382cca6938e0a4b870adad93e44747e6/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:526e86aced14a65a5b0ec50827c745597c782ff46b571dbfe46192ab9e0b3c33", size = 595615, upload-time = "2025-10-14T15:05:32.074Z" }, - { url = "https://files.pythonhosted.org/packages/6a/0f/c6988c91d06e93cd0bb3d4a808bcf32375ca1904609835c3031799e3ecae/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04e78dd0b6352db95507fd8cb46f39d185cf8c74e4cf1e4fbad1d3df96faf510", size = 474836, upload-time = "2025-10-14T15:05:33.209Z" }, - { url = "https://files.pythonhosted.org/packages/b4/36/ded8aebea91919485b7bbabbd14f5f359326cb5ec218cd67074d1e426d74/watchfiles-1.1.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05", size = 455099, upload-time = "2025-10-14T15:05:34.189Z" }, - { url = "https://files.pythonhosted.org/packages/98/e0/8c9bdba88af756a2fce230dd365fab2baf927ba42cd47521ee7498fd5211/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:74d5012b7630714b66be7b7b7a78855ef7ad58e8650c73afc4c076a1f480a8d6", size = 630626, upload-time = "2025-10-14T15:05:35.216Z" }, - { url = "https://files.pythonhosted.org/packages/2a/84/a95db05354bf2d19e438520d92a8ca475e578c647f78f53197f5a2f17aaf/watchfiles-1.1.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:8fbe85cb3201c7d380d3d0b90e63d520f15d6afe217165d7f98c9c649654db81", size = 622519, upload-time = "2025-10-14T15:05:36.259Z" }, - { url = "https://files.pythonhosted.org/packages/1d/ce/d8acdc8de545de995c339be67711e474c77d643555a9bb74a9334252bd55/watchfiles-1.1.1-cp314-cp314-win32.whl", hash = "sha256:3fa0b59c92278b5a7800d3ee7733da9d096d4aabcfabb9a928918bd276ef9b9b", size = 272078, upload-time = "2025-10-14T15:05:37.63Z" }, - { url = "https://files.pythonhosted.org/packages/c4/c9/a74487f72d0451524be827e8edec251da0cc1fcf111646a511ae752e1a3d/watchfiles-1.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:c2047d0b6cea13b3316bdbafbfa0c4228ae593d995030fda39089d36e64fc03a", size = 287664, upload-time = "2025-10-14T15:05:38.95Z" }, - { url = "https://files.pythonhosted.org/packages/df/b8/8ac000702cdd496cdce998c6f4ee0ca1f15977bba51bdf07d872ebdfc34c/watchfiles-1.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:842178b126593addc05acf6fce960d28bc5fae7afbaa2c6c1b3a7b9460e5be02", size = 277154, upload-time = "2025-10-14T15:05:39.954Z" }, - { url = "https://files.pythonhosted.org/packages/47/a8/e3af2184707c29f0f14b1963c0aace6529f9d1b8582d5b99f31bbf42f59e/watchfiles-1.1.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:88863fbbc1a7312972f1c511f202eb30866370ebb8493aef2812b9ff28156a21", size = 403820, upload-time = "2025-10-14T15:05:40.932Z" }, - { url = "https://files.pythonhosted.org/packages/c0/ec/e47e307c2f4bd75f9f9e8afbe3876679b18e1bcec449beca132a1c5ffb2d/watchfiles-1.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:55c7475190662e202c08c6c0f4d9e345a29367438cf8e8037f3155e10a88d5a5", size = 390510, upload-time = "2025-10-14T15:05:41.945Z" }, - { url = "https://files.pythonhosted.org/packages/d5/a0/ad235642118090f66e7b2f18fd5c42082418404a79205cdfca50b6309c13/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f53fa183d53a1d7a8852277c92b967ae99c2d4dcee2bfacff8868e6e30b15f7", size = 448408, upload-time = "2025-10-14T15:05:43.385Z" }, - { url = "https://files.pythonhosted.org/packages/df/85/97fa10fd5ff3332ae17e7e40e20784e419e28521549780869f1413742e9d/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6aae418a8b323732fa89721d86f39ec8f092fc2af67f4217a2b07fd3e93c6101", size = 458968, upload-time = "2025-10-14T15:05:44.404Z" }, - { url = "https://files.pythonhosted.org/packages/47/c2/9059c2e8966ea5ce678166617a7f75ecba6164375f3b288e50a40dc6d489/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f096076119da54a6080e8920cbdaac3dbee667eb91dcc5e5b78840b87415bd44", size = 488096, upload-time = "2025-10-14T15:05:45.398Z" }, - { url = "https://files.pythonhosted.org/packages/94/44/d90a9ec8ac309bc26db808a13e7bfc0e4e78b6fc051078a554e132e80160/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00485f441d183717038ed2e887a7c868154f216877653121068107b227a2f64c", size = 596040, upload-time = "2025-10-14T15:05:46.502Z" }, - { url = "https://files.pythonhosted.org/packages/95/68/4e3479b20ca305cfc561db3ed207a8a1c745ee32bf24f2026a129d0ddb6e/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a55f3e9e493158d7bfdb60a1165035f1cf7d320914e7b7ea83fe22c6023b58fc", size = 473847, upload-time = "2025-10-14T15:05:47.484Z" }, - { url = "https://files.pythonhosted.org/packages/4f/55/2af26693fd15165c4ff7857e38330e1b61ab8c37d15dc79118cdba115b7a/watchfiles-1.1.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c91ed27800188c2ae96d16e3149f199d62f86c7af5f5f4d2c61a3ed8cd3666c", size = 455072, upload-time = "2025-10-14T15:05:48.928Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/d0d200b10c9311ec25d2273f8aad8c3ef7cc7ea11808022501811208a750/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:311ff15a0bae3714ffb603e6ba6dbfba4065ab60865d15a6ec544133bdb21099", size = 629104, upload-time = "2025-10-14T15:05:49.908Z" }, - { url = "https://files.pythonhosted.org/packages/e3/bd/fa9bb053192491b3867ba07d2343d9f2252e00811567d30ae8d0f78136fe/watchfiles-1.1.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a916a2932da8f8ab582f242c065f5c81bed3462849ca79ee357dd9551b0e9b01", size = 622112, upload-time = "2025-10-14T15:05:50.941Z" }, + { url = "https://files.pythonhosted.org/packages/d1/4d/70a7feced9f87e2ff26dba42667290f41694fc64646c67261fbb8cab5d5c/watchfiles-1.2.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:01ea8d66f0693b9b60a6541c8d10263091ca9a9060d242f3c1f3143f9aad2c98", size = 399730, upload-time = "2026-05-18T04:31:38.162Z" }, + { url = "https://files.pythonhosted.org/packages/31/3a/0da302f2307aee316922806ebd5726c542cbd787c938271cf14a074c7daf/watchfiles-1.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ba0480b9a74af058f43b337e937a451e109295c420916d68ad24e3dc02f5e44", size = 392842, upload-time = "2026-05-18T04:30:27.051Z" }, + { url = "https://files.pythonhosted.org/packages/db/ef/d5bdb705c224dbc256aa0c1ec47bf4e61ec52558f2afb44a71a1fe4d7015/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f34e26a19f91f710c08e0183429f0d1d15df734e6bc78c31e77b9ea9c433658", size = 452989, upload-time = "2026-05-18T04:31:11.945Z" }, + { url = "https://files.pythonhosted.org/packages/71/29/5495f2c1661949ef7a35e4d71111d129cfe7606414a26887a919d0a55406/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b4e77f6a55f858504069abd35d336a637555c09bca453dde1ee1e5ada8a6a1fb", size = 458978, upload-time = "2026-05-18T04:30:52.606Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/7f9c07c433811c2fffd93e13fdfb7135de9aab5f2ae41be08960fa0047dc/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0cb4d80e212f116474a545c21c912b445f16bb0cef9e6a73a498164223e14e2f", size = 490248, upload-time = "2026-05-18T04:31:36.003Z" }, + { url = "https://files.pythonhosted.org/packages/3c/11/d93632febc52fbc21be90231bb7c17fd5387f46c9076fd40a5f9c2ae6910/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b974946a10af379d425e2eef5b62f5c6ebeaccf91d45eaad6f5b27ecd4f91aa0", size = 571847, upload-time = "2026-05-18T04:31:10.862Z" }, + { url = "https://files.pythonhosted.org/packages/55/b4/383173e73aabb07ad1d9c7aa859d95437ac46a6d6a1e11005facda0c9d19/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86bc13c25a8d1fcd70b51d0ce7c9b65e90de5666fcbfd3e34957cc73ee19aeb5", size = 465974, upload-time = "2026-05-18T04:30:17.006Z" }, + { url = "https://files.pythonhosted.org/packages/a7/6c/89b1a230a78f57c52dd8893adb1f92f94411721b6ec12596c56d98c74356/watchfiles-1.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca148d73dea36c9763aaa351e4d7a51780ec1584217c45276f4fe8239c768b71", size = 454782, upload-time = "2026-05-18T04:30:35.656Z" }, + { url = "https://files.pythonhosted.org/packages/24/62/1732118367cfff0a9fce3bf62ff4bfded09ef5df21d9d446b858b3f70a96/watchfiles-1.2.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:c525543d91961c6955b2636b308569e84a1d1c5f5f2932041ab9ef46422f43e3", size = 465182, upload-time = "2026-05-18T04:30:20.846Z" }, + { url = "https://files.pythonhosted.org/packages/28/96/716f7e5f51339bf22963f3345f9f27d7f3b30e2eadc597e257c881dd3c53/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a204794696ffb8f9b10fba6f7cb5216d42f3b2b71860ccac6b6e42f5f10973b0", size = 629841, upload-time = "2026-05-18T04:31:05.397Z" }, + { url = "https://files.pythonhosted.org/packages/4c/fe/c40783950fd771ccf66ab3ec2722d188a9af1c7f96c6e811f36e40c6e03f/watchfiles-1.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:10d86db20695afe7997ac9e1717637d6714a8d0220458c33f3d2061f54cec427", size = 658028, upload-time = "2026-05-18T04:31:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/71/72/4508db1856d1d87fcbb3b63f4839bab1b5682cb0e8d224d122263c09654a/watchfiles-1.2.0-cp313-cp313-win32.whl", hash = "sha256:eb283ee99e21ad6443c8cdb06ac5b34b1308c329cbdf03fa02b445363714c799", size = 275183, upload-time = "2026-05-18T04:30:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/f9/36/14b76ca57652e5cc5fd1c11f32a261292c08a0d19a00351013c2549cbfb2/watchfiles-1.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a0f27f01bee51861392bb6b7c4fdb290b27d1eb194e9e28788d68102a0e898d9", size = 288059, upload-time = "2026-05-18T04:32:07.937Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8d/0a85e395398d8d20fadfe5c5d32c726eee17a519e78fb356f2cf7531bffe/watchfiles-1.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:3651aa7058595e9cfb75d35dd5ada2bf9f48a5b8a0f3562821d3e210c507e077", size = 280186, upload-time = "2026-05-18T04:31:54.484Z" }, + { url = "https://files.pythonhosted.org/packages/37/68/36db056f1fdcc5f07302f56e631774d6835bcd6fa3ace402304621d5f9e5/watchfiles-1.2.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:faea288b6f0ab1902ef08f4ca6de005dccf856c4e0c4f21b8c5fce02d90a1b08", size = 399031, upload-time = "2026-05-18T04:30:44.576Z" }, + { url = "https://files.pythonhosted.org/packages/c1/64/01a9d6f66a82a5c101ce939274106cc72759d62427e153f01edd2b9f87c2/watchfiles-1.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:01859b11fd9fbca670f4d5da00fbac282cfea9bd67a2125d8b2833a3b5617ea9", size = 391205, upload-time = "2026-05-18T04:30:25.413Z" }, + { url = "https://files.pythonhosted.org/packages/84/2c/0a44fe058cb4bb7b8ede6b6670698bbb7c0400740e378d00022189b7b31d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fff610d7bb2256a317bb1e96f0d7862c7aa8076733ee5df0fd41bbe76a24a4f4", size = 451892, upload-time = "2026-05-18T04:32:14.005Z" }, + { url = "https://files.pythonhosted.org/packages/67/a1/351e0d56cd35e6488b5c8b4fb11a809a5bc923e8fe8fed9faf8920be0c89/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b141a4891c995a039cd89e9a49e62df1dc8a559a5d1a6e4c7106d16c12777a55", size = 458867, upload-time = "2026-05-18T04:31:22.279Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7d/9d09605187f1b838998624049fcf8bf47b73c1a3b76901fcac1782f62277/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f22943b7770483f6ea0721c6b11d022947a98eb0acae14694de034f4d0d38925", size = 490217, upload-time = "2026-05-18T04:31:43.657Z" }, + { url = "https://files.pythonhosted.org/packages/60/5d/a17a16eccb182f04188cd308ec24b1a71a9b5c4e7098269cf35d9fa56d02/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1bc6195825b7dcd217968bb1f801a60fd4c16e8eeab5bedc7fe917d7d5995ab4", size = 571458, upload-time = "2026-05-18T04:32:11.875Z" }, + { url = "https://files.pythonhosted.org/packages/d3/3d/4dd457062083ab1938e5dfd45032eb425cee2ac817287ca8ff4356183e5d/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d4a4b147f5dca2a5d325a06a832fb43f345751adfbc63204aec30e0d9ca965a2", size = 464707, upload-time = "2026-05-18T04:30:43.492Z" }, + { url = "https://files.pythonhosted.org/packages/c6/71/ea8c57b128f5383de74d0c7d2d9c57ad7c9a65a930c451bd25d524b295b7/watchfiles-1.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4543579a9bdb0c9560039b4ffddbdb39545707659fbc430ce4c10f3f68d557f9", size = 454663, upload-time = "2026-05-18T04:30:16.061Z" }, + { url = "https://files.pythonhosted.org/packages/53/fd/2e812bf938406d7db351f0703ddd3fc6c061cf30d96153a77bc79a943a44/watchfiles-1.2.0-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:20aa0e708b920bde876a4aa82dc7dd6ebea228a63a67cda6632c2fc87b787efa", size = 463537, upload-time = "2026-05-18T04:31:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/86/56/d17a7f1dd1bc3035f1072694a551301272f1739c2d8e319c927cb9e29b38/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:d413349d565dab74297f2a63e84a097936be69bf8f3b3801f27f380e32040f44", size = 629194, upload-time = "2026-05-18T04:31:14.141Z" }, + { url = "https://files.pythonhosted.org/packages/be/06/f1ff66bf5cae50aa4062779a0ecd0bbaf15e466195719074078947d9a17d/watchfiles-1.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:f28b2725eb8cce327b9b3ab02415c853011dc55c95832fe90de6bc56f5315f72", size = 656194, upload-time = "2026-05-18T04:31:47.14Z" }, + { url = "https://files.pythonhosted.org/packages/e7/54/a9c7ea9a82a4ac65e7004c0a03920b5cdd2f9c3b678757d9cd425aa51d53/watchfiles-1.2.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:b8c8358484d5fa12ef34f05b7f4168eaf1932f408725ff6d023c33ec17bd79d4", size = 400205, upload-time = "2026-05-18T04:32:05.153Z" }, + { url = "https://files.pythonhosted.org/packages/aa/5d/c9ab3534374a4a67450696905d6ef16a04405448b8dc52bd752ae50423d4/watchfiles-1.2.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f04b092229ad2c50126dd3c922c8822e51e605993764a33058d4a791ab42281", size = 392508, upload-time = "2026-05-18T04:30:54.849Z" }, + { url = "https://files.pythonhosted.org/packages/26/ca/1ad30103535cf0cecd7b993e8d50edc5351b1820e38f2d22e3df58962feb/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a7ce236284f002a156f70add88efe5c70879cccbb658be0822c54b1306fc09d", size = 452448, upload-time = "2026-05-18T04:30:53.727Z" }, + { url = "https://files.pythonhosted.org/packages/37/a1/ceee2cdf2afbd715fa07758d39c9859513eae411b23196f7fd039e5feedd/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b9909cc2b48468b575eefa944919e1fe8a36c5849d5c7c168f80a8c1db69398e", size = 459605, upload-time = "2026-05-18T04:30:23.312Z" }, + { url = "https://files.pythonhosted.org/packages/e8/f6/421e30fd1cb3907a84ed92ab3f1983e37ba2dca015e9a894a048418417a2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a37faaed405c67e28e6be45a1fa4f206ef5a2860f27c237db9fa30704c38242", size = 490757, upload-time = "2026-05-18T04:30:47.358Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/55ed1b97ed08be7bba6f9a541cac15f2a858e1d74d2b07b6da70a82aab00/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9649193aa27bd9ff2e80ff29bfaa93085496c7a3a377592823cc58b77ee88add", size = 568672, upload-time = "2026-05-18T04:30:38.915Z" }, + { url = "https://files.pythonhosted.org/packages/d1/cf/d8ae8a80dd7bafab395ea7681c10237311bbf34d37704a8c744e7cf31fc7/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4e4ff8e37f99cf1da89e255e07c9c4b37c214038c4283707bdec308cb1b0ea1f", size = 464197, upload-time = "2026-05-18T04:30:09.914Z" }, + { url = "https://files.pythonhosted.org/packages/7c/8a/3076c496ca8dafe0e8cd03fcebdfc47be4b1174b4e5b24ff6e396e6b3af2/watchfiles-1.2.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:054dc20fd2e3132b4c3883b4a00d72fd6e1f56fdaf89fccd12e8057d74cd74d7", size = 453181, upload-time = "2026-05-18T04:30:14.829Z" }, + { url = "https://files.pythonhosted.org/packages/e5/10/9745e17c98e7b8a86454df0a3c7b5686bd650383f1e9f26e4ebcbd6cc0c0/watchfiles-1.2.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e140ed30ebde76796b686e67c182cff10ea2fbab186fafd1560f74bb5a473a6e", size = 465109, upload-time = "2026-05-18T04:30:28.123Z" }, + { url = "https://files.pythonhosted.org/packages/8f/95/8ef4a95481d3e0cb52d62a06fa6e972e81424be2d9698b91a2fecca9904c/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:bb7e52ecf68ba46d22df23467b87cffeb2146908aa523ebfe803019618cfda06", size = 630653, upload-time = "2026-05-18T04:31:49.304Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e4/3b3bf36b0f829b50c6ebcb8d031583863c59f923d6a6af3d485e470d0fac/watchfiles-1.2.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:23282a321c8baf9b3a3c4afff673f9fe65eb7fdc2338d765ccad9d3d1916a5ba", size = 657838, upload-time = "2026-05-18T04:31:06.497Z" }, + { url = "https://files.pythonhosted.org/packages/21/b1/6cbbb50c1f3002ab568777d44aa21206dfb8807a840990c4037523b51812/watchfiles-1.2.0-cp314-cp314-win32.whl", hash = "sha256:c0db965c5f79aa49fe672d297cf1febc5ad149b658594944f49a54a2b96270a7", size = 275108, upload-time = "2026-05-18T04:30:06.891Z" }, + { url = "https://files.pythonhosted.org/packages/92/45/190ce6db8dcb4536682cf75d3889ff1a27182a58cb519d343cb6d9ea63d8/watchfiles-1.2.0-cp314-cp314-win_amd64.whl", hash = "sha256:71283b39fd17e5408eb123bd37aeecfd9d54c81fc184421943208aadb879d103", size = 288441, upload-time = "2026-05-18T04:32:12.901Z" }, + { url = "https://files.pythonhosted.org/packages/74/0d/3eae1c2313ab08378431d907c3f8095ecca00f3eda33111cf4f0f2591799/watchfiles-1.2.0-cp314-cp314-win_arm64.whl", hash = "sha256:c5c19526f4e54a00f2666a6c0e9e40d582c09e865055ea7378bf0009aab857b3", size = 280684, upload-time = "2026-05-18T04:31:26.902Z" }, + { url = "https://files.pythonhosted.org/packages/b1/75/fb64e6c25d6b5ca636d03df34ffb1c6e9873303e76d27967e045f8df088f/watchfiles-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:d73a585accffa5ae39c17264c36ec3166d2fad7000c780f5ef83b2722afb9dd2", size = 398857, upload-time = "2026-05-18T04:32:17.108Z" }, + { url = "https://files.pythonhosted.org/packages/73/4e/9f7adf01754cbf81843722ccfec169d8f26c69778281a302855cecd2ee08/watchfiles-1.2.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ae99b14c5f21e026e0e9d96f40e07d8570ebee6cafd9d8fc318354606daa7a28", size = 392413, upload-time = "2026-05-18T04:31:07.911Z" }, + { url = "https://files.pythonhosted.org/packages/47/c8/bec626bcc2d69f44b9acb24ce7d60ed7b16b73628eea747fcbd169d8edda/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4429f3b105524a10b72c3a819b091c495d2811d419c1e1e8df773a5a5974f831", size = 452409, upload-time = "2026-05-18T04:31:20.142Z" }, + { url = "https://files.pythonhosted.org/packages/00/b7/b6362068e81e7c556d155a34c35d40ac3ef42d747b06d7f6e5bf58e359c2/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:43d818978d06062d9b22c4fab2ebe44cf5213d42dc8e62bda8c2760cfa2eeb33", size = 458827, upload-time = "2026-05-18T04:32:06.219Z" }, + { url = "https://files.pythonhosted.org/packages/67/f8/9a813fa42afb1e0b4625e75f0479826644d3ee8dc287e093799bc01f390c/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9f732dc58b2dbe69e464ccf8fff7a03b0dd0be439da4c0720d3558527d3d6b4", size = 490104, upload-time = "2026-05-18T04:31:56.034Z" }, + { url = "https://files.pythonhosted.org/packages/2f/bf/27dfb6094ca4c9aad21298b5525b6c53cb36121ee454331d05161e58d130/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f200104103feb097de4cab8fe4f5dd18a2026934c7dea98c55a2f5fd6d5a33b", size = 571360, upload-time = "2026-05-18T04:31:57.133Z" }, + { url = "https://files.pythonhosted.org/packages/fb/39/44a096d67270ea93df91d33877dbe91fbda3aa4f8ec2edf799d93eda8736/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:63ac26eefbf4af1741247d6fb68b11c49a25b2f7413fbd318a83a12aaa9cf666", size = 464644, upload-time = "2026-05-18T04:30:57.33Z" }, + { url = "https://files.pythonhosted.org/packages/0e/80/c7472203bad6268e3ef1ad260739704847898938ad7ea8b63a5131f46b50/watchfiles-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c4997d4e4a55f0d02b6cde327322daf3a0400e5df6c6b15948994bf72497925", size = 454771, upload-time = "2026-05-18T04:30:48.736Z" }, + { url = "https://files.pythonhosted.org/packages/51/cf/3b10b268b4b7f0fc26e9debb5eef1998b515887840f444cd3ec80c688755/watchfiles-1.2.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:4c887eba18b7945ac73067a8b4a66f21cd46c2539b2bc68588f7be6c7eb6d26b", size = 463494, upload-time = "2026-05-18T04:31:33.826Z" }, + { url = "https://files.pythonhosted.org/packages/3d/3e/a4302545cd589262a0dc7d140e86f7688eba3f9c72776c27f7e23b8864c4/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:3416ff151bb6b5a8d8d11664974fbef4d9305b9b2957839ab5a270468fd8df30", size = 629383, upload-time = "2026-05-18T04:31:15.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/99/d5649df0a9a410d45b7c882304d0b790903ac9b6e8f2cfd12114e0c6b9f2/watchfiles-1.2.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:0e831a271c035d89789cffc386b6aa1375f39f1cd25eb7ca0997e4970d152fc5", size = 656093, upload-time = "2026-05-18T04:31:58.707Z" }, + { url = "https://files.pythonhosted.org/packages/92/b9/362702539275019a54dd2e94511b31a9b89c5f9e6a21966de7eb692549fc/watchfiles-1.2.0-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:37a6721cdf3f65dbb13aa9503510ccb4451603ac837e44d265d7992a597e1374", size = 400109, upload-time = "2026-05-18T04:31:16.879Z" }, + { url = "https://files.pythonhosted.org/packages/8f/75/71d5ba62db781e5587bded1d944c675374bc4aa37ff33d5018d98e8b6538/watchfiles-1.2.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2b37d10b5a63bd4d87e18472d80fa525bd670586fae62e5dd580452764879b65", size = 392167, upload-time = "2026-05-18T04:31:28.058Z" }, + { url = "https://files.pythonhosted.org/packages/3c/01/c66dd95d0423fe30d31820e2d1d5bda773764131bbb6ac0cb1cf303ac328/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a105bc2283f67e8fbec74253ec2d94925de92ed72c0393f1206bf326b7b7b69", size = 452372, upload-time = "2026-05-18T04:31:00.836Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/2fe99557e72f85627c6a8eed50d889e8d101623e060a22ad75b875cb932d/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5327989a465505f05cfe06f04fa9d0c2fd5432bb243e10e6f012b1bdca3c8579", size = 459596, upload-time = "2026-05-18T04:31:34.96Z" }, + { url = "https://files.pythonhosted.org/packages/ed/23/d4acfa0023367428ed48351b3b9b267893037b6cadae55620c61c24bcfd4/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecb47f183a8025b2aa18b546725c3657e542112ae9c0613a2af79b4fa8d04ad7", size = 490869, upload-time = "2026-05-18T04:31:59.923Z" }, + { url = "https://files.pythonhosted.org/packages/a4/5f/3164cbdce06c9fb95c4f7b9e2f9760b5e2797af43a9ecc317ef42a23a278/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8520a4ab0e37f770afc34459c4f8f7019e153f9124dc101c15538365875d1ab2", size = 571641, upload-time = "2026-05-18T04:32:00.948Z" }, + { url = "https://files.pythonhosted.org/packages/41/e6/85d3731c55e65cd7690f3f803d24c139588aaf863e4bf2148fe7a7fa1a19/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71cd71740ed2c15211ebb237ced4e39a1cdf6f80566e5fe95428da1626f4fde6", size = 464444, upload-time = "2026-05-18T04:30:34.298Z" }, + { url = "https://files.pythonhosted.org/packages/f4/7d/562641012b8b09872742c3b8adf9629ec479fd78f8d68ae4a0c13da8add6/watchfiles-1.2.0-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f88af53d6ddaf72179ef613ddc905e6f4785f712b49b80b3bef9f3525e6194b4", size = 453593, upload-time = "2026-05-18T04:31:23.464Z" }, + { url = "https://files.pythonhosted.org/packages/56/fe/cb8ef3d6f929d14158fdaaad9925985b7310abc9384dcd4d82dd0016fb59/watchfiles-1.2.0-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:cee9d5efd929efdac5f7e58f72b3376f676b64050a91c5b99a7094c5b2317488", size = 465096, upload-time = "2026-05-18T04:31:30.384Z" }, + { url = "https://files.pythonhosted.org/packages/25/91/80908e835e100527a9267147b08c0eee1fa6ab0ffec15edc04d1d44885f7/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:b718bf356bbc15e559bd8ef41782b573b8ae0e3f177ab244b440568d7ea02cfb", size = 630638, upload-time = "2026-05-18T04:30:49.89Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/95ab2f256bb4af3cb2eb23b9317bda984ee6e0f11733a5c004a6c95b06e3/watchfiles-1.2.0-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:922c0e019fe68b3ae392965a766b02a71ba1168c932cebc3733cd52c5fe5b377", size = 657684, upload-time = "2026-05-18T04:31:32.027Z" }, ] [[package]] @@ -972,88 +995,67 @@ wheels = [ [[package]] name = "yarl" -version = "1.23.0" +version = "1.24.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/12/1e8f37460ea0f7eb59c221fdaf0ed75e7ac43e97f8093b9c6f411df50a78/yarl-1.24.2.tar.gz", hash = "sha256:9ac374123c6fd7abf64d1fec93962b0bd4ee2c19751755a762a72dd96c0378f8", size = 210798, upload-time = "2026-05-19T21:31:05.599Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, - { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, - { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, - { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, - { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, - { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, - { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, - { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, - { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, - { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, - { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, - { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, - { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, - { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, - { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, - { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, - { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, - { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, - { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, - { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, - { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, - { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, - { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, - { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, - { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, - { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, - { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, - { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, - { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, - { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, - { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, - { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, - { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, - { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, - { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, - { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, - { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, - { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, - { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, - { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, - { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, - { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, - { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, - { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, - { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, - { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, - { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, - { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, - { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, - { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, - { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, - { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, - { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, - { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, - { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, - { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, - { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, - { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, - { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, - { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, - { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, - { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, - { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, - { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, - { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, - { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, - { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, - { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, - { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, - { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, - { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, + { url = "https://files.pythonhosted.org/packages/82/62/fcf0ce677f17e5c471c06311dd25964be38a4c586993632910d2e75278bc/yarl-1.24.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:491ac9141decf49ee8030199e1ee251cdff0e131f25678817ff6aa5f837a3536", size = 128978, upload-time = "2026-05-19T21:29:23.83Z" }, + { url = "https://files.pythonhosted.org/packages/d3/58/8e63299bb71ed61a834121d9d3fe6c9fcf2a6a5d09754ff4f20f2d20baf5/yarl-1.24.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e89418f65eda18f99030386305bd44d7d504e328a7945db1ead514fbe03a0607", size = 91733, upload-time = "2026-05-19T21:29:25.375Z" }, + { url = "https://files.pythonhosted.org/packages/c1/24/16748d5dab6daec8b0ed81ccec639a1cded0f18dcc62a4f696b4fe366c37/yarl-1.24.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cdfcce633b4a4bb8281913c57fcafd4b5933fbc19111a5e3930bbd299d6102f1", size = 91113, upload-time = "2026-05-19T21:29:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/1b/66/b63fff7b71211e866624b21432d5943cbb633eb0c2872d9ee3070648f22c/yarl-1.24.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:863297ddede92ee49024e9a9b11ecb59f310ca85b60d8537f56bed9bbb5b1986", size = 103899, upload-time = "2026-05-19T21:29:28.842Z" }, + { url = "https://files.pythonhosted.org/packages/9d/ac/ba1974b8533909636f7733fe86cf677e3619527c3c2fa913e0ea89c48757/yarl-1.24.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:374423f70754a2c96942ede36a29d37dc6b0cb8f92f8d009ddf3ed78d3da5488", size = 97862, upload-time = "2026-05-19T21:29:31.086Z" }, + { url = "https://files.pythonhosted.org/packages/1b/a5/123ac993b5c2ba6f554a140305620cb8f150fa543711bbc49be3ec0a65a4/yarl-1.24.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33a29b5d00ccbf3219bb3e351d7875739c19481e030779f48cc46a7a71681a9b", size = 111060, upload-time = "2026-05-19T21:29:32.657Z" }, + { url = "https://files.pythonhosted.org/packages/23/37/c472d3af3509688392134a88a825276770a187f1daa4de3f6dc0a327a751/yarl-1.24.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a9532c57211730c515341af11fef6e9b61d157487272a096d0c04da445642592", size = 110613, upload-time = "2026-05-19T21:29:34.379Z" }, + { url = "https://files.pythonhosted.org/packages/df/88/09c28dad91e662ccfaa1b78f1c57badde74fc9d0b23e74aef644750ecd73/yarl-1.24.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91e72cf093fd833483a97ee648e0c053c7c629f51ff4a0e7edd84f806b0c5617", size = 107012, upload-time = "2026-05-19T21:29:36.216Z" }, + { url = "https://files.pythonhosted.org/packages/07/ab/9d4f69d571a94f4d112fa7e2e007200f5a54d319f58c82ac7b7baa61f5c6/yarl-1.24.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:b3177bc0a768ef3bacceb4f272632990b7bea352f1b2f1eee9d6d6ff16516f92", size = 105887, upload-time = "2026-05-19T21:29:38.746Z" }, + { url = "https://files.pythonhosted.org/packages/8e/9a/000b2b66c0d772a499fc531d21dab92dfeb73b640a12eed6ba89f49bb2d0/yarl-1.24.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e196952aacaf3b232e265ff02980b64d483dc0972bd49bcb061171ff22ac203a", size = 103620, upload-time = "2026-05-19T21:29:40.368Z" }, + { url = "https://files.pythonhosted.org/packages/41/7c/7c1050f73450fbdaa3f0c72017059f00ce5e13366692f3dba25275a1083d/yarl-1.24.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:204e7a61ce99919c0de1bf904ab5d7aa188a129ea8f690a8f76cfb6e2844dc44", size = 100599, upload-time = "2026-05-19T21:29:42.66Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b1/29e5756b3926705f5f6089bd5b9f50a56eaac550da6e260bf713ead44d04/yarl-1.24.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b156914620f0b9d78dc1adb3751141daee561cfec796088abb89ed49d220f1a", size = 110604, upload-time = "2026-05-19T21:29:44.632Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4b/8415bc96e9b150cde942fbac9a8182985e58f40ce5c54c34ed015407d3ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:8372a2b976cf70654b2be6619ab6068acabb35f724c0fda7b277fbf53d66a5cf", size = 105161, upload-time = "2026-05-19T21:29:46.755Z" }, + { url = "https://files.pythonhosted.org/packages/8b/d4/cde059abfa229553b7298a2eadde2752e723d50aeedaef86ce59da2718ee/yarl-1.24.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f9a1e9b622ca284143aab5d885848686dcd85453bb1ca9abcdb7503e64dc0056", size = 110619, upload-time = "2026-05-19T21:29:48.972Z" }, + { url = "https://files.pythonhosted.org/packages/e7/2c/d6a6c9a61549f7b6c7e6dc6937d195bcf069582b47b7200dcd0e7b256acf/yarl-1.24.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:810e19b685c8c3c5862f6a38160a1f4e4c0916c9390024ec347b6157a45a0992", size = 107362, upload-time = "2026-05-19T21:29:51Z" }, + { url = "https://files.pythonhosted.org/packages/92/dd/3ae5fe417e9d1c353a548553326eb9935e76b6b727161563b424cc296df3/yarl-1.24.2-cp313-cp313-win_amd64.whl", hash = "sha256:7d37fb7c38f2b6edab0f845c4f85148d4c44204f52bc127021bd2bc9fdbf1656", size = 92667, upload-time = "2026-05-19T21:29:52.743Z" }, + { url = "https://files.pythonhosted.org/packages/10/cc/a7beb239f78f27fca1b053c8e8595e4179c02e62249b4687ec218c370c50/yarl-1.24.2-cp313-cp313-win_arm64.whl", hash = "sha256:1e831894be7c2954240e49791fa4b50c05a0dc881de2552cfe3ffd8631c7f461", size = 87069, upload-time = "2026-05-19T21:29:54.442Z" }, + { url = "https://files.pythonhosted.org/packages/40/0e/e08087695fc12789263821c5dc0f8dc52b5b17efd0887cacf419f8a43ba3/yarl-1.24.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f9312b3c02d9b3d23840f67952913c9c8721d7f1b7db305289faefa878f364c2", size = 129670, upload-time = "2026-05-19T21:29:56.631Z" }, + { url = "https://files.pythonhosted.org/packages/3a/98/ab4b5ed1b1b5cd973c8a3eb994c3a6aefb6ce6d399e21bb5f0316c33815c/yarl-1.24.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a4f4d6cd615823bfc7fb7e9b5987c3f41666371d870d51058f77e2680fbe9630", size = 91916, upload-time = "2026-05-19T21:29:58.645Z" }, + { url = "https://files.pythonhosted.org/packages/ba/b1/5297bb6a7df4782f7605bffc43b31f5044070935fbbcaa6c705a07e6ac65/yarl-1.24.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0c3063e5c0a8e8e62fae6c2596fa01da1561e4cd1da6fec5789f5cf99a8aefd8", size = 91625, upload-time = "2026-05-19T21:30:00.412Z" }, + { url = "https://files.pythonhosted.org/packages/02/a7/45baabfff76829264e623b185cff0c340d7e11bf3e1cd9ea37e7d17934bd/yarl-1.24.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fecd17873a096036c1c87ab3486f1aef7f269ada7f23f7f856f93b1cc7744f14", size = 104574, upload-time = "2026-05-19T21:30:02.544Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/3a5ab144d3d650ca37d4f4b57e56169be8af3ca34c448793e064b30baaed/yarl-1.24.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a46d1ab4ba4d32e6dc80daf8a28ce0bd83d08df52fbc32f3e288663427734535", size = 97534, upload-time = "2026-05-19T21:30:04.319Z" }, + { url = "https://files.pythonhosted.org/packages/9c/b5/5658fef3681fb5776b4513b052bec750009f47b3a592251c705d75375798/yarl-1.24.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73e68edf6dfd5f73f9ca127d84e2a6f9213c65bdffb736bda19524c0564fcd14", size = 111481, upload-time = "2026-05-19T21:30:05.988Z" }, + { url = "https://files.pythonhosted.org/packages/4c/06/fdcd7dde037f00866dce123ed4ba23dba94beb56fc4cf561668d27be37f2/yarl-1.24.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a296ca617f2d25fbceafb962b88750d627e5984e75732c712154d058ae8d79a3", size = 111529, upload-time = "2026-05-19T21:30:07.738Z" }, + { url = "https://files.pythonhosted.org/packages/c2/53/d81269aaafccea0d33396c03035de997b743f11e648e6e27a0df99c72980/yarl-1.24.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51b2cf5ec89a8b8470177641ed62a3ba22d74e1e898e06ad53aa77972487208", size = 107338, upload-time = "2026-05-19T21:30:09.713Z" }, + { url = "https://files.pythonhosted.org/packages/ae/04/23049463f729bd899df203a7960505a75333edd499cda8aa1d5a82b64df5/yarl-1.24.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:310fc687f7b2044ec54e372c8cbe923bb88f5c37bded0d3079e5791c2fc3cf50", size = 106147, upload-time = "2026-05-19T21:30:11.365Z" }, + { url = "https://files.pythonhosted.org/packages/14/18/04a4b5830b43ed5e4c5015b40e9f6241ad91487d71611061b4e111d6ac80/yarl-1.24.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:297a2fe352ecf858b30a98f87948746ec16f001d279f84aebdbd3bd965e2f1bd", size = 104272, upload-time = "2026-05-19T21:30:12.978Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f7/8cffdf319aee7a7c1dbd07b61d91c3e3fda460c7a93b5f93e445f3806c4c/yarl-1.24.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2a263e76b97bc42bdcd7c5f4953dec1f7cd62a1112fa7f869e57255229390d67", size = 99962, upload-time = "2026-05-19T21:30:15.001Z" }, + { url = "https://files.pythonhosted.org/packages/d7/39/b3cce3b7dbef64ac700ad4cea156a207d01bede0f507587616c364b5468e/yarl-1.24.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:822519b64cf0b474f1a0aaef1dc621438ea46bb77c94df97a5b4d213a7d8a8b1", size = 111063, upload-time = "2026-05-19T21:30:16.683Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ea/100818505e7ebf165c7242ff17fdf7d9fee79e27234aeca871c1082920d7/yarl-1.24.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b6067060d9dc594899ba83e6db6c48c68d1e494a6dab158156ed86977ca7bcb1", size = 105438, upload-time = "2026-05-19T21:30:18.769Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d2/e075a0b32aa6625087de9e653087df0759fed5de4a435fef594181102a77/yarl-1.24.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:0063adad533e57171b79db3943b229d40dfafeeee579767f96541f106bac5f1b", size = 111458, upload-time = "2026-05-19T21:30:21.024Z" }, + { url = "https://files.pythonhosted.org/packages/e6/5c/ceea7ba98b65c8eb8d947fdc52f9bedfcd43c6a57c9e3c90c17be8f324a3/yarl-1.24.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ee8e3fb34513e8dc082b586ef4910c98335d43a6fab688cd44d4851bacfce3e8", size = 107589, upload-time = "2026-05-19T21:30:23.412Z" }, + { url = "https://files.pythonhosted.org/packages/fa/d9/5582d57e2b2db9b85eb6663a22efdd78e08805f3f5389566e9fcad254d1b/yarl-1.24.2-cp314-cp314-win_amd64.whl", hash = "sha256:afb00d7fd8e0f285ca29a44cc50df2d622ff2f7a6d933fa641577b5f9d5f3db0", size = 94424, upload-time = "2026-05-19T21:30:25.425Z" }, + { url = "https://files.pythonhosted.org/packages/92/10/7dc07a0e22806a9280f42a57361395506e800c64e22737cd7b0886feab42/yarl-1.24.2-cp314-cp314-win_arm64.whl", hash = "sha256:68cf6eacd6028ef1142bc4b48376b81566385ca6f9e7dde3b0fa91be08ffcb57", size = 88690, upload-time = "2026-05-19T21:30:27.623Z" }, + { url = "https://files.pythonhosted.org/packages/9e/13/d5b8e2c8667db955bcb3de233f18798fefe7edf1d7429c2c9d4f9c401114/yarl-1.24.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:221ce1dd921ac4f603957f17d7c18c5cc0797fbb52f156941f92e04605d1d67b", size = 136248, upload-time = "2026-05-19T21:30:29.297Z" }, + { url = "https://files.pythonhosted.org/packages/de/46/a4a97c05c9c9b8fd266bb2a0df12992c7fbd02391eb9640583411b6dab32/yarl-1.24.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5f3224db28173a00d7afacdee07045cc4673dfab2b15492c7ae10deddbece761", size = 95084, upload-time = "2026-05-19T21:30:31.031Z" }, + { url = "https://files.pythonhosted.org/packages/95/b2/845cf2074a015e6fe0d0808cf1a2d9e868386c4220d657ebd8302b199043/yarl-1.24.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c557165320d6244ebe3a02431b2a201a20080e02f41f0cfa0ccc47a183765da8", size = 95272, upload-time = "2026-05-19T21:30:33.062Z" }, + { url = "https://files.pythonhosted.org/packages/fe/16/e69d4aa244aef45235ddfebc0e04036a6829842bc5a6a795aedc6c998d23/yarl-1.24.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:904065e6e85b1fa54d0d87438bd58c14c0bad97aad654ad1077fd9d87e8478ed", size = 101497, upload-time = "2026-05-19T21:30:34.842Z" }, + { url = "https://files.pythonhosted.org/packages/15/94/c07107715d621076863ee88b3ddf183fa5e9d4aba5769623c9979828410a/yarl-1.24.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8cec2a38d70edc10e0e856ceda886af5327a017ccbde8e1de1bd44d300357543", size = 94002, upload-time = "2026-05-19T21:30:37.724Z" }, + { url = "https://files.pythonhosted.org/packages/a9/35/fc1bbdd895b5e4010b8fdd037f7ed3aa289d3863e08231b30231ca9a0815/yarl-1.24.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e7484b9361ed222ee1ca5b4337aa4cbdcc4618ce5aff57d9ef1582fd95893fc0", size = 106524, upload-time = "2026-05-19T21:30:40.196Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/32b66d0a4ba47c296cf86d03e2c67bff58399fe6d6d84d5205c04c66cc6d/yarl-1.24.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:84f9670b89f34db07f81e53aee83e0b938a3412329d51c8f922488be7fcc4024", size = 106165, upload-time = "2026-05-19T21:30:41.888Z" }, + { url = "https://files.pythonhosted.org/packages/95/47/37cb5ff50c5e825d4d38e81bb04d1b7e96bf960f7ab89f9850b162f3f114/yarl-1.24.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:abb2759733d63a28b4956500a5dd57140f26486c92b2caedfb964ab7d9b79dbf", size = 103010, upload-time = "2026-05-19T21:30:43.985Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d2/4597912315096f7bb359e46e13bf8b60994fcbb2db29b804c0902ef4eff5/yarl-1.24.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:081c2bf54efe03774d0311172bc04fedf9ca01e644d4cd8c805688e527209bdc", size = 101128, upload-time = "2026-05-19T21:30:46.291Z" }, + { url = "https://files.pythonhosted.org/packages/b9/d5/c8e86e120521e646013d02a8e3b8884392e28494be8f392366e50d208efc/yarl-1.24.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:86746bef442aa479107fe28132e1277237f9c24c2f00b0b0cf22b3ee0904f2bb", size = 101382, upload-time = "2026-05-19T21:30:48.085Z" }, + { url = "https://files.pythonhosted.org/packages/fa/98/70b229236118f89dbeb739b76f10225bbf53b5497725502594c9a01d699a/yarl-1.24.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:2d07d21d0bc4b17558e8de0b02fbfdf1e347d3bb3699edd00bb92e7c57925420", size = 95964, upload-time = "2026-05-19T21:30:49.785Z" }, + { url = "https://files.pythonhosted.org/packages/87/f8/56c386981e3c8648d279fdef2397ffec577e8320fd5649745e34d54faeb7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:4fb1ac3fc5fecd8ae7453ea237e4d22b49befa70266dfe1629924245c21a0c7f", size = 106204, upload-time = "2026-05-19T21:30:51.862Z" }, + { url = "https://files.pythonhosted.org/packages/1a/1e/765afe97811ca35933e2a7de70ac57b1997ea2e4ee895719ee7a231fb7e5/yarl-1.24.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4da31a5512ed1729ca8d8aacde3f7faeb8843cde3165d6bcf7f88f74f17bb8aa", size = 101510, upload-time = "2026-05-19T21:30:53.62Z" }, + { url = "https://files.pythonhosted.org/packages/ee/78/393913f4b9039e1edd09ae8a9bbb9d539be909a8abf6d8a2084585bed4b7/yarl-1.24.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:533ded4dceb5f1f3da7906244f4e82cf46cfd40d84c69a1faf5ac506aa65ecbe", size = 105584, upload-time = "2026-05-19T21:30:55.962Z" }, + { url = "https://files.pythonhosted.org/packages/78/87/deb17b7049bbe74ea11a713b86f8f27800cc1c8648b0b797243ebb4830ba/yarl-1.24.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7b3a85525f6e7eeabcfdd372862b21ee1915db1b498a04e8bf0e389b607ff0bd", size = 103410, upload-time = "2026-05-19T21:30:57.962Z" }, + { url = "https://files.pythonhosted.org/packages/8f/be/f9f7594e23b5b93affff0318e4593c1920331bcaefda326cabcad94296a1/yarl-1.24.2-cp314-cp314t-win_amd64.whl", hash = "sha256:a7624b1ca46ca5d7b864ef0d2f8efe3091454085ee1855b4e992314529972215", size = 102980, upload-time = "2026-05-19T21:30:59.735Z" }, + { url = "https://files.pythonhosted.org/packages/65/a4/ba80dccd3593ff1f01051a818694d07b58cb8232677ee9a22a5a1f93a9fc/yarl-1.24.2-cp314-cp314t-win_arm64.whl", hash = "sha256:e434a45ce2e7a947f951fc5a8944c8cc080b7e59f9c50ae80fd39107cf88126d", size = 91219, upload-time = "2026-05-19T21:31:01.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/4d/4b880086bd0d3e034d25647be1d830afc3e3f610e98c4ab3490af6b1b6d5/yarl-1.24.2-py3-none-any.whl", hash = "sha256:2783d9226db8797636cd6896e4de81feed252d1db72265686c9558d97a4d94b9", size = 53576, upload-time = "2026-05-19T21:31:03.909Z" }, ] [[package]] From 56c0ad3b5fdc3a871622c17daf49afb835f53e32 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 29 May 2026 13:23:25 +0300 Subject: [PATCH 45/88] fix catch --- ui/src/app/app.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index 089b78f..d539070 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -1250,12 +1250,13 @@ export class App implements AfterViewInit, OnInit, OnDestroy { return; } await navigator.share(payload); - } catch (err: any) { + } catch (err) { + const e = err as { name?: string; message?: string }; // AbortError = user dismissed the share sheet β†’ silent no-op. - if (err?.name === 'AbortError') return; + if (e.name === 'AbortError') return; console.error('Share failed:', err); window.alert( - `Share failed: ${err?.message || 'unknown error'}. ` + + `Share failed: ${e.message || 'unknown error'}. ` + `Please use the download button instead.` ); } From cf2d2dd46582462e23f389bbe209c1eb3bc95a2e Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 29 May 2026 14:13:47 +0300 Subject: [PATCH 46/88] review fixes --- ui/src/app/app.html | 25 ++++++++++++++++++------- ui/src/app/app.ts | 26 -------------------------- 2 files changed, 18 insertions(+), 33 deletions(-) diff --git a/ui/src/app/app.html b/ui/src/app/app.html index 7081047..fb87050 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -384,7 +384,6 @@ (click)="folderClick$.next($any($event.target).value)" #folderTypeahead="ngbTypeahead" [disabled]="addInProgress || subscribeInProgress || downloads.loading"> -
@@ -450,12 +449,17 @@
-
-
- +
+ Auto Start +
@@ -500,7 +504,14 @@ - +
diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index d145db7..a10ff6e 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -104,7 +104,6 @@ export class App implements AfterViewInit, OnInit, OnDestroy { cookieUploadInProgress = false; themes: Theme[] = Themes; activeTheme: Theme | undefined; - customDirs$!: Observable; readonly folderTypeahead = viewChild('folderTypeahead'); folderFocus$ = new Subject(); folderClick$ = new Subject(); @@ -310,7 +309,6 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.getConfiguration(); this.getYtdlOptionsUpdateTime(); this.getYtdlOptionPresets(); - this.customDirs$ = this.getMatchingCustomDir(); this.setTheme(this.activeTheme!); this.colorSchemeMediaQuery.addEventListener('change', this.onColorSchemeChanged); @@ -377,13 +375,6 @@ export class App implements AfterViewInit, OnInit, OnDestroy { return this.downloads.configuration['ALLOW_YTDL_OPTIONS_OVERRIDES'] === true; } - allowCustomDir(tag: string) { - if (this.downloads.configuration['CREATE_CUSTOM_DIRS']) { - return tag; - } - return false; - } - searchFolder: OperatorFunction = (text$: Observable) => { const debouncedText$ = text$.pipe(debounceTime(150), distinctUntilChanged()); const clicksWithClosedPopup$ = this.folderClick$.pipe( @@ -404,23 +395,6 @@ export class App implements AfterViewInit, OnInit, OnDestroy { return this.downloadType === 'audio'; } - getMatchingCustomDir() : Observable { - return this.downloads.customDirsChanged.asObservable().pipe( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - map((output: any) => { - // Keep logic consistent with app/ytdl.py - if (this.isAudioType()) { - console.debug("Showing audio-specific download directories"); - return output["audio_download_dir"]; - } else { - console.debug("Showing default download directories"); - return output["download_dir"]; - } - }), - distinctUntilChanged((prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)) - ); - } - getYtdlOptionsUpdateTime() { this.downloads.ytdlOptionsChanged.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({ // eslint-disable-next-line @typescript-eslint/no-explicit-any From baa72c0e9467bd31a5097dd4809acb53fc7c05d4 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 29 May 2026 14:36:05 +0300 Subject: [PATCH 47/88] fix pnpm upgrade to the correct package age limit --- ui/package.json | 20 +-- ui/pnpm-lock.yaml | 304 +++++++++++++++++++++++----------------------- 2 files changed, 162 insertions(+), 162 deletions(-) diff --git a/ui/package.json b/ui/package.json index af3305b..e827e85 100644 --- a/ui/package.json +++ b/ui/package.json @@ -23,14 +23,14 @@ }, "private": true, "dependencies": { - "@angular/animations": "^21.2.15", - "@angular/common": "^21.2.15", - "@angular/compiler": "^21.2.15", - "@angular/core": "^21.2.15", - "@angular/forms": "^21.2.15", - "@angular/platform-browser": "^21.2.15", - "@angular/platform-browser-dynamic": "^21.2.15", - "@angular/service-worker": "^21.2.15", + "@angular/animations": "^21.2.14", + "@angular/common": "^21.2.14", + "@angular/compiler": "^21.2.14", + "@angular/core": "^21.2.14", + "@angular/forms": "^21.2.14", + "@angular/platform-browser": "^21.2.14", + "@angular/platform-browser-dynamic": "^21.2.14", + "@angular/service-worker": "^21.2.14", "@fortawesome/angular-fontawesome": "~4.0.0", "@fortawesome/fontawesome-svg-core": "^7.2.0", "@fortawesome/free-brands-svg-icons": "^7.2.0", @@ -50,8 +50,8 @@ "@angular-eslint/builder": "21.1.0", "@angular/build": "^21.2.13", "@angular/cli": "^21.2.13", - "@angular/compiler-cli": "^21.2.15", - "@angular/localize": "^21.2.15", + "@angular/compiler-cli": "^21.2.14", + "@angular/localize": "^21.2.14", "@eslint/js": "^9.39.4", "angular-eslint": "21.1.0", "eslint": "^9.39.4", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 9c0d5c8..1202bd9 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -9,32 +9,32 @@ importers: .: dependencies: '@angular/animations': - specifier: ^21.2.15 - version: 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^21.2.14 + version: 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/common': - specifier: ^21.2.15 - version: 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^21.2.14 + version: 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@angular/compiler': - specifier: ^21.2.15 - version: 21.2.15 + specifier: ^21.2.14 + version: 21.2.14 '@angular/core': - specifier: ^21.2.15 - version: 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^21.2.14 + version: 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) '@angular/forms': - specifier: ^21.2.15 - version: 21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + specifier: ^21.2.14 + version: 21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) '@angular/platform-browser': - specifier: ^21.2.15 - version: 21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^21.2.14 + version: 21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/platform-browser-dynamic': - specifier: ^21.2.15 - version: 21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.15)(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))) + specifier: ^21.2.14 + version: 21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.14)(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))) '@angular/service-worker': - specifier: ^21.2.15 - version: 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^21.2.14 + version: 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@fortawesome/angular-fontawesome': specifier: ~4.0.0 - version: 4.0.0(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) + version: 4.0.0(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) '@fortawesome/fontawesome-svg-core': specifier: ^7.2.0 version: 7.2.0 @@ -49,10 +49,10 @@ importers: version: 7.2.0 '@ng-bootstrap/ng-bootstrap': specifier: ^20.0.0 - version: 20.0.0(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15))(@popperjs/core@2.11.8)(rxjs@7.8.2) + version: 20.0.0(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14))(@popperjs/core@2.11.8)(rxjs@7.8.2) '@ng-select/ng-select': specifier: ^21.8.2 - version: 21.8.2(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) + version: 21.8.2(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) '@popperjs/core': specifier: ^2.11.8 version: 2.11.8 @@ -61,10 +61,10 @@ importers: version: 5.3.8(@popperjs/core@2.11.8) ngx-cookie-service: specifier: ^21.3.1 - version: 21.3.1(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) + version: 21.3.1(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) ngx-socket-io: specifier: ~4.10.0 - version: 4.10.0(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + version: 4.10.0(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) rxjs: specifier: ~7.8.2 version: 7.8.2 @@ -80,16 +80,16 @@ importers: version: 21.1.0(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular/build': specifier: ^21.2.13 - version: 21.2.13(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15)(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.9.1)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3))) + version: 21.2.13(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14)(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3))) '@angular/cli': specifier: ^21.2.13 version: 21.2.13(@types/node@25.9.1)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^21.2.15 - version: 21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3) + specifier: ^21.2.14 + version: 21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3) '@angular/localize': - specifier: ^21.2.15 - version: 21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15) + specifier: ^21.2.14 + version: 21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14) '@eslint/js': specifier: ^9.39.4 version: 9.39.4 @@ -239,11 +239,11 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '*' - '@angular/animations@21.2.15': - resolution: {integrity: sha512-Z8AsLTwc++Fcu0fJnclAF9zMfumAd5KXrwtSdyECqLpqd+lEmmsOpeOl6P7loqdDz99KYh/8UF4eJxdMvnsaKw==} + '@angular/animations@21.2.14': + resolution: {integrity: sha512-9WLnsJE0xqtd1rVtHMvsAUxFy3OdPks4bdmUIqyw23X/je7ytUALAGWNadffcZBwRpa1A6TUnLr9X4+Draz3kw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.2.15 + '@angular/core': 21.2.14 '@angular/build@21.2.13': resolution: {integrity: sha512-Y9TDAaTQ+E5LScCKA/hPZmns/7Mpu6J2BiPj2cETA1xNjvgRpeb5Mh32KuhZb20NSFLvjpdnLuBTTtbym7hevw==} @@ -296,33 +296,33 @@ packages: 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'} hasBin: true - '@angular/common@21.2.15': - resolution: {integrity: sha512-PHbICQe4YCXnax2FcmKUpiffs8XPW9A0KlZF35qgJoQyBMBZx5F8c8geCh25jxtq77n3eBTmOa/WIAdSqiitkQ==} + '@angular/common@21.2.14': + resolution: {integrity: sha512-J6K7cE7uKOKmg4+sxLeGfsmaYDjP5l1XCiMMI0WPT0t68uxLk8g3MzV5Trqfb6ZnRxWcfp9c4c+XxAvMBB7ymA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.2.15 + '@angular/core': 21.2.14 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@21.2.15': - resolution: {integrity: sha512-/MU7OA9d/e9P5SthR+N6JJObBmzcGsgNQaeQ2YfSUnU0lCRVQweTWwxLFDbfU6UX8MZFWB6pdI57zod8r5kXUw==} + '@angular/compiler-cli@21.2.14': + resolution: {integrity: sha512-h+WQfPKFxaDfDhMqUUdOQ1TsDMccav8kLFERmKTRfD4MNOczSMpOMyeXJHCL0Rq4I8WDQvaBJGMG7DXRDefSog==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.15 + '@angular/compiler': 21.2.14 typescript: '>=5.9 <6.1' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@21.2.15': - resolution: {integrity: sha512-nwpNb+NbVUNzR3cck0QXbU/oFK7BpmXOXVnN/w7+P4+TsFUYeTtO1Ojbc15jkqe6mSM0lBvGlcoztVblHQkqcw==} + '@angular/compiler@21.2.14': + resolution: {integrity: sha512-8mqgwRYfn2Z1vg/5YVt60dDBattnZL45nNJd2vTMwAiDTzhWhgKgRWKOeVL0aj2JqHeHiwuIlrLnz46acJMulQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - '@angular/core@21.2.15': - resolution: {integrity: sha512-J5JsUnNtQURdeA7EA3DoCsMBizW3l01gfqM326Al72Ou3woFWmRb5P3LOXpIOzAeMQhO6Z5tW+B1t+4qmoq7uw==} + '@angular/core@21.2.14': + resolution: {integrity: sha512-Z1Ivjh7L2lT//8LA7vQ3tj7Rg6wl2XRA5kPSAukgn8u0Yu0XxG8NE8KG0Eypb3v9CEcbwATwpgnxzbJFZ8TFcw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/compiler': 21.2.15 + '@angular/compiler': 21.2.14 rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 || ~0.16.0 peerDependenciesMeta: @@ -331,49 +331,49 @@ packages: zone.js: optional: true - '@angular/forms@21.2.15': - resolution: {integrity: sha512-swGUHgbBrPNvODPR9qBP6+vT2EHiyW361iEgS3HpTmvDhF/kD4l8NE0vh3P5N0DnEtGh4umOCKfQ1w6hPJ7lqA==} + '@angular/forms@21.2.14': + resolution: {integrity: sha512-HQYIybyMt0CrI31rW6vXbiDsSM2DDtTcOVeT/nWDRNCoqBrREDg8rVsm2Y+fUMsiQVJNa6dCXPwvYhjzJ4r7ug==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.2.15 - '@angular/core': 21.2.15 - '@angular/platform-browser': 21.2.15 + '@angular/common': 21.2.14 + '@angular/core': 21.2.14 + '@angular/platform-browser': 21.2.14 rxjs: ^6.5.3 || ^7.4.0 - '@angular/localize@21.2.15': - resolution: {integrity: sha512-zZWw3zidZDURx8iTXR8W5XqE8t6adWAVyE/R0954l6p1JdC5RJuBCs9XxHL8yZEjWZye7inpI/ypJ1+vNHCycg==} + '@angular/localize@21.2.14': + resolution: {integrity: sha512-cVqQag+O8mIuH5Dz8Lw4uElWtFogr+xgUFbakI3QQAbnLaqeQaDqHvXE8VGkTmhGvEMnE0cvPn+rOQ/N5exzbw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.15 - '@angular/compiler-cli': 21.2.15 + '@angular/compiler': 21.2.14 + '@angular/compiler-cli': 21.2.14 - '@angular/platform-browser-dynamic@21.2.15': - resolution: {integrity: sha512-3xvlWLZlsWjPyJFGatOOsod/f5AFjmSUDoOXo0zsr2ckHc4TxbDTnkLULhRSWv6m68fKOdQb8Si8rI15gC5yqA==} + '@angular/platform-browser-dynamic@21.2.14': + resolution: {integrity: sha512-m5U4zX8JFnxTAIGpsBXIAyefSmYqdORY/OfHC0aMmZovuFCbXXIYqYRQDBB7+YVNpSDSHllCrKEZFu/CC6dq3g==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.2.15 - '@angular/compiler': 21.2.15 - '@angular/core': 21.2.15 - '@angular/platform-browser': 21.2.15 + '@angular/common': 21.2.14 + '@angular/compiler': 21.2.14 + '@angular/core': 21.2.14 + '@angular/platform-browser': 21.2.14 - '@angular/platform-browser@21.2.15': - resolution: {integrity: sha512-O4ZHVV/rxkK1AuiD9M3UssL/HkoQvBcZy2+U421IMNibclGhwH9aRwc/0ZlQ7zpseS9+KPZ23FebvN4/92IbPg==} + '@angular/platform-browser@21.2.14': + resolution: {integrity: sha512-34tBwxh86yN2YifBDhCesm6N+nn9WcbuXjRwfo0mTme15OZ/zt56yw7v1mcK3UFLegIIALtsIgpXXrPWWQoKkA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/animations': 21.2.15 - '@angular/common': 21.2.15 - '@angular/core': 21.2.15 + '@angular/animations': 21.2.14 + '@angular/common': 21.2.14 + '@angular/core': 21.2.14 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/service-worker@21.2.15': - resolution: {integrity: sha512-fnt5gFip6ZIz5D4yOipCfGTafTsg8fqbRIumCelfHFmPy1RtdK9Ij3ogos+yS1WpkNDWmP+OdLqC2JB+0LfSeQ==} + '@angular/service-worker@21.2.14': + resolution: {integrity: sha512-60DMV71QcaRtj3HwmV8Oz5zOKKudEyWourpsfpFptzF4hQ+7T7I4xSla1J0q/7b16Kwf44Ey90vCPlUrsMLVrA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/core': 21.2.15 + '@angular/core': 21.2.14 rxjs: ^6.5.3 || ^7.4.0 '@asamuzakjp/css-color@4.1.2': @@ -496,14 +496,14 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} - '@emnapi/core@1.9.1': - resolution: {integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==} + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - '@emnapi/runtime@1.9.1': - resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - '@emnapi/wasi-threads@1.2.0': - resolution: {integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==} + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} '@esbuild/aix-ppc64@0.27.3': resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} @@ -2055,8 +2055,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.364: - resolution: {integrity: sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==} + electron-to-chromium@1.5.363: + resolution: {integrity: sha512-VjUKPyWzGnT1fujlkEGC/BvN70Hh70KXtAqcmniXviYlJC/ivcT+BWGPyxWVbJZLfvtKR6dqg1L7T7pgAMBtWA==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2350,8 +2350,8 @@ packages: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} - hasown@2.0.4: - resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} engines: {node: '>= 0.4'} hono@4.12.23: @@ -2400,8 +2400,8 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - immutable@5.1.6: - resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==} + immutable@5.1.5: + resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -3160,8 +3160,8 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.2.3: - resolution: {integrity: sha512-g62dB+w1/OEFnPvmX0yd/HnetYITOL+1nJW7kitOycOeAvmbWC/nu0fwmmQ/kupNojqExzyC/T++pST/jRJ2mQ==} + tinyexec@1.2.2: + resolution: {integrity: sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==} engines: {node: '>=18'} tinyglobby@0.2.15: @@ -3176,8 +3176,8 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tldts-core@7.4.1: - resolution: {integrity: sha512-sc2nGvGbixlJRHwTh/qQdPXTxJU1UDJboGPQm4d/01YUJ9r/u6aeIulQvEaxUlvKDN7hb1qCLjax+jhVAPLa/g==} + tldts-core@7.4.0: + resolution: {integrity: sha512-/mb9kRld+x1sIMXxWNOAp5m6C+D4GrAORWlJkOJ5dElvxdN1eutz/o7qHLp9gFvDF4Y3/L2xeScoxz6AbEo8rQ==} tldts@7.4.0: resolution: {integrity: sha512-yHBe+zVfzNZ3QfTPW/Z6KK1G2t340gFjMHqI/4KKSt/abzYydzuCnpqdaF5gCCABby+9Yfbj59oR5F2Fd5CBzg==} @@ -3668,17 +3668,17 @@ snapshots: eslint: 9.39.4 typescript: 5.9.3 - '@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - '@angular/build@21.2.13(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15)(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)(@types/node@25.9.1)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)))': + '@angular/build@21.2.13(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14)(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)))': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2102.13(chokidar@5.0.0) - '@angular/compiler': 21.2.15 - '@angular/compiler-cli': 21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3) + '@angular/compiler': 21.2.14 + '@angular/compiler-cli': 21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3) '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 @@ -3696,7 +3696,7 @@ snapshots: parse5-html-rewriting-stream: 8.0.0 picomatch: 4.0.4 piscina: 5.1.4 - rolldown: 1.0.0-rc.4(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1) + rolldown: 1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) sass: 1.97.3 semver: 7.7.4 source-map-support: 0.5.21 @@ -3707,10 +3707,10 @@ snapshots: vite: 7.3.2(@types/node@25.9.1)(sass@1.97.3) watchpack: 2.5.1 optionalDependencies: - '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/localize': 21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15) - '@angular/platform-browser': 21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/localize': 21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14) + '@angular/platform-browser': 21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/service-worker': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) lmdb: 3.5.1 postcss: 8.5.15 vitest: 4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)) @@ -3755,15 +3755,15 @@ snapshots: - chokidar - supports-color - '@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3)': + '@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3)': dependencies: - '@angular/compiler': 21.2.15 + '@angular/compiler': 21.2.14 '@babel/core': 7.29.0 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 5.0.0 @@ -3777,31 +3777,31 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/compiler@21.2.15': + '@angular/compiler@21.2.14': dependencies: tslib: 2.8.1 - '@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)': + '@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 21.2.15 + '@angular/compiler': 21.2.14 zone.js: 0.15.0 - '@angular/forms@21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': + '@angular/forms@21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) '@standard-schema/spec': 1.1.0 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/localize@21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15)': + '@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14)': dependencies: - '@angular/compiler': 21.2.15 - '@angular/compiler-cli': 21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3) + '@angular/compiler': 21.2.14 + '@angular/compiler-cli': 21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3) '@babel/core': 7.29.0 '@types/babel__core': 7.20.5 tinyglobby: 0.2.16 @@ -3809,25 +3809,25 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/platform-browser-dynamic@21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.15)(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))': + '@angular/platform-browser-dynamic@21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.14)(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))': dependencies: - '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/compiler': 21.2.15 - '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/compiler': 21.2.14 + '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) tslib: 2.8.1 - '@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 optionalDependencies: - '@angular/animations': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/animations': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/service-worker@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 @@ -3981,18 +3981,18 @@ snapshots: '@csstools/css-tokenizer@4.0.0': {} - '@emnapi/core@1.9.1': + '@emnapi/core@1.10.0': dependencies: - '@emnapi/wasi-threads': 1.2.0 + '@emnapi/wasi-threads': 1.2.1 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.9.1': + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.2.0': + '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 optional: true @@ -4123,9 +4123,9 @@ snapshots: '@exodus/bytes@1.15.1': {} - '@fortawesome/angular-fontawesome@4.0.0(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))': + '@fortawesome/angular-fontawesome@4.0.0(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) '@fortawesome/fontawesome-svg-core': 7.2.0 tslib: 2.8.1 @@ -4463,28 +4463,28 @@ snapshots: '@napi-rs/nice-win32-x64-msvc': 1.1.1 optional: true - '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)': + '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: - '@emnapi/core': 1.9.1 - '@emnapi/runtime': 1.9.1 + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 '@tybys/wasm-util': 0.10.2 optional: true - '@ng-bootstrap/ng-bootstrap@20.0.0(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15))(@popperjs/core@2.11.8)(rxjs@7.8.2)': + '@ng-bootstrap/ng-bootstrap@20.0.0(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14))(@popperjs/core@2.11.8)(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) - '@angular/localize': 21.2.15(@angular/compiler-cli@21.2.15(@angular/compiler@21.2.15)(typescript@5.9.3))(@angular/compiler@21.2.15) + '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/localize': 21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14) '@popperjs/core': 2.11.8 rxjs: 7.8.2 tslib: 2.8.1 - '@ng-select/ng-select@21.8.2(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': + '@ng-select/ng-select@21.8.2(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': dependencies: - '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 21.2.15(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.15(@angular/animations@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) tslib: 2.8.1 '@nodelib/fs.scandir@2.1.5': @@ -4650,9 +4650,9 @@ snapshots: '@rolldown/binding-openharmony-arm64@1.0.0-rc.4': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.4(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1)': + '@rolldown/binding-wasm32-wasi@1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1) + '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -5208,7 +5208,7 @@ snapshots: dependencies: baseline-browser-mapping: 2.10.32 caniuse-lite: 1.0.30001793 - electron-to-chromium: 1.5.364 + electron-to-chromium: 1.5.363 node-releases: 2.0.46 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -5386,7 +5386,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.364: {} + electron-to-chromium@1.5.363: {} emoji-regex@10.6.0: {} @@ -5699,7 +5699,7 @@ snapshots: get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.4 + hasown: 2.0.3 math-intrinsics: 1.1.0 get-proto@1.0.1: @@ -5735,7 +5735,7 @@ snapshots: has-symbols@1.1.0: {} - hasown@2.0.4: + hasown@2.0.3: dependencies: function-bind: 1.1.2 @@ -5794,7 +5794,7 @@ snapshots: ignore@7.0.5: {} - immutable@5.1.6: {} + immutable@5.1.5: {} import-fresh@3.3.1: dependencies: @@ -6093,16 +6093,16 @@ snapshots: negotiator@1.0.0: {} - ngx-cookie-service@21.3.1(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0)): + ngx-cookie-service@21.3.1(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)): dependencies: - '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - ngx-socket-io@4.10.0(@angular/common@21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): + ngx-socket-io@4.10.0(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): dependencies: - '@angular/common': 21.2.15(@angular/core@21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.15(@angular/compiler@21.2.15)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) core-js: 3.49.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 @@ -6372,7 +6372,7 @@ snapshots: rfdc@1.4.1: {} - rolldown@1.0.0-rc.4(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1): + rolldown@1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): dependencies: '@oxc-project/types': 0.113.0 '@rolldown/pluginutils': 1.0.0-rc.4 @@ -6387,7 +6387,7 @@ snapshots: '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.4 '@rolldown/binding-linux-x64-musl': 1.0.0-rc.4 '@rolldown/binding-openharmony-arm64': 1.0.0-rc.4 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.4(@emnapi/core@1.9.1)(@emnapi/runtime@1.9.1) + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.4 '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.4 transitivePeerDependencies: @@ -6448,7 +6448,7 @@ snapshots: sass@1.97.3: dependencies: chokidar: 4.0.3 - immutable: 5.1.6 + immutable: 5.1.5 source-map-js: 1.2.1 optionalDependencies: '@parcel/watcher': 2.5.6 @@ -6682,7 +6682,7 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.2.3: {} + tinyexec@1.2.2: {} tinyglobby@0.2.15: dependencies: @@ -6696,11 +6696,11 @@ snapshots: tinyrainbow@3.1.0: {} - tldts-core@7.4.1: {} + tldts-core@7.4.0: {} tldts@7.4.0: dependencies: - tldts-core: 7.4.1 + tldts-core: 7.4.0 to-regex-range@5.0.1: dependencies: @@ -6805,7 +6805,7 @@ snapshots: picomatch: 4.0.4 std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 1.2.3 + tinyexec: 1.2.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 vite: 7.3.2(@types/node@25.9.1)(sass@1.97.3) From 897d52cd0d979bde2734117c484efaa9297bbed8 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Sat, 30 May 2026 15:35:52 +0300 Subject: [PATCH 48/88] styling improvements --- ui/src/app/app.html | 2 +- ui/src/app/app.sass | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/ui/src/app/app.html b/ui/src/app/app.html index 0b6f7fb..f9699fd 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -926,7 +926,7 @@ Name URL - Sub. title filter + Filter Interval (min) Last checked Status diff --git a/ui/src/app/app.sass b/ui/src/app/app.sass index c84d122..da92501 100644 --- a/ui/src/app/app.sass +++ b/ui/src/app/app.sass @@ -202,8 +202,6 @@ main margin-bottom: 0.4rem .help-title - text-decoration: underline dotted - text-underline-offset: 0.2em cursor: help &:focus From ee2051241010baa06b8364b78b5bc577890c20e8 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Sat, 6 Jun 2026 09:42:26 +0300 Subject: [PATCH 49/88] add option for following nightly yt-dlp releases (closes #999) --- README.md | 1 + app/main.py | 76 ++++++++++++++++++++++++++++++-- app/tests/test_config.py | 16 +++++++ app/tests/test_nightly_update.py | 29 ++++++++++++ docker-entrypoint.sh | 53 +++++++++++++++++++++- ui/src/app/app.ts | 5 +++ 6 files changed, 174 insertions(+), 6 deletions(-) create mode 100644 app/tests/test_nightly_update.py diff --git a/README.md b/README.md index d6f80aa..5d5a25f 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ Certain values can be set via environment variables, using the `-e` parameter on * __YTDL_OPTIONS_PRESETS__: Named bundles of yt-dlp options, selectable per download in the UI. See [Configuring yt-dlp options](#%EF%B8%8F-configuring-yt-dlp-options) for format and examples. * __YTDL_OPTIONS_PRESETS_FILE__: Path to a JSON file containing presets. Monitored and reloaded automatically on changes. See [Configuring yt-dlp options](#%EF%B8%8F-configuring-yt-dlp-options). * __ALLOW_YTDL_OPTIONS_OVERRIDES__: Whether to show a free-text field in the UI for per-download yt-dlp option overrides. Defaults to `false`. See [Configuring yt-dlp options](#%EF%B8%8F-configuring-yt-dlp-options) for details and security considerations. +* __YTDL_NIGHTLY_UPDATE_TIME__: If set, will cause MeTube to use [nightly yt-dlp builds](https://github.com/yt-dlp/yt-dlp-nightly-builds) instead of the stable releases. Set to the time (`HH:MM`, 24-hour) when you want the daily upgrades and MeTube restart to happen. Defaults to empty (disabled). ### 🌐 Web Server & URLs diff --git a/app/main.py b/app/main.py index 61d7e6e..f5154ca 100644 --- a/app/main.py +++ b/app/main.py @@ -4,8 +4,10 @@ import os import sys import asyncio +from datetime import datetime, timedelta from pathlib import Path from aiohttp import web +from aiohttp.web import GracefulExit from aiohttp.log import access_logger import ssl import socket @@ -23,6 +25,22 @@ from yt_dlp.version import __version__ as yt_dlp_version log = logging.getLogger('main') +_NIGHTLY_TIME_RE = re.compile(r'^([01]\d|2[0-3]):[0-5]\d$') +_RESTART_FOR_UPDATE = False + +def _request_graceful_exit() -> None: + raise GracefulExit() + + +def seconds_until_next_daily_time(time_hhmm: str, now: datetime | None = None) -> float: + """Seconds until the next occurrence of HH:MM in local time.""" + now = now or datetime.now() + hour, minute = map(int, time_hhmm.split(':')) + target = now.replace(hour=hour, minute=minute, second=0, microsecond=0) + if target <= now: + target += timedelta(days=1) + return (target - now).total_seconds() + def parseLogLevel(logLevel): if not isinstance(logLevel, str): return None @@ -73,6 +91,7 @@ class Config: 'MAX_CONCURRENT_DOWNLOADS': '3', 'LOGLEVEL': 'INFO', 'ENABLE_ACCESSLOG': 'false', + 'YTDL_NIGHTLY_UPDATE_TIME': '', } _BOOLEAN = ('DOWNLOAD_DIRS_INDEXABLE', 'CUSTOM_DIRS', 'CREATE_CUSTOM_DIRS', 'DELETE_FILE_ON_TRASHCAN', 'HTTPS', 'ENABLE_ACCESSLOG', 'ALLOW_YTDL_OPTIONS_OVERRIDES') @@ -104,6 +123,13 @@ class Config: if self.YTDL_OPTIONS_PRESETS_FILE and self.YTDL_OPTIONS_PRESETS_FILE.startswith('.'): self.YTDL_OPTIONS_PRESETS_FILE = str(Path(self.YTDL_OPTIONS_PRESETS_FILE).resolve()) + if self.YTDL_NIGHTLY_UPDATE_TIME and not _NIGHTLY_TIME_RE.match(self.YTDL_NIGHTLY_UPDATE_TIME): + log.error( + 'Environment variable "YTDL_NIGHTLY_UPDATE_TIME" must be HH:MM (24-hour), got "%s"', + self.YTDL_NIGHTLY_UPDATE_TIME, + ) + sys.exit(1) + self._runtime_overrides = {} success,_ = self.load_ytdl_options() @@ -465,8 +491,18 @@ class Notifier(DownloadQueueNotifier): await sio.emit('cleared', serializer.encode(id)) dqueue = DownloadQueue(config, Notifier()) -app.on_startup.append(lambda app: dqueue.initialize()) -app.on_cleanup.append(lambda app: Download.shutdown_manager()) + + +async def _download_queue_startup(app): + await dqueue.initialize() + + +async def _shutdown_download_manager(app): + Download.shutdown_manager() + + +app.on_startup.append(_download_queue_startup) +app.on_cleanup.append(_shutdown_download_manager) class MetubeSubscriptionNotifier(SubscriptionNotifier): @@ -486,7 +522,13 @@ class MetubeSubscriptionNotifier(SubscriptionNotifier): submgr = SubscriptionManager(config, dqueue, MetubeSubscriptionNotifier()) -app.on_cleanup.append(lambda app: submgr.close()) + + +async def _shutdown_subscriptions(app): + submgr.close() + + +app.on_cleanup.append(_shutdown_subscriptions) async def _subscription_loop_startup(app): @@ -496,6 +538,26 @@ async def _subscription_loop_startup(app): app.on_startup.append(_subscription_loop_startup) + +async def _schedule_nightly_update() -> None: + global _RESTART_FOR_UPDATE + time_hhmm = config.YTDL_NIGHTLY_UPDATE_TIME + if not time_hhmm: + return + delay = seconds_until_next_daily_time(time_hhmm) + log.info('Next yt-dlp nightly update in %.0f seconds (at %s local time)', delay, time_hhmm) + await asyncio.sleep(delay) + log.info('Scheduled yt-dlp nightly update: requesting restart') + _RESTART_FOR_UPDATE = True + asyncio.get_running_loop().call_soon(_request_graceful_exit) + + +async def _start_nightly_update_schedule(app): + asyncio.create_task(_schedule_nightly_update()) + + +app.on_startup.append(_start_nightly_update_schedule) + class FileOpsFilter(DefaultFilter): def __call__(self, change_type: int, path: str) -> bool: # Check if this path matches our YTDL_OPTIONS_FILE @@ -542,8 +604,12 @@ async def watch_files(): log.info(f'Starting Watch File: {config.YTDL_OPTIONS_FILE}') asyncio.create_task(_watch_files()) +async def _watch_files_startup(app): + await watch_files() + + if config.YTDL_OPTIONS_FILE: - app.on_startup.append(lambda app: watch_files()) + app.on_startup.append(_watch_files_startup) async def _read_json_request(request: web.Request) -> dict: @@ -1122,3 +1188,5 @@ if __name__ == '__main__': web.run_app(app, host=config.HOST, port=int(config.PORT), reuse_port=supports_reuse_port(), ssl_context=ssl_context, access_log=isAccessLogEnabled()) else: web.run_app(app, host=config.HOST, port=int(config.PORT), reuse_port=supports_reuse_port(), access_log=isAccessLogEnabled()) + if _RESTART_FOR_UPDATE: + sys.exit(42) diff --git a/app/tests/test_config.py b/app/tests/test_config.py index 6b096bf..8105e79 100644 --- a/app/tests/test_config.py +++ b/app/tests/test_config.py @@ -107,6 +107,22 @@ class ConfigTests(unittest.TestCase): c = Config() self.assertTrue(c.ALLOW_YTDL_OPTIONS_OVERRIDES) + def test_ytdl_nightly_update_time_empty_default(self): + with patch.dict(os.environ, _base_env(YTDL_NIGHTLY_UPDATE_TIME=""), clear=False): + c = Config() + self.assertEqual(c.YTDL_NIGHTLY_UPDATE_TIME, "") + + def test_ytdl_nightly_update_time_valid(self): + with patch.dict(os.environ, _base_env(YTDL_NIGHTLY_UPDATE_TIME="04:00"), clear=False): + c = Config() + self.assertEqual(c.YTDL_NIGHTLY_UPDATE_TIME, "04:00") + + def test_ytdl_nightly_update_time_invalid_exits(self): + for bad in ("25:00", "4am", "12:60"): + with patch.dict(os.environ, _base_env(YTDL_NIGHTLY_UPDATE_TIME=bad), clear=False): + with self.assertRaises(SystemExit): + Config() + def test_runtime_override_roundtrip(self): with patch.dict(os.environ, _base_env(), clear=False): c = Config() diff --git a/app/tests/test_nightly_update.py b/app/tests/test_nightly_update.py new file mode 100644 index 0000000..749ca8a --- /dev/null +++ b/app/tests/test_nightly_update.py @@ -0,0 +1,29 @@ +"""Tests for nightly yt-dlp update scheduling helpers.""" + +from __future__ import annotations + +import unittest +from datetime import datetime + +from main import seconds_until_next_daily_time + + +class NightlyUpdateTests(unittest.TestCase): + def test_seconds_until_later_today(self): + now = datetime(2026, 6, 4, 10, 0, 0) + delay = seconds_until_next_daily_time("15:30", now) + self.assertEqual(delay, 5 * 3600 + 30 * 60) + + def test_seconds_until_wraps_to_next_day(self): + now = datetime(2026, 6, 4, 18, 0, 0) + delay = seconds_until_next_daily_time("04:00", now) + self.assertEqual(delay, 10 * 3600) + + def test_seconds_until_same_minute_is_next_day(self): + now = datetime(2026, 6, 4, 4, 0, 30) + delay = seconds_until_next_daily_time("04:00", now) + self.assertAlmostEqual(delay, 24 * 3600 - 30, delta=1) + + +if __name__ == "__main__": + unittest.main() diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 5f07bc5..e1d976c 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -8,6 +8,48 @@ umask ${UMASK} echo "Creating download directory (${DOWNLOAD_DIR}), state directory (${STATE_DIR}), and temp dir (${TEMP_DIR})" mkdir -p "${DOWNLOAD_DIR}" "${STATE_DIR}" "${TEMP_DIR}" +do_upgrade() { + echo "Upgrading yt-dlp to nightly channel..." + if ! python3 -m pip --version >/dev/null 2>&1; then + echo "pip not found; attempting ensurepip" + python3 -m ensurepip --upgrade >/dev/null 2>&1 || true + fi + if ! python3 -m pip install -U --pre "yt-dlp[default,curl-cffi,deno]"; then + echo "Warning: yt-dlp nightly upgrade failed; continuing with existing installation" + return 1 + fi + echo "yt-dlp nightly upgrade complete" + return 0 +} + +run_supervised() { + while true; do + "$@" & + child_pid=$! + trap 'kill -TERM "$child_pid" 2>/dev/null; wait "$child_pid" 2>/dev/null' TERM INT + wait "$child_pid" + exit_code=$? + trap - TERM INT + if [ "$exit_code" -eq 42 ]; then + echo "MeTube requested yt-dlp update restart (exit 42)" + do_upgrade || true + continue + fi + return "$exit_code" + done +} + +nightly_enabled() { + [ -n "${YTDL_NIGHTLY_UPDATE_TIME}" ] +} + +disable_nightly_for_non_root() { + if nightly_enabled; then + echo "YTDL_NIGHTLY_UPDATE_TIME is set but this container runs as a non-root user; nightly yt-dlp updates are not supported. Ignoring YTDL_NIGHTLY_UPDATE_TIME." + unset YTDL_NIGHTLY_UPDATE_TIME + fi +} + if [ `id -u` -eq 0 ] && [ `id -g` -eq 0 ]; then if [ "${PUID}" -eq 0 ]; then echo "Warning: it is not recommended to run as root user, please check your setting of the PUID/PGID (or legacy UID/GID) environment variables" @@ -16,13 +58,20 @@ if [ `id -u` -eq 0 ] && [ `id -g` -eq 0 ]; then echo "Changing ownership of download and state directories to ${PUID}:${PGID}" chown -R "${PUID}":"${PGID}" /app "${DOWNLOAD_DIR}" "${STATE_DIR}" "${TEMP_DIR}" fi + if nightly_enabled; then + echo "YTDL_NIGHTLY_UPDATE_TIME is set to ${YTDL_NIGHTLY_UPDATE_TIME}; upgrading yt-dlp on startup" + do_upgrade || true + fi echo "Starting BgUtils POT Provider" gosu "${PUID}":"${PGID}" bgutil-pot server >/tmp/bgutil-pot.log 2>&1 & echo "Running MeTube as user ${PUID}:${PGID}" - exec gosu "${PUID}":"${PGID}" python3 app/main.py + run_supervised gosu "${PUID}":"${PGID}" python3 app/main.py + exit $? else echo "User set by docker; running MeTube as `id -u`:`id -g`" + disable_nightly_for_non_root echo "Starting BgUtils POT Provider" bgutil-pot server >/tmp/bgutil-pot.log 2>&1 & - exec python3 app/main.py + run_supervised python3 app/main.py + exit $? fi diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index f1a2edb..5dc8709 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -11,6 +11,7 @@ import { faTrashAlt, faCheckCircle, faTimesCircle, faRedoAlt, faSun, faMoon, faC import { faGithub } from '@fortawesome/free-brands-svg-icons'; import { CookieService } from 'ngx-cookie-service'; import { AddDownloadPayload, DownloadsService } from './services/downloads.service'; +import { MeTubeSocket } from './services/metube-socket.service'; import { SubscriptionsService } from './services/subscriptions.service'; import { SubscriptionRow } from './interfaces/subscription'; import { Themes } from './theme'; @@ -56,6 +57,7 @@ import { SelectAllCheckboxComponent, ItemCheckboxComponent } from './components/ export class App implements AfterViewInit, OnInit, OnDestroy { downloads = inject(DownloadsService); subscriptionsSvc = inject(SubscriptionsService); + private socket = inject(MeTubeSocket); private cookieService = inject(CookieService); private http = inject(HttpClient); private cdr = inject(ChangeDetectorRef); @@ -328,6 +330,9 @@ export class App implements AfterViewInit, OnInit, OnDestroy { // Initialize action button states for already-loaded entries. this.updateDoneActionButtons(); this.fetchVersionInfo(); + this.socket.fromEvent('connect') + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => this.fetchVersionInfo()); } ngOnDestroy() { From e30a24ff70b0cccad31eb0fe214a44a6a21c78fb Mon Sep 17 00:00:00 2001 From: AutoUpdater Date: Wed, 10 Jun 2026 00:36:22 +0000 Subject: [PATCH 50/88] upgrade yt-dlp from 2026.3.17 to 2026.6.9 --- uv.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/uv.lock b/uv.lock index 7d2c324..1a1b236 100644 --- a/uv.lock +++ b/uv.lock @@ -1060,11 +1060,11 @@ wheels = [ [[package]] name = "yt-dlp" -version = "2026.3.17" +version = "2026.6.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/8b/34/7c6b4e3f89cb6416d2cd7ab6dab141a1df97ab0fb22d15816db2c92148c9/yt_dlp-2026.3.17.tar.gz", hash = "sha256:ba7aa31d533f1ffccfe70e421596d7ca8ff0bf1398dc6bb658b7d9dec057d2c9", size = 3119221, upload-time = "2026-03-17T23:43:00.244Z" } +sdist = { url = "https://files.pythonhosted.org/packages/88/a4/1b0979d28f87774bb67fbbc66bce44f9dd1aa0e547a99e22985fac945c33/yt_dlp-2026.6.9.tar.gz", hash = "sha256:d50fcb95f48d61bedde33e408c1881d4c279e51c31354a599ce09e96ba0f4b86", size = 3030590, upload-time = "2026-06-09T23:27:14.831Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cd/13/5093bcb954878e50f7217fd2ab94282b53934022e4e4a03265582da83bf5/yt_dlp-2026.3.17-py3-none-any.whl", hash = "sha256:32992db94303a8a5d211a183f2174834fe7f8c29d83ed2e7a324eae97a8f26d8", size = 3315134, upload-time = "2026-03-17T23:42:57.863Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ee/188a3dadf9dfdac713243521f919feca1cd091d4358c9ea7e8ebb710a7cc/yt_dlp-2026.6.9-py3-none-any.whl", hash = "sha256:442ba4c75724b9496144c8434b617962ee08d0ee7c26ec663848fe9b78d5a3e4", size = 3169035, upload-time = "2026-06-09T23:27:12.58Z" }, ] [package.optional-dependencies] @@ -1072,7 +1072,7 @@ curl-cffi = [ { name = "curl-cffi", marker = "implementation_name == 'cpython'" }, ] default = [ - { name = "brotli", marker = "implementation_name == 'cpython'" }, + { name = "brotli", marker = "implementation_name == 'cpython' and sys_platform != 'ios'" }, { name = "brotlicffi", marker = "implementation_name != 'cpython'" }, { name = "certifi" }, { name = "mutagen" }, From d1574448774c1350af850785eff93d9577dfc021 Mon Sep 17 00:00:00 2001 From: Andrew Keeton <208120+akeeton@users.noreply.github.com> Date: Thu, 11 Jun 2026 17:21:56 -0400 Subject: [PATCH 51/88] Create AUDIO_DOWNLOAD_DIR in Docker image --- Dockerfile | 1 + docker-entrypoint.sh | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index f9896bc..5a25570 100644 --- a/Dockerfile +++ b/Dockerfile @@ -61,6 +61,7 @@ ENV PGID=1000 ENV UMASK=022 ENV DOWNLOAD_DIR=/downloads +ENV AUDIO_DOWNLOAD_DIR=/downloads ENV STATE_DIR=/downloads/.metube ENV TEMP_DIR=/downloads ENV PORT=8081 diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index e1d976c..615d477 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -5,8 +5,8 @@ PGID="${GID:-$PGID}" echo "Setting umask to ${UMASK}" umask ${UMASK} -echo "Creating download directory (${DOWNLOAD_DIR}), state directory (${STATE_DIR}), and temp dir (${TEMP_DIR})" -mkdir -p "${DOWNLOAD_DIR}" "${STATE_DIR}" "${TEMP_DIR}" +echo "Creating download directory (${DOWNLOAD_DIR}), audio download directory (${AUDIO_DOWNLOAD_DIR}), state directory (${STATE_DIR}), and temp dir (${TEMP_DIR})" +mkdir -p "${DOWNLOAD_DIR}" "${AUDIO_DOWNLOAD_DIR}" "${STATE_DIR}" "${TEMP_DIR}" do_upgrade() { echo "Upgrading yt-dlp to nightly channel..." @@ -56,7 +56,7 @@ if [ `id -u` -eq 0 ] && [ `id -g` -eq 0 ]; then fi if [ "${CHOWN_DIRS:-true}" != "false" ]; then echo "Changing ownership of download and state directories to ${PUID}:${PGID}" - chown -R "${PUID}":"${PGID}" /app "${DOWNLOAD_DIR}" "${STATE_DIR}" "${TEMP_DIR}" + chown -R "${PUID}":"${PGID}" /app "${DOWNLOAD_DIR}" "${AUDIO_DOWNLOAD_DIR}" "${STATE_DIR}" "${TEMP_DIR}" fi if nightly_enabled; then echo "YTDL_NIGHTLY_UPDATE_TIME is set to ${YTDL_NIGHTLY_UPDATE_TIME}; upgrading yt-dlp on startup" From a9b2e07a599dec218708a8ab933cd404f62accd5 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 12 Jun 2026 10:16:19 +0300 Subject: [PATCH 52/88] nightly release README update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5d5a25f..3de99d0 100644 --- a/README.md +++ b/README.md @@ -347,7 +347,7 @@ example.com { ## πŸ”„ Updating yt-dlp -MeTube is powered by [yt-dlp](https://github.com/yt-dlp/yt-dlp), which requires frequent updates as video sites change their layouts. A nightly build automatically publishes a new Docker image whenever a new yt-dlp version is available, so keep your container up to date β€” [watchtower](https://github.com/nicholas-fedor/watchtower) works well for this. +MeTube is powered by [yt-dlp](https://github.com/yt-dlp/yt-dlp), which requires frequent updates as video sites change their layouts. A new MeTube Docker image is published automatically when a new yt-dlp stable release is available, so keep your container up to date β€” [watchtower](https://github.com/nicholas-fedor/watchtower) works well for this. To follow yt-dlp's nightly channel instead, set `YTDL_NIGHTLY_UPDATE_TIME`. ## πŸ”§ Troubleshooting and submitting issues From 72d60ea55aa266b4378e06ec1d31e8968de510cf Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 12 Jun 2026 12:45:38 +0300 Subject: [PATCH 53/88] upgrade dependencies --- ui/package.json | 26 +- ui/pnpm-lock.yaml | 1075 ++++++++++++++++++++++----------------------- uv.lock | 215 +++++---- 3 files changed, 685 insertions(+), 631 deletions(-) diff --git a/ui/package.json b/ui/package.json index e827e85..ef3b4b5 100644 --- a/ui/package.json +++ b/ui/package.json @@ -23,14 +23,14 @@ }, "private": true, "dependencies": { - "@angular/animations": "^21.2.14", - "@angular/common": "^21.2.14", - "@angular/compiler": "^21.2.14", - "@angular/core": "^21.2.14", - "@angular/forms": "^21.2.14", - "@angular/platform-browser": "^21.2.14", - "@angular/platform-browser-dynamic": "^21.2.14", - "@angular/service-worker": "^21.2.14", + "@angular/animations": "^21.2.17", + "@angular/common": "^21.2.17", + "@angular/compiler": "^21.2.17", + "@angular/core": "^21.2.17", + "@angular/forms": "^21.2.17", + "@angular/platform-browser": "^21.2.17", + "@angular/platform-browser-dynamic": "^21.2.17", + "@angular/service-worker": "^21.2.17", "@fortawesome/angular-fontawesome": "~4.0.0", "@fortawesome/fontawesome-svg-core": "^7.2.0", "@fortawesome/free-brands-svg-icons": "^7.2.0", @@ -48,16 +48,16 @@ }, "devDependencies": { "@angular-eslint/builder": "21.1.0", - "@angular/build": "^21.2.13", - "@angular/cli": "^21.2.13", - "@angular/compiler-cli": "^21.2.14", - "@angular/localize": "^21.2.14", + "@angular/build": "^21.2.14", + "@angular/cli": "^21.2.14", + "@angular/compiler-cli": "^21.2.17", + "@angular/localize": "^21.2.17", "@eslint/js": "^9.39.4", "angular-eslint": "21.1.0", "eslint": "^9.39.4", "jsdom": "^27.4.0", "typescript": "~5.9.3", "typescript-eslint": "8.47.0", - "vitest": "^4.1.7" + "vitest": "^4.1.8" } } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 1202bd9..c787cbd 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -9,32 +9,32 @@ importers: .: dependencies: '@angular/animations': - specifier: ^21.2.14 - version: 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^21.2.17 + version: 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/common': - specifier: ^21.2.14 - version: 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^21.2.17 + version: 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@angular/compiler': - specifier: ^21.2.14 - version: 21.2.14 + specifier: ^21.2.17 + version: 21.2.17 '@angular/core': - specifier: ^21.2.14 - version: 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^21.2.17 + version: 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) '@angular/forms': - specifier: ^21.2.14 - version: 21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + specifier: ^21.2.17 + version: 21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) '@angular/platform-browser': - specifier: ^21.2.14 - version: 21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^21.2.17 + version: 21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/platform-browser-dynamic': - specifier: ^21.2.14 - version: 21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.14)(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))) + specifier: ^21.2.17 + version: 21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))) '@angular/service-worker': - specifier: ^21.2.14 - version: 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^21.2.17 + version: 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@fortawesome/angular-fontawesome': specifier: ~4.0.0 - version: 4.0.0(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) + version: 4.0.0(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) '@fortawesome/fontawesome-svg-core': specifier: ^7.2.0 version: 7.2.0 @@ -49,10 +49,10 @@ importers: version: 7.2.0 '@ng-bootstrap/ng-bootstrap': specifier: ^20.0.0 - version: 20.0.0(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14))(@popperjs/core@2.11.8)(rxjs@7.8.2) + version: 20.0.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@popperjs/core@2.11.8)(rxjs@7.8.2) '@ng-select/ng-select': specifier: ^21.8.2 - version: 21.8.2(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) + version: 21.8.2(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) '@popperjs/core': specifier: ^2.11.8 version: 2.11.8 @@ -61,10 +61,10 @@ importers: version: 5.3.8(@popperjs/core@2.11.8) ngx-cookie-service: specifier: ^21.3.1 - version: 21.3.1(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) + version: 21.3.1(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) ngx-socket-io: specifier: ~4.10.0 - version: 4.10.0(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + version: 4.10.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) rxjs: specifier: ~7.8.2 version: 7.8.2 @@ -77,25 +77,25 @@ importers: devDependencies: '@angular-eslint/builder': specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular/build': - specifier: ^21.2.13 - version: 21.2.13(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14)(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3))) + specifier: ^21.2.14 + version: 21.2.14(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.3)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.8(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3))) '@angular/cli': - specifier: ^21.2.13 - version: 21.2.13(@types/node@25.9.1)(chokidar@5.0.0) + specifier: ^21.2.14 + version: 21.2.14(@types/node@25.9.3)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^21.2.14 - version: 21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3) + specifier: ^21.2.17 + version: 21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3) '@angular/localize': - specifier: ^21.2.14 - version: 21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14) + specifier: ^21.2.17 + version: 21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17) '@eslint/js': specifier: ^9.39.4 version: 9.39.4 angular-eslint: specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) eslint: specifier: ^9.39.4 version: 9.39.4 @@ -109,8 +109,8 @@ importers: specifier: 8.47.0 version: 8.47.0(eslint@9.39.4)(typescript@5.9.3) vitest: - specifier: ^4.1.7 - version: 4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)) + specifier: ^4.1.8 + version: 4.1.8(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) packages: @@ -177,13 +177,13 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.2102.13': - resolution: {integrity: sha512-fheyi0gPx6b7tT+WQ+ePlzdGqKjPLUK72wg5Z9pkVtQ5+VN/8yB9mlRlmoivngd2FeNG9wMeNynWZGYycnOWVw==} + '@angular-devkit/architect@0.2102.14': + resolution: {integrity: sha512-0+vjVsCkMyJdVjz5XkPW+Bdf/9TI8V2voomx/+o0o+oOaqqiEhptQWFnaIlLr7HasjB0LxXK5P9L0oQ61vxj8Q==} 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'} hasBin: true - '@angular-devkit/core@21.2.13': - resolution: {integrity: sha512-9jLaHcUr6BumIY9nCsBib1q62p259nf++gd2igYJ7mLm1w/0wEacsZ1cC8wCGEe6vx8a+DrD+EVCQ6zivePG2A==} + '@angular-devkit/core@21.2.14': + resolution: {integrity: sha512-RSOWXB9bFc2nwRWMxbIT0RbSNFUrwfBo4N5MNxbyQ69Ndc0gVm3h+3ArHv0qotH4d+pJYbm5ttXu8YqR2kc0CA==} 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: ^5.0.0 @@ -191,8 +191,8 @@ packages: chokidar: optional: true - '@angular-devkit/schematics@21.2.13': - resolution: {integrity: sha512-gifpOcMNiAy49lQmQKhzpxoSfS3qJQSEdJSF5m7RVFkAcmllfcCD76GPN4dhho3wdAnbZ3qr54LtDqrGY4xNjw==} + '@angular-devkit/schematics@21.2.14': + resolution: {integrity: sha512-KMJlQSBEzI4+Cy1Zh72gmGQNN2I1vY+nj9CoRcZPBIi1si+0ZAc49XT85eYl+eQumNTVQviUG7LQqgLDAHml+g==} 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'} '@angular-eslint/builder@21.1.0': @@ -239,14 +239,15 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '*' - '@angular/animations@21.2.14': - resolution: {integrity: sha512-9WLnsJE0xqtd1rVtHMvsAUxFy3OdPks4bdmUIqyw23X/je7ytUALAGWNadffcZBwRpa1A6TUnLr9X4+Draz3kw==} + '@angular/animations@21.2.17': + resolution: {integrity: sha512-zOW8FFa9qfbVkZ5TulxDkl1C3+gEjWfAAD5Z2MycA6pjVJQlLYPiTAGq+flOQ3yZfTT0z6kd5rejQMXWI81Dvg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + deprecated: '@angular/animations is deprecated. Use `animate.enter` and `animate.leave` instead. For more information see: https://v22.angular.dev/guide/animations.' peerDependencies: - '@angular/core': 21.2.14 + '@angular/core': 21.2.17 - '@angular/build@21.2.13': - resolution: {integrity: sha512-Y9TDAaTQ+E5LScCKA/hPZmns/7Mpu6J2BiPj2cETA1xNjvgRpeb5Mh32KuhZb20NSFLvjpdnLuBTTtbym7hevw==} + '@angular/build@21.2.14': + resolution: {integrity: sha512-l8JB326iIwum2WmbopUUFdiuYsbHchix6MH8o6F6FA7LJr8QLTvipwwbw+Jx31/RE50WkGmzsZ1fBDw/cMbmUw==} 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: '@angular/compiler': ^21.0.0 @@ -256,7 +257,7 @@ packages: '@angular/platform-browser': ^21.0.0 '@angular/platform-server': ^21.0.0 '@angular/service-worker': ^21.0.0 - '@angular/ssr': ^21.2.13 + '@angular/ssr': ^21.2.14 karma: ^6.4.0 less: ^4.2.0 ng-packagr: ^21.0.0 @@ -291,38 +292,38 @@ packages: vitest: optional: true - '@angular/cli@21.2.13': - resolution: {integrity: sha512-j1kOV/f0og/3xCwG7Y8RyPd6V7uYfX2NuvXbvN1mzgxLLN2mu6CTsvPg5l/9Pu9SJI3KOPRgDxWyuP3k8KuzMg==} + '@angular/cli@21.2.14': + resolution: {integrity: sha512-S8jExTjxPJILwpg2lu3DohSASVZ8DLhSNCmOe7z0qF9VskRSjC7SIQv1rq36tsJkenxuA72gjVOHZv+uSRT8HA==} 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'} hasBin: true - '@angular/common@21.2.14': - resolution: {integrity: sha512-J6K7cE7uKOKmg4+sxLeGfsmaYDjP5l1XCiMMI0WPT0t68uxLk8g3MzV5Trqfb6ZnRxWcfp9c4c+XxAvMBB7ymA==} + '@angular/common@21.2.17': + resolution: {integrity: sha512-hqAQxRfi5ldFE42suAXRcY+JCANrUh7fuSQ/DtZ7L896id5BT/exuv6dWNBC1PyAfQmRbpD5Pt6/pd+tNLyhDQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.2.14 + '@angular/core': 21.2.17 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@21.2.14': - resolution: {integrity: sha512-h+WQfPKFxaDfDhMqUUdOQ1TsDMccav8kLFERmKTRfD4MNOczSMpOMyeXJHCL0Rq4I8WDQvaBJGMG7DXRDefSog==} + '@angular/compiler-cli@21.2.17': + resolution: {integrity: sha512-KithZ3b0HBFH0NbUcswBcjpN9y09vLbarMD7qmGWTnGUBk4W8cn4sbT8zJyv9CRKg9ZcuUBeJYKUfUPn/u/5OQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.14 + '@angular/compiler': 21.2.17 typescript: '>=5.9 <6.1' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@21.2.14': - resolution: {integrity: sha512-8mqgwRYfn2Z1vg/5YVt60dDBattnZL45nNJd2vTMwAiDTzhWhgKgRWKOeVL0aj2JqHeHiwuIlrLnz46acJMulQ==} + '@angular/compiler@21.2.17': + resolution: {integrity: sha512-p+NdjYiwAz9Zmu2yul0LlMXaFjMISVVa24+/MVMoKFeQeI82QE8jDywPlnOSHQHvdCcQVpS7saeEriZzX3JuBQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - '@angular/core@21.2.14': - resolution: {integrity: sha512-Z1Ivjh7L2lT//8LA7vQ3tj7Rg6wl2XRA5kPSAukgn8u0Yu0XxG8NE8KG0Eypb3v9CEcbwATwpgnxzbJFZ8TFcw==} + '@angular/core@21.2.17': + resolution: {integrity: sha512-wYHpwIdnUnjQFOJJNqRcGx7LS3u64jT+R9L0TnMR/ViBM9dQgGYImlSikkftg2yrFCNo5aKRxhG2LLskQurVdg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/compiler': 21.2.14 + '@angular/compiler': 21.2.17 rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 || ~0.16.0 peerDependenciesMeta: @@ -331,49 +332,49 @@ packages: zone.js: optional: true - '@angular/forms@21.2.14': - resolution: {integrity: sha512-HQYIybyMt0CrI31rW6vXbiDsSM2DDtTcOVeT/nWDRNCoqBrREDg8rVsm2Y+fUMsiQVJNa6dCXPwvYhjzJ4r7ug==} + '@angular/forms@21.2.17': + resolution: {integrity: sha512-WKu8XeRSNZo+a+aDDZ3M5OtReF7KYqR/PmZ2l1lSf6N5EEAmc+Ky4aqbRhTL/mTSfHrO4+TDJ4C5A2tFmuwIeA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.2.14 - '@angular/core': 21.2.14 - '@angular/platform-browser': 21.2.14 + '@angular/common': 21.2.17 + '@angular/core': 21.2.17 + '@angular/platform-browser': 21.2.17 rxjs: ^6.5.3 || ^7.4.0 - '@angular/localize@21.2.14': - resolution: {integrity: sha512-cVqQag+O8mIuH5Dz8Lw4uElWtFogr+xgUFbakI3QQAbnLaqeQaDqHvXE8VGkTmhGvEMnE0cvPn+rOQ/N5exzbw==} + '@angular/localize@21.2.17': + resolution: {integrity: sha512-CkiSHyag6W1cc2n8Y3sC4eYSqTqRslORU7DJnelEoJvh/2KM6/BmxxfjDlL6JcZURiiTPgxNjRgpb9zREj3elg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.14 - '@angular/compiler-cli': 21.2.14 + '@angular/compiler': 21.2.17 + '@angular/compiler-cli': 21.2.17 - '@angular/platform-browser-dynamic@21.2.14': - resolution: {integrity: sha512-m5U4zX8JFnxTAIGpsBXIAyefSmYqdORY/OfHC0aMmZovuFCbXXIYqYRQDBB7+YVNpSDSHllCrKEZFu/CC6dq3g==} + '@angular/platform-browser-dynamic@21.2.17': + resolution: {integrity: sha512-r/BU/T8bOTghP3fIXhzYf5wcMcAmhWnAFv3p4asCCPXomaktoas70wYcMaDH+pK1LAFBxLwzBWHm36MpFlTMFg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.2.14 - '@angular/compiler': 21.2.14 - '@angular/core': 21.2.14 - '@angular/platform-browser': 21.2.14 + '@angular/common': 21.2.17 + '@angular/compiler': 21.2.17 + '@angular/core': 21.2.17 + '@angular/platform-browser': 21.2.17 - '@angular/platform-browser@21.2.14': - resolution: {integrity: sha512-34tBwxh86yN2YifBDhCesm6N+nn9WcbuXjRwfo0mTme15OZ/zt56yw7v1mcK3UFLegIIALtsIgpXXrPWWQoKkA==} + '@angular/platform-browser@21.2.17': + resolution: {integrity: sha512-ROdSliejY37g1EphYmweYdm5cHM8HY3X4tbWt4ubxmhTyYgfN3nxrxfGQ/n7Mz5tDY9VXVLIGDgjLOGYOo4uTQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/animations': 21.2.14 - '@angular/common': 21.2.14 - '@angular/core': 21.2.14 + '@angular/animations': 21.2.17 + '@angular/common': 21.2.17 + '@angular/core': 21.2.17 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/service-worker@21.2.14': - resolution: {integrity: sha512-60DMV71QcaRtj3HwmV8Oz5zOKKudEyWourpsfpFptzF4hQ+7T7I4xSla1J0q/7b16Kwf44Ey90vCPlUrsMLVrA==} + '@angular/service-worker@21.2.17': + resolution: {integrity: sha512-6/uKwxBA3udngVHuIVqD8kdMV1whfym9ESB1UyjoNINx+2zj7A749X89tiV0TW2CvWpUYUE7VxpoapEiv5lvfw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/core': 21.2.14 + '@angular/core': 21.2.17 rxjs: ^6.5.3 || ^7.4.0 '@asamuzakjp/css-color@4.1.2': @@ -471,8 +472,8 @@ packages: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-color-parser@4.1.1': - resolution: {integrity: sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==} + '@csstools/css-color-parser@4.1.3': + resolution: {integrity: sha512-DOgvIPkikIOixQRlD4YF31VN6fLLUTdrzhfRbis8vm0kMTgIbEPX0Ip/YX9fOeV9iywAS4sUUbTclpan7yYP8Q==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -484,8 +485,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.4': - resolution: {integrity: sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==} + '@csstools/css-syntax-patches-for-csstree@1.1.5': + resolution: {integrity: sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -1119,8 +1120,8 @@ packages: resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} engines: {node: '>= 10'} - '@napi-rs/wasm-runtime@1.1.4': - resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} + '@napi-rs/wasm-runtime@1.1.5': + resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 @@ -1370,146 +1371,146 @@ packages: '@rolldown/pluginutils@1.0.0-rc.4': resolution: {integrity: sha512-1BrrmTu0TWfOP1riA8uakjFc9bpIUGzVKETsOtzY39pPga8zELGDl8eu1Dx7/gjM5CAz14UknsUMpBO8L+YntQ==} - '@rollup/rollup-android-arm-eabi@4.60.4': - resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} + '@rollup/rollup-android-arm-eabi@4.61.1': + resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.60.4': - resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} + '@rollup/rollup-android-arm64@4.61.1': + resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.60.4': - resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} + '@rollup/rollup-darwin-arm64@4.61.1': + resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.60.4': - resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} + '@rollup/rollup-darwin-x64@4.61.1': + resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.60.4': - resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} + '@rollup/rollup-freebsd-arm64@4.61.1': + resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.60.4': - resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} + '@rollup/rollup-freebsd-x64@4.61.1': + resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.60.4': - resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} + '@rollup/rollup-linux-arm-gnueabihf@4.61.1': + resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.60.4': - resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} + '@rollup/rollup-linux-arm-musleabihf@4.61.1': + resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.60.4': - resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} + '@rollup/rollup-linux-arm64-gnu@4.61.1': + resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.60.4': - resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} + '@rollup/rollup-linux-arm64-musl@4.61.1': + resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.60.4': - resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} + '@rollup/rollup-linux-loong64-gnu@4.61.1': + resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.60.4': - resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} + '@rollup/rollup-linux-loong64-musl@4.61.1': + resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.60.4': - resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} + '@rollup/rollup-linux-ppc64-gnu@4.61.1': + resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.60.4': - resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} + '@rollup/rollup-linux-ppc64-musl@4.61.1': + resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.60.4': - resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} + '@rollup/rollup-linux-riscv64-gnu@4.61.1': + resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.60.4': - resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} + '@rollup/rollup-linux-riscv64-musl@4.61.1': + resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.60.4': - resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} + '@rollup/rollup-linux-s390x-gnu@4.61.1': + resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.60.4': - resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} + '@rollup/rollup-linux-x64-gnu@4.61.1': + resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.60.4': - resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} + '@rollup/rollup-linux-x64-musl@4.61.1': + resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.60.4': - resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} + '@rollup/rollup-openbsd-x64@4.61.1': + resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.60.4': - resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} + '@rollup/rollup-openharmony-arm64@4.61.1': + resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.60.4': - resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} + '@rollup/rollup-win32-arm64-msvc@4.61.1': + resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.4': - resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} + '@rollup/rollup-win32-ia32-msvc@4.61.1': + resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.60.4': - resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} + '@rollup/rollup-win32-x64-gnu@4.61.1': + resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.4': - resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} + '@rollup/rollup-win32-x64-msvc@4.61.1': + resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==} cpu: [x64] os: [win32] - '@schematics/angular@21.2.13': - resolution: {integrity: sha512-e5guslSLKbb3PJ6gUuVqM+V9xgn68cJkG1IyBohho34shbpOeoWW2eYdWQQjxvn0KUdgEhYSRBluBamCHngaUA==} + '@schematics/angular@21.2.14': + resolution: {integrity: sha512-rIEdtNTdCCTwuo7B4tMoq5qmbLXdBgmW6Ays1hyno//4OE+HFtvlWZd+hl6KceEyN00IcZ2HRaPnfd71E1JnoA==} 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'} '@sigstore/bundle@4.0.0': @@ -1577,17 +1578,14 @@ packages: '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@25.9.1': - resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} + '@types/node@25.9.3': + resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==} '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -1613,8 +1611,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.60.0': - resolution: {integrity: sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==} + '@typescript-eslint/project-service@8.61.0': + resolution: {integrity: sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1623,8 +1621,8 @@ packages: resolution: {integrity: sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.60.0': - resolution: {integrity: sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==} + '@typescript-eslint/scope-manager@8.61.0': + resolution: {integrity: sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.47.0': @@ -1633,8 +1631,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/tsconfig-utils@8.60.0': - resolution: {integrity: sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==} + '@typescript-eslint/tsconfig-utils@8.61.0': + resolution: {integrity: sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1650,8 +1648,8 @@ packages: resolution: {integrity: sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.60.0': - resolution: {integrity: sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==} + '@typescript-eslint/types@8.61.0': + resolution: {integrity: sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.47.0': @@ -1660,8 +1658,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.60.0': - resolution: {integrity: sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==} + '@typescript-eslint/typescript-estree@8.61.0': + resolution: {integrity: sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1673,8 +1671,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.60.0': - resolution: {integrity: sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==} + '@typescript-eslint/utils@8.61.0': + resolution: {integrity: sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -1684,8 +1682,8 @@ packages: resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.60.0': - resolution: {integrity: sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==} + '@typescript-eslint/visitor-keys@8.61.0': + resolution: {integrity: sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-basic-ssl@2.1.4': @@ -1694,11 +1692,11 @@ packages: peerDependencies: vite: ^6.0.0 || ^7.0.0 - '@vitest/expect@4.1.7': - resolution: {integrity: sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==} + '@vitest/expect@4.1.8': + resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} - '@vitest/mocker@4.1.7': - resolution: {integrity: sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==} + '@vitest/mocker@4.1.8': + resolution: {integrity: sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1708,20 +1706,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.7': - resolution: {integrity: sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==} + '@vitest/pretty-format@4.1.8': + resolution: {integrity: sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==} - '@vitest/runner@4.1.7': - resolution: {integrity: sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==} + '@vitest/runner@4.1.8': + resolution: {integrity: sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==} - '@vitest/snapshot@4.1.7': - resolution: {integrity: sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==} + '@vitest/snapshot@4.1.8': + resolution: {integrity: sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==} - '@vitest/spy@4.1.7': - resolution: {integrity: sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==} + '@vitest/spy@4.1.8': + resolution: {integrity: sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==} - '@vitest/utils@4.1.7': - resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==} + '@vitest/utils@4.1.8': + resolution: {integrity: sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==} '@yarnpkg/lockfile@1.1.0': resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} @@ -1827,8 +1825,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.10.32: - resolution: {integrity: sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==} + baseline-browser-mapping@2.10.35: + resolution: {integrity: sha512-honAfLBde0HAFLdNyBEfuuENkF6zR+ozxqxa/2zJKHBe1qzLqyTSeRKpdPEHAP03rlDGyQOPnCSxnVpVqQo9Mg==} engines: {node: '>=6.0.0'} hasBin: true @@ -1893,8 +1891,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001793: - resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} + caniuse-lite@1.0.30001797: + resolution: {integrity: sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -2055,8 +2053,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.363: - resolution: {integrity: sha512-VjUKPyWzGnT1fujlkEGC/BvN70Hh70KXtAqcmniXviYlJC/ivcT+BWGPyxWVbJZLfvtKR6dqg1L7T7pgAMBtWA==} + electron-to-chromium@1.5.371: + resolution: {integrity: sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2350,12 +2348,12 @@ packages: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} - hasown@2.0.3: - resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} - hono@4.12.23: - resolution: {integrity: sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==} + hono@4.12.25: + resolution: {integrity: sha512-2NFaIyNVgJmBs/ecmtGzlmluTFs5cHEWGTdu0t1HBwYzoGXOL5nUQBRMXsXWla5i4KkG//QMzVP88m1+I3fdAQ==} engines: {node: '>=16.9.0'} hosted-git-info@9.0.3: @@ -2400,8 +2398,8 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - immutable@5.1.5: - resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==} + immutable@5.1.6: + resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -2481,8 +2479,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + js-yaml@4.2.0: + resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} hasBin: true jsdom@27.4.0: @@ -2671,8 +2669,8 @@ packages: resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==} hasBin: true - msgpackr@1.11.12: - resolution: {integrity: sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg==} + msgpackr@1.12.1: + resolution: {integrity: sha512-4EUH9tQHnMmEgzW/MdAP0KIfa1T9AF+htl0ffe2n5vb2EKn9y2co8ccpgWko6S52Jy1PQZKwRnx5/KkYjtd9MQ==} mute-stream@2.0.0: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} @@ -2717,13 +2715,13 @@ packages: resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} hasBin: true - node-gyp@12.3.0: - resolution: {integrity: sha512-QNcUWM+HgJplcPzBvFBZ9VXacyGZ4+VTOb80PwWR+TlVzoHbRKULNEzpRsnaoxG3Wzr7Qh7BYxGDU3CbKib2Yg==} + node-gyp@12.4.0: + resolution: {integrity: sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==} engines: {node: ^20.17.0 || >=22.9.0} hasBin: true - node-releases@2.0.46: - resolution: {integrity: sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==} + node-releases@2.0.47: + resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==} engines: {node: '>=18'} nopt@9.0.0: @@ -2770,8 +2768,9 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + obug@2.1.2: + resolution: {integrity: sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==} + engines: {node: '>=12.20.0'} on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} @@ -2953,8 +2952,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.60.4: - resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} + rollup@4.61.1: + resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2994,8 +2993,8 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.8.1: - resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} + semver@7.8.4: + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} engines: {node: '>=10'} hasBin: true @@ -3030,8 +3029,8 @@ packages: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} engines: {node: '>= 0.4'} - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} siginfo@2.0.0: @@ -3153,34 +3152,34 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tar@7.5.15: - resolution: {integrity: sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==} + tar@7.5.16: + resolution: {integrity: sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==} engines: {node: '>=18'} tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.2.2: - resolution: {integrity: sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==} + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} engines: {node: '>=18'} tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} - tinyglobby@0.2.16: - resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} tinyrainbow@3.1.0: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tldts-core@7.4.0: - resolution: {integrity: sha512-/mb9kRld+x1sIMXxWNOAp5m6C+D4GrAORWlJkOJ5dElvxdN1eutz/o7qHLp9gFvDF4Y3/L2xeScoxz6AbEo8rQ==} + tldts-core@7.4.2: + resolution: {integrity: sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA==} - tldts@7.4.0: - resolution: {integrity: sha512-yHBe+zVfzNZ3QfTPW/Z6KK1G2t340gFjMHqI/4KKSt/abzYydzuCnpqdaF5gCCABby+9Yfbj59oR5F2Fd5CBzg==} + tldts@7.4.2: + resolution: {integrity: sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw==} hasBin: true to-regex-range@5.0.1: @@ -3304,20 +3303,20 @@ packages: yaml: optional: true - vitest@4.1.7: - resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==} + vitest@4.1.8: + resolution: {integrity: sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.7 - '@vitest/browser-preview': 4.1.7 - '@vitest/browser-webdriverio': 4.1.7 - '@vitest/coverage-istanbul': 4.1.7 - '@vitest/coverage-v8': 4.1.7 - '@vitest/ui': 4.1.7 + '@vitest/browser-playwright': 4.1.8 + '@vitest/browser-preview': 4.1.8 + '@vitest/browser-webdriverio': 4.1.8 + '@vitest/coverage-istanbul': 4.1.8 + '@vitest/coverage-v8': 4.1.8 + '@vitest/ui': 4.1.8 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3575,14 +3574,14 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.2102.13(chokidar@5.0.0)': + '@angular-devkit/architect@0.2102.14(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.13(chokidar@5.0.0) + '@angular-devkit/core': 21.2.14(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/core@21.2.13(chokidar@5.0.0)': + '@angular-devkit/core@21.2.14(chokidar@5.0.0)': dependencies: ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) @@ -3593,9 +3592,9 @@ snapshots: optionalDependencies: chokidar: 5.0.0 - '@angular-devkit/schematics@21.2.13(chokidar@5.0.0)': + '@angular-devkit/schematics@21.2.14(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.13(chokidar@5.0.0) + '@angular-devkit/core': 21.2.14(chokidar@5.0.0) jsonc-parser: 3.3.1 magic-string: 0.30.21 ora: 9.3.0 @@ -3603,11 +3602,11 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-eslint/builder@21.1.0(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/builder@21.1.0(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@angular-devkit/architect': 0.2102.13(chokidar@5.0.0) - '@angular-devkit/core': 21.2.13(chokidar@5.0.0) - '@angular/cli': 21.2.13(@types/node@25.9.1)(chokidar@5.0.0) + '@angular-devkit/architect': 0.2102.14(chokidar@5.0.0) + '@angular-devkit/core': 21.2.14(chokidar@5.0.0) + '@angular/cli': 21.2.14(@types/node@25.9.3)(chokidar@5.0.0) eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: @@ -3615,34 +3614,34 @@ snapshots: '@angular-eslint/bundled-angular-compiler@21.1.0': {} - '@angular-eslint/eslint-plugin-template@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin-template@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.0)(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/types': 8.60.0 - '@typescript-eslint/utils': 8.60.0(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/utils': 8.61.0(eslint@9.39.4)(typescript@5.9.3) aria-query: 5.3.2 axobject-query: 4.1.0 eslint: 9.39.4 typescript: 5.9.3 - '@angular-eslint/eslint-plugin@21.1.0(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin@21.1.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/utils': 8.60.0(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.61.0(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 - '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(@typescript-eslint/types@8.61.0)(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@angular-devkit/core': 21.2.13(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.13(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.13(@types/node@25.9.1)(chokidar@5.0.0) + '@angular-devkit/core': 21.2.14(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.14(chokidar@5.0.0) + '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.0)(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular/cli': 21.2.14(@types/node@25.9.3)(chokidar@5.0.0) ignore: 7.0.5 semver: 7.7.3 strip-json-comments: 3.1.1 @@ -3661,29 +3660,29 @@ snapshots: eslint-scope: 9.1.2 typescript: 5.9.3 - '@angular-eslint/utils@21.1.0(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/utils@21.1.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@typescript-eslint/utils': 8.60.0(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.61.0(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 - '@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - '@angular/build@21.2.13(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14)(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)))': + '@angular/build@21.2.14(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.3)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.8(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)))': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2102.13(chokidar@5.0.0) - '@angular/compiler': 21.2.14 - '@angular/compiler-cli': 21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3) + '@angular-devkit/architect': 0.2102.14(chokidar@5.0.0) + '@angular/compiler': 21.2.17 + '@angular/compiler-cli': 21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3) '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@25.9.1) - '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)) + '@inquirer/confirm': 5.1.21(@types/node@25.9.3) + '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) beasties: 0.4.1 browserslist: 4.28.2 esbuild: 0.27.3 @@ -3704,16 +3703,16 @@ snapshots: tslib: 2.8.1 typescript: 5.9.3 undici: 7.24.4 - vite: 7.3.2(@types/node@25.9.1)(sass@1.97.3) + vite: 7.3.2(@types/node@25.9.3)(sass@1.97.3) watchpack: 2.5.1 optionalDependencies: - '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/localize': 21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14) - '@angular/platform-browser': 21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/localize': 21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17) + '@angular/platform-browser': 21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/service-worker': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) lmdb: 3.5.1 postcss: 8.5.15 - vitest: 4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)) + vitest: 4.1.8(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -3729,15 +3728,15 @@ snapshots: - tsx - yaml - '@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0)': + '@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2102.13(chokidar@5.0.0) - '@angular-devkit/core': 21.2.13(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.13(chokidar@5.0.0) - '@inquirer/prompts': 7.10.1(@types/node@25.9.1) - '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.1))(@types/node@25.9.1)(listr2@9.0.5) + '@angular-devkit/architect': 0.2102.14(chokidar@5.0.0) + '@angular-devkit/core': 21.2.14(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.14(chokidar@5.0.0) + '@inquirer/prompts': 7.10.1(@types/node@25.9.3) + '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.3))(@types/node@25.9.3)(listr2@9.0.5) '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) - '@schematics/angular': 21.2.13(chokidar@5.0.0) + '@schematics/angular': 21.2.14(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 algoliasearch: 5.48.1 ini: 6.0.0 @@ -3755,21 +3754,21 @@ snapshots: - chokidar - supports-color - '@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3)': + '@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3)': dependencies: - '@angular/compiler': 21.2.14 + '@angular/compiler': 21.2.17 '@babel/core': 7.29.0 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 5.0.0 convert-source-map: 1.9.0 reflect-metadata: 0.2.2 - semver: 7.8.1 + semver: 7.8.4 tslib: 2.8.1 yargs: 18.0.0 optionalDependencies: @@ -3777,64 +3776,64 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/compiler@21.2.14': + '@angular/compiler@21.2.17': dependencies: tslib: 2.8.1 - '@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)': + '@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 21.2.14 + '@angular/compiler': 21.2.17 zone.js: 0.15.0 - '@angular/forms@21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': + '@angular/forms@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) '@standard-schema/spec': 1.1.0 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14)': + '@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)': dependencies: - '@angular/compiler': 21.2.14 - '@angular/compiler-cli': 21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3) + '@angular/compiler': 21.2.17 + '@angular/compiler-cli': 21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3) '@babel/core': 7.29.0 '@types/babel__core': 7.20.5 - tinyglobby: 0.2.16 + tinyglobby: 0.2.17 yargs: 18.0.0 transitivePeerDependencies: - supports-color - '@angular/platform-browser-dynamic@21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.14)(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))': + '@angular/platform-browser-dynamic@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))': dependencies: - '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/compiler': 21.2.14 - '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/compiler': 21.2.17 + '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) tslib: 2.8.1 - '@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 optionalDependencies: - '@angular/animations': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/animations': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 '@asamuzakjp/css-color@4.1.2': dependencies: '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.3(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 lru-cache: 11.5.1 @@ -3964,7 +3963,7 @@ snapshots: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-color-parser@4.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.1.3(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/color-helpers': 6.0.2 '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) @@ -3975,7 +3974,7 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.4(css-tree@3.2.1)': + '@csstools/css-syntax-patches-for-csstree@1.1.5(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 @@ -4106,7 +4105,7 @@ snapshots: globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.1 + js-yaml: 4.2.0 minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: @@ -4123,9 +4122,9 @@ snapshots: '@exodus/bytes@1.15.1': {} - '@fortawesome/angular-fontawesome@4.0.0(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))': + '@fortawesome/angular-fontawesome@4.0.0(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) '@fortawesome/fontawesome-svg-core': 7.2.0 tslib: 2.8.1 @@ -4152,9 +4151,9 @@ snapshots: '@harperfast/extended-iterable@1.0.3': optional: true - '@hono/node-server@1.19.14(hono@4.12.23)': + '@hono/node-server@1.19.14(hono@4.12.25)': dependencies: - hono: 4.12.23 + hono: 4.12.25 '@humanfs/core@0.19.2': dependencies: @@ -4174,128 +4173,128 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.2(@types/node@25.9.1)': + '@inquirer/checkbox@4.3.2(@types/node@25.9.3)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@25.9.3) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.3) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 - '@inquirer/confirm@5.1.21(@types/node@25.9.1)': + '@inquirer/confirm@5.1.21(@types/node@25.9.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@25.9.3) + '@inquirer/type': 3.0.10(@types/node@25.9.3) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 - '@inquirer/core@10.3.2(@types/node@25.9.1)': + '@inquirer/core@10.3.2(@types/node@25.9.3)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.3) 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 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 - '@inquirer/editor@4.2.23(@types/node@25.9.1)': + '@inquirer/editor@4.2.23(@types/node@25.9.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/external-editor': 1.0.3(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@25.9.3) + '@inquirer/external-editor': 1.0.3(@types/node@25.9.3) + '@inquirer/type': 3.0.10(@types/node@25.9.3) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 - '@inquirer/expand@4.0.23(@types/node@25.9.1)': + '@inquirer/expand@4.0.23(@types/node@25.9.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@25.9.3) + '@inquirer/type': 3.0.10(@types/node@25.9.3) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 - '@inquirer/external-editor@1.0.3(@types/node@25.9.1)': + '@inquirer/external-editor@1.0.3(@types/node@25.9.3)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.3.1(@types/node@25.9.1)': + '@inquirer/input@4.3.1(@types/node@25.9.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@25.9.3) + '@inquirer/type': 3.0.10(@types/node@25.9.3) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 - '@inquirer/number@3.0.23(@types/node@25.9.1)': + '@inquirer/number@3.0.23(@types/node@25.9.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@25.9.3) + '@inquirer/type': 3.0.10(@types/node@25.9.3) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 - '@inquirer/password@4.0.23(@types/node@25.9.1)': + '@inquirer/password@4.0.23(@types/node@25.9.3)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@25.9.3) + '@inquirer/type': 3.0.10(@types/node@25.9.3) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 - '@inquirer/prompts@7.10.1(@types/node@25.9.1)': + '@inquirer/prompts@7.10.1(@types/node@25.9.3)': dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@25.9.1) - '@inquirer/confirm': 5.1.21(@types/node@25.9.1) - '@inquirer/editor': 4.2.23(@types/node@25.9.1) - '@inquirer/expand': 4.0.23(@types/node@25.9.1) - '@inquirer/input': 4.3.1(@types/node@25.9.1) - '@inquirer/number': 3.0.23(@types/node@25.9.1) - '@inquirer/password': 4.0.23(@types/node@25.9.1) - '@inquirer/rawlist': 4.1.11(@types/node@25.9.1) - '@inquirer/search': 3.2.2(@types/node@25.9.1) - '@inquirer/select': 4.4.2(@types/node@25.9.1) + '@inquirer/checkbox': 4.3.2(@types/node@25.9.3) + '@inquirer/confirm': 5.1.21(@types/node@25.9.3) + '@inquirer/editor': 4.2.23(@types/node@25.9.3) + '@inquirer/expand': 4.0.23(@types/node@25.9.3) + '@inquirer/input': 4.3.1(@types/node@25.9.3) + '@inquirer/number': 3.0.23(@types/node@25.9.3) + '@inquirer/password': 4.0.23(@types/node@25.9.3) + '@inquirer/rawlist': 4.1.11(@types/node@25.9.3) + '@inquirer/search': 3.2.2(@types/node@25.9.3) + '@inquirer/select': 4.4.2(@types/node@25.9.3) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 - '@inquirer/rawlist@4.1.11(@types/node@25.9.1)': + '@inquirer/rawlist@4.1.11(@types/node@25.9.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@25.9.3) + '@inquirer/type': 3.0.10(@types/node@25.9.3) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 - '@inquirer/search@3.2.2(@types/node@25.9.1)': + '@inquirer/search@3.2.2(@types/node@25.9.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@25.9.3) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.3) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 - '@inquirer/select@4.4.2(@types/node@25.9.1)': + '@inquirer/select@4.4.2(@types/node@25.9.3)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.9.1) + '@inquirer/core': 10.3.2(@types/node@25.9.3) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/type': 3.0.10(@types/node@25.9.3) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 - '@inquirer/type@3.0.10(@types/node@25.9.1)': + '@inquirer/type@3.0.10(@types/node@25.9.3)': optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 '@isaacs/fs-minipass@4.0.1': dependencies: @@ -4322,10 +4321,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.1))(@types/node@25.9.1)(listr2@9.0.5)': + '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.3))(@types/node@25.9.3)(listr2@9.0.5)': dependencies: - '@inquirer/prompts': 7.10.1(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/prompts': 7.10.1(@types/node@25.9.3) + '@inquirer/type': 3.0.10(@types/node@25.9.3) listr2: 9.0.5 transitivePeerDependencies: - '@types/node' @@ -4353,7 +4352,7 @@ snapshots: '@modelcontextprotocol/sdk@1.26.0(zod@4.3.6)': dependencies: - '@hono/node-server': 1.19.14(hono@4.12.23) + '@hono/node-server': 1.19.14(hono@4.12.25) ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) content-type: 1.0.5 @@ -4363,7 +4362,7 @@ snapshots: eventsource-parser: 3.1.0 express: 5.2.1 express-rate-limit: 8.5.2(express@5.2.1) - hono: 4.12.23 + hono: 4.12.25 jose: 6.2.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -4463,28 +4462,28 @@ snapshots: '@napi-rs/nice-win32-x64-msvc': 1.1.1 optional: true - '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 '@tybys/wasm-util': 0.10.2 optional: true - '@ng-bootstrap/ng-bootstrap@20.0.0(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14))(@popperjs/core@2.11.8)(rxjs@7.8.2)': + '@ng-bootstrap/ng-bootstrap@20.0.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@popperjs/core@2.11.8)(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) - '@angular/localize': 21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(typescript@5.9.3))(@angular/compiler@21.2.14) + '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/localize': 21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17) '@popperjs/core': 2.11.8 rxjs: 7.8.2 tslib: 2.8.1 - '@ng-select/ng-select@21.8.2(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': + '@ng-select/ng-select@21.8.2(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': dependencies: - '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 21.2.14(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.14(@angular/animations@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) tslib: 2.8.1 '@nodelib/fs.scandir@2.1.5': @@ -4552,7 +4551,7 @@ snapshots: '@npmcli/node-gyp': 5.0.0 '@npmcli/package-json': 7.0.5 '@npmcli/promise-spawn': 9.0.1 - node-gyp: 12.3.0 + node-gyp: 12.4.0 proc-log: 6.1.0 '@oxc-project/types@0.113.0': {} @@ -4652,7 +4651,7 @@ snapshots: '@rolldown/binding-wasm32-wasi@1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -4666,85 +4665,85 @@ snapshots: '@rolldown/pluginutils@1.0.0-rc.4': {} - '@rollup/rollup-android-arm-eabi@4.60.4': + '@rollup/rollup-android-arm-eabi@4.61.1': optional: true - '@rollup/rollup-android-arm64@4.60.4': + '@rollup/rollup-android-arm64@4.61.1': optional: true - '@rollup/rollup-darwin-arm64@4.60.4': + '@rollup/rollup-darwin-arm64@4.61.1': optional: true - '@rollup/rollup-darwin-x64@4.60.4': + '@rollup/rollup-darwin-x64@4.61.1': optional: true - '@rollup/rollup-freebsd-arm64@4.60.4': + '@rollup/rollup-freebsd-arm64@4.61.1': optional: true - '@rollup/rollup-freebsd-x64@4.60.4': + '@rollup/rollup-freebsd-x64@4.61.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + '@rollup/rollup-linux-arm-gnueabihf@4.61.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.4': + '@rollup/rollup-linux-arm-musleabihf@4.61.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.4': + '@rollup/rollup-linux-arm64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.60.4': + '@rollup/rollup-linux-arm64-musl@4.61.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.4': + '@rollup/rollup-linux-loong64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-loong64-musl@4.60.4': + '@rollup/rollup-linux-loong64-musl@4.61.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.4': + '@rollup/rollup-linux-ppc64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.4': + '@rollup/rollup-linux-ppc64-musl@4.61.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.4': + '@rollup/rollup-linux-riscv64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.4': + '@rollup/rollup-linux-riscv64-musl@4.61.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.4': + '@rollup/rollup-linux-s390x-gnu@4.61.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.60.4': + '@rollup/rollup-linux-x64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-x64-musl@4.60.4': + '@rollup/rollup-linux-x64-musl@4.61.1': optional: true - '@rollup/rollup-openbsd-x64@4.60.4': + '@rollup/rollup-openbsd-x64@4.61.1': optional: true - '@rollup/rollup-openharmony-arm64@4.60.4': + '@rollup/rollup-openharmony-arm64@4.61.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.4': + '@rollup/rollup-win32-arm64-msvc@4.61.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.4': + '@rollup/rollup-win32-ia32-msvc@4.61.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.60.4': + '@rollup/rollup-win32-x64-gnu@4.61.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.60.4': + '@rollup/rollup-win32-x64-msvc@4.61.1': optional: true - '@schematics/angular@21.2.13(chokidar@5.0.0)': + '@schematics/angular@21.2.14(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.13(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.13(chokidar@5.0.0) + '@angular-devkit/core': 21.2.14(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.14(chokidar@5.0.0) jsonc-parser: 3.3.1 transitivePeerDependencies: - chokidar @@ -4825,25 +4824,23 @@ snapshots: '@types/cors@2.8.19': dependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 '@types/deep-eql@4.0.2': {} '@types/esrecurse@4.3.1': {} - '@types/estree@1.0.8': {} - '@types/estree@1.0.9': {} '@types/json-schema@7.0.15': {} - '@types/node@25.9.1': + '@types/node@25.9.3': dependencies: undici-types: 7.24.6 '@types/ws@8.18.1': dependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 '@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: @@ -4883,10 +4880,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.60.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.61.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.9.3) - '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@5.9.3) + '@typescript-eslint/types': 8.61.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -4897,16 +4894,16 @@ snapshots: '@typescript-eslint/types': 8.47.0 '@typescript-eslint/visitor-keys': 8.47.0 - '@typescript-eslint/scope-manager@8.60.0': + '@typescript-eslint/scope-manager@8.61.0': dependencies: - '@typescript-eslint/types': 8.60.0 - '@typescript-eslint/visitor-keys': 8.60.0 + '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/visitor-keys': 8.61.0 '@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.60.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.61.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 @@ -4924,7 +4921,7 @@ snapshots: '@typescript-eslint/types@8.47.0': {} - '@typescript-eslint/types@8.60.0': {} + '@typescript-eslint/types@8.61.0': {} '@typescript-eslint/typescript-estree@8.47.0(typescript@5.9.3)': dependencies: @@ -4936,22 +4933,22 @@ snapshots: fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.9 - semver: 7.8.1 + semver: 7.8.4 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.60.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.61.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.60.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.9.3) - '@typescript-eslint/types': 8.60.0 - '@typescript-eslint/visitor-keys': 8.60.0 + '@typescript-eslint/project-service': 8.61.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@5.9.3) + '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/visitor-keys': 8.61.0 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.1 - tinyglobby: 0.2.16 + semver: 7.8.4 + tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: @@ -4968,12 +4965,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3)': + '@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) - '@typescript-eslint/scope-manager': 8.60.0 - '@typescript-eslint/types': 8.60.0 - '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.61.0 + '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/typescript-estree': 8.61.0(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: @@ -4984,53 +4981,53 @@ snapshots: '@typescript-eslint/types': 8.47.0 eslint-visitor-keys: 4.2.1 - '@typescript-eslint/visitor-keys@8.60.0': + '@typescript-eslint/visitor-keys@8.61.0': dependencies: - '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/types': 8.61.0 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3))': + '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3))': dependencies: - vite: 7.3.2(@types/node@25.9.1)(sass@1.97.3) + vite: 7.3.2(@types/node@25.9.3)(sass@1.97.3) - '@vitest/expect@4.1.7': + '@vitest/expect@4.1.8': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.7 - '@vitest/utils': 4.1.7 + '@vitest/spy': 4.1.8 + '@vitest/utils': 4.1.8 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.7(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3))': + '@vitest/mocker@4.1.8(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3))': dependencies: - '@vitest/spy': 4.1.7 + '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.2(@types/node@25.9.1)(sass@1.97.3) + vite: 7.3.2(@types/node@25.9.3)(sass@1.97.3) - '@vitest/pretty-format@4.1.7': + '@vitest/pretty-format@4.1.8': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.7': + '@vitest/runner@4.1.8': dependencies: - '@vitest/utils': 4.1.7 + '@vitest/utils': 4.1.8 pathe: 2.0.3 - '@vitest/snapshot@4.1.7': + '@vitest/snapshot@4.1.8': dependencies: - '@vitest/pretty-format': 4.1.7 - '@vitest/utils': 4.1.7 + '@vitest/pretty-format': 4.1.8 + '@vitest/utils': 4.1.8 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.7': {} + '@vitest/spy@4.1.8': {} - '@vitest/utils@4.1.7': + '@vitest/utils@4.1.8': dependencies: - '@vitest/pretty-format': 4.1.7 + '@vitest/pretty-format': 4.1.8 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -5102,18 +5099,18 @@ snapshots: '@algolia/requester-fetch': 5.48.1 '@algolia/requester-node-http': 5.48.1 - angular-eslint@21.1.0(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): + angular-eslint@21.1.0(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): dependencies: - '@angular-devkit/core': 21.2.13(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.13(chokidar@5.0.0) - '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.13(@types/node@25.9.1)(chokidar@5.0.0))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-devkit/core': 21.2.14(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.14(chokidar@5.0.0) + '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.0)(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(@typescript-eslint/types@8.61.0)(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.13(@types/node@25.9.1)(chokidar@5.0.0) - '@typescript-eslint/types': 8.60.0 - '@typescript-eslint/utils': 8.60.0(eslint@9.39.4)(typescript@5.9.3) + '@angular/cli': 21.2.14(@types/node@25.9.3)(chokidar@5.0.0) + '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/utils': 8.61.0(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 typescript-eslint: 8.47.0(eslint@9.39.4)(typescript@5.9.3) @@ -5149,7 +5146,7 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.10.32: {} + baseline-browser-mapping@2.10.35: {} beasties@0.4.1: dependencies: @@ -5206,10 +5203,10 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.32 - caniuse-lite: 1.0.30001793 - electron-to-chromium: 1.5.363 - node-releases: 2.0.46 + baseline-browser-mapping: 2.10.35 + caniuse-lite: 1.0.30001797 + electron-to-chromium: 1.5.371 + node-releases: 2.0.47 update-browserslist-db: 1.2.3(browserslist@4.28.2) buffer-from@1.1.2: {} @@ -5241,7 +5238,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001793: {} + caniuse-lite@1.0.30001797: {} chai@6.2.2: {} @@ -5338,7 +5335,7 @@ snapshots: cssstyle@5.3.7: dependencies: '@asamuzakjp/css-color': 4.1.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.4(css-tree@3.2.1) + '@csstools/css-syntax-patches-for-csstree': 1.1.5(css-tree@3.2.1) css-tree: 3.2.1 lru-cache: 11.5.1 @@ -5386,7 +5383,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.363: {} + electron-to-chromium@1.5.371: {} emoji-regex@10.6.0: {} @@ -5411,7 +5408,7 @@ snapshots: engine.io@6.6.8: dependencies: '@types/cors': 2.8.19 - '@types/node': 25.9.1 + '@types/node': 25.9.3 '@types/ws': 8.18.1 accepts: 1.3.8 base64id: 2.0.0 @@ -5699,7 +5696,7 @@ snapshots: get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.3 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-proto@1.0.1: @@ -5735,11 +5732,11 @@ snapshots: has-symbols@1.1.0: {} - hasown@2.0.3: + hasown@2.0.4: dependencies: function-bind: 1.1.2 - hono@4.12.23: {} + hono@4.12.25: {} hosted-git-info@9.0.3: dependencies: @@ -5794,7 +5791,7 @@ snapshots: ignore@7.0.5: {} - immutable@5.1.5: {} + immutable@5.1.6: {} import-fresh@3.3.1: dependencies: @@ -5853,7 +5850,7 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@4.1.1: + js-yaml@4.2.0: dependencies: argparse: 2.0.1 @@ -5926,7 +5923,7 @@ snapshots: lmdb@3.5.1: dependencies: '@harperfast/extended-iterable': 1.0.3 - msgpackr: 1.11.12 + msgpackr: 1.12.1 node-addon-api: 6.1.0 node-gyp-build-optional-packages: 5.2.2 ordered-binary: 1.6.1 @@ -6078,7 +6075,7 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.4 optional: true - msgpackr@1.11.12: + msgpackr@1.12.1: optionalDependencies: msgpackr-extract: 3.0.4 optional: true @@ -6093,16 +6090,16 @@ snapshots: negotiator@1.0.0: {} - ngx-cookie-service@21.3.1(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0)): + ngx-cookie-service@21.3.1(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)): dependencies: - '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - ngx-socket-io@4.10.0(@angular/common@21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): + ngx-socket-io@4.10.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): dependencies: - '@angular/common': 21.2.14(@angular/core@21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.14(@angular/compiler@21.2.14)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) core-js: 3.49.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 @@ -6125,7 +6122,7 @@ snapshots: detect-libc: 2.1.2 optional: true - node-gyp@12.3.0: + node-gyp@12.4.0: dependencies: env-paths: 2.2.1 exponential-backoff: 3.1.3 @@ -6133,12 +6130,12 @@ snapshots: nopt: 9.0.0 proc-log: 6.1.0 semver: 7.7.4 - tar: 7.5.15 - tinyglobby: 0.2.16 + tar: 7.5.16 + tinyglobby: 0.2.17 undici: 6.26.0 which: 6.0.1 - node-releases@2.0.46: {} + node-releases@2.0.47: {} nopt@9.0.0: dependencies: @@ -6194,7 +6191,7 @@ snapshots: object-inspect@1.13.4: {} - obug@2.1.1: {} + obug@2.1.2: {} on-finished@2.4.1: dependencies: @@ -6259,7 +6256,7 @@ snapshots: promise-retry: 2.0.1 sigstore: 4.1.1 ssri: 13.0.1 - tar: 7.5.15 + tar: 7.5.16 transitivePeerDependencies: - supports-color @@ -6338,7 +6335,7 @@ snapshots: qs@6.15.2: dependencies: - side-channel: 1.1.0 + side-channel: 1.1.1 queue-microtask@1.2.3: {} @@ -6394,35 +6391,35 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - rollup@4.60.4: + rollup@4.61.1: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.4 - '@rollup/rollup-android-arm64': 4.60.4 - '@rollup/rollup-darwin-arm64': 4.60.4 - '@rollup/rollup-darwin-x64': 4.60.4 - '@rollup/rollup-freebsd-arm64': 4.60.4 - '@rollup/rollup-freebsd-x64': 4.60.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 - '@rollup/rollup-linux-arm-musleabihf': 4.60.4 - '@rollup/rollup-linux-arm64-gnu': 4.60.4 - '@rollup/rollup-linux-arm64-musl': 4.60.4 - '@rollup/rollup-linux-loong64-gnu': 4.60.4 - '@rollup/rollup-linux-loong64-musl': 4.60.4 - '@rollup/rollup-linux-ppc64-gnu': 4.60.4 - '@rollup/rollup-linux-ppc64-musl': 4.60.4 - '@rollup/rollup-linux-riscv64-gnu': 4.60.4 - '@rollup/rollup-linux-riscv64-musl': 4.60.4 - '@rollup/rollup-linux-s390x-gnu': 4.60.4 - '@rollup/rollup-linux-x64-gnu': 4.60.4 - '@rollup/rollup-linux-x64-musl': 4.60.4 - '@rollup/rollup-openbsd-x64': 4.60.4 - '@rollup/rollup-openharmony-arm64': 4.60.4 - '@rollup/rollup-win32-arm64-msvc': 4.60.4 - '@rollup/rollup-win32-ia32-msvc': 4.60.4 - '@rollup/rollup-win32-x64-gnu': 4.60.4 - '@rollup/rollup-win32-x64-msvc': 4.60.4 + '@rollup/rollup-android-arm-eabi': 4.61.1 + '@rollup/rollup-android-arm64': 4.61.1 + '@rollup/rollup-darwin-arm64': 4.61.1 + '@rollup/rollup-darwin-x64': 4.61.1 + '@rollup/rollup-freebsd-arm64': 4.61.1 + '@rollup/rollup-freebsd-x64': 4.61.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.61.1 + '@rollup/rollup-linux-arm-musleabihf': 4.61.1 + '@rollup/rollup-linux-arm64-gnu': 4.61.1 + '@rollup/rollup-linux-arm64-musl': 4.61.1 + '@rollup/rollup-linux-loong64-gnu': 4.61.1 + '@rollup/rollup-linux-loong64-musl': 4.61.1 + '@rollup/rollup-linux-ppc64-gnu': 4.61.1 + '@rollup/rollup-linux-ppc64-musl': 4.61.1 + '@rollup/rollup-linux-riscv64-gnu': 4.61.1 + '@rollup/rollup-linux-riscv64-musl': 4.61.1 + '@rollup/rollup-linux-s390x-gnu': 4.61.1 + '@rollup/rollup-linux-x64-gnu': 4.61.1 + '@rollup/rollup-linux-x64-musl': 4.61.1 + '@rollup/rollup-openbsd-x64': 4.61.1 + '@rollup/rollup-openharmony-arm64': 4.61.1 + '@rollup/rollup-win32-arm64-msvc': 4.61.1 + '@rollup/rollup-win32-ia32-msvc': 4.61.1 + '@rollup/rollup-win32-x64-gnu': 4.61.1 + '@rollup/rollup-win32-x64-msvc': 4.61.1 fsevents: 2.3.3 router@2.2.0: @@ -6448,7 +6445,7 @@ snapshots: sass@1.97.3: dependencies: chokidar: 4.0.3 - immutable: 5.1.5 + immutable: 5.1.6 source-map-js: 1.2.1 optionalDependencies: '@parcel/watcher': 2.5.6 @@ -6463,7 +6460,7 @@ snapshots: semver@7.7.4: {} - semver@7.8.1: {} + semver@7.8.4: {} send@1.2.1: dependencies: @@ -6518,7 +6515,7 @@ snapshots: object-inspect: 1.13.4 side-channel-map: 1.0.1 - side-channel@1.1.0: + side-channel@1.1.1: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 @@ -6672,7 +6669,7 @@ snapshots: symbol-tree@3.2.4: {} - tar@7.5.15: + tar@7.5.16: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 @@ -6682,25 +6679,25 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.2.2: {} + tinyexec@1.2.4: {} tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - tinyglobby@0.2.16: + tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 tinyrainbow@3.1.0: {} - tldts-core@7.4.0: {} + tldts-core@7.4.2: {} - tldts@7.4.0: + tldts@7.4.2: dependencies: - tldts-core: 7.4.0 + tldts-core: 7.4.2 to-regex-range@5.0.1: dependencies: @@ -6710,7 +6707,7 @@ snapshots: tough-cookie@6.0.1: dependencies: - tldts: 7.4.0 + tldts: 7.4.2 tr46@6.0.0: dependencies: @@ -6775,43 +6772,43 @@ snapshots: vary@1.1.2: {} - vite@7.3.2(@types/node@25.9.1)(sass@1.97.3): + vite@7.3.2(@types/node@25.9.3)(sass@1.97.3): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 postcss: 8.5.15 - rollup: 4.60.4 + rollup: 4.61.1 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 fsevents: 2.3.3 sass: 1.97.3 - vitest@4.1.7(@types/node@25.9.1)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)): + vitest@4.1.8(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)): dependencies: - '@vitest/expect': 4.1.7 - '@vitest/mocker': 4.1.7(vite@7.3.2(@types/node@25.9.1)(sass@1.97.3)) - '@vitest/pretty-format': 4.1.7 - '@vitest/runner': 4.1.7 - '@vitest/snapshot': 4.1.7 - '@vitest/spy': 4.1.7 - '@vitest/utils': 4.1.7 + '@vitest/expect': 4.1.8 + '@vitest/mocker': 4.1.8(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) + '@vitest/pretty-format': 4.1.8 + '@vitest/runner': 4.1.8 + '@vitest/snapshot': 4.1.8 + '@vitest/spy': 4.1.8 + '@vitest/utils': 4.1.8 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 - obug: 2.1.1 + obug: 2.1.2 pathe: 2.0.3 picomatch: 4.0.4 std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 1.2.2 - tinyglobby: 0.2.16 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 7.3.2(@types/node@25.9.1)(sass@1.97.3) + vite: 7.3.2(@types/node@25.9.3)(sass@1.97.3) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 25.9.3 jsdom: 27.4.0 transitivePeerDependencies: - msw diff --git a/uv.lock b/uv.lock index 1a1b236..f10d089 100644 --- a/uv.lock +++ b/uv.lock @@ -13,7 +13,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.13.5" +version = "3.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -24,59 +24,72 @@ dependencies = [ { name = "propcache" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } +sdist = { url = "https://files.pythonhosted.org/packages/82/78/8ea7308cac6934de8c74a14f3d5f65d1c89287426688be79538d0e5c013d/aiohttp-3.14.1.tar.gz", hash = "sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035", size = 7955794, upload-time = "2026-06-07T21:09:35.529Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" }, - { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" }, - { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" }, - { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" }, - { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" }, - { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" }, - { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" }, - { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" }, - { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" }, - { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" }, - { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" }, - { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" }, - { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" }, - { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" }, - { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" }, - { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" }, - { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, - { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, - { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, - { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, - { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, - { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, - { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, - { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, - { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, - { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, - { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, - { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, - { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, - { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, - { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, - { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, - { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, - { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, - { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, - { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, - { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, - { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, - { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, - { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, - { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, - { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, - { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, - { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, - { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, - { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, - { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, - { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, - { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, - { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, + { url = "https://files.pythonhosted.org/packages/bc/97/bd137012dd97e1649162b099135a80e1fd59aaa807b2430fc448d1029aff/aiohttp-3.14.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:b3a03285a7f9c7b016324574a6d92a1c895da6b978cb8f1deee3ac72bc6da178", size = 506882, upload-time = "2026-06-07T21:07:15.501Z" }, + { url = "https://files.pythonhosted.org/packages/ef/79/e5cc690e9d922a66887ceeaca53a8ffd5a7b0be3816142b7abc433742d89/aiohttp-3.14.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:2a73f487ab8ef5abbb24b7aa9b73e98eaba9e9e031804ff2416f02eca315ccaf", size = 515270, upload-time = "2026-06-07T21:07:17.53Z" }, + { url = "https://files.pythonhosted.org/packages/fe/22/a73ccbf9dbd6e26dda0b24d5fd5db7da92ee3383a79f47677ffb834c5c5b/aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:915fbb7b41b115192259f8c9ae58f3ddc444d2b5579917270211858e606a4afd", size = 485841, upload-time = "2026-06-07T21:07:19.555Z" }, + { url = "https://files.pythonhosted.org/packages/3b/b9/57ed8eaf596321c2ad747bd480fb1700dbd7177c60dfc9e4c187f629662e/aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:7fb4bdf95b0561a79f259f9d28fbc109728c5ee7f27aff6391f0ca703a329abe", size = 492088, upload-time = "2026-06-07T21:07:21.581Z" }, + { url = "https://files.pythonhosted.org/packages/78/c0/5ebe5270a7c140d7c6f79dcb018640225f14d406c149e4eec04a7d82fe71/aiohttp-3.14.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1b9748363260121d2927704f5d4fc498150669ca3ae93625986ee89c8f80dcd4", size = 501564, upload-time = "2026-06-07T21:07:23.388Z" }, + { url = "https://files.pythonhosted.org/packages/75/7f/8cdaa24fc7983865e0915153b96a9ac5bcdd3548d64c5a27d17cecccad2d/aiohttp-3.14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:86a6dab78b0e43e2897a3bbe15745aa60dc5423ca437b7b0b164c069bf91b876", size = 751998, upload-time = "2026-06-07T21:07:25.046Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f4/c4227aacfacc5cb0cc2d119b65301d177912a6842cd64e120c47af76064f/aiohttp-3.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dfd6e47d3c44c2279907607f73a4240b88c69eb8b90da7e2441a8045dfd21da", size = 510918, upload-time = "2026-06-07T21:07:27.28Z" }, + { url = "https://files.pythonhosted.org/packages/ab/01/a2d5f96cd4e74424864d30bc0a7e44d0a12dacdcfa91b5b2d1bd3dca6bf3/aiohttp-3.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:317acd9f8602858dc7d59679812c376c7f0b97bcbbf16e0d6237f54141d8a8a6", size = 508657, upload-time = "2026-06-07T21:07:29.252Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ed/3c0fb5c500fdd8e7ebc10d1889c04384fffa1a9163eac1356088ca9da1b1/aiohttp-3.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd869c427324e5cb15195793de951295710db28be7d818247f3097b4ab5d4b96", size = 1757907, upload-time = "2026-06-07T21:07:31.03Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ab/d4c924d9bd5be3050c226612413ce68cb54c70d2c31b661bfc8d9a5b6a70/aiohttp-3.14.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93b032b5ec3255473c143627d21a69ac74ae12f7f33974cb587c564d11b1066f", size = 1737565, upload-time = "2026-06-07T21:07:33.031Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/37326821ff779084020cdc33224d20b19f42f4183a500ff92022a739eda7/aiohttp-3.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f234b4deb12f3ad59127e037bc57c40c21e45b45282df7d3a55a0f409f595296", size = 1799018, upload-time = "2026-06-07T21:07:35.003Z" }, + { url = "https://files.pythonhosted.org/packages/b3/4f/6e947ba73e4ce09070761c05ed3a8ceb7c21f5e46798671d8b2aac0e4626/aiohttp-3.14.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9af6779bfb46abf124068327abcdf9ce95c9ef8287a3e8da76ccf2d0f16c28fa", size = 1894416, upload-time = "2026-06-07T21:07:36.956Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6e/dbf1d0625dc711fb2851f4f3c3055c39ed58bae92082d8c627dbe6013736/aiohttp-3.14.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:faccab372e66bc76d5731525e7f1143c922271725b9d38c9f97edcc66266b451", size = 1783881, upload-time = "2026-06-07T21:07:39.063Z" }, + { url = "https://files.pythonhosted.org/packages/44/c2/5e25098a67268ed369483ae7d1a58bd0a13d03aab860d2a0e4a6eb25b046/aiohttp-3.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f380468b09d2a81633ee863b0ec5648d364bd17bb8ecfb8c2f387f7ac1faf42c", size = 1587572, upload-time = "2026-06-07T21:07:41.058Z" }, + { url = "https://files.pythonhosted.org/packages/2a/bd/cf9cee17e140f942a3de73e658a543aa8fbf35a5fc67a9d2538d52d77f0b/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:97e704dcd26271f5bda3fa07c3ce0fb76d6d3f8659f4baa1a24442cc9ba177ca", size = 1722137, upload-time = "2026-06-07T21:07:43.014Z" }, + { url = "https://files.pythonhosted.org/packages/89/6d/5684f8c59045c96f81a18cefbc1fbbd79d25b88f1c622f2a5c5c08fcb632/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:269b76ac5394092b95bc4a098f4fc6c191c083c3bd12775d1e30e663132f6a09", size = 1755953, upload-time = "2026-06-07T21:07:45.933Z" }, + { url = "https://files.pythonhosted.org/packages/a8/40/35caf3170f8359760740a7d9aa0fff2e344bef98e1d1186f5a0f6dec17e6/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0b3e614340c889d575451696374c9d17affd54cd607ca0babed8f8c37b9397", size = 1766479, upload-time = "2026-06-07T21:07:48.047Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a1/b0c61e7a137f0d81de49a82023a6df73c3c16d6fefb0f8e4a93d21639002/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5663ee9257cfa1add7253a7da3035a02f31b6600ec48261585e1800a81533080", size = 1580077, upload-time = "2026-06-07T21:07:50.069Z" }, + { url = "https://files.pythonhosted.org/packages/0b/41/194ea4623693009fcefebef7aef63c141754f153e9cd0d39d3b9e36c175c/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:603a2c834142172ffddc054067f5ec0ca65d57a0aa98a71bc81952573208e345", size = 1791688, upload-time = "2026-06-07T21:07:52.106Z" }, + { url = "https://files.pythonhosted.org/packages/ba/45/4de841f005cfe1fd63e2a2fe011262c515e2a62aa6994b15947e7d717ac9/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cb21957bb8aca671c1765e32f58164cf0c50e6bf41c0bbbd16da20732ecaf588", size = 1761094, upload-time = "2026-06-07T21:07:54.113Z" }, + { url = "https://files.pythonhosted.org/packages/e4/ae/dbce10533d3896d544d5053939ed75b7dc31a1b0973d959b1b5ae21028d6/aiohttp-3.14.1-cp313-cp313-win32.whl", hash = "sha256:e509a55f681e6158c20f70f102f9cf61fb20fbc382272bc6d94b7343f2582780", size = 452662, upload-time = "2026-06-07T21:07:56.06Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d9/0bf1a19362c32f06229da5e7ddfcec91f93474d6307f7a2d3135e9c674dc/aiohttp-3.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:1ac8531b638959718e18c2207fbfe297819875da46a740b29dfa29beba64355a", size = 479748, upload-time = "2026-06-07T21:07:58.319Z" }, + { url = "https://files.pythonhosted.org/packages/22/0a/62e7232dc9484fbec112ceb32efb6a624cc7994ec6e2b019286f17c4e8f2/aiohttp-3.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:250d14af67f6b6a1a4a811049b1afa69d61d617fca6bf33149b3ab1a6dbcf7b8", size = 447723, upload-time = "2026-06-07T21:08:00.154Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a1/5fafa04e1ca91ddb47608699d60649c1c6db3cf41c99e78fc4056f9513db/aiohttp-3.14.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:7c106c26852ca1c2047c6b80384f17100b4e439af276f21ef3d4e2f450ae7e15", size = 508531, upload-time = "2026-06-07T21:08:02.093Z" }, + { url = "https://files.pythonhosted.org/packages/fa/2e/bfa02f699d87ffc86d5959270b28f1cb410add3ccaced8ed2e0b8a5238fc/aiohttp-3.14.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:20205f7f5ade7aaec9f4b500549bbc071b046453aed72f9c06dcab87896a83e8", size = 514718, upload-time = "2026-06-07T21:08:04.476Z" }, + { url = "https://files.pythonhosted.org/packages/85/a5/9594ad6289eebbc97d167c44213d557807f90e59115caad24de21ad2c3b1/aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:62a759436b29e677181a9e76bab8b8f689a29cb9c535f45f7c48c9c830d3f8c3", size = 487918, upload-time = "2026-06-07T21:08:06.377Z" }, + { url = "https://files.pythonhosted.org/packages/b4/61/16a32c36c3c49edec122a3dc811f2057df2f94d3b14aa107c8017d981618/aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2964cbf553df4d7a57348da44d961d871895fc1ee4e8c322b2a95612c7b17fba", size = 494014, upload-time = "2026-06-07T21:08:08.263Z" }, + { url = "https://files.pythonhosted.org/packages/9b/89/3ebcf96ed99c05bec9c434aaac6963fd3cbab4a786ae739908a144d9ce44/aiohttp-3.14.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:237651caadc3a59badd39319c54642b5299e9cc98a3a194310e55d5bb9f5e397", size = 502398, upload-time = "2026-06-07T21:08:10.244Z" }, + { url = "https://files.pythonhosted.org/packages/fd/3d/b74870a0c2d40c355928cd5b96c7a11fa821b8a40fc41365e64479b151fb/aiohttp-3.14.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:896e12dfdbbab9d8f7e16d2b28c6769a60126fa92095d1ebf9473d02593a2448", size = 758018, upload-time = "2026-06-07T21:08:12.447Z" }, + { url = "https://files.pythonhosted.org/packages/d3/66/f42f5c984d99e49c6cff5f26f590750f2e2f7ef1fcfb99966ab5be1b632e/aiohttp-3.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d03f281ed22579314ba00821ce20115a7c0ac430660b4cc05704a3f818b3e004", size = 512462, upload-time = "2026-06-07T21:08:14.624Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a7/248e1aebe0c7810b0271e021a0f2a5eb6e78a051885b3c9df49f42a5802d/aiohttp-3.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:07eabb979d236335fed927e137a928c9adfb7df3b9ec7aa31726f133a62be983", size = 512824, upload-time = "2026-06-07T21:08:16.572Z" }, + { url = "https://files.pythonhosted.org/packages/26/97/2aa0e5ba0727dc3bd5aaebb7ccbc510f7dfb7fb961ec87497cd496635ab1/aiohttp-3.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4fe1f1087cbadb280b5e1bb054a4f00d1423c74d6626c5e48400d871d34ecefe", size = 1749898, upload-time = "2026-06-07T21:08:18.635Z" }, + { url = "https://files.pythonhosted.org/packages/00/8d/e97f6c96c891d457c8479d92a514ba194d0412f981d72c70341ee18488ed/aiohttp-3.14.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:367a9314fdc79dab0fac96e216cb41dd73c85bdca85306ce8999118ba7e0f333", size = 1710114, upload-time = "2026-06-07T21:08:20.892Z" }, + { url = "https://files.pythonhosted.org/packages/6f/e6/aa8d7e863048c8fceb5cd6ce74017311cec3ead07847387e12265fb4444e/aiohttp-3.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a24f677ebe83749039e7bdf862ff0bbb16818ae4193d4ef96505e269375bcce0", size = 1802541, upload-time = "2026-06-07T21:08:23.044Z" }, + { url = "https://files.pythonhosted.org/packages/83/a8/72193137de57fda4ebfae4563182d082c8856e3b6e9871d0b46f028fb369/aiohttp-3.14.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c83afe0ba876be7e943d2e0ba645809ad441575d2840c895c21ee5de93b9377a", size = 1875776, upload-time = "2026-06-07T21:08:25.288Z" }, + { url = "https://files.pythonhosted.org/packages/a0/18/938441025db6769a3464596b2410af3afde0b21eb2f204c6f766f68af4bd/aiohttp-3.14.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:634e385930fb6d2d479cf3aa66515955863b77a5e3c2b5894ca259a25b308602", size = 1760329, upload-time = "2026-06-07T21:08:27.363Z" }, + { url = "https://files.pythonhosted.org/packages/60/29/bf2496b4065e76e09fe48015aaffe5ce161d8f089b06ac6982070f653076/aiohttp-3.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeea07c4397bbc57719c4eed8f9c284874d4f175f9b6d57f7a1546b976d455ca", size = 1587293, upload-time = "2026-06-07T21:08:29.805Z" }, + { url = "https://files.pythonhosted.org/packages/49/a2/2136674d52123b1354bd05dd5753c318db47dc0c927cc70b27bab3755456/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:335c0cc3e3545ce98dcb9cfcb836f40c3411f43fa03dab757597d80c89af8a35", size = 1714756, upload-time = "2026-06-07T21:08:32.094Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b9/e5fd2e6f915503081c0f9b1e8540947037929c70c191da2e4d54b31a21a1/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ae6be797afdef264e8a84864a85b196ca06045586481b3df8a967322fd2fa844", size = 1721052, upload-time = "2026-06-07T21:08:34.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/5a/2833e324a2263e104e31e2e91bc5bbee81bc499afd32203faee048a883f0/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:8560b4d712474335d08907db7973f71912d3a9a8f1dee992ec06b5d2fe359496", size = 1766888, upload-time = "2026-06-07T21:08:36.95Z" }, + { url = "https://files.pythonhosted.org/packages/57/fa/dea6511870913162f3b2e8c42a7614eb203a4540b8c2da43e0bfb0548f3c/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7edd08e0a5deb1e8564a2fcd8f4561014a3f05252334671bbf55ddd47db0e5", size = 1581679, upload-time = "2026-06-07T21:08:39.292Z" }, + { url = "https://files.pythonhosted.org/packages/14/bd/3cf0d55e71784b33534e9710a67d382d900598b4787fbce6cc7317f8c42a/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:b6ff7fcee63287ae57b5df3e4f5957ce032122802509246dec1a5bcc55904c95", size = 1782021, upload-time = "2026-06-07T21:08:41.407Z" }, + { url = "https://files.pythonhosted.org/packages/c1/af/14bb5843eccbe234f4dfb78ab73e549d99727247e62ae5d62cbd22eaf5b0/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6ffbb2f4ec1ceaff7e07d43922954da26b223d188bf30658e561b98e23089444", size = 1742574, upload-time = "2026-06-07T21:08:43.795Z" }, + { url = "https://files.pythonhosted.org/packages/f2/1e/fbeb7af9210a67ac0f9c9bec0f8f4568497924e33137a3d5b48e1cf85f3f/aiohttp-3.14.1-cp314-cp314-win32.whl", hash = "sha256:a9875b46d910cff3ea2f5962f9d266b465459fe634e22556ab9bd6fc1192eea0", size = 457773, upload-time = "2026-06-07T21:08:46.168Z" }, + { url = "https://files.pythonhosted.org/packages/f0/2b/13e8d741a9ec5db7d900c060554cf8352ab85e44e2a4469ebb9d377bda17/aiohttp-3.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:af8b4b81a960eeaf1234971ac3cd0ba5901f3cd42eae42a46b4d089a8b492719", size = 485001, upload-time = "2026-06-07T21:08:48.401Z" }, + { url = "https://files.pythonhosted.org/packages/df/30/491acfa2c4d6c3ff59c49a14fc1b50be3241e25bbb0c84c09e2da4d11395/aiohttp-3.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:cf4491381b1b57425c315a56a439251b1bdac07b2275f19a8c44bc57744532ec", size = 453809, upload-time = "2026-06-07T21:08:50.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/e3/19dbe1a1f4cc6230eb9e314de7fe68053b0992f9302b27d12141a0b5db53/aiohttp-3.14.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:819c054312f1af92947e6a55883d1b66feefab11531a7fc45e0fb9b63880b5c2", size = 793320, upload-time = "2026-06-07T21:08:52.775Z" }, + { url = "https://files.pythonhosted.org/packages/7f/20/1b7182219ba1b108430d6e4dc53d25ae02dcfcf5a045b33af4e8c5167527/aiohttp-3.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10ee9c1753a8f706345b22496c79fbddb5be0599e0823f3738b1534058e25340", size = 529077, upload-time = "2026-06-07T21:08:55Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c8/14ce60ec31a2e5f5274bb17d383a6f7a3aabca31ac04eee05585bbadab16/aiohttp-3.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1601cc37baf5750ccacae618ec2daf020769581695550e3b654a911f859c563d", size = 532476, upload-time = "2026-06-07T21:08:57.176Z" }, + { url = "https://files.pythonhosted.org/packages/7e/02/9ac85e081e53da2e061b02fa7758fe0a12d17b8ce2d1f5e6c7cb76730328/aiohttp-3.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d6e0ac9da31c9c04c84e1c0182ad8d6df35965a85cae29cd71d089621b3ae94", size = 1922347, upload-time = "2026-06-07T21:08:59.563Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3e/d3ba07a0ab38b5389e10bec4362d21e10a4f667cba2d79ba30837b3a5059/aiohttp-3.14.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e8f2d660c350b3d0e259c7a7e3d9b7fc8b41210cbcc3d4a7076ff0a5e5c2fdc", size = 1786465, upload-time = "2026-06-07T21:09:01.909Z" }, + { url = "https://files.pythonhosted.org/packages/0b/cb/e2ee978a00cfb2df829704a69528b18154eba5939f45bc1efa8f33aee4c5/aiohttp-3.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4691802dda97be727f79d86818acaad7eb8e9252626a1d6b519fedbb92d5e251", size = 1909423, upload-time = "2026-06-07T21:09:04.357Z" }, + { url = "https://files.pythonhosted.org/packages/73/5d/1430334858b1022b58ae50399a918f0bd6fe8fa7fa183598d657ff61e040/aiohttp-3.14.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c389c482a7e9b9dc3ee2701ac46c4125297a3818875b9c305ddb603c04828fd1", size = 2001906, upload-time = "2026-06-07T21:09:06.722Z" }, + { url = "https://files.pythonhosted.org/packages/66/4e/560c7472d3d198a23aa5c8b19a5115bf6a9b77b7d3e4bb363da320430ad2/aiohttp-3.14.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc0cacab7ba4e56f0f81c82a98c09bed2f39c940107b03a34b168bdf7597edd3", size = 1877095, upload-time = "2026-06-07T21:09:09.011Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f1/4745806578d447db4a784a8591e2dae3afdfc2bcb96f8f81271b13df6543/aiohttp-3.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:979ed4717f59b8bb12e3963378fa285d93d367e15bcd66c721311826d3c44a6c", size = 1676222, upload-time = "2026-06-07T21:09:11.461Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c9/48255813cca749a229ef0ab476004ec623728ad79a9c0840616f6c076325/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:38e1e7daaea81df51c952e18483f323d878499a1e2bfe564790e0f9701d6f203", size = 1842922, upload-time = "2026-06-07T21:09:14.118Z" }, + { url = "https://files.pythonhosted.org/packages/3d/c0/bbd054e2bee909f529523a5af3891052606af5143c09f5f183ec3b234676/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:4132e72c608fe9fecb8f409113567605915b83e9bdd3ea56538d2f9cd35002f1", size = 1825035, upload-time = "2026-06-07T21:09:16.447Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ae/90395d4376deceb74e09ec26b6adf7d2015a6f8802d6d84446af860fef04/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:eefd9cc9b6d4a2db5f00a26bc3e4f9acf71926a6ec557cd56c9c6f27c290b665", size = 1849512, upload-time = "2026-06-07T21:09:18.742Z" }, + { url = "https://files.pythonhosted.org/packages/93/bd/fb25f3049957553d4ce0ba6ae480aa2f592a6985497fca590837d16c1be0/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b165790117eea512d7f3fb22f1f6dad3d55a7189571993eb015591c1401276d1", size = 1668571, upload-time = "2026-06-07T21:09:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/3f/22/7f73303d64dd567ff3addca90b556690ed1233a47b8f55d242fb90af3681/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ed09c7eb1c391271c2ed0314a51903e72a3acb653d5ccfc264cdf3ef11f8269d", size = 1881159, upload-time = "2026-06-07T21:09:23.813Z" }, + { url = "https://files.pythonhosted.org/packages/44/be/0474c5a8b5640e1e4aa1923430a91f4151be82e511373fe764189b89aef5/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:99abd37084b82f5830c635fddd0b4993b9742a66eb746dacf433c8590e8f9e3c", size = 1841409, upload-time = "2026-06-07T21:09:26.207Z" }, + { url = "https://files.pythonhosted.org/packages/7b/3c/bb4a7cba26956cb3da4553cc2056cf67be5b5ff6e6d8fa4fbdff73bfb7ae/aiohttp-3.14.1-cp314-cp314t-win32.whl", hash = "sha256:47ddf841cdecc810749921d25606dee45857d12d2ad5ddb7b5bd7eab12e4b365", size = 494166, upload-time = "2026-06-07T21:09:28.505Z" }, + { url = "https://files.pythonhosted.org/packages/8a/84/ec80c2c1f66a952555a9f86df6b33af65108a6febfa0471b69013a12f807/aiohttp-3.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5e78b522b7a6e27e0b25d19b247b75039ac4c94f99823e3c9e53ae1603a9f7e9", size = 530255, upload-time = "2026-06-07T21:09:30.843Z" }, + { url = "https://files.pythonhosted.org/packages/2a/71/6e22be134a4061ada85a92951b842f2657f17d926b727f3f94c56ae963d6/aiohttp-3.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:90d53f1609c29ccc2193945ef732428382a28f78d0456ae4d3daf0d48b74f0f6", size = 469640, upload-time = "2026-06-07T21:09:33.028Z" }, ] [[package]] @@ -301,38 +314,48 @@ wheels = [ [[package]] name = "curl-cffi" -version = "0.14.0" +version = "0.15.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "certifi" }, { name = "cffi" }, + { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9b/c9/0067d9a25ed4592b022d4558157fcdb6e123516083700786d38091688767/curl_cffi-0.14.0.tar.gz", hash = "sha256:5ffbc82e59f05008ec08ea432f0e535418823cda44178ee518906a54f27a5f0f", size = 162633, upload-time = "2025-12-16T03:25:07.931Z" } +sdist = { url = "https://files.pythonhosted.org/packages/48/5b/89fcfebd3e5e85134147ac99e9f2b2271165fd4d71984fc65da5f17819b7/curl_cffi-0.15.0.tar.gz", hash = "sha256:ea0c67652bf6893d34ee0f82c944f37e488f6147e9421bef1771cc6545b02ded", size = 196437, upload-time = "2026-04-03T11:12:31.525Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/aa/f0/0f21e9688eaac85e705537b3a87a5588d0cefb2f09d83e83e0e8be93aa99/curl_cffi-0.14.0-cp39-abi3-macosx_14_0_arm64.whl", hash = "sha256:e35e89c6a69872f9749d6d5fda642ed4fc159619329e99d577d0104c9aad5893", size = 3087277, upload-time = "2025-12-16T03:24:49.607Z" }, - { url = "https://files.pythonhosted.org/packages/ba/a3/0419bd48fce5b145cb6a2344c6ac17efa588f5b0061f212c88e0723da026/curl_cffi-0.14.0-cp39-abi3-macosx_15_0_x86_64.whl", hash = "sha256:5945478cd28ad7dfb5c54473bcfb6743ee1d66554d57951fdf8fc0e7d8cf4e45", size = 5804650, upload-time = "2025-12-16T03:24:51.518Z" }, - { url = "https://files.pythonhosted.org/packages/e2/07/a238dd062b7841b8caa2fa8a359eb997147ff3161288f0dd46654d898b4d/curl_cffi-0.14.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c42e8fa3c667db9ccd2e696ee47adcd3cd5b0838d7282f3fc45f6c0ef3cfdfa7", size = 8231918, upload-time = "2025-12-16T03:24:52.862Z" }, - { url = "https://files.pythonhosted.org/packages/7c/d2/ce907c9b37b5caf76ac08db40cc4ce3d9f94c5500db68a195af3513eacbc/curl_cffi-0.14.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:060fe2c99c41d3cb7f894de318ddf4b0301b08dca70453d769bd4e74b36b8483", size = 8654624, upload-time = "2025-12-16T03:24:54.579Z" }, - { url = "https://files.pythonhosted.org/packages/f2/ae/6256995b18c75e6ef76b30753a5109e786813aa79088b27c8eabb1ef85c9/curl_cffi-0.14.0-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b158c41a25388690dd0d40b5bc38d1e0f512135f17fdb8029868cbc1993d2e5b", size = 8010654, upload-time = "2025-12-16T03:24:56.507Z" }, - { url = "https://files.pythonhosted.org/packages/fb/10/ff64249e516b103cb762e0a9dca3ee0f04cf25e2a1d5d9838e0f1273d071/curl_cffi-0.14.0-cp39-abi3-manylinux_2_28_i686.whl", hash = "sha256:1439fbef3500fb723333c826adf0efb0e2e5065a703fb5eccce637a2250db34a", size = 7781969, upload-time = "2025-12-16T03:24:57.885Z" }, - { url = "https://files.pythonhosted.org/packages/51/76/d6f7bb76c2d12811aa7ff16f5e17b678abdd1b357b9a8ac56310ceccabd5/curl_cffi-0.14.0-cp39-abi3-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e7176f2c2d22b542e3cf261072a81deb018cfa7688930f95dddef215caddb469", size = 7969133, upload-time = "2025-12-16T03:24:59.261Z" }, - { url = "https://files.pythonhosted.org/packages/23/7c/cca39c0ed4e1772613d3cba13091c0e9d3b89365e84b9bf9838259a3cd8f/curl_cffi-0.14.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:03f21ade2d72978c2bb8670e9b6de5260e2755092b02d94b70b906813662998d", size = 9080167, upload-time = "2025-12-16T03:25:00.946Z" }, - { url = "https://files.pythonhosted.org/packages/75/03/a942d7119d3e8911094d157598ae0169b1c6ca1bd3f27d7991b279bcc45b/curl_cffi-0.14.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:58ebf02de64ee5c95613209ddacb014c2d2f86298d7080c0a1c12ed876ee0690", size = 9520464, upload-time = "2025-12-16T03:25:02.922Z" }, - { url = "https://files.pythonhosted.org/packages/a2/77/78900e9b0833066d2274bda75cba426fdb4cef7fbf6a4f6a6ca447607bec/curl_cffi-0.14.0-cp39-abi3-win_amd64.whl", hash = "sha256:6e503f9a103f6ae7acfb3890c843b53ec030785a22ae7682a22cc43afb94123e", size = 1677416, upload-time = "2025-12-16T03:25:04.902Z" }, - { url = "https://files.pythonhosted.org/packages/5c/7c/d2ba86b0b3e1e2830bd94163d047de122c69a8df03c5c7c36326c456ad82/curl_cffi-0.14.0-cp39-abi3-win_arm64.whl", hash = "sha256:2eed50a969201605c863c4c31269dfc3e0da52916086ac54553cfa353022425c", size = 1425067, upload-time = "2025-12-16T03:25:06.454Z" }, + { url = "https://files.pythonhosted.org/packages/5e/42/54ddd442c795f30ce5dd4e49f87ce77505958d3777cd96a91567a3975d2a/curl_cffi-0.15.0-cp310-abi3-macosx_10_9_x86_64.whl", hash = "sha256:bda66404010e9ed743b1b83c20c86f24fe21a9a6873e17479d6e67e29d8ded28", size = 2795267, upload-time = "2026-04-03T11:11:46.48Z" }, + { url = "https://files.pythonhosted.org/packages/83/2d/3915e238579b3c5a92cead5c79130c3b8d20caaba7616cc4d894650e1d6b/curl_cffi-0.15.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:a25620d9bf989c9c029a7d1642999c4c265abb0bad811deb2f77b0b5b2b12e5b", size = 2573544, upload-time = "2026-04-03T11:11:47.951Z" }, + { url = "https://files.pythonhosted.org/packages/2a/b3/9d2f1057749a1b07ba1989db3c1503ce8bed998310bae9aea2c43aa64f20/curl_cffi-0.15.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:582e570aa2586b96ed47cf4a17586b9a3c462cbe43f780487c3dc245c6ef1527", size = 10515369, upload-time = "2026-04-03T11:11:50.126Z" }, + { url = "https://files.pythonhosted.org/packages/b5/1d/6d10dded5ce3fd8157e558ebd97d09e551b77a62cdc1c31e93d0a633cee5/curl_cffi-0.15.0-cp310-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:838e48212447d9c81364b04707a5c861daf08f8320f9ecb3406a8919d1d5c3b3", size = 10160045, upload-time = "2026-04-03T11:11:52.664Z" }, + { url = "https://files.pythonhosted.org/packages/5c/12/c70b835487ace3b9ba1502631912e3440082b8ae3a162f60b59cb0b6444d/curl_cffi-0.15.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2b6c847d86283b07ae69bb72c82eb8a59242277142aa35b89850f89e792a02fc", size = 11090433, upload-time = "2026-04-03T11:11:55.049Z" }, + { url = "https://files.pythonhosted.org/packages/ea/0d/78edcc4f71934225db99df68197a107386d59080742fc7bf6bb4d007924f/curl_cffi-0.15.0-cp310-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e5e69eee735f659287e2c84444319d68a1fa68dd37abf228943a4074864283a", size = 10479178, upload-time = "2026-04-03T11:11:57.685Z" }, + { url = "https://files.pythonhosted.org/packages/5b/84/1e101c1acb1ea2f0b4992f5c3024f596d8e21db0d53540b9d583f673c4e7/curl_cffi-0.15.0-cp310-abi3-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa1323950224db24f4c510d010b3affa02196ca853fb424191fa917a513d3f4b", size = 10317051, upload-time = "2026-04-03T11:12:00.295Z" }, + { url = "https://files.pythonhosted.org/packages/28/42/8ef236b22a6c23d096c85a1dc507efe37bfdfc7a2f8a4b34efb590197369/curl_cffi-0.15.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:41f80170ba844009273b2660da1964ec31e99e5719d16b3422ada87177e32e13", size = 11299660, upload-time = "2026-04-03T11:12:02.791Z" }, + { url = "https://files.pythonhosted.org/packages/1d/01/56aeb055d962da87a1be0d74c6c644e251c7e88129b5471dc44ac724e678/curl_cffi-0.15.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1977e1e12cfb5c11352cbb74acef1bed24eb7d226dab61ca57c168c21acd4d61", size = 11945049, upload-time = "2026-04-03T11:12:05.912Z" }, + { url = "https://files.pythonhosted.org/packages/d8/8c/2abf99a38d6340d66cf0557e0c750ef3f8883dfc5d450087e01c85861343/curl_cffi-0.15.0-cp310-abi3-win_amd64.whl", hash = "sha256:5a0c1896a0d5a5ac1eb89cd24b008d2b718dd1df6fd2f75451b59ca66e49e572", size = 1661649, upload-time = "2026-04-03T11:12:07.948Z" }, + { url = "https://files.pythonhosted.org/packages/3d/39/dfd54f2240d3a9b96d77bacc62b97813b35e2aa8ecf5cd5013c683f1ba96/curl_cffi-0.15.0-cp310-abi3-win_arm64.whl", hash = "sha256:a6d57f8389273a3a1f94370473c74897467bcc36af0a17336989780c507fa43d", size = 1410741, upload-time = "2026-04-03T11:12:10.073Z" }, + { url = "https://files.pythonhosted.org/packages/19/6a/c24df8a4fc22fa84070dcd94abeba43c15e08cc09e35869565c0bad196fd/curl_cffi-0.15.0-cp313-abi3-android_24_arm64_v8a.whl", hash = "sha256:4682dc38d4336e0eb0b185374db90a760efde63cbea994b4e63f3521d44c4c92", size = 7190427, upload-time = "2026-04-03T11:12:12.142Z" }, + { url = "https://files.pythonhosted.org/packages/11/56/132225cb3491d07cc6adcce5fe395e059bde87c68cff1ef87a31c88c7819/curl_cffi-0.15.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:967ad7355bd8e9586f8c2d02eaa99953747549e7ea4a9b25cd53353e6b67fe6d", size = 2795723, upload-time = "2026-04-03T11:12:13.668Z" }, + { url = "https://files.pythonhosted.org/packages/07/8f/f4f83cd303bef7e8f1749512e5dd157e7e5d08b0a36c8211f9640a2757bf/curl_cffi-0.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7e63539d0d839d0a8c5eacf86229bc68c57803547f35e0db7ee0986328b478c3", size = 2573739, upload-time = "2026-04-03T11:12:15.08Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5c/643d65c7fc9acd742876aa55c2d7823c438cb7665810acd2e66c9976c4d9/curl_cffi-0.15.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:08c799b89740b9bc49c09fbc3d5907f13ac1f845ca52620507ef9466d4639dd5", size = 10521046, upload-time = "2026-04-03T11:12:17.034Z" }, + { url = "https://files.pythonhosted.org/packages/7f/0b/9b8037113c93f4c5323096163471fa7c35c7676c3f608eeaf1287cd99d58/curl_cffi-0.15.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7b7a92767a888ee90147e18964b396d8435ff42737030d6fb00824ffd6094805", size = 11096115, upload-time = "2026-04-03T11:12:19.694Z" }, + { url = "https://files.pythonhosted.org/packages/5f/96/fff2fcbd924ef4042e0d67379f751a8a4e3186a91e75e35a4cf218b306ee/curl_cffi-0.15.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:829cc357061ecb99cc2d406301f609a039e05665322f5c025ec67c38b0dc49ce", size = 11305346, upload-time = "2026-04-03T11:12:22.151Z" }, + { url = "https://files.pythonhosted.org/packages/53/1b/304b253a45ab28691c8c5e8cca1e6cbb9cf8e46dfceae4648dd536f75e73/curl_cffi-0.15.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:408d6f14e346841cd889c2e0962832bb235ba3b6749ebf609f347f747da5e60f", size = 11949834, upload-time = "2026-04-03T11:12:24.986Z" }, + { url = "https://files.pythonhosted.org/packages/5a/ff/4723d92f08259c707a974aba27a08d0a822b9555e35ca581bf18d055a364/curl_cffi-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b624c7ce087bfda967a013ed0a64702a525444e5b6e97d23534d567ccc6525aa", size = 1702771, upload-time = "2026-04-03T11:12:28.201Z" }, + { url = "https://files.pythonhosted.org/packages/59/8c/36bbe06d66fa2b765e4a07199f643a59a9cd1a754207a96335402a9520f4/curl_cffi-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0b6c0543b993996670e9e4b78e305a2d60809d5681903ffb5568e21a387434d3", size = 1466312, upload-time = "2026-04-03T11:12:30.054Z" }, ] [[package]] name = "deno" -version = "2.8.1" +version = "2.8.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/eb/b743a520cdd668e070a4535296123f1c62d054b00699f58e49c32ab5925c/deno-2.8.1.tar.gz", hash = "sha256:fb65e568bef30b1a7e63f033713f1a6792a8456e339febdb7d638c6bb2c4c008", size = 8167, upload-time = "2026-05-27T13:01:06.508Z" } +sdist = { url = "https://files.pythonhosted.org/packages/db/ec/98f972cca4ca734534947bdf28b86a99e9ed8a5a31a061be2dec9b4105c2/deno-2.8.3.tar.gz", hash = "sha256:5413f4a1814ac3b8e441ca7fe9eb677a4152a42eef05efd56a61d750088a7fc8", size = 8163, upload-time = "2026-06-11T16:13:53.129Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d1/71/f9dc8ad874dcc26cf1a154c8f89d77e1155ced1f6a64be9d21127bd555ce/deno-2.8.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:733e24e4883c9516534cae7a10277048ffea7cc034f9f726e8c145d48ba75d19", size = 42749443, upload-time = "2026-05-27T13:00:49.439Z" }, - { url = "https://files.pythonhosted.org/packages/f5/db/1721aca1a9bd132a3f721bd547022534fcdd6221701561c5e33705cdfb6d/deno-2.8.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7b4638504f3730f9b25229db08d1bce87bc64f52498f9f8f5aeba702c7ff2115", size = 39460813, upload-time = "2026-05-27T13:00:52.578Z" }, - { url = "https://files.pythonhosted.org/packages/1a/20/b50646f865562b8f21532cad6f9804b126efd4030cfd0c5e1d11217b15ca/deno-2.8.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:91f29a2df4cb6135872d68f38005c08fe0c389c84cb7349d7399eedbfbe19829", size = 43893026, upload-time = "2026-05-27T13:00:55.939Z" }, - { url = "https://files.pythonhosted.org/packages/0d/1e/75b84e7096b53f077ac01f9b5c979b7b42b0a6497f56d7c4ae072381e059/deno-2.8.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:f0958910ffa88f6b6e142129ab34b7a3aec361fc63904ff803ce1594beca230a", size = 46038250, upload-time = "2026-05-27T13:00:59.662Z" }, - { url = "https://files.pythonhosted.org/packages/14/d6/14f6cf25025644bb8b7d9b4606780b5ec6ee429a55c0d1f05681718c7fc0/deno-2.8.1-py3-none-win_amd64.whl", hash = "sha256:71ec55c0a0944beee376aa824722734cf3e617661bca5e143caa83991921e4f5", size = 41962337, upload-time = "2026-05-27T13:01:03.453Z" }, + { url = "https://files.pythonhosted.org/packages/52/42/71f88d6c04c7f4335f94c1c9eb5aee7e928e1a53056bb9b20c0c4d889f48/deno-2.8.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:57825615e8d4182160401b56907b2e36712aed55a5dfdef2c0e3089379bb6988", size = 42314540, upload-time = "2026-06-11T16:13:39.134Z" }, + { url = "https://files.pythonhosted.org/packages/df/95/032101e28532fa9ed28ca7dc737934d4631544fa3b11f4efe3573970a05a/deno-2.8.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:05dbbbd8edf1907abcbcf55395e63f00d74c2672b6039dd1ecd5656baebaa56c", size = 38101511, upload-time = "2026-06-11T16:13:42.192Z" }, + { url = "https://files.pythonhosted.org/packages/23/24/68c1d2d79933738acb8e5ef3a4584f57c6d5deb56c84073a5f4079c24647/deno-2.8.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:9fdb7574a6437fdd59f668e745dc3b3e32725fa360b64204a6b000689fa534e9", size = 42053660, upload-time = "2026-06-11T16:13:45.006Z" }, + { url = "https://files.pythonhosted.org/packages/d2/56/0d23c6daa1b139c31591df801feda3ac7d0d6225f1686cbf5caaa5db5c27/deno-2.8.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:9ca5727e5650f8459f39875f0af4b2242ead77da4ce8a8d52e86647036f3d63a", size = 43800396, upload-time = "2026-06-11T16:13:47.821Z" }, + { url = "https://files.pythonhosted.org/packages/6f/aa/e35b736205b89c98f31ed4abeebaa7846774e51006b14601e4edb7728e67/deno-2.8.3-py3-none-win_amd64.whl", hash = "sha256:37da75ee91448e4e6f5626f0d5cb18e295dbb6cff5cf780fab1f92ebbbd44071", size = 41552180, upload-time = "2026-06-11T16:13:50.711Z" }, ] [[package]] @@ -428,11 +451,11 @@ wheels = [ [[package]] name = "idna" -version = "3.17" +version = "3.18" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b9/28/99c51f664567218d824af024c0251650fb27e4ca066df188dab0769c5b91/idna-3.17.tar.gz", hash = "sha256:5eb0cb53bc467c12eadcf6de83163ad8527cec9416f44b9b61b19caedad2b87f", size = 196048, upload-time = "2026-05-28T14:32:38.55Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/de/a7/f76514cc40ad6234098ecdebda08732d75964776c51a42845b7da10649e2/idna-3.17-py3-none-any.whl", hash = "sha256:466e48829084efe2548012b855df21540b96f2e20e51bd124c851536556a592c", size = 65316, upload-time = "2026-05-28T14:32:37.035Z" }, + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, ] [[package]] @@ -453,6 +476,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3e/95/c7c34aa53c16353c56d0b802fba48d5f5caa2cdee7958acbcb795c830416/isort-8.0.1-py3-none-any.whl", hash = "sha256:28b89bc70f751b559aeca209e6120393d43fbe2490de0559662be7a9787e3d75", size = 89733, upload-time = "2026-02-28T10:08:19.466Z" }, ] +[[package]] +name = "markdown-it-py" +version = "4.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/ff/7841249c247aa650a76b9ee4bbaeae59370dc8bfd2f6c01f3630c35eb134/markdown_it_py-4.2.0.tar.gz", hash = "sha256:04a21681d6fbb623de53f6f364d352309d4094dd4194040a10fd51833e418d49", size = 82454, upload-time = "2026-05-07T12:08:28.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/81/4da04ced5a082363ecfa159c010d200ecbd959ae410c10c0264a38cac0f5/markdown_it_py-4.2.0-py3-none-any.whl", hash = "sha256:9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a", size = 91687, upload-time = "2026-05-07T12:08:27.182Z" }, +] + [[package]] name = "mccabe" version = "0.7.0" @@ -462,6 +497,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, ] +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + [[package]] name = "metube" version = "0.1.0" @@ -779,16 +823,16 @@ wheels = [ [[package]] name = "pytest-aiohttp" -version = "1.1.0" +version = "1.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, { name = "pytest" }, { name = "pytest-asyncio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/4b/d326890c153f2c4ce1bf45d07683c08c10a1766058a22934620bc6ac6592/pytest_aiohttp-1.1.0.tar.gz", hash = "sha256:147de8cb164f3fc9d7196967f109ab3c0b93ea3463ab50631e56438eab7b5adc", size = 12842, upload-time = "2025-01-23T12:44:04.465Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/4d/c6621fc79022f6c84a806e23d9b7eca24fae4f3ee779219bbe524339d666/pytest_aiohttp-1.1.1.tar.gz", hash = "sha256:3aa9c9fe26e543eaccc7eb0add381c685ba3ed3e2fed0af74540f63bcd31458d", size = 13704, upload-time = "2026-06-07T23:56:34.173Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/0f/e6af71c02e0f1098eaf7d2dbf3ffdf0a69fc1e0ef174f96af05cef161f1b/pytest_aiohttp-1.1.0-py3-none-any.whl", hash = "sha256:f39a11693a0dce08dd6c542d241e199dd8047a6e6596b2bcfa60d373f143456d", size = 8932, upload-time = "2025-01-23T12:44:03.27Z" }, + { url = "https://files.pythonhosted.org/packages/fa/b0/5056ed4c3f68a4db2b4a39fb0ec61b1e4cf1d89ee14effe5261cc587264c/pytest_aiohttp-1.1.1-py3-none-any.whl", hash = "sha256:f293441ad4f8446a1e12257130c26c7de03a615c2a5572a8cb046e5b3b4e5211", size = 9007, upload-time = "2026-06-07T23:56:33.333Z" }, ] [[package]] @@ -843,6 +887,19 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, ] +[[package]] +name = "rich" +version = "15.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/8f/0722ca900cc807c13a6a0c696dacf35430f72e0ec571c4275d2371fca3e9/rich-15.0.0.tar.gz", hash = "sha256:edd07a4824c6b40189fb7ac9bc4c52536e9780fbbfbddf6f1e2502c31b068c36", size = 230680, upload-time = "2026-04-12T08:24:00.75Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/82/3b/64d4899d73f91ba49a8c18a8ff3f0ea8f1c1d75481760df8c68ef5235bf5/rich-15.0.0-py3-none-any.whl", hash = "sha256:33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb", size = 310654, upload-time = "2026-04-12T08:24:02.83Z" }, +] + [[package]] name = "simple-websocket" version = "1.1.0" From 5429200fba7e57d3a3c5275e89888050f2ef487f Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Sat, 13 Jun 2026 17:39:14 +0300 Subject: [PATCH 54/88] support live streams (closes #302, closes #752, closes #978) --- app/tests/test_download_queue.py | 243 +++++++++++++++++++++++++++++- app/ytdl.py | 211 +++++++++++++++++++++++++- ui/src/app/app.html | 23 ++- ui/src/app/app.spec.ts | 31 ++++ ui/src/app/app.ts | 28 +++- ui/src/app/interfaces/download.ts | 2 + 6 files changed, 524 insertions(+), 14 deletions(-) diff --git a/app/tests/test_download_queue.py b/app/tests/test_download_queue.py index 20bf863..7e07c58 100644 --- a/app/tests/test_download_queue.py +++ b/app/tests/test_download_queue.py @@ -7,8 +7,9 @@ import tempfile from unittest.mock import AsyncMock, MagicMock, patch import pytest +import time -from ytdl import DownloadQueue +from ytdl import DownloadInfo, DownloadQueue @pytest.fixture @@ -386,3 +387,243 @@ async def test_add_sets_clip_bounds_on_download_info(dq_env): download = dq.pending.get("https://example.com/clip") assert download.info.clip_start == 10.0 assert download.info.clip_end == 99.5 + + +def _upcoming_entry(url: str, *, release_timestamp: float | None = None) -> dict: + return { + "_type": "video", + "id": "live1", + "title": "Upcoming Stream", + "url": url, + "webpage_url": url, + "live_status": "is_upcoming", + "release_timestamp": release_timestamp if release_timestamp is not None else time.time() + 3600, + } + + +@pytest.mark.asyncio +async def test_add_upcoming_stream_scheduled_without_starting(dq_env): + notifier = AsyncMock() + url = "https://example.com/live-upcoming" + start_mock = AsyncMock() + + dq = DownloadQueue(dq_env, notifier) + with patch.object(DownloadQueue, "_DownloadQueue__start_download", start_mock): + result = await dq.add_entry( + _upcoming_entry(url), + "video", + "auto", + "any", + "best", + "", + "", + 0, + auto_start=True, + ) + + assert result["status"] == "ok" + assert dq.queue.exists(url) + download = dq.queue.get(url) + assert download.info.status == "scheduled" + assert download.info.live_status == "is_upcoming" + assert download.info.live_release_timestamp is not None + start_mock.assert_not_called() + assert url in dq._scheduled_probe_at + + +@pytest.mark.asyncio +async def test_probe_scheduled_starts_when_live(dq_env): + notifier = AsyncMock() + url = "https://example.com/live-upcoming" + start_mock = AsyncMock() + + dq = DownloadQueue(dq_env, notifier) + with patch.object(DownloadQueue, "_DownloadQueue__start_download", start_mock): + await dq.add_entry( + _upcoming_entry(url), + "video", + "auto", + "any", + "best", + "", + "", + 0, + auto_start=True, + ) + + download = dq.queue.get(url) + + def fake_probe_extract(self, probe_url, ytdl_options_presets=None, ytdl_options_overrides=None): + assert probe_url == url + return { + "_type": "video", + "id": "live1", + "title": "Live Now", + "url": url, + "webpage_url": url, + "live_status": "is_live", + "formats": [{"format_id": "22"}], + } + + with patch.object(DownloadQueue, "_DownloadQueue__extract_info", fake_probe_extract), \ + patch.object(DownloadQueue, "_DownloadQueue__start_download", start_mock): + await dq._probe_scheduled_download(download) + + assert url not in dq._scheduled_probe_at + assert download.info.live_status == "is_live" + assert download.info.status == "pending" + start_mock.assert_called_once_with(download) + + +@pytest.mark.asyncio +async def test_import_scheduled_re_registers_monitor(dq_env): + notifier = AsyncMock() + url = "https://example.com/live-restart" + release = time.time() + 7200 + + info = DownloadInfo( + id="live1", + title="Upcoming Stream", + url=url, + quality="best", + download_type="video", + codec="auto", + format="any", + folder="", + custom_name_prefix="", + error=None, + entry=None, + playlist_item_limit=0, + split_by_chapters=False, + chapter_template="", + live_status="is_upcoming", + live_release_timestamp=release, + ) + info.status = "scheduled" + + dq = DownloadQueue(dq_env, notifier) + start_mock = AsyncMock() + with patch.object(DownloadQueue, "_DownloadQueue__start_download", start_mock): + await dq._DownloadQueue__add_download(info, True) + + assert dq.queue.exists(url) + assert dq.queue.get(url).info.status == "scheduled" + assert url in dq._scheduled_probe_at + start_mock.assert_not_called() + + +@pytest.mark.asyncio +async def test_probe_transient_error_retries_without_failing(dq_env): + """A single probe failure must not abandon the scheduled stream.""" + import ytdl + + notifier = AsyncMock() + url = "https://example.com/live-transient" + start_mock = AsyncMock() + + dq = DownloadQueue(dq_env, notifier) + with patch.object(DownloadQueue, "_DownloadQueue__start_download", start_mock): + await dq.add_entry( + _upcoming_entry(url), + "video", "auto", "any", "best", "", "", 0, + auto_start=True, + ) + download = dq.queue.get(url) + + def boom(self, *args, **kwargs): + raise ytdl.yt_dlp.utils.YoutubeDLError("temporary network glitch") + + before = time.time() + with patch.object(DownloadQueue, "_DownloadQueue__extract_info", boom): + await dq._probe_scheduled_download(download) + + # Still scheduled, still monitored, probe rescheduled into the future. + assert download.info.status == "scheduled" + assert url in dq._scheduled_probe_at + assert dq._scheduled_probe_at[url] >= before + assert dq._scheduled_probe_failures[url] == 1 + notifier.completed.assert_not_called() + + +@pytest.mark.asyncio +async def test_probe_gives_up_after_max_failures(dq_env): + import ytdl + + notifier = AsyncMock() + url = "https://example.com/live-dead" + start_mock = AsyncMock() + + dq = DownloadQueue(dq_env, notifier) + with patch.object(DownloadQueue, "_DownloadQueue__start_download", start_mock): + await dq.add_entry( + _upcoming_entry(url), + "video", "auto", "any", "best", "", "", 0, + auto_start=True, + ) + download = dq.queue.get(url) + + def boom(self, *args, **kwargs): + raise ytdl.yt_dlp.utils.YoutubeDLError("stream was deleted") + + with patch.object(DownloadQueue, "_DownloadQueue__extract_info", boom): + for _ in range(ytdl._LIVE_PROBE_MAX_FAILURES): + await dq._probe_scheduled_download(download) + + assert url not in dq._scheduled_probe_at + assert not dq.queue.exists(url) + assert dq.done.exists(url) + assert download.info.status == "error" + notifier.completed.assert_awaited() + + +@pytest.mark.asyncio +async def test_probe_recovers_after_transient_then_starts(dq_env): + """A transient failure followed by a successful live probe should start the download.""" + import ytdl + + notifier = AsyncMock() + url = "https://example.com/live-recover" + start_mock = AsyncMock() + + dq = DownloadQueue(dq_env, notifier) + with patch.object(DownloadQueue, "_DownloadQueue__start_download", start_mock): + await dq.add_entry( + _upcoming_entry(url), + "video", "auto", "any", "best", "", "", 0, + auto_start=True, + ) + download = dq.queue.get(url) + # The scheduling placeholder error is set on add. + assert download.info.error + + def boom(self, *args, **kwargs): + raise ytdl.yt_dlp.utils.YoutubeDLError("temporary glitch") + + with patch.object(DownloadQueue, "_DownloadQueue__extract_info", boom): + await dq._probe_scheduled_download(download) + assert dq._scheduled_probe_failures[url] == 1 + + def live_now(self, *args, **kwargs): + return { + "_type": "video", "id": "live1", "title": "Live Now", + "url": url, "webpage_url": url, "live_status": "is_live", + "formats": [{"format_id": "22"}], + } + + with patch.object(DownloadQueue, "_DownloadQueue__extract_info", live_now), \ + patch.object(DownloadQueue, "_DownloadQueue__start_download", start_mock): + await dq._probe_scheduled_download(download) + + assert url not in dq._scheduled_probe_at + assert url not in dq._scheduled_probe_failures + assert download.info.status == "pending" + # Placeholder error/msg cleared now that a real download is starting. + assert download.info.error is None + assert download.info.msg is None + start_mock.assert_called_once_with(download) + + +def test_seconds_until_next_probe_none_when_empty(dq_env): + notifier = AsyncMock() + dq = DownloadQueue(dq_env, notifier) + assert dq._seconds_until_next_probe() is None diff --git a/app/ytdl.py b/app/ytdl.py index d928c0f..31bca5e 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -24,6 +24,12 @@ from subscriptions import _entry_id log = logging.getLogger('ytdl') +_LIVE_CHECK_INTERVAL = 60 +_LIVE_MAX_CHECK_INTERVAL = 3600 +# Consecutive probe failures (network blips, rate limits, transient extractor +# errors) tolerated before a scheduled live download is abandoned as errored. +_LIVE_PROBE_MAX_FAILURES = 5 + # Characters that are invalid in Windows/NTFS path components. These are pre- # sanitised when substituting playlist/channel titles into output templates so @@ -194,6 +200,8 @@ class DownloadInfo: ytdl_options_overrides=None, clip_start=None, clip_end=None, + live_status=None, + live_release_timestamp=None, ): self.id = id if len(custom_name_prefix) == 0 else f'{custom_name_prefix}.{id}' self.title = title if len(custom_name_prefix) == 0 else f'{custom_name_prefix}.{title}' @@ -220,6 +228,8 @@ class DownloadInfo: self.ytdl_options_overrides = dict(ytdl_options_overrides or {}) self.clip_start = clip_start self.clip_end = clip_end + self.live_status = live_status + self.live_release_timestamp = live_release_timestamp self.subtitle_files = [] def __setstate__(self, state): @@ -292,6 +302,10 @@ class DownloadInfo: self.clip_start = None if not hasattr(self, "clip_end"): self.clip_end = None + if not hasattr(self, "live_status"): + self.live_status = None + if not hasattr(self, "live_release_timestamp"): + self.live_release_timestamp = None _PERSISTED_DOWNLOAD_FIELDS = ( @@ -313,6 +327,8 @@ _PERSISTED_DOWNLOAD_FIELDS = ( "ytdl_options_overrides", "clip_start", "clip_end", + "live_status", + "live_release_timestamp", "status", "timestamp", "error", @@ -757,6 +773,10 @@ class DownloadQueue: self.done.load() self._add_generation = 0 self._canceled_urls = set() # URLs canceled during current playlist add + self._scheduled_probe_at: dict[str, float] = {} + self._scheduled_probe_failures: dict[str, int] = {} + self._live_monitor_task: Optional[asyncio.Task] = None + self._live_monitor_wakeup = asyncio.Event() def cancel_add(self): self._add_generation += 1 @@ -772,9 +792,165 @@ class DownloadQueue: async def initialize(self): log.info("Initializing DownloadQueue") + self._start_live_monitor() asyncio.create_task(self.__import_queue()) asyncio.create_task(self.__import_pending()) + def _start_live_monitor(self) -> None: + if self._live_monitor_task is not None and not self._live_monitor_task.done(): + return + self._live_monitor_task = asyncio.create_task(self._live_monitor_loop()) + self._live_monitor_task.add_done_callback( + lambda t: log.error("Live monitor loop failed: %s", t.exception()) + if not t.cancelled() and t.exception() + else None + ) + + def _register_scheduled(self, download: Download) -> None: + self._scheduled_probe_at[download.info.url] = 0 + self._scheduled_probe_failures.pop(download.info.url, None) + self._start_live_monitor() + self._wake_live_monitor() + + def _unregister_scheduled(self, url: str) -> None: + self._scheduled_probe_at.pop(url, None) + self._scheduled_probe_failures.pop(url, None) + + def _wake_live_monitor(self) -> None: + try: + self._live_monitor_wakeup.set() + except RuntimeError: + pass + + def _probe_interval_seconds(self, release_timestamp: Any) -> float: + if release_timestamp is not None: + try: + diff = float(release_timestamp) - time.time() + if diff > 0: + return max(_LIVE_CHECK_INTERVAL, min(diff, _LIVE_MAX_CHECK_INTERVAL)) + except (TypeError, ValueError): + pass + return float(_LIVE_CHECK_INTERVAL) + + def _seconds_until_next_probe(self) -> Optional[float]: + """Time until the earliest scheduled probe, or None when nothing is scheduled.""" + if not self._scheduled_probe_at: + return None + return max(0.0, min(self._scheduled_probe_at.values()) - time.time()) + + async def _live_monitor_loop(self) -> None: + while True: + timeout = self._seconds_until_next_probe() + try: + await asyncio.wait_for(self._live_monitor_wakeup.wait(), timeout=timeout) + except asyncio.TimeoutError: + pass + self._live_monitor_wakeup.clear() + now = time.time() + due: list[Download] = [] + for url, probe_at in list(self._scheduled_probe_at.items()): + if now < probe_at: + continue + if not self.queue.exists(url): + self._unregister_scheduled(url) + continue + download = self.queue.get(url) + if download.info.status != 'scheduled' or download.canceled: + self._unregister_scheduled(url) + continue + due.append(download) + for download in due: + try: + await self._probe_scheduled_download(download) + except Exception as exc: + # Defensive: _probe_scheduled_download handles its own errors, + # but never let an unexpected failure leave probe_at in the past + # (which would spin this loop) or kill the monitor task. + log.exception("Scheduled live probe crashed for %s: %s", download.info.url, exc) + if download.info.url in self._scheduled_probe_at: + self._scheduled_probe_at[download.info.url] = time.time() + _LIVE_CHECK_INTERVAL + + async def _probe_scheduled_download(self, download: Download) -> None: + url = download.info.url + info = download.info + if info.status != 'scheduled' or download.canceled: + self._unregister_scheduled(url) + return + + try: + entry = await asyncio.get_running_loop().run_in_executor( + None, + partial( + self.__extract_info, + url, + getattr(info, 'ytdl_options_presets', None), + getattr(info, 'ytdl_options_overrides', {}) or {}, + ), + ) + except Exception as exc: + # Treat all probe failures (transient network blips, rate limits, + # extractor errors) as recoverable up to a point: retry on the next + # interval and only give up after repeated consecutive failures so a + # momentary glitch doesn't abandon a stream the user is waiting for. + fails = self._scheduled_probe_failures.get(url, 0) + 1 + self._scheduled_probe_failures[url] = fails + if fails >= _LIVE_PROBE_MAX_FAILURES: + log.warning( + "Giving up on scheduled live probe for %s after %d consecutive failures: %s", + info.title, fails, exc, + ) + info.status = 'error' + info.msg = str(exc) + if not info.error: + info.error = str(exc) + self._unregister_scheduled(url) + self.queue.delete(url) + self.done.put(download) + await self.notifier.completed(info) + else: + log.warning( + "Scheduled live probe failed for %s (attempt %d/%d), will retry: %s", + info.title, fails, _LIVE_PROBE_MAX_FAILURES, exc, + ) + self._scheduled_probe_at[url] = time.time() + _LIVE_CHECK_INTERVAL + return + + # Successful probe resets the transient-failure streak. + self._scheduled_probe_failures.pop(url, None) + + release_ts = entry.get('release_timestamp') + live_status = entry.get('live_status') + if release_ts is not None: + info.live_release_timestamp = release_ts + if live_status is not None: + info.live_status = live_status + + if live_status == 'is_upcoming': + self._scheduled_probe_at[url] = time.time() + self._probe_interval_seconds(release_ts) + await self.notifier.updated(info) + return + + self._unregister_scheduled(url) + info.status = 'pending' + # Clear the "scheduled to start at ..." placeholder now that the stream + # is live and a real download is about to begin. + info.error = None + info.msg = None + await self.notifier.updated(info) + asyncio.create_task(self.__start_download(download)) + + def _schedule_upcoming_download(self, download: Download) -> None: + download.info.status = 'scheduled' + self.queue.put(download) + self._register_scheduled(download) + + def _force_start_scheduled(self, download: Download) -> None: + self._unregister_scheduled(download.info.url) + download.info.status = 'pending' + download.info.error = None + download.info.msg = None + asyncio.create_task(self.__start_download(download)) + async def __start_download(self, download): if download.canceled: log.info(f"Download {download.info.title} was canceled, skipping start.") @@ -886,9 +1062,16 @@ class DownloadQueue: log.info(f'playlist limit is set. Processing only first {playlist_item_limit} entries') ytdl_options['playlistend'] = playlist_item_limit download = Download(dldirectory, self.config.TEMP_DIR, output, output_chapter, dl.quality, dl.format, ytdl_options, dl) + is_upcoming = ( + getattr(dl, 'live_status', None) == 'is_upcoming' + or getattr(dl, 'status', None) == 'scheduled' + ) if auto_start is True: - self.queue.put(download) - asyncio.create_task(self.__start_download(download)) + if is_upcoming: + self._schedule_upcoming_download(download) + else: + self.queue.put(download) + asyncio.create_task(self.__start_download(download)) else: self.pending.put(download) await self.notifier.added(dl) @@ -1036,6 +1219,8 @@ class DownloadQueue: ytdl_options_overrides=ytdl_options_overrides, clip_start=clip_start, clip_end=clip_end, + live_status=entry.get('live_status'), + live_release_timestamp=entry.get('release_timestamp'), ) await self.__add_download(dl, auto_start) return {'status': 'ok'} @@ -1156,13 +1341,21 @@ class DownloadQueue: async def start_pending(self, ids): for id in ids: - if not self.pending.exists(id): - log.warning(f'requested start for non-existent download {id}') + if self.pending.exists(id): + dl = self.pending.get(id) + self.pending.delete(id) + if getattr(dl.info, 'live_status', None) == 'is_upcoming': + self._schedule_upcoming_download(dl) + else: + self.queue.put(dl) + asyncio.create_task(self.__start_download(dl)) continue - dl = self.pending.get(id) - self.queue.put(dl) - self.pending.delete(id) - asyncio.create_task(self.__start_download(dl)) + if self.queue.exists(id): + dl = self.queue.get(id) + if dl.info.status == 'scheduled': + self._force_start_scheduled(dl) + continue + log.warning(f'requested start for non-existent download {id}') return {'status': 'ok'} async def cancel(self, ids): @@ -1177,6 +1370,8 @@ class DownloadQueue: log.warning(f'requested cancel for non-existent download {id}') continue dl = self.queue.get(id) + if dl.info.status == 'scheduled': + self._unregister_scheduled(id) if dl.started(): dl.cancel() else: diff --git a/ui/src/app/app.html b/ui/src/app/app.html index f9699fd..0bd8300 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -706,16 +706,31 @@
-
{{ download.value.title }}
- +
+ {{ download.value.title }} + @if (download.value.live_status === 'is_live' && download.value.status !== 'scheduled') { + LIVE + } +
+ @if (download.value.status === 'scheduled') { + + + Waiting for stream + @if (liveCountdownSeconds(download.value); as secs) { + - starts in {{ secs | eta }} + } + + } @else { + + }
{{ download.value.speed | speed }} {{ download.value.eta | eta }}
- @if (download.value.status === 'pending') { + @if (download.value.status === 'pending' || download.value.status === 'scheduled') { } diff --git a/ui/src/app/app.spec.ts b/ui/src/app/app.spec.ts index cdaf356..a77b171 100644 --- a/ui/src/app/app.spec.ts +++ b/ui/src/app/app.spec.ts @@ -182,6 +182,37 @@ describe('App', () => { expect(payload.ytdlOptionsOverrides).toBe(''); }); + it('shows waiting badge for scheduled live stream', () => { + downloads.queue.set('https://example.com/live', { + id: 'live1', + title: 'Upcoming Stream', + url: 'https://example.com/live', + download_type: 'video', + quality: 'best', + format: 'any', + folder: '', + custom_name_prefix: '', + playlist_item_limit: 0, + status: 'scheduled', + live_status: 'is_upcoming', + live_release_timestamp: Date.now() / 1000 + 3600, + msg: '', + percent: 0, + speed: 0, + eta: 0, + filename: '', + checked: false, + }); + downloads.queueChanged.next(); + + const fixture = TestBed.createComponent(App); + fixture.detectChanges(); + + const root = fixture.nativeElement as HTMLElement; + expect(root.textContent).toContain('Waiting for stream'); + expect(root.textContent).toContain('starts in'); + }); + it('includes titleRegex in subscribe payload', () => { const fixture = TestBed.createComponent(App); const app = fixture.componentInstance; diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index 5dc8709..7245ab3 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -132,6 +132,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { lastCopiedErrorId: string | null = null; private previousDownloadType = 'video'; private addRequestSub?: Subscription; + private liveCountdownTimer?: ReturnType; private selectionsByType: Record { this.updateMetrics(); + this.syncLiveCountdownTimer(); this.cdr.markForCheck(); }); this.downloads.doneChanged.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { @@ -295,6 +297,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { // Subscribe to real-time updates this.downloads.updated.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { this.updateMetrics(); + this.syncLiveCountdownTimer(); this.cdr.markForCheck(); }); @@ -337,6 +340,9 @@ export class App implements AfterViewInit, OnInit, OnDestroy { ngOnDestroy() { this.addRequestSub?.unsubscribe(); + if (this.liveCountdownTimer) { + clearInterval(this.liveCountdownTimer); + } this.colorSchemeMediaQuery.removeEventListener('change', this.onColorSchemeChanged); } @@ -1106,6 +1112,26 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.downloads.startById([id]).subscribe(); } + liveCountdownSeconds(download: Download): number | null { + const ts = download.live_release_timestamp; + if (ts == null || download.status !== 'scheduled') { + return null; + } + return Math.max(0, ts - Date.now() / 1000); + } + + private syncLiveCountdownTimer() { + const hasScheduled = Array.from(this.downloads.queue.values()).some( + (download) => download.status === 'scheduled', + ); + if (hasScheduled && !this.liveCountdownTimer) { + this.liveCountdownTimer = setInterval(() => this.cdr.markForCheck(), 1000); + } else if (!hasScheduled && this.liveCountdownTimer) { + clearInterval(this.liveCountdownTimer); + this.liveCountdownTimer = undefined; + } + } + retryDownload(key: string, download: Download) { this.addDownload({ url: download.url, @@ -1631,7 +1657,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { speed += download.speed || 0; } else if (download.status === 'preparing') { active++; - } else if (download.status === 'pending') { + } else if (download.status === 'pending' || download.status === 'scheduled') { queued++; } }); diff --git a/ui/src/app/interfaces/download.ts b/ui/src/app/interfaces/download.ts index 935d4da..50de3cd 100644 --- a/ui/src/app/interfaces/download.ts +++ b/ui/src/app/interfaces/download.ts @@ -18,6 +18,8 @@ export interface Download { ytdl_options_overrides?: Record; clip_start?: number; clip_end?: number; + live_status?: string; + live_release_timestamp?: number; status: string; msg: string; percent: number; From 5aa7d033e27e38a066d66a48ca8e542d63ef158d Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Tue, 16 Jun 2026 21:35:07 +0300 Subject: [PATCH 55/88] review fixes --- app/main.py | 37 +++++- app/subscriptions.py | 17 +++ app/tests/test_config.py | 23 ++++ app/tests/test_download_queue.py | 60 +++++++++ app/ytdl.py | 61 +++++++-- ui/src/app/app.html | 2 + ui/src/app/app.spec.ts | 8 +- ui/src/app/app.ts | 121 ++++++------------ ui/src/app/components/index.ts | 3 +- .../components/toast-container.component.ts | 58 +++++++++ ui/src/app/services/batch-urls.service.ts | 62 +++++++++ ui/src/app/services/index.ts | 4 +- ui/src/app/services/toast.service.ts | 86 +++++++++++++ 13 files changed, 441 insertions(+), 101 deletions(-) create mode 100644 ui/src/app/components/toast-container.component.ts create mode 100644 ui/src/app/services/batch-urls.service.ts create mode 100644 ui/src/app/services/toast.service.ts diff --git a/app/main.py b/app/main.py index f5154ca..f6aed10 100644 --- a/app/main.py +++ b/app/main.py @@ -130,6 +130,10 @@ class Config: ) sys.exit(1) + self._validate_int('MAX_CONCURRENT_DOWNLOADS', minimum=1) + self._validate_int('PORT', minimum=1, maximum=65535) + self._validate_int('CLEAR_COMPLETED_AFTER', minimum=0) + self._runtime_overrides = {} success,_ = self.load_ytdl_options() @@ -139,6 +143,20 @@ class Config: if not success: sys.exit(1) + def _validate_int(self, key, *, minimum=None, maximum=None): + raw = getattr(self, key) + try: + value = int(raw) + except (TypeError, ValueError): + log.error('Environment variable "%s" must be an integer, got "%s"', key, raw) + sys.exit(1) + if minimum is not None and value < minimum: + log.error('Environment variable "%s" must be >= %d, got "%s"', key, minimum, raw) + sys.exit(1) + if maximum is not None and value > maximum: + log.error('Environment variable "%s" must be <= %d, got "%s"', key, maximum, raw) + sys.exit(1) + def set_runtime_override(self, key, value): self._runtime_overrides[key] = value self.YTDL_OPTIONS[key] = value @@ -241,7 +259,13 @@ logging.getLogger().setLevel(parseLogLevel(str(config.LOGLEVEL)) or logging.INFO class ObjectSerializer(json.JSONEncoder): def default(self, obj): - # First try to use __dict__ for custom objects + # Prefer an explicit client-facing view when the object provides one + # (e.g. DownloadInfo / SubscriptionInfo) so server-only or bulky fields + # are never broadcast to browser clients. + to_public = getattr(obj, 'to_public_dict', None) + if callable(to_public): + return to_public() + # Fall back to __dict__ for other custom objects if hasattr(obj, '__dict__'): return obj.__dict__ # Convert iterables (generators, dict_items, etc.) to lists @@ -827,10 +851,7 @@ async def cancel_add(request): @routes.post(config.URL_PREFIX + 'subscribe') async def subscribe(request): post = await _read_json_request(request) - try: - o = parse_download_options(post) - except web.HTTPBadRequest: - raise + o = parse_download_options(post) cic = post.get('check_interval_minutes') if cic is None: cic = config.SUBSCRIPTION_DEFAULT_CHECK_INTERVAL @@ -964,6 +985,12 @@ async def upload_cookies(request): tmp_cookie_path = f"{COOKIES_PATH}.tmp" with open(tmp_cookie_path, 'wb') as f: f.write(content) + # Cookies are sensitive auth material; restrict to owner read/write only + # (the container's default umask would otherwise leave them group/world readable). + try: + os.chmod(tmp_cookie_path, 0o600) + except OSError as exc: + log.warning(f'Could not restrict permissions on cookies file: {exc}') os.replace(tmp_cookie_path, COOKIES_PATH) config.set_runtime_override('cookiefile', COOKIES_PATH) log.info(f'Cookies file uploaded ({size} bytes)') diff --git a/app/subscriptions.py b/app/subscriptions.py index 0b5774a..9a3c4a3 100644 --- a/app/subscriptions.py +++ b/app/subscriptions.py @@ -312,6 +312,7 @@ class SubscriptionManager: self._subs: dict[str, SubscriptionInfo] = {} self._url_index: dict[str, str] = {} # normalized url -> id self._pending_urls: set[str] = set() + self._checks_in_flight: set[str] = set() # subscription ids being checked right now self._lock = asyncio.Lock() self._loop_task: Optional[asyncio.Task] = None self._load_all() @@ -677,6 +678,22 @@ class SubscriptionManager: return {"status": "ok"} async def _check_one_unlocked(self, sub: SubscriptionInfo) -> None: + sid = sub.id + # Prevent overlapping checks for the same subscription (e.g. the periodic + # loop and a manual check-now firing together), which could double-queue + # entries and drop seen_ids via a read-modify-write race. + async with self._lock: + if sid in self._checks_in_flight: + log.info("Subscription check already in progress for %s, skipping", sub.name) + return + self._checks_in_flight.add(sid) + try: + await self._check_one_inner(sub) + finally: + async with self._lock: + self._checks_in_flight.discard(sid) + + async def _check_one_inner(self, sub: SubscriptionInfo) -> None: sid = sub.id scan = int(getattr(self.config, "SUBSCRIPTION_SCAN_PLAYLIST_END", 50)) log.info("Checking subscription: %s", sub.name) diff --git a/app/tests/test_config.py b/app/tests/test_config.py index 8105e79..bbb6c6d 100644 --- a/app/tests/test_config.py +++ b/app/tests/test_config.py @@ -123,6 +123,29 @@ class ConfigTests(unittest.TestCase): with self.assertRaises(SystemExit): Config() + def test_invalid_max_concurrent_downloads_exits(self): + for bad in ("0", "-1", "abc"): + with patch.dict(os.environ, _base_env(MAX_CONCURRENT_DOWNLOADS=bad), clear=False): + with self.assertRaises(SystemExit): + Config() + + def test_invalid_port_exits(self): + for bad in ("0", "70000", "notaport"): + with patch.dict(os.environ, _base_env(PORT=bad), clear=False): + with self.assertRaises(SystemExit): + Config() + + def test_invalid_clear_completed_after_exits(self): + for bad in ("-5", "soon"): + with patch.dict(os.environ, _base_env(CLEAR_COMPLETED_AFTER=bad), clear=False): + with self.assertRaises(SystemExit): + Config() + + def test_clear_completed_after_zero_allowed(self): + with patch.dict(os.environ, _base_env(CLEAR_COMPLETED_AFTER="0"), clear=False): + c = Config() + self.assertEqual(c.CLEAR_COMPLETED_AFTER, "0") + def test_runtime_override_roundtrip(self): with patch.dict(os.environ, _base_env(), clear=False): c = Config() diff --git a/app/tests/test_download_queue.py b/app/tests/test_download_queue.py index 7e07c58..695788f 100644 --- a/app/tests/test_download_queue.py +++ b/app/tests/test_download_queue.py @@ -627,3 +627,63 @@ def test_seconds_until_next_probe_none_when_empty(dq_env): notifier = AsyncMock() dq = DownloadQueue(dq_env, notifier) assert dq._seconds_until_next_probe() is None + + +def test_calc_download_path_allows_subfolder(dq_env): + notifier = AsyncMock() + dq = DownloadQueue(dq_env, notifier) + path, err = dq._DownloadQueue__calc_download_path("video", "sub/dir") + assert err is None + assert os.path.realpath(path) == os.path.join(os.path.realpath(dq_env.DOWNLOAD_DIR), "sub", "dir") + + +def test_calc_download_path_rejects_sibling_prefix_escape(dq_env): + """A folder resolving to a sibling sharing a name prefix must be rejected. + + Regression test: ``startswith`` would have accepted ``../downloads-secret`` + when the base directory is ``.../downloads``. + """ + notifier = AsyncMock() + base = os.path.realpath(dq_env.DOWNLOAD_DIR) + sibling = base + "-secret" + os.makedirs(sibling, exist_ok=True) + dq = DownloadQueue(dq_env, notifier) + escape_folder = os.path.join("..", os.path.basename(sibling), "x") + path, err = dq._DownloadQueue__calc_download_path("video", escape_folder) + assert path is None + assert err is not None and err["status"] == "error" + + +def test_calc_download_path_rejects_parent_escape(dq_env): + notifier = AsyncMock() + dq = DownloadQueue(dq_env, notifier) + path, err = dq._DownloadQueue__calc_download_path("video", "../../etc") + assert path is None + assert err is not None and err["status"] == "error" + + +def test_download_info_to_public_dict_excludes_server_only_fields(): + info = DownloadInfo( + id="vid1", + title="Test Video", + url="https://example.com/watch?v=1", + quality="best", + download_type="video", + codec="auto", + format="any", + folder="", + custom_name_prefix="", + error=None, + entry={"id": "vid1", "huge": "x" * 100000}, + playlist_item_limit=0, + split_by_chapters=False, + chapter_template="", + ) + info.subtitle_files = [{"filename": "a.srt", "size": 10}] + public = info.to_public_dict() + assert "entry" not in public + assert "subtitle_files" not in public + # Client-facing fields are still present. + assert public["url"] == "https://example.com/watch?v=1" + assert public["title"] == "Test Video" + assert public["status"] == "pending" diff --git a/app/ytdl.py b/app/ytdl.py index 31bca5e..78acc82 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -232,6 +232,20 @@ class DownloadInfo: self.live_release_timestamp = live_release_timestamp self.subtitle_files = [] + # Fields that are useful server-side but must not be broadcast to browser + # clients: ``entry`` is the full yt-dlp info-dict (potentially large and + # re-sent on every progress tick) and ``subtitle_files`` is only used + # internally to derive the primary caption ``filename``. + _PUBLIC_EXCLUDED_FIELDS = ("entry", "subtitle_files") + + def to_public_dict(self) -> dict: + """Return the client-facing view, omitting server-only/bulky fields.""" + return { + k: v + for k, v in self.__dict__.items() + if k not in self._PUBLIC_EXCLUDED_FIELDS + } + def __setstate__(self, state): """BACKWARD COMPATIBILITY: migrate old DownloadInfo from persistent queue files.""" self.__dict__.update(state) @@ -584,7 +598,10 @@ class Download: self.info.filename = rel_name self.info.size = os.path.getsize(fileName) if os.path.exists(fileName) else None if getattr(self.info, 'download_type', '') == 'thumbnail': - self.info.filename = re.sub(r'\.webm$', '.jpg', self.info.filename) + # The thumbnail convertor always emits a .jpg, but yt-dlp may + # report the pre-conversion media/thumbnail extension + # (.webm/.mp4/.png/.webp/...). Normalise to .jpg regardless. + self.info.filename = os.path.splitext(self.info.filename)[0] + '.jpg' # Handle chapter files log.debug(f"Update status for {self.info.title}: {status}") @@ -647,8 +664,8 @@ class PersistentQueue: def __init__(self, name, path): self.identifier = name pdir = os.path.dirname(path) - if not os.path.isdir(pdir): - os.mkdir(pdir) + if pdir and not os.path.isdir(pdir): + os.makedirs(pdir, exist_ok=True) self.legacy_path = path self.path = f"{path}.json" self.store = AtomicJsonStore(self.path, kind=f"persistent_queue:{name}") @@ -1026,7 +1043,16 @@ class DownloadQueue: return None, {'status': 'error', 'msg': 'A folder for the download was specified but CUSTOM_DIRS is not true in the configuration.'} dldirectory = os.path.realpath(os.path.join(base_directory, folder)) real_base_directory = os.path.realpath(base_directory) - if not dldirectory.startswith(real_base_directory): + # Use commonpath rather than startswith so that a sibling directory + # sharing a name prefix (e.g. base "/downloads" vs "/downloads-secret") + # cannot be reached via "../downloads-secret". + try: + inside_base = os.path.commonpath([real_base_directory, dldirectory]) == real_base_directory + except ValueError: + # Raised when paths are on different drives (Windows) or mix + # absolute/relative; treat as outside the base directory. + inside_base = False + if not inside_base: return None, {'status': 'error', 'msg': f'Folder "{folder}" must resolve inside the base download directory "{real_base_directory}"'} if not os.path.isdir(dldirectory): if not self.config.CREATE_CUSTOM_DIRS: @@ -1387,11 +1413,28 @@ class DownloadQueue: continue if self.config.DELETE_FILE_ON_TRASHCAN: dl = self.done.get(id) - try: - dldirectory, _ = self.__calc_download_path(dl.info.download_type, dl.info.folder) - os.remove(os.path.join(dldirectory, dl.info.filename)) - except Exception as e: - log.warning(f'deleting file for download {id} failed with error message {e!r}') + dldirectory, calc_error = self.__calc_download_path(dl.info.download_type, dl.info.folder) + if calc_error is not None or not dldirectory: + log.warning(f'deleting files for download {id} skipped: could not resolve download directory') + else: + # Remove the primary output plus any per-chapter / per-subtitle + # outputs. Each filename is relative to the download directory. + rel_names = [] + if getattr(dl.info, 'filename', None): + rel_names.append(dl.info.filename) + for extra in (getattr(dl.info, 'chapter_files', None) or []): + if isinstance(extra, dict) and extra.get('filename'): + rel_names.append(extra['filename']) + for extra in (getattr(dl.info, 'subtitle_files', None) or []): + if isinstance(extra, dict) and extra.get('filename'): + rel_names.append(extra['filename']) + for rel_name in rel_names: + try: + os.remove(os.path.join(dldirectory, rel_name)) + except FileNotFoundError: + pass + except OSError as e: + log.warning(f'deleting file "{rel_name}" for download {id} failed with error message {e!r}') self.done.delete(id) await self.notifier.cleared(id) return {'status': 'ok'} diff --git a/ui/src/app/app.html b/ui/src/app/app.html index 0bd8300..1299b05 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -1078,3 +1078,5 @@ }
+ + diff --git a/ui/src/app/app.spec.ts b/ui/src/app/app.spec.ts index a77b171..be19b65 100644 --- a/ui/src/app/app.spec.ts +++ b/ui/src/app/app.spec.ts @@ -4,6 +4,7 @@ import { Subject, of } from 'rxjs'; import { App } from './app'; import { DownloadsService } from './services/downloads.service'; import { SubscriptionsService } from './services/subscriptions.service'; +import { ToastService } from './services/toast.service'; import { CookieService } from 'ngx-cookie-service'; class DownloadsServiceStub { @@ -263,7 +264,8 @@ describe('App', () => { }); it('blocks subscribe with invalid title regex', () => { - const alertSpy = vi.spyOn(window, 'alert').mockImplementation(() => undefined); + const toasts = TestBed.inject(ToastService); + const errorSpy = vi.spyOn(toasts, 'error').mockImplementation(() => undefined); const fixture = TestBed.createComponent(App); const app = fixture.componentInstance; const subs = TestBed.inject(SubscriptionsService) as unknown as SubscriptionsServiceStub; @@ -271,7 +273,7 @@ describe('App', () => { app.titleRegex = '['; app.addSubscription(); expect(subs.subscribeCalls.length).toBe(0); - expect(alertSpy).toHaveBeenCalledWith('Invalid subscription title filter (regex)'); - alertSpy.mockRestore(); + expect(errorSpy).toHaveBeenCalledWith('Invalid subscription title filter (regex)'); + errorSpy.mockRestore(); }); }); diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index 7245ab3..4f58747 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -13,6 +13,8 @@ import { CookieService } from 'ngx-cookie-service'; import { AddDownloadPayload, DownloadsService } from './services/downloads.service'; import { MeTubeSocket } from './services/metube-socket.service'; import { SubscriptionsService } from './services/subscriptions.service'; +import { ToastService } from './services/toast.service'; +import { BatchUrlsService, BatchUrlFilter } from './services/batch-urls.service'; import { SubscriptionRow } from './interfaces/subscription'; import { Themes } from './theme'; import { @@ -32,7 +34,7 @@ import { State, } from './interfaces'; import { EtaPipe, SpeedPipe, FileSizePipe } from './pipes'; -import { SelectAllCheckboxComponent, ItemCheckboxComponent } from './components/'; +import { SelectAllCheckboxComponent, ItemCheckboxComponent, ToastContainerComponent } from './components/'; @Component({ selector: 'app-root', @@ -50,6 +52,7 @@ import { SelectAllCheckboxComponent, ItemCheckboxComponent } from './components/ FileSizePipe, SelectAllCheckboxComponent, ItemCheckboxComponent, + ToastContainerComponent, ], templateUrl: './app.html', styleUrl: './app.sass', @@ -57,6 +60,8 @@ import { SelectAllCheckboxComponent, ItemCheckboxComponent } from './components/ export class App implements AfterViewInit, OnInit, OnDestroy { downloads = inject(DownloadsService); subscriptionsSvc = inject(SubscriptionsService); + private toasts = inject(ToastService); + private batchUrls = inject(BatchUrlsService); private socket = inject(MeTubeSocket); private cookieService = inject(CookieService); private http = inject(HttpClient); @@ -415,7 +420,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { const date = new Date(data['update_time'] * 1000); this.ytDlpOptionsUpdateTime=date.toLocaleString(); }else{ - alert("Error reload yt-dlp options: "+data['msg']); + this.toasts.error("Error reloading yt-dlp options: " + data['msg']); } this.cdr.markForCheck(); } @@ -490,11 +495,11 @@ export class App implements AfterViewInit, OnInit, OnDestroy { try { const parsed = JSON.parse(trimmed); if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object') { - alert('Custom yt-dlp options must be a JSON object'); + this.toasts.error('Custom yt-dlp options must be a JSON object'); return false; } } catch { - alert('Custom yt-dlp options must be valid JSON'); + this.toasts.error('Custom yt-dlp options must be valid JSON'); return false; } return true; @@ -525,7 +530,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.subscriptionsSvc.refreshList().pipe(takeUntilDestroyed(this.destroyRef)).subscribe((refreshRes) => { const error = this.getStatusError(refreshRes); if (error) { - alert(error || 'Refresh subscriptions failed'); + this.toasts.error(error || 'Refresh subscriptions failed'); return; } this.cdr.markForCheck(); @@ -569,7 +574,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { } const payload = this.buildAddPayload(); if (!payload.url?.trim()) { - alert('Please enter a URL'); + this.toasts.error('Please enter a URL'); return; } const tr = (this.titleRegex || '').trim(); @@ -577,12 +582,12 @@ export class App implements AfterViewInit, OnInit, OnDestroy { try { void RegExp(tr); } catch { - alert('Invalid subscription title filter (regex)'); + this.toasts.error('Invalid subscription title filter (regex)'); return; } } if (payload.splitByChapters && !payload.chapterTemplate.includes('%(section_number)')) { - alert('Chapter template must include %(section_number)'); + this.toasts.error('Chapter template must include %(section_number)'); return; } if (!this.validateYtdlOptionsOverrides(payload.ytdlOptionsOverrides)) { @@ -611,7 +616,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { next: (res) => { const r = res as { status?: string; msg?: string }; if (r.status === 'error') { - alert(r.msg || 'Subscribe failed'); + this.toasts.error(r.msg || 'Subscribe failed'); } else { this.addUrl = ''; this.titleRegex = ''; @@ -639,14 +644,14 @@ export class App implements AfterViewInit, OnInit, OnDestroy { try { void RegExp(raw); } catch { - alert('Invalid subscription title filter (regex)'); + this.toasts.error('Invalid subscription title filter (regex)'); return; } } this.subscriptionsSvc.update(id, { title_regex: raw }).subscribe((res) => { const error = this.getStatusError(res); if (error) { - alert(error || 'Update subscription failed'); + this.toasts.error(error || 'Update subscription failed'); return; } this.cancelEditTitleRegex(); @@ -657,7 +662,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.subscriptionsSvc.delete([id]).subscribe((res) => { const error = this.getStatusError(res); if (error) { - alert(error || 'Delete subscription failed'); + this.toasts.error(error || 'Delete subscription failed'); return; } this.selectedSubscriptionIds.delete(id); @@ -673,7 +678,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.subscriptionsSvc.delete(ids).subscribe((res) => { const error = this.getStatusError(res); if (error) { - alert(error || 'Delete subscriptions failed'); + this.toasts.error(error || 'Delete subscriptions failed'); return; } this.selectedSubscriptionIds.clear(); @@ -699,7 +704,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { .subscribe((res) => { const error = this.getStatusError(res); if (error) { - alert(error || 'Subscription check failed'); + this.toasts.error(error || 'Subscription check failed'); return; } this.refreshSubscriptionsWithAlert(); @@ -746,7 +751,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { .subscribe((res) => { const error = this.getStatusError(res); if (error) { - alert(error || 'Subscription check failed'); + this.toasts.error(error || 'Subscription check failed'); return; } this.refreshSubscriptionsWithAlert(); @@ -769,7 +774,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.subscriptionsSvc.update(row.id, { enabled: !row.enabled }).subscribe((res) => { const error = this.getStatusError(res); if (error) { - alert(error || 'Update subscription failed'); + this.toasts.error(error || 'Update subscription failed'); } }); } @@ -1066,20 +1071,19 @@ export class App implements AfterViewInit, OnInit, OnDestroy { // Validate chapter template if chapter splitting is enabled if (payload.splitByChapters && !payload.chapterTemplate.includes('%(section_number)')) { - alert('Chapter template must include %(section_number)'); + this.toasts.error('Chapter template must include %(section_number)'); return; } if (!this.validateYtdlOptionsOverrides(payload.ytdlOptionsOverrides)) { return; } - console.debug('Downloading:', payload); this.addInProgress = true; this.cancelRequested = false; this.addRequestSub?.unsubscribe(); this.addRequestSub = this.downloads.add(payload).subscribe((status: Status) => { if (status.status === 'error' && !this.cancelRequested) { - alert(`Error adding URL: ${status.msg}`); + this.toasts.error(`Error adding URL: ${status.msg}`); } else if (status.status !== 'error') { this.addUrl = ''; } @@ -1241,10 +1245,12 @@ export class App implements AfterViewInit, OnInit, OnDestroy { // file into memory only to have navigator.canShare reject it. if (download.size && download.size > App.SHARE_SIZE_WARN_BYTES) { const sizeMb = Math.round(download.size / 1024 / 1024); - const proceed = window.confirm( + const proceed = await this.toasts.confirm( `This file is ${sizeMb} MB. iOS' share sheet often refuses files ` + `larger than ~100 MB and the share will silently fail. ` + - `Try anyway? (Use the download button instead if it fails.)` + `Try anyway? (Use the download button instead if it fails.)`, + 'Try anyway', + 'Cancel', ); if (!proceed) return; } @@ -1265,7 +1271,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { // download button right next to this one instead of staring at // a button that quietly did nothing. console.warn('navigator.canShare rejected payload for', download.filename); - window.alert( + this.toasts.error( `Your device's share sheet doesn't accept this file ` + `(most likely because it's too large). ` + `Please use the download button instead.` @@ -1278,7 +1284,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { // AbortError = user dismissed the share sheet β†’ silent no-op. if (e.name === 'AbortError') return; console.error('Share failed:', err); - window.alert( + this.toasts.error( `Share failed: ${e.message || 'unknown error'}. ` + `Please use the download button instead.` ); @@ -1370,7 +1376,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { .map(url => url.trim()) .filter(url => url.length > 0); if (urls.length === 0) { - alert('No valid URLs found.'); + this.toasts.error('No valid URLs found.'); return; } this.importInProgress = true; @@ -1435,62 +1441,13 @@ export class App implements AfterViewInit, OnInit, OnDestroy { } // Export URLs based on filter: 'pending', 'completed', 'failed', or 'all' - exportBatchUrls(filter: 'pending' | 'completed' | 'failed' | 'all'): void { - let urls: string[]; - if (filter === 'pending') { - urls = Array.from(this.downloads.queue.values()).map(dl => dl.url); - } else if (filter === 'completed') { - // Only finished downloads in the "done" Map - urls = Array.from(this.downloads.done.values()).filter(dl => dl.status === 'finished').map(dl => dl.url); - } else if (filter === 'failed') { - // Only error downloads from the "done" Map - urls = Array.from(this.downloads.done.values()).filter(dl => dl.status === 'error').map(dl => dl.url); - } else { - // All: pending + both finished and error in done - urls = [ - ...Array.from(this.downloads.queue.values()).map(dl => dl.url), - ...Array.from(this.downloads.done.values()).map(dl => dl.url) - ]; - } - if (!urls.length) { - alert('No URLs found for the selected filter.'); - return; - } - const content = urls.join('\n'); - const blob = new Blob([content], { type: 'text/plain' }); - const downloadUrl = window.URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = downloadUrl; - a.download = 'metube_urls.txt'; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - window.URL.revokeObjectURL(downloadUrl); + exportBatchUrls(filter: BatchUrlFilter): void { + this.batchUrls.export(filter); } // Copy URLs to clipboard based on filter: 'pending', 'completed', 'failed', or 'all' - copyBatchUrls(filter: 'pending' | 'completed' | 'failed' | 'all'): void { - let urls: string[]; - if (filter === 'pending') { - urls = Array.from(this.downloads.queue.values()).map(dl => dl.url); - } else if (filter === 'completed') { - urls = Array.from(this.downloads.done.values()).filter(dl => dl.status === 'finished').map(dl => dl.url); - } else if (filter === 'failed') { - urls = Array.from(this.downloads.done.values()).filter(dl => dl.status === 'error').map(dl => dl.url); - } else { - urls = [ - ...Array.from(this.downloads.queue.values()).map(dl => dl.url), - ...Array.from(this.downloads.done.values()).map(dl => dl.url) - ]; - } - if (!urls.length) { - alert('No URLs found for the selected filter.'); - return; - } - const content = urls.join('\n'); - navigator.clipboard.writeText(content) - .then(() => alert('URLs copied to clipboard.')) - .catch(() => alert('Failed to copy URLs.')); + copyBatchUrls(filter: BatchUrlFilter): void { + this.batchUrls.copy(filter); } fetchVersionInfo(): void { @@ -1550,7 +1507,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { }; const fail = (err?: unknown) => { console.error('Clipboard write failed:', err); - alert('Failed to copy to clipboard. Your browser may require HTTPS for clipboard access.'); + this.toasts.error('Failed to copy to clipboard. Your browser may require HTTPS for clipboard access.'); }; if (navigator.clipboard?.writeText) { navigator.clipboard.writeText(text).then(done).catch(fail); @@ -1586,7 +1543,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.hasCookies = true; } else { this.refreshCookieStatus(); - alert(`Error uploading cookies: ${this.formatErrorMessage(response?.msg)}`); + this.toasts.error(`Error uploading cookies: ${this.formatErrorMessage(response?.msg)}`); } this.cookieUploadInProgress = false; input.value = ''; @@ -1595,7 +1552,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.refreshCookieStatus(); this.cookieUploadInProgress = false; input.value = ''; - alert('Error uploading cookies.'); + this.toasts.error('Error uploading cookies.'); } }); } @@ -1629,11 +1586,11 @@ export class App implements AfterViewInit, OnInit, OnDestroy { return; } this.refreshCookieStatus(); - alert(`Error deleting cookies: ${this.formatErrorMessage(response?.msg)}`); + this.toasts.error(`Error deleting cookies: ${this.formatErrorMessage(response?.msg)}`); }, error: () => { this.refreshCookieStatus(); - alert('Error deleting cookies.'); + this.toasts.error('Error deleting cookies.'); } }); } diff --git a/ui/src/app/components/index.ts b/ui/src/app/components/index.ts index 6aafae3..ff2a21b 100644 --- a/ui/src/app/components/index.ts +++ b/ui/src/app/components/index.ts @@ -1,2 +1,3 @@ export { SelectAllCheckboxComponent } from './master-checkbox.component'; -export { ItemCheckboxComponent } from './slave-checkbox.component'; \ No newline at end of file +export { ItemCheckboxComponent } from './slave-checkbox.component'; +export { ToastContainerComponent } from './toast-container.component'; \ No newline at end of file diff --git a/ui/src/app/components/toast-container.component.ts b/ui/src/app/components/toast-container.component.ts new file mode 100644 index 0000000..d03718a --- /dev/null +++ b/ui/src/app/components/toast-container.component.ts @@ -0,0 +1,58 @@ +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; +import { faCheckCircle, faTimesCircle, faInfoCircle, faXmark } from '@fortawesome/free-solid-svg-icons'; +import { ToastService } from '../services/toast.service'; + +@Component({ + selector: 'app-toast-container', + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [FontAwesomeModule], + template: ` +
+ @for (toast of toasts.toasts(); track toast.id) { + + } +
+`, +}) +export class ToastContainerComponent { + protected readonly toasts = inject(ToastService); + protected readonly faCheckCircle = faCheckCircle; + protected readonly faTimesCircle = faTimesCircle; + protected readonly faInfoCircle = faInfoCircle; + protected readonly faXmark = faXmark; +} diff --git a/ui/src/app/services/batch-urls.service.ts b/ui/src/app/services/batch-urls.service.ts new file mode 100644 index 0000000..47447b8 --- /dev/null +++ b/ui/src/app/services/batch-urls.service.ts @@ -0,0 +1,62 @@ +import { inject, Injectable } from '@angular/core'; +import { DownloadsService } from './downloads.service'; +import { ToastService } from './toast.service'; + +export type BatchUrlFilter = 'pending' | 'completed' | 'failed' | 'all'; + +/** + * Encapsulates collecting download URLs by status and exporting/copying them. + * Extracted from the main app component to keep it focused on view concerns. + */ +@Injectable({ providedIn: 'root' }) +export class BatchUrlsService { + private downloads = inject(DownloadsService); + private toasts = inject(ToastService); + + collect(filter: BatchUrlFilter): string[] { + const queueUrls = () => Array.from(this.downloads.queue.values()).map((dl) => dl.url); + const doneUrls = (status?: string) => + Array.from(this.downloads.done.values()) + .filter((dl) => status === undefined || dl.status === status) + .map((dl) => dl.url); + switch (filter) { + case 'pending': + return queueUrls(); + case 'completed': + return doneUrls('finished'); + case 'failed': + return doneUrls('error'); + default: + return [...queueUrls(), ...doneUrls()]; + } + } + + export(filter: BatchUrlFilter): void { + const urls = this.collect(filter); + if (!urls.length) { + this.toasts.info('No URLs found for the selected filter.'); + return; + } + const blob = new Blob([urls.join('\n')], { type: 'text/plain' }); + const downloadUrl = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = downloadUrl; + a.download = 'metube_urls.txt'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + window.URL.revokeObjectURL(downloadUrl); + } + + copy(filter: BatchUrlFilter): void { + const urls = this.collect(filter); + if (!urls.length) { + this.toasts.info('No URLs found for the selected filter.'); + return; + } + navigator.clipboard + .writeText(urls.join('\n')) + .then(() => this.toasts.success('URLs copied to clipboard.')) + .catch(() => this.toasts.error('Failed to copy URLs.')); + } +} diff --git a/ui/src/app/services/index.ts b/ui/src/app/services/index.ts index 21ef047..8db6fec 100644 --- a/ui/src/app/services/index.ts +++ b/ui/src/app/services/index.ts @@ -1,2 +1,4 @@ export { DownloadsService } from './downloads.service'; -export { MeTubeSocket } from './metube-socket.service'; \ No newline at end of file +export { MeTubeSocket } from './metube-socket.service'; +export { ToastService } from './toast.service'; +export { BatchUrlsService } from './batch-urls.service'; \ No newline at end of file diff --git a/ui/src/app/services/toast.service.ts b/ui/src/app/services/toast.service.ts new file mode 100644 index 0000000..71ab21b --- /dev/null +++ b/ui/src/app/services/toast.service.ts @@ -0,0 +1,86 @@ +import { Injectable, signal } from '@angular/core'; + +export type ToastLevel = 'info' | 'success' | 'error'; + +export interface ToastAction { + label: string; + value: boolean; + primary?: boolean; +} + +export interface Toast { + id: number; + level: ToastLevel; + message: string; + actions?: ToastAction[]; + /** Resolver for confirm() toasts; resolved when the user picks an action or dismisses. */ + _resolve?: (value: boolean) => void; +} + +/** + * Lightweight non-blocking notification service. Replaces the blocking + * window.alert()/confirm() dialogs that previously littered the app component. + */ +@Injectable({ providedIn: 'root' }) +export class ToastService { + private counter = 0; + readonly toasts = signal([]); + + info(message: string): void { + this.show('info', message, 4000); + } + + success(message: string): void { + this.show('success', message, 4000); + } + + error(message: string): void { + this.show('error', message, 8000); + } + + /** + * Show a confirmation toast with confirm/cancel actions. Resolves true when + * confirmed, false when cancelled or auto-dismissed. + */ + confirm(message: string, confirmLabel = 'OK', cancelLabel = 'Cancel'): Promise { + return new Promise((resolve) => { + const id = ++this.counter; + this.toasts.update((list) => [ + ...list, + { + id, + level: 'info', + message, + actions: [ + { label: cancelLabel, value: false }, + { label: confirmLabel, value: true, primary: true }, + ], + _resolve: resolve, + }, + ]); + }); + } + + respond(id: number, value: boolean): void { + const toast = this.toasts().find((t) => t.id === id); + toast?._resolve?.(value); + this.remove(id); + } + + dismiss(id: number): void { + const toast = this.toasts().find((t) => t.id === id); + // A confirm toast dismissed without an explicit choice resolves to false. + toast?._resolve?.(false); + this.remove(id); + } + + private remove(id: number): void { + this.toasts.update((list) => list.filter((t) => t.id !== id)); + } + + private show(level: ToastLevel, message: string, autoDismissMs: number): void { + const id = ++this.counter; + this.toasts.update((list) => [...list, { id, level, message }]); + setTimeout(() => this.remove(id), autoDismissMs); + } +} From 37f7af05550019611e43e84d5de22188746ad476 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Tue, 16 Jun 2026 21:37:05 +0300 Subject: [PATCH 56/88] fix batch download (closes #1008) --- ui/src/app/app.ts | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index 4f58747..e48161e 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -1189,19 +1189,39 @@ export class App implements AfterViewInit, OnInit, OnDestroy { }); } - downloadSelectedFiles() { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.downloads.done.forEach((dl, _) => { + // Chromium-based browsers silently drop programmatic downloads beyond ~10 when + // triggered in a tight loop. Trigger in batches with a short pause in between so + // large selections download cleanly. See issue #1008. + private static readonly DOWNLOAD_BATCH_SIZE = 10; + private static readonly DOWNLOAD_BATCH_DELAY_MS = 1000; + + async downloadSelectedFiles() { + const selected: Download[] = []; + this.downloads.done.forEach((dl) => { if (dl.status === 'finished' && dl.checked) { - const link = document.createElement('a'); - link.href = this.buildDownloadLink(dl); - link.setAttribute('download', dl.filename); - link.setAttribute('target', '_self'); - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); + selected.push(dl); } }); + + for (let i = 0; i < selected.length; i++) { + const dl = selected[i]; + const link = document.createElement('a'); + link.href = this.buildDownloadLink(dl); + link.setAttribute('download', dl.filename); + link.setAttribute('target', '_self'); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + if ( + (i + 1) % App.DOWNLOAD_BATCH_SIZE === 0 && + i + 1 < selected.length + ) { + await new Promise((resolve) => + setTimeout(resolve, App.DOWNLOAD_BATCH_DELAY_MS), + ); + } + } } buildDownloadLink(download: Download) { From 64d0d628783814d1d04b889a14aff436c9994147 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Tue, 16 Jun 2026 21:47:07 +0300 Subject: [PATCH 57/88] fix empty PUBLIC_HOST_AUDIO_URL handling (closes #1010) --- app/main.py | 7 +++++++ app/tests/test_config.py | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/app/main.py b/app/main.py index f6aed10..d2283ca 100644 --- a/app/main.py +++ b/app/main.py @@ -112,6 +112,13 @@ class Config: if not self.URL_PREFIX.endswith('/'): self.URL_PREFIX += '/' + # A blank PUBLIC_HOST_AUDIO_URL (e.g. set empty in a compose file) bypasses the + # default via os.environ.get, which would leave audio links root-relative and 404. + # Fall back to the 'audio_download/' route that serves AUDIO_DOWNLOAD_DIR. When + # PUBLIC_HOST_URL is also blank we leave it blank to preserve serving from web root. + if not self.PUBLIC_HOST_AUDIO_URL and self.PUBLIC_HOST_URL: + self.PUBLIC_HOST_AUDIO_URL = self._DEFAULTS['PUBLIC_HOST_AUDIO_URL'] + for attr in ('PUBLIC_HOST_URL', 'PUBLIC_HOST_AUDIO_URL'): val = getattr(self, attr) if val and not val.endswith('/'): diff --git a/app/tests/test_config.py b/app/tests/test_config.py index bbb6c6d..b918a93 100644 --- a/app/tests/test_config.py +++ b/app/tests/test_config.py @@ -51,6 +51,19 @@ class ConfigTests(unittest.TestCase): self.assertEqual(c.PUBLIC_HOST_URL, "") self.assertEqual(c.PUBLIC_HOST_AUDIO_URL, "") + def test_blank_audio_host_falls_back_to_audio_download_route(self): + # Regression: a present-but-blank PUBLIC_HOST_AUDIO_URL must not stay empty + # (which produced root-relative, 404ing audio links). It falls back to the + # 'audio_download/' route that serves AUDIO_DOWNLOAD_DIR. + with patch.dict( + os.environ, + _base_env(PUBLIC_HOST_URL="https://ytdl.example.com", PUBLIC_HOST_AUDIO_URL=""), + clear=False, + ): + c = Config() + self.assertEqual(c.PUBLIC_HOST_URL, "https://ytdl.example.com/") + self.assertEqual(c.PUBLIC_HOST_AUDIO_URL, "audio_download/") + def test_public_host_url_already_slashed_unchanged(self): with patch.dict( os.environ, From b73e95f40598fc595116d3f5ae496b47234394dc Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Tue, 16 Jun 2026 21:57:07 +0300 Subject: [PATCH 58/88] upgrade dependencies --- ui/package.json | 6 +- ui/pnpm-lock.yaml | 582 +++++++++++++++++++++++----------------------- uv.lock | 24 +- 3 files changed, 306 insertions(+), 306 deletions(-) diff --git a/ui/package.json b/ui/package.json index ef3b4b5..0f5280d 100644 --- a/ui/package.json +++ b/ui/package.json @@ -48,8 +48,8 @@ }, "devDependencies": { "@angular-eslint/builder": "21.1.0", - "@angular/build": "^21.2.14", - "@angular/cli": "^21.2.14", + "@angular/build": "^21.2.15", + "@angular/cli": "^21.2.15", "@angular/compiler-cli": "^21.2.17", "@angular/localize": "^21.2.17", "@eslint/js": "^9.39.4", @@ -58,6 +58,6 @@ "jsdom": "^27.4.0", "typescript": "~5.9.3", "typescript-eslint": "8.47.0", - "vitest": "^4.1.8" + "vitest": "^4.1.9" } } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index c787cbd..5aa9c96 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -77,13 +77,13 @@ importers: devDependencies: '@angular-eslint/builder': specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular/build': - specifier: ^21.2.14 - version: 21.2.14(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.3)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.8(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3))) + specifier: ^21.2.15 + version: 21.2.15(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.3)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3))) '@angular/cli': - specifier: ^21.2.14 - version: 21.2.14(@types/node@25.9.3)(chokidar@5.0.0) + specifier: ^21.2.15 + version: 21.2.15(@types/node@25.9.3)(chokidar@5.0.0) '@angular/compiler-cli': specifier: ^21.2.17 version: 21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3) @@ -95,7 +95,7 @@ importers: version: 9.39.4 angular-eslint: specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) eslint: specifier: ^9.39.4 version: 9.39.4 @@ -109,8 +109,8 @@ importers: specifier: 8.47.0 version: 8.47.0(eslint@9.39.4)(typescript@5.9.3) vitest: - specifier: ^4.1.8 - version: 4.1.8(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) + specifier: ^4.1.9 + version: 4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) packages: @@ -177,13 +177,13 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.2102.14': - resolution: {integrity: sha512-0+vjVsCkMyJdVjz5XkPW+Bdf/9TI8V2voomx/+o0o+oOaqqiEhptQWFnaIlLr7HasjB0LxXK5P9L0oQ61vxj8Q==} + '@angular-devkit/architect@0.2102.15': + resolution: {integrity: sha512-EZQXu6j7J7OUxmpxIO2mxd58NTlCb7HOAOfLLGdk7lRcwZeCxnjGRzb72tXlJgIEi3IoOYwcW0ft2cg7r/d6qA==} 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'} hasBin: true - '@angular-devkit/core@21.2.14': - resolution: {integrity: sha512-RSOWXB9bFc2nwRWMxbIT0RbSNFUrwfBo4N5MNxbyQ69Ndc0gVm3h+3ArHv0qotH4d+pJYbm5ttXu8YqR2kc0CA==} + '@angular-devkit/core@21.2.15': + resolution: {integrity: sha512-x7EwuQtMGHANVznHpwyEi/3lMo/kRoaxV2w7lXbRGjTezwv4SPaOBwhrIGCbAVFs5B1ziff4jZzors4owlCTgg==} 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: ^5.0.0 @@ -191,8 +191,8 @@ packages: chokidar: optional: true - '@angular-devkit/schematics@21.2.14': - resolution: {integrity: sha512-KMJlQSBEzI4+Cy1Zh72gmGQNN2I1vY+nj9CoRcZPBIi1si+0ZAc49XT85eYl+eQumNTVQviUG7LQqgLDAHml+g==} + '@angular-devkit/schematics@21.2.15': + resolution: {integrity: sha512-kHHV694KUPuXlItjvnxUspn+KYjlTKu8KINX/kT1qlogzLYojpnRwcUtZyRPUz1f/M1d3TUHVFeBKx+h5HoNcA==} 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'} '@angular-eslint/builder@21.1.0': @@ -246,8 +246,8 @@ packages: peerDependencies: '@angular/core': 21.2.17 - '@angular/build@21.2.14': - resolution: {integrity: sha512-l8JB326iIwum2WmbopUUFdiuYsbHchix6MH8o6F6FA7LJr8QLTvipwwbw+Jx31/RE50WkGmzsZ1fBDw/cMbmUw==} + '@angular/build@21.2.15': + resolution: {integrity: sha512-APJ5v0/hL38CKSqSQDos5Y8/O2LSiSSqP9FagdcMT5j9JAlENXdhVaA3IxPMXDWcxtzD5T1IlN8Z9aFxDXxHyg==} 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: '@angular/compiler': ^21.0.0 @@ -257,7 +257,7 @@ packages: '@angular/platform-browser': ^21.0.0 '@angular/platform-server': ^21.0.0 '@angular/service-worker': ^21.0.0 - '@angular/ssr': ^21.2.14 + '@angular/ssr': ^21.2.15 karma: ^6.4.0 less: ^4.2.0 ng-packagr: ^21.0.0 @@ -292,8 +292,8 @@ packages: vitest: optional: true - '@angular/cli@21.2.14': - resolution: {integrity: sha512-S8jExTjxPJILwpg2lu3DohSASVZ8DLhSNCmOe7z0qF9VskRSjC7SIQv1rq36tsJkenxuA72gjVOHZv+uSRT8HA==} + '@angular/cli@21.2.15': + resolution: {integrity: sha512-8CT1iST5CwdxHEzC0NVFUsMenAY7aE30ayTAw5PpA6MPs6bdHlL6QxTmkqThW7hoCj6PJ56YfQMzSQU+aYRLQA==} 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'} hasBin: true @@ -472,8 +472,8 @@ packages: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-color-parser@4.1.3': - resolution: {integrity: sha512-DOgvIPkikIOixQRlD4YF31VN6fLLUTdrzhfRbis8vm0kMTgIbEPX0Ip/YX9fOeV9iywAS4sUUbTclpan7yYP8Q==} + '@csstools/css-color-parser@4.1.7': + resolution: {integrity: sha512-CmjJFQTFQx/U/xNJhSjCQ0ilpesPmNQ8+eOUeM/+kDOVW33qsIjeOXc27vrQDdWVkf83ZSWwtg7kXSUvKDJ8cQ==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -1371,146 +1371,146 @@ packages: '@rolldown/pluginutils@1.0.0-rc.4': resolution: {integrity: sha512-1BrrmTu0TWfOP1riA8uakjFc9bpIUGzVKETsOtzY39pPga8zELGDl8eu1Dx7/gjM5CAz14UknsUMpBO8L+YntQ==} - '@rollup/rollup-android-arm-eabi@4.61.1': - resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==} + '@rollup/rollup-android-arm-eabi@4.62.0': + resolution: {integrity: sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.61.1': - resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==} + '@rollup/rollup-android-arm64@4.62.0': + resolution: {integrity: sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.61.1': - resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==} + '@rollup/rollup-darwin-arm64@4.62.0': + resolution: {integrity: sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.61.1': - resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==} + '@rollup/rollup-darwin-x64@4.62.0': + resolution: {integrity: sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.61.1': - resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==} + '@rollup/rollup-freebsd-arm64@4.62.0': + resolution: {integrity: sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.61.1': - resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==} + '@rollup/rollup-freebsd-x64@4.62.0': + resolution: {integrity: sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.61.1': - resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==} + '@rollup/rollup-linux-arm-gnueabihf@4.62.0': + resolution: {integrity: sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.61.1': - resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==} + '@rollup/rollup-linux-arm-musleabihf@4.62.0': + resolution: {integrity: sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.61.1': - resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==} + '@rollup/rollup-linux-arm64-gnu@4.62.0': + resolution: {integrity: sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.61.1': - resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==} + '@rollup/rollup-linux-arm64-musl@4.62.0': + resolution: {integrity: sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.61.1': - resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==} + '@rollup/rollup-linux-loong64-gnu@4.62.0': + resolution: {integrity: sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.61.1': - resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==} + '@rollup/rollup-linux-loong64-musl@4.62.0': + resolution: {integrity: sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.61.1': - resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==} + '@rollup/rollup-linux-ppc64-gnu@4.62.0': + resolution: {integrity: sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.61.1': - resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==} + '@rollup/rollup-linux-ppc64-musl@4.62.0': + resolution: {integrity: sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.61.1': - resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==} + '@rollup/rollup-linux-riscv64-gnu@4.62.0': + resolution: {integrity: sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.61.1': - resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==} + '@rollup/rollup-linux-riscv64-musl@4.62.0': + resolution: {integrity: sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.61.1': - resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==} + '@rollup/rollup-linux-s390x-gnu@4.62.0': + resolution: {integrity: sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.61.1': - resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==} + '@rollup/rollup-linux-x64-gnu@4.62.0': + resolution: {integrity: sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.61.1': - resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==} + '@rollup/rollup-linux-x64-musl@4.62.0': + resolution: {integrity: sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.61.1': - resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==} + '@rollup/rollup-openbsd-x64@4.62.0': + resolution: {integrity: sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.61.1': - resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==} + '@rollup/rollup-openharmony-arm64@4.62.0': + resolution: {integrity: sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.61.1': - resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==} + '@rollup/rollup-win32-arm64-msvc@4.62.0': + resolution: {integrity: sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.61.1': - resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==} + '@rollup/rollup-win32-ia32-msvc@4.62.0': + resolution: {integrity: sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.61.1': - resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==} + '@rollup/rollup-win32-x64-gnu@4.62.0': + resolution: {integrity: sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.61.1': - resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==} + '@rollup/rollup-win32-x64-msvc@4.62.0': + resolution: {integrity: sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==} cpu: [x64] os: [win32] - '@schematics/angular@21.2.14': - resolution: {integrity: sha512-rIEdtNTdCCTwuo7B4tMoq5qmbLXdBgmW6Ays1hyno//4OE+HFtvlWZd+hl6KceEyN00IcZ2HRaPnfd71E1JnoA==} + '@schematics/angular@21.2.15': + resolution: {integrity: sha512-xdTirIUUegMXtyEDsYabCRcAnlVatVYb6S8v77fgK2eqUhGalI2e/3+L51N3XF1+6K2vEhyDpmmhFZLsdhLYbw==} 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'} '@sigstore/bundle@4.0.0': @@ -1611,8 +1611,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.61.0': - resolution: {integrity: sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==} + '@typescript-eslint/project-service@8.61.1': + resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1621,8 +1621,8 @@ packages: resolution: {integrity: sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.61.0': - resolution: {integrity: sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==} + '@typescript-eslint/scope-manager@8.61.1': + resolution: {integrity: sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.47.0': @@ -1631,8 +1631,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/tsconfig-utils@8.61.0': - resolution: {integrity: sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==} + '@typescript-eslint/tsconfig-utils@8.61.1': + resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1648,8 +1648,8 @@ packages: resolution: {integrity: sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.61.0': - resolution: {integrity: sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==} + '@typescript-eslint/types@8.61.1': + resolution: {integrity: sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.47.0': @@ -1658,8 +1658,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.61.0': - resolution: {integrity: sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==} + '@typescript-eslint/typescript-estree@8.61.1': + resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1671,8 +1671,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.61.0': - resolution: {integrity: sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==} + '@typescript-eslint/utils@8.61.1': + resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -1682,8 +1682,8 @@ packages: resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.61.0': - resolution: {integrity: sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==} + '@typescript-eslint/visitor-keys@8.61.1': + resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-basic-ssl@2.1.4': @@ -1692,11 +1692,11 @@ packages: peerDependencies: vite: ^6.0.0 || ^7.0.0 - '@vitest/expect@4.1.8': - resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} + '@vitest/expect@4.1.9': + resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} - '@vitest/mocker@4.1.8': - resolution: {integrity: sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==} + '@vitest/mocker@4.1.9': + resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1706,20 +1706,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.8': - resolution: {integrity: sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==} + '@vitest/pretty-format@4.1.9': + resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==} - '@vitest/runner@4.1.8': - resolution: {integrity: sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==} + '@vitest/runner@4.1.9': + resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==} - '@vitest/snapshot@4.1.8': - resolution: {integrity: sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==} + '@vitest/snapshot@4.1.9': + resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==} - '@vitest/spy@4.1.8': - resolution: {integrity: sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==} + '@vitest/spy@4.1.9': + resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==} - '@vitest/utils@4.1.8': - resolution: {integrity: sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==} + '@vitest/utils@4.1.9': + resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} '@yarnpkg/lockfile@1.1.0': resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} @@ -1741,8 +1741,8 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.16.0: - resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} engines: {node: '>=0.4.0'} hasBin: true @@ -1825,8 +1825,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.10.35: - resolution: {integrity: sha512-honAfLBde0HAFLdNyBEfuuENkF6zR+ozxqxa/2zJKHBe1qzLqyTSeRKpdPEHAP03rlDGyQOPnCSxnVpVqQo9Mg==} + baseline-browser-mapping@2.10.37: + resolution: {integrity: sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==} engines: {node: '>=6.0.0'} hasBin: true @@ -1837,8 +1837,8 @@ packages: bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} - body-parser@2.2.2: - resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} + body-parser@2.3.0: + resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} engines: {node: '>=18'} boolbase@1.0.0: @@ -1891,8 +1891,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001797: - resolution: {integrity: sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==} + caniuse-lite@1.0.30001799: + resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -2053,8 +2053,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.371: - resolution: {integrity: sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w==} + electron-to-chromium@1.5.372: + resolution: {integrity: sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2768,8 +2768,8 @@ packages: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} - obug@2.1.2: - resolution: {integrity: sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==} + obug@2.1.3: + resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} engines: {node: '>=12.20.0'} on-finished@2.4.1: @@ -2952,8 +2952,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.61.1: - resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==} + rollup@4.62.0: + resolution: {integrity: sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -3175,11 +3175,11 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tldts-core@7.4.2: - resolution: {integrity: sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA==} + tldts-core@7.4.3: + resolution: {integrity: sha512-27ep5H9PzdBrNd5OFM/j3WCU8F3kPwM9D0BOaOf7uYfxMJfyr0K5Tjj69Gri+sZlh2WXd5buIm47NuPF29CDiw==} - tldts@7.4.2: - resolution: {integrity: sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw==} + tldts@7.4.3: + resolution: {integrity: sha512-A3BDQBeeukYPzB4QdQ1DtdlUmp4x2OCH8n5UVhEWbyANxNep8GavottKzd1xYKFJKjUgMyPT7EzOfnBO55s8Sg==} hasBin: true to-regex-range@5.0.1: @@ -3234,8 +3234,8 @@ packages: undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - undici@6.26.0: - resolution: {integrity: sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==} + undici@6.27.0: + resolution: {integrity: sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==} engines: {node: '>=18.17'} undici@7.24.4: @@ -3303,20 +3303,20 @@ packages: yaml: optional: true - vitest@4.1.8: - resolution: {integrity: sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==} + vitest@4.1.9: + resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.8 - '@vitest/browser-preview': 4.1.8 - '@vitest/browser-webdriverio': 4.1.8 - '@vitest/coverage-istanbul': 4.1.8 - '@vitest/coverage-v8': 4.1.8 - '@vitest/ui': 4.1.8 + '@vitest/browser-playwright': 4.1.9 + '@vitest/browser-preview': 4.1.9 + '@vitest/browser-webdriverio': 4.1.9 + '@vitest/coverage-istanbul': 4.1.9 + '@vitest/coverage-v8': 4.1.9 + '@vitest/ui': 4.1.9 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3574,14 +3574,14 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.2102.14(chokidar@5.0.0)': + '@angular-devkit/architect@0.2102.15(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.14(chokidar@5.0.0) + '@angular-devkit/core': 21.2.15(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/core@21.2.14(chokidar@5.0.0)': + '@angular-devkit/core@21.2.15(chokidar@5.0.0)': dependencies: ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) @@ -3592,9 +3592,9 @@ snapshots: optionalDependencies: chokidar: 5.0.0 - '@angular-devkit/schematics@21.2.14(chokidar@5.0.0)': + '@angular-devkit/schematics@21.2.15(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.14(chokidar@5.0.0) + '@angular-devkit/core': 21.2.15(chokidar@5.0.0) jsonc-parser: 3.3.1 magic-string: 0.30.21 ora: 9.3.0 @@ -3602,11 +3602,11 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-eslint/builder@21.1.0(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/builder@21.1.0(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@angular-devkit/architect': 0.2102.14(chokidar@5.0.0) - '@angular-devkit/core': 21.2.14(chokidar@5.0.0) - '@angular/cli': 21.2.14(@types/node@25.9.3)(chokidar@5.0.0) + '@angular-devkit/architect': 0.2102.15(chokidar@5.0.0) + '@angular-devkit/core': 21.2.15(chokidar@5.0.0) + '@angular/cli': 21.2.15(@types/node@25.9.3)(chokidar@5.0.0) eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: @@ -3614,34 +3614,34 @@ snapshots: '@angular-eslint/bundled-angular-compiler@21.1.0': {} - '@angular-eslint/eslint-plugin-template@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.0)(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin-template@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/utils': 8.61.0(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.9.3) aria-query: 5.3.2 axobject-query: 4.1.0 eslint: 9.39.4 typescript: 5.9.3 - '@angular-eslint/eslint-plugin@21.1.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin@21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/utils': 8.61.0(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 - '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(@typescript-eslint/types@8.61.0)(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@angular-devkit/core': 21.2.14(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.14(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.0)(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.14(@types/node@25.9.3)(chokidar@5.0.0) + '@angular-devkit/core': 21.2.15(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.15(chokidar@5.0.0) + '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular/cli': 21.2.15(@types/node@25.9.3)(chokidar@5.0.0) ignore: 7.0.5 semver: 7.7.3 strip-json-comments: 3.1.1 @@ -3660,10 +3660,10 @@ snapshots: eslint-scope: 9.1.2 typescript: 5.9.3 - '@angular-eslint/utils@21.1.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/utils@21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@typescript-eslint/utils': 8.61.0(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 @@ -3672,10 +3672,10 @@ snapshots: '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - '@angular/build@21.2.14(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.3)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.8(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)))': + '@angular/build@21.2.15(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.3)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)))': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2102.14(chokidar@5.0.0) + '@angular-devkit/architect': 0.2102.15(chokidar@5.0.0) '@angular/compiler': 21.2.17 '@angular/compiler-cli': 21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3) '@babel/core': 7.29.0 @@ -3712,7 +3712,7 @@ snapshots: '@angular/service-worker': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) lmdb: 3.5.1 postcss: 8.5.15 - vitest: 4.1.8(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) + vitest: 4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -3728,15 +3728,15 @@ snapshots: - tsx - yaml - '@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0)': + '@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2102.14(chokidar@5.0.0) - '@angular-devkit/core': 21.2.14(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.14(chokidar@5.0.0) + '@angular-devkit/architect': 0.2102.15(chokidar@5.0.0) + '@angular-devkit/core': 21.2.15(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.15(chokidar@5.0.0) '@inquirer/prompts': 7.10.1(@types/node@25.9.3) '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.3))(@types/node@25.9.3)(listr2@9.0.5) '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) - '@schematics/angular': 21.2.14(chokidar@5.0.0) + '@schematics/angular': 21.2.15(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 algoliasearch: 5.48.1 ini: 6.0.0 @@ -3833,7 +3833,7 @@ snapshots: '@asamuzakjp/css-color@4.1.2': dependencies: '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.1.3(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.7(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 lru-cache: 11.5.1 @@ -3963,7 +3963,7 @@ snapshots: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-color-parser@4.1.3(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.1.7(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/color-helpers': 6.0.2 '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) @@ -4665,85 +4665,85 @@ snapshots: '@rolldown/pluginutils@1.0.0-rc.4': {} - '@rollup/rollup-android-arm-eabi@4.61.1': + '@rollup/rollup-android-arm-eabi@4.62.0': optional: true - '@rollup/rollup-android-arm64@4.61.1': + '@rollup/rollup-android-arm64@4.62.0': optional: true - '@rollup/rollup-darwin-arm64@4.61.1': + '@rollup/rollup-darwin-arm64@4.62.0': optional: true - '@rollup/rollup-darwin-x64@4.61.1': + '@rollup/rollup-darwin-x64@4.62.0': optional: true - '@rollup/rollup-freebsd-arm64@4.61.1': + '@rollup/rollup-freebsd-arm64@4.62.0': optional: true - '@rollup/rollup-freebsd-x64@4.61.1': + '@rollup/rollup-freebsd-x64@4.62.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.61.1': + '@rollup/rollup-linux-arm-gnueabihf@4.62.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.61.1': + '@rollup/rollup-linux-arm-musleabihf@4.62.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.61.1': + '@rollup/rollup-linux-arm64-gnu@4.62.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.61.1': + '@rollup/rollup-linux-arm64-musl@4.62.0': optional: true - '@rollup/rollup-linux-loong64-gnu@4.61.1': + '@rollup/rollup-linux-loong64-gnu@4.62.0': optional: true - '@rollup/rollup-linux-loong64-musl@4.61.1': + '@rollup/rollup-linux-loong64-musl@4.62.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.61.1': + '@rollup/rollup-linux-ppc64-gnu@4.62.0': optional: true - '@rollup/rollup-linux-ppc64-musl@4.61.1': + '@rollup/rollup-linux-ppc64-musl@4.62.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.61.1': + '@rollup/rollup-linux-riscv64-gnu@4.62.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.61.1': + '@rollup/rollup-linux-riscv64-musl@4.62.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.61.1': + '@rollup/rollup-linux-s390x-gnu@4.62.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.61.1': + '@rollup/rollup-linux-x64-gnu@4.62.0': optional: true - '@rollup/rollup-linux-x64-musl@4.61.1': + '@rollup/rollup-linux-x64-musl@4.62.0': optional: true - '@rollup/rollup-openbsd-x64@4.61.1': + '@rollup/rollup-openbsd-x64@4.62.0': optional: true - '@rollup/rollup-openharmony-arm64@4.61.1': + '@rollup/rollup-openharmony-arm64@4.62.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.61.1': + '@rollup/rollup-win32-arm64-msvc@4.62.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.61.1': + '@rollup/rollup-win32-ia32-msvc@4.62.0': optional: true - '@rollup/rollup-win32-x64-gnu@4.61.1': + '@rollup/rollup-win32-x64-gnu@4.62.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.61.1': + '@rollup/rollup-win32-x64-msvc@4.62.0': optional: true - '@schematics/angular@21.2.14(chokidar@5.0.0)': + '@schematics/angular@21.2.15(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.14(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.14(chokidar@5.0.0) + '@angular-devkit/core': 21.2.15(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.15(chokidar@5.0.0) jsonc-parser: 3.3.1 transitivePeerDependencies: - chokidar @@ -4880,10 +4880,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.61.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.61.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@5.9.3) - '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.9.3) + '@typescript-eslint/types': 8.61.1 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -4894,16 +4894,16 @@ snapshots: '@typescript-eslint/types': 8.47.0 '@typescript-eslint/visitor-keys': 8.47.0 - '@typescript-eslint/scope-manager@8.61.0': + '@typescript-eslint/scope-manager@8.61.1': dependencies: - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/visitor-keys': 8.61.0 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 '@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.61.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.61.1(typescript@5.9.3)': dependencies: typescript: 5.9.3 @@ -4921,7 +4921,7 @@ snapshots: '@typescript-eslint/types@8.47.0': {} - '@typescript-eslint/types@8.61.0': {} + '@typescript-eslint/types@8.61.1': {} '@typescript-eslint/typescript-estree@8.47.0(typescript@5.9.3)': dependencies: @@ -4939,12 +4939,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.61.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.61.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.61.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@5.9.3) - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/visitor-keys': 8.61.0 + '@typescript-eslint/project-service': 8.61.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.9.3) + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/visitor-keys': 8.61.1 debug: 4.4.3 minimatch: 10.2.5 semver: 7.8.4 @@ -4965,12 +4965,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3)': + '@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) - '@typescript-eslint/scope-manager': 8.61.0 - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/typescript-estree': 8.61.0(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.61.1 + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: @@ -4981,53 +4981,53 @@ snapshots: '@typescript-eslint/types': 8.47.0 eslint-visitor-keys: 4.2.1 - '@typescript-eslint/visitor-keys@8.61.0': + '@typescript-eslint/visitor-keys@8.61.1': dependencies: - '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/types': 8.61.1 eslint-visitor-keys: 5.0.1 '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3))': dependencies: vite: 7.3.2(@types/node@25.9.3)(sass@1.97.3) - '@vitest/expect@4.1.8': + '@vitest/expect@4.1.9': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.8 - '@vitest/utils': 4.1.8 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.8(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3))': + '@vitest/mocker@4.1.9(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3))': dependencies: - '@vitest/spy': 4.1.8 + '@vitest/spy': 4.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 7.3.2(@types/node@25.9.3)(sass@1.97.3) - '@vitest/pretty-format@4.1.8': + '@vitest/pretty-format@4.1.9': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.8': + '@vitest/runner@4.1.9': dependencies: - '@vitest/utils': 4.1.8 + '@vitest/utils': 4.1.9 pathe: 2.0.3 - '@vitest/snapshot@4.1.8': + '@vitest/snapshot@4.1.9': dependencies: - '@vitest/pretty-format': 4.1.8 - '@vitest/utils': 4.1.8 + '@vitest/pretty-format': 4.1.9 + '@vitest/utils': 4.1.9 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.8': {} + '@vitest/spy@4.1.9': {} - '@vitest/utils@4.1.8': + '@vitest/utils@4.1.9': dependencies: - '@vitest/pretty-format': 4.1.8 + '@vitest/pretty-format': 4.1.9 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -5045,11 +5045,11 @@ snapshots: mime-types: 3.0.2 negotiator: 1.0.0 - acorn-jsx@5.3.2(acorn@8.16.0): + acorn-jsx@5.3.2(acorn@8.17.0): dependencies: - acorn: 8.16.0 + acorn: 8.17.0 - acorn@8.16.0: {} + acorn@8.17.0: {} agent-base@7.1.4: {} @@ -5099,18 +5099,18 @@ snapshots: '@algolia/requester-fetch': 5.48.1 '@algolia/requester-node-http': 5.48.1 - angular-eslint@21.1.0(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): + angular-eslint@21.1.0(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): dependencies: - '@angular-devkit/core': 21.2.14(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.14(chokidar@5.0.0) - '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.0)(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.14(@types/node@25.9.3)(chokidar@5.0.0))(@typescript-eslint/types@8.61.0)(@typescript-eslint/utils@8.61.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-devkit/core': 21.2.15(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.15(chokidar@5.0.0) + '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.14(@types/node@25.9.3)(chokidar@5.0.0) - '@typescript-eslint/types': 8.61.0 - '@typescript-eslint/utils': 8.61.0(eslint@9.39.4)(typescript@5.9.3) + '@angular/cli': 21.2.15(@types/node@25.9.3)(chokidar@5.0.0) + '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 typescript-eslint: 8.47.0(eslint@9.39.4)(typescript@5.9.3) @@ -5146,7 +5146,7 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.10.35: {} + baseline-browser-mapping@2.10.37: {} beasties@0.4.1: dependencies: @@ -5164,10 +5164,10 @@ snapshots: dependencies: require-from-string: 2.0.2 - body-parser@2.2.2: + body-parser@2.3.0: dependencies: bytes: 3.1.2 - content-type: 1.0.5 + content-type: 2.0.0 debug: 4.4.3 http-errors: 2.0.1 iconv-lite: 0.7.2 @@ -5203,9 +5203,9 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.35 - caniuse-lite: 1.0.30001797 - electron-to-chromium: 1.5.371 + baseline-browser-mapping: 2.10.37 + caniuse-lite: 1.0.30001799 + electron-to-chromium: 1.5.372 node-releases: 2.0.47 update-browserslist-db: 1.2.3(browserslist@4.28.2) @@ -5238,7 +5238,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001797: {} + caniuse-lite@1.0.30001799: {} chai@6.2.2: {} @@ -5383,7 +5383,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.371: {} + electron-to-chromium@1.5.372: {} emoji-regex@10.6.0: {} @@ -5540,8 +5540,8 @@ snapshots: espree@10.4.0: dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) eslint-visitor-keys: 4.2.1 esquery@1.7.0: @@ -5582,7 +5582,7 @@ snapshots: express@5.2.1: dependencies: accepts: 2.0.0 - body-parser: 2.2.2 + body-parser: 2.3.0 content-disposition: 1.1.0 content-type: 1.0.5 cookie: 0.7.2 @@ -6132,7 +6132,7 @@ snapshots: semver: 7.7.4 tar: 7.5.16 tinyglobby: 0.2.17 - undici: 6.26.0 + undici: 6.27.0 which: 6.0.1 node-releases@2.0.47: {} @@ -6191,7 +6191,7 @@ snapshots: object-inspect@1.13.4: {} - obug@2.1.2: {} + obug@2.1.3: {} on-finished@2.4.1: dependencies: @@ -6391,35 +6391,35 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - rollup@4.61.1: + rollup@4.62.0: dependencies: '@types/estree': 1.0.9 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.61.1 - '@rollup/rollup-android-arm64': 4.61.1 - '@rollup/rollup-darwin-arm64': 4.61.1 - '@rollup/rollup-darwin-x64': 4.61.1 - '@rollup/rollup-freebsd-arm64': 4.61.1 - '@rollup/rollup-freebsd-x64': 4.61.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.61.1 - '@rollup/rollup-linux-arm-musleabihf': 4.61.1 - '@rollup/rollup-linux-arm64-gnu': 4.61.1 - '@rollup/rollup-linux-arm64-musl': 4.61.1 - '@rollup/rollup-linux-loong64-gnu': 4.61.1 - '@rollup/rollup-linux-loong64-musl': 4.61.1 - '@rollup/rollup-linux-ppc64-gnu': 4.61.1 - '@rollup/rollup-linux-ppc64-musl': 4.61.1 - '@rollup/rollup-linux-riscv64-gnu': 4.61.1 - '@rollup/rollup-linux-riscv64-musl': 4.61.1 - '@rollup/rollup-linux-s390x-gnu': 4.61.1 - '@rollup/rollup-linux-x64-gnu': 4.61.1 - '@rollup/rollup-linux-x64-musl': 4.61.1 - '@rollup/rollup-openbsd-x64': 4.61.1 - '@rollup/rollup-openharmony-arm64': 4.61.1 - '@rollup/rollup-win32-arm64-msvc': 4.61.1 - '@rollup/rollup-win32-ia32-msvc': 4.61.1 - '@rollup/rollup-win32-x64-gnu': 4.61.1 - '@rollup/rollup-win32-x64-msvc': 4.61.1 + '@rollup/rollup-android-arm-eabi': 4.62.0 + '@rollup/rollup-android-arm64': 4.62.0 + '@rollup/rollup-darwin-arm64': 4.62.0 + '@rollup/rollup-darwin-x64': 4.62.0 + '@rollup/rollup-freebsd-arm64': 4.62.0 + '@rollup/rollup-freebsd-x64': 4.62.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.0 + '@rollup/rollup-linux-arm-musleabihf': 4.62.0 + '@rollup/rollup-linux-arm64-gnu': 4.62.0 + '@rollup/rollup-linux-arm64-musl': 4.62.0 + '@rollup/rollup-linux-loong64-gnu': 4.62.0 + '@rollup/rollup-linux-loong64-musl': 4.62.0 + '@rollup/rollup-linux-ppc64-gnu': 4.62.0 + '@rollup/rollup-linux-ppc64-musl': 4.62.0 + '@rollup/rollup-linux-riscv64-gnu': 4.62.0 + '@rollup/rollup-linux-riscv64-musl': 4.62.0 + '@rollup/rollup-linux-s390x-gnu': 4.62.0 + '@rollup/rollup-linux-x64-gnu': 4.62.0 + '@rollup/rollup-linux-x64-musl': 4.62.0 + '@rollup/rollup-openbsd-x64': 4.62.0 + '@rollup/rollup-openharmony-arm64': 4.62.0 + '@rollup/rollup-win32-arm64-msvc': 4.62.0 + '@rollup/rollup-win32-ia32-msvc': 4.62.0 + '@rollup/rollup-win32-x64-gnu': 4.62.0 + '@rollup/rollup-win32-x64-msvc': 4.62.0 fsevents: 2.3.3 router@2.2.0: @@ -6693,11 +6693,11 @@ snapshots: tinyrainbow@3.1.0: {} - tldts-core@7.4.2: {} + tldts-core@7.4.3: {} - tldts@7.4.2: + tldts@7.4.3: dependencies: - tldts-core: 7.4.2 + tldts-core: 7.4.3 to-regex-range@5.0.1: dependencies: @@ -6707,7 +6707,7 @@ snapshots: tough-cookie@6.0.1: dependencies: - tldts: 7.4.2 + tldts: 7.4.3 tr46@6.0.0: dependencies: @@ -6752,7 +6752,7 @@ snapshots: undici-types@7.24.6: {} - undici@6.26.0: {} + undici@6.27.0: {} undici@7.24.4: {} @@ -6778,26 +6778,26 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 postcss: 8.5.15 - rollup: 4.61.1 + rollup: 4.62.0 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 25.9.3 fsevents: 2.3.3 sass: 1.97.3 - vitest@4.1.8(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)): + vitest@4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)): dependencies: - '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) - '@vitest/pretty-format': 4.1.8 - '@vitest/runner': 4.1.8 - '@vitest/snapshot': 4.1.8 - '@vitest/spy': 4.1.8 - '@vitest/utils': 4.1.8 + '@vitest/expect': 4.1.9 + '@vitest/mocker': 4.1.9(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) + '@vitest/pretty-format': 4.1.9 + '@vitest/runner': 4.1.9 + '@vitest/snapshot': 4.1.9 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 - obug: 2.1.2 + obug: 2.1.3 pathe: 2.0.3 picomatch: 4.0.4 std-env: 4.1.0 diff --git a/uv.lock b/uv.lock index f10d089..f570288 100644 --- a/uv.lock +++ b/uv.lock @@ -106,14 +106,14 @@ wheels = [ [[package]] name = "anyio" -version = "4.13.0" +version = "4.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/b5/001890774a9552aff22502b8da382593109ce0c95314abaebbb116567545/anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89", size = 253586, upload-time = "2026-06-15T22:00:49.021Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, + { url = "https://files.pythonhosted.org/packages/ba/16/9826f089383c593cdfc4a6e5aca94d9e91ae1692c57af82c3b2aa5e810f7/anyio-4.14.0-py3-none-any.whl", hash = "sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9", size = 123506, upload-time = "2026-06-15T22:00:47.595Z" }, ] [[package]] @@ -789,7 +789,7 @@ wheels = [ [[package]] name = "pylint" -version = "4.0.5" +version = "4.0.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "astroid" }, @@ -800,14 +800,14 @@ dependencies = [ { name = "platformdirs" }, { name = "tomlkit" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/e4/b6/74d9a8a68b8067efce8d07707fe6a236324ee1e7808d2eb3646ec8517c7d/pylint-4.0.5.tar.gz", hash = "sha256:8cd6a618df75deb013bd7eb98327a95f02a6fb839205a6bbf5456ef96afb317c", size = 1572474, upload-time = "2026-02-20T09:07:33.621Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/1d/3bb57f303701549550d74bf7ced2b07412be97125c167a0c9d216aa9f762/pylint-4.0.6.tar.gz", hash = "sha256:52f19191bee08bf103f9705ad1a0ece4aa5a0a4ef2bdcbd969375a1e6f6579d5", size = 1585588, upload-time = "2026-06-14T14:43:26.772Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d5/6f/9ac2548e290764781f9e7e2aaf0685b086379dabfb29ca38536985471eaf/pylint-4.0.5-py3-none-any.whl", hash = "sha256:00f51c9b14a3b3ae08cff6b2cdd43f28165c78b165b628692e428fb1f8dc2cf2", size = 536694, upload-time = "2026-02-20T09:07:31.028Z" }, + { url = "https://files.pythonhosted.org/packages/ab/da/acb2e7d4dbd2dfb792d38c0d850481f29ad7049b356d23f56c687d35203b/pylint-4.0.6-py3-none-any.whl", hash = "sha256:d11a0e1fdb7b1cd46ec5d6fc78fee8b95f28695b2d6140e5809925f61e32ea54", size = 538389, upload-time = "2026-06-14T14:43:24.873Z" }, ] [[package]] name = "pytest" -version = "9.0.3" +version = "9.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -816,9 +816,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +sdist = { url = "https://files.pythonhosted.org/packages/84/0e/b5858858d74958632c49b72cb25a3976ff9f632397626715be71c89d3971/pytest-9.1.0.tar.gz", hash = "sha256:41dd9148c08072446394cefd3d79701701335a9f4cae69ba92e39f6c7f5c061c", size = 1634181, upload-time = "2026-06-13T18:52:45.983Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, + { url = "https://files.pythonhosted.org/packages/8b/5a/ba30a81239b909821b3153e303e7def45178bf353da4f72380e6c5e8793b/pytest-9.1.0-py3-none-any.whl", hash = "sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32", size = 386453, upload-time = "2026-06-13T18:52:44.045Z" }, ] [[package]] @@ -861,15 +861,15 @@ wheels = [ [[package]] name = "python-socketio" -version = "5.16.2" +version = "5.16.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "bidict" }, { name = "python-engineio" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/07/dd/6fd4112b941f7d39b8171b6ba17902609bd8fa2059c3812a3c29dade13e7/python_socketio-5.16.2.tar.gz", hash = "sha256:ad88c228d921646efa436c0a0df217e364ef30ec072df4041484e54d49c15989", size = 128011, upload-time = "2026-05-21T22:03:44.418Z" } +sdist = { url = "https://files.pythonhosted.org/packages/32/2d/ffce71017c106b75099fea569df6518c63fee5d6202ce0cfe7b01e6f22c3/python_socketio-5.16.3.tar.gz", hash = "sha256:89b136f677ae65607a84cecda9b4d6c5377b40a97582c504c25df89af16d520e", size = 128095, upload-time = "2026-06-15T22:07:04.003Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/72/dc/0decaf5da92a7a969374474025787102d811d42aed1d32191fa338620e15/python_socketio-5.16.2-py3-none-any.whl", hash = "sha256:bef2da3374fd533aed4297f57b4f6512b52aa51604cb0da2165f401291c5ca20", size = 82137, upload-time = "2026-05-21T22:03:42.616Z" }, + { url = "https://files.pythonhosted.org/packages/0a/38/8c5e72d53ff8eb27497c4f268a7f6d9121e727a50b65248288ad79a93053/python_socketio-5.16.3-py3-none-any.whl", hash = "sha256:e7ad14202a5e6448824c7c2f86161d04e13dec05992257df5c709e6a2798c041", size = 82087, upload-time = "2026-06-15T22:07:02.498Z" }, ] [[package]] From 04b93667647fe11a28a4b99734da508a1fc948fb Mon Sep 17 00:00:00 2001 From: Andrew Keeton <208120+akeeton@users.noreply.github.com> Date: Wed, 17 Jun 2026 17:17:44 -0400 Subject: [PATCH 59/88] Incorporate PR feedback Move the default assignment of AUDIO_DOWNLOAD_DIR from the Dockerfile to docker-entrypoint.sh, and change the default value from "/downloads" to $DOWNLOAD_DIR. --- Dockerfile | 1 - docker-entrypoint.sh | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 5a25570..f9896bc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -61,7 +61,6 @@ ENV PGID=1000 ENV UMASK=022 ENV DOWNLOAD_DIR=/downloads -ENV AUDIO_DOWNLOAD_DIR=/downloads ENV STATE_DIR=/downloads/.metube ENV TEMP_DIR=/downloads ENV PORT=8081 diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 615d477..ad74e2f 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -2,6 +2,7 @@ PUID="${UID:-$PUID}" PGID="${GID:-$PGID}" +AUDIO_DOWNLOAD_DIR="${AUDIO_DOWNLOAD_DIR:-$DOWNLOAD_DIR}" echo "Setting umask to ${UMASK}" umask ${UMASK} From dd1b4c24369a057249955fc315641d1df9beb0c7 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Sat, 20 Jun 2026 09:52:14 +0300 Subject: [PATCH 60/88] upgrade dependencies --- ui/package.json | 4 +- ui/pnpm-lock.yaml | 217 ++++++++++++++++++++-------------------------- uv.lock | 12 +-- 3 files changed, 100 insertions(+), 133 deletions(-) diff --git a/ui/package.json b/ui/package.json index 0f5280d..63ba1ba 100644 --- a/ui/package.json +++ b/ui/package.json @@ -48,8 +48,8 @@ }, "devDependencies": { "@angular-eslint/builder": "21.1.0", - "@angular/build": "^21.2.15", - "@angular/cli": "^21.2.15", + "@angular/build": "^21.2.16", + "@angular/cli": "^21.2.16", "@angular/compiler-cli": "^21.2.17", "@angular/localize": "^21.2.17", "@eslint/js": "^9.39.4", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 5aa9c96..2edc38e 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -77,13 +77,13 @@ importers: devDependencies: '@angular-eslint/builder': specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular/build': - specifier: ^21.2.15 - version: 21.2.15(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.3)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3))) + specifier: ^21.2.16 + version: 21.2.16(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.3)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3))) '@angular/cli': - specifier: ^21.2.15 - version: 21.2.15(@types/node@25.9.3)(chokidar@5.0.0) + specifier: ^21.2.16 + version: 21.2.16(@types/node@25.9.3)(chokidar@5.0.0) '@angular/compiler-cli': specifier: ^21.2.17 version: 21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3) @@ -95,7 +95,7 @@ importers: version: 9.39.4 angular-eslint: specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) eslint: specifier: ^9.39.4 version: 9.39.4 @@ -177,13 +177,13 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.2102.15': - resolution: {integrity: sha512-EZQXu6j7J7OUxmpxIO2mxd58NTlCb7HOAOfLLGdk7lRcwZeCxnjGRzb72tXlJgIEi3IoOYwcW0ft2cg7r/d6qA==} + '@angular-devkit/architect@0.2102.16': + resolution: {integrity: sha512-FDUKPpq70nJwGK4CICPD31XmesBEGv57Z+JBCPWrTa5mVZIXCQkeo5waIaNfzAnLdbpd74ULJJ3MDNVt4iaGZg==} 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'} hasBin: true - '@angular-devkit/core@21.2.15': - resolution: {integrity: sha512-x7EwuQtMGHANVznHpwyEi/3lMo/kRoaxV2w7lXbRGjTezwv4SPaOBwhrIGCbAVFs5B1ziff4jZzors4owlCTgg==} + '@angular-devkit/core@21.2.16': + resolution: {integrity: sha512-bRot0dqonxdSuGzXyOYtVJis/u9CJycrfC/aaxLeMF37gKtWIyCR2KFkMRXAoiV/AKk5/NuuqDNqcQS9w5G3Fg==} 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: ^5.0.0 @@ -191,8 +191,8 @@ packages: chokidar: optional: true - '@angular-devkit/schematics@21.2.15': - resolution: {integrity: sha512-kHHV694KUPuXlItjvnxUspn+KYjlTKu8KINX/kT1qlogzLYojpnRwcUtZyRPUz1f/M1d3TUHVFeBKx+h5HoNcA==} + '@angular-devkit/schematics@21.2.16': + resolution: {integrity: sha512-3wTn2N6iWxYLrRaFDk3J3a6P3OxL+yvYGoDA7pNKfI+Nu0PpTK8BBwhNQD8L5P3US/QGWTkMNbzZ7XxBBfFP/g==} 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'} '@angular-eslint/builder@21.1.0': @@ -246,8 +246,8 @@ packages: peerDependencies: '@angular/core': 21.2.17 - '@angular/build@21.2.15': - resolution: {integrity: sha512-APJ5v0/hL38CKSqSQDos5Y8/O2LSiSSqP9FagdcMT5j9JAlENXdhVaA3IxPMXDWcxtzD5T1IlN8Z9aFxDXxHyg==} + '@angular/build@21.2.16': + resolution: {integrity: sha512-40Ra2lM/KDPwA68wP6ZX7zVQak/ouo1sTmtUmjUpqihDp5ftXAZWD3t2Mm4Ja88cyHzG3kaI3C0ew/wAdcEeDA==} 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: '@angular/compiler': ^21.0.0 @@ -257,7 +257,7 @@ packages: '@angular/platform-browser': ^21.0.0 '@angular/platform-server': ^21.0.0 '@angular/service-worker': ^21.0.0 - '@angular/ssr': ^21.2.15 + '@angular/ssr': ^21.2.16 karma: ^6.4.0 less: ^4.2.0 ng-packagr: ^21.0.0 @@ -292,8 +292,8 @@ packages: vitest: optional: true - '@angular/cli@21.2.15': - resolution: {integrity: sha512-8CT1iST5CwdxHEzC0NVFUsMenAY7aE30ayTAw5PpA6MPs6bdHlL6QxTmkqThW7hoCj6PJ56YfQMzSQU+aYRLQA==} + '@angular/cli@21.2.16': + resolution: {integrity: sha512-/O2Bsy4jae/op06ejyfsL6K4cD4yo7TEH9iesD4UPEvcWTnV8lCdmE2oxbc1WGT3DIsZ00yBQhURSbetDPGFCg==} 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'} hasBin: true @@ -352,6 +352,7 @@ packages: '@angular/platform-browser-dynamic@21.2.17': resolution: {integrity: sha512-r/BU/T8bOTghP3fIXhzYf5wcMcAmhWnAFv3p4asCCPXomaktoas70wYcMaDH+pK1LAFBxLwzBWHm36MpFlTMFg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + deprecated: '@angular/platform-browser-dynamic is deprecated. Use `@angular/platform-browser` instead.' peerDependencies: '@angular/common': 21.2.17 '@angular/compiler': 21.2.17 @@ -1509,8 +1510,8 @@ packages: cpu: [x64] os: [win32] - '@schematics/angular@21.2.15': - resolution: {integrity: sha512-xdTirIUUegMXtyEDsYabCRcAnlVatVYb6S8v77fgK2eqUhGalI2e/3+L51N3XF1+6K2vEhyDpmmhFZLsdhLYbw==} + '@schematics/angular@21.2.16': + resolution: {integrity: sha512-ctvsRartACu77VAM416VlNV3mag7FhU08I/734f4+sS/UZmnhuTM5a4tTTWEI1U7iPeJoBtjreh6LgeP+QZLbQ==} 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'} '@sigstore/bundle@4.0.0': @@ -1825,8 +1826,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.10.37: - resolution: {integrity: sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==} + baseline-browser-mapping@2.10.38: + resolution: {integrity: sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==} engines: {node: '>=6.0.0'} hasBin: true @@ -2053,8 +2054,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.372: - resolution: {integrity: sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==} + electron-to-chromium@1.5.376: + resolution: {integrity: sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2066,15 +2067,15 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - engine.io-client@6.6.5: - resolution: {integrity: sha512-QCwxUDULPlXv8F6tqMMKx5dNkTe6OaBYRMPYeXKBlyOoKvAmE0ac6pW7fFhSscJ/5SI7666/U/B+MElbsrJlIg==} + engine.io-client@6.6.6: + resolution: {integrity: sha512-iY6QdftLQ9pyiPoX082bpf/u1UewnOaJrtJIF9T0++QB34lZrj0uP+Q/bj8AlUsAxqhnkTV2BS8SBZSxOmoV5Q==} engine.io-parser@5.2.3: resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} engines: {node: '>=10.0.0'} - engine.io@6.6.8: - resolution: {integrity: sha512-2agL3ueZhqxoVrfmntO8yuVj+uNSlIOnhykYHk3Cq0ShYPdUjjUiSJrQvXjq01I9jAuI0Zl2YO8Evv5Mqytm5g==} + engine.io@6.6.9: + resolution: {integrity: sha512-clKkw4C7nJ22mGgoVcCg6V/W/TxdNyIOTr89k2ONZu81qqkddPFDF0LXcbAwhzPD8DjkiRCjzuiO6Y+fkpD4vg==} engines: {node: '>=10.2.0'} entities@4.5.0: @@ -2101,9 +2102,6 @@ packages: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} - err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -2352,8 +2350,8 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} - hono@4.12.25: - resolution: {integrity: sha512-2NFaIyNVgJmBs/ecmtGzlmluTFs5cHEWGTdu0t1HBwYzoGXOL5nUQBRMXsXWla5i4KkG//QMzVP88m1+I3fdAQ==} + hono@4.12.26: + resolution: {integrity: sha512-uyZtpnYxM9CmQ7QsQknM4zN8EftNqhON1qYeIKM0Se67CCEe2c44xyGURwB0axX2fBDu1dqHrHAc1hmNT8ITkw==} engines: {node: '>=16.9.0'} hosted-git-info@9.0.3: @@ -2676,8 +2674,8 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} - nanoid@3.3.12: - resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + nanoid@3.3.13: + resolution: {integrity: sha512-sPdqC6ByMVVGvF1ynvvMo0/o+oD1VX7DaHhijt1bFgjvBkHBib4t49GoNDhf2NDta4oeUNlaGbSt5K7qjZ955Q==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -2720,8 +2718,8 @@ packages: engines: {node: ^20.17.0 || >=22.9.0} hasBin: true - node-releases@2.0.47: - resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==} + node-releases@2.0.48: + resolution: {integrity: sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==} engines: {node: '>=18'} nopt@9.0.0: @@ -2806,8 +2804,8 @@ packages: resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} engines: {node: '>=18'} - pacote@21.3.1: - resolution: {integrity: sha512-O0EDXi85LF4AzdjG74GUwEArhdvawi/YOHcsW6IijKNj7wm8IvEWNF5GnfuxNpQ/ZpO3L37+v8hqdVh8GgWYhg==} + pacote@21.5.1: + resolution: {integrity: sha512-KvcJ9iy3crysCsgqc4+PknH/w6jkrp8JN36mpZBPwNaDRwTfMZD37YzRazNstiZUOhuF5pno9f78n9mEJBavwg==} engines: {node: ^20.17.0 || >=22.9.0} hasBin: true @@ -2886,10 +2884,6 @@ packages: resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==} engines: {node: ^20.17.0 || >=22.9.0} - promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} - proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -2936,10 +2930,6 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} - retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -3056,8 +3046,8 @@ packages: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - socket.io-adapter@2.5.7: - resolution: {integrity: sha512-e0LyK91f3cUxTmv95/KzoLg47+zF+s/sbxRGDNsyG4dmIP8ZSX8ax6byOxfJXeNNtS/8AZlfD+uP7gBeR7DLlg==} + socket.io-adapter@2.5.8: + resolution: {integrity: sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==} socket.io-client@4.8.3: resolution: {integrity: sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==} @@ -3401,18 +3391,6 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.20.1: - resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} - 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 - ws@8.21.0: resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} engines: {node: '>=10.0.0'} @@ -3574,14 +3552,14 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.2102.15(chokidar@5.0.0)': + '@angular-devkit/architect@0.2102.16(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.15(chokidar@5.0.0) + '@angular-devkit/core': 21.2.16(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/core@21.2.15(chokidar@5.0.0)': + '@angular-devkit/core@21.2.16(chokidar@5.0.0)': dependencies: ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) @@ -3592,9 +3570,9 @@ snapshots: optionalDependencies: chokidar: 5.0.0 - '@angular-devkit/schematics@21.2.15(chokidar@5.0.0)': + '@angular-devkit/schematics@21.2.16(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.15(chokidar@5.0.0) + '@angular-devkit/core': 21.2.16(chokidar@5.0.0) jsonc-parser: 3.3.1 magic-string: 0.30.21 ora: 9.3.0 @@ -3602,11 +3580,11 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-eslint/builder@21.1.0(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/builder@21.1.0(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@angular-devkit/architect': 0.2102.15(chokidar@5.0.0) - '@angular-devkit/core': 21.2.15(chokidar@5.0.0) - '@angular/cli': 21.2.15(@types/node@25.9.3)(chokidar@5.0.0) + '@angular-devkit/architect': 0.2102.16(chokidar@5.0.0) + '@angular-devkit/core': 21.2.16(chokidar@5.0.0) + '@angular/cli': 21.2.16(@types/node@25.9.3)(chokidar@5.0.0) eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: @@ -3635,13 +3613,13 @@ snapshots: ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 - '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@angular-devkit/core': 21.2.15(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.15(chokidar@5.0.0) + '@angular-devkit/core': 21.2.16(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.16(chokidar@5.0.0) '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.15(@types/node@25.9.3)(chokidar@5.0.0) + '@angular/cli': 21.2.16(@types/node@25.9.3)(chokidar@5.0.0) ignore: 7.0.5 semver: 7.7.3 strip-json-comments: 3.1.1 @@ -3672,10 +3650,10 @@ snapshots: '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - '@angular/build@21.2.15(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.3)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)))': + '@angular/build@21.2.16(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.3)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)))': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2102.15(chokidar@5.0.0) + '@angular-devkit/architect': 0.2102.16(chokidar@5.0.0) '@angular/compiler': 21.2.17 '@angular/compiler-cli': 21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3) '@babel/core': 7.29.0 @@ -3728,22 +3706,22 @@ snapshots: - tsx - yaml - '@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0)': + '@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2102.15(chokidar@5.0.0) - '@angular-devkit/core': 21.2.15(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.15(chokidar@5.0.0) + '@angular-devkit/architect': 0.2102.16(chokidar@5.0.0) + '@angular-devkit/core': 21.2.16(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.16(chokidar@5.0.0) '@inquirer/prompts': 7.10.1(@types/node@25.9.3) '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.3))(@types/node@25.9.3)(listr2@9.0.5) '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) - '@schematics/angular': 21.2.15(chokidar@5.0.0) + '@schematics/angular': 21.2.16(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 algoliasearch: 5.48.1 ini: 6.0.0 jsonc-parser: 3.3.1 listr2: 9.0.5 npm-package-arg: 13.0.2 - pacote: 21.3.1 + pacote: 21.5.1 parse5-html-rewriting-stream: 8.0.0 semver: 7.7.4 yargs: 18.0.0 @@ -4151,9 +4129,9 @@ snapshots: '@harperfast/extended-iterable@1.0.3': optional: true - '@hono/node-server@1.19.14(hono@4.12.25)': + '@hono/node-server@1.19.14(hono@4.12.26)': dependencies: - hono: 4.12.25 + hono: 4.12.26 '@humanfs/core@0.19.2': dependencies: @@ -4352,7 +4330,7 @@ snapshots: '@modelcontextprotocol/sdk@1.26.0(zod@4.3.6)': dependencies: - '@hono/node-server': 1.19.14(hono@4.12.25) + '@hono/node-server': 1.19.14(hono@4.12.26) ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) content-type: 1.0.5 @@ -4362,7 +4340,7 @@ snapshots: eventsource-parser: 3.1.0 express: 5.2.1 express-rate-limit: 8.5.2(express@5.2.1) - hono: 4.12.25 + hono: 4.12.26 jose: 6.2.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -4740,10 +4718,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.62.0': optional: true - '@schematics/angular@21.2.15(chokidar@5.0.0)': + '@schematics/angular@21.2.16(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.15(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.15(chokidar@5.0.0) + '@angular-devkit/core': 21.2.16(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.16(chokidar@5.0.0) jsonc-parser: 3.3.1 transitivePeerDependencies: - chokidar @@ -5099,16 +5077,16 @@ snapshots: '@algolia/requester-fetch': 5.48.1 '@algolia/requester-node-http': 5.48.1 - angular-eslint@21.1.0(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): + angular-eslint@21.1.0(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): dependencies: - '@angular-devkit/core': 21.2.15(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.15(chokidar@5.0.0) - '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-devkit/core': 21.2.16(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.16(chokidar@5.0.0) + '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.15(@types/node@25.9.3)(chokidar@5.0.0))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.15(@types/node@25.9.3)(chokidar@5.0.0) + '@angular/cli': 21.2.16(@types/node@25.9.3)(chokidar@5.0.0) '@typescript-eslint/types': 8.61.1 '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 @@ -5146,7 +5124,7 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.10.37: {} + baseline-browser-mapping@2.10.38: {} beasties@0.4.1: dependencies: @@ -5203,10 +5181,10 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.37 + baseline-browser-mapping: 2.10.38 caniuse-lite: 1.0.30001799 - electron-to-chromium: 1.5.372 - node-releases: 2.0.47 + electron-to-chromium: 1.5.376 + node-releases: 2.0.48 update-browserslist-db: 1.2.3(browserslist@4.28.2) buffer-from@1.1.2: {} @@ -5383,7 +5361,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.372: {} + electron-to-chromium@1.5.376: {} emoji-regex@10.6.0: {} @@ -5391,12 +5369,12 @@ snapshots: encodeurl@2.0.0: {} - engine.io-client@6.6.5: + engine.io-client@6.6.6: dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.4.3 engine.io-parser: 5.2.3 - ws: 8.20.1 + ws: 8.21.0 xmlhttprequest-ssl: 2.1.2 transitivePeerDependencies: - bufferutil @@ -5405,7 +5383,7 @@ snapshots: engine.io-parser@5.2.3: {} - engine.io@6.6.8: + engine.io@6.6.9: dependencies: '@types/cors': 2.8.19 '@types/node': 25.9.3 @@ -5416,7 +5394,7 @@ snapshots: cors: 2.8.6 debug: 4.4.3 engine.io-parser: 5.2.3 - ws: 8.20.1 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - supports-color @@ -5434,8 +5412,6 @@ snapshots: environment@1.1.0: {} - err-code@2.0.3: {} - es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -5736,7 +5712,7 @@ snapshots: dependencies: function-bind: 1.1.2 - hono@4.12.25: {} + hono@4.12.26: {} hosted-git-info@9.0.3: dependencies: @@ -6082,7 +6058,7 @@ snapshots: mute-stream@2.0.0: {} - nanoid@3.3.12: {} + nanoid@3.3.13: {} natural-compare@1.4.0: {} @@ -6135,7 +6111,7 @@ snapshots: undici: 6.27.0 which: 6.0.1 - node-releases@2.0.47: {} + node-releases@2.0.48: {} nopt@9.0.0: dependencies: @@ -6238,8 +6214,9 @@ snapshots: p-map@7.0.4: {} - pacote@21.3.1: + pacote@21.5.1: dependencies: + '@gar/promise-retry': 1.0.3 '@npmcli/git': 7.0.2 '@npmcli/installed-package-contents': 4.0.0 '@npmcli/package-json': 7.0.5 @@ -6253,7 +6230,6 @@ snapshots: npm-pick-manifest: 11.0.3 npm-registry-fetch: 19.1.1 proc-log: 6.1.0 - promise-retry: 2.0.1 sigstore: 4.1.1 ssri: 13.0.1 tar: 7.5.16 @@ -6313,7 +6289,7 @@ snapshots: postcss@8.5.15: dependencies: - nanoid: 3.3.12 + nanoid: 3.3.13 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -6321,11 +6297,6 @@ snapshots: proc-log@6.1.0: {} - promise-retry@2.0.1: - dependencies: - err-code: 2.0.3 - retry: 0.12.0 - proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -6363,8 +6334,6 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 - retry@0.12.0: {} - reusify@1.1.0: {} rfdc@1.4.1: {} @@ -6550,10 +6519,10 @@ snapshots: smart-buffer@4.2.0: {} - socket.io-adapter@2.5.7: + socket.io-adapter@2.5.8: dependencies: debug: 4.4.3 - ws: 8.20.1 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - supports-color @@ -6563,7 +6532,7 @@ snapshots: dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.4.3 - engine.io-client: 6.6.5 + engine.io-client: 6.6.6 socket.io-parser: 4.2.6 transitivePeerDependencies: - bufferutil @@ -6583,8 +6552,8 @@ snapshots: base64id: 2.0.0 cors: 2.8.6 debug: 4.4.3 - engine.io: 6.6.8 - socket.io-adapter: 2.5.7 + engine.io: 6.6.9 + socket.io-adapter: 2.5.8 socket.io-parser: 4.2.6 transitivePeerDependencies: - bufferutil @@ -6865,8 +6834,6 @@ snapshots: wrappy@1.0.2: {} - ws@8.20.1: {} - ws@8.21.0: {} xml-name-validator@5.0.0: {} diff --git a/uv.lock b/uv.lock index f570288..8d92749 100644 --- a/uv.lock +++ b/uv.lock @@ -194,11 +194,11 @@ wheels = [ [[package]] name = "certifi" -version = "2026.5.20" +version = "2026.6.17" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c9/c7/424b75da314c1045981bd9777432fad05a9e0c69daa4ed7e308bbaffe405/certifi-2026.6.17.tar.gz", hash = "sha256:024c88eeec92ca068db80f02b8b07c9cef7b9fe261d1d535abfd5abd6f6af432", size = 134594, upload-time = "2026-06-17T10:31:07.894Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" }, + { url = "https://files.pythonhosted.org/packages/ef/2f/c5464532e965badff2f4c4c1a3a83f5697f0d7c407ed0cda44aaa99bb451/certifi-2026.6.17-py3-none-any.whl", hash = "sha256:2227dcbaafe0d2f59279d1762ddddc37783ed4354594f194ffc31d20f41fc3db", size = 133289, upload-time = "2026-06-17T10:31:06.348Z" }, ] [[package]] @@ -807,7 +807,7 @@ wheels = [ [[package]] name = "pytest" -version = "9.1.0" +version = "9.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama", marker = "sys_platform == 'win32'" }, @@ -816,9 +816,9 @@ dependencies = [ { name = "pluggy" }, { name = "pygments" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/84/0e/b5858858d74958632c49b72cb25a3976ff9f632397626715be71c89d3971/pytest-9.1.0.tar.gz", hash = "sha256:41dd9148c08072446394cefd3d79701701335a9f4cae69ba92e39f6c7f5c061c", size = 1634181, upload-time = "2026-06-13T18:52:45.983Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8b/5a/ba30a81239b909821b3153e303e7def45178bf353da4f72380e6c5e8793b/pytest-9.1.0-py3-none-any.whl", hash = "sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32", size = 386453, upload-time = "2026-06-13T18:52:44.045Z" }, + { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" }, ] [[package]] From ce897ee00903bf7ded406f0d7852d95dd4164add Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Sat, 20 Jun 2026 09:52:21 +0300 Subject: [PATCH 61/88] fix open download of cookie files --- app/main.py | 27 +++++++++++++++++++++++++-- app/tests/test_api.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/app/main.py b/app/main.py index d2283ca..03a222c 100644 --- a/app/main.py +++ b/app/main.py @@ -16,7 +16,7 @@ import logging import json import pathlib import re -from urllib.parse import parse_qs, urlencode, urlparse, urlunparse +from urllib.parse import parse_qs, unquote, urlencode, urlparse, urlunparse from watchfiles import DefaultFilter, Change, awatch from ytdl import DownloadQueueNotifier, DownloadQueue, Download @@ -286,7 +286,30 @@ class ObjectSerializer(json.JSONEncoder): return json.JSONEncoder.default(self, obj) serializer = ObjectSerializer() -app = web.Application() + +_STATE_DIR_REAL = os.path.realpath(config.STATE_DIR) + + +def _is_within_state_dir(real_target: str) -> bool: + return real_target == _STATE_DIR_REAL or real_target.startswith(_STATE_DIR_REAL + os.sep) + + +@web.middleware +async def state_dir_guard(request, handler): + for prefix, base in ( + (config.URL_PREFIX + 'download/', config.DOWNLOAD_DIR), + (config.URL_PREFIX + 'audio_download/', config.AUDIO_DOWNLOAD_DIR), + ): + if request.path.startswith(prefix): + rel = unquote(request.path[len(prefix):]) + target = os.path.realpath(os.path.join(base, rel)) + if _is_within_state_dir(target): + raise web.HTTPNotFound() + break + return await handler(request) + + +app = web.Application(middlewares=[state_dir_guard]) _cors_origins = [o.strip() for o in config.CORS_ALLOWED_ORIGINS.split(',') if o.strip()] if config.CORS_ALLOWED_ORIGINS else [] sio = socketio.AsyncServer(cors_allowed_origins=_cors_origins if _cors_origins else []) sio.attach(app, socketio_path=config.URL_PREFIX + 'socket.io') diff --git a/app/tests/test_api.py b/app/tests/test_api.py index 9776856..6481520 100644 --- a/app/tests/test_api.py +++ b/app/tests/test_api.py @@ -3,10 +3,13 @@ from __future__ import annotations import json +import os +from pathlib import Path from unittest.mock import AsyncMock, MagicMock import pytest from aiohttp import web +from aiohttp.test_utils import TestClient, TestServer import main @@ -306,3 +309,41 @@ async def test_subscribe_rejects_clip_options(mock_dqueue, monkeypatch): with pytest.raises(web.HTTPBadRequest): await main.subscribe(req) main.submgr.add_subscription.assert_not_awaited() + + +def test_is_within_state_dir_blocks_state_subtree(): + state_dir = main._STATE_DIR_REAL + assert main._is_within_state_dir(state_dir) + assert main._is_within_state_dir(os.path.join(state_dir, "cookies.txt")) + assert main._is_within_state_dir(os.path.join(state_dir, "queue", "item.json")) + + +def test_is_within_state_dir_allows_sibling_downloads(): + download_dir = os.path.realpath(main.config.DOWNLOAD_DIR) + assert not main._is_within_state_dir(os.path.join(download_dir, "video.mp4")) + assert not main._is_within_state_dir("/tmp/unrelated/video.mp4") + + +@pytest.mark.asyncio +async def test_download_blocks_state_dir_files(monkeypatch): + download_dir = Path(main.config.DOWNLOAD_DIR) + state_dir = download_dir / ".metube" + state_dir.mkdir(parents=True, exist_ok=True) + (state_dir / "cookies.txt").write_text("# Netscape HTTP Cookie File\n", encoding="utf-8") + (download_dir / "video.mp4").write_bytes(b"video") + + monkeypatch.setattr(main.config, "STATE_DIR", str(state_dir)) + monkeypatch.setattr(main, "_STATE_DIR_REAL", os.path.realpath(str(state_dir))) + + try: + async with TestClient(TestServer(main.app)) as client: + blocked = await client.get("/download/.metube/cookies.txt") + assert blocked.status == 404 + + allowed = await client.get("/download/video.mp4") + assert allowed.status == 200 + assert await allowed.read() == b"video" + finally: + (state_dir / "cookies.txt").unlink(missing_ok=True) + (download_dir / "video.mp4").unlink(missing_ok=True) + state_dir.rmdir() From 33f1412fac28be4b3cadadc9fba0b9dcdd0a7886 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 21 Jun 2026 16:12:26 +0000 Subject: [PATCH 62/88] Bump actions/checkout from 6 to 7 in the github-actions group Bumps the github-actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 6 to 7 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 6 +++--- .github/workflows/update-yt-dlp.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 41cd17e..a98732d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Set up Node.js uses: actions/setup-node@v6 with: @@ -59,7 +59,7 @@ jobs: run: echo "date=$(date +'%Y.%m.%d')" >> "$GITHUB_OUTPUT" - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Set up QEMU uses: docker/setup-qemu-action@v4 @@ -118,7 +118,7 @@ jobs: id: date run: echo "date=$(date +'%Y.%m.%d')" >> $GITHUB_OUTPUT - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 0 - name: Get commits since last release diff --git a/.github/workflows/update-yt-dlp.yml b/.github/workflows/update-yt-dlp.yml index b9104e6..ea7d7e9 100644 --- a/.github/workflows/update-yt-dlp.yml +++ b/.github/workflows/update-yt-dlp.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: token: ${{ secrets.AUTOUPDATE_PAT }} - From d136344c269f6a66dc0995c66823c112739ca557 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Sun, 28 Jun 2026 21:08:08 +0300 Subject: [PATCH 63/88] upgrade dependencies --- ui/package.json | 12 +- ui/pnpm-lock.yaml | 1356 +++++++++++++++++++++++++++------------------ uv.lock | 32 +- 3 files changed, 851 insertions(+), 549 deletions(-) diff --git a/ui/package.json b/ui/package.json index 63ba1ba..14b94d0 100644 --- a/ui/package.json +++ b/ui/package.json @@ -32,10 +32,10 @@ "@angular/platform-browser-dynamic": "^21.2.17", "@angular/service-worker": "^21.2.17", "@fortawesome/angular-fontawesome": "~4.0.0", - "@fortawesome/fontawesome-svg-core": "^7.2.0", - "@fortawesome/free-brands-svg-icons": "^7.2.0", - "@fortawesome/free-regular-svg-icons": "^7.2.0", - "@fortawesome/free-solid-svg-icons": "^7.2.0", + "@fortawesome/fontawesome-svg-core": "^7.3.0", + "@fortawesome/free-brands-svg-icons": "^7.3.0", + "@fortawesome/free-regular-svg-icons": "^7.3.0", + "@fortawesome/free-solid-svg-icons": "^7.3.0", "@ng-bootstrap/ng-bootstrap": "^20.0.0", "@ng-select/ng-select": "^21.8.2", "@popperjs/core": "^2.11.8", @@ -48,8 +48,8 @@ }, "devDependencies": { "@angular-eslint/builder": "21.1.0", - "@angular/build": "^21.2.16", - "@angular/cli": "^21.2.16", + "@angular/build": "^21.2.17", + "@angular/cli": "^21.2.17", "@angular/compiler-cli": "^21.2.17", "@angular/localize": "^21.2.17", "@eslint/js": "^9.39.4", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 2edc38e..7e40abe 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -36,17 +36,17 @@ importers: specifier: ~4.0.0 version: 4.0.0(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) '@fortawesome/fontawesome-svg-core': - specifier: ^7.2.0 - version: 7.2.0 + specifier: ^7.3.0 + version: 7.3.0 '@fortawesome/free-brands-svg-icons': - specifier: ^7.2.0 - version: 7.2.0 + specifier: ^7.3.0 + version: 7.3.0 '@fortawesome/free-regular-svg-icons': - specifier: ^7.2.0 - version: 7.2.0 + specifier: ^7.3.0 + version: 7.3.0 '@fortawesome/free-solid-svg-icons': - specifier: ^7.2.0 - version: 7.2.0 + specifier: ^7.3.0 + version: 7.3.0 '@ng-bootstrap/ng-bootstrap': specifier: ^20.0.0 version: 20.0.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@popperjs/core@2.11.8)(rxjs@7.8.2) @@ -77,13 +77,13 @@ importers: devDependencies: '@angular-eslint/builder': specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular/build': - specifier: ^21.2.16 - version: 21.2.16(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.3)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3))) + specifier: ^21.2.17 + version: 21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@26.0.1)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3))) '@angular/cli': - specifier: ^21.2.16 - version: 21.2.16(@types/node@25.9.3)(chokidar@5.0.0) + specifier: ^21.2.17 + version: 21.2.17(@types/node@26.0.1)(chokidar@5.0.0) '@angular/compiler-cli': specifier: ^21.2.17 version: 21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3) @@ -95,7 +95,7 @@ importers: version: 9.39.4 angular-eslint: specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) + version: 21.1.0(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) eslint: specifier: ^9.39.4 version: 9.39.4 @@ -110,7 +110,7 @@ importers: version: 8.47.0(eslint@9.39.4)(typescript@5.9.3) vitest: specifier: ^4.1.9 - version: 4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) + version: 4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3)) packages: @@ -177,13 +177,13 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.2102.16': - resolution: {integrity: sha512-FDUKPpq70nJwGK4CICPD31XmesBEGv57Z+JBCPWrTa5mVZIXCQkeo5waIaNfzAnLdbpd74ULJJ3MDNVt4iaGZg==} + '@angular-devkit/architect@0.2102.17': + resolution: {integrity: sha512-+lkmpEfKXuPZaGZRCCT/B2JGSnafudEVMLeolAW0G7de28bwJIwTE5nmr/rcKTy4NnEDX/8B53tWTXGENuQvmg==} 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'} hasBin: true - '@angular-devkit/core@21.2.16': - resolution: {integrity: sha512-bRot0dqonxdSuGzXyOYtVJis/u9CJycrfC/aaxLeMF37gKtWIyCR2KFkMRXAoiV/AKk5/NuuqDNqcQS9w5G3Fg==} + '@angular-devkit/core@21.2.17': + resolution: {integrity: sha512-JbGWwFX1Nv4Np0S9b4HP2SUKhR2bz6l/S8zBXnam95+RXzDaqXJDmQHOHZGS+4La06SjlcXwFyrgSI0rm50A1A==} 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: ^5.0.0 @@ -191,8 +191,8 @@ packages: chokidar: optional: true - '@angular-devkit/schematics@21.2.16': - resolution: {integrity: sha512-3wTn2N6iWxYLrRaFDk3J3a6P3OxL+yvYGoDA7pNKfI+Nu0PpTK8BBwhNQD8L5P3US/QGWTkMNbzZ7XxBBfFP/g==} + '@angular-devkit/schematics@21.2.17': + resolution: {integrity: sha512-IMsbo/OBG0mdCRxezbq/CLo6JxUJBOAHZfbUDzxbSRwJm8FT5AbXO7rW+Z3haoqcb+WrC5Pr+DQ6WolX2x2brQ==} 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'} '@angular-eslint/builder@21.1.0': @@ -246,8 +246,8 @@ packages: peerDependencies: '@angular/core': 21.2.17 - '@angular/build@21.2.16': - resolution: {integrity: sha512-40Ra2lM/KDPwA68wP6ZX7zVQak/ouo1sTmtUmjUpqihDp5ftXAZWD3t2Mm4Ja88cyHzG3kaI3C0ew/wAdcEeDA==} + '@angular/build@21.2.17': + resolution: {integrity: sha512-1tKQoRH8/21lf3Z1+ezaHmzdhAmeJx34wVW69DU+NuumsjDHDuq2YdUbiC936SnI33tDXov5UOBMqzjHe8qUTg==} 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: '@angular/compiler': ^21.0.0 @@ -257,7 +257,7 @@ packages: '@angular/platform-browser': ^21.0.0 '@angular/platform-server': ^21.0.0 '@angular/service-worker': ^21.0.0 - '@angular/ssr': ^21.2.16 + '@angular/ssr': ^21.2.17 karma: ^6.4.0 less: ^4.2.0 ng-packagr: ^21.0.0 @@ -292,8 +292,8 @@ packages: vitest: optional: true - '@angular/cli@21.2.16': - resolution: {integrity: sha512-/O2Bsy4jae/op06ejyfsL6K4cD4yo7TEH9iesD4UPEvcWTnV8lCdmE2oxbc1WGT3DIsZ00yBQhURSbetDPGFCg==} + '@angular/cli@21.2.17': + resolution: {integrity: sha512-wyEPOszxza7kUa1BWyERUqSDmAC/DF5Iun0YJ1sN22jDriOuOlYh9hXSk1EHs06fQ1JcTYLml+timHVHg4V1/w==} 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'} hasBin: true @@ -399,6 +399,10 @@ packages: resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} engines: {node: '>=6.9.0'} + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.29.7': resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} @@ -462,8 +466,8 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} - '@csstools/color-helpers@6.0.2': - resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} + '@csstools/color-helpers@6.1.0': + resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==} engines: {node: '>=20.19.0'} '@csstools/css-calc@3.2.1': @@ -473,8 +477,8 @@ packages: '@csstools/css-parser-algorithms': ^4.0.0 '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-color-parser@4.1.7': - resolution: {integrity: sha512-CmjJFQTFQx/U/xNJhSjCQ0ilpesPmNQ8+eOUeM/+kDOVW33qsIjeOXc27vrQDdWVkf83ZSWwtg7kXSUvKDJ8cQ==} + '@csstools/css-color-parser@4.1.9': + resolution: {integrity: sha512-paQcIaOO53Rk5+YrBaBjm/SgrV4INImjo2BT1DtQRYr+XeTRbeAYlS+jxXp9drqvKmtFnWRJKIalDLhZZDu42A==} engines: {node: '>=20.19.0'} peerDependencies: '@csstools/css-parser-algorithms': ^4.0.0 @@ -507,158 +511,314 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -715,24 +875,24 @@ packages: peerDependencies: '@angular/core': ^21.0.0 - '@fortawesome/fontawesome-common-types@7.2.0': - resolution: {integrity: sha512-IpR0bER9FY25p+e7BmFH25MZKEwFHTfRAfhOyJubgiDnoJNsSvJ7nigLraHtp4VOG/cy8D7uiV0dLkHOne5Fhw==} + '@fortawesome/fontawesome-common-types@7.3.0': + resolution: {integrity: sha512-X/vND0Y1l9fVJ9O79UgtZnXSpz4aNF3bXlDxiJAEAm6kgeSftp9wjjBPgqzazJV8YlmxfRoeXNfSCJ48sf/Hhw==} engines: {node: '>=6'} - '@fortawesome/fontawesome-svg-core@7.2.0': - resolution: {integrity: sha512-6639htZMjEkwskf3J+e6/iar+4cTNM9qhoWuRfj9F3eJD6r7iCzV1SWnQr2Mdv0QT0suuqU8BoJCZUyCtP9R4Q==} + '@fortawesome/fontawesome-svg-core@7.3.0': + resolution: {integrity: sha512-MFbTNLDWkLJwbozDvHOZ7hwyDjQcBMBattlcOQ6ZmV5YD9bBrqdl1rNtmVjQ/lzqveXXX3sMz2Ew6fAgXoxmkw==} engines: {node: '>=6'} - '@fortawesome/free-brands-svg-icons@7.2.0': - resolution: {integrity: sha512-VNG8xqOip1JuJcC3zsVsKRQ60oXG9+oYNDCosjoU/H9pgYmLTEwWw8pE0jhPz/JWdHeUuK6+NQ3qsM4gIbdbYQ==} + '@fortawesome/free-brands-svg-icons@7.3.0': + resolution: {integrity: sha512-W6C9ZbPWpwcUycq6U90lVbvWTrEnr01Td2x5jlO8fOtvww4kqDBMSmZqXQCc6FIIJD4kTH6G1MBRExAaSXz3yg==} engines: {node: '>=6'} - '@fortawesome/free-regular-svg-icons@7.2.0': - resolution: {integrity: sha512-iycmlN51EULlQ4D/UU9WZnHiN0CvjJ2TuuCrAh+1MVdzD+4ViKYH2deNAll4XAAYlZa8WAefHR5taSK8hYmSMw==} + '@fortawesome/free-regular-svg-icons@7.3.0': + resolution: {integrity: sha512-4675NiHzJJs0dLStFpp5G1JNfMYxqFSxZ2iCaiMfHptjlc8McLG9oqcd5pFEiPiqfiV2YG25cJ9EYWIEhUvp+A==} engines: {node: '>=6'} - '@fortawesome/free-solid-svg-icons@7.2.0': - resolution: {integrity: sha512-YTVITFGN0/24PxzXrwqCgnyd7njDuzp5ZvaCx5nq/jg55kUYd94Nj8UTchBdBofi/L0nwRfjGOg0E41d2u9T1w==} + '@fortawesome/free-solid-svg-icons@7.3.0': + resolution: {integrity: sha512-YxI/CuwWeI3nPIoYU//vkDS+3ige/67DPZ6XwMATpYEFESzO9L8zfJOKllGRgIlpT/uebrZCcvAzp3peD7GmTw==} engines: {node: '>=6'} '@gar/promise-retry@1.0.3': @@ -1121,8 +1281,8 @@ packages: resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} engines: {node: '>= 10'} - '@napi-rs/wasm-runtime@1.1.5': - resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 @@ -1372,146 +1532,146 @@ packages: '@rolldown/pluginutils@1.0.0-rc.4': resolution: {integrity: sha512-1BrrmTu0TWfOP1riA8uakjFc9bpIUGzVKETsOtzY39pPga8zELGDl8eu1Dx7/gjM5CAz14UknsUMpBO8L+YntQ==} - '@rollup/rollup-android-arm-eabi@4.62.0': - resolution: {integrity: sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==} + '@rollup/rollup-android-arm-eabi@4.62.2': + resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.62.0': - resolution: {integrity: sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==} + '@rollup/rollup-android-arm64@4.62.2': + resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.62.0': - resolution: {integrity: sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==} + '@rollup/rollup-darwin-arm64@4.62.2': + resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.62.0': - resolution: {integrity: sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==} + '@rollup/rollup-darwin-x64@4.62.2': + resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.62.0': - resolution: {integrity: sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==} + '@rollup/rollup-freebsd-arm64@4.62.2': + resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.62.0': - resolution: {integrity: sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==} + '@rollup/rollup-freebsd-x64@4.62.2': + resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.62.0': - resolution: {integrity: sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.62.0': - resolution: {integrity: sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==} + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.62.0': - resolution: {integrity: sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==} + '@rollup/rollup-linux-arm64-gnu@4.62.2': + resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.62.0': - resolution: {integrity: sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==} + '@rollup/rollup-linux-arm64-musl@4.62.2': + resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.62.0': - resolution: {integrity: sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==} + '@rollup/rollup-linux-loong64-gnu@4.62.2': + resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.62.0': - resolution: {integrity: sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==} + '@rollup/rollup-linux-loong64-musl@4.62.2': + resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.62.0': - resolution: {integrity: sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==} + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.62.0': - resolution: {integrity: sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==} + '@rollup/rollup-linux-ppc64-musl@4.62.2': + resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.62.0': - resolution: {integrity: sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==} + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.62.0': - resolution: {integrity: sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==} + '@rollup/rollup-linux-riscv64-musl@4.62.2': + resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.62.0': - resolution: {integrity: sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==} + '@rollup/rollup-linux-s390x-gnu@4.62.2': + resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.62.0': - resolution: {integrity: sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==} + '@rollup/rollup-linux-x64-gnu@4.62.2': + resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.62.0': - resolution: {integrity: sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==} + '@rollup/rollup-linux-x64-musl@4.62.2': + resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.62.0': - resolution: {integrity: sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==} + '@rollup/rollup-openbsd-x64@4.62.2': + resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.62.0': - resolution: {integrity: sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==} + '@rollup/rollup-openharmony-arm64@4.62.2': + resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.62.0': - resolution: {integrity: sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==} + '@rollup/rollup-win32-arm64-msvc@4.62.2': + resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.62.0': - resolution: {integrity: sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==} + '@rollup/rollup-win32-ia32-msvc@4.62.2': + resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.62.0': - resolution: {integrity: sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==} + '@rollup/rollup-win32-x64-gnu@4.62.2': + resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.62.0': - resolution: {integrity: sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==} + '@rollup/rollup-win32-x64-msvc@4.62.2': + resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} cpu: [x64] os: [win32] - '@schematics/angular@21.2.16': - resolution: {integrity: sha512-ctvsRartACu77VAM416VlNV3mag7FhU08I/734f4+sS/UZmnhuTM5a4tTTWEI1U7iPeJoBtjreh6LgeP+QZLbQ==} + '@schematics/angular@21.2.17': + resolution: {integrity: sha512-rKHz2//1S3j4MKfsRDtJjnNoCfqSj2dNJDN27pLT9h4oLfBs2wJinfRTQywo2fVGdUcDd+mXBnltqdLJlcoFNw==} 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'} '@sigstore/bundle@4.0.0': @@ -1552,8 +1712,8 @@ packages: resolution: {integrity: sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==} engines: {node: ^20.17.0 || >=22.9.0} - '@tybys/wasm-util@0.10.2': - resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1585,8 +1745,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@25.9.3': - resolution: {integrity: sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==} + '@types/node@26.0.1': + resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==} '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -1612,8 +1772,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.61.1': - resolution: {integrity: sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==} + '@typescript-eslint/project-service@8.62.0': + resolution: {integrity: sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1622,8 +1782,8 @@ packages: resolution: {integrity: sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.61.1': - resolution: {integrity: sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==} + '@typescript-eslint/scope-manager@8.62.0': + resolution: {integrity: sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.47.0': @@ -1632,8 +1792,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/tsconfig-utils@8.61.1': - resolution: {integrity: sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==} + '@typescript-eslint/tsconfig-utils@8.62.0': + resolution: {integrity: sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1649,8 +1809,8 @@ packages: resolution: {integrity: sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.61.1': - resolution: {integrity: sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==} + '@typescript-eslint/types@8.62.0': + resolution: {integrity: sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.47.0': @@ -1659,8 +1819,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.61.1': - resolution: {integrity: sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==} + '@typescript-eslint/typescript-estree@8.62.0': + resolution: {integrity: sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1672,8 +1832,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.61.1': - resolution: {integrity: sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==} + '@typescript-eslint/utils@8.62.0': + resolution: {integrity: sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -1683,8 +1843,8 @@ packages: resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.61.1': - resolution: {integrity: sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==} + '@typescript-eslint/visitor-keys@8.62.0': + resolution: {integrity: sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-basic-ssl@2.1.4': @@ -1826,8 +1986,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.10.38: - resolution: {integrity: sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==} + baseline-browser-mapping@2.10.40: + resolution: {integrity: sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==} engines: {node: '>=6.0.0'} hasBin: true @@ -1864,8 +2024,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.28.2: - resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + browserslist@4.28.4: + resolution: {integrity: sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1907,8 +2067,8 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - chardet@2.1.1: - resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + chardet@2.2.0: + resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} @@ -2054,8 +2214,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.376: - resolution: {integrity: sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA==} + electron-to-chromium@1.5.380: + resolution: {integrity: sha512-W6d5AbuEoRayO447cqrg6lKJIlscgRnnxOZl/08kfV71BQDoEBC7Wwis68z87LjyK6f4kWyTaubuDbhHKrZkbA==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2117,8 +2277,13 @@ packages: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} - esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} engines: {node: '>=18'} hasBin: true @@ -2201,8 +2366,8 @@ packages: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} exponential-backoff@3.1.3: @@ -2350,8 +2515,8 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} - hono@4.12.26: - resolution: {integrity: sha512-uyZtpnYxM9CmQ7QsQknM4zN8EftNqhON1qYeIKM0Se67CCEe2c44xyGURwB0axX2fBDu1dqHrHAc1hmNT8ITkw==} + hono@4.12.27: + resolution: {integrity: sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==} engines: {node: '>=16.9.0'} hosted-git-info@9.0.3: @@ -2396,8 +2561,8 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - immutable@5.1.6: - resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==} + immutable@5.1.8: + resolution: {integrity: sha512-TM5YqrGeTsVIPPpILzeqZ8D2Zc2TvNgSDi88zPF2a4cyqQdWV/wVWBDRDbNzzrLeRWScrFcOX9lW2iX6GOtUDw==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -2477,8 +2642,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.2.0: - resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} + js-yaml@4.3.0: + resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} hasBin: true jsdom@27.4.0: @@ -2674,8 +2839,8 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} - nanoid@3.3.13: - resolution: {integrity: sha512-sPdqC6ByMVVGvF1ynvvMo0/o+oD1VX7DaHhijt1bFgjvBkHBib4t49GoNDhf2NDta4oeUNlaGbSt5K7qjZ955Q==} + nanoid@3.3.15: + resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -2718,8 +2883,8 @@ packages: engines: {node: ^20.17.0 || >=22.9.0} hasBin: true - node-releases@2.0.48: - resolution: {integrity: sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==} + node-releases@2.0.50: + resolution: {integrity: sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==} engines: {node: '>=18'} nopt@9.0.0: @@ -2855,8 +3020,8 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} - piscina@5.1.4: - resolution: {integrity: sha512-7uU4ZnKeQq22t9AsmHGD2w4OYQGonwFnTypDypaWi7Qr2EvQIFVtG8J5D/3bE7W123Wdc9+v4CZDu5hJXVCtBg==} + piscina@5.2.0: + resolution: {integrity: sha512-DszUCKeVN/5G5QKo6jAVHL8fmKnkJvQ0ACiVgY7YGCq3TUB2oznAOayvZPIAdEThvhczkXR+qm3IHsNXpFCYfA==} engines: {node: '>=20.x'} pkce-challenge@5.0.1: @@ -2892,15 +3057,15 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - qs@6.15.2: - resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} + qs@6.15.3: + resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} engines: {node: '>=0.6'} queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + range-parser@1.3.0: + resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} engines: {node: '>= 0.6'} raw-body@3.0.2: @@ -2942,8 +3107,8 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.62.0: - resolution: {integrity: sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==} + rollup@4.62.2: + resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2983,8 +3148,8 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.8.4: - resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true @@ -3142,8 +3307,8 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tar@7.5.16: - resolution: {integrity: sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w==} + tar@7.5.17: + resolution: {integrity: sha512-wPEBwzapC+2PaTYPH6e2L+cNOEE227S47wUYFqlegcs8zlLLmeb9Fcff1HVZY4Fwku/1Eyv38n7GYwB2aaS71g==} engines: {node: '>=18'} tinybench@2.9.0: @@ -3165,11 +3330,11 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tldts-core@7.4.3: - resolution: {integrity: sha512-27ep5H9PzdBrNd5OFM/j3WCU8F3kPwM9D0BOaOf7uYfxMJfyr0K5Tjj69Gri+sZlh2WXd5buIm47NuPF29CDiw==} + tldts-core@7.4.4: + resolution: {integrity: sha512-vwVLJVvvpslm7vqAH7+XNj/neA/Ynq7DT2EEcMuwc5YzN5XaMyRAqxwU+uX3azZ1FQtB2gvrvnLnAEkvYlVdfg==} - tldts@7.4.3: - resolution: {integrity: sha512-A3BDQBeeukYPzB4QdQ1DtdlUmp4x2OCH8n5UVhEWbyANxNep8GavottKzd1xYKFJKjUgMyPT7EzOfnBO55s8Sg==} + tldts@7.4.4: + resolution: {integrity: sha512-kFXFK7O4WPextIUAOk8qtnw9dxR9UIXP9CjuH1cTBVBZMDeQcUPgr/IazGiw1B0Yiw5L75gHLWeW4iD793r90g==} hasBin: true to-regex-range@5.0.1: @@ -3221,8 +3386,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - undici-types@7.24.6: - resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} undici@6.27.0: resolution: {integrity: sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==} @@ -3253,8 +3418,8 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite@7.3.2: - resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==} + vite@7.3.5: + resolution: {integrity: sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3552,14 +3717,14 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.2102.16(chokidar@5.0.0)': + '@angular-devkit/architect@0.2102.17(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.16(chokidar@5.0.0) + '@angular-devkit/core': 21.2.17(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/core@21.2.16(chokidar@5.0.0)': + '@angular-devkit/core@21.2.17(chokidar@5.0.0)': dependencies: ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) @@ -3570,9 +3735,9 @@ snapshots: optionalDependencies: chokidar: 5.0.0 - '@angular-devkit/schematics@21.2.16(chokidar@5.0.0)': + '@angular-devkit/schematics@21.2.17(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.16(chokidar@5.0.0) + '@angular-devkit/core': 21.2.17(chokidar@5.0.0) jsonc-parser: 3.3.1 magic-string: 0.30.21 ora: 9.3.0 @@ -3580,11 +3745,11 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-eslint/builder@21.1.0(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/builder@21.1.0(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@angular-devkit/architect': 0.2102.16(chokidar@5.0.0) - '@angular-devkit/core': 21.2.16(chokidar@5.0.0) - '@angular/cli': 21.2.16(@types/node@25.9.3)(chokidar@5.0.0) + '@angular-devkit/architect': 0.2102.17(chokidar@5.0.0) + '@angular-devkit/core': 21.2.17(chokidar@5.0.0) + '@angular/cli': 21.2.17(@types/node@26.0.1)(chokidar@5.0.0) eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: @@ -3592,34 +3757,34 @@ snapshots: '@angular-eslint/bundled-angular-compiler@21.1.0': {} - '@angular-eslint/eslint-plugin-template@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin-template@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@5.9.3) aria-query: 5.3.2 axobject-query: 4.1.0 eslint: 9.39.4 typescript: 5.9.3 - '@angular-eslint/eslint-plugin@21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin@21.1.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 - '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': dependencies: - '@angular-devkit/core': 21.2.16(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.16(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.16(@types/node@25.9.3)(chokidar@5.0.0) + '@angular-devkit/core': 21.2.17(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.17(chokidar@5.0.0) + '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular/cli': 21.2.17(@types/node@26.0.1)(chokidar@5.0.0) ignore: 7.0.5 semver: 7.7.3 strip-json-comments: 3.1.1 @@ -3638,10 +3803,10 @@ snapshots: eslint-scope: 9.1.2 typescript: 5.9.3 - '@angular-eslint/utils@21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/utils@21.1.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 @@ -3650,20 +3815,20 @@ snapshots: '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - '@angular/build@21.2.16(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.3)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)))': + '@angular/build@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@26.0.1)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3)))': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2102.16(chokidar@5.0.0) + '@angular-devkit/architect': 0.2102.17(chokidar@5.0.0) '@angular/compiler': 21.2.17 '@angular/compiler-cli': 21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3) - '@babel/core': 7.29.0 + '@babel/core': 7.29.7 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@25.9.3) - '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) + '@inquirer/confirm': 5.1.21(@types/node@26.0.1) + '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3)) beasties: 0.4.1 - browserslist: 4.28.2 - esbuild: 0.27.3 + browserslist: 4.28.4 + esbuild: 0.28.1 https-proxy-agent: 7.0.6 istanbul-lib-instrument: 6.0.3 jsonc-parser: 3.3.1 @@ -3672,7 +3837,7 @@ snapshots: mrmime: 2.0.1 parse5-html-rewriting-stream: 8.0.0 picomatch: 4.0.4 - piscina: 5.1.4 + piscina: 5.2.0 rolldown: 1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) sass: 1.97.3 semver: 7.7.4 @@ -3681,7 +3846,7 @@ snapshots: tslib: 2.8.1 typescript: 5.9.3 undici: 7.24.4 - vite: 7.3.2(@types/node@25.9.3)(sass@1.97.3) + vite: 7.3.5(@types/node@26.0.1)(sass@1.97.3) watchpack: 2.5.1 optionalDependencies: '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) @@ -3690,7 +3855,7 @@ snapshots: '@angular/service-worker': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) lmdb: 3.5.1 postcss: 8.5.15 - vitest: 4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) + vitest: 4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3)) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -3706,15 +3871,15 @@ snapshots: - tsx - yaml - '@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0)': + '@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2102.16(chokidar@5.0.0) - '@angular-devkit/core': 21.2.16(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.16(chokidar@5.0.0) - '@inquirer/prompts': 7.10.1(@types/node@25.9.3) - '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.3))(@types/node@25.9.3)(listr2@9.0.5) + '@angular-devkit/architect': 0.2102.17(chokidar@5.0.0) + '@angular-devkit/core': 21.2.17(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.17(chokidar@5.0.0) + '@inquirer/prompts': 7.10.1(@types/node@26.0.1) + '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@26.0.1))(@types/node@26.0.1)(listr2@9.0.5) '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) - '@schematics/angular': 21.2.16(chokidar@5.0.0) + '@schematics/angular': 21.2.17(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 algoliasearch: 5.48.1 ini: 6.0.0 @@ -3746,7 +3911,7 @@ snapshots: chokidar: 5.0.0 convert-source-map: 1.9.0 reflect-metadata: 0.2.2 - semver: 7.8.4 + semver: 7.8.5 tslib: 2.8.1 yargs: 18.0.0 optionalDependencies: @@ -3811,7 +3976,7 @@ snapshots: '@asamuzakjp/css-color@4.1.2': dependencies: '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - '@csstools/css-color-parser': 4.1.7(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 lru-cache: 11.5.1 @@ -3854,6 +4019,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/generator@7.29.7': dependencies: '@babel/parser': 7.29.7 @@ -3870,7 +4055,7 @@ snapshots: dependencies: '@babel/compat-data': 7.29.7 '@babel/helper-validator-option': 7.29.7 - browserslist: 4.28.2 + browserslist: 4.28.4 lru-cache: 5.1.1 semver: 6.3.1 @@ -3892,6 +4077,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + '@babel/helper-split-export-declaration@7.24.7': dependencies: '@babel/types': 7.29.7 @@ -3934,16 +4128,16 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@csstools/color-helpers@6.0.2': {} + '@csstools/color-helpers@6.1.0': {} '@csstools/css-calc@3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-color-parser@4.1.7(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + '@csstools/css-color-parser@4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: - '@csstools/color-helpers': 6.0.2 + '@csstools/color-helpers': 6.1.0 '@csstools/css-calc': 3.2.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 @@ -3974,82 +4168,160 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.27.3': + '@esbuild/aix-ppc64@0.27.7': optional: true - '@esbuild/android-arm64@0.27.3': + '@esbuild/aix-ppc64@0.28.1': optional: true - '@esbuild/android-arm@0.27.3': + '@esbuild/android-arm64@0.27.7': optional: true - '@esbuild/android-x64@0.27.3': + '@esbuild/android-arm64@0.28.1': optional: true - '@esbuild/darwin-arm64@0.27.3': + '@esbuild/android-arm@0.27.7': optional: true - '@esbuild/darwin-x64@0.27.3': + '@esbuild/android-arm@0.28.1': optional: true - '@esbuild/freebsd-arm64@0.27.3': + '@esbuild/android-x64@0.27.7': optional: true - '@esbuild/freebsd-x64@0.27.3': + '@esbuild/android-x64@0.28.1': optional: true - '@esbuild/linux-arm64@0.27.3': + '@esbuild/darwin-arm64@0.27.7': optional: true - '@esbuild/linux-arm@0.27.3': + '@esbuild/darwin-arm64@0.28.1': optional: true - '@esbuild/linux-ia32@0.27.3': + '@esbuild/darwin-x64@0.27.7': optional: true - '@esbuild/linux-loong64@0.27.3': + '@esbuild/darwin-x64@0.28.1': optional: true - '@esbuild/linux-mips64el@0.27.3': + '@esbuild/freebsd-arm64@0.27.7': optional: true - '@esbuild/linux-ppc64@0.27.3': + '@esbuild/freebsd-arm64@0.28.1': optional: true - '@esbuild/linux-riscv64@0.27.3': + '@esbuild/freebsd-x64@0.27.7': optional: true - '@esbuild/linux-s390x@0.27.3': + '@esbuild/freebsd-x64@0.28.1': optional: true - '@esbuild/linux-x64@0.27.3': + '@esbuild/linux-arm64@0.27.7': optional: true - '@esbuild/netbsd-arm64@0.27.3': + '@esbuild/linux-arm64@0.28.1': optional: true - '@esbuild/netbsd-x64@0.27.3': + '@esbuild/linux-arm@0.27.7': optional: true - '@esbuild/openbsd-arm64@0.27.3': + '@esbuild/linux-arm@0.28.1': optional: true - '@esbuild/openbsd-x64@0.27.3': + '@esbuild/linux-ia32@0.27.7': optional: true - '@esbuild/openharmony-arm64@0.27.3': + '@esbuild/linux-ia32@0.28.1': optional: true - '@esbuild/sunos-x64@0.27.3': + '@esbuild/linux-loong64@0.27.7': optional: true - '@esbuild/win32-arm64@0.27.3': + '@esbuild/linux-loong64@0.28.1': optional: true - '@esbuild/win32-ia32@0.27.3': + '@esbuild/linux-mips64el@0.27.7': optional: true - '@esbuild/win32-x64@0.27.3': + '@esbuild/linux-mips64el@0.28.1': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.28.1': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.28.1': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.28.1': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/linux-x64@0.28.1': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.28.1': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.28.1': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.28.1': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.28.1': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.28.1': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.28.1': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.28.1': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.28.1': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + + '@esbuild/win32-x64@0.28.1': optional: true '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)': @@ -4083,7 +4355,7 @@ snapshots: globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.2.0 + js-yaml: 4.3.0 minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: @@ -4103,35 +4375,35 @@ snapshots: '@fortawesome/angular-fontawesome@4.0.0(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) - '@fortawesome/fontawesome-svg-core': 7.2.0 + '@fortawesome/fontawesome-svg-core': 7.3.0 tslib: 2.8.1 - '@fortawesome/fontawesome-common-types@7.2.0': {} + '@fortawesome/fontawesome-common-types@7.3.0': {} - '@fortawesome/fontawesome-svg-core@7.2.0': + '@fortawesome/fontawesome-svg-core@7.3.0': dependencies: - '@fortawesome/fontawesome-common-types': 7.2.0 + '@fortawesome/fontawesome-common-types': 7.3.0 - '@fortawesome/free-brands-svg-icons@7.2.0': + '@fortawesome/free-brands-svg-icons@7.3.0': dependencies: - '@fortawesome/fontawesome-common-types': 7.2.0 + '@fortawesome/fontawesome-common-types': 7.3.0 - '@fortawesome/free-regular-svg-icons@7.2.0': + '@fortawesome/free-regular-svg-icons@7.3.0': dependencies: - '@fortawesome/fontawesome-common-types': 7.2.0 + '@fortawesome/fontawesome-common-types': 7.3.0 - '@fortawesome/free-solid-svg-icons@7.2.0': + '@fortawesome/free-solid-svg-icons@7.3.0': dependencies: - '@fortawesome/fontawesome-common-types': 7.2.0 + '@fortawesome/fontawesome-common-types': 7.3.0 '@gar/promise-retry@1.0.3': {} '@harperfast/extended-iterable@1.0.3': optional: true - '@hono/node-server@1.19.14(hono@4.12.26)': + '@hono/node-server@1.19.14(hono@4.12.27)': dependencies: - hono: 4.12.26 + hono: 4.12.27 '@humanfs/core@0.19.2': dependencies: @@ -4151,128 +4423,128 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.2(@types/node@25.9.3)': + '@inquirer/checkbox@4.3.2(@types/node@26.0.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.9.3) + '@inquirer/core': 10.3.2(@types/node@26.0.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.3) + '@inquirer/type': 3.0.10(@types/node@26.0.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 - '@inquirer/confirm@5.1.21(@types/node@25.9.3)': + '@inquirer/confirm@5.1.21(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.3) - '@inquirer/type': 3.0.10(@types/node@25.9.3) + '@inquirer/core': 10.3.2(@types/node@26.0.1) + '@inquirer/type': 3.0.10(@types/node@26.0.1) optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 - '@inquirer/core@10.3.2(@types/node@25.9.3)': + '@inquirer/core@10.3.2(@types/node@26.0.1)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.3) + '@inquirer/type': 3.0.10(@types/node@26.0.1) 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 optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 - '@inquirer/editor@4.2.23(@types/node@25.9.3)': + '@inquirer/editor@4.2.23(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.3) - '@inquirer/external-editor': 1.0.3(@types/node@25.9.3) - '@inquirer/type': 3.0.10(@types/node@25.9.3) + '@inquirer/core': 10.3.2(@types/node@26.0.1) + '@inquirer/external-editor': 1.0.3(@types/node@26.0.1) + '@inquirer/type': 3.0.10(@types/node@26.0.1) optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 - '@inquirer/expand@4.0.23(@types/node@25.9.3)': + '@inquirer/expand@4.0.23(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.3) - '@inquirer/type': 3.0.10(@types/node@25.9.3) + '@inquirer/core': 10.3.2(@types/node@26.0.1) + '@inquirer/type': 3.0.10(@types/node@26.0.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 - '@inquirer/external-editor@1.0.3(@types/node@25.9.3)': + '@inquirer/external-editor@1.0.3(@types/node@26.0.1)': dependencies: - chardet: 2.1.1 + chardet: 2.2.0 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.3.1(@types/node@25.9.3)': + '@inquirer/input@4.3.1(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.3) - '@inquirer/type': 3.0.10(@types/node@25.9.3) + '@inquirer/core': 10.3.2(@types/node@26.0.1) + '@inquirer/type': 3.0.10(@types/node@26.0.1) optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 - '@inquirer/number@3.0.23(@types/node@25.9.3)': + '@inquirer/number@3.0.23(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.3) - '@inquirer/type': 3.0.10(@types/node@25.9.3) + '@inquirer/core': 10.3.2(@types/node@26.0.1) + '@inquirer/type': 3.0.10(@types/node@26.0.1) optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 - '@inquirer/password@4.0.23(@types/node@25.9.3)': + '@inquirer/password@4.0.23(@types/node@26.0.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.9.3) - '@inquirer/type': 3.0.10(@types/node@25.9.3) + '@inquirer/core': 10.3.2(@types/node@26.0.1) + '@inquirer/type': 3.0.10(@types/node@26.0.1) optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 - '@inquirer/prompts@7.10.1(@types/node@25.9.3)': + '@inquirer/prompts@7.10.1(@types/node@26.0.1)': dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@25.9.3) - '@inquirer/confirm': 5.1.21(@types/node@25.9.3) - '@inquirer/editor': 4.2.23(@types/node@25.9.3) - '@inquirer/expand': 4.0.23(@types/node@25.9.3) - '@inquirer/input': 4.3.1(@types/node@25.9.3) - '@inquirer/number': 3.0.23(@types/node@25.9.3) - '@inquirer/password': 4.0.23(@types/node@25.9.3) - '@inquirer/rawlist': 4.1.11(@types/node@25.9.3) - '@inquirer/search': 3.2.2(@types/node@25.9.3) - '@inquirer/select': 4.4.2(@types/node@25.9.3) + '@inquirer/checkbox': 4.3.2(@types/node@26.0.1) + '@inquirer/confirm': 5.1.21(@types/node@26.0.1) + '@inquirer/editor': 4.2.23(@types/node@26.0.1) + '@inquirer/expand': 4.0.23(@types/node@26.0.1) + '@inquirer/input': 4.3.1(@types/node@26.0.1) + '@inquirer/number': 3.0.23(@types/node@26.0.1) + '@inquirer/password': 4.0.23(@types/node@26.0.1) + '@inquirer/rawlist': 4.1.11(@types/node@26.0.1) + '@inquirer/search': 3.2.2(@types/node@26.0.1) + '@inquirer/select': 4.4.2(@types/node@26.0.1) optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 - '@inquirer/rawlist@4.1.11(@types/node@25.9.3)': + '@inquirer/rawlist@4.1.11(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.3) - '@inquirer/type': 3.0.10(@types/node@25.9.3) + '@inquirer/core': 10.3.2(@types/node@26.0.1) + '@inquirer/type': 3.0.10(@types/node@26.0.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 - '@inquirer/search@3.2.2(@types/node@25.9.3)': + '@inquirer/search@3.2.2(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.3) + '@inquirer/core': 10.3.2(@types/node@26.0.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.3) + '@inquirer/type': 3.0.10(@types/node@26.0.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 - '@inquirer/select@4.4.2(@types/node@25.9.3)': + '@inquirer/select@4.4.2(@types/node@26.0.1)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.9.3) + '@inquirer/core': 10.3.2(@types/node@26.0.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.3) + '@inquirer/type': 3.0.10(@types/node@26.0.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 - '@inquirer/type@3.0.10(@types/node@25.9.3)': + '@inquirer/type@3.0.10(@types/node@26.0.1)': optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 '@isaacs/fs-minipass@4.0.1': dependencies: @@ -4299,10 +4571,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.3))(@types/node@25.9.3)(listr2@9.0.5)': + '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@26.0.1))(@types/node@26.0.1)(listr2@9.0.5)': dependencies: - '@inquirer/prompts': 7.10.1(@types/node@25.9.3) - '@inquirer/type': 3.0.10(@types/node@25.9.3) + '@inquirer/prompts': 7.10.1(@types/node@26.0.1) + '@inquirer/type': 3.0.10(@types/node@26.0.1) listr2: 9.0.5 transitivePeerDependencies: - '@types/node' @@ -4330,7 +4602,7 @@ snapshots: '@modelcontextprotocol/sdk@1.26.0(zod@4.3.6)': dependencies: - '@hono/node-server': 1.19.14(hono@4.12.26) + '@hono/node-server': 1.19.14(hono@4.12.27) ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) content-type: 1.0.5 @@ -4340,7 +4612,7 @@ snapshots: eventsource-parser: 3.1.0 express: 5.2.1 express-rate-limit: 8.5.2(express@5.2.1) - hono: 4.12.26 + hono: 4.12.27 jose: 6.2.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -4440,11 +4712,11 @@ snapshots: '@napi-rs/nice-win32-x64-msvc': 1.1.1 optional: true - '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.2 + '@tybys/wasm-util': 0.10.3 optional: true '@ng-bootstrap/ng-bootstrap@20.0.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@popperjs/core@2.11.8)(rxjs@7.8.2)': @@ -4629,7 +4901,7 @@ snapshots: '@rolldown/binding-wasm32-wasi@1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -4643,85 +4915,85 @@ snapshots: '@rolldown/pluginutils@1.0.0-rc.4': {} - '@rollup/rollup-android-arm-eabi@4.62.0': + '@rollup/rollup-android-arm-eabi@4.62.2': optional: true - '@rollup/rollup-android-arm64@4.62.0': + '@rollup/rollup-android-arm64@4.62.2': optional: true - '@rollup/rollup-darwin-arm64@4.62.0': + '@rollup/rollup-darwin-arm64@4.62.2': optional: true - '@rollup/rollup-darwin-x64@4.62.0': + '@rollup/rollup-darwin-x64@4.62.2': optional: true - '@rollup/rollup-freebsd-arm64@4.62.0': + '@rollup/rollup-freebsd-arm64@4.62.2': optional: true - '@rollup/rollup-freebsd-x64@4.62.0': + '@rollup/rollup-freebsd-x64@4.62.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.62.0': + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.62.0': + '@rollup/rollup-linux-arm-musleabihf@4.62.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.62.0': + '@rollup/rollup-linux-arm64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.62.0': + '@rollup/rollup-linux-arm64-musl@4.62.2': optional: true - '@rollup/rollup-linux-loong64-gnu@4.62.0': + '@rollup/rollup-linux-loong64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-loong64-musl@4.62.0': + '@rollup/rollup-linux-loong64-musl@4.62.2': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.62.0': + '@rollup/rollup-linux-ppc64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-ppc64-musl@4.62.0': + '@rollup/rollup-linux-ppc64-musl@4.62.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.62.0': + '@rollup/rollup-linux-riscv64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.62.0': + '@rollup/rollup-linux-riscv64-musl@4.62.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.62.0': + '@rollup/rollup-linux-s390x-gnu@4.62.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.62.0': + '@rollup/rollup-linux-x64-gnu@4.62.2': optional: true - '@rollup/rollup-linux-x64-musl@4.62.0': + '@rollup/rollup-linux-x64-musl@4.62.2': optional: true - '@rollup/rollup-openbsd-x64@4.62.0': + '@rollup/rollup-openbsd-x64@4.62.2': optional: true - '@rollup/rollup-openharmony-arm64@4.62.0': + '@rollup/rollup-openharmony-arm64@4.62.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.62.0': + '@rollup/rollup-win32-arm64-msvc@4.62.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.62.0': + '@rollup/rollup-win32-ia32-msvc@4.62.2': optional: true - '@rollup/rollup-win32-x64-gnu@4.62.0': + '@rollup/rollup-win32-x64-gnu@4.62.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.62.0': + '@rollup/rollup-win32-x64-msvc@4.62.2': optional: true - '@schematics/angular@21.2.16(chokidar@5.0.0)': + '@schematics/angular@21.2.17(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.16(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.16(chokidar@5.0.0) + '@angular-devkit/core': 21.2.17(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.17(chokidar@5.0.0) jsonc-parser: 3.3.1 transitivePeerDependencies: - chokidar @@ -4769,7 +5041,7 @@ snapshots: '@tufjs/canonical-json': 2.0.0 minimatch: 10.2.5 - '@tybys/wasm-util@0.10.2': + '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 optional: true @@ -4802,7 +5074,7 @@ snapshots: '@types/cors@2.8.19': dependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 '@types/deep-eql@4.0.2': {} @@ -4812,13 +5084,13 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@25.9.3': + '@types/node@26.0.1': dependencies: - undici-types: 7.24.6 + undici-types: 8.3.0 '@types/ws@8.18.1': dependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 '@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': dependencies: @@ -4858,10 +5130,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.61.1(typescript@5.9.3)': + '@typescript-eslint/project-service@8.62.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.9.3) - '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@5.9.3) + '@typescript-eslint/types': 8.62.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -4872,16 +5144,16 @@ snapshots: '@typescript-eslint/types': 8.47.0 '@typescript-eslint/visitor-keys': 8.47.0 - '@typescript-eslint/scope-manager@8.61.1': + '@typescript-eslint/scope-manager@8.62.0': dependencies: - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/visitor-keys': 8.62.0 '@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/tsconfig-utils@8.61.1(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.62.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 @@ -4899,7 +5171,7 @@ snapshots: '@typescript-eslint/types@8.47.0': {} - '@typescript-eslint/types@8.61.1': {} + '@typescript-eslint/types@8.62.0': {} '@typescript-eslint/typescript-estree@8.47.0(typescript@5.9.3)': dependencies: @@ -4911,21 +5183,21 @@ snapshots: fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.9 - semver: 7.8.4 + semver: 7.8.5 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.61.1(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.62.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.61.1(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.9.3) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/visitor-keys': 8.61.1 + '@typescript-eslint/project-service': 8.62.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@5.9.3) + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/visitor-keys': 8.62.0 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.4 + semver: 7.8.5 tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 @@ -4943,12 +5215,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3)': + '@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) - '@typescript-eslint/scope-manager': 8.61.1 - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/typescript-estree': 8.61.1(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.62.0 + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/typescript-estree': 8.62.0(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 transitivePeerDependencies: @@ -4959,14 +5231,14 @@ snapshots: '@typescript-eslint/types': 8.47.0 eslint-visitor-keys: 4.2.1 - '@typescript-eslint/visitor-keys@8.61.1': + '@typescript-eslint/visitor-keys@8.62.0': dependencies: - '@typescript-eslint/types': 8.61.1 + '@typescript-eslint/types': 8.62.0 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3))': + '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3))': dependencies: - vite: 7.3.2(@types/node@25.9.3)(sass@1.97.3) + vite: 7.3.5(@types/node@26.0.1)(sass@1.97.3) '@vitest/expect@4.1.9': dependencies: @@ -4977,13 +5249,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.9(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3))': + '@vitest/mocker@4.1.9(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3))': dependencies: '@vitest/spy': 4.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.2(@types/node@25.9.3)(sass@1.97.3) + vite: 7.3.5(@types/node@26.0.1)(sass@1.97.3) '@vitest/pretty-format@4.1.9': dependencies: @@ -5077,18 +5349,18 @@ snapshots: '@algolia/requester-fetch': 5.48.1 '@algolia/requester-node-http': 5.48.1 - angular-eslint@21.1.0(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): + angular-eslint@21.1.0(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): dependencies: - '@angular-devkit/core': 21.2.16(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.16(chokidar@5.0.0) - '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.16(@types/node@25.9.3)(chokidar@5.0.0))(@typescript-eslint/types@8.61.1)(@typescript-eslint/utils@8.61.1(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-devkit/core': 21.2.17(chokidar@5.0.0) + '@angular-devkit/schematics': 21.2.17(chokidar@5.0.0) + '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.16(@types/node@25.9.3)(chokidar@5.0.0) - '@typescript-eslint/types': 8.61.1 - '@typescript-eslint/utils': 8.61.1(eslint@9.39.4)(typescript@5.9.3) + '@angular/cli': 21.2.17(@types/node@26.0.1)(chokidar@5.0.0) + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@5.9.3) eslint: 9.39.4 typescript: 5.9.3 typescript-eslint: 8.47.0(eslint@9.39.4)(typescript@5.9.3) @@ -5124,7 +5396,7 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.10.38: {} + baseline-browser-mapping@2.10.40: {} beasties@0.4.1: dependencies: @@ -5150,7 +5422,7 @@ snapshots: http-errors: 2.0.1 iconv-lite: 0.7.2 on-finished: 2.4.1 - qs: 6.15.2 + qs: 6.15.3 raw-body: 3.0.2 type-is: 2.1.0 transitivePeerDependencies: @@ -5179,13 +5451,13 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.28.2: + browserslist@4.28.4: dependencies: - baseline-browser-mapping: 2.10.38 + baseline-browser-mapping: 2.10.40 caniuse-lite: 1.0.30001799 - electron-to-chromium: 1.5.376 - node-releases: 2.0.48 - update-browserslist-db: 1.2.3(browserslist@4.28.2) + electron-to-chromium: 1.5.380 + node-releases: 2.0.50 + update-browserslist-db: 1.2.3(browserslist@4.28.4) buffer-from@1.1.2: {} @@ -5227,7 +5499,7 @@ snapshots: chalk@5.6.2: {} - chardet@2.1.1: {} + chardet@2.2.0: {} chokidar@4.0.3: dependencies: @@ -5361,7 +5633,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.376: {} + electron-to-chromium@1.5.380: {} emoji-regex@10.6.0: {} @@ -5386,7 +5658,7 @@ snapshots: engine.io@6.6.9: dependencies: '@types/cors': 2.8.19 - '@types/node': 25.9.3 + '@types/node': 26.0.1 '@types/ws': 8.18.1 accepts: 1.3.8 base64id: 2.0.0 @@ -5422,34 +5694,63 @@ snapshots: dependencies: es-errors: 1.3.0 - esbuild@0.27.3: + esbuild@0.27.7: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.3 - '@esbuild/android-arm': 0.27.3 - '@esbuild/android-arm64': 0.27.3 - '@esbuild/android-x64': 0.27.3 - '@esbuild/darwin-arm64': 0.27.3 - '@esbuild/darwin-x64': 0.27.3 - '@esbuild/freebsd-arm64': 0.27.3 - '@esbuild/freebsd-x64': 0.27.3 - '@esbuild/linux-arm': 0.27.3 - '@esbuild/linux-arm64': 0.27.3 - '@esbuild/linux-ia32': 0.27.3 - '@esbuild/linux-loong64': 0.27.3 - '@esbuild/linux-mips64el': 0.27.3 - '@esbuild/linux-ppc64': 0.27.3 - '@esbuild/linux-riscv64': 0.27.3 - '@esbuild/linux-s390x': 0.27.3 - '@esbuild/linux-x64': 0.27.3 - '@esbuild/netbsd-arm64': 0.27.3 - '@esbuild/netbsd-x64': 0.27.3 - '@esbuild/openbsd-arm64': 0.27.3 - '@esbuild/openbsd-x64': 0.27.3 - '@esbuild/openharmony-arm64': 0.27.3 - '@esbuild/sunos-x64': 0.27.3 - '@esbuild/win32-arm64': 0.27.3 - '@esbuild/win32-ia32': 0.27.3 - '@esbuild/win32-x64': 0.27.3 + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + + esbuild@0.28.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 escalade@3.2.0: {} @@ -5546,7 +5847,7 @@ snapshots: dependencies: eventsource-parser: 3.1.0 - expect-type@1.3.0: {} + expect-type@1.4.0: {} exponential-backoff@3.1.3: {} @@ -5577,8 +5878,8 @@ snapshots: once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.15.2 - range-parser: 1.2.1 + qs: 6.15.3 + range-parser: 1.3.0 router: 2.2.0 send: 1.2.1 serve-static: 2.2.1 @@ -5712,7 +6013,7 @@ snapshots: dependencies: function-bind: 1.1.2 - hono@4.12.26: {} + hono@4.12.27: {} hosted-git-info@9.0.3: dependencies: @@ -5767,7 +6068,7 @@ snapshots: ignore@7.0.5: {} - immutable@5.1.6: {} + immutable@5.1.8: {} import-fresh@3.3.1: dependencies: @@ -5814,7 +6115,7 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7 '@babel/parser': 7.29.7 '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 @@ -5826,7 +6127,7 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@4.2.0: + js-yaml@4.3.0: dependencies: argparse: 2.0.1 @@ -6058,7 +6359,7 @@ snapshots: mute-stream@2.0.0: {} - nanoid@3.3.13: {} + nanoid@3.3.15: {} natural-compare@1.4.0: {} @@ -6106,12 +6407,12 @@ snapshots: nopt: 9.0.0 proc-log: 6.1.0 semver: 7.7.4 - tar: 7.5.16 + tar: 7.5.17 tinyglobby: 0.2.17 undici: 6.27.0 which: 6.0.1 - node-releases@2.0.48: {} + node-releases@2.0.50: {} nopt@9.0.0: dependencies: @@ -6232,7 +6533,7 @@ snapshots: proc-log: 6.1.0 sigstore: 4.1.1 ssri: 13.0.1 - tar: 7.5.16 + tar: 7.5.17 transitivePeerDependencies: - supports-color @@ -6275,7 +6576,7 @@ snapshots: picomatch@4.0.4: {} - piscina@5.1.4: + piscina@5.2.0: optionalDependencies: '@napi-rs/nice': 1.1.1 @@ -6289,7 +6590,7 @@ snapshots: postcss@8.5.15: dependencies: - nanoid: 3.3.13 + nanoid: 3.3.15 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -6304,13 +6605,14 @@ snapshots: punycode@2.3.1: {} - qs@6.15.2: + qs@6.15.3: dependencies: + es-define-property: 1.0.1 side-channel: 1.1.1 queue-microtask@1.2.3: {} - range-parser@1.2.1: {} + range-parser@1.3.0: {} raw-body@3.0.2: dependencies: @@ -6360,35 +6662,35 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - rollup@4.62.0: + rollup@4.62.2: dependencies: '@types/estree': 1.0.9 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.62.0 - '@rollup/rollup-android-arm64': 4.62.0 - '@rollup/rollup-darwin-arm64': 4.62.0 - '@rollup/rollup-darwin-x64': 4.62.0 - '@rollup/rollup-freebsd-arm64': 4.62.0 - '@rollup/rollup-freebsd-x64': 4.62.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.62.0 - '@rollup/rollup-linux-arm-musleabihf': 4.62.0 - '@rollup/rollup-linux-arm64-gnu': 4.62.0 - '@rollup/rollup-linux-arm64-musl': 4.62.0 - '@rollup/rollup-linux-loong64-gnu': 4.62.0 - '@rollup/rollup-linux-loong64-musl': 4.62.0 - '@rollup/rollup-linux-ppc64-gnu': 4.62.0 - '@rollup/rollup-linux-ppc64-musl': 4.62.0 - '@rollup/rollup-linux-riscv64-gnu': 4.62.0 - '@rollup/rollup-linux-riscv64-musl': 4.62.0 - '@rollup/rollup-linux-s390x-gnu': 4.62.0 - '@rollup/rollup-linux-x64-gnu': 4.62.0 - '@rollup/rollup-linux-x64-musl': 4.62.0 - '@rollup/rollup-openbsd-x64': 4.62.0 - '@rollup/rollup-openharmony-arm64': 4.62.0 - '@rollup/rollup-win32-arm64-msvc': 4.62.0 - '@rollup/rollup-win32-ia32-msvc': 4.62.0 - '@rollup/rollup-win32-x64-gnu': 4.62.0 - '@rollup/rollup-win32-x64-msvc': 4.62.0 + '@rollup/rollup-android-arm-eabi': 4.62.2 + '@rollup/rollup-android-arm64': 4.62.2 + '@rollup/rollup-darwin-arm64': 4.62.2 + '@rollup/rollup-darwin-x64': 4.62.2 + '@rollup/rollup-freebsd-arm64': 4.62.2 + '@rollup/rollup-freebsd-x64': 4.62.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 + '@rollup/rollup-linux-arm-musleabihf': 4.62.2 + '@rollup/rollup-linux-arm64-gnu': 4.62.2 + '@rollup/rollup-linux-arm64-musl': 4.62.2 + '@rollup/rollup-linux-loong64-gnu': 4.62.2 + '@rollup/rollup-linux-loong64-musl': 4.62.2 + '@rollup/rollup-linux-ppc64-gnu': 4.62.2 + '@rollup/rollup-linux-ppc64-musl': 4.62.2 + '@rollup/rollup-linux-riscv64-gnu': 4.62.2 + '@rollup/rollup-linux-riscv64-musl': 4.62.2 + '@rollup/rollup-linux-s390x-gnu': 4.62.2 + '@rollup/rollup-linux-x64-gnu': 4.62.2 + '@rollup/rollup-linux-x64-musl': 4.62.2 + '@rollup/rollup-openbsd-x64': 4.62.2 + '@rollup/rollup-openharmony-arm64': 4.62.2 + '@rollup/rollup-win32-arm64-msvc': 4.62.2 + '@rollup/rollup-win32-ia32-msvc': 4.62.2 + '@rollup/rollup-win32-x64-gnu': 4.62.2 + '@rollup/rollup-win32-x64-msvc': 4.62.2 fsevents: 2.3.3 router@2.2.0: @@ -6414,7 +6716,7 @@ snapshots: sass@1.97.3: dependencies: chokidar: 4.0.3 - immutable: 5.1.6 + immutable: 5.1.8 source-map-js: 1.2.1 optionalDependencies: '@parcel/watcher': 2.5.6 @@ -6429,7 +6731,7 @@ snapshots: semver@7.7.4: {} - semver@7.8.4: {} + semver@7.8.5: {} send@1.2.1: dependencies: @@ -6442,7 +6744,7 @@ snapshots: mime-types: 3.0.2 ms: 2.1.3 on-finished: 2.4.1 - range-parser: 1.2.1 + range-parser: 1.3.0 statuses: 2.0.2 transitivePeerDependencies: - supports-color @@ -6638,7 +6940,7 @@ snapshots: symbol-tree@3.2.4: {} - tar@7.5.16: + tar@7.5.17: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 @@ -6662,11 +6964,11 @@ snapshots: tinyrainbow@3.1.0: {} - tldts-core@7.4.3: {} + tldts-core@7.4.4: {} - tldts@7.4.3: + tldts@7.4.4: dependencies: - tldts-core: 7.4.3 + tldts-core: 7.4.4 to-regex-range@5.0.1: dependencies: @@ -6676,7 +6978,7 @@ snapshots: tough-cookie@6.0.1: dependencies: - tldts: 7.4.3 + tldts: 7.4.4 tr46@6.0.0: dependencies: @@ -6719,7 +7021,7 @@ snapshots: typescript@5.9.3: {} - undici-types@7.24.6: {} + undici-types@8.3.0: {} undici@6.27.0: {} @@ -6727,9 +7029,9 @@ snapshots: unpipe@1.0.0: {} - update-browserslist-db@1.2.3(browserslist@4.28.2): + update-browserslist-db@1.2.3(browserslist@4.28.4): dependencies: - browserslist: 4.28.2 + browserslist: 4.28.4 escalade: 3.2.0 picocolors: 1.1.1 @@ -6741,30 +7043,30 @@ snapshots: vary@1.1.2: {} - vite@7.3.2(@types/node@25.9.3)(sass@1.97.3): + vite@7.3.5(@types/node@26.0.1)(sass@1.97.3): dependencies: - esbuild: 0.27.3 + esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 postcss: 8.5.15 - rollup: 4.62.0 + rollup: 4.62.2 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 fsevents: 2.3.3 sass: 1.97.3 - vitest@4.1.9(@types/node@25.9.3)(jsdom@27.4.0)(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)): + vitest@4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3)): dependencies: '@vitest/expect': 4.1.9 - '@vitest/mocker': 4.1.9(vite@7.3.2(@types/node@25.9.3)(sass@1.97.3)) + '@vitest/mocker': 4.1.9(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3)) '@vitest/pretty-format': 4.1.9 '@vitest/runner': 4.1.9 '@vitest/snapshot': 4.1.9 '@vitest/spy': 4.1.9 '@vitest/utils': 4.1.9 es-module-lexer: 2.1.0 - expect-type: 1.3.0 + expect-type: 1.4.0 magic-string: 0.30.21 obug: 2.1.3 pathe: 2.0.3 @@ -6774,10 +7076,10 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 7.3.2(@types/node@25.9.3)(sass@1.97.3) + vite: 7.3.5(@types/node@26.0.1)(sass@1.97.3) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.9.3 + '@types/node': 26.0.1 jsdom: 27.4.0 transitivePeerDependencies: - msw diff --git a/uv.lock b/uv.lock index 8d92749..84ff80a 100644 --- a/uv.lock +++ b/uv.lock @@ -106,14 +106,14 @@ wheels = [ [[package]] name = "anyio" -version = "4.14.0" +version = "4.14.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/1c/b5/001890774a9552aff22502b8da382593109ce0c95314abaebbb116567545/anyio-4.14.0.tar.gz", hash = "sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89", size = 253586, upload-time = "2026-06-15T22:00:49.021Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3b/72/5562aabb8dd7181e8e860622a38bea08d17842b99ecd4c91f84ac95251b0/anyio-4.14.1.tar.gz", hash = "sha256:8d648a3544c1a700e3ff78615cd679e4c5c3f149904287e73687b2596963629e", size = 254831, upload-time = "2026-06-24T20:56:06.017Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/16/9826f089383c593cdfc4a6e5aca94d9e91ae1692c57af82c3b2aa5e810f7/anyio-4.14.0-py3-none-any.whl", hash = "sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9", size = 123506, upload-time = "2026-06-15T22:00:47.595Z" }, + { url = "https://files.pythonhosted.org/packages/b0/7b/90df4a0a816d98d6ea26f559d87836d494a2cf1fcf063be67df50a7bcc30/anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72", size = 124875, upload-time = "2026-06-24T20:56:04.413Z" }, ] [[package]] @@ -347,15 +347,15 @@ wheels = [ [[package]] name = "deno" -version = "2.8.3" +version = "2.9.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/db/ec/98f972cca4ca734534947bdf28b86a99e9ed8a5a31a061be2dec9b4105c2/deno-2.8.3.tar.gz", hash = "sha256:5413f4a1814ac3b8e441ca7fe9eb677a4152a42eef05efd56a61d750088a7fc8", size = 8163, upload-time = "2026-06-11T16:13:53.129Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/96/098c84eff4b8ec7da8fb1b43d3371c22f021b7642d5b2bdad8d1780cd605/deno-2.9.0.tar.gz", hash = "sha256:7548d37528eaca993a2d4483ce092eca971fa206faadb0f4c8af8c3f039677d2", size = 8168, upload-time = "2026-06-25T15:03:34.872Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/52/42/71f88d6c04c7f4335f94c1c9eb5aee7e928e1a53056bb9b20c0c4d889f48/deno-2.8.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:57825615e8d4182160401b56907b2e36712aed55a5dfdef2c0e3089379bb6988", size = 42314540, upload-time = "2026-06-11T16:13:39.134Z" }, - { url = "https://files.pythonhosted.org/packages/df/95/032101e28532fa9ed28ca7dc737934d4631544fa3b11f4efe3573970a05a/deno-2.8.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:05dbbbd8edf1907abcbcf55395e63f00d74c2672b6039dd1ecd5656baebaa56c", size = 38101511, upload-time = "2026-06-11T16:13:42.192Z" }, - { url = "https://files.pythonhosted.org/packages/23/24/68c1d2d79933738acb8e5ef3a4584f57c6d5deb56c84073a5f4079c24647/deno-2.8.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:9fdb7574a6437fdd59f668e745dc3b3e32725fa360b64204a6b000689fa534e9", size = 42053660, upload-time = "2026-06-11T16:13:45.006Z" }, - { url = "https://files.pythonhosted.org/packages/d2/56/0d23c6daa1b139c31591df801feda3ac7d0d6225f1686cbf5caaa5db5c27/deno-2.8.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:9ca5727e5650f8459f39875f0af4b2242ead77da4ce8a8d52e86647036f3d63a", size = 43800396, upload-time = "2026-06-11T16:13:47.821Z" }, - { url = "https://files.pythonhosted.org/packages/6f/aa/e35b736205b89c98f31ed4abeebaa7846774e51006b14601e4edb7728e67/deno-2.8.3-py3-none-win_amd64.whl", hash = "sha256:37da75ee91448e4e6f5626f0d5cb18e295dbb6cff5cf780fab1f92ebbbd44071", size = 41552180, upload-time = "2026-06-11T16:13:50.711Z" }, + { url = "https://files.pythonhosted.org/packages/0a/64/ebd8bb0fc2ef57b0f8cff53cd9b43261c9c16dfaf0a07db37b9fcb2ec186/deno-2.9.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f53bbb11a9a7eeae3865470baa1d5cb02da105ac073019953e9d59e0588bc10e", size = 42301281, upload-time = "2026-06-25T15:03:18.675Z" }, + { url = "https://files.pythonhosted.org/packages/63/3c/1cc1c8c731e5dada562640dc42c0603c017bc1ba731a69255cecd9b25be0/deno-2.9.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e58aa71c144dcd91ef9fd5b044dd465d1be571b6763c3a777924d4d54f7033e7", size = 37950190, upload-time = "2026-06-25T15:03:21.958Z" }, + { url = "https://files.pythonhosted.org/packages/30/86/e5487fe222840aca26c292cfa2532fb0ba4b080b6106f89cce530a7470a5/deno-2.9.0-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:3401337f5c3b75e8f7c1b07589612cddac66ddc17a0a18e7eff6e55f23a1b53b", size = 42017185, upload-time = "2026-06-25T15:03:25.328Z" }, + { url = "https://files.pythonhosted.org/packages/5a/d0/7f73a8d5f77947c327272147d89d005426888d4d5b3d6575181c1a7271e9/deno-2.9.0-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:63988d645e591bc171a574aa6b3ada2ddfc9bcf59f0178412a9fde1991bfc111", size = 43869469, upload-time = "2026-06-25T15:03:28.311Z" }, + { url = "https://files.pythonhosted.org/packages/01/34/ca326c53558f99792eb4e2c19ca88763e6890ce1ffb44b9536ecd7ae983f/deno-2.9.0-py3-none-win_amd64.whl", hash = "sha256:29f6f616b47e5babd9604bcd1a76ec3576895275f8696dedfb33ab6545f6ed85", size = 41589185, upload-time = "2026-06-25T15:03:32.002Z" }, ] [[package]] @@ -628,11 +628,11 @@ wheels = [ [[package]] name = "mutagen" -version = "1.47.0" +version = "1.48.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/81/e6/64bc71b74eef4b68e61eb921dcf72dabd9e4ec4af1e11891bbd312ccbb77/mutagen-1.47.0.tar.gz", hash = "sha256:719fadef0a978c31b4cf3c956261b3c58b6948b32023078a2117b1de09f0fc99", size = 1274186, upload-time = "2023-09-03T16:33:33.411Z" } +sdist = { url = "https://files.pythonhosted.org/packages/df/70/1675da133ea92227da41bf5b24e1c66be597ff736a1533ade41da986852f/mutagen-1.48.1.tar.gz", hash = "sha256:8f95637ab9f6f305cec6bd1294e197debe207998e3e068596563c74f86b0a173", size = 1276978, upload-time = "2026-06-25T09:47:32.443Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/7a/620f945b96be1f6ee357d211d5bf74ab1b7fe72a9f1525aafbfe3aee6875/mutagen-1.47.0-py3-none-any.whl", hash = "sha256:edd96f50c5907a9539d8e5bba7245f62c9f520aef333d13392a79a4f70aca719", size = 194391, upload-time = "2023-09-03T16:33:29.955Z" }, + { url = "https://files.pythonhosted.org/packages/47/d8/a29e4e3991765e7ce4ed1f7e4074fe1ba9da03e0048639734de60f9cadb9/mutagen-1.48.1-py3-none-any.whl", hash = "sha256:4f077fe87d3fc7fba259aa63d8c026b18382ca6a42ef37c61e16f1b1b5b82fe7", size = 195706, upload-time = "2026-06-25T09:47:30.296Z" }, ] [[package]] @@ -849,14 +849,14 @@ wheels = [ [[package]] name = "python-engineio" -version = "4.13.2" +version = "4.13.3" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "simple-websocket" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/fa/6d/4384c2723adad93a3d6de4297e6d9c8b93be7f778a407f34f6ee0b2bea3e/python_engineio-4.13.2.tar.gz", hash = "sha256:a7732e99cfb7db6ed1aee31f18d7f73bbae086a92f31dee019bc646155d9684e", size = 79639, upload-time = "2026-05-21T21:45:07.578Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/a0/f75491f942184d9960b15e763270f765fe9f239745ca5f9e16289011aed4/python_engineio-4.13.3.tar.gz", hash = "sha256:572b7783e341fed21edbc7cea297ccd378dad79265fdde96aa4664420a7c06c9", size = 79734, upload-time = "2026-06-20T22:53:52.197Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b7/28/180bfc5c95e83d40cb2abce512684ccad44e4819ec899fc36cb404a19061/python_engineio-4.13.2-py3-none-any.whl", hash = "sha256:8c101cd170e400dc4e970cd523325cde22df8fc25140953f379327055d701a6b", size = 59993, upload-time = "2026-05-21T21:45:06.162Z" }, + { url = "https://files.pythonhosted.org/packages/5b/96/82f6328e410515fab21d5602ba35b9377a47b5a141a0c1f9efa00ce21eb4/python_engineio-4.13.3-py3-none-any.whl", hash = "sha256:1f60ecaf1358190f0e26c48c578a60428dc02a8f1295bc3dbf53d1b31116821f", size = 59993, upload-time = "2026-06-20T22:53:50.775Z" }, ] [[package]] From 51fd203b71a4518fa7bf01744139fe7fe30f17cb Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Sun, 28 Jun 2026 21:12:20 +0300 Subject: [PATCH 64/88] upgrade to Angular 22 --- AGENTS.md | 2 +- ui/package.json | 38 +- ui/pnpm-lock.yaml | 2170 ++++++++--------- ui/src/app/app.config.ts | 4 +- .../components/master-checkbox.component.ts | 7 +- .../components/slave-checkbox.component.ts | 7 +- ui/tsconfig.app.json | 10 +- 7 files changed, 1023 insertions(+), 1215 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 35bae0d..4a27f4d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,7 +9,7 @@ If an addition would exceed the limit, trim existing prose elsewhere β€” prefer ## Tech stack - **Backend:** Python 3.13+, aiohttp, python-socketio 5.x, yt-dlp -- **Frontend:** Angular 21, TypeScript, Bootstrap 5, SASS, ngx-socket-io +- **Frontend:** Angular 22, TypeScript, Bootstrap 5, SASS, ngx-socket-io - **Package managers:** uv (Python), pnpm (frontend) - **Container:** Multi-stage Docker (Node builder + Python runtime), multi-arch (amd64/arm64) diff --git a/ui/package.json b/ui/package.json index 14b94d0..aecff95 100644 --- a/ui/package.json +++ b/ui/package.json @@ -23,41 +23,41 @@ }, "private": true, "dependencies": { - "@angular/animations": "^21.2.17", - "@angular/common": "^21.2.17", - "@angular/compiler": "^21.2.17", - "@angular/core": "^21.2.17", - "@angular/forms": "^21.2.17", - "@angular/platform-browser": "^21.2.17", - "@angular/platform-browser-dynamic": "^21.2.17", - "@angular/service-worker": "^21.2.17", + "@angular/animations": "^22.0.4", + "@angular/common": "^22.0.4", + "@angular/compiler": "^22.0.4", + "@angular/core": "^22.0.4", + "@angular/forms": "^22.0.4", + "@angular/platform-browser": "^22.0.4", + "@angular/platform-browser-dynamic": "^22.0.4", + "@angular/service-worker": "^22.0.4", "@fortawesome/angular-fontawesome": "~4.0.0", "@fortawesome/fontawesome-svg-core": "^7.3.0", "@fortawesome/free-brands-svg-icons": "^7.3.0", "@fortawesome/free-regular-svg-icons": "^7.3.0", "@fortawesome/free-solid-svg-icons": "^7.3.0", - "@ng-bootstrap/ng-bootstrap": "^20.0.0", - "@ng-select/ng-select": "^21.8.2", + "@ng-bootstrap/ng-bootstrap": "^21.0.0", + "@ng-select/ng-select": "^23.2.0", "@popperjs/core": "^2.11.8", "bootstrap": "^5.3.8", - "ngx-cookie-service": "^21.3.1", + "ngx-cookie-service": "^22.0.0", "ngx-socket-io": "~4.10.0", "rxjs": "~7.8.2", "tslib": "^2.8.1", "zone.js": "0.15.0" }, "devDependencies": { - "@angular-eslint/builder": "21.1.0", - "@angular/build": "^21.2.17", - "@angular/cli": "^21.2.17", - "@angular/compiler-cli": "^21.2.17", - "@angular/localize": "^21.2.17", + "@angular-eslint/builder": "22.0.0", + "@angular/build": "^22.0.4", + "@angular/cli": "^22.0.4", + "@angular/compiler-cli": "^22.0.4", + "@angular/localize": "^22.0.4", "@eslint/js": "^9.39.4", - "angular-eslint": "21.1.0", + "angular-eslint": "22.0.0", "eslint": "^9.39.4", "jsdom": "^27.4.0", - "typescript": "~5.9.3", - "typescript-eslint": "8.47.0", + "typescript": "~6.0.3", + "typescript-eslint": "8.62.0", "vitest": "^4.1.9" } } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 7e40abe..4fef557 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -9,32 +9,32 @@ importers: .: dependencies: '@angular/animations': - specifier: ^21.2.17 - version: 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^22.0.4 + version: 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/common': - specifier: ^21.2.17 - version: 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.4 + version: 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@angular/compiler': - specifier: ^21.2.17 - version: 21.2.17 + specifier: ^22.0.4 + version: 22.0.4 '@angular/core': - specifier: ^21.2.17 - version: 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.4 + version: 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) '@angular/forms': - specifier: ^21.2.17 - version: 21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + specifier: ^22.0.4 + version: 22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) '@angular/platform-browser': - specifier: ^21.2.17 - version: 21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^22.0.4 + version: 22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/platform-browser-dynamic': - specifier: ^21.2.17 - version: 21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))) + specifier: ^22.0.4 + version: 22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@22.0.4)(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))) '@angular/service-worker': - specifier: ^21.2.17 - version: 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.4 + version: 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@fortawesome/angular-fontawesome': specifier: ~4.0.0 - version: 4.0.0(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) + version: 4.0.0(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) '@fortawesome/fontawesome-svg-core': specifier: ^7.3.0 version: 7.3.0 @@ -48,11 +48,11 @@ importers: specifier: ^7.3.0 version: 7.3.0 '@ng-bootstrap/ng-bootstrap': - specifier: ^20.0.0 - version: 20.0.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@popperjs/core@2.11.8)(rxjs@7.8.2) + specifier: ^21.0.0 + version: 21.0.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4))(@popperjs/core@2.11.8)(rxjs@7.8.2) '@ng-select/ng-select': - specifier: ^21.8.2 - version: 21.8.2(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) + specifier: ^23.2.0 + version: 23.2.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) '@popperjs/core': specifier: ^2.11.8 version: 2.11.8 @@ -60,11 +60,11 @@ importers: specifier: ^5.3.8 version: 5.3.8(@popperjs/core@2.11.8) ngx-cookie-service: - specifier: ^21.3.1 - version: 21.3.1(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^22.0.0 + version: 22.0.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) ngx-socket-io: specifier: ~4.10.0 - version: 4.10.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + version: 4.10.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) rxjs: specifier: ~7.8.2 version: 7.8.2 @@ -76,26 +76,26 @@ importers: version: 0.15.0 devDependencies: '@angular-eslint/builder': - specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) + specifier: 22.0.0 + version: 22.0.0(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3) '@angular/build': - specifier: ^21.2.17 - version: 21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@26.0.1)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3))) + specifier: ^22.0.4 + version: 22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4)(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@types/node@26.0.1)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(postcss@8.5.15)(tslib@2.8.1)(typescript@6.0.3)(vitest@4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0))) '@angular/cli': - specifier: ^21.2.17 - version: 21.2.17(@types/node@26.0.1)(chokidar@5.0.0) + specifier: ^22.0.4 + version: 22.0.4(@types/node@26.0.1)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^21.2.17 - version: 21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3) + specifier: ^22.0.4 + version: 22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3) '@angular/localize': - specifier: ^21.2.17 - version: 21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17) + specifier: ^22.0.4 + version: 22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4) '@eslint/js': specifier: ^9.39.4 version: 9.39.4 angular-eslint: - specifier: 21.1.0 - version: 21.1.0(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3) + specifier: 22.0.0 + version: 22.0.0(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.62.0(eslint@9.39.4)(typescript@6.0.3))(typescript@6.0.3) eslint: specifier: ^9.39.4 version: 9.39.4 @@ -103,168 +103,169 @@ importers: specifier: ^27.4.0 version: 27.4.0 typescript: - specifier: ~5.9.3 - version: 5.9.3 + specifier: ~6.0.3 + version: 6.0.3 typescript-eslint: - specifier: 8.47.0 - version: 8.47.0(eslint@9.39.4)(typescript@5.9.3) + specifier: 8.62.0 + version: 8.62.0(eslint@9.39.4)(typescript@6.0.3) vitest: specifier: ^4.1.9 - version: 4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3)) + version: 4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0)) packages: '@acemir/cssom@0.9.31': resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} - '@algolia/abtesting@1.14.1': - resolution: {integrity: sha512-Dkj0BgPiLAaim9sbQ97UKDFHJE/880wgStAM18U++NaJ/2Cws34J5731ovJifr6E3Pv4T2CqvMXf8qLCC417Ew==} + '@algolia/abtesting@1.18.0': + resolution: {integrity: sha512-8siuLG+FIns1AjZ/g2SDVwHz9S+ObacDQISEJvS8XsNei1zl3FXqfqQrBpmrG7ACWCyesXHbicMJtvRbg00FEw==} engines: {node: '>= 14.0.0'} - '@algolia/client-abtesting@5.48.1': - resolution: {integrity: sha512-LV5qCJdj+/m9I+Aj91o+glYszrzd7CX6NgKaYdTOj4+tUYfbS62pwYgUfZprYNayhkQpVFcrW8x8ZlIHpS23Vw==} + '@algolia/client-abtesting@5.52.0': + resolution: {integrity: sha512-wtwPgyPmO7b7sQPVgoK29c1VpfS08DnnJCmxX/oU1pV2DlMRJCzQcLN7JSloYpodyKHwM8+9wOzlAM0co3TDmA==} engines: {node: '>= 14.0.0'} - '@algolia/client-analytics@5.48.1': - resolution: {integrity: sha512-/AVoMqHhPm14CcHq7mwB+bUJbfCv+jrxlNvRjXAuO+TQa+V37N8k1b0ijaRBPdmSjULMd8KtJbQyUyabXOu6Kg==} + '@algolia/client-analytics@5.52.0': + resolution: {integrity: sha512-9KY36bRl4AH7RjqSeDDOKnjsz4IxQFBEOB8/fWmEbdQe+Isbs5jGzVJu9NEPQ1Tgwxlf8Uf07Swj3jZyMNUZ2g==} engines: {node: '>= 14.0.0'} - '@algolia/client-common@5.48.1': - resolution: {integrity: sha512-VXO+qu2Ep6ota28ktvBm3sG53wUHS2n7bgLWmce5jTskdlCD0/JrV4tnBm1l7qpla1CeoQb8D7ShFhad+UoSOw==} + '@algolia/client-common@5.52.0': + resolution: {integrity: sha512-3a/qM3dzJqqfTx7Yrw7uGQ98I3Q0rDfb4Vkv0wEzko96l7YQMxfBVz/VbLq2N+c59GweYv6Vhp8mPeqnWJSITw==} engines: {node: '>= 14.0.0'} - '@algolia/client-insights@5.48.1': - resolution: {integrity: sha512-zl+Qyb0nLg+Y5YvKp1Ij+u9OaPaKg2/EPzTwKNiVyOHnQJlFxmXyUZL1EInczAZsEY8hVpPCLtNfhMhfxluXKQ==} + '@algolia/client-insights@5.52.0': + resolution: {integrity: sha512-Rki7ACbMcvbQW0BuM84x9dkGHY47ABmv4jU6tYssat2k02p3mIUms2YOLUAMeknhmnFsj6lb6ZzOXdMWMyc1sA==} engines: {node: '>= 14.0.0'} - '@algolia/client-personalization@5.48.1': - resolution: {integrity: sha512-r89Qf9Oo9mKWQXumRu/1LtvVJAmEDpn8mHZMc485pRfQUMAwSSrsnaw1tQ3sszqzEgAr1c7rw6fjBI+zrAXTOw==} + '@algolia/client-personalization@5.52.0': + resolution: {integrity: sha512-96s4Uzc3kk+/f4jJXIVVGWP5XlngOGNQ1x6hW9AT59pOixHlOs5tqJg+ZUS/GQ6h/iYP0ceQcmxDQeLyCLTaDQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-query-suggestions@5.48.1': - resolution: {integrity: sha512-TPKNPKfghKG/bMSc7mQYD9HxHRUkBZA4q1PEmHgICaSeHQscGqL4wBrKkhfPlDV1uYBKW02pbFMUhsOt7p4ZpA==} + '@algolia/client-query-suggestions@5.52.0': + resolution: {integrity: sha512-lqeycNpSPe5Qa0OUWpejVvYQjQWV5nQuLT0a4aq7XzRAvCxprV/6Lf841EygdD2nrFnuS58ok7Au1uOtXzpnkg==} engines: {node: '>= 14.0.0'} - '@algolia/client-search@5.48.1': - resolution: {integrity: sha512-4Fu7dnzQyQmMFknYwTiN/HxPbH4DyxvQ1m+IxpPp5oslOgz8m6PG5qhiGbqJzH4HiT1I58ecDiCAC716UyVA8Q==} + '@algolia/client-search@5.52.0': + resolution: {integrity: sha512-ly1wETVGRo30cx61O7fetESN+ElL9c9K+bD/AVgnT1ar4c6v+/Yqjrhdtu6Fm4D0s4NZP081Isf6tunH1wUXHg==} engines: {node: '>= 14.0.0'} - '@algolia/ingestion@1.48.1': - resolution: {integrity: sha512-/RFq3TqtXDUUawwic/A9xylA2P3LDMO8dNhphHAUOU51b1ZLHrmZ6YYJm3df1APz7xLY1aht6okCQf+/vmrV9w==} + '@algolia/ingestion@1.52.0': + resolution: {integrity: sha512-U4EeTvgmluRjj39ykZSAd5X+a6LD5m7/mcOWDmB7hqm1R6QY0yT8jLxpNVEjYhzgEN5hcDGW6X67EWQY8KiYGQ==} engines: {node: '>= 14.0.0'} - '@algolia/monitoring@1.48.1': - resolution: {integrity: sha512-Of0jTeAZRyRhC7XzDSjJef0aBkgRcvRAaw0ooYRlOw57APii7lZdq+layuNdeL72BRq1snaJhoMMwkmLIpJScw==} + '@algolia/monitoring@1.52.0': + resolution: {integrity: sha512-FCPnDcILfpTE94u7BVlV4DmnSV5wE3+j25EEF+3dYPrVzkVCSoAHs318oWDGxnxsAgiL4HpL12Jc4XHmw9shpA==} engines: {node: '>= 14.0.0'} - '@algolia/recommend@5.48.1': - resolution: {integrity: sha512-bE7JcpFXzxF5zHwj/vkl2eiCBvyR1zQ7aoUdO+GDXxGp0DGw7nI0p8Xj6u8VmRQ+RDuPcICFQcCwRIJT5tDJFw==} + '@algolia/recommend@5.52.0': + resolution: {integrity: sha512-br3DO7n4N8CXwTRbZS0MnB4WQ9YHfNjCwkCEzVR/wek/qNTDQKDb0nROmkFaNZ8ucUqUVKZi074dbwMwRDlK8Q==} engines: {node: '>= 14.0.0'} - '@algolia/requester-browser-xhr@5.48.1': - resolution: {integrity: sha512-MK3wZ2koLDnvH/AmqIF1EKbJlhRS5j74OZGkLpxI4rYvNi9Jn/C7vb5DytBnQ4KUWts7QsmbdwHkxY5txQHXVw==} + '@algolia/requester-browser-xhr@5.52.0': + resolution: {integrity: sha512-b0T/Ca2c9KyEslKsVrGZvbe1UrrKKSdfXhBZ2pbpKahFUzJfziRZ0urbOm7V65O0tO/jwU+Lo/+bIiiyhzGt8w==} engines: {node: '>= 14.0.0'} - '@algolia/requester-fetch@5.48.1': - resolution: {integrity: sha512-2oDT43Y5HWRSIQMPQI4tA/W+TN/N2tjggZCUsqQV440kxzzoPGsvv9QP1GhQ4CoDa+yn6ygUsGp6Dr+a9sPPSg==} + '@algolia/requester-fetch@5.52.0': + resolution: {integrity: sha512-ozBT8J/mtD4H4IAojw8QPirlcL2gHrI1BGuZ4/ZXXO/rTE1yQ4VIPJj4mTTbwo4FbkS1MoJsD/DsrqLzhnc4/g==} engines: {node: '>= 14.0.0'} - '@algolia/requester-node-http@5.48.1': - resolution: {integrity: sha512-xcaCqbhupVWhuBP1nwbk1XNvwrGljozutEiLx06mvqDf3o8cHyEgQSHS4fKJM+UAggaWVnnFW+Nne5aQ8SUJXg==} + '@algolia/requester-node-http@5.52.0': + resolution: {integrity: sha512-gyyWcLD22tnabmoit4iukCXuoRc5HYJuUjPSEa8a0D/f/NlRafpWi52AlAaa4Uu/rsl7saHsJFTNjTptWbu2+A==} engines: {node: '>= 14.0.0'} '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.2102.17': - resolution: {integrity: sha512-+lkmpEfKXuPZaGZRCCT/B2JGSnafudEVMLeolAW0G7de28bwJIwTE5nmr/rcKTy4NnEDX/8B53tWTXGENuQvmg==} - 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'} + '@angular-devkit/architect@0.2200.4': + resolution: {integrity: sha512-X/iEQiZ0pRmpjUt11jCM+mtOLRl4XxI9hnM0IC9aAcsm5AzRBb9WY6QIEqOSficjxC/+MI7MGwrerrcP6QN8UA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular-devkit/core@21.2.17': - resolution: {integrity: sha512-JbGWwFX1Nv4Np0S9b4HP2SUKhR2bz6l/S8zBXnam95+RXzDaqXJDmQHOHZGS+4La06SjlcXwFyrgSI0rm50A1A==} - 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'} + '@angular-devkit/core@22.0.4': + resolution: {integrity: sha512-zA2UJSMAU3su5uJTOn5ul/gCLRcw6/uIQ6EC5v/Ju/ePjgDIw9R3y3MAvWQ4Ibi/fXiq0FVxpF8hE7RUclYmJA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: chokidar: ^5.0.0 peerDependenciesMeta: chokidar: optional: true - '@angular-devkit/schematics@21.2.17': - resolution: {integrity: sha512-IMsbo/OBG0mdCRxezbq/CLo6JxUJBOAHZfbUDzxbSRwJm8FT5AbXO7rW+Z3haoqcb+WrC5Pr+DQ6WolX2x2brQ==} - 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'} + '@angular-devkit/schematics@22.0.4': + resolution: {integrity: sha512-VRxL1hD/Q3TQglM6EfQ0ksAW4OIvtKyZgtaUpyGsJRfD6tGmLFn7MDnmyyq1ceLX/Clq+3tzH/wN0tF6rcE0jA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@angular-eslint/builder@21.1.0': - resolution: {integrity: sha512-pcUlDkGqeZ+oQC0oEjnkDDlB96gbgHQhnBUKdhYAiAOSuiBod4+npP0xQOq5chYtRNPBprhDqgrJrp5DBeDMOA==} + '@angular-eslint/builder@22.0.0': + resolution: {integrity: sha512-T2vWQYUhJs6iUlgocHV12OgoxbmN63f17a+tgW+3sYrKN0KAB3xuHsPOoYpRYoWqkVVC44HD441Ju4IDvo8vKg==} peerDependencies: - '@angular/cli': '>= 21.0.0 < 22.0.0' - eslint: ^8.57.0 || ^9.0.0 + '@angular/cli': '>= 22.0.0 < 23.0.0' + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular-eslint/bundled-angular-compiler@21.1.0': - resolution: {integrity: sha512-t52J6FszgEHaJ+IjuzU9qaWfVxsjlVNkAP+B5z2t4NDgbbDDsmI+QJh0OtP1qdlqzjh2pbocEml30KhYmNZm/Q==} + '@angular-eslint/bundled-angular-compiler@22.0.0': + resolution: {integrity: sha512-rv15vGDpGW8zZFaLdhQ+iIO1f0bZds/xvuxoX277hFisXp5Kt6FumJNNIb4g/qxq3xsY46a7fD6R7KvGY3smHg==} - '@angular-eslint/eslint-plugin-template@21.1.0': - resolution: {integrity: sha512-FlbRfOCn8IUHvP1ebcCSQFVNh+4X/HqZqL7SW5oj9WIYPiOX9ijS03ndNbfX/pBPSIi8GHLKMjLt8zIy1l5Lww==} + '@angular-eslint/eslint-plugin-template@22.0.0': + resolution: {integrity: sha512-y6XL5HJ8C31NpBvkVHpU3bWc+Rk9g1zRtHrs39omhuT29eEUcS3zu47HMFV6tf8rHOI97B2Mstg6qYS5XL9ATg==} peerDependencies: - '@angular-eslint/template-parser': 21.1.0 - '@typescript-eslint/types': ^7.11.0 || ^8.0.0 - '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 + '@angular-eslint/template-parser': 22.0.0 + '@typescript-eslint/types': ^8.0.0 + '@typescript-eslint/utils': ^8.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular-eslint/eslint-plugin@21.1.0': - resolution: {integrity: sha512-oNp+4UzN2M3KwGwEw03NUdXz93vqJd9sMzTbGXWF9+KVfA2LjckGDTrI6g6asGcJMdyTo07rDcnw0m0MkLB5VA==} + '@angular-eslint/eslint-plugin@22.0.0': + resolution: {integrity: sha512-mKLScPZhqG64ic0KIQoxqSqCdkPwtEZuTOuunvc9lYTw05MJSHRUM2yVFODlCGq97c6BN1F6KBk2I+a+KFnr1g==} peerDependencies: - '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 + '@typescript-eslint/utils': ^8.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular-eslint/schematics@21.1.0': - resolution: {integrity: sha512-Hal1mYwx4MTjCcNHqfIlua31xrk2tZJoyTiXiGQ21cAeK4sFuY+9V7/8cxbwJMGftX0G4J7uhx8woOdIFuqiZw==} + '@angular-eslint/schematics@22.0.0': + resolution: {integrity: sha512-gsJQx6c+WIWC5d+NAqn4rRdUzwhinUCTNmCM9x4wygV9DrbAfVG+6OFPEbaDMryNvf0HYDcnGclbIbXjukGCaw==} peerDependencies: - '@angular/cli': '>= 21.0.0 < 22.0.0' + '@angular/cli': '>= 22.0.0 < 23.0.0' - '@angular-eslint/template-parser@21.1.0': - resolution: {integrity: sha512-PYVgNbjNtuD5/QOuS6cHR8A7bRqsVqxtUUXGqdv76FYMAajQcAvyfR0QxOkqf3NmYxgNgO3hlUHWq0ILjVbcow==} + '@angular-eslint/template-parser@22.0.0': + resolution: {integrity: sha512-jU5MKQ24bBB4J99gSSexmUrLm2LvTJZCuCHhNTQ1LavWX4e1lrIxhm+6pJILOm6Cixf8jyNXnHMty6nljX8J+Q==} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular-eslint/utils@21.1.0': - resolution: {integrity: sha512-rWINgxGREu+NFUPCpAVsBGG8B4hfXxyswM0N5GbjykvsfB5W6PUix2Gsoh++iEsZPT+c9lvgXL5GbpwfanjOow==} + '@angular-eslint/utils@22.0.0': + resolution: {integrity: sha512-VFodMojghnPYm+B3U+HRYrqebPMj8NyobNjVzDdY8V5XIBW+4ivOSEINIz81G48rmm/NZKwj56+bJ88bVX4KIw==} peerDependencies: - '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 + '@typescript-eslint/utils': ^8.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular/animations@21.2.17': - resolution: {integrity: sha512-zOW8FFa9qfbVkZ5TulxDkl1C3+gEjWfAAD5Z2MycA6pjVJQlLYPiTAGq+flOQ3yZfTT0z6kd5rejQMXWI81Dvg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/animations@22.0.4': + resolution: {integrity: sha512-drb9ndon6J+t6Do72zdIRZWbmkaCovExXtvwY5f/4dTlXcvLCTPtf//02wn0fn6zq9oUUWISu6MHU9gZ6Cqwwg==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} deprecated: '@angular/animations is deprecated. Use `animate.enter` and `animate.leave` instead. For more information see: https://v22.angular.dev/guide/animations.' peerDependencies: - '@angular/core': 21.2.17 + '@angular/core': 22.0.4 - '@angular/build@21.2.17': - resolution: {integrity: sha512-1tKQoRH8/21lf3Z1+ezaHmzdhAmeJx34wVW69DU+NuumsjDHDuq2YdUbiC936SnI33tDXov5UOBMqzjHe8qUTg==} - 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'} + '@angular/build@22.0.4': + resolution: {integrity: sha512-hst/KhP5mMPajY32l7qmyW/5fuqrMHCjHEeNhMMNqP82+j8jPBFfMEL26AH9w2kIIdKGpC3WKN5JgLotyFD7tQ==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} 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.2.17 + '@angular/compiler': ^22.0.0 + '@angular/compiler-cli': ^22.0.0 + '@angular/core': ^22.0.0 + '@angular/localize': ^22.0.0 + '@angular/platform-browser': ^22.0.0 + '@angular/platform-server': ^22.0.0 + '@angular/service-worker': ^22.0.0 + '@angular/ssr': ^22.0.4 + istanbul-lib-instrument: ^6.0.0 karma: ^6.4.0 less: ^4.2.0 - ng-packagr: ^21.0.0 + ng-packagr: ^22.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' + typescript: '>=6.0 <6.1' vitest: ^4.0.8 peerDependenciesMeta: '@angular/core': @@ -279,6 +280,8 @@ packages: optional: true '@angular/ssr': optional: true + istanbul-lib-instrument: + optional: true karma: optional: true less: @@ -292,38 +295,38 @@ packages: vitest: optional: true - '@angular/cli@21.2.17': - resolution: {integrity: sha512-wyEPOszxza7kUa1BWyERUqSDmAC/DF5Iun0YJ1sN22jDriOuOlYh9hXSk1EHs06fQ1JcTYLml+timHVHg4V1/w==} - 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'} + '@angular/cli@22.0.4': + resolution: {integrity: sha512-3eJy6VoNlgskKFzvqy3AJsYXFRhSBLLGObF2iTpJymsukuxWUen7hlVVVWrO5++bW1LEgd6PTCCD5fdT2UuRiA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular/common@21.2.17': - resolution: {integrity: sha512-hqAQxRfi5ldFE42suAXRcY+JCANrUh7fuSQ/DtZ7L896id5BT/exuv6dWNBC1PyAfQmRbpD5Pt6/pd+tNLyhDQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/common@22.0.4': + resolution: {integrity: sha512-ibHINcvajoXfKfb4aGdLx8aOFkUOadAYF9/Yy4j2C1gQdLS6uXOCPFD9+W9zWf8OeMhL0H+i+5KeNlh6XbGaZw==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/core': 21.2.17 + '@angular/core': 22.0.4 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@21.2.17': - resolution: {integrity: sha512-KithZ3b0HBFH0NbUcswBcjpN9y09vLbarMD7qmGWTnGUBk4W8cn4sbT8zJyv9CRKg9ZcuUBeJYKUfUPn/u/5OQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/compiler-cli@22.0.4': + resolution: {integrity: sha512-UmZRpw3pStRmH3ZOLg3BfyGaTxu/aqL00U1eoE3yfsUea25zJLU+gC4vx3NYtHzYKQu84wX3gTZXhLuuYYTa8w==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.17 - typescript: '>=5.9 <6.1' + '@angular/compiler': 22.0.4 + typescript: '>=6.0 <6.1' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@21.2.17': - resolution: {integrity: sha512-p+NdjYiwAz9Zmu2yul0LlMXaFjMISVVa24+/MVMoKFeQeI82QE8jDywPlnOSHQHvdCcQVpS7saeEriZzX3JuBQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/compiler@22.0.4': + resolution: {integrity: sha512-PvbsPr3d0OHm90vgq1v+0oPP/oHNuCcZQAhhD3fhHlx3DTxf6nkjUqBulzdJ6d5QwDsy378pwf8+dpGquFzbGQ==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} - '@angular/core@21.2.17': - resolution: {integrity: sha512-wYHpwIdnUnjQFOJJNqRcGx7LS3u64jT+R9L0TnMR/ViBM9dQgGYImlSikkftg2yrFCNo5aKRxhG2LLskQurVdg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/core@22.0.4': + resolution: {integrity: sha512-QTGwZcnc6dBhGPMID8DvwRQdHMvpqV0vXDX8jSdmVDiWDsFUjbe9AUFTHEizDm+wnmxOrCQ0DHkgmHICuh0GpA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/compiler': 21.2.17 + '@angular/compiler': 22.0.4 rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 || ~0.16.0 peerDependenciesMeta: @@ -332,50 +335,50 @@ packages: zone.js: optional: true - '@angular/forms@21.2.17': - resolution: {integrity: sha512-WKu8XeRSNZo+a+aDDZ3M5OtReF7KYqR/PmZ2l1lSf6N5EEAmc+Ky4aqbRhTL/mTSfHrO4+TDJ4C5A2tFmuwIeA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/forms@22.0.4': + resolution: {integrity: sha512-MId7Dcxl2/i/ztvf3sJggFBNOqR+btHfolEMRruqdD+o9b+eq/U3Yu0+4FQls1yJ84Lk75C5H6sN1Pc7kzNVgw==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/common': 21.2.17 - '@angular/core': 21.2.17 - '@angular/platform-browser': 21.2.17 + '@angular/common': 22.0.4 + '@angular/core': 22.0.4 + '@angular/platform-browser': 22.0.4 rxjs: ^6.5.3 || ^7.4.0 - '@angular/localize@21.2.17': - resolution: {integrity: sha512-CkiSHyag6W1cc2n8Y3sC4eYSqTqRslORU7DJnelEoJvh/2KM6/BmxxfjDlL6JcZURiiTPgxNjRgpb9zREj3elg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/localize@22.0.4': + resolution: {integrity: sha512-BJELtGDcTqPjNn10pUa5Ly0Zv+yOyhNOhMl7OA7izRwgK0bWCqgTRnCFh9OWuvn+KyTLfsd9HKVxSDTmtJYEJQ==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.17 - '@angular/compiler-cli': 21.2.17 + '@angular/compiler': 22.0.4 + '@angular/compiler-cli': 22.0.4 - '@angular/platform-browser-dynamic@21.2.17': - resolution: {integrity: sha512-r/BU/T8bOTghP3fIXhzYf5wcMcAmhWnAFv3p4asCCPXomaktoas70wYcMaDH+pK1LAFBxLwzBWHm36MpFlTMFg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/platform-browser-dynamic@22.0.4': + resolution: {integrity: sha512-AzXItNWzxLwKw/xST4TsB/HlPPe1i7OXvs+dGud3SYBlEMRiJFoz0BBQHNLM4aHmDgrTJFwSpzboG/Nub/ckFg==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} deprecated: '@angular/platform-browser-dynamic is deprecated. Use `@angular/platform-browser` instead.' peerDependencies: - '@angular/common': 21.2.17 - '@angular/compiler': 21.2.17 - '@angular/core': 21.2.17 - '@angular/platform-browser': 21.2.17 + '@angular/common': 22.0.4 + '@angular/compiler': 22.0.4 + '@angular/core': 22.0.4 + '@angular/platform-browser': 22.0.4 - '@angular/platform-browser@21.2.17': - resolution: {integrity: sha512-ROdSliejY37g1EphYmweYdm5cHM8HY3X4tbWt4ubxmhTyYgfN3nxrxfGQ/n7Mz5tDY9VXVLIGDgjLOGYOo4uTQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/platform-browser@22.0.4': + resolution: {integrity: sha512-lkl8ZIydRsMKb5PMIpVizKmOWzk7fQRmjQMjvLIW733M2OgpEpfYMohzsBVop6oaRImG6Row5uBBSu1j4adGzg==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/animations': 21.2.17 - '@angular/common': 21.2.17 - '@angular/core': 21.2.17 + '@angular/animations': 22.0.4 + '@angular/common': 22.0.4 + '@angular/core': 22.0.4 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/service-worker@21.2.17': - resolution: {integrity: sha512-6/uKwxBA3udngVHuIVqD8kdMV1whfym9ESB1UyjoNINx+2zj7A749X89tiV0TW2CvWpUYUE7VxpoapEiv5lvfw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/service-worker@22.0.4': + resolution: {integrity: sha512-b4nkSvGci/SqnJdl7ga81fBlOfQcK2e/FBExoJbcQeu4YO1fxbd7OZiFe3T1NWPjwVk6fe0GIfVB9oaUCUwSQw==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/core': 21.2.17 + '@angular/core': 22.0.4 rxjs: ^6.5.3 || ^7.4.0 '@asamuzakjp/css-color@4.1.2': @@ -502,15 +505,6 @@ packages: resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} - '@emnapi/core@1.10.0': - resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - - '@emnapi/runtime@1.10.0': - resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} - - '@emnapi/wasi-threads@1.2.1': - resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@esbuild/aix-ppc64@0.27.7': resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} @@ -928,134 +922,143 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@inquirer/ansi@1.0.2': - resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} - engines: {node: '>=18'} + '@inquirer/ansi@2.0.7': + resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - '@inquirer/checkbox@4.3.2': - resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} - engines: {node: '>=18'} + '@inquirer/checkbox@5.2.1': + resolution: {integrity: sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/confirm@5.1.21': - resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} - engines: {node: '>=18'} + '@inquirer/confirm@6.0.12': + resolution: {integrity: sha512-h9FgGun3QwVYNj5TWIZZ+slii73bMoBFjPfVIGtnFuL4t8gBiNDV9PcSfIzkuxvgquJKt9nr1QzszpBzTbH8Og==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/core@10.3.2': - resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} - engines: {node: '>=18'} + '@inquirer/confirm@6.1.1': + resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/editor@4.2.23': - resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} - engines: {node: '>=18'} + '@inquirer/core@11.2.1': + resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/expand@4.0.23': - resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} - engines: {node: '>=18'} + '@inquirer/editor@5.2.2': + resolution: {integrity: sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/external-editor@1.0.3': - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} - engines: {node: '>=18'} + '@inquirer/expand@5.1.1': + resolution: {integrity: sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/figures@1.0.15': - resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} - engines: {node: '>=18'} - - '@inquirer/input@4.3.1': - resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} - engines: {node: '>=18'} + '@inquirer/external-editor@3.0.3': + resolution: {integrity: sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/number@3.0.23': - resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} - engines: {node: '>=18'} + '@inquirer/figures@2.0.7': + resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/input@5.1.2': + resolution: {integrity: sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/password@4.0.23': - resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} - engines: {node: '>=18'} + '@inquirer/number@4.1.1': + resolution: {integrity: sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/prompts@7.10.1': - resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==} - engines: {node: '>=18'} + '@inquirer/password@5.1.1': + resolution: {integrity: sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/rawlist@4.1.11': - resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} - engines: {node: '>=18'} + '@inquirer/prompts@8.4.2': + resolution: {integrity: sha512-XJmn/wY4AX56l1BRU+ZjDrFtg9+2uBEi4JvJQj82kwJDQKiPgSn4CEsbfGGygS4Gw6rkL4W18oATjfVfaqub2Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/search@3.2.2': - resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} - engines: {node: '>=18'} + '@inquirer/rawlist@5.3.1': + resolution: {integrity: sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/select@4.4.2': - resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} - engines: {node: '>=18'} + '@inquirer/search@4.2.1': + resolution: {integrity: sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/type@3.0.10': - resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} - engines: {node: '>=18'} + '@inquirer/select@5.2.1': + resolution: {integrity: sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@4.0.7': + resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -1086,50 +1089,50 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@listr2/prompt-adapter-inquirer@3.0.5': - resolution: {integrity: sha512-WELs+hj6xcilkloBXYf9XXK8tYEnKsgLj01Xl5ONUJpKjmT5hGVUzNUS5tooUxs7pGMrw+jFD/41WpqW4V3LDA==} - engines: {node: '>=20.0.0'} + '@listr2/prompt-adapter-inquirer@4.2.3': + resolution: {integrity: sha512-Co9U3AJ3LW0J8XBHjVoNKA79dMAyFt8EZH3OaKTMcDTj8r+6kG3vSUPq/eGLHT7P0iK3uLaFfhdFYd3033P24g==} + engines: {node: '>=22.13.0'} peerDependencies: - '@inquirer/prompts': '>= 3 < 8' - listr2: 9.0.5 + '@inquirer/prompts': '>= 3 < 9' + listr2: 10.2.1 - '@lmdb/lmdb-darwin-arm64@3.5.1': - resolution: {integrity: sha512-tpfN4kKrrMpQ+If1l8bhmoNkECJi0iOu6AEdrTJvWVC+32sLxTARX5Rsu579mPImRP9YFWfWgeRQ5oav7zApQQ==} + '@lmdb/lmdb-darwin-arm64@3.5.4': + resolution: {integrity: sha512-Kk4Kz3iyu1QiLsLZBS9Af1eSKUC8VR2T+/jyE2iAyuGw2VwK08pp5iTbZnXn6sWu0LogO/RFktMxOjiDA2sS3w==} cpu: [arm64] os: [darwin] - '@lmdb/lmdb-darwin-x64@3.5.1': - resolution: {integrity: sha512-+a2tTfc3rmWhLAolFUWRgJtpSuu+Fw/yjn4rF406NMxhfjbMuiOUTDRvRlMFV+DzyjkwnokisskHbCWkS3Ly5w==} + '@lmdb/lmdb-darwin-x64@3.5.4': + resolution: {integrity: sha512-BEe5Rp3trn26oxoXOVL5HVDoiYmjUDwr8NRPkBOdUdCSBEorKI+7JrZLRKAdxO+G6cGQLgseXk0gR7qIQa7aGw==} cpu: [x64] os: [darwin] - '@lmdb/lmdb-linux-arm64@3.5.1': - resolution: {integrity: sha512-aoERa5B6ywXdyFeYGQ1gbQpkMkDbEo45qVoXE5QpIRavqjnyPwjOulMkmkypkmsbJ5z4Wi0TBztON8agCTG0Vg==} + '@lmdb/lmdb-linux-arm64@3.5.4': + resolution: {integrity: sha512-cUXEengO8o60v1SWerJTH4/RH4U3+9jC0/4njp2Z9NdmvaGzhKsbRM2wpXuRYrN8tytsoJCg0SvWEWwHAwLbCA==} cpu: [arm64] os: [linux] - '@lmdb/lmdb-linux-arm@3.5.1': - resolution: {integrity: sha512-0EgcE6reYr8InjD7V37EgXcYrloqpxVPINy3ig1MwDSbl6LF/vXTYRH9OE1Ti1D8YZnB35ZH9aTcdfSb5lql2A==} + '@lmdb/lmdb-linux-arm@3.5.4': + resolution: {integrity: sha512-SGbFR7816uBcTHc2ZY4S6WyOkl9bICnzqTQd2Mv4V/j24cfds88xx2nC6cm/y8zGQL7Ds31YF/5NGxjgcdM5Hw==} cpu: [arm] os: [linux] - '@lmdb/lmdb-linux-x64@3.5.1': - resolution: {integrity: sha512-SqNDY1+vpji7bh0sFH5wlWyFTOzjbDOl0/kB5RLLYDAFyd/uw3n7wyrmas3rYPpAW7z18lMOi1yKlTPv967E3g==} + '@lmdb/lmdb-linux-x64@3.5.4': + resolution: {integrity: sha512-Gxq8jpgOWXwd0PUR+c9R2Ik1/uBnGd5GMIIzRRDqABCkvmjtC3KWcyhesV9jSPCz759isl0NlbsstZ2oyvk8lA==} cpu: [x64] os: [linux] - '@lmdb/lmdb-win32-arm64@3.5.1': - resolution: {integrity: sha512-50v0O1Lt37cwrmR9vWZK5hRW0Aw+KEmxJJ75fge/zIYdvNKB/0bSMSVR5Uc2OV9JhosIUyklOmrEvavwNJ8D6w==} + '@lmdb/lmdb-win32-arm64@3.5.4': + resolution: {integrity: sha512-pKv1DJ1bPZAaHkdFsSz5IDfUG8x9vntgquXF9/Dm2xuupcIe/EkLzylpoBxppFVK5vzbV561Dq26jNY2fIMA7g==} cpu: [arm64] os: [win32] - '@lmdb/lmdb-win32-x64@3.5.1': - resolution: {integrity: sha512-qwosvPyl+zpUlp3gRb7UcJ3H8S28XHCzkv0Y0EgQToXjQP91ZD67EHSCDmaLjtKhe+GVIW5om1KUpzVLA0l6pg==} + '@lmdb/lmdb-win32-x64@3.5.4': + resolution: {integrity: sha512-JF1BmLCm9kGEVZgYmJq43zeQVdHVgAJnTi/NURWEsy6L1ZrrlSmdltS+D17QN4LODwf+1LMXAA9auIZVXtWwzw==} cpu: [x64] os: [win32] - '@modelcontextprotocol/sdk@1.26.0': - resolution: {integrity: sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==} + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -1281,41 +1284,23 @@ packages: resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} engines: {node: '>= 10'} - '@napi-rs/wasm-runtime@1.1.6': - resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + '@ng-bootstrap/ng-bootstrap@21.0.0': + resolution: {integrity: sha512-H7lGYGQGERoX5PeU1nf99U8tvPBJwQod6G99NoaJYwhBtKDoA/W0auUJqcbPMOGnO3gHtjtnOCji49tRbUIbPw==} peerDependencies: - '@emnapi/core': ^1.7.1 - '@emnapi/runtime': ^1.7.1 - - '@ng-bootstrap/ng-bootstrap@20.0.0': - resolution: {integrity: sha512-Jt+GUQ0PdM8VsOUUVr7vTQXhwcGwe2DCe1mmfS21vz9pLSOtGRz41ohZKc1egUevj5Rxm2sHVq5Sve68/nTMfA==} - peerDependencies: - '@angular/common': ^21.0.0 - '@angular/core': ^21.0.0 - '@angular/forms': ^21.0.0 - '@angular/localize': ^21.0.0 + '@angular/common': ^22.0.0 + '@angular/core': ^22.0.0 + '@angular/forms': ^22.0.0 + '@angular/localize': ^22.0.0 '@popperjs/core': ^2.11.8 rxjs: ^6.5.3 || ^7.4.0 - '@ng-select/ng-select@21.8.2': - resolution: {integrity: sha512-Gk5xlVKLOG3WTIgOteduwlKBZTt0njkoUXheKgUSKn+h+poYQAf61uENCK08Tj0XWxes9BQUc5J+R2GC+86jgA==} - engines: {node: ^20.19.0 || ^22.12.0 || ^24.0.0} + '@ng-select/ng-select@23.2.0': + resolution: {integrity: sha512-AM1/60UnZmowGvehrr4f8Ff0PJlK5doQh1LH6rTche+DhquaSkG2WBEWFfyK0ujIXNdjeWx+9yf2RcFSZWZ+Wg==} + engines: {node: ^22.12.0 || ^24.0.0 || ^26.0.0} peerDependencies: - '@angular/common': ^21.0.0 - '@angular/core': ^21.0.0 - '@angular/forms': ^21.0.0 - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@angular/common': ^22.0.0 + '@angular/core': ^22.0.0 + '@angular/forms': ^22.0.0 '@npmcli/agent@4.0.2': resolution: {integrity: sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg==} @@ -1354,9 +1339,6 @@ packages: resolution: {integrity: sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==} engines: {node: ^20.17.0 || >=22.9.0} - '@oxc-project/types@0.113.0': - resolution: {integrity: sha512-Tp3XmgxwNQ9pEN9vxgJBAqdRamHibi76iowQ38O2I4PMpcvNRQNVsU2n1x1nv9yh0XoTrGFzf7cZSGxmixxrhA==} - '@parcel/watcher-android-arm64@2.5.6': resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} engines: {node: '>= 10.0.0'} @@ -1448,231 +1430,285 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - '@rolldown/binding-android-arm64@1.0.0-rc.4': - resolution: {integrity: sha512-vRq9f4NzvbdZavhQbjkJBx7rRebDKYR9zHfO/Wg486+I7bSecdUapzCm5cyXoK+LHokTxgSq7A5baAXUZkIz0w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - - '@rolldown/binding-darwin-arm64@1.0.0-rc.4': - resolution: {integrity: sha512-kFgEvkWLqt3YCgKB5re9RlIrx9bRsvyVUnaTakEpOPuLGzLpLapYxE9BufJNvPg8GjT6mB1alN4yN1NjzoeM8Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - - '@rolldown/binding-darwin-x64@1.0.0-rc.4': - resolution: {integrity: sha512-JXmaOJGsL/+rsmMfutcDjxWM2fTaVgCHGoXS7nE8Z3c9NAYjGqHvXrAhMUZvMpHS/k7Mg+X7n/MVKb7NYWKKww==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - - '@rolldown/binding-freebsd-x64@1.0.0-rc.4': - resolution: {integrity: sha512-ep3Catd6sPnHTM0P4hNEvIv5arnDvk01PfyJIJ+J3wVCG1eEaPo09tvFqdtcaTrkwQy0VWR24uz+cb4IsK53Qw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.4': - resolution: {integrity: sha512-LwA5ayKIpnsgXJEwWc3h8wPiS33NMIHd9BhsV92T8VetVAbGe2qXlJwNVDGHN5cOQ22R9uYvbrQir2AB+ntT2w==} - engines: {node: ^20.19.0 || >=22.12.0} + '@rollup/rollup-android-arm-eabi@4.60.2': + resolution: {integrity: sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==} cpu: [arm] - os: [linux] - - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.4': - resolution: {integrity: sha512-AC1WsGdlV1MtGay/OQ4J9T7GRadVnpYRzTcygV1hKnypbYN20Yh4t6O1Sa2qRBMqv1etulUknqXjc3CTIsBu6A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.4': - resolution: {integrity: sha512-lU+6rgXXViO61B4EudxtVMXSOfiZONR29Sys5VGSetUY7X8mg9FCKIIjcPPj8xNDeYzKl+H8F/qSKOBVFJChCQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.4': - resolution: {integrity: sha512-DZaN1f0PGp/bSvKhtw50pPsnln4T13ycDq1FrDWRiHmWt1JeW+UtYg9touPFf8yt993p8tS2QjybpzKNTxYEwg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-x64-musl@1.0.0-rc.4': - resolution: {integrity: sha512-RnGxwZLN7fhMMAItnD6dZ7lvy+TI7ba+2V54UF4dhaWa/p8I/ys1E73KO6HmPmgz92ZkfD8TXS1IMV8+uhbR9g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rolldown/binding-openharmony-arm64@1.0.0-rc.4': - resolution: {integrity: sha512-6lcI79+X8klGiGd8yHuTgQRjuuJYNggmEml+RsyN596P23l/zf9FVmJ7K0KVKkFAeYEdg0iMUKyIxiV5vebDNQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - - '@rolldown/binding-wasm32-wasi@1.0.0-rc.4': - resolution: {integrity: sha512-wz7ohsKCAIWy91blZ/1FlpPdqrsm1xpcEOQVveWoL6+aSPKL4VUcoYmmzuLTssyZxRpEwzuIxL/GDsvpjaBtOw==} - engines: {node: '>=14.0.0'} - cpu: [wasm32] - - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.4': - resolution: {integrity: sha512-cfiMrfuWCIgsFmcVG0IPuO6qTRHvF7NuG3wngX1RZzc6dU8FuBFb+J3MIR5WrdTNozlumfgL4cvz+R4ozBCvsQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.4': - resolution: {integrity: sha512-p6UeR9y7ht82AH57qwGuFYn69S6CZ7LLKdCKy/8T3zS9VTrJei2/CGsTUV45Da4Z9Rbhc7G4gyWQ/Ioamqn09g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - - '@rolldown/pluginutils@1.0.0-rc.4': - resolution: {integrity: sha512-1BrrmTu0TWfOP1riA8uakjFc9bpIUGzVKETsOtzY39pPga8zELGDl8eu1Dx7/gjM5CAz14UknsUMpBO8L+YntQ==} + os: [android] '@rollup/rollup-android-arm-eabi@4.62.2': resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} cpu: [arm] os: [android] + '@rollup/rollup-android-arm64@4.60.2': + resolution: {integrity: sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==} + cpu: [arm64] + os: [android] + '@rollup/rollup-android-arm64@4.62.2': resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} cpu: [arm64] os: [android] + '@rollup/rollup-darwin-arm64@4.60.2': + resolution: {integrity: sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-arm64@4.62.2': resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-x64@4.60.2': + resolution: {integrity: sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.62.2': resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} cpu: [x64] os: [darwin] + '@rollup/rollup-freebsd-arm64@4.60.2': + resolution: {integrity: sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.62.2': resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.60.2': + resolution: {integrity: sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.62.2': resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} cpu: [x64] os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.60.2': + resolution: {integrity: sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==} + cpu: [arm] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} cpu: [arm] os: [linux] libc: [glibc] + '@rollup/rollup-linux-arm-musleabihf@4.60.2': + resolution: {integrity: sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==} + cpu: [arm] + os: [linux] + libc: [musl] + '@rollup/rollup-linux-arm-musleabihf@4.62.2': resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} cpu: [arm] os: [linux] libc: [musl] + '@rollup/rollup-linux-arm64-gnu@4.60.2': + resolution: {integrity: sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-arm64-gnu@4.62.2': resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} cpu: [arm64] os: [linux] libc: [glibc] + '@rollup/rollup-linux-arm64-musl@4.60.2': + resolution: {integrity: sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==} + cpu: [arm64] + os: [linux] + libc: [musl] + '@rollup/rollup-linux-arm64-musl@4.62.2': resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} cpu: [arm64] os: [linux] libc: [musl] + '@rollup/rollup-linux-loong64-gnu@4.60.2': + resolution: {integrity: sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==} + cpu: [loong64] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-loong64-gnu@4.62.2': resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} cpu: [loong64] os: [linux] libc: [glibc] + '@rollup/rollup-linux-loong64-musl@4.60.2': + resolution: {integrity: sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==} + cpu: [loong64] + os: [linux] + libc: [musl] + '@rollup/rollup-linux-loong64-musl@4.62.2': resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} cpu: [loong64] os: [linux] libc: [musl] + '@rollup/rollup-linux-ppc64-gnu@4.60.2': + resolution: {integrity: sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-ppc64-gnu@4.62.2': resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} cpu: [ppc64] os: [linux] libc: [glibc] + '@rollup/rollup-linux-ppc64-musl@4.60.2': + resolution: {integrity: sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==} + cpu: [ppc64] + os: [linux] + libc: [musl] + '@rollup/rollup-linux-ppc64-musl@4.62.2': resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} cpu: [ppc64] os: [linux] libc: [musl] + '@rollup/rollup-linux-riscv64-gnu@4.60.2': + resolution: {integrity: sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-riscv64-gnu@4.62.2': resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} cpu: [riscv64] os: [linux] libc: [glibc] + '@rollup/rollup-linux-riscv64-musl@4.60.2': + resolution: {integrity: sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==} + cpu: [riscv64] + os: [linux] + libc: [musl] + '@rollup/rollup-linux-riscv64-musl@4.62.2': resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} cpu: [riscv64] os: [linux] libc: [musl] + '@rollup/rollup-linux-s390x-gnu@4.60.2': + resolution: {integrity: sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-s390x-gnu@4.62.2': resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} cpu: [s390x] os: [linux] libc: [glibc] + '@rollup/rollup-linux-x64-gnu@4.60.2': + resolution: {integrity: sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==} + cpu: [x64] + os: [linux] + libc: [glibc] + '@rollup/rollup-linux-x64-gnu@4.62.2': resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} cpu: [x64] os: [linux] libc: [glibc] + '@rollup/rollup-linux-x64-musl@4.60.2': + resolution: {integrity: sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==} + cpu: [x64] + os: [linux] + libc: [musl] + '@rollup/rollup-linux-x64-musl@4.62.2': resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} cpu: [x64] os: [linux] libc: [musl] + '@rollup/rollup-openbsd-x64@4.60.2': + resolution: {integrity: sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==} + cpu: [x64] + os: [openbsd] + '@rollup/rollup-openbsd-x64@4.62.2': resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} cpu: [x64] os: [openbsd] + '@rollup/rollup-openharmony-arm64@4.60.2': + resolution: {integrity: sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==} + cpu: [arm64] + os: [openharmony] + '@rollup/rollup-openharmony-arm64@4.62.2': resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} cpu: [arm64] os: [openharmony] + '@rollup/rollup-win32-arm64-msvc@4.60.2': + resolution: {integrity: sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.62.2': resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.60.2': + resolution: {integrity: sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.62.2': resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-x64-gnu@4.60.2': + resolution: {integrity: sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==} + cpu: [x64] + os: [win32] + '@rollup/rollup-win32-x64-gnu@4.62.2': resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.60.2': + resolution: {integrity: sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==} + cpu: [x64] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.62.2': resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} cpu: [x64] os: [win32] - '@schematics/angular@21.2.17': - resolution: {integrity: sha512-rKHz2//1S3j4MKfsRDtJjnNoCfqSj2dNJDN27pLT9h4oLfBs2wJinfRTQywo2fVGdUcDd+mXBnltqdLJlcoFNw==} - 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'} + '@schematics/angular@22.0.4': + resolution: {integrity: sha512-P3V3tkqIR+n0GJSv0ibf34/zMtKbFp6kaTjBe5cm/RyXuHbmdaPYjk8PNphkGMypDtWCof1RtNnW/hl832Wnew==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@sigstore/bundle@4.0.0': resolution: {integrity: sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==} @@ -1712,9 +1748,6 @@ packages: resolution: {integrity: sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==} engines: {node: ^20.17.0 || >=22.9.0} - '@tybys/wasm-util@0.10.3': - resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} - '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -1739,6 +1772,9 @@ packages: '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} @@ -1751,26 +1787,20 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@typescript-eslint/eslint-plugin@8.47.0': - resolution: {integrity: sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==} + '@typescript-eslint/eslint-plugin@8.62.0': + resolution: {integrity: sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.47.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' + '@typescript-eslint/parser': ^8.62.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.47.0': - resolution: {integrity: sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==} + '@typescript-eslint/parser@8.62.0': + resolution: {integrity: sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/project-service@8.47.0': - resolution: {integrity: sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/project-service@8.62.0': resolution: {integrity: sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==} @@ -1778,60 +1808,33 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.47.0': - resolution: {integrity: sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.62.0': resolution: {integrity: sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.47.0': - resolution: {integrity: sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/tsconfig-utils@8.62.0': resolution: {integrity: sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.47.0': - resolution: {integrity: sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==} + '@typescript-eslint/type-utils@8.62.0': + resolution: {integrity: sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/types@8.47.0': - resolution: {integrity: sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/types@8.62.0': resolution: {integrity: sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.47.0': - resolution: {integrity: sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/typescript-estree@8.62.0': resolution: {integrity: sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.47.0': - resolution: {integrity: sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.62.0': resolution: {integrity: sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1839,19 +1842,15 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.47.0': - resolution: {integrity: sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.62.0': resolution: {integrity: sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@vitejs/plugin-basic-ssl@2.1.4': - resolution: {integrity: sha512-HXciTXN/sDBYWgeAD4V4s0DN0g72x5mlxQhHxtYu3Tt8BLa6MzcJZUyDVFCdtjNs3bfENVHVzOsmooTVuNgAAw==} + '@vitejs/plugin-basic-ssl@2.3.0': + resolution: {integrity: sha512-bdyo8rB3NnQbikdMpHaML9Z1OZPBu6fFOBo+OtxsBlvMJtysWskmBcnbIDhUqgC8tcxNv/a+BcV5U+2nQMm1OQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} peerDependencies: - vite: ^6.0.0 || ^7.0.0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 '@vitest/expect@4.1.9': resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} @@ -1911,6 +1910,10 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + agent-base@9.0.0: + resolution: {integrity: sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==} + engines: {node: '>= 20'} + ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} peerDependencies: @@ -1922,21 +1925,18 @@ packages: ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} - ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} - ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} - algoliasearch@5.48.1: - resolution: {integrity: sha512-Rf7xmeuIo7nb6S4mp4abW2faW8DauZyE2faBIKFaUfP3wnpOvNSbiI5AwVhqBNj0jPgBWEvhyCu0sLjN2q77Rg==} + algoliasearch@5.52.0: + resolution: {integrity: sha512-0ZzY9mjqV7gop/AH8pIBiAS8giXP7WcSiUfoFYIzYAK9QC5c37E4SIVtJVBMwlURc0/uNt2o4RcNRvdHa4CJ5w==} engines: {node: '>= 14.0.0'} - angular-eslint@21.1.0: - resolution: {integrity: sha512-qXpIEBNYpfgpBaFblnyFegVSQjWCVUdCXTHvMcvtNtmMgtPwIDKvG8wuJo5BbQ/MNt2d8npmnRUaS2ddzdCzww==} + angular-eslint@22.0.0: + resolution: {integrity: sha512-6tHLndzM6rU+2iuICakJS/hD1scK5sWLkcD7828zStT1ViA9zX8z9g/V1IlBiKEdZeMsl+m7K2DlNc34AkYyoQ==} peerDependencies: - '@angular/cli': '>= 21.0.0 < 22.0.0' - eslint: ^8.57.0 || ^9.0.0 + '@angular/cli': '>= 22.0.0 < 23.0.0' + eslint: ^9.0.0 || ^10.0.0 typescript: '*' typescript-eslint: ^8.0.0 @@ -1944,10 +1944,6 @@ packages: resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} engines: {node: '>=18'} - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - ansi-regex@6.2.2: resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} @@ -1991,8 +1987,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - beasties@0.4.1: - resolution: {integrity: sha512-2Imdcw3LznDuxAbJM26RHniOLAzE6WgrK8OuvVXCQtNBS8rsnD9zsSEa3fHl4hHpUY7BYTlrpvtPVbvu9G6neg==} + beasties@0.4.2: + resolution: {integrity: sha512-NvcGjG/7AVUAfRbvrJmHunDQS9uHnE6Q/7AkaPr8oKE8HjOlpjRG5075z/th2Tmlezk3VlaaS8+X9I1RwHJMQw==} engines: {node: '>=18.0.0'} bidi-js@1.0.3: @@ -2013,17 +2009,10 @@ packages: brace-expansion@1.1.15: resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} - brace-expansion@2.1.1: - resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} - brace-expansion@5.0.6: resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - browserslist@4.28.4: resolution: {integrity: sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -2109,9 +2098,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -2220,9 +2206,6 @@ packages: emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} @@ -2242,10 +2225,6 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} - entities@6.0.1: - resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} - engines: {node: '>=0.12'} - entities@7.0.1: resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} engines: {node: '>=0.12'} @@ -2386,21 +2365,23 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + fast-uri@3.1.2: resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -2415,10 +2396,6 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - finalhandler@2.1.1: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} @@ -2474,10 +2451,6 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -2500,9 +2473,6 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -2545,6 +2515,10 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + https-proxy-agent@9.0.0: + resolution: {integrity: sha512-/MVmHp58WkOypgFhCLk4fzpPcFQvTJ/e6LBI7irpIO2HfxUbpmYoHF+KzipzJpxxzJu7aJNWQ0xojJ/dzV2G5g==} + engines: {node: '>= 20'} + iconv-lite@0.7.2: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} @@ -2591,10 +2565,6 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - is-fullwidth-code-point@5.1.0: resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} engines: {node: '>=18'} @@ -2607,10 +2577,6 @@ packages: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -2698,12 +2664,12 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - listr2@9.0.5: - resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} - engines: {node: '>=20.0.0'} + listr2@10.2.1: + resolution: {integrity: sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==} + engines: {node: '>=22.13.0'} - lmdb@3.5.1: - resolution: {integrity: sha512-NYHA0MRPjvNX+vSw8Xxg6FLKxzAG+e7Pt8RqAQA/EehzHVXq9SxDqJIN3JL1hK0dweb884y8kIh6rkWvPyg9Wg==} + lmdb@3.5.4: + resolution: {integrity: sha512-9FKQA6G1MMtqNxfxvSBNXD/axeG2QRjYbNh0/ykRL5xYcRbCm2vXq7B9bhc7nSuKdHzr8/BHIwfPuYYH1UsXXw==} hasBin: true locate-path@6.0.0: @@ -2750,14 +2716,6 @@ packages: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -2785,10 +2743,6 @@ packages: minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@9.0.9: - resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} - engines: {node: '>=16 || 14 >=14.17'} - minipass-collect@2.0.1: resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} engines: {node: '>=16 || 14 >=14.17'} @@ -2835,9 +2789,9 @@ packages: msgpackr@1.12.1: resolution: {integrity: sha512-4EUH9tQHnMmEgzW/MdAP0KIfa1T9AF+htl0ffe2n5vb2EKn9y2co8ccpgWko6S52Jy1PQZKwRnx5/KkYjtd9MQ==} - mute-stream@2.0.0: - resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} - engines: {node: ^18.17.0 || >=20.5.0} + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} nanoid@3.3.15: resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} @@ -2855,11 +2809,11 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} - ngx-cookie-service@21.3.1: - resolution: {integrity: sha512-8VEA2W7W2W3yPXhemJoVtXxr+3WW2DNLV4OaCIKDzLdzUUxJ6SzPHMmXXa26Pg8pa+fZxHK1hZfqJfUxr9RMBw==} + ngx-cookie-service@22.0.0: + resolution: {integrity: sha512-7V8am8MqTr7R1xCePfo3w+AfbunNQl+vaWPAeLtIruhsAqBXp0uunh4mYv+ikrYrdPg9pqBjckCX3ubNv8HX7w==} peerDependencies: - '@angular/common': ^21.0.0 - '@angular/core': ^21.0.0 + '@angular/common': ^22.0.0 + '@angular/core': ^22.0.0 ngx-socket-io@4.10.0: resolution: {integrity: sha512-FuOF/IX4mYyRHjfFi5osBgo8uN+/+zyRhYnAtuOBLOLpRo/uNS/6kggwLGKainOdieJs3vmreSP6lQEWRS4otw==} @@ -2950,8 +2904,8 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - ora@9.3.0: - resolution: {integrity: sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==} + ora@9.4.0: + resolution: {integrity: sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==} engines: {node: '>=20'} ordered-binary@1.6.1: @@ -2978,8 +2932,8 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} - parse5-html-rewriting-stream@8.0.0: - resolution: {integrity: sha512-wzh11mj8KKkno1pZEu+l2EVeWsuKDfR5KNWZOTsslfUX8lPDZx77m9T0kIoAVkFtD1nx6YF8oh4BnPHvxMtNMw==} + parse5-html-rewriting-stream@8.0.1: + resolution: {integrity: sha512-NaRku2aMpUN1Sh1Gyk1KWUh2A7EJx2c6qYzvwsPtqhoHoaURshdrceYK3LunVCm3WHhm6FS7Vcczbvdh3/UIVw==} parse5-sax-parser@8.0.0: resolution: {integrity: sha512-/dQ8UzHZwnrzs3EvDj6IkKrD/jIZyTlB+8XrHJvcjNgRdmWruNdN9i9RK/JtxakmlUdPwKubKPTCqvbTgzGhrw==} @@ -3012,10 +2966,6 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - picomatch@4.0.4: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} @@ -3061,9 +3011,6 @@ packages: resolution: {integrity: sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==} engines: {node: '>=0.6'} - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - range-parser@1.3.0: resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} engines: {node: '>= 0.6'} @@ -3095,16 +3042,12 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rolldown@1.0.0-rc.4: - resolution: {integrity: sha512-V2tPDUrY3WSevrvU2E41ijZlpF+5PbZu4giH+VpNraaadsJGHa4fR6IFwsocVwEXDoAdIv5qgPPxgrvKAOIPtA==} - engines: {node: ^20.19.0 || >=22.12.0} + rollup@4.60.2: + resolution: {integrity: sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true rollup@4.62.2: @@ -3116,17 +3059,14 @@ packages: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sass@1.97.3: - resolution: {integrity: sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==} + sass@1.99.0: + resolution: {integrity: sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==} engines: {node: '>=14.0.0'} hasBin: true @@ -3138,13 +3078,13 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} hasBin: true - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} engines: {node: '>=10'} hasBin: true @@ -3276,10 +3216,6 @@ packages: resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} engines: {node: '>=18'} - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - string-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} @@ -3288,10 +3224,6 @@ packages: resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} engines: {node: '>=20'} - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - strip-ansi@7.2.0: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} @@ -3318,8 +3250,8 @@ packages: resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} engines: {node: '>=18'} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} tinyglobby@0.2.17: @@ -3337,10 +3269,6 @@ packages: resolution: {integrity: sha512-kFXFK7O4WPextIUAOk8qtnw9dxR9UIXP9CjuH1cTBVBZMDeQcUPgr/IazGiw1B0Yiw5L75gHLWeW4iD793r90g==} hasBin: true - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} @@ -3374,15 +3302,15 @@ packages: resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} engines: {node: '>= 18'} - typescript-eslint@8.47.0: - resolution: {integrity: sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q==} + typescript-eslint@8.62.0: + resolution: {integrity: sha512-8QxXi+ZACKX0kaqO4gY8kn0RSD9gFfaHDWwjqtEN48aWCBkX4MJaufWN+c3BzlrXLOxfywDL8CaoqUwcRq4j4Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true @@ -3393,10 +3321,6 @@ packages: resolution: {integrity: sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==} engines: {node: '>=18.17'} - undici@7.24.4: - resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==} - engines: {node: '>=20.18.1'} - unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} @@ -3545,9 +3469,9 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} + wrap-ansi@10.0.0: + resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} + engines: {node: '>=20'} wrap-ansi@9.0.2: resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} @@ -3605,10 +3529,6 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yoctocolors-cjs@2.1.3: - resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} - engines: {node: '>=18'} - yoctocolors@2.1.2: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} @@ -3618,8 +3538,8 @@ packages: peerDependencies: zod: ^3.25.28 || ^4 - zod@4.3.6: - resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zod@4.4.2: + resolution: {integrity: sha512-IynmDyxsEsb9RKzO3J9+4SxXnl2FTFSzNBaKKaMV6tsSk0rw9gYw9gs+JFCq/qk2LCZ78KDwyj+Z289TijSkUw==} zone.js@0.15.0: resolution: {integrity: sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA==} @@ -3628,106 +3548,106 @@ snapshots: '@acemir/cssom@0.9.31': {} - '@algolia/abtesting@1.14.1': + '@algolia/abtesting@1.18.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/client-abtesting@5.48.1': + '@algolia/client-abtesting@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/client-analytics@5.48.1': + '@algolia/client-analytics@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/client-common@5.48.1': {} + '@algolia/client-common@5.52.0': {} - '@algolia/client-insights@5.48.1': + '@algolia/client-insights@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/client-personalization@5.48.1': + '@algolia/client-personalization@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/client-query-suggestions@5.48.1': + '@algolia/client-query-suggestions@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/client-search@5.48.1': + '@algolia/client-search@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/ingestion@1.48.1': + '@algolia/ingestion@1.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/monitoring@1.48.1': + '@algolia/monitoring@1.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/recommend@5.48.1': + '@algolia/recommend@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/requester-browser-xhr@5.48.1': + '@algolia/requester-browser-xhr@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 + '@algolia/client-common': 5.52.0 - '@algolia/requester-fetch@5.48.1': + '@algolia/requester-fetch@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 + '@algolia/client-common': 5.52.0 - '@algolia/requester-node-http@5.48.1': + '@algolia/requester-node-http@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 + '@algolia/client-common': 5.52.0 '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.2102.17(chokidar@5.0.0)': + '@angular-devkit/architect@0.2200.4(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.17(chokidar@5.0.0) + '@angular-devkit/core': 22.0.4(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/core@21.2.17(chokidar@5.0.0)': + '@angular-devkit/core@22.0.4(chokidar@5.0.0)': dependencies: - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) jsonc-parser: 3.3.1 picomatch: 4.0.4 rxjs: 7.8.2 @@ -3735,58 +3655,58 @@ snapshots: optionalDependencies: chokidar: 5.0.0 - '@angular-devkit/schematics@21.2.17(chokidar@5.0.0)': + '@angular-devkit/schematics@22.0.4(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.17(chokidar@5.0.0) + '@angular-devkit/core': 22.0.4(chokidar@5.0.0) jsonc-parser: 3.3.1 magic-string: 0.30.21 - ora: 9.3.0 + ora: 9.4.0 rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-eslint/builder@21.1.0(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/builder@22.0.0(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3)': dependencies: - '@angular-devkit/architect': 0.2102.17(chokidar@5.0.0) - '@angular-devkit/core': 21.2.17(chokidar@5.0.0) - '@angular/cli': 21.2.17(@types/node@26.0.1)(chokidar@5.0.0) + '@angular-devkit/architect': 0.2200.4(chokidar@5.0.0) + '@angular-devkit/core': 22.0.4(chokidar@5.0.0) + '@angular/cli': 22.0.4(@types/node@26.0.1)(chokidar@5.0.0) eslint: 9.39.4 - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - chokidar - '@angular-eslint/bundled-angular-compiler@21.1.0': {} + '@angular-eslint/bundled-angular-compiler@22.0.0': {} - '@angular-eslint/eslint-plugin-template@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin-template@22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/bundled-angular-compiler': 22.0.0 + '@angular-eslint/template-parser': 22.0.0(eslint@9.39.4)(typescript@6.0.3) + '@angular-eslint/utils': 22.0.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) aria-query: 5.3.2 axobject-query: 4.1.0 eslint: 9.39.4 - typescript: 5.9.3 + typescript: 6.0.3 - '@angular-eslint/eslint-plugin@21.1.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/eslint-plugin@22.0.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@angular-eslint/utils': 21.1.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/bundled-angular-compiler': 22.0.0 + '@angular-eslint/utils': 22.0.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) eslint: 9.39.4 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 - '@angular-eslint/schematics@21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/schematics@22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3)': dependencies: - '@angular-devkit/core': 21.2.17(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.17(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.17(@types/node@26.0.1)(chokidar@5.0.0) + '@angular-devkit/core': 22.0.4(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.4(chokidar@5.0.0) + '@angular-eslint/eslint-plugin': 22.0.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) + '@angular-eslint/eslint-plugin-template': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) + '@angular/cli': 22.0.4(@types/node@26.0.1)(chokidar@5.0.0) ignore: 7.0.5 - semver: 7.7.3 + semver: 7.8.0 strip-json-comments: 3.1.1 transitivePeerDependencies: - '@angular-eslint/template-parser' @@ -3796,69 +3716,66 @@ snapshots: - eslint - typescript - '@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 21.1.0 + '@angular-eslint/bundled-angular-compiler': 22.0.0 eslint: 9.39.4 eslint-scope: 9.1.2 - typescript: 5.9.3 + typescript: 6.0.3 - '@angular-eslint/utils@21.1.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@angular-eslint/utils@22.0.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 21.1.0 - '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@5.9.3) + '@angular-eslint/bundled-angular-compiler': 22.0.0 + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) eslint: 9.39.4 - typescript: 5.9.3 + typescript: 6.0.3 - '@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - '@angular/build@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@26.0.1)(chokidar@5.0.0)(postcss@8.5.15)(tslib@2.8.1)(typescript@5.9.3)(vitest@4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3)))': + '@angular/build@22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4)(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@types/node@26.0.1)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(postcss@8.5.15)(tslib@2.8.1)(typescript@6.0.3)(vitest@4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0)))': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2102.17(chokidar@5.0.0) - '@angular/compiler': 21.2.17 - '@angular/compiler-cli': 21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3) - '@babel/core': 7.29.7 + '@angular-devkit/architect': 0.2200.4(chokidar@5.0.0) + '@angular/compiler': 22.0.4 + '@angular/compiler-cli': 22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3) + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@26.0.1) - '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3)) - beasties: 0.4.1 + '@inquirer/confirm': 6.0.12(@types/node@26.0.1) + '@vitejs/plugin-basic-ssl': 2.3.0(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0)) + beasties: 0.4.2 browserslist: 4.28.4 esbuild: 0.28.1 - https-proxy-agent: 7.0.6 - istanbul-lib-instrument: 6.0.3 + https-proxy-agent: 9.0.0 jsonc-parser: 3.3.1 - listr2: 9.0.5 + listr2: 10.2.1 magic-string: 0.30.21 mrmime: 2.0.1 - parse5-html-rewriting-stream: 8.0.0 + parse5-html-rewriting-stream: 8.0.1 picomatch: 4.0.4 piscina: 5.2.0 - rolldown: 1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - sass: 1.97.3 + rollup: 4.60.2 + sass: 1.99.0 semver: 7.7.4 source-map-support: 0.5.21 - tinyglobby: 0.2.15 + tinyglobby: 0.2.16 tslib: 2.8.1 - typescript: 5.9.3 - undici: 7.24.4 - vite: 7.3.5(@types/node@26.0.1)(sass@1.97.3) + typescript: 6.0.3 + vite: 7.3.5(@types/node@26.0.1)(sass@1.99.0) watchpack: 2.5.1 optionalDependencies: - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/localize': 21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17) - '@angular/platform-browser': 21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - lmdb: 3.5.1 + '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/localize': 22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4) + '@angular/platform-browser': 22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/service-worker': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + istanbul-lib-instrument: 6.0.3 + lmdb: 3.5.4 postcss: 8.5.15 - vitest: 4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3)) + vitest: 4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0)) transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - '@types/node' - chokidar - jiti @@ -3871,42 +3788,42 @@ snapshots: - tsx - yaml - '@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0)': + '@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2102.17(chokidar@5.0.0) - '@angular-devkit/core': 21.2.17(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.17(chokidar@5.0.0) - '@inquirer/prompts': 7.10.1(@types/node@26.0.1) - '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@26.0.1))(@types/node@26.0.1)(listr2@9.0.5) - '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) - '@schematics/angular': 21.2.17(chokidar@5.0.0) + '@angular-devkit/architect': 0.2200.4(chokidar@5.0.0) + '@angular-devkit/core': 22.0.4(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.4(chokidar@5.0.0) + '@inquirer/prompts': 8.4.2(@types/node@26.0.1) + '@listr2/prompt-adapter-inquirer': 4.2.3(@inquirer/prompts@8.4.2(@types/node@26.0.1))(@types/node@26.0.1)(listr2@10.2.1) + '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.2) + '@schematics/angular': 22.0.4(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 - algoliasearch: 5.48.1 + algoliasearch: 5.52.0 ini: 6.0.0 jsonc-parser: 3.3.1 - listr2: 9.0.5 + listr2: 10.2.1 npm-package-arg: 13.0.2 pacote: 21.5.1 - parse5-html-rewriting-stream: 8.0.0 + parse5-html-rewriting-stream: 8.0.1 semver: 7.7.4 yargs: 18.0.0 - zod: 4.3.6 + zod: 4.4.2 transitivePeerDependencies: - '@cfworker/json-schema' - '@types/node' - chokidar - supports-color - '@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3)': + '@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3)': dependencies: - '@angular/compiler': 21.2.17 - '@babel/core': 7.29.0 + '@angular/compiler': 22.0.4 + '@babel/core': 7.29.7 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 5.0.0 convert-source-map: 1.9.0 @@ -3915,61 +3832,62 @@ snapshots: tslib: 2.8.1 yargs: 18.0.0 optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@angular/compiler@21.2.17': + '@angular/compiler@22.0.4': dependencies: tslib: 2.8.1 - '@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)': + '@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 21.2.17 + '@angular/compiler': 22.0.4 zone.js: 0.15.0 - '@angular/forms@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': + '@angular/forms@22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) '@standard-schema/spec': 1.1.0 rxjs: 7.8.2 tslib: 2.8.1 + zod: 4.4.2 - '@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)': + '@angular/localize@22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4)': dependencies: - '@angular/compiler': 21.2.17 - '@angular/compiler-cli': 21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3) - '@babel/core': 7.29.0 + '@angular/compiler': 22.0.4 + '@angular/compiler-cli': 22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3) + '@babel/core': 7.29.7 '@types/babel__core': 7.20.5 tinyglobby: 0.2.17 yargs: 18.0.0 transitivePeerDependencies: - supports-color - '@angular/platform-browser-dynamic@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))': + '@angular/platform-browser-dynamic@22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@22.0.4)(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))': dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/compiler': 21.2.17 - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/compiler': 22.0.4 + '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) tslib: 2.8.1 - '@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 optionalDependencies: - '@angular/animations': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/animations': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/service-worker@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 @@ -4152,22 +4070,6 @@ snapshots: '@csstools/css-tokenizer@4.0.0': {} - '@emnapi/core@1.10.0': - dependencies: - '@emnapi/wasi-threads': 1.2.1 - tslib: 2.8.1 - optional: true - - '@emnapi/runtime@1.10.0': - dependencies: - tslib: 2.8.1 - optional: true - - '@emnapi/wasi-threads@1.2.1': - dependencies: - tslib: 2.8.1 - optional: true - '@esbuild/aix-ppc64@0.27.7': optional: true @@ -4372,9 +4274,9 @@ snapshots: '@exodus/bytes@1.15.1': {} - '@fortawesome/angular-fontawesome@4.0.0(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))': + '@fortawesome/angular-fontawesome@4.0.0(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) '@fortawesome/fontawesome-svg-core': 7.3.0 tslib: 2.8.1 @@ -4421,128 +4323,129 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@inquirer/ansi@1.0.2': {} + '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@4.3.2(@types/node@26.0.1)': + '@inquirer/checkbox@5.2.1(@types/node@26.0.1)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.0.1) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.0.1) - yoctocolors-cjs: 2.1.3 + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@26.0.1) optionalDependencies: '@types/node': 26.0.1 - '@inquirer/confirm@5.1.21(@types/node@26.0.1)': + '@inquirer/confirm@6.0.12(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.0.1) - '@inquirer/type': 3.0.10(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/type': 4.0.7(@types/node@26.0.1) optionalDependencies: '@types/node': 26.0.1 - '@inquirer/core@10.3.2(@types/node@26.0.1)': + '@inquirer/confirm@6.1.1(@types/node@26.0.1)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/type': 4.0.7(@types/node@26.0.1) + optionalDependencies: + '@types/node': 26.0.1 + + '@inquirer/core@11.2.1(@types/node@26.0.1)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@26.0.1) cli-width: 4.1.0 - mute-stream: 2.0.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 signal-exit: 4.1.0 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.3 optionalDependencies: '@types/node': 26.0.1 - '@inquirer/editor@4.2.23(@types/node@26.0.1)': + '@inquirer/editor@5.2.2(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.0.1) - '@inquirer/external-editor': 1.0.3(@types/node@26.0.1) - '@inquirer/type': 3.0.10(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/external-editor': 3.0.3(@types/node@26.0.1) + '@inquirer/type': 4.0.7(@types/node@26.0.1) optionalDependencies: '@types/node': 26.0.1 - '@inquirer/expand@4.0.23(@types/node@26.0.1)': + '@inquirer/expand@5.1.1(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.0.1) - '@inquirer/type': 3.0.10(@types/node@26.0.1) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/type': 4.0.7(@types/node@26.0.1) optionalDependencies: '@types/node': 26.0.1 - '@inquirer/external-editor@1.0.3(@types/node@26.0.1)': + '@inquirer/external-editor@3.0.3(@types/node@26.0.1)': dependencies: chardet: 2.2.0 iconv-lite: 0.7.2 optionalDependencies: '@types/node': 26.0.1 - '@inquirer/figures@1.0.15': {} + '@inquirer/figures@2.0.7': {} - '@inquirer/input@4.3.1(@types/node@26.0.1)': + '@inquirer/input@5.1.2(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.0.1) - '@inquirer/type': 3.0.10(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/type': 4.0.7(@types/node@26.0.1) optionalDependencies: '@types/node': 26.0.1 - '@inquirer/number@3.0.23(@types/node@26.0.1)': + '@inquirer/number@4.1.1(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.0.1) - '@inquirer/type': 3.0.10(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/type': 4.0.7(@types/node@26.0.1) optionalDependencies: '@types/node': 26.0.1 - '@inquirer/password@4.0.23(@types/node@26.0.1)': + '@inquirer/password@5.1.1(@types/node@26.0.1)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.0.1) - '@inquirer/type': 3.0.10(@types/node@26.0.1) + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/type': 4.0.7(@types/node@26.0.1) optionalDependencies: '@types/node': 26.0.1 - '@inquirer/prompts@7.10.1(@types/node@26.0.1)': + '@inquirer/prompts@8.4.2(@types/node@26.0.1)': dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@26.0.1) - '@inquirer/confirm': 5.1.21(@types/node@26.0.1) - '@inquirer/editor': 4.2.23(@types/node@26.0.1) - '@inquirer/expand': 4.0.23(@types/node@26.0.1) - '@inquirer/input': 4.3.1(@types/node@26.0.1) - '@inquirer/number': 3.0.23(@types/node@26.0.1) - '@inquirer/password': 4.0.23(@types/node@26.0.1) - '@inquirer/rawlist': 4.1.11(@types/node@26.0.1) - '@inquirer/search': 3.2.2(@types/node@26.0.1) - '@inquirer/select': 4.4.2(@types/node@26.0.1) + '@inquirer/checkbox': 5.2.1(@types/node@26.0.1) + '@inquirer/confirm': 6.1.1(@types/node@26.0.1) + '@inquirer/editor': 5.2.2(@types/node@26.0.1) + '@inquirer/expand': 5.1.1(@types/node@26.0.1) + '@inquirer/input': 5.1.2(@types/node@26.0.1) + '@inquirer/number': 4.1.1(@types/node@26.0.1) + '@inquirer/password': 5.1.1(@types/node@26.0.1) + '@inquirer/rawlist': 5.3.1(@types/node@26.0.1) + '@inquirer/search': 4.2.1(@types/node@26.0.1) + '@inquirer/select': 5.2.1(@types/node@26.0.1) optionalDependencies: '@types/node': 26.0.1 - '@inquirer/rawlist@4.1.11(@types/node@26.0.1)': + '@inquirer/rawlist@5.3.1(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.0.1) - '@inquirer/type': 3.0.10(@types/node@26.0.1) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/type': 4.0.7(@types/node@26.0.1) optionalDependencies: '@types/node': 26.0.1 - '@inquirer/search@3.2.2(@types/node@26.0.1)': + '@inquirer/search@4.2.1(@types/node@26.0.1)': dependencies: - '@inquirer/core': 10.3.2(@types/node@26.0.1) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.0.1) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@26.0.1) optionalDependencies: '@types/node': 26.0.1 - '@inquirer/select@4.4.2(@types/node@26.0.1)': + '@inquirer/select@5.2.1(@types/node@26.0.1)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@26.0.1) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@26.0.1) - yoctocolors-cjs: 2.1.3 + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@26.0.1) optionalDependencies: '@types/node': 26.0.1 - '@inquirer/type@3.0.10(@types/node@26.0.1)': + '@inquirer/type@4.0.7(@types/node@26.0.1)': optionalDependencies: '@types/node': 26.0.1 @@ -4550,7 +4453,8 @@ snapshots: dependencies: minipass: 7.1.3 - '@istanbuljs/schema@0.1.6': {} + '@istanbuljs/schema@0.1.6': + optional: true '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -4571,36 +4475,36 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@26.0.1))(@types/node@26.0.1)(listr2@9.0.5)': + '@listr2/prompt-adapter-inquirer@4.2.3(@inquirer/prompts@8.4.2(@types/node@26.0.1))(@types/node@26.0.1)(listr2@10.2.1)': dependencies: - '@inquirer/prompts': 7.10.1(@types/node@26.0.1) - '@inquirer/type': 3.0.10(@types/node@26.0.1) - listr2: 9.0.5 + '@inquirer/prompts': 8.4.2(@types/node@26.0.1) + '@inquirer/type': 4.0.7(@types/node@26.0.1) + listr2: 10.2.1 transitivePeerDependencies: - '@types/node' - '@lmdb/lmdb-darwin-arm64@3.5.1': + '@lmdb/lmdb-darwin-arm64@3.5.4': optional: true - '@lmdb/lmdb-darwin-x64@3.5.1': + '@lmdb/lmdb-darwin-x64@3.5.4': optional: true - '@lmdb/lmdb-linux-arm64@3.5.1': + '@lmdb/lmdb-linux-arm64@3.5.4': optional: true - '@lmdb/lmdb-linux-arm@3.5.1': + '@lmdb/lmdb-linux-arm@3.5.4': optional: true - '@lmdb/lmdb-linux-x64@3.5.1': + '@lmdb/lmdb-linux-x64@3.5.4': optional: true - '@lmdb/lmdb-win32-arm64@3.5.1': + '@lmdb/lmdb-win32-arm64@3.5.4': optional: true - '@lmdb/lmdb-win32-x64@3.5.1': + '@lmdb/lmdb-win32-x64@3.5.4': optional: true - '@modelcontextprotocol/sdk@1.26.0(zod@4.3.6)': + '@modelcontextprotocol/sdk@1.29.0(zod@4.4.2)': dependencies: '@hono/node-server': 1.19.14(hono@4.12.27) ajv: 8.20.0 @@ -4617,8 +4521,8 @@ snapshots: json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 - zod: 4.3.6 - zod-to-json-schema: 3.25.2(zod@4.3.6) + zod: 4.4.2 + zod-to-json-schema: 3.25.2(zod@4.4.2) transitivePeerDependencies: - supports-color @@ -4712,42 +4616,23 @@ snapshots: '@napi-rs/nice-win32-x64-msvc': 1.1.1 optional: true - '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@ng-bootstrap/ng-bootstrap@21.0.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4))(@popperjs/core@2.11.8)(rxjs@7.8.2)': dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@tybys/wasm-util': 0.10.3 - optional: true - - '@ng-bootstrap/ng-bootstrap@20.0.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@popperjs/core@2.11.8)(rxjs@7.8.2)': - dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) - '@angular/localize': 21.2.17(@angular/compiler-cli@21.2.17(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17) + '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/localize': 22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4) '@popperjs/core': 2.11.8 rxjs: 7.8.2 tslib: 2.8.1 - '@ng-select/ng-select@21.8.2(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': + '@ng-select/ng-select@23.2.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 21.2.17(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@21.2.17(@angular/animations@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) tslib: 2.8.1 - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 - '@npmcli/agent@4.0.2': dependencies: agent-base: 7.1.4 @@ -4760,7 +4645,7 @@ snapshots: '@npmcli/fs@5.0.0': dependencies: - semver: 7.7.4 + semver: 7.8.5 '@npmcli/git@7.0.2': dependencies: @@ -4770,7 +4655,7 @@ snapshots: lru-cache: 11.5.1 npm-pick-manifest: 11.0.3 proc-log: 6.1.0 - semver: 7.7.4 + semver: 7.8.5 which: 6.0.1 '@npmcli/installed-package-contents@4.0.0': @@ -4787,7 +4672,7 @@ snapshots: hosted-git-info: 9.0.3 json-parse-even-better-errors: 5.0.0 proc-log: 6.1.0 - semver: 7.7.4 + semver: 7.8.5 spdx-expression-parse: 4.0.0 '@npmcli/promise-spawn@9.0.1': @@ -4804,8 +4689,6 @@ snapshots: node-gyp: 12.4.0 proc-log: 6.1.0 - '@oxc-project/types@0.113.0': {} - '@parcel/watcher-android-arm64@2.5.6': optional: true @@ -4869,132 +4752,162 @@ snapshots: '@popperjs/core@2.11.8': {} - '@rolldown/binding-android-arm64@1.0.0-rc.4': + '@rollup/rollup-android-arm-eabi@4.60.2': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-rc.4': - optional: true - - '@rolldown/binding-darwin-x64@1.0.0-rc.4': - optional: true - - '@rolldown/binding-freebsd-x64@1.0.0-rc.4': - optional: true - - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.4': - optional: true - - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.4': - optional: true - - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.4': - optional: true - - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.4': - optional: true - - '@rolldown/binding-linux-x64-musl@1.0.0-rc.4': - optional: true - - '@rolldown/binding-openharmony-arm64@1.0.0-rc.4': - optional: true - - '@rolldown/binding-wasm32-wasi@1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': - dependencies: - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - optional: true - - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.4': - optional: true - - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.4': - optional: true - - '@rolldown/pluginutils@1.0.0-rc.4': {} - '@rollup/rollup-android-arm-eabi@4.62.2': optional: true + '@rollup/rollup-android-arm64@4.60.2': + optional: true + '@rollup/rollup-android-arm64@4.62.2': optional: true + '@rollup/rollup-darwin-arm64@4.60.2': + optional: true + '@rollup/rollup-darwin-arm64@4.62.2': optional: true + '@rollup/rollup-darwin-x64@4.60.2': + optional: true + '@rollup/rollup-darwin-x64@4.62.2': optional: true + '@rollup/rollup-freebsd-arm64@4.60.2': + optional: true + '@rollup/rollup-freebsd-arm64@4.62.2': optional: true + '@rollup/rollup-freebsd-x64@4.60.2': + optional: true + '@rollup/rollup-freebsd-x64@4.62.2': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.60.2': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.60.2': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.62.2': optional: true + '@rollup/rollup-linux-arm64-gnu@4.60.2': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.62.2': optional: true + '@rollup/rollup-linux-arm64-musl@4.60.2': + optional: true + '@rollup/rollup-linux-arm64-musl@4.62.2': optional: true + '@rollup/rollup-linux-loong64-gnu@4.60.2': + optional: true + '@rollup/rollup-linux-loong64-gnu@4.62.2': optional: true + '@rollup/rollup-linux-loong64-musl@4.60.2': + optional: true + '@rollup/rollup-linux-loong64-musl@4.62.2': optional: true + '@rollup/rollup-linux-ppc64-gnu@4.60.2': + optional: true + '@rollup/rollup-linux-ppc64-gnu@4.62.2': optional: true + '@rollup/rollup-linux-ppc64-musl@4.60.2': + optional: true + '@rollup/rollup-linux-ppc64-musl@4.62.2': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.60.2': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.62.2': optional: true + '@rollup/rollup-linux-riscv64-musl@4.60.2': + optional: true + '@rollup/rollup-linux-riscv64-musl@4.62.2': optional: true + '@rollup/rollup-linux-s390x-gnu@4.60.2': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.62.2': optional: true + '@rollup/rollup-linux-x64-gnu@4.60.2': + optional: true + '@rollup/rollup-linux-x64-gnu@4.62.2': optional: true + '@rollup/rollup-linux-x64-musl@4.60.2': + optional: true + '@rollup/rollup-linux-x64-musl@4.62.2': optional: true + '@rollup/rollup-openbsd-x64@4.60.2': + optional: true + '@rollup/rollup-openbsd-x64@4.62.2': optional: true + '@rollup/rollup-openharmony-arm64@4.60.2': + optional: true + '@rollup/rollup-openharmony-arm64@4.62.2': optional: true + '@rollup/rollup-win32-arm64-msvc@4.60.2': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.62.2': optional: true + '@rollup/rollup-win32-ia32-msvc@4.60.2': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.62.2': optional: true + '@rollup/rollup-win32-x64-gnu@4.60.2': + optional: true + '@rollup/rollup-win32-x64-gnu@4.62.2': optional: true + '@rollup/rollup-win32-x64-msvc@4.60.2': + optional: true + '@rollup/rollup-win32-x64-msvc@4.62.2': optional: true - '@schematics/angular@21.2.17(chokidar@5.0.0)': + '@schematics/angular@22.0.4(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.17(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.17(chokidar@5.0.0) + '@angular-devkit/core': 22.0.4(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.4(chokidar@5.0.0) jsonc-parser: 3.3.1 + typescript: 6.0.3 transitivePeerDependencies: - chokidar @@ -5041,11 +4954,6 @@ snapshots: '@tufjs/canonical-json': 2.0.0 minimatch: 10.2.5 - '@tybys/wasm-util@0.10.3': - dependencies: - tslib: 2.8.1 - optional: true - '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.29.7 @@ -5080,6 +4988,8 @@ snapshots: '@types/esrecurse@4.3.1': {} + '@types/estree@1.0.8': {} + '@types/estree@1.0.9': {} '@types/json-schema@7.0.15': {} @@ -5092,153 +5002,100 @@ snapshots: dependencies: '@types/node': 26.0.1 - '@typescript-eslint/eslint-plugin@8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.47.0(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/type-utils': 8.47.0(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.47.0 + '@typescript-eslint/parser': 8.62.0(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.62.0 + '@typescript-eslint/type-utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.62.0 eslint: 9.39.4 - graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.47.0(eslint@9.39.4)(typescript@5.9.3)': + '@typescript-eslint/parser@8.62.0(eslint@9.39.4)(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.47.0 + '@typescript-eslint/scope-manager': 8.62.0 + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.62.0 debug: 4.4.3 eslint: 9.39.4 - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.47.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.62.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) - '@typescript-eslint/types': 8.47.0 - debug: 4.4.3 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/project-service@8.62.0(typescript@5.9.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@6.0.3) '@typescript-eslint/types': 8.62.0 debug: 4.4.3 - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.47.0': - dependencies: - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/visitor-keys': 8.47.0 - '@typescript-eslint/scope-manager@8.62.0': dependencies: '@typescript-eslint/types': 8.62.0 '@typescript-eslint/visitor-keys': 8.62.0 - '@typescript-eslint/tsconfig-utils@8.47.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.62.0(typescript@6.0.3)': dependencies: - typescript: 5.9.3 + typescript: 6.0.3 - '@typescript-eslint/tsconfig-utils@8.62.0(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.62.0(eslint@9.39.4)(typescript@6.0.3)': dependencies: - typescript: 5.9.3 - - '@typescript-eslint/type-utils@8.47.0(eslint@9.39.4)(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) debug: 4.4.3 eslint: 9.39.4 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.47.0': {} - '@typescript-eslint/types@8.62.0': {} - '@typescript-eslint/typescript-estree@8.47.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.62.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.47.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.47.0(typescript@5.9.3) - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/visitor-keys': 8.47.0 - debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.9 - semver: 7.8.5 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/typescript-estree@8.62.0(typescript@5.9.3)': - dependencies: - '@typescript-eslint/project-service': 8.62.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@5.9.3) + '@typescript-eslint/project-service': 8.62.0(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@6.0.3) '@typescript-eslint/types': 8.62.0 '@typescript-eslint/visitor-keys': 8.62.0 debug: 4.4.3 minimatch: 10.2.5 semver: 7.8.5 tinyglobby: 0.2.17 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.47.0(eslint@9.39.4)(typescript@5.9.3)': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) - '@typescript-eslint/scope-manager': 8.47.0 - '@typescript-eslint/types': 8.47.0 - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - eslint: 9.39.4 - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3)': + '@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) '@typescript-eslint/scope-manager': 8.62.0 '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/typescript-estree': 8.62.0(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) eslint: 9.39.4 - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.47.0': - dependencies: - '@typescript-eslint/types': 8.47.0 - eslint-visitor-keys: 4.2.1 - '@typescript-eslint/visitor-keys@8.62.0': dependencies: '@typescript-eslint/types': 8.62.0 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3))': + '@vitejs/plugin-basic-ssl@2.3.0(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0))': dependencies: - vite: 7.3.5(@types/node@26.0.1)(sass@1.97.3) + vite: 7.3.5(@types/node@26.0.1)(sass@1.99.0) '@vitest/expect@4.1.9': dependencies: @@ -5249,13 +5106,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.9(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3))': + '@vitest/mocker@4.1.9(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0))': dependencies: '@vitest/spy': 4.1.9 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.5(@types/node@26.0.1)(sass@1.97.3) + vite: 7.3.5(@types/node@26.0.1)(sass@1.99.0) '@vitest/pretty-format@4.1.9': dependencies: @@ -5303,9 +5160,7 @@ snapshots: agent-base@7.1.4: {} - ajv-formats@3.0.1(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 + agent-base@9.0.0: {} ajv-formats@3.0.1(ajv@8.20.0): optionalDependencies: @@ -5318,13 +5173,6 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.18.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.2 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 @@ -5332,38 +5180,38 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - algoliasearch@5.48.1: + algoliasearch@5.52.0: dependencies: - '@algolia/abtesting': 1.14.1 - '@algolia/client-abtesting': 5.48.1 - '@algolia/client-analytics': 5.48.1 - '@algolia/client-common': 5.48.1 - '@algolia/client-insights': 5.48.1 - '@algolia/client-personalization': 5.48.1 - '@algolia/client-query-suggestions': 5.48.1 - '@algolia/client-search': 5.48.1 - '@algolia/ingestion': 1.48.1 - '@algolia/monitoring': 1.48.1 - '@algolia/recommend': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/abtesting': 1.18.0 + '@algolia/client-abtesting': 5.52.0 + '@algolia/client-analytics': 5.52.0 + '@algolia/client-common': 5.52.0 + '@algolia/client-insights': 5.52.0 + '@algolia/client-personalization': 5.52.0 + '@algolia/client-query-suggestions': 5.52.0 + '@algolia/client-search': 5.52.0 + '@algolia/ingestion': 1.52.0 + '@algolia/monitoring': 1.52.0 + '@algolia/recommend': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - angular-eslint@21.1.0(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3))(typescript@5.9.3): + angular-eslint@22.0.0(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.62.0(eslint@9.39.4)(typescript@6.0.3))(typescript@6.0.3): dependencies: - '@angular-devkit/core': 21.2.17(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.17(chokidar@5.0.0) - '@angular-eslint/builder': 21.1.0(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin': 21.1.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/schematics': 21.1.0(@angular-eslint/template-parser@21.1.0(eslint@9.39.4)(typescript@5.9.3))(@angular/cli@21.2.17(@types/node@26.0.1)(chokidar@5.0.0))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@5.9.3) - '@angular-eslint/template-parser': 21.1.0(eslint@9.39.4)(typescript@5.9.3) - '@angular/cli': 21.2.17(@types/node@26.0.1)(chokidar@5.0.0) + '@angular-devkit/core': 22.0.4(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.4(chokidar@5.0.0) + '@angular-eslint/builder': 22.0.0(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3) + '@angular-eslint/eslint-plugin': 22.0.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) + '@angular-eslint/eslint-plugin-template': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) + '@angular-eslint/schematics': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3) + '@angular-eslint/template-parser': 22.0.0(eslint@9.39.4)(typescript@6.0.3) + '@angular/cli': 22.0.4(@types/node@26.0.1)(chokidar@5.0.0) '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) eslint: 9.39.4 - typescript: 5.9.3 - typescript-eslint: 8.47.0(eslint@9.39.4)(typescript@5.9.3) + typescript: 6.0.3 + typescript-eslint: 8.62.0(eslint@9.39.4)(typescript@6.0.3) transitivePeerDependencies: - chokidar - supports-color @@ -5372,8 +5220,6 @@ snapshots: dependencies: environment: 1.1.0 - ansi-regex@5.0.1: {} - ansi-regex@6.2.2: {} ansi-styles@4.3.0: @@ -5398,7 +5244,7 @@ snapshots: baseline-browser-mapping@2.10.40: {} - beasties@0.4.1: + beasties@0.4.2: dependencies: css-select: 6.0.0 css-what: 7.0.0 @@ -5439,18 +5285,10 @@ snapshots: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.1.1: - dependencies: - balanced-match: 1.0.2 - brace-expansion@5.0.6: dependencies: balanced-match: 4.0.4 - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - browserslist@4.28.4: dependencies: baseline-browser-mapping: 2.10.40 @@ -5536,8 +5374,6 @@ snapshots: color-name@1.1.4: {} - colorette@2.0.20: {} - concat-map@0.0.1: {} content-disposition@1.1.0: {} @@ -5637,8 +5473,6 @@ snapshots: emoji-regex@10.6.0: {} - emoji-regex@8.0.0: {} - encodeurl@2.0.0: {} engine.io-client@6.6.6: @@ -5674,8 +5508,6 @@ snapshots: entities@4.5.0: {} - entities@6.0.1: {} - entities@7.0.1: {} entities@8.0.0: {} @@ -5891,23 +5723,21 @@ snapshots: fast-deep-equal@3.1.3: {} - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + fast-uri@3.1.2: {} - fastq@1.20.1: + fast-wrap-ansi@0.2.2: dependencies: - reusify: 1.1.0 + fast-string-width: 3.0.2 fdir@6.5.0(picomatch@4.0.4): optionalDependencies: @@ -5917,10 +5747,6 @@ snapshots: dependencies: flat-cache: 4.0.1 - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - finalhandler@2.1.1: dependencies: debug: 4.4.3 @@ -5981,10 +5807,6 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.2 - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -6003,8 +5825,6 @@ snapshots: graceful-fs@4.2.11: {} - graphemer@1.4.0: {} - has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -6056,6 +5876,13 @@ snapshots: transitivePeerDependencies: - supports-color + https-proxy-agent@9.0.0: + dependencies: + agent-base: 9.0.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 @@ -6087,8 +5914,6 @@ snapshots: is-extglob@2.1.1: {} - is-fullwidth-code-point@3.0.0: {} - is-fullwidth-code-point@5.1.0: dependencies: get-east-asian-width: 1.6.0 @@ -6099,8 +5924,6 @@ snapshots: is-interactive@2.0.0: {} - is-number@7.0.0: {} - is-potential-custom-element-name@1.0.1: {} is-promise@4.0.0: {} @@ -6111,7 +5934,8 @@ snapshots: isexe@4.0.0: {} - istanbul-lib-coverage@3.2.2: {} + istanbul-lib-coverage@3.2.2: + optional: true istanbul-lib-instrument@6.0.3: dependencies: @@ -6119,9 +5943,10 @@ snapshots: '@babel/parser': 7.29.7 '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 - semver: 7.7.4 + semver: 7.8.5 transitivePeerDependencies: - supports-color + optional: true jose@6.2.3: {} @@ -6188,16 +6013,15 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - listr2@9.0.5: + listr2@10.2.1: dependencies: cli-truncate: 5.2.0 - colorette: 2.0.20 eventemitter3: 5.0.4 log-update: 6.1.0 rfdc: 1.4.1 - wrap-ansi: 9.0.2 + wrap-ansi: 10.0.0 - lmdb@3.5.1: + lmdb@3.5.4: dependencies: '@harperfast/extended-iterable': 1.0.3 msgpackr: 1.12.1 @@ -6206,13 +6030,13 @@ snapshots: ordered-binary: 1.6.1 weak-lru-cache: 1.2.2 optionalDependencies: - '@lmdb/lmdb-darwin-arm64': 3.5.1 - '@lmdb/lmdb-darwin-x64': 3.5.1 - '@lmdb/lmdb-linux-arm': 3.5.1 - '@lmdb/lmdb-linux-arm64': 3.5.1 - '@lmdb/lmdb-linux-x64': 3.5.1 - '@lmdb/lmdb-win32-arm64': 3.5.1 - '@lmdb/lmdb-win32-x64': 3.5.1 + '@lmdb/lmdb-darwin-arm64': 3.5.4 + '@lmdb/lmdb-darwin-x64': 3.5.4 + '@lmdb/lmdb-linux-arm': 3.5.4 + '@lmdb/lmdb-linux-arm64': 3.5.4 + '@lmdb/lmdb-linux-x64': 3.5.4 + '@lmdb/lmdb-win32-arm64': 3.5.4 + '@lmdb/lmdb-win32-x64': 3.5.4 optional: true locate-path@6.0.0: @@ -6269,13 +6093,6 @@ snapshots: merge-descriptors@2.0.0: {} - merge2@1.4.1: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.2 - mime-db@1.52.0: {} mime-db@1.54.0: {} @@ -6298,10 +6115,6 @@ snapshots: dependencies: brace-expansion: 1.1.15 - minimatch@9.0.9: - dependencies: - brace-expansion: 2.1.1 - minipass-collect@2.0.1: dependencies: minipass: 7.1.3 @@ -6357,7 +6170,7 @@ snapshots: msgpackr-extract: 3.0.4 optional: true - mute-stream@2.0.0: {} + mute-stream@3.0.0: {} nanoid@3.3.15: {} @@ -6367,16 +6180,16 @@ snapshots: negotiator@1.0.0: {} - ngx-cookie-service@21.3.1(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0)): + ngx-cookie-service@22.0.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)): dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - ngx-socket-io@4.10.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): + ngx-socket-io@4.10.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) core-js: 3.49.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 @@ -6406,7 +6219,7 @@ snapshots: graceful-fs: 4.2.11 nopt: 9.0.0 proc-log: 6.1.0 - semver: 7.7.4 + semver: 7.8.5 tar: 7.5.17 tinyglobby: 0.2.17 undici: 6.27.0 @@ -6424,7 +6237,7 @@ snapshots: npm-install-checks@8.0.0: dependencies: - semver: 7.7.4 + semver: 7.8.5 npm-normalize-package-bin@5.0.0: {} @@ -6432,7 +6245,7 @@ snapshots: dependencies: hosted-git-info: 9.0.3 proc-log: 6.1.0 - semver: 7.7.4 + semver: 7.8.5 validate-npm-package-name: 7.0.2 npm-packlist@10.0.4: @@ -6445,7 +6258,7 @@ snapshots: npm-install-checks: 8.0.0 npm-normalize-package-bin: 5.0.0 npm-package-arg: 13.0.2 - semver: 7.7.4 + semver: 7.8.5 npm-registry-fetch@19.1.1: dependencies: @@ -6491,7 +6304,7 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - ora@9.3.0: + ora@9.4.0: dependencies: chalk: 5.6.2 cli-cursor: 5.0.0 @@ -6541,9 +6354,9 @@ snapshots: dependencies: callsites: 3.1.0 - parse5-html-rewriting-stream@8.0.0: + parse5-html-rewriting-stream@8.0.1: dependencies: - entities: 6.0.1 + entities: 8.0.0 parse5: 8.0.1 parse5-sax-parser: 8.0.0 @@ -6572,8 +6385,6 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.2: {} - picomatch@4.0.4: {} piscina@5.2.0: @@ -6610,8 +6421,6 @@ snapshots: es-define-property: 1.0.1 side-channel: 1.1.1 - queue-microtask@1.2.3: {} - range-parser@1.3.0: {} raw-body@3.0.2: @@ -6636,31 +6445,38 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 - reusify@1.1.0: {} - rfdc@1.4.1: {} - rolldown@1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + rollup@4.60.2: dependencies: - '@oxc-project/types': 0.113.0 - '@rolldown/pluginutils': 1.0.0-rc.4 + '@types/estree': 1.0.8 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-rc.4 - '@rolldown/binding-darwin-arm64': 1.0.0-rc.4 - '@rolldown/binding-darwin-x64': 1.0.0-rc.4 - '@rolldown/binding-freebsd-x64': 1.0.0-rc.4 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.4 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.4 - '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.4 - '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.4 - '@rolldown/binding-linux-x64-musl': 1.0.0-rc.4 - '@rolldown/binding-openharmony-arm64': 1.0.0-rc.4 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.4 - '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.4 - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' + '@rollup/rollup-android-arm-eabi': 4.60.2 + '@rollup/rollup-android-arm64': 4.60.2 + '@rollup/rollup-darwin-arm64': 4.60.2 + '@rollup/rollup-darwin-x64': 4.60.2 + '@rollup/rollup-freebsd-arm64': 4.60.2 + '@rollup/rollup-freebsd-x64': 4.60.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.2 + '@rollup/rollup-linux-arm-musleabihf': 4.60.2 + '@rollup/rollup-linux-arm64-gnu': 4.60.2 + '@rollup/rollup-linux-arm64-musl': 4.60.2 + '@rollup/rollup-linux-loong64-gnu': 4.60.2 + '@rollup/rollup-linux-loong64-musl': 4.60.2 + '@rollup/rollup-linux-ppc64-gnu': 4.60.2 + '@rollup/rollup-linux-ppc64-musl': 4.60.2 + '@rollup/rollup-linux-riscv64-gnu': 4.60.2 + '@rollup/rollup-linux-riscv64-musl': 4.60.2 + '@rollup/rollup-linux-s390x-gnu': 4.60.2 + '@rollup/rollup-linux-x64-gnu': 4.60.2 + '@rollup/rollup-linux-x64-musl': 4.60.2 + '@rollup/rollup-openbsd-x64': 4.60.2 + '@rollup/rollup-openharmony-arm64': 4.60.2 + '@rollup/rollup-win32-arm64-msvc': 4.60.2 + '@rollup/rollup-win32-ia32-msvc': 4.60.2 + '@rollup/rollup-win32-x64-gnu': 4.60.2 + '@rollup/rollup-win32-x64-msvc': 4.60.2 + fsevents: 2.3.3 rollup@4.62.2: dependencies: @@ -6703,17 +6519,13 @@ snapshots: transitivePeerDependencies: - supports-color - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - rxjs@7.8.2: dependencies: tslib: 2.8.1 safer-buffer@2.1.2: {} - sass@1.97.3: + sass@1.99.0: dependencies: chokidar: 4.0.3 immutable: 5.1.8 @@ -6727,10 +6539,10 @@ snapshots: semver@6.3.1: {} - semver@7.7.3: {} - semver@7.7.4: {} + semver@7.8.0: {} + semver@7.8.5: {} send@1.2.1: @@ -6907,12 +6719,6 @@ snapshots: stdin-discarder@0.3.2: {} - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - string-width@7.2.0: dependencies: emoji-regex: 10.6.0 @@ -6924,10 +6730,6 @@ snapshots: get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - strip-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 @@ -6952,7 +6754,7 @@ snapshots: tinyexec@1.2.4: {} - tinyglobby@0.2.15: + tinyglobby@0.2.16: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 @@ -6970,10 +6772,6 @@ snapshots: dependencies: tldts-core: 7.4.4 - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - toidentifier@1.0.1: {} tough-cookie@6.0.1: @@ -6984,9 +6782,9 @@ snapshots: dependencies: punycode: 2.3.1 - ts-api-utils@2.5.0(typescript@5.9.3): + ts-api-utils@2.5.0(typescript@6.0.3): dependencies: - typescript: 5.9.3 + typescript: 6.0.3 tslib@2.8.1: {} @@ -7008,25 +6806,23 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.2 - typescript-eslint@8.47.0(eslint@9.39.4)(typescript@5.9.3): + typescript-eslint@8.62.0(eslint@9.39.4)(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/parser': 8.47.0(eslint@9.39.4)(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.47.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.47.0(eslint@9.39.4)(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/parser': 8.62.0(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) eslint: 9.39.4 - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - typescript@5.9.3: {} + typescript@6.0.3: {} undici-types@8.3.0: {} undici@6.27.0: {} - undici@7.24.4: {} - unpipe@1.0.0: {} update-browserslist-db@1.2.3(browserslist@4.28.4): @@ -7043,23 +6839,23 @@ snapshots: vary@1.1.2: {} - vite@7.3.5(@types/node@26.0.1)(sass@1.97.3): + vite@7.3.5(@types/node@26.0.1)(sass@1.99.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 postcss: 8.5.15 rollup: 4.62.2 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 26.0.1 fsevents: 2.3.3 - sass: 1.97.3 + sass: 1.99.0 - vitest@4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3)): + vitest@4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0)): dependencies: '@vitest/expect': 4.1.9 - '@vitest/mocker': 4.1.9(vite@7.3.5(@types/node@26.0.1)(sass@1.97.3)) + '@vitest/mocker': 4.1.9(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0)) '@vitest/pretty-format': 4.1.9 '@vitest/runner': 4.1.9 '@vitest/snapshot': 4.1.9 @@ -7076,7 +6872,7 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 7.3.5(@types/node@26.0.1)(sass@1.97.3) + vite: 7.3.5(@types/node@26.0.1)(sass@1.99.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 26.0.1 @@ -7122,11 +6918,11 @@ snapshots: word-wrap@1.2.5: {} - wrap-ansi@6.2.0: + wrap-ansi@10.0.0: dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 + ansi-styles: 6.2.3 + string-width: 8.2.1 + strip-ansi: 7.2.0 wrap-ansi@9.0.2: dependencies: @@ -7165,14 +6961,12 @@ snapshots: yocto-queue@0.1.0: {} - yoctocolors-cjs@2.1.3: {} - yoctocolors@2.1.2: {} - zod-to-json-schema@3.25.2(zod@4.3.6): + zod-to-json-schema@3.25.2(zod@4.4.2): dependencies: - zod: 4.3.6 + zod: 4.4.2 - zod@4.3.6: {} + zod@4.4.2: {} zone.js@0.15.0: {} diff --git a/ui/src/app/app.config.ts b/ui/src/app/app.config.ts index 6642eac..a2611b0 100644 --- a/ui/src/app/app.config.ts +++ b/ui/src/app/app.config.ts @@ -1,6 +1,6 @@ import { ApplicationConfig, provideBrowserGlobalErrorListeners, isDevMode, provideZoneChangeDetection } from '@angular/core'; import { provideServiceWorker } from '@angular/service-worker'; -import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { provideHttpClient, withInterceptorsFromDi, withXhr } from '@angular/common/http'; export const appConfig: ApplicationConfig = { providers: [ @@ -12,6 +12,6 @@ export const appConfig: ApplicationConfig = { // or after 30 seconds (whichever comes first). registrationStrategy: 'registerWhenStable:30000' }), - provideHttpClient(withInterceptorsFromDi()), + provideHttpClient(withXhr(), withInterceptorsFromDi()), ] }; diff --git a/ui/src/app/components/master-checkbox.component.ts b/ui/src/app/components/master-checkbox.component.ts index 6cc8a36..6b4c147 100644 --- a/ui/src/app/components/master-checkbox.component.ts +++ b/ui/src/app/components/master-checkbox.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, viewChild, output, input } from "@angular/core"; +import { Component, ElementRef, viewChild, output, input, ChangeDetectionStrategy } from "@angular/core"; import { Checkable } from "../interfaces"; import { FormsModule } from "@angular/forms"; @@ -10,7 +10,10 @@ import { FormsModule } from "@angular/forms";
`, -imports: [ + // Shared Checkable objects are mutated in place; Eager preserves pre-v22 behavior. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Eager, + imports: [ FormsModule ] }) diff --git a/ui/src/app/components/slave-checkbox.component.ts b/ui/src/app/components/slave-checkbox.component.ts index 6fb5f66..4100a16 100644 --- a/ui/src/app/components/slave-checkbox.component.ts +++ b/ui/src/app/components/slave-checkbox.component.ts @@ -1,4 +1,4 @@ -import { Component, input } from '@angular/core'; +import { Component, input, ChangeDetectionStrategy } from '@angular/core'; import { SelectAllCheckboxComponent } from './master-checkbox.component'; import { Checkable } from '../interfaces'; import { FormsModule } from '@angular/forms'; @@ -11,7 +11,10 @@ import { FormsModule } from '@angular/forms';
`, -imports: [ + // Shared Checkable objects are mutated in place; Eager preserves pre-v22 behavior. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Eager, + imports: [ FormsModule ] }) diff --git a/ui/tsconfig.app.json b/ui/tsconfig.app.json index 6eece41..0d51a9f 100644 --- a/ui/tsconfig.app.json +++ b/ui/tsconfig.app.json @@ -12,5 +12,13 @@ ], "exclude": [ "src/**/*.spec.ts" - ] + ], + "angularCompilerOptions": { + "extendedDiagnostics": { + "checks": { + "nullishCoalescingNotNullable": "suppress", + "optionalChainNotNullable": "suppress" + } + } + } } From 0a946cc352be4536247f3d5be2a395c2b45b11e2 Mon Sep 17 00:00:00 2001 From: AutoUpdater Date: Sun, 5 Jul 2026 00:28:09 +0000 Subject: [PATCH 65/88] upgrade yt-dlp from 2026.6.9 to 2026.7.4 --- uv.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uv.lock b/uv.lock index 84ff80a..045d60f 100644 --- a/uv.lock +++ b/uv.lock @@ -1117,11 +1117,11 @@ wheels = [ [[package]] name = "yt-dlp" -version = "2026.6.9" +version = "2026.7.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/88/a4/1b0979d28f87774bb67fbbc66bce44f9dd1aa0e547a99e22985fac945c33/yt_dlp-2026.6.9.tar.gz", hash = "sha256:d50fcb95f48d61bedde33e408c1881d4c279e51c31354a599ce09e96ba0f4b86", size = 3030590, upload-time = "2026-06-09T23:27:14.831Z" } +sdist = { url = "https://files.pythonhosted.org/packages/47/c5/9972af4b472b0d55badf841ebafd2f98944cb0ae0f46e11d01f363ea5b91/yt_dlp-2026.7.4.tar.gz", hash = "sha256:b094813404f87a9dd2186f00815231df32e5fd8a5403be0f807b3bb2d21a4432", size = 3049326, upload-time = "2026-07-04T22:42:14.837Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f3/ee/188a3dadf9dfdac713243521f919feca1cd091d4358c9ea7e8ebb710a7cc/yt_dlp-2026.6.9-py3-none-any.whl", hash = "sha256:442ba4c75724b9496144c8434b617962ee08d0ee7c26ec663848fe9b78d5a3e4", size = 3169035, upload-time = "2026-06-09T23:27:12.58Z" }, + { url = "https://files.pythonhosted.org/packages/f9/8a/cd4c9b02c10c563adfe78118310129641900e1cd6de888cfae2452072696/yt_dlp-2026.7.4-py3-none-any.whl", hash = "sha256:f11f2b11d5a8ac4059f9bdf29fa4407dc7c6bb00c5097e95ca22a7a9db518266", size = 3184705, upload-time = "2026-07-04T22:42:12.989Z" }, ] [package.optional-dependencies] From 24ae8f07425a60b522f5133595c6b6f43e23d1c2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Jul 2026 04:18:28 +0000 Subject: [PATCH 66/88] Initial plan From 38c0ca22f48350fcb8d61900a5f352625bff4156 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 5 Jul 2026 04:21:32 +0000 Subject: [PATCH 67/88] Pin pnpm version in packageManager field to fix Docker build corepack prepare pnpm --activate without a version was resolving to 12.0.0-alpha.0 (broken pre-release), causing the dockerhub-build-push job to fail. Adding packageManager field pins it to a stable release. --- ui/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/package.json b/ui/package.json index aecff95..ceefd96 100644 --- a/ui/package.json +++ b/ui/package.json @@ -21,6 +21,7 @@ } ] }, + "packageManager": "pnpm@11.5.2", "private": true, "dependencies": { "@angular/animations": "^22.0.4", From c2c129db6102b88b65e9f8c296dad91e3ada0057 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sun, 5 Jul 2026 03:42:07 -0700 Subject: [PATCH 68/88] fix: fall back to direct write when atomic state save hits EPERM on NFS --- app/state_store.py | 35 ++++++++++++++++++++++-- app/tests/test_state_store.py | 50 +++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/app/state_store.py b/app/state_store.py index cab711a..2548ad6 100644 --- a/app/state_store.py +++ b/app/state_store.py @@ -62,6 +62,7 @@ class AtomicJsonStore: self.path = path self.kind = kind self.schema_version = schema_version + self._direct_write_fallback_warned = False def _ensure_parent(self) -> None: parent = os.path.dirname(self.path) @@ -96,6 +97,13 @@ class AtomicJsonStore: def save(self, data: dict[str, Any]) -> None: self._ensure_parent() payload = self._build_payload(data) + try: + self._atomic_write(payload) + except OSError as exc: + self._warn_direct_write_fallback(exc) + self._direct_write(payload) + + def _atomic_write(self, payload: dict[str, Any]) -> None: parent = os.path.dirname(self.path) or "." fd, tmp_path = tempfile.mkstemp( prefix=f".{os.path.basename(self.path)}.", @@ -105,8 +113,7 @@ class AtomicJsonStore: ) try: with os.fdopen(fd, "w", encoding="utf-8") as f: - json.dump(payload, f, ensure_ascii=False, separators=(",", ":")) - f.write("\n") + self._write_payload(payload, f) f.flush() os.fsync(f.fileno()) os.replace(tmp_path, self.path) @@ -118,6 +125,30 @@ class AtomicJsonStore: pass raise + def _direct_write(self, payload: dict[str, Any]) -> None: + with open(self.path, "w", encoding="utf-8") as f: + self._write_payload(payload, f) + f.flush() + try: + os.fsync(f.fileno()) + except OSError: + pass + + @staticmethod + def _write_payload(payload: dict[str, Any], f: Any) -> None: + json.dump(payload, f, ensure_ascii=False, separators=(",", ":")) + f.write("\n") + + def _warn_direct_write_fallback(self, exc: OSError) -> None: + if self._direct_write_fallback_warned: + return + self._direct_write_fallback_warned = True + log.warning( + "Atomic state write failed for %s (%s); falling back to direct write", + self.path, + exc, + ) + def quarantine_invalid_file(self, exc: Exception) -> None: if not os.path.exists(self.path): return diff --git a/app/tests/test_state_store.py b/app/tests/test_state_store.py index fb71b08..759d89a 100644 --- a/app/tests/test_state_store.py +++ b/app/tests/test_state_store.py @@ -4,6 +4,7 @@ import os import tempfile import unittest from datetime import datetime +from unittest.mock import patch from state_store import AtomicJsonStore, from_json_compatible, to_json_compatible @@ -21,6 +22,55 @@ class StateStoreTests(unittest.TestCase): self.assertEqual(payload["schema_version"], 2) self.assertEqual(payload["items"][0]["info"]["title"], "hello") + def test_save_falls_back_to_direct_write_when_mkstemp_fails(self): + with tempfile.TemporaryDirectory() as tmp: + path = os.path.join(tmp, "queue.json") + store = AtomicJsonStore(path, kind="persistent_queue:queue") + + with self.assertLogs("state_store", level="WARNING") as logs: + with patch( + "state_store.tempfile.mkstemp", + side_effect=PermissionError(1, "Operation not permitted"), + ): + store.save({"items": [{"key": "a"}]}) + + self.assertTrue(os.path.exists(path)) + self.assertTrue(any(path in message for message in logs.output)) + payload = store.load() + self.assertEqual(payload["items"], [{"key": "a"}]) + + def test_save_falls_back_to_direct_write_when_replace_fails(self): + with tempfile.TemporaryDirectory() as tmp: + path = os.path.join(tmp, "queue.json") + store = AtomicJsonStore(path, kind="persistent_queue:queue") + + with patch( + "state_store.os.replace", + side_effect=PermissionError(1, "Operation not permitted"), + ): + store.save({"items": [{"key": "a"}]}) + + self.assertTrue(os.path.exists(path)) + payload = store.load() + self.assertEqual(payload["items"], [{"key": "a"}]) + self.assertEqual([], [name for name in os.listdir(tmp) if name.endswith(".tmp")]) + + def test_save_reraises_when_atomic_and_direct_write_fail(self): + with tempfile.TemporaryDirectory() as tmp: + path = os.path.join(tmp, "queue.json") + store = AtomicJsonStore(path, kind="persistent_queue:queue") + + with patch( + "state_store.tempfile.mkstemp", + side_effect=PermissionError(1, "Operation not permitted"), + ): + with patch("builtins.open", side_effect=PermissionError(13, "Permission denied")): + with self.assertRaises(PermissionError) as ctx: + store.save({"items": [{"key": "a"}]}) + + self.assertEqual(ctx.exception.errno, 13) + self.assertFalse(os.path.exists(path)) + def test_invalid_file_is_quarantined(self): with tempfile.TemporaryDirectory() as tmp: path = os.path.join(tmp, "queue.json") From f315b75bb2436cb200e3b47110c83a09754465ad Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sun, 5 Jul 2026 03:44:57 -0700 Subject: [PATCH 69/88] fix: limit atomic-write fallback to atomic-unsupported errnos --- app/state_store.py | 22 ++++++++++++++++++++++ app/tests/test_state_store.py | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/app/state_store.py b/app/state_store.py index 2548ad6..2d2b2b7 100644 --- a/app/state_store.py +++ b/app/state_store.py @@ -2,6 +2,7 @@ from __future__ import annotations import base64 import collections.abc +import errno import json import logging import os @@ -17,6 +18,25 @@ STATE_SCHEMA_VERSION = 2 _BYTES_MARKER = "__metube_bytes__" _DATETIME_MARKER = "__metube_datetime__" +# Errnos that signal the filesystem cannot support the temp-file + rename +# atomic-write strategy (for example an NFS-backed state dir returning EPERM on +# mkstemp). These are safe to fall back on because they mean the atomic +# mechanism is unavailable, not that the data write itself failed. Errors like +# ENOSPC/EIO are deliberately excluded so a genuine storage failure surfaces +# instead of silently truncating an existing good state file. +_ATOMIC_UNSUPPORTED_ERRNOS = frozenset( + e + for e in ( + errno.EPERM, + errno.EACCES, + errno.ENOSYS, + errno.EINVAL, + getattr(errno, "EOPNOTSUPP", None), + getattr(errno, "ENOTSUP", None), + ) + if e is not None +) + def to_json_compatible(value: Any) -> Any: if value is None or isinstance(value, (bool, int, float, str)): @@ -100,6 +120,8 @@ class AtomicJsonStore: try: self._atomic_write(payload) except OSError as exc: + if exc.errno not in _ATOMIC_UNSUPPORTED_ERRNOS: + raise self._warn_direct_write_fallback(exc) self._direct_write(payload) diff --git a/app/tests/test_state_store.py b/app/tests/test_state_store.py index 759d89a..089ee95 100644 --- a/app/tests/test_state_store.py +++ b/app/tests/test_state_store.py @@ -71,6 +71,28 @@ class StateStoreTests(unittest.TestCase): self.assertEqual(ctx.exception.errno, 13) self.assertFalse(os.path.exists(path)) + def test_save_reraises_and_preserves_state_on_non_atomic_errno(self): + # A storage failure such as ENOSPC is not an "atomic unavailable" + # signal, so it must surface instead of falling back to a direct write + # that would truncate the existing good state file. + import errno as _errno + + with tempfile.TemporaryDirectory() as tmp: + path = os.path.join(tmp, "queue.json") + store = AtomicJsonStore(path, kind="persistent_queue:queue") + store.save({"items": [{"key": "good"}]}) + + with patch( + "state_store.tempfile.mkstemp", + side_effect=OSError(_errno.ENOSPC, "No space left on device"), + ): + with self.assertRaises(OSError) as ctx: + store.save({"items": [{"key": "new"}]}) + + self.assertEqual(ctx.exception.errno, _errno.ENOSPC) + # Existing state is untouched. + self.assertEqual(store.load()["items"], [{"key": "good"}]) + def test_invalid_file_is_quarantined(self): with tempfile.TemporaryDirectory() as tmp: path = os.path.join(tmp, "queue.json") From e0549d6c24518014c16296b3e54f4bd2e25af8e0 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sun, 5 Jul 2026 03:49:15 -0700 Subject: [PATCH 70/88] fix: surface real storage errors from direct-write fsync fallback --- app/state_store.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/state_store.py b/app/state_store.py index 2d2b2b7..5de4b24 100644 --- a/app/state_store.py +++ b/app/state_store.py @@ -153,8 +153,13 @@ class AtomicJsonStore: f.flush() try: os.fsync(f.fileno()) - except OSError: - pass + except OSError as exc: + # Tolerate fsync being unsupported on the underlying filesystem + # (the same class of filesystem that forced this fallback), but + # let genuine storage failures such as ENOSPC/EIO surface rather + # than reporting a durable write that did not happen. + if exc.errno not in _ATOMIC_UNSUPPORTED_ERRNOS: + raise @staticmethod def _write_payload(payload: dict[str, Any], f: Any) -> None: From 961b54aa837f3903bfcff7ff7fbf148c3305d540 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sun, 5 Jul 2026 03:53:22 -0700 Subject: [PATCH 71/88] fix: make fsync best-effort so only mkstemp/replace failures fall back --- app/state_store.py | 25 +++++++++++++++---------- app/tests/test_state_store.py | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/app/state_store.py b/app/state_store.py index 5de4b24..11bb5bb 100644 --- a/app/state_store.py +++ b/app/state_store.py @@ -137,7 +137,7 @@ class AtomicJsonStore: with os.fdopen(fd, "w", encoding="utf-8") as f: self._write_payload(payload, f) f.flush() - os.fsync(f.fileno()) + self._best_effort_fsync(f.fileno()) os.replace(tmp_path, self.path) self._fsync_directory(parent) except Exception: @@ -151,15 +151,20 @@ class AtomicJsonStore: with open(self.path, "w", encoding="utf-8") as f: self._write_payload(payload, f) f.flush() - try: - os.fsync(f.fileno()) - except OSError as exc: - # Tolerate fsync being unsupported on the underlying filesystem - # (the same class of filesystem that forced this fallback), but - # let genuine storage failures such as ENOSPC/EIO surface rather - # than reporting a durable write that did not happen. - if exc.errno not in _ATOMIC_UNSUPPORTED_ERRNOS: - raise + self._best_effort_fsync(f.fileno()) + + @staticmethod + def _best_effort_fsync(fileno: int) -> None: + # Tolerate fsync being unsupported on the underlying filesystem (for + # example a network mount that returns EINVAL/ENOSYS), but let genuine + # storage failures such as ENOSPC/EIO surface so a non-durable write is + # never reported as success. An unsupported fsync must not by itself + # abandon the atomic rename path. + try: + os.fsync(fileno) + except OSError as exc: + if exc.errno not in _ATOMIC_UNSUPPORTED_ERRNOS: + raise @staticmethod def _write_payload(payload: dict[str, Any], f: Any) -> None: diff --git a/app/tests/test_state_store.py b/app/tests/test_state_store.py index 089ee95..0189808 100644 --- a/app/tests/test_state_store.py +++ b/app/tests/test_state_store.py @@ -71,6 +71,25 @@ class StateStoreTests(unittest.TestCase): self.assertEqual(ctx.exception.errno, 13) self.assertFalse(os.path.exists(path)) + def test_unsupported_fsync_keeps_atomic_path(self): + # fsync being unsupported (EINVAL/ENOSYS) must not by itself trigger the + # direct-write fallback; the atomic temp-file + rename path still runs. + import errno as _errno + + with tempfile.TemporaryDirectory() as tmp: + path = os.path.join(tmp, "queue.json") + store = AtomicJsonStore(path, kind="persistent_queue:queue") + + with patch( + "state_store.os.fsync", + side_effect=OSError(_errno.EINVAL, "Invalid argument"), + ): + with self.assertNoLogs("state_store", level="WARNING"): + store.save({"items": [{"key": "a"}]}) + + self.assertEqual(store.load()["items"], [{"key": "a"}]) + self.assertEqual([], [name for name in os.listdir(tmp) if name.endswith(".tmp")]) + def test_save_reraises_and_preserves_state_on_non_atomic_errno(self): # A storage failure such as ENOSPC is not an "atomic unavailable" # signal, so it must surface instead of falling back to a direct write From 49a46a7d1c953eb927b887fe0441ac990973894c Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sun, 5 Jul 2026 03:57:19 -0700 Subject: [PATCH 72/88] fix: create fallback state file with owner-only 0600 permissions --- app/state_store.py | 6 +++++- app/tests/test_state_store.py | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/state_store.py b/app/state_store.py index 11bb5bb..c2a69f7 100644 --- a/app/state_store.py +++ b/app/state_store.py @@ -148,7 +148,11 @@ class AtomicJsonStore: raise def _direct_write(self, payload: dict[str, Any]) -> None: - with open(self.path, "w", encoding="utf-8") as f: + # Create with 0o600 so the fallback keeps the owner-only permissions the + # atomic path gets from mkstemp; state files can contain URLs and + # per-download option overrides that must not leak on shared mounts. + fd = os.open(self.path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) + with os.fdopen(fd, "w", encoding="utf-8") as f: self._write_payload(payload, f) f.flush() self._best_effort_fsync(f.fileno()) diff --git a/app/tests/test_state_store.py b/app/tests/test_state_store.py index 0189808..9309f4f 100644 --- a/app/tests/test_state_store.py +++ b/app/tests/test_state_store.py @@ -36,6 +36,8 @@ class StateStoreTests(unittest.TestCase): self.assertTrue(os.path.exists(path)) self.assertTrue(any(path in message for message in logs.output)) + # Fallback keeps owner-only permissions, matching the atomic path. + self.assertEqual(os.stat(path).st_mode & 0o777, 0o600) payload = store.load() self.assertEqual(payload["items"], [{"key": "a"}]) @@ -64,7 +66,10 @@ class StateStoreTests(unittest.TestCase): "state_store.tempfile.mkstemp", side_effect=PermissionError(1, "Operation not permitted"), ): - with patch("builtins.open", side_effect=PermissionError(13, "Permission denied")): + with patch( + "state_store.os.open", + side_effect=PermissionError(13, "Permission denied"), + ): with self.assertRaises(PermissionError) as ctx: store.save({"items": [{"key": "a"}]}) From 96e88a355528fa05f0de1cd2f4719b5c4d634382 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sun, 5 Jul 2026 04:00:59 -0700 Subject: [PATCH 73/88] fix: force 0600 on fallback state rewrites, not just creation --- app/state_store.py | 8 ++++++++ app/tests/test_state_store.py | 17 +++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/app/state_store.py b/app/state_store.py index c2a69f7..1958286 100644 --- a/app/state_store.py +++ b/app/state_store.py @@ -153,6 +153,14 @@ class AtomicJsonStore: # per-download option overrides that must not leak on shared mounts. fd = os.open(self.path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600) with os.fdopen(fd, "w", encoding="utf-8") as f: + # The 0o600 mode above only applies when the file is created; force + # it on rewrites too so an existing, broadly-permissioned state file + # is tightened to match the atomic path. Best-effort because some + # network filesystems reject chmod, and that must not re-crash save. + try: + os.fchmod(f.fileno(), 0o600) + except OSError: + pass self._write_payload(payload, f) f.flush() self._best_effort_fsync(f.fileno()) diff --git a/app/tests/test_state_store.py b/app/tests/test_state_store.py index 9309f4f..82e3e30 100644 --- a/app/tests/test_state_store.py +++ b/app/tests/test_state_store.py @@ -41,6 +41,23 @@ class StateStoreTests(unittest.TestCase): payload = store.load() self.assertEqual(payload["items"], [{"key": "a"}]) + def test_fallback_tightens_permissions_on_existing_file(self): + with tempfile.TemporaryDirectory() as tmp: + path = os.path.join(tmp, "queue.json") + with open(path, "w", encoding="utf-8") as f: + f.write("{}") + os.chmod(path, 0o644) + + store = AtomicJsonStore(path, kind="persistent_queue:queue") + with patch( + "state_store.tempfile.mkstemp", + side_effect=PermissionError(1, "Operation not permitted"), + ): + store.save({"items": [{"key": "a"}]}) + + self.assertEqual(os.stat(path).st_mode & 0o777, 0o600) + self.assertEqual(store.load()["items"], [{"key": "a"}]) + def test_save_falls_back_to_direct_write_when_replace_fails(self): with tempfile.TemporaryDirectory() as tmp: path = os.path.join(tmp, "queue.json") From b00d4785eeab9cf3e250781d060c004059cc5682 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sun, 5 Jul 2026 04:04:54 -0700 Subject: [PATCH 74/88] fix: serialize state before truncating in the direct-write fallback --- app/state_store.py | 14 +++++++++----- app/tests/test_state_store.py | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/app/state_store.py b/app/state_store.py index 1958286..57ead6c 100644 --- a/app/state_store.py +++ b/app/state_store.py @@ -126,6 +126,7 @@ class AtomicJsonStore: self._direct_write(payload) def _atomic_write(self, payload: dict[str, Any]) -> None: + text = self._serialize(payload) parent = os.path.dirname(self.path) or "." fd, tmp_path = tempfile.mkstemp( prefix=f".{os.path.basename(self.path)}.", @@ -135,7 +136,7 @@ class AtomicJsonStore: ) try: with os.fdopen(fd, "w", encoding="utf-8") as f: - self._write_payload(payload, f) + f.write(text) f.flush() self._best_effort_fsync(f.fileno()) os.replace(tmp_path, self.path) @@ -148,6 +149,10 @@ class AtomicJsonStore: raise def _direct_write(self, payload: dict[str, Any]) -> None: + # Serialize before truncating so a serialization failure never destroys + # the existing state file (the atomic path gets this for free via its + # temp file). + text = self._serialize(payload) # Create with 0o600 so the fallback keeps the owner-only permissions the # atomic path gets from mkstemp; state files can contain URLs and # per-download option overrides that must not leak on shared mounts. @@ -161,7 +166,7 @@ class AtomicJsonStore: os.fchmod(f.fileno(), 0o600) except OSError: pass - self._write_payload(payload, f) + f.write(text) f.flush() self._best_effort_fsync(f.fileno()) @@ -179,9 +184,8 @@ class AtomicJsonStore: raise @staticmethod - def _write_payload(payload: dict[str, Any], f: Any) -> None: - json.dump(payload, f, ensure_ascii=False, separators=(",", ":")) - f.write("\n") + def _serialize(payload: dict[str, Any]) -> str: + return json.dumps(payload, ensure_ascii=False, separators=(",", ":")) + "\n" def _warn_direct_write_fallback(self, exc: OSError) -> None: if self._direct_write_fallback_warned: diff --git a/app/tests/test_state_store.py b/app/tests/test_state_store.py index 82e3e30..15e0880 100644 --- a/app/tests/test_state_store.py +++ b/app/tests/test_state_store.py @@ -134,6 +134,23 @@ class StateStoreTests(unittest.TestCase): # Existing state is untouched. self.assertEqual(store.load()["items"], [{"key": "good"}]) + def test_serialization_failure_preserves_existing_state(self): + with tempfile.TemporaryDirectory() as tmp: + path = os.path.join(tmp, "queue.json") + store = AtomicJsonStore(path, kind="persistent_queue:queue") + store.save({"items": [{"key": "good"}]}) + + # Even on the fallback path, a non-serializable payload must raise + # before the existing good state file is touched. + with patch( + "state_store.tempfile.mkstemp", + side_effect=PermissionError(1, "Operation not permitted"), + ): + with self.assertRaises(TypeError): + store.save({"items": object()}) + + self.assertEqual(store.load()["items"], [{"key": "good"}]) + def test_invalid_file_is_quarantined(self): with tempfile.TemporaryDirectory() as tmp: path = os.path.join(tmp, "queue.json") From 54463baf0ea8d27dcef1f6be18f42346d06014f8 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Sun, 5 Jul 2026 04:11:12 -0700 Subject: [PATCH 75/88] fix: fsync parent dir after direct-write fallback for durability parity --- app/state_store.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/state_store.py b/app/state_store.py index 57ead6c..940a882 100644 --- a/app/state_store.py +++ b/app/state_store.py @@ -169,6 +169,8 @@ class AtomicJsonStore: f.write(text) f.flush() self._best_effort_fsync(f.fileno()) + # Make the new directory entry durable too, matching the atomic path. + self._fsync_directory(os.path.dirname(self.path) or ".") @staticmethod def _best_effort_fsync(fileno: int) -> None: From ad90609c9b64f31dc89ffc229071d29c233e4002 Mon Sep 17 00:00:00 2001 From: Markus Lanthaler Date: Wed, 8 Jul 2026 22:54:33 +0000 Subject: [PATCH 76/88] Don't mark a subscription as broken just because all entries are filtered out as they have already been downloaded This happens if archive.txt is used --- app/subscriptions.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/app/subscriptions.py b/app/subscriptions.py index 9a3c4a3..b2662d5 100644 --- a/app/subscriptions.py +++ b/app/subscriptions.py @@ -717,7 +717,7 @@ class SubscriptionManager: entries = [ent for ent in entries if _is_media_entry(ent)] etype = (info or {}).get("_type") or "video" - if etype == "video" or not entries: + if etype == "video": async with self._lock: cur = self._subs.get(sid) if cur: @@ -732,6 +732,22 @@ class SubscriptionManager: log.warning("Subscription %s no longer resolves to a subscribable feed", sub.name) await self.notifier.subscription_updated(sub) return + if not entries: + async with self._lock: + cur = self._subs.get(sid) + if cur: + previous = copy.deepcopy(cur) + cur.last_checked = time.time() + cur.error = None + try: + self._save_locked() + except Exception: + self._subs[sid] = previous + raise + sub = cur + log.warning("Subscription check finished for %s: No entries found", sub.name) + await self.notifier.subscription_updated(sub) + return async with self._lock: cur = self._subs.get(sid) From c34a18de7abc026651c89efa13e00dccab573b78 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 10 Jul 2026 09:48:48 +0300 Subject: [PATCH 77/88] upgrade dependencies --- ui/package.json | 26 +- ui/pnpm-lock.yaml | 1210 ++++++++++++++++++++------------------------- uv.lock | 297 ++++++----- 3 files changed, 715 insertions(+), 818 deletions(-) diff --git a/ui/package.json b/ui/package.json index ceefd96..a5d75d5 100644 --- a/ui/package.json +++ b/ui/package.json @@ -24,14 +24,14 @@ "packageManager": "pnpm@11.5.2", "private": true, "dependencies": { - "@angular/animations": "^22.0.4", - "@angular/common": "^22.0.4", - "@angular/compiler": "^22.0.4", - "@angular/core": "^22.0.4", - "@angular/forms": "^22.0.4", - "@angular/platform-browser": "^22.0.4", - "@angular/platform-browser-dynamic": "^22.0.4", - "@angular/service-worker": "^22.0.4", + "@angular/animations": "^22.0.6", + "@angular/common": "^22.0.6", + "@angular/compiler": "^22.0.6", + "@angular/core": "^22.0.6", + "@angular/forms": "^22.0.6", + "@angular/platform-browser": "^22.0.6", + "@angular/platform-browser-dynamic": "^22.0.6", + "@angular/service-worker": "^22.0.6", "@fortawesome/angular-fontawesome": "~4.0.0", "@fortawesome/fontawesome-svg-core": "^7.3.0", "@fortawesome/free-brands-svg-icons": "^7.3.0", @@ -49,16 +49,16 @@ }, "devDependencies": { "@angular-eslint/builder": "22.0.0", - "@angular/build": "^22.0.4", - "@angular/cli": "^22.0.4", - "@angular/compiler-cli": "^22.0.4", - "@angular/localize": "^22.0.4", + "@angular/build": "^22.0.5", + "@angular/cli": "^22.0.5", + "@angular/compiler-cli": "^22.0.6", + "@angular/localize": "^22.0.6", "@eslint/js": "^9.39.4", "angular-eslint": "22.0.0", "eslint": "^9.39.4", "jsdom": "^27.4.0", "typescript": "~6.0.3", "typescript-eslint": "8.62.0", - "vitest": "^4.1.9" + "vitest": "^4.1.10" } } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 4fef557..cbb013c 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -9,32 +9,32 @@ importers: .: dependencies: '@angular/animations': - specifier: ^22.0.4 - version: 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^22.0.6 + version: 22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/common': - specifier: ^22.0.4 - version: 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.6 + version: 22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@angular/compiler': - specifier: ^22.0.4 - version: 22.0.4 + specifier: ^22.0.6 + version: 22.0.6 '@angular/core': - specifier: ^22.0.4 - version: 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) + specifier: ^22.0.6 + version: 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) '@angular/forms': - specifier: ^22.0.4 - version: 22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + specifier: ^22.0.6 + version: 22.0.6(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) '@angular/platform-browser': - specifier: ^22.0.4 - version: 22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) + specifier: ^22.0.6 + version: 22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)) '@angular/platform-browser-dynamic': - specifier: ^22.0.4 - version: 22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@22.0.4)(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))) + specifier: ^22.0.6 + version: 22.0.6(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@22.0.6)(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))) '@angular/service-worker': - specifier: ^22.0.4 - version: 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + specifier: ^22.0.6 + version: 22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) '@fortawesome/angular-fontawesome': specifier: ~4.0.0 - version: 4.0.0(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) + version: 4.0.0(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)) '@fortawesome/fontawesome-svg-core': specifier: ^7.3.0 version: 7.3.0 @@ -49,10 +49,10 @@ importers: version: 7.3.0 '@ng-bootstrap/ng-bootstrap': specifier: ^21.0.0 - version: 21.0.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4))(@popperjs/core@2.11.8)(rxjs@7.8.2) + version: 21.0.0(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@22.0.6(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6))(@popperjs/core@2.11.8)(rxjs@7.8.2) '@ng-select/ng-select': specifier: ^23.2.0 - version: 23.2.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) + version: 23.2.0(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@22.0.6(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)) '@popperjs/core': specifier: ^2.11.8 version: 2.11.8 @@ -61,10 +61,10 @@ importers: version: 5.3.8(@popperjs/core@2.11.8) ngx-cookie-service: specifier: ^22.0.0 - version: 22.0.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) + version: 22.0.0(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)) ngx-socket-io: specifier: ~4.10.0 - version: 4.10.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + version: 4.10.0(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) rxjs: specifier: ~7.8.2 version: 7.8.2 @@ -77,25 +77,25 @@ importers: devDependencies: '@angular-eslint/builder': specifier: 22.0.0 - version: 22.0.0(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3) + version: 22.0.0(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3) '@angular/build': - specifier: ^22.0.4 - version: 22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4)(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@types/node@26.0.1)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(postcss@8.5.15)(tslib@2.8.1)(typescript@6.0.3)(vitest@4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0))) + specifier: ^22.0.5 + version: 22.0.5(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6)(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@types/node@26.1.1)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(postcss@8.5.16)(tslib@2.8.1)(typescript@6.0.3)(vitest@4.1.10(@types/node@26.1.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0))) '@angular/cli': - specifier: ^22.0.4 - version: 22.0.4(@types/node@26.0.1)(chokidar@5.0.0) + specifier: ^22.0.5 + version: 22.0.5(@types/node@26.1.1)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ^22.0.4 - version: 22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3) + specifier: ^22.0.6 + version: 22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3) '@angular/localize': - specifier: ^22.0.4 - version: 22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4) + specifier: ^22.0.6 + version: 22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6) '@eslint/js': specifier: ^9.39.4 version: 9.39.4 angular-eslint: specifier: 22.0.0 - version: 22.0.0(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.62.0(eslint@9.39.4)(typescript@6.0.3))(typescript@6.0.3) + version: 22.0.0(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.62.0(eslint@9.39.4)(typescript@6.0.3))(typescript@6.0.3) eslint: specifier: ^9.39.4 version: 9.39.4 @@ -109,8 +109,8 @@ importers: specifier: 8.62.0 version: 8.62.0(eslint@9.39.4)(typescript@6.0.3) vitest: - specifier: ^4.1.9 - version: 4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0)) + specifier: ^4.1.10 + version: 4.1.10(@types/node@26.1.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0)) packages: @@ -177,13 +177,13 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.2200.4': - resolution: {integrity: sha512-X/iEQiZ0pRmpjUt11jCM+mtOLRl4XxI9hnM0IC9aAcsm5AzRBb9WY6QIEqOSficjxC/+MI7MGwrerrcP6QN8UA==} + '@angular-devkit/architect@0.2200.5': + resolution: {integrity: sha512-Fbki7xEx37gn8uH5AFOh0EmfGV4y5xag2fTv1vn6KxY/h33L02KuAjiUX3h2YmSjnYSc1dXto3Q5c4lWs/I5Zw==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular-devkit/core@22.0.4': - resolution: {integrity: sha512-zA2UJSMAU3su5uJTOn5ul/gCLRcw6/uIQ6EC5v/Ju/ePjgDIw9R3y3MAvWQ4Ibi/fXiq0FVxpF8hE7RUclYmJA==} + '@angular-devkit/core@22.0.5': + resolution: {integrity: sha512-xxd3b9X0bbdGuYqDj1OgxtlGotrb/gsxQ5+4aqivWBHq4q/1RI8JfVB7gqcoYTvAfTq63Dwsk7Qd33hC4yJ1Wg==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: chokidar: ^5.0.0 @@ -191,8 +191,8 @@ packages: chokidar: optional: true - '@angular-devkit/schematics@22.0.4': - resolution: {integrity: sha512-VRxL1hD/Q3TQglM6EfQ0ksAW4OIvtKyZgtaUpyGsJRfD6tGmLFn7MDnmyyq1ceLX/Clq+3tzH/wN0tF6rcE0jA==} + '@angular-devkit/schematics@22.0.5': + resolution: {integrity: sha512-yKkImZuFLIbylmk1REyoW3EWQeqnr6qwtrzs4EPeaJgzjj+MZurq5slQ3fanK9lfX2q+W+dnSQFKBXHxlUGJ8Q==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@angular-eslint/builder@22.0.0': @@ -239,15 +239,15 @@ packages: eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular/animations@22.0.4': - resolution: {integrity: sha512-drb9ndon6J+t6Do72zdIRZWbmkaCovExXtvwY5f/4dTlXcvLCTPtf//02wn0fn6zq9oUUWISu6MHU9gZ6Cqwwg==} + '@angular/animations@22.0.6': + resolution: {integrity: sha512-5bBAuKtawVK4KPq6U/yoE5yCyKymEnXNN7UJ1Lb7PUuuYmLnUXb7zJpC7HwtHTuV/0PTCkGlLE59NtcrrolTuA==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} deprecated: '@angular/animations is deprecated. Use `animate.enter` and `animate.leave` instead. For more information see: https://v22.angular.dev/guide/animations.' peerDependencies: - '@angular/core': 22.0.4 + '@angular/core': 22.0.6 - '@angular/build@22.0.4': - resolution: {integrity: sha512-hst/KhP5mMPajY32l7qmyW/5fuqrMHCjHEeNhMMNqP82+j8jPBFfMEL26AH9w2kIIdKGpC3WKN5JgLotyFD7tQ==} + '@angular/build@22.0.5': + resolution: {integrity: sha512-uVXf1cSKTMDvpiP5x0X8h7D7dICHC2XsbmvRYtxdyTRy+zJduqtCdZTwnwmCrwIc5hOF+bmwa5p17P6XirYVfw==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler': ^22.0.0 @@ -257,7 +257,7 @@ packages: '@angular/platform-browser': ^22.0.0 '@angular/platform-server': ^22.0.0 '@angular/service-worker': ^22.0.0 - '@angular/ssr': ^22.0.4 + '@angular/ssr': ^22.0.5 istanbul-lib-instrument: ^6.0.0 karma: ^6.4.0 less: ^4.2.0 @@ -295,38 +295,38 @@ packages: vitest: optional: true - '@angular/cli@22.0.4': - resolution: {integrity: sha512-3eJy6VoNlgskKFzvqy3AJsYXFRhSBLLGObF2iTpJymsukuxWUen7hlVVVWrO5++bW1LEgd6PTCCD5fdT2UuRiA==} + '@angular/cli@22.0.5': + resolution: {integrity: sha512-BYg2dWsbqdbpv3UVLNtDns1DoUbsROR8htPPIJEETj4Nfu7VF+g0nnM1CX2Ia+3002xtASgHp4YnRGQN/vAhWA==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular/common@22.0.4': - resolution: {integrity: sha512-ibHINcvajoXfKfb4aGdLx8aOFkUOadAYF9/Yy4j2C1gQdLS6uXOCPFD9+W9zWf8OeMhL0H+i+5KeNlh6XbGaZw==} + '@angular/common@22.0.6': + resolution: {integrity: sha512-+sC0W0FWGeZ/HKUhueCBX8kXdFQDlStbUSK6WF/32igfNYR/PdUrp2L5bU7q654yjxnMzFWDAB+Uvzv6YI/cQg==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/core': 22.0.4 + '@angular/core': 22.0.6 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@22.0.4': - resolution: {integrity: sha512-UmZRpw3pStRmH3ZOLg3BfyGaTxu/aqL00U1eoE3yfsUea25zJLU+gC4vx3NYtHzYKQu84wX3gTZXhLuuYYTa8w==} + '@angular/compiler-cli@22.0.6': + resolution: {integrity: sha512-HPiceCIEyIcTmOuIFiyqu04kbLoJhSUavcOy/kzJkJ2OPXtPvGKaimC4lgs0C86uFy4P6rj6PDITBe3AVHkMTA==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/compiler': 22.0.4 + '@angular/compiler': 22.0.6 typescript: '>=6.0 <6.1' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@22.0.4': - resolution: {integrity: sha512-PvbsPr3d0OHm90vgq1v+0oPP/oHNuCcZQAhhD3fhHlx3DTxf6nkjUqBulzdJ6d5QwDsy378pwf8+dpGquFzbGQ==} + '@angular/compiler@22.0.6': + resolution: {integrity: sha512-iFHsXOA14TsxK4jowmGfogI9Jb0ApL3z8EGTGvKMialqtw088HYjwJP1LT4OlO/wgmjSTq2peZ19jLPuhevNvw==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} - '@angular/core@22.0.4': - resolution: {integrity: sha512-QTGwZcnc6dBhGPMID8DvwRQdHMvpqV0vXDX8jSdmVDiWDsFUjbe9AUFTHEizDm+wnmxOrCQ0DHkgmHICuh0GpA==} + '@angular/core@22.0.6': + resolution: {integrity: sha512-9AYGxmZlJhjLSepZaF/cn1LTC9xRHpUPtXlyyJdauTb4f77X/P0kVdmADztgF1Qy5oMd1V9UKebAe81gmqOH2g==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/compiler': 22.0.4 + '@angular/compiler': 22.0.6 rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 || ~0.16.0 peerDependenciesMeta: @@ -335,50 +335,50 @@ packages: zone.js: optional: true - '@angular/forms@22.0.4': - resolution: {integrity: sha512-MId7Dcxl2/i/ztvf3sJggFBNOqR+btHfolEMRruqdD+o9b+eq/U3Yu0+4FQls1yJ84Lk75C5H6sN1Pc7kzNVgw==} + '@angular/forms@22.0.6': + resolution: {integrity: sha512-pZGMk1TvT+m8z2ZGm90W0sBlo6fhtMAA2baZbMt3HbGRmY23vZv1Orv9npX3Z6a9CCNDY3XBMBvkuj8g7vde9Q==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/common': 22.0.4 - '@angular/core': 22.0.4 - '@angular/platform-browser': 22.0.4 + '@angular/common': 22.0.6 + '@angular/core': 22.0.6 + '@angular/platform-browser': 22.0.6 rxjs: ^6.5.3 || ^7.4.0 - '@angular/localize@22.0.4': - resolution: {integrity: sha512-BJELtGDcTqPjNn10pUa5Ly0Zv+yOyhNOhMl7OA7izRwgK0bWCqgTRnCFh9OWuvn+KyTLfsd9HKVxSDTmtJYEJQ==} + '@angular/localize@22.0.6': + resolution: {integrity: sha512-A4z/DD5IqqqiFUzb8tEStRX0zFu+ZvydVhLMOb9Zjun+n3h8hclEjIlK2fa1Gg/kuLy7DN8At2HOHz1BKCNHlA==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/compiler': 22.0.4 - '@angular/compiler-cli': 22.0.4 + '@angular/compiler': 22.0.6 + '@angular/compiler-cli': 22.0.6 - '@angular/platform-browser-dynamic@22.0.4': - resolution: {integrity: sha512-AzXItNWzxLwKw/xST4TsB/HlPPe1i7OXvs+dGud3SYBlEMRiJFoz0BBQHNLM4aHmDgrTJFwSpzboG/Nub/ckFg==} + '@angular/platform-browser-dynamic@22.0.6': + resolution: {integrity: sha512-VKf8mg+BviBp3wlElZYxWFuXtMLQqd8rfm6jEzwP1Emaz2MCx69Y54dXopD81KJCqRwTmJUVxOjoubqUOlJzRw==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} deprecated: '@angular/platform-browser-dynamic is deprecated. Use `@angular/platform-browser` instead.' peerDependencies: - '@angular/common': 22.0.4 - '@angular/compiler': 22.0.4 - '@angular/core': 22.0.4 - '@angular/platform-browser': 22.0.4 + '@angular/common': 22.0.6 + '@angular/compiler': 22.0.6 + '@angular/core': 22.0.6 + '@angular/platform-browser': 22.0.6 - '@angular/platform-browser@22.0.4': - resolution: {integrity: sha512-lkl8ZIydRsMKb5PMIpVizKmOWzk7fQRmjQMjvLIW733M2OgpEpfYMohzsBVop6oaRImG6Row5uBBSu1j4adGzg==} + '@angular/platform-browser@22.0.6': + resolution: {integrity: sha512-DyyYRx4jXlfaenf0mwsp9jcIak/c17QCFgJKA7OauSkQnk3dL+voMYEwdigfnTIUdw+2Y9wYDywLeuM6NychNQ==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/animations': 22.0.4 - '@angular/common': 22.0.4 - '@angular/core': 22.0.4 + '@angular/animations': 22.0.6 + '@angular/common': 22.0.6 + '@angular/core': 22.0.6 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/service-worker@22.0.4': - resolution: {integrity: sha512-b4nkSvGci/SqnJdl7ga81fBlOfQcK2e/FBExoJbcQeu4YO1fxbd7OZiFe3T1NWPjwVk6fe0GIfVB9oaUCUwSQw==} + '@angular/service-worker@22.0.6': + resolution: {integrity: sha512-s0zE2MFVptHNd21c5sD2VXd9Dp5x6cgyDjIePKjRsfiILegX3i6pAj9xeZ6y160dsrLVmkHST+uY+oxOVMVUhg==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/core': 22.0.4 + '@angular/core': 22.0.6 rxjs: ^6.5.3 || ^7.4.0 '@asamuzakjp/css-color@4.1.2': @@ -493,8 +493,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.5': - resolution: {integrity: sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==} + '@csstools/css-syntax-patches-for-csstree@1.1.6': + resolution: {integrity: sha512-TcJCWFbXLPpJYq6z7bfOyjWYJDiDg2/I4gyUC9pqPNqHFRIey0EB0q0L5cSnQDfWJg8Jd6VadakxdIez/3zkqQ==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: @@ -1435,279 +1435,141 @@ packages: cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.62.2': - resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} - cpu: [arm] - os: [android] - '@rollup/rollup-android-arm64@4.60.2': resolution: {integrity: sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==} cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.62.2': - resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} - cpu: [arm64] - os: [android] - '@rollup/rollup-darwin-arm64@4.60.2': resolution: {integrity: sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.62.2': - resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} - cpu: [arm64] - os: [darwin] - '@rollup/rollup-darwin-x64@4.60.2': resolution: {integrity: sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==} cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.62.2': - resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} - cpu: [x64] - os: [darwin] - '@rollup/rollup-freebsd-arm64@4.60.2': resolution: {integrity: sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.62.2': - resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} - cpu: [arm64] - os: [freebsd] - '@rollup/rollup-freebsd-x64@4.60.2': resolution: {integrity: sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.62.2': - resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} - cpu: [x64] - os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.60.2': resolution: {integrity: sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-gnueabihf@4.62.2': - resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} - cpu: [arm] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.60.2': resolution: {integrity: sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm-musleabihf@4.62.2': - resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} - cpu: [arm] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.60.2': resolution: {integrity: sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-gnu@4.62.2': - resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.60.2': resolution: {integrity: sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-musl@4.62.2': - resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} - cpu: [arm64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.60.2': resolution: {integrity: sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-gnu@4.62.2': - resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} - cpu: [loong64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.60.2': resolution: {integrity: sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-musl@4.62.2': - resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} - cpu: [loong64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.60.2': resolution: {integrity: sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-gnu@4.62.2': - resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.60.2': resolution: {integrity: sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-musl@4.62.2': - resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} - cpu: [ppc64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.60.2': resolution: {integrity: sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.62.2': - resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.60.2': resolution: {integrity: sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-musl@4.62.2': - resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} - cpu: [riscv64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.60.2': resolution: {integrity: sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-s390x-gnu@4.62.2': - resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} - cpu: [s390x] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.60.2': resolution: {integrity: sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.62.2': - resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} - cpu: [x64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.60.2': resolution: {integrity: sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-linux-x64-musl@4.62.2': - resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} - cpu: [x64] - os: [linux] - libc: [musl] - '@rollup/rollup-openbsd-x64@4.60.2': resolution: {integrity: sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openbsd-x64@4.62.2': - resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} - cpu: [x64] - os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.60.2': resolution: {integrity: sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-openharmony-arm64@4.62.2': - resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} - cpu: [arm64] - os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.60.2': resolution: {integrity: sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.62.2': - resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} - cpu: [arm64] - os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.2': resolution: {integrity: sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.62.2': - resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} - cpu: [ia32] - os: [win32] - '@rollup/rollup-win32-x64-gnu@4.60.2': resolution: {integrity: sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.62.2': - resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.2': resolution: {integrity: sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.62.2': - resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} - cpu: [x64] - os: [win32] - - '@schematics/angular@22.0.4': - resolution: {integrity: sha512-P3V3tkqIR+n0GJSv0ibf34/zMtKbFp6kaTjBe5cm/RyXuHbmdaPYjk8PNphkGMypDtWCof1RtNnW/hl832Wnew==} + '@schematics/angular@22.0.5': + resolution: {integrity: sha512-e+1Ob5Kxt9QH2Gige0Bl56CXclG819RMl5Fso3kwO58+9AQnR2QxvjbetB7LDRl+YEG3TXCE4s3vdrV4gqRRAQ==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@sigstore/bundle@4.0.0': @@ -1781,8 +1643,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@26.0.1': - resolution: {integrity: sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==} + '@types/node@26.1.1': + resolution: {integrity: sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==} '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} @@ -1808,16 +1670,32 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/project-service@8.63.0': + resolution: {integrity: sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/scope-manager@8.62.0': resolution: {integrity: sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.63.0': + resolution: {integrity: sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/tsconfig-utils@8.62.0': resolution: {integrity: sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/tsconfig-utils@8.63.0': + resolution: {integrity: sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/type-utils@8.62.0': resolution: {integrity: sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1829,12 +1707,22 @@ packages: resolution: {integrity: sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.63.0': + resolution: {integrity: sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.62.0': resolution: {integrity: sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/typescript-estree@8.63.0': + resolution: {integrity: sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@8.62.0': resolution: {integrity: sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1842,21 +1730,32 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/utils@8.63.0': + resolution: {integrity: sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + '@typescript-eslint/visitor-keys@8.62.0': resolution: {integrity: sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.63.0': + resolution: {integrity: sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitejs/plugin-basic-ssl@2.3.0': resolution: {integrity: sha512-bdyo8rB3NnQbikdMpHaML9Z1OZPBu6fFOBo+OtxsBlvMJtysWskmBcnbIDhUqgC8tcxNv/a+BcV5U+2nQMm1OQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} peerDependencies: vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - '@vitest/expect@4.1.9': - resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} + '@vitest/expect@4.1.10': + resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} - '@vitest/mocker@4.1.9': - resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==} + '@vitest/mocker@4.1.10': + resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1866,20 +1765,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.9': - resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==} + '@vitest/pretty-format@4.1.10': + resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} - '@vitest/runner@4.1.9': - resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==} + '@vitest/runner@4.1.10': + resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} - '@vitest/snapshot@4.1.9': - resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==} + '@vitest/snapshot@4.1.10': + resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} - '@vitest/spy@4.1.9': - resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==} + '@vitest/spy@4.1.10': + resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} - '@vitest/utils@4.1.9': - resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} + '@vitest/utils@4.1.10': + resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} '@yarnpkg/lockfile@1.1.0': resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} @@ -1982,8 +1881,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.10.40: - resolution: {integrity: sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==} + baseline-browser-mapping@2.10.42: + resolution: {integrity: sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==} engines: {node: '>=6.0.0'} hasBin: true @@ -2006,15 +1905,15 @@ packages: peerDependencies: '@popperjs/core': ^2.11.8 - brace-expansion@1.1.15: - resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} + brace-expansion@1.1.16: + resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==} - brace-expansion@5.0.6: - resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + brace-expansion@5.0.7: + resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} engines: {node: 18 || 20 || >=22} - browserslist@4.28.4: - resolution: {integrity: sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==} + browserslist@4.28.5: + resolution: {integrity: sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -2041,8 +1940,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001799: - resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} + caniuse-lite@1.0.30001803: + resolution: {integrity: sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -2200,8 +2099,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.380: - resolution: {integrity: sha512-W6d5AbuEoRayO447cqrg6lKJIlscgRnnxOZl/08kfV71BQDoEBC7Wwis68z87LjyK6f4kWyTaubuDbhHKrZkbA==} + electron-to-chromium@1.5.389: + resolution: {integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2249,8 +2148,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@2.1.0: - resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + es-module-lexer@2.3.0: + resolution: {integrity: sha512-KLdwQm2NvGLDkQDCGvmiQrhkd0JbMzXthwQAUgWjQuQdBLFa3eiBP5arXZyA+f8x+x7OXgud6bq2rxjGtHV2tw==} es-object-atoms@1.1.2: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} @@ -2377,8 +2276,8 @@ packages: fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-uri@3.1.2: - resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fast-uri@3.1.3: + resolution: {integrity: sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==} fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} @@ -2485,8 +2384,8 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} - hono@4.12.27: - resolution: {integrity: sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==} + hono@4.12.28: + resolution: {integrity: sha512-YwUvVpSF7m1yOblFPrU3Hbo8XhPheBoiyfGuII6z19LnOr6JpDnyyp7LFNrfV56wS8tpvtBFGRISHN02pDdLOA==} engines: {node: '>=16.9.0'} hosted-git-info@9.0.3: @@ -2519,8 +2418,8 @@ packages: resolution: {integrity: sha512-/MVmHp58WkOypgFhCLk4fzpPcFQvTJ/e6LBI7irpIO2HfxUbpmYoHF+KzipzJpxxzJu7aJNWQ0xojJ/dzV2G5g==} engines: {node: '>= 20'} - iconv-lite@0.7.2: - resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} ignore-walk@8.0.0: @@ -2535,8 +2434,8 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - immutable@5.1.8: - resolution: {integrity: sha512-TM5YqrGeTsVIPPpILzeqZ8D2Zc2TvNgSDi88zPF2a4cyqQdWV/wVWBDRDbNzzrLeRWScrFcOX9lW2iX6GOtUDw==} + immutable@5.1.9: + resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -2687,8 +2586,8 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} - lru-cache@11.5.1: - resolution: {integrity: sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==} + lru-cache@11.5.2: + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -2919,8 +2818,8 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-map@7.0.4: - resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} + p-map@7.0.5: + resolution: {integrity: sha512-e8vJF4XdVkzqqSHguEMz41mQO1wKwxKm5ENrUJQUu9kLDCtn83cxbyHZcszr4QC5zEA7WffRRC4gsTecC7J9oA==} engines: {node: '>=18'} pacote@21.5.1: @@ -2970,6 +2869,10 @@ packages: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + piscina@5.2.0: resolution: {integrity: sha512-DszUCKeVN/5G5QKo6jAVHL8fmKnkJvQ0ACiVgY7YGCq3TUB2oznAOayvZPIAdEThvhczkXR+qm3IHsNXpFCYfA==} engines: {node: '>=20.x'} @@ -2987,8 +2890,8 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss@8.5.15: - resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + postcss@8.5.16: + resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -3050,11 +2953,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rollup@4.62.2: - resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - router@2.2.0: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} @@ -3209,8 +3107,8 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - std-env@4.1.0: - resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} stdin-discarder@0.3.2: resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} @@ -3220,8 +3118,8 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} - string-width@8.2.1: - resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} + string-width@8.2.2: + resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} engines: {node: '>=20'} strip-ansi@7.2.0: @@ -3239,8 +3137,8 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tar@7.5.17: - resolution: {integrity: sha512-wPEBwzapC+2PaTYPH6e2L+cNOEE227S47wUYFqlegcs8zlLLmeb9Fcff1HVZY4Fwku/1Eyv38n7GYwB2aaS71g==} + tar@7.5.19: + resolution: {integrity: sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==} engines: {node: '>=18'} tinybench@2.9.0: @@ -3262,19 +3160,19 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tldts-core@7.4.4: - resolution: {integrity: sha512-vwVLJVvvpslm7vqAH7+XNj/neA/Ynq7DT2EEcMuwc5YzN5XaMyRAqxwU+uX3azZ1FQtB2gvrvnLnAEkvYlVdfg==} + tldts-core@7.4.7: + resolution: {integrity: sha512-rNlAI8fKn/JckBMUSbNL/ES2kmDiurWaE49l+ikwEc9A6lFR7gMx9AhgQMQKBK4H5w4pKLH64JzZfB99uRsGNQ==} - tldts@7.4.4: - resolution: {integrity: sha512-kFXFK7O4WPextIUAOk8qtnw9dxR9UIXP9CjuH1cTBVBZMDeQcUPgr/IazGiw1B0Yiw5L75gHLWeW4iD793r90g==} + tldts@7.4.7: + resolution: {integrity: sha512-56L0/9HELHSsG1bFCzay8UoLxzRL7kpFf7Wl5q/kSYwiSJGACvro61xnKzPNM+SadxllzdtXsKDSXE7HPeqIAw==} hasBin: true toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - tough-cookie@6.0.1: - resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} + tough-cookie@6.0.2: + resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} engines: {node: '>=16'} tr46@6.0.0: @@ -3382,20 +3280,20 @@ packages: yaml: optional: true - vitest@4.1.9: - resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==} + vitest@4.1.10: + resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.9 - '@vitest/browser-preview': 4.1.9 - '@vitest/browser-webdriverio': 4.1.9 - '@vitest/coverage-istanbul': 4.1.9 - '@vitest/coverage-v8': 4.1.9 - '@vitest/ui': 4.1.9 + '@vitest/browser-playwright': 4.1.10 + '@vitest/browser-preview': 4.1.10 + '@vitest/browser-webdriverio': 4.1.10 + '@vitest/coverage-istanbul': 4.1.10 + '@vitest/coverage-v8': 4.1.10 + '@vitest/ui': 4.1.10 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -3541,6 +3439,9 @@ packages: zod@4.4.2: resolution: {integrity: sha512-IynmDyxsEsb9RKzO3J9+4SxXnl2FTFSzNBaKKaMV6tsSk0rw9gYw9gs+JFCq/qk2LCZ78KDwyj+Z289TijSkUw==} + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + zone.js@0.15.0: resolution: {integrity: sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA==} @@ -3637,14 +3538,14 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.2200.4(chokidar@5.0.0)': + '@angular-devkit/architect@0.2200.5(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 22.0.4(chokidar@5.0.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/core@22.0.4(chokidar@5.0.0)': + '@angular-devkit/core@22.0.5(chokidar@5.0.0)': dependencies: ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) @@ -3655,9 +3556,9 @@ snapshots: optionalDependencies: chokidar: 5.0.0 - '@angular-devkit/schematics@22.0.4(chokidar@5.0.0)': + '@angular-devkit/schematics@22.0.5(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 22.0.4(chokidar@5.0.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) jsonc-parser: 3.3.1 magic-string: 0.30.21 ora: 9.4.0 @@ -3665,11 +3566,11 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-eslint/builder@22.0.0(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3)': + '@angular-eslint/builder@22.0.0(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3)': dependencies: - '@angular-devkit/architect': 0.2200.4(chokidar@5.0.0) - '@angular-devkit/core': 22.0.4(chokidar@5.0.0) - '@angular/cli': 22.0.4(@types/node@26.0.1)(chokidar@5.0.0) + '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular/cli': 22.0.5(@types/node@26.1.1)(chokidar@5.0.0) eslint: 9.39.4 typescript: 6.0.3 transitivePeerDependencies: @@ -3677,34 +3578,34 @@ snapshots: '@angular-eslint/bundled-angular-compiler@22.0.0': {} - '@angular-eslint/eslint-plugin-template@22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': + '@angular-eslint/eslint-plugin-template@22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@typescript-eslint/types@8.63.0)(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.0.0 '@angular-eslint/template-parser': 22.0.0(eslint@9.39.4)(typescript@6.0.3) - '@angular-eslint/utils': 22.0.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) - '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) + '@angular-eslint/utils': 22.0.0(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/utils': 8.63.0(eslint@9.39.4)(typescript@6.0.3) aria-query: 5.3.2 axobject-query: 4.1.0 eslint: 9.39.4 typescript: 6.0.3 - '@angular-eslint/eslint-plugin@22.0.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': + '@angular-eslint/eslint-plugin@22.0.0(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.0.0 - '@angular-eslint/utils': 22.0.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) - '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) + '@angular-eslint/utils': 22.0.0(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/utils': 8.63.0(eslint@9.39.4)(typescript@6.0.3) eslint: 9.39.4 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 - '@angular-eslint/schematics@22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3)': + '@angular-eslint/schematics@22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(@typescript-eslint/types@8.63.0)(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3)': dependencies: - '@angular-devkit/core': 22.0.4(chokidar@5.0.0) - '@angular-devkit/schematics': 22.0.4(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 22.0.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) - '@angular-eslint/eslint-plugin-template': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) - '@angular/cli': 22.0.4(@types/node@26.0.1)(chokidar@5.0.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) + '@angular-eslint/eslint-plugin': 22.0.0(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) + '@angular-eslint/eslint-plugin-template': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@typescript-eslint/types@8.63.0)(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) + '@angular/cli': 22.0.5(@types/node@26.1.1)(chokidar@5.0.0) ignore: 7.0.5 semver: 7.8.0 strip-json-comments: 3.1.1 @@ -3723,31 +3624,31 @@ snapshots: eslint-scope: 9.1.2 typescript: 6.0.3 - '@angular-eslint/utils@22.0.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': + '@angular-eslint/utils@22.0.0(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.0.0 - '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/utils': 8.63.0(eslint@9.39.4)(typescript@6.0.3) eslint: 9.39.4 typescript: 6.0.3 - '@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - '@angular/build@22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4)(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@types/node@26.0.1)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(postcss@8.5.15)(tslib@2.8.1)(typescript@6.0.3)(vitest@4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0)))': + '@angular/build@22.0.5(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6)(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@types/node@26.1.1)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(postcss@8.5.16)(tslib@2.8.1)(typescript@6.0.3)(vitest@4.1.10(@types/node@26.1.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0)))': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2200.4(chokidar@5.0.0) - '@angular/compiler': 22.0.4 - '@angular/compiler-cli': 22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3) + '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) + '@angular/compiler': 22.0.6 + '@angular/compiler-cli': 22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3) '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 6.0.12(@types/node@26.0.1) - '@vitejs/plugin-basic-ssl': 2.3.0(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0)) + '@inquirer/confirm': 6.0.12(@types/node@26.1.1) + '@vitejs/plugin-basic-ssl': 2.3.0(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0)) beasties: 0.4.2 - browserslist: 4.28.4 + browserslist: 4.28.5 esbuild: 0.28.1 https-proxy-agent: 9.0.0 jsonc-parser: 3.3.1 @@ -3764,17 +3665,17 @@ snapshots: tinyglobby: 0.2.16 tslib: 2.8.1 typescript: 6.0.3 - vite: 7.3.5(@types/node@26.0.1)(sass@1.99.0) + vite: 7.3.5(@types/node@26.1.1)(sass@1.99.0) watchpack: 2.5.1 optionalDependencies: - '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/localize': 22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4) - '@angular/platform-browser': 22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/localize': 22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6) + '@angular/platform-browser': 22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/service-worker': 22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) istanbul-lib-instrument: 6.0.3 lmdb: 3.5.4 - postcss: 8.5.15 - vitest: 4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0)) + postcss: 8.5.16 + vitest: 4.1.10(@types/node@26.1.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0)) transitivePeerDependencies: - '@types/node' - chokidar @@ -3788,15 +3689,15 @@ snapshots: - tsx - yaml - '@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0)': + '@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2200.4(chokidar@5.0.0) - '@angular-devkit/core': 22.0.4(chokidar@5.0.0) - '@angular-devkit/schematics': 22.0.4(chokidar@5.0.0) - '@inquirer/prompts': 8.4.2(@types/node@26.0.1) - '@listr2/prompt-adapter-inquirer': 4.2.3(@inquirer/prompts@8.4.2(@types/node@26.0.1))(@types/node@26.0.1)(listr2@10.2.1) + '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) + '@inquirer/prompts': 8.4.2(@types/node@26.1.1) + '@listr2/prompt-adapter-inquirer': 4.2.3(@inquirer/prompts@8.4.2(@types/node@26.1.1))(@types/node@26.1.1)(listr2@10.2.1) '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.2) - '@schematics/angular': 22.0.4(chokidar@5.0.0) + '@schematics/angular': 22.0.5(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 algoliasearch: 5.52.0 ini: 6.0.0 @@ -3814,15 +3715,15 @@ snapshots: - chokidar - supports-color - '@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3)': + '@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3)': dependencies: - '@angular/compiler': 22.0.4 + '@angular/compiler': 22.0.6 '@babel/core': 7.29.7 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 5.0.0 @@ -3836,32 +3737,32 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/compiler@22.0.4': + '@angular/compiler@22.0.6': dependencies: tslib: 2.8.1 - '@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)': + '@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 22.0.4 + '@angular/compiler': 22.0.6 zone.js: 0.15.0 - '@angular/forms@22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': + '@angular/forms@22.0.6(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2)': dependencies: - '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)) '@standard-schema/spec': 1.1.0 rxjs: 7.8.2 tslib: 2.8.1 - zod: 4.4.2 + zod: 4.4.3 - '@angular/localize@22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4)': + '@angular/localize@22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6)': dependencies: - '@angular/compiler': 22.0.4 - '@angular/compiler-cli': 22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3) + '@angular/compiler': 22.0.6 + '@angular/compiler-cli': 22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3) '@babel/core': 7.29.7 '@types/babel__core': 7.20.5 tinyglobby: 0.2.17 @@ -3869,25 +3770,25 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/platform-browser-dynamic@22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@22.0.4)(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))': + '@angular/platform-browser-dynamic@22.0.6(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/compiler@22.0.6)(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))': dependencies: - '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/compiler': 22.0.4 - '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/platform-browser': 22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/common': 22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/compiler': 22.0.6 + '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/platform-browser': 22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)) tslib: 2.8.1 - '@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))': + '@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 optionalDependencies: - '@angular/animations': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)) + '@angular/animations': 22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)) - '@angular/service-worker@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': + '@angular/service-worker@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2)': dependencies: - '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) rxjs: 7.8.2 tslib: 2.8.1 @@ -3897,7 +3798,7 @@ snapshots: '@csstools/css-color-parser': 4.1.9(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - lru-cache: 11.5.1 + lru-cache: 11.5.2 '@asamuzakjp/dom-selector@6.8.1': dependencies: @@ -3905,7 +3806,7 @@ snapshots: bidi-js: 1.0.3 css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 - lru-cache: 11.5.1 + lru-cache: 11.5.2 '@asamuzakjp/nwsapi@2.3.9': {} @@ -3973,7 +3874,7 @@ snapshots: dependencies: '@babel/compat-data': 7.29.7 '@babel/helper-validator-option': 7.29.7 - browserslist: 4.28.4 + browserslist: 4.28.5 lru-cache: 5.1.1 semver: 6.3.1 @@ -4064,7 +3965,7 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.1.5(css-tree@3.2.1)': + '@csstools/css-syntax-patches-for-csstree@1.1.6(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 @@ -4274,9 +4175,9 @@ snapshots: '@exodus/bytes@1.15.1': {} - '@fortawesome/angular-fontawesome@4.0.0(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))': + '@fortawesome/angular-fontawesome@4.0.0(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: - '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) '@fortawesome/fontawesome-svg-core': 7.3.0 tslib: 2.8.1 @@ -4303,9 +4204,9 @@ snapshots: '@harperfast/extended-iterable@1.0.3': optional: true - '@hono/node-server@1.19.14(hono@4.12.27)': + '@hono/node-server@1.19.14(hono@4.12.28)': dependencies: - hono: 4.12.27 + hono: 4.12.28 '@humanfs/core@0.19.2': dependencies: @@ -4325,129 +4226,129 @@ snapshots: '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@5.2.1(@types/node@26.0.1)': + '@inquirer/checkbox@5.2.1(@types/node@26.1.1)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.1.1) '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.0.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 - '@inquirer/confirm@6.0.12(@types/node@26.0.1)': + '@inquirer/confirm@6.0.12(@types/node@26.1.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.0.1) - '@inquirer/type': 4.0.7(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 - '@inquirer/confirm@6.1.1(@types/node@26.0.1)': + '@inquirer/confirm@6.1.1(@types/node@26.1.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.0.1) - '@inquirer/type': 4.0.7(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 - '@inquirer/core@11.2.1(@types/node@26.0.1)': + '@inquirer/core@11.2.1(@types/node@26.1.1)': dependencies: '@inquirer/ansi': 2.0.7 '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.0.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) cli-width: 4.1.0 fast-wrap-ansi: 0.2.2 mute-stream: 3.0.0 signal-exit: 4.1.0 optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 - '@inquirer/editor@5.2.2(@types/node@26.0.1)': + '@inquirer/editor@5.2.2(@types/node@26.1.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.0.1) - '@inquirer/external-editor': 3.0.3(@types/node@26.0.1) - '@inquirer/type': 4.0.7(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/external-editor': 3.0.3(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 - '@inquirer/expand@5.1.1(@types/node@26.0.1)': + '@inquirer/expand@5.1.1(@types/node@26.1.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.0.1) - '@inquirer/type': 4.0.7(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 - '@inquirer/external-editor@3.0.3(@types/node@26.0.1)': + '@inquirer/external-editor@3.0.3(@types/node@26.1.1)': dependencies: chardet: 2.2.0 - iconv-lite: 0.7.2 + iconv-lite: 0.7.3 optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 '@inquirer/figures@2.0.7': {} - '@inquirer/input@5.1.2(@types/node@26.0.1)': + '@inquirer/input@5.1.2(@types/node@26.1.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.0.1) - '@inquirer/type': 4.0.7(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 - '@inquirer/number@4.1.1(@types/node@26.0.1)': + '@inquirer/number@4.1.1(@types/node@26.1.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.0.1) - '@inquirer/type': 4.0.7(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 - '@inquirer/password@5.1.1(@types/node@26.0.1)': + '@inquirer/password@5.1.1(@types/node@26.1.1)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@26.0.1) - '@inquirer/type': 4.0.7(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 - '@inquirer/prompts@8.4.2(@types/node@26.0.1)': + '@inquirer/prompts@8.4.2(@types/node@26.1.1)': dependencies: - '@inquirer/checkbox': 5.2.1(@types/node@26.0.1) - '@inquirer/confirm': 6.1.1(@types/node@26.0.1) - '@inquirer/editor': 5.2.2(@types/node@26.0.1) - '@inquirer/expand': 5.1.1(@types/node@26.0.1) - '@inquirer/input': 5.1.2(@types/node@26.0.1) - '@inquirer/number': 4.1.1(@types/node@26.0.1) - '@inquirer/password': 5.1.1(@types/node@26.0.1) - '@inquirer/rawlist': 5.3.1(@types/node@26.0.1) - '@inquirer/search': 4.2.1(@types/node@26.0.1) - '@inquirer/select': 5.2.1(@types/node@26.0.1) + '@inquirer/checkbox': 5.2.1(@types/node@26.1.1) + '@inquirer/confirm': 6.1.1(@types/node@26.1.1) + '@inquirer/editor': 5.2.2(@types/node@26.1.1) + '@inquirer/expand': 5.1.1(@types/node@26.1.1) + '@inquirer/input': 5.1.2(@types/node@26.1.1) + '@inquirer/number': 4.1.1(@types/node@26.1.1) + '@inquirer/password': 5.1.1(@types/node@26.1.1) + '@inquirer/rawlist': 5.3.1(@types/node@26.1.1) + '@inquirer/search': 4.2.1(@types/node@26.1.1) + '@inquirer/select': 5.2.1(@types/node@26.1.1) optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 - '@inquirer/rawlist@5.3.1(@types/node@26.0.1)': + '@inquirer/rawlist@5.3.1(@types/node@26.1.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.0.1) - '@inquirer/type': 4.0.7(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 - '@inquirer/search@4.2.1(@types/node@26.0.1)': + '@inquirer/search@4.2.1(@types/node@26.1.1)': dependencies: - '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.1.1) '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.0.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 - '@inquirer/select@5.2.1(@types/node@26.0.1)': + '@inquirer/select@5.2.1(@types/node@26.1.1)': dependencies: '@inquirer/ansi': 2.0.7 - '@inquirer/core': 11.2.1(@types/node@26.0.1) + '@inquirer/core': 11.2.1(@types/node@26.1.1) '@inquirer/figures': 2.0.7 - '@inquirer/type': 4.0.7(@types/node@26.0.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 - '@inquirer/type@4.0.7(@types/node@26.0.1)': + '@inquirer/type@4.0.7(@types/node@26.1.1)': optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 '@isaacs/fs-minipass@4.0.1': dependencies: @@ -4475,10 +4376,10 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@listr2/prompt-adapter-inquirer@4.2.3(@inquirer/prompts@8.4.2(@types/node@26.0.1))(@types/node@26.0.1)(listr2@10.2.1)': + '@listr2/prompt-adapter-inquirer@4.2.3(@inquirer/prompts@8.4.2(@types/node@26.1.1))(@types/node@26.1.1)(listr2@10.2.1)': dependencies: - '@inquirer/prompts': 8.4.2(@types/node@26.0.1) - '@inquirer/type': 4.0.7(@types/node@26.0.1) + '@inquirer/prompts': 8.4.2(@types/node@26.1.1) + '@inquirer/type': 4.0.7(@types/node@26.1.1) listr2: 10.2.1 transitivePeerDependencies: - '@types/node' @@ -4506,7 +4407,7 @@ snapshots: '@modelcontextprotocol/sdk@1.29.0(zod@4.4.2)': dependencies: - '@hono/node-server': 1.19.14(hono@4.12.27) + '@hono/node-server': 1.19.14(hono@4.12.28) ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) content-type: 1.0.5 @@ -4516,7 +4417,7 @@ snapshots: eventsource-parser: 3.1.0 express: 5.2.1 express-rate-limit: 8.5.2(express@5.2.1) - hono: 4.12.27 + hono: 4.12.28 jose: 6.2.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -4616,21 +4517,21 @@ snapshots: '@napi-rs/nice-win32-x64-msvc': 1.1.1 optional: true - '@ng-bootstrap/ng-bootstrap@21.0.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4))(@popperjs/core@2.11.8)(rxjs@7.8.2)': + '@ng-bootstrap/ng-bootstrap@21.0.0(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@22.0.6(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6))(@popperjs/core@2.11.8)(rxjs@7.8.2)': dependencies: - '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) - '@angular/localize': 22.0.4(@angular/compiler-cli@22.0.4(@angular/compiler@22.0.4)(typescript@6.0.3))(@angular/compiler@22.0.4) + '@angular/common': 22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 22.0.6(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/localize': 22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6) '@popperjs/core': 2.11.8 rxjs: 7.8.2 tslib: 2.8.1 - '@ng-select/ng-select@23.2.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': + '@ng-select/ng-select@23.2.0(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@22.0.6(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))': dependencies: - '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) - '@angular/forms': 22.0.4(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.4(@angular/animations@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) + '@angular/common': 22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/forms': 22.0.6(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2) tslib: 2.8.1 '@npmcli/agent@4.0.2': @@ -4638,24 +4539,24 @@ snapshots: agent-base: 7.1.4 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 - lru-cache: 11.5.1 + lru-cache: 11.5.2 socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color '@npmcli/fs@5.0.0': dependencies: - semver: 7.8.5 + semver: 7.7.4 '@npmcli/git@7.0.2': dependencies: '@gar/promise-retry': 1.0.3 '@npmcli/promise-spawn': 9.0.1 ini: 6.0.0 - lru-cache: 11.5.1 + lru-cache: 11.5.2 npm-pick-manifest: 11.0.3 proc-log: 6.1.0 - semver: 7.8.5 + semver: 7.7.4 which: 6.0.1 '@npmcli/installed-package-contents@4.0.0': @@ -4672,7 +4573,7 @@ snapshots: hosted-git-info: 9.0.3 json-parse-even-better-errors: 5.0.0 proc-log: 6.1.0 - semver: 7.8.5 + semver: 7.7.4 spdx-expression-parse: 4.0.0 '@npmcli/promise-spawn@9.0.1': @@ -4755,157 +4656,82 @@ snapshots: '@rollup/rollup-android-arm-eabi@4.60.2': optional: true - '@rollup/rollup-android-arm-eabi@4.62.2': - optional: true - '@rollup/rollup-android-arm64@4.60.2': optional: true - '@rollup/rollup-android-arm64@4.62.2': - optional: true - '@rollup/rollup-darwin-arm64@4.60.2': optional: true - '@rollup/rollup-darwin-arm64@4.62.2': - optional: true - '@rollup/rollup-darwin-x64@4.60.2': optional: true - '@rollup/rollup-darwin-x64@4.62.2': - optional: true - '@rollup/rollup-freebsd-arm64@4.60.2': optional: true - '@rollup/rollup-freebsd-arm64@4.62.2': - optional: true - '@rollup/rollup-freebsd-x64@4.60.2': optional: true - '@rollup/rollup-freebsd-x64@4.62.2': - optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.62.2': - optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.62.2': - optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.62.2': - optional: true - '@rollup/rollup-linux-arm64-musl@4.60.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.62.2': - optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.2': optional: true - '@rollup/rollup-linux-loong64-gnu@4.62.2': - optional: true - '@rollup/rollup-linux-loong64-musl@4.60.2': optional: true - '@rollup/rollup-linux-loong64-musl@4.62.2': - optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.2': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.62.2': - optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.2': optional: true - '@rollup/rollup-linux-ppc64-musl@4.62.2': - optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.62.2': - optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.62.2': - optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.62.2': - optional: true - '@rollup/rollup-linux-x64-gnu@4.60.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.62.2': - optional: true - '@rollup/rollup-linux-x64-musl@4.60.2': optional: true - '@rollup/rollup-linux-x64-musl@4.62.2': - optional: true - '@rollup/rollup-openbsd-x64@4.60.2': optional: true - '@rollup/rollup-openbsd-x64@4.62.2': - optional: true - '@rollup/rollup-openharmony-arm64@4.60.2': optional: true - '@rollup/rollup-openharmony-arm64@4.62.2': - optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.62.2': - optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.62.2': - optional: true - '@rollup/rollup-win32-x64-gnu@4.60.2': optional: true - '@rollup/rollup-win32-x64-gnu@4.62.2': - optional: true - '@rollup/rollup-win32-x64-msvc@4.60.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.62.2': - optional: true - - '@schematics/angular@22.0.4(chokidar@5.0.0)': + '@schematics/angular@22.0.5(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 22.0.4(chokidar@5.0.0) - '@angular-devkit/schematics': 22.0.4(chokidar@5.0.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) jsonc-parser: 3.3.1 typescript: 6.0.3 transitivePeerDependencies: @@ -4982,7 +4808,7 @@ snapshots: '@types/cors@2.8.19': dependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 '@types/deep-eql@4.0.2': {} @@ -4994,13 +4820,13 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@26.0.1': + '@types/node@26.1.1': dependencies: undici-types: 8.3.0 '@types/ws@8.18.1': dependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 '@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': dependencies: @@ -5039,15 +4865,33 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.63.0(typescript@6.0.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.63.0(typescript@6.0.3) + '@typescript-eslint/types': 8.63.0 + debug: 4.4.3 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.62.0': dependencies: '@typescript-eslint/types': 8.62.0 '@typescript-eslint/visitor-keys': 8.62.0 + '@typescript-eslint/scope-manager@8.63.0': + dependencies: + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/visitor-keys': 8.63.0 + '@typescript-eslint/tsconfig-utils@8.62.0(typescript@6.0.3)': dependencies: typescript: 6.0.3 + '@typescript-eslint/tsconfig-utils@8.63.0(typescript@6.0.3)': + dependencies: + typescript: 6.0.3 + '@typescript-eslint/type-utils@8.62.0(eslint@9.39.4)(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.62.0 @@ -5062,6 +4906,8 @@ snapshots: '@typescript-eslint/types@8.62.0': {} + '@typescript-eslint/types@8.63.0': {} + '@typescript-eslint/typescript-estree@8.62.0(typescript@6.0.3)': dependencies: '@typescript-eslint/project-service': 8.62.0(typescript@6.0.3) @@ -5077,6 +4923,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.63.0(typescript@6.0.3)': + dependencies: + '@typescript-eslint/project-service': 8.63.0(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.63.0(typescript@6.0.3) + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/visitor-keys': 8.63.0 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) @@ -5088,53 +4949,69 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/typescript-estree': 8.63.0(typescript@6.0.3) + eslint: 9.39.4 + typescript: 6.0.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@8.62.0': dependencies: '@typescript-eslint/types': 8.62.0 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-basic-ssl@2.3.0(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0))': + '@typescript-eslint/visitor-keys@8.63.0': dependencies: - vite: 7.3.5(@types/node@26.0.1)(sass@1.99.0) + '@typescript-eslint/types': 8.63.0 + eslint-visitor-keys: 5.0.1 - '@vitest/expect@4.1.9': + '@vitejs/plugin-basic-ssl@2.3.0(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0))': + dependencies: + vite: 7.3.5(@types/node@26.1.1)(sass@1.99.0) + + '@vitest/expect@4.1.10': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.9 - '@vitest/utils': 4.1.9 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.9(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0))': + '@vitest/mocker@4.1.10(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0))': dependencies: - '@vitest/spy': 4.1.9 + '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.5(@types/node@26.0.1)(sass@1.99.0) + vite: 7.3.5(@types/node@26.1.1)(sass@1.99.0) - '@vitest/pretty-format@4.1.9': + '@vitest/pretty-format@4.1.10': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.9': + '@vitest/runner@4.1.10': dependencies: - '@vitest/utils': 4.1.9 + '@vitest/utils': 4.1.10 pathe: 2.0.3 - '@vitest/snapshot@4.1.9': + '@vitest/snapshot@4.1.10': dependencies: - '@vitest/pretty-format': 4.1.9 - '@vitest/utils': 4.1.9 + '@vitest/pretty-format': 4.1.10 + '@vitest/utils': 4.1.10 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.9': {} + '@vitest/spy@4.1.10': {} - '@vitest/utils@4.1.9': + '@vitest/utils@4.1.10': dependencies: - '@vitest/pretty-format': 4.1.9 + '@vitest/pretty-format': 4.1.10 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -5176,7 +5053,7 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.2 + fast-uri: 3.1.3 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 @@ -5197,18 +5074,18 @@ snapshots: '@algolia/requester-fetch': 5.52.0 '@algolia/requester-node-http': 5.52.0 - angular-eslint@22.0.0(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.62.0(eslint@9.39.4)(typescript@6.0.3))(typescript@6.0.3): + angular-eslint@22.0.0(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.62.0(eslint@9.39.4)(typescript@6.0.3))(typescript@6.0.3): dependencies: - '@angular-devkit/core': 22.0.4(chokidar@5.0.0) - '@angular-devkit/schematics': 22.0.4(chokidar@5.0.0) - '@angular-eslint/builder': 22.0.0(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3) - '@angular-eslint/eslint-plugin': 22.0.0(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) - '@angular-eslint/eslint-plugin-template': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) - '@angular-eslint/schematics': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@angular/cli@22.0.4(@types/node@26.0.1)(chokidar@5.0.0))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) + '@angular-eslint/builder': 22.0.0(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3) + '@angular-eslint/eslint-plugin': 22.0.0(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) + '@angular-eslint/eslint-plugin-template': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@typescript-eslint/types@8.63.0)(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) + '@angular-eslint/schematics': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(@typescript-eslint/types@8.63.0)(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3) '@angular-eslint/template-parser': 22.0.0(eslint@9.39.4)(typescript@6.0.3) - '@angular/cli': 22.0.4(@types/node@26.0.1)(chokidar@5.0.0) - '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) + '@angular/cli': 22.0.5(@types/node@26.1.1)(chokidar@5.0.0) + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/utils': 8.63.0(eslint@9.39.4)(typescript@6.0.3) eslint: 9.39.4 typescript: 6.0.3 typescript-eslint: 8.62.0(eslint@9.39.4)(typescript@6.0.3) @@ -5242,7 +5119,7 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.10.40: {} + baseline-browser-mapping@2.10.42: {} beasties@0.4.2: dependencies: @@ -5252,9 +5129,9 @@ snapshots: domhandler: 5.0.3 htmlparser2: 10.1.0 picocolors: 1.1.1 - postcss: 8.5.15 + postcss: 8.5.16 postcss-media-query-parser: 0.2.3 - postcss-safe-parser: 7.0.1(postcss@8.5.15) + postcss-safe-parser: 7.0.1(postcss@8.5.16) bidi-js@1.0.3: dependencies: @@ -5266,7 +5143,7 @@ snapshots: content-type: 2.0.0 debug: 4.4.3 http-errors: 2.0.1 - iconv-lite: 0.7.2 + iconv-lite: 0.7.3 on-finished: 2.4.1 qs: 6.15.3 raw-body: 3.0.2 @@ -5280,22 +5157,22 @@ snapshots: dependencies: '@popperjs/core': 2.11.8 - brace-expansion@1.1.15: + brace-expansion@1.1.16: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@5.0.6: + brace-expansion@5.0.7: dependencies: balanced-match: 4.0.4 - browserslist@4.28.4: + browserslist@4.28.5: dependencies: - baseline-browser-mapping: 2.10.40 - caniuse-lite: 1.0.30001799 - electron-to-chromium: 1.5.380 + baseline-browser-mapping: 2.10.42 + caniuse-lite: 1.0.30001803 + electron-to-chromium: 1.5.389 node-releases: 2.0.50 - update-browserslist-db: 1.2.3(browserslist@4.28.4) + update-browserslist-db: 1.2.3(browserslist@4.28.5) buffer-from@1.1.2: {} @@ -5306,12 +5183,12 @@ snapshots: '@npmcli/fs': 5.0.0 fs-minipass: 3.0.3 glob: 13.0.6 - lru-cache: 11.5.1 + lru-cache: 11.5.2 minipass: 7.1.3 minipass-collect: 2.0.1 minipass-flush: 1.0.7 minipass-pipeline: 1.2.4 - p-map: 7.0.4 + p-map: 7.0.5 ssri: 13.0.1 call-bind-apply-helpers@1.0.2: @@ -5326,7 +5203,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001799: {} + caniuse-lite@1.0.30001803: {} chai@6.2.2: {} @@ -5358,7 +5235,7 @@ snapshots: cli-truncate@5.2.0: dependencies: slice-ansi: 8.0.0 - string-width: 8.2.1 + string-width: 8.2.2 cli-width@4.1.0: {} @@ -5421,9 +5298,9 @@ snapshots: cssstyle@5.3.7: dependencies: '@asamuzakjp/css-color': 4.1.2 - '@csstools/css-syntax-patches-for-csstree': 1.1.5(css-tree@3.2.1) + '@csstools/css-syntax-patches-for-csstree': 1.1.6(css-tree@3.2.1) css-tree: 3.2.1 - lru-cache: 11.5.1 + lru-cache: 11.5.2 data-urls@6.0.1: dependencies: @@ -5469,7 +5346,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.380: {} + electron-to-chromium@1.5.389: {} emoji-regex@10.6.0: {} @@ -5492,7 +5369,7 @@ snapshots: engine.io@6.6.9: dependencies: '@types/cors': 2.8.19 - '@types/node': 26.0.1 + '@types/node': 26.1.1 '@types/ws': 8.18.1 accepts: 1.3.8 base64id: 2.0.0 @@ -5520,7 +5397,7 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@2.1.0: {} + es-module-lexer@2.3.0: {} es-object-atoms@1.1.2: dependencies: @@ -5733,7 +5610,7 @@ snapshots: dependencies: fast-string-truncated-width: 3.0.3 - fast-uri@3.1.2: {} + fast-uri@3.1.3: {} fast-wrap-ansi@0.2.2: dependencies: @@ -5743,6 +5620,10 @@ snapshots: optionalDependencies: picomatch: 4.0.4 + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -5833,11 +5714,11 @@ snapshots: dependencies: function-bind: 1.1.2 - hono@4.12.27: {} + hono@4.12.28: {} hosted-git-info@9.0.3: dependencies: - lru-cache: 11.5.1 + lru-cache: 11.5.2 html-encoding-sniffer@6.0.0: dependencies: @@ -5883,7 +5764,7 @@ snapshots: transitivePeerDependencies: - supports-color - iconv-lite@0.7.2: + iconv-lite@0.7.3: dependencies: safer-buffer: 2.1.2 @@ -5895,7 +5776,7 @@ snapshots: ignore@7.0.5: {} - immutable@5.1.8: {} + immutable@5.1.9: {} import-fresh@3.3.1: dependencies: @@ -5971,7 +5852,7 @@ snapshots: parse5: 8.0.1 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 6.0.1 + tough-cookie: 6.0.2 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 4.0.0 @@ -6058,7 +5939,7 @@ snapshots: strip-ansi: 7.2.0 wrap-ansi: 9.0.2 - lru-cache@11.5.1: {} + lru-cache@11.5.2: {} lru-cache@5.1.1: dependencies: @@ -6109,11 +5990,11 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.6 + brace-expansion: 5.0.7 minimatch@3.1.5: dependencies: - brace-expansion: 1.1.15 + brace-expansion: 1.1.16 minipass-collect@2.0.1: dependencies: @@ -6125,7 +6006,7 @@ snapshots: minipass-sized: 2.0.0 minizlib: 3.1.0 optionalDependencies: - iconv-lite: 0.7.2 + iconv-lite: 0.7.3 minipass-flush@1.0.7: dependencies: @@ -6180,16 +6061,16 @@ snapshots: negotiator@1.0.0: {} - ngx-cookie-service@22.0.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0)): + ngx-cookie-service@22.0.0(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)): dependencies: - '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - ngx-socket-io@4.10.0(@angular/common@22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): + ngx-socket-io@4.10.0(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2): dependencies: - '@angular/common': 22.0.4(@angular/core@22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) - '@angular/core': 22.0.4(@angular/compiler@22.0.4)(rxjs@7.8.2)(zone.js@0.15.0) + '@angular/common': 22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) + '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) core-js: 3.49.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 @@ -6219,8 +6100,8 @@ snapshots: graceful-fs: 4.2.11 nopt: 9.0.0 proc-log: 6.1.0 - semver: 7.8.5 - tar: 7.5.17 + semver: 7.7.4 + tar: 7.5.19 tinyglobby: 0.2.17 undici: 6.27.0 which: 6.0.1 @@ -6237,7 +6118,7 @@ snapshots: npm-install-checks@8.0.0: dependencies: - semver: 7.8.5 + semver: 7.7.4 npm-normalize-package-bin@5.0.0: {} @@ -6245,7 +6126,7 @@ snapshots: dependencies: hosted-git-info: 9.0.3 proc-log: 6.1.0 - semver: 7.8.5 + semver: 7.7.4 validate-npm-package-name: 7.0.2 npm-packlist@10.0.4: @@ -6258,7 +6139,7 @@ snapshots: npm-install-checks: 8.0.0 npm-normalize-package-bin: 5.0.0 npm-package-arg: 13.0.2 - semver: 7.8.5 + semver: 7.7.4 npm-registry-fetch@19.1.1: dependencies: @@ -6313,7 +6194,7 @@ snapshots: is-unicode-supported: 2.1.0 log-symbols: 7.0.1 stdin-discarder: 0.3.2 - string-width: 8.2.1 + string-width: 8.2.2 ordered-binary@1.6.1: optional: true @@ -6326,7 +6207,7 @@ snapshots: dependencies: p-limit: 3.1.0 - p-map@7.0.4: {} + p-map@7.0.5: {} pacote@21.5.1: dependencies: @@ -6346,7 +6227,7 @@ snapshots: proc-log: 6.1.0 sigstore: 4.1.1 ssri: 13.0.1 - tar: 7.5.17 + tar: 7.5.19 transitivePeerDependencies: - supports-color @@ -6376,7 +6257,7 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.5.1 + lru-cache: 11.5.2 minipass: 7.1.3 path-to-regexp@8.4.2: {} @@ -6387,6 +6268,8 @@ snapshots: picomatch@4.0.4: {} + picomatch@4.0.5: {} + piscina@5.2.0: optionalDependencies: '@napi-rs/nice': 1.1.1 @@ -6395,11 +6278,11 @@ snapshots: postcss-media-query-parser@0.2.3: {} - postcss-safe-parser@7.0.1(postcss@8.5.15): + postcss-safe-parser@7.0.1(postcss@8.5.16): dependencies: - postcss: 8.5.15 + postcss: 8.5.16 - postcss@8.5.15: + postcss@8.5.16: dependencies: nanoid: 3.3.15 picocolors: 1.1.1 @@ -6427,7 +6310,7 @@ snapshots: dependencies: bytes: 3.1.2 http-errors: 2.0.1 - iconv-lite: 0.7.2 + iconv-lite: 0.7.3 unpipe: 1.0.0 readdirp@4.1.2: {} @@ -6478,37 +6361,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.60.2 fsevents: 2.3.3 - rollup@4.62.2: - dependencies: - '@types/estree': 1.0.9 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.62.2 - '@rollup/rollup-android-arm64': 4.62.2 - '@rollup/rollup-darwin-arm64': 4.62.2 - '@rollup/rollup-darwin-x64': 4.62.2 - '@rollup/rollup-freebsd-arm64': 4.62.2 - '@rollup/rollup-freebsd-x64': 4.62.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 - '@rollup/rollup-linux-arm-musleabihf': 4.62.2 - '@rollup/rollup-linux-arm64-gnu': 4.62.2 - '@rollup/rollup-linux-arm64-musl': 4.62.2 - '@rollup/rollup-linux-loong64-gnu': 4.62.2 - '@rollup/rollup-linux-loong64-musl': 4.62.2 - '@rollup/rollup-linux-ppc64-gnu': 4.62.2 - '@rollup/rollup-linux-ppc64-musl': 4.62.2 - '@rollup/rollup-linux-riscv64-gnu': 4.62.2 - '@rollup/rollup-linux-riscv64-musl': 4.62.2 - '@rollup/rollup-linux-s390x-gnu': 4.62.2 - '@rollup/rollup-linux-x64-gnu': 4.62.2 - '@rollup/rollup-linux-x64-musl': 4.62.2 - '@rollup/rollup-openbsd-x64': 4.62.2 - '@rollup/rollup-openharmony-arm64': 4.62.2 - '@rollup/rollup-win32-arm64-msvc': 4.62.2 - '@rollup/rollup-win32-ia32-msvc': 4.62.2 - '@rollup/rollup-win32-x64-gnu': 4.62.2 - '@rollup/rollup-win32-x64-msvc': 4.62.2 - fsevents: 2.3.3 - router@2.2.0: dependencies: debug: 4.4.3 @@ -6528,7 +6380,7 @@ snapshots: sass@1.99.0: dependencies: chokidar: 4.0.3 - immutable: 5.1.8 + immutable: 5.1.9 source-map-js: 1.2.1 optionalDependencies: '@parcel/watcher': 2.5.6 @@ -6715,7 +6567,7 @@ snapshots: statuses@2.0.2: {} - std-env@4.1.0: {} + std-env@4.2.0: {} stdin-discarder@0.3.2: {} @@ -6725,7 +6577,7 @@ snapshots: get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 - string-width@8.2.1: + string-width@8.2.2: dependencies: get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 @@ -6742,7 +6594,7 @@ snapshots: symbol-tree@3.2.4: {} - tar@7.5.17: + tar@7.5.19: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 @@ -6761,22 +6613,22 @@ snapshots: tinyglobby@0.2.17: dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 tinyrainbow@3.1.0: {} - tldts-core@7.4.4: {} + tldts-core@7.4.7: {} - tldts@7.4.4: + tldts@7.4.7: dependencies: - tldts-core: 7.4.4 + tldts-core: 7.4.7 toidentifier@1.0.1: {} - tough-cookie@6.0.1: + tough-cookie@6.0.2: dependencies: - tldts: 7.4.4 + tldts: 7.4.7 tr46@6.0.0: dependencies: @@ -6825,9 +6677,9 @@ snapshots: unpipe@1.0.0: {} - update-browserslist-db@1.2.3(browserslist@4.28.4): + update-browserslist-db@1.2.3(browserslist@4.28.5): dependencies: - browserslist: 4.28.4 + browserslist: 4.28.5 escalade: 3.2.0 picocolors: 1.1.1 @@ -6839,43 +6691,43 @@ snapshots: vary@1.1.2: {} - vite@7.3.5(@types/node@26.0.1)(sass@1.99.0): + vite@7.3.5(@types/node@26.1.1)(sass@1.99.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.15 - rollup: 4.62.2 - tinyglobby: 0.2.17 + postcss: 8.5.16 + rollup: 4.60.2 + tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 fsevents: 2.3.3 sass: 1.99.0 - vitest@4.1.9(@types/node@26.0.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0)): + vitest@4.1.10(@types/node@26.1.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0)): dependencies: - '@vitest/expect': 4.1.9 - '@vitest/mocker': 4.1.9(vite@7.3.5(@types/node@26.0.1)(sass@1.99.0)) - '@vitest/pretty-format': 4.1.9 - '@vitest/runner': 4.1.9 - '@vitest/snapshot': 4.1.9 - '@vitest/spy': 4.1.9 - '@vitest/utils': 4.1.9 - es-module-lexer: 2.1.0 + '@vitest/expect': 4.1.10 + '@vitest/mocker': 4.1.10(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0)) + '@vitest/pretty-format': 4.1.10 + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + es-module-lexer: 2.3.0 expect-type: 1.4.0 magic-string: 0.30.21 obug: 2.1.3 pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 + picomatch: 4.0.5 + std-env: 4.2.0 tinybench: 2.9.0 tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 7.3.5(@types/node@26.0.1)(sass@1.99.0) + vite: 7.3.5(@types/node@26.1.1)(sass@1.99.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 26.0.1 + '@types/node': 26.1.1 jsdom: 27.4.0 transitivePeerDependencies: - msw @@ -6921,7 +6773,7 @@ snapshots: wrap-ansi@10.0.0: dependencies: ansi-styles: 6.2.3 - string-width: 8.2.1 + string-width: 8.2.2 strip-ansi: 7.2.0 wrap-ansi@9.0.2: @@ -6969,4 +6821,6 @@ snapshots: zod@4.4.2: {} + zod@4.4.3: {} + zone.js@0.15.0: {} diff --git a/uv.lock b/uv.lock index 045d60f..afaba4b 100644 --- a/uv.lock +++ b/uv.lock @@ -4,11 +4,11 @@ requires-python = ">=3.13" [[package]] name = "aiohappyeyeballs" -version = "2.6.2" +version = "2.7.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/33/c6/61a2d7b7572279226bb2e7f61d7a19ca7c90da0329c93fa0d560cbf288d8/aiohappyeyeballs-2.6.2.tar.gz", hash = "sha256:e202810ee718bd01fc6ef49e8ea53d023d5cb6b581076d7925aa499fa55dbe64", size = 22591, upload-time = "2026-05-20T15:12:24.631Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/f4/eec0465c2f67b2664688d0240b3212d5196fd89e741df67ddb81f8d35658/aiohappyeyeballs-2.7.1.tar.gz", hash = "sha256:065665c041c42a5938ed220bdcd7230f22527fbec085e1853d2402c8a3615d9d", size = 24757, upload-time = "2026-07-01T17:11:55.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/fc/a7bf5b6e4e617b45f90f2d9d2a68519c249c81dd4fc2658c7a2a61c4f4b7/aiohappyeyeballs-2.6.2-py3-none-any.whl", hash = "sha256:4708045e2d7a6c6bdf8aafa8ed39649eaf926a4543b54560659129e3365953c4", size = 15062, upload-time = "2026-05-20T15:12:23.328Z" }, + { url = "https://files.pythonhosted.org/packages/71/43/1947f06babed6b3f1d7f38b0c767f52df66bfb2bc10b468c4a7de9eceff2/aiohappyeyeballs-2.7.1-py3-none-any.whl", hash = "sha256:9243213661e29250eb41368e5daa826fc017156c3b8a11440826b2e3ed376472", size = 15038, upload-time = "2026-07-01T17:11:54.055Z" }, ] [[package]] @@ -203,104 +203,123 @@ wheels = [ [[package]] name = "cffi" -version = "2.0.0" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "pycparser", marker = "implementation_name != 'PyPy'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +sdist = { url = "https://files.pythonhosted.org/packages/57/5f/ff100cae70ebe9d8df1c01a00e510e45d9adb5c1fdda84791b199141de97/cffi-2.1.0.tar.gz", hash = "sha256:efc1cdd798b1aaf39b4610bba7aad28c9bea9b910f25c784ccf9ec1fa719d1f9", size = 531036, upload-time = "2026-07-06T21:34:30.382Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/4b/8d/a0a47a0c9e413a658623d014e91e74a50cdd2c423f7ccfd44086ef767f90/cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb", size = 185230, upload-time = "2025-09-08T23:23:00.879Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d2/a6c0296814556c68ee32009d9c2ad4f85f2707cdecfd7727951ec228005d/cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca", size = 181043, upload-time = "2025-09-08T23:23:02.231Z" }, - { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, - { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, - { url = "https://files.pythonhosted.org/packages/f2/7f/e6647792fc5850d634695bc0e6ab4111ae88e89981d35ac269956605feba/cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2", size = 207948, upload-time = "2025-09-08T23:23:06.127Z" }, - { url = "https://files.pythonhosted.org/packages/cb/1e/a5a1bd6f1fb30f22573f76533de12a00bf274abcdc55c8edab639078abb6/cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3", size = 206422, upload-time = "2025-09-08T23:23:07.753Z" }, - { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, - { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, - { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, - { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, - { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, - { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, - { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, - { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, - { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, - { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, - { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, - { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, - { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, - { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, - { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, - { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, - { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, - { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, - { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, - { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, - { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, - { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, - { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, - { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, - { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, - { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, - { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, - { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, + { url = "https://files.pythonhosted.org/packages/96/88/a996879e2eeccb815f6e3a5967b12a308257412acec882039d386bd2aa7b/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:10537b1df4967ca26d21e5072d7d54188354483b91dc75058968d3f0cf13fbda", size = 194331, upload-time = "2026-07-06T21:33:03.697Z" }, + { url = "https://files.pythonhosted.org/packages/58/85/7ae00d5c8dd6266f4e944c3db630f3c5c9a98b61d469c714d848b1d8138a/cffi-2.1.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a95b05f9baf29b91171b3a8bd2020b028835243e7b0ff6bb23e2a3c228518b1b", size = 196966, upload-time = "2026-07-06T21:33:05.353Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e9/45c3a76ad8d43ad9261f4c95436da61128d3ca545d72b9612c0ab5be0b1c/cffi-2.1.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:15faec4adfff450819f3aee0e2e02c812de6edb88203aa58807955db2003472a", size = 184795, upload-time = "2026-07-06T21:33:06.699Z" }, + { url = "https://files.pythonhosted.org/packages/84/4c/82f132cb4418ee6d953d982b19191e87e2a6372c8a4ce36e50b69d6ade4a/cffi-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:716ff8ec22f20b4d988b12884086bcef0fc99737043e503f7a3935a6be99b1ea", size = 184746, upload-time = "2026-07-06T21:33:08.071Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1c/4ed5a0e5bdca6cbc275556de3328dd1b76fd0c11cc13c88fe66d1d8715f2/cffi-2.1.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:63960549e4f8dc41e31accb97b975abaecfc44c03e396c093a6436763c2ea7db", size = 214747, upload-time = "2026-07-06T21:33:09.671Z" }, + { url = "https://files.pythonhosted.org/packages/3a/a6/e879bb68cc23a2bc9ba8f4b7d8019f0c2694bad2ab6c4a3701d429439f58/cffi-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ff067a8d8d880e7809e4ac88eb009bb848870115317b306666502ccad30b147f", size = 222392, upload-time = "2026-07-06T21:33:10.896Z" }, + { url = "https://files.pythonhosted.org/packages/88/f6/01890cfd63c08f8eb96a8319b0443690197d240a8bd6346048cf7bde9190/cffi-2.1.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3b926723c13eba9f81d2ef3820d63aeceec3b2d4639906047bf675cb8a7a500d", size = 210285, upload-time = "2026-07-06T21:33:12.251Z" }, + { url = "https://files.pythonhosted.org/packages/a6/cf/2b684132056f438567b61e19d690dd31cd0921ace051e0a458be6074369e/cffi-2.1.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:47ff3a8bfd8cb9da1af7524b965127095055654c177fcfc7578debcb015eecd0", size = 208801, upload-time = "2026-07-06T21:33:13.617Z" }, + { url = "https://files.pythonhosted.org/packages/6f/08/f2e7d62c460faae0926f2d6e423694aa409ced3bc1fe2927a0a6e5f05416/cffi-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:799416bae98336e400981ff6e532d67d5c709cfb30afb79865a1315f94b0e224", size = 221808, upload-time = "2026-07-06T21:33:15.466Z" }, + { url = "https://files.pythonhosted.org/packages/38/37/04f54b8e63a02f3d908332c9effbf8c366167c6f733ed8a3d4f79b7e2a1e/cffi-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:961be50688f7fba2fa65f63712d3b9b341a22311f5253460ce933f52f0de1c8c", size = 225241, upload-time = "2026-07-06T21:33:16.869Z" }, + { url = "https://files.pythonhosted.org/packages/a9/d6/c72eecca433cd3e681c65ed313ab4835d9d4a379704d0f628a6a05f51c2e/cffi-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bf5c6cf48238b0eb4c086978c492ad1cbc22373fc5b2d7353b3a598ce6db887a", size = 223588, upload-time = "2026-07-06T21:33:18.239Z" }, + { url = "https://files.pythonhosted.org/packages/c6/4b/e706f67279140f92939da3475ad610df18bfd52d50f14953a8e5fede71d5/cffi-2.1.0-cp313-cp313-win32.whl", hash = "sha256:db3eb7d46527159a878ec3460e9d40615bc25ba337d477db681aea6e4f05c5d2", size = 175248, upload-time = "2026-07-06T21:33:19.799Z" }, + { url = "https://files.pythonhosted.org/packages/5a/47/59eb7975cb0e4ef0afa764ea945b29a5bb4537a9f771cb7d6c8a5dd74c95/cffi-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:8e74a6135550c4748af665b1b1118b6aab33b1fc6a16f9aff630af107c3b4512", size = 185717, upload-time = "2026-07-06T21:33:21.47Z" }, + { url = "https://files.pythonhosted.org/packages/5a/af/34fee85c48f8d94efc8597bc09470c9dd274c145f1c12e0fbc6ab6d38d74/cffi-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:2282cd5e38aa8accd03e99d1256af8411c84cdbee6a89d841b563fdbd1f3e50f", size = 180114, upload-time = "2026-07-06T21:33:22.515Z" }, + { url = "https://files.pythonhosted.org/packages/d8/f0/81478e482afa03f6d18dc8f2afb5edc45b3080853b634b5ed91961be0998/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d2117334c3af3bdcb9a88522b844a2bdb5efdc4f71c6c822df55486ae1c3347a", size = 194142, upload-time = "2026-07-06T21:33:23.657Z" }, + { url = "https://files.pythonhosted.org/packages/7d/95/8de304305cd9204974b0ca051b86d307cafca13aa575a0ef1b44d92c0d8c/cffi-2.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:702c436735fbe99d59ada02a1f65cfc0d31c0ee8b7290912f8fbc5cd1e4b16c3", size = 196819, upload-time = "2026-07-06T21:33:25.007Z" }, + { url = "https://files.pythonhosted.org/packages/20/71/7c8372d30e42415602ed9f268f7cfd66f1b855fed881ecd168bcb45dbc0b/cffi-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:1ff3456eab0d889592d1936d6125bbfbc7ae4d3354a700f8bd80450a66445d4d", size = 184965, upload-time = "2026-07-06T21:33:26.605Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5c/584e626835f0375c928176c04137c96927165cb8733cdb3150ec04e5ee5e/cffi-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c4165821e131d6d4ca444347c2b694e2311bcfa3fe5a861cc72968f28867beac", size = 184952, upload-time = "2026-07-06T21:33:27.823Z" }, + { url = "https://files.pythonhosted.org/packages/2e/d2/065fcae1c73979fac8e054462478d0ff8a29c40cdc2ed7ea5676a061df53/cffi-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:276f20fffd7b396e12516ba8edf9509210ac248cbbc5acbc39cd512f9f59ebe6", size = 222353, upload-time = "2026-07-06T21:33:29.178Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a5/e8bbb1ce5b3ac2f53ad6a10bde44318a5a8d99d4f4a000d44a6e39aeb3e4/cffi-2.1.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:7d5980a3433d4b71a5e120f9dd551403d7824e31e2e67124fe2769c404c06913", size = 210051, upload-time = "2026-07-06T21:33:30.534Z" }, + { url = "https://files.pythonhosted.org/packages/28/ed/c127d3ac36e899c965e3361357c3befacd6578c03f40125183e41c3b219e/cffi-2.1.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6ca4919c6e4f89aa99c42510b42cf54596892c00b3f9077f6bdd1505e24b9c8d", size = 208630, upload-time = "2026-07-06T21:33:31.753Z" }, + { url = "https://files.pythonhosted.org/packages/cc/d7/97d3136f81db489ec8d1d67748c110d6c994268fd7528014aa9f2b085e4e/cffi-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d53d10f7da99ae46f7373b9150393e9c5eab9b224909982b43832668de4779f5", size = 221593, upload-time = "2026-07-06T21:33:33.044Z" }, + { url = "https://files.pythonhosted.org/packages/d3/27/93195977168ee63aed233a1a0993a2178798654d1f4bddcdd321d6fd3b21/cffi-2.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c351efb95e832a853a29361675f33a7ce53de1a109cd73fd47af0712213aa4ce", size = 225146, upload-time = "2026-07-06T21:33:34.224Z" }, + { url = "https://files.pythonhosted.org/packages/b3/c1/6dbd291ee2ae5a50a034aa057207081f545923bbf15dad4511e985aafff5/cffi-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:dbf7c7a88e2bac086f06d14577332760bdeecc42bdec8ac4077f6260557d9326", size = 223240, upload-time = "2026-07-06T21:33:35.57Z" }, + { url = "https://files.pythonhosted.org/packages/0f/6f/ade5ce9863a57992a6ea3d0d10d7e29b8749fc127204b3d493d667b2815f/cffi-2.1.0-cp314-cp314-win32.whl", hash = "sha256:1854b724d00f6654c742097d5387569021be12d3a0f770eae1df8f8acfcc6acd", size = 177723, upload-time = "2026-07-06T21:33:51.626Z" }, + { url = "https://files.pythonhosted.org/packages/41/de/92b9eeed4ae4a21d6fd9b2a2c8505cbed573299902ea73981cc13f7ff62c/cffi-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:1b96bfe2c4bd825681b7d311ad6d9b7280a091f43e8f63da5729638083cd3bfb", size = 187937, upload-time = "2026-07-06T21:33:53.403Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1a/cc6ae6c2913a03aab8898eee57963cf1035b8df5872ed8b9115fcc7e2be8/cffi-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:7d28dff1db6764108bc30788d85d61c876beff416d9a49cb9dd7c5a9f34f5804", size = 183001, upload-time = "2026-07-06T21:33:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/14/f0/134c00ce0779ec86dea2aa1aac69339c2741a8045072676763512363a2ea/cffi-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7ea6b3e2c4250ff1de21c630fe72d0f63eb95c2c32ffbf64a358cf4a8836d714", size = 188538, upload-time = "2026-07-06T21:33:36.792Z" }, + { url = "https://files.pythonhosted.org/packages/50/d8/3b86aba791cb610d24e8a3e1b2cd529e71fa15096b04e4d4e360049d4a4c/cffi-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6af371f3767faeffc6ac1ef57cdfd25844403e9d3f476c5537caee499de96376", size = 188230, upload-time = "2026-07-06T21:33:38.011Z" }, + { url = "https://files.pythonhosted.org/packages/14/d0/117dcd9209255ad8571fbc8c92ef32593a1d294dcec91ddc4e4db50606f2/cffi-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb4e8997a49aa2c08a3e43c9045d224448b8941d88e7ac163c7d383e560cbf98", size = 223899, upload-time = "2026-07-06T21:33:39.514Z" }, + { url = "https://files.pythonhosted.org/packages/b6/3d/f20f8b886b254e3ad10e15cd4186d3aed49f3e6a35ab37aab9f8f25f7c03/cffi-2.1.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bf01d8c84cbea96b944c73b22182e6c7c432b3475632b8111dbfdc95ddad6e13", size = 211652, upload-time = "2026-07-06T21:33:40.851Z" }, + { url = "https://files.pythonhosted.org/packages/28/3b/fad54de07260b93ddeef4b96d0131d57ea900675df1d410ae1deee52d7a6/cffi-2.1.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:33eb1ad83ebe8f313e0df035c406227d55a79456704a863fad9842136af5ad7d", size = 210755, upload-time = "2026-07-06T21:33:42.183Z" }, + { url = "https://files.pythonhosted.org/packages/cc/82/3d5c705acb7abbba9bbd7d79b8e62e0f25b6120eb7ae6ac49f1b721722fe/cffi-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ac0f1a2d0cfa7eea3f2aaf006ab6e70e8feeb16b75d65b7e5939982ca2f11056", size = 223933, upload-time = "2026-07-06T21:33:43.603Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d0/47e338384ab6b1004241002fa616301020cea4fc95f283506565d252f276/cffi-2.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c16914df9fb7f500e440e6875fa23ff5e0b31db01fa9c06af98d59a91f0dc2e4", size = 226749, upload-time = "2026-07-06T21:33:45.046Z" }, + { url = "https://files.pythonhosted.org/packages/70/25/65bd5b58ea4bfdfc15cde02cb5365f89ef8ab8b2adfb8fe5c4bd4233382f/cffi-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5ecbd0499275d57506d397eebe1981cee87b47fcd9ef5c22cab7ed7644a39a94", size = 225703, upload-time = "2026-07-06T21:33:46.374Z" }, + { url = "https://files.pythonhosted.org/packages/dc/78/aa01ac599a8a4322533d45a1f9bc93b338276d2d59dabbe7c6d92a775c81/cffi-2.1.0-cp314-cp314t-win32.whl", hash = "sha256:7d034dcffa09e9a46c93fa3a3be402096cb5354ac6e41ab8e5cc9cd8b642ad76", size = 182857, upload-time = "2026-07-06T21:33:47.696Z" }, + { url = "https://files.pythonhosted.org/packages/b9/26/d00496b22de4d4228f32dde94ad996f350c8aad676d63bcca0743c8dea4d/cffi-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0582a58f3051372229ca8e7f5f589f9e5632678208d8636fea3676711fdf7fe5", size = 194065, upload-time = "2026-07-06T21:33:48.953Z" }, + { url = "https://files.pythonhosted.org/packages/d5/dd/0c7dbf815a579ff005008a2d815a55d6bb047c349eef536d9dc53d3f0a8d/cffi-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:510aeeeac94811b138077451da1fb18b308a5feab47dd2b603af55804155e1c8", size = 186404, upload-time = "2026-07-06T21:33:50.309Z" }, + { url = "https://files.pythonhosted.org/packages/55/c7/8c8c50cb11c6750051daf12164098a9a6f027ac4356967fd4d800a07f242/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:2e9dabb9abcb7ad15938c7196ad5c1718a4e6d33cc79b4c0209bdb64c4a54a5c", size = 194121, upload-time = "2026-07-06T21:33:56.109Z" }, + { url = "https://files.pythonhosted.org/packages/99/e2/67680bf19a6b60d2bb7ff83baefa2a4c3d2d7dc0f3277034b802e1fc504c/cffi-2.1.0-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:37f525a7e7e50c017fdebe58b787be310ad59357ae43a053943a6e1a6c526001", size = 196820, upload-time = "2026-07-06T21:33:57.288Z" }, + { url = "https://files.pythonhosted.org/packages/ed/da/4bbe583a3b3a5c8c60892124fe17f3fa3656523faf0d3484eae90f091853/cffi-2.1.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:95f2954c2c9473d892eca6e0409f3568b37ab62a8eedb122461f73cc273476e3", size = 184936, upload-time = "2026-07-06T21:33:58.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/4b/1f4c36ab273980d7aa75bb126ea4f8971f24a96108acad3a0a084028c57b/cffi-2.1.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:cdf2448aab5f661c9315308ec8b93f4e8a1a67a3c733f8631067a2b67d5913dc", size = 185045, upload-time = "2026-07-06T21:34:00.085Z" }, + { url = "https://files.pythonhosted.org/packages/ef/c3/ad299dc38f3583f8d916b299f028af418a9ec98bc695fcbebeae7420691c/cffi-2.1.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:90bec57cf82089383bd06a605b3eb8daebf7e5a668520beaf6e327a83a947699", size = 222342, upload-time = "2026-07-06T21:34:01.814Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d8/df4543cc087245044ed02ef3ad8e0a26619d0075ac7a77a12dc81177851b/cffi-2.1.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6274dcb2d15cef48daa73ed1be5a40d501d74dccd0cd6db364776d12cb6ba022", size = 210073, upload-time = "2026-07-06T21:34:03.255Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/fac738d73728c6cea2a88a2883dca54892496cbba88a1dc1f2909cb8a6f5/cffi-2.1.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:2b71d409cccee78310ab5dec549aed052aaea483346e282c7b02362596e01bb0", size = 208551, upload-time = "2026-07-06T21:34:04.433Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3f/0b04a700dd64f465c93020253a793a82c9b4dff9961f48facd0df945d9b8/cffi-2.1.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7d3538f9c0e50670f4deb93dbb696576e60590369cae2faf7de681e597a8a1f1", size = 221649, upload-time = "2026-07-06T21:34:06.157Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7c/b7379a5704c79eda57ce075869ba70a0368d1c850f803b3c0d078d39dcaf/cffi-2.1.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:8f9ec95b8a043d3dfbc74d9abc6f7baf524dd27a8dc160b0a32ff9cdab650c28", size = 225203, upload-time = "2026-07-06T21:34:07.489Z" }, + { url = "https://files.pythonhosted.org/packages/5a/02/d5e6c43ea85c41bda2a184a3418f195fe7cf602967a8d2b94e085b83deef/cffi-2.1.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:af5e2915d41fe6c961694d7bfdc8562942638200f3ce2765dfb8b745cf997629", size = 223263, upload-time = "2026-07-06T21:34:08.712Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d8/772b8259bf75749adffb1c546828978381fb516f60cf701f6c83daf60c85/cffi-2.1.0-cp315-cp315-win32.whl", hash = "sha256:0a42c688d19fca6e095a53c6a6e2295a5b050a8b289f109adab02a9e61a25de6", size = 177696, upload-time = "2026-07-06T21:34:26.355Z" }, + { url = "https://files.pythonhosted.org/packages/2f/dd/afa2191fc6d57fedd26e5844a2fe2fcc0bbfa00961bbaa5a41e4921e7cca/cffi-2.1.0-cp315-cp315-win_amd64.whl", hash = "sha256:bccbbb5ee76a61f9d99b5bf3846a51d7fca4b6a732fe46f89295610edaf41853", size = 187914, upload-time = "2026-07-06T21:34:27.58Z" }, + { url = "https://files.pythonhosted.org/packages/05/ef/6cd4f8c671517162379dc79cfae5aea9106bc38abb89628d5c16adf6a838/cffi-2.1.0-cp315-cp315-win_arm64.whl", hash = "sha256:8d35c139744adb3e727cd51b1a18324bbe44b8bd41bf8322bca4d41289f48eda", size = 183004, upload-time = "2026-07-06T21:34:28.905Z" }, + { url = "https://files.pythonhosted.org/packages/11/b6/12fc55092817a5faa26fb8c40c7f9d662e11a46ee248c137aafc42517d92/cffi-2.1.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:f9912624a0c0b834b7520d7769b3644453aabc0a7e1c839da7359f050750e9bc", size = 188378, upload-time = "2026-07-06T21:34:09.926Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2e/cdac88979f295fde5daa69622c7d2111e56e7ceb94f211357fbe452339e4/cffi-2.1.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:df92f2aba50eb4d96718b68ef76f2e57a57b54f2fa62333496d16c6d585a85ca", size = 188319, upload-time = "2026-07-06T21:34:11.101Z" }, + { url = "https://files.pythonhosted.org/packages/e0/27/1d0b408497e41a74795af122d7b603c418c5fed0171450f899afd04e594f/cffi-2.1.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0520e1f4c35f44e209cbbb421b67eec42e6a157f59444dfb6058874ff3610e5d", size = 223904, upload-time = "2026-07-06T21:34:12.606Z" }, + { url = "https://files.pythonhosted.org/packages/8b/31/e115c985105dd7ffb32444505f18ceb874bb42d992af05d5dced7ecf1980/cffi-2.1.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:3681e031db29958a7502f5c0c9d6bbc4c36cb20f7b104086fa642d1799631ff8", size = 211554, upload-time = "2026-07-06T21:34:13.987Z" }, + { url = "https://files.pythonhosted.org/packages/5a/67/9e6e09409336d9e515c58367e7cfcf4f89df06ad25252675595a58eb59d5/cffi-2.1.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:762f99479dcb369f60ab9017ad4ab97a36a1dd7c1ee5a3b15db0f4b8659120cd", size = 210795, upload-time = "2026-07-06T21:34:15.972Z" }, + { url = "https://files.pythonhosted.org/packages/19/e5/d3cc82a4a0be7902af279c04181ad038449c096734464a5ae1de3e1401bd/cffi-2.1.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0611e7ebf90573a535ebdc33ae9da222d037853983e13359f580fab781ca017f", size = 223843, upload-time = "2026-07-06T21:34:17.509Z" }, + { url = "https://files.pythonhosted.org/packages/b9/65/b434abc97ce7cecc2c640fde160507c0ecc7e21544b483ba3325d2e2ea17/cffi-2.1.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:86cf8755a791f72c85dc287128cc62d4f24d392e3f1e15837245623f4a33cccc", size = 226773, upload-time = "2026-07-06T21:34:19.05Z" }, + { url = "https://files.pythonhosted.org/packages/b5/9f/d4dc66ca651eb1145a133314cda721abf13cfac3d28c4a0402263ae6ad75/cffi-2.1.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:ba00f661f8ba35d075c937174e27c2c421cec3942fd2e0ea3e66996757c0fdd9", size = 225719, upload-time = "2026-07-06T21:34:20.576Z" }, + { url = "https://files.pythonhosted.org/packages/68/5a/e536c528bc8057496c360c0978559a2dc45653f89dd6151078aa7d8fca1a/cffi-2.1.0-cp315-cp315t-win32.whl", hash = "sha256:cb96698e3c7413d906ce83f8ffd245ec1bd94707541f299d0ce4d6b0193e982b", size = 182760, upload-time = "2026-07-06T21:34:22.059Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0b/0ffe8b82d3875bced5fa1e7986a7a46b748262a40ab7f60b475eb9fb1bb3/cffi-2.1.0-cp315-cp315t-win_amd64.whl", hash = "sha256:f146d154428a2523f9cc7936c02353c2459b8f6cf07d3cd1ee1c0a611109c5d5", size = 193769, upload-time = "2026-07-06T21:34:23.589Z" }, + { url = "https://files.pythonhosted.org/packages/a0/17/1073b53b68c9b5ca6914adf5f8bf55aacc2d3be102418c90700160ea8605/cffi-2.1.0-cp315-cp315t-win_arm64.whl", hash = "sha256:cbb7640ce37159548d2147b5b8c241f962143d4c71231431820783f4dc78f210", size = 186405, upload-time = "2026-07-06T21:34:24.857Z" }, ] [[package]] name = "charset-normalizer" -version = "3.4.7" +version = "3.4.9" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/2a/23f34ec9d04624958e137efdc394888716353190e75f25dd22c7a2c7a8aa/charset_normalizer-3.4.9.tar.gz", hash = "sha256:673611bbd43f0810bec0b0f028ddeaaa501190339cac411f347ac76917c3ae7b", size = 152439, upload-time = "2026-07-07T14:34:58.454Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" }, - { url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" }, - { url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" }, - { url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" }, - { url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" }, - { url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" }, - { url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" }, - { url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" }, - { url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" }, - { url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" }, - { url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" }, - { url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" }, - { url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" }, - { url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" }, - { url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" }, - { url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" }, - { url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" }, - { url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" }, - { url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" }, - { url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" }, - { url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" }, - { url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" }, - { url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" }, - { url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" }, - { url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" }, - { url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" }, - { url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" }, - { url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" }, - { url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" }, - { url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" }, - { url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" }, - { url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" }, - { url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" }, - { url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" }, - { url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" }, - { url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" }, - { url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" }, - { url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" }, - { url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" }, - { url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" }, - { url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" }, - { url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" }, - { url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" }, - { url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" }, - { url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" }, - { url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" }, - { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" }, + { url = "https://files.pythonhosted.org/packages/b2/06/97ec2aeae780b31d742b6352218b43841a6871e2564578ca522dce4a45c3/charset_normalizer-3.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:440eede837960000d74978f0eba527be106b5b9aee0daf779d395276ed0b0614", size = 317688, upload-time = "2026-07-07T14:33:35.408Z" }, + { url = "https://files.pythonhosted.org/packages/d0/39/8ff066c672434225f8d25f8b739f992af250944392173dcc88362681c9bf/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21e764fd1e70b6a3e205a0e46f3051701f98a8cb3fad66eeb80e48bb502f8698", size = 214982, upload-time = "2026-07-07T14:33:36.996Z" }, + { url = "https://files.pythonhosted.org/packages/92/8f/3a47a3667c83c2df9483d91644c6c107de3bf8874aa1793da9d3012eb986/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e4fd89cc178bced6ad29cb3e6dd4aa63fa5017c3524dbd0b25998fb64a87cc8b", size = 236460, upload-time = "2026-07-07T14:33:38.536Z" }, + { url = "https://files.pythonhosted.org/packages/f1/60/b22cdbee7e4013dab8b0d7647fc6181120fbbbc8f7025c226d15bd5a47fc/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bd47ba7fc3ca94896759ea0109775132d3e7ab921fbf54038e1bab2e46c313c9", size = 232003, upload-time = "2026-07-07T14:33:40.059Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f8/72eb13dcabe7257035cea8aefd922caad2f110d252bf9f67c4c2ca763aee/charset_normalizer-3.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84fd18bcc17526fc2b3c1af7d2b9217d32c9c04448c16ec693b9b4f1985c3d33", size = 223149, upload-time = "2026-07-07T14:33:41.631Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3e/faee8f9de92b14ee1198e9163252bb15efee7301b31256a3b6d9ebfdd0dd/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:5b10cd92fc5c498b35a8635df6d5a100207f88b63a4dc1de7ef9a548e1e2cd63", size = 207901, upload-time = "2026-07-07T14:33:43.209Z" }, + { url = "https://files.pythonhosted.org/packages/3a/25/45f30093ae27dd7b92a793b61882a38685f993700113ca36e0c9c14965e1/charset_normalizer-3.4.9-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a4fbdde9dd4a9ce5fd52c2b3a347bb50cc89483ef783f1cb00d408c13f7a96c0", size = 219176, upload-time = "2026-07-07T14:33:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/48/18/c8f397329c35e32f6a837e488986f4ae03bd2abebc453b48714991630c2f/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:416c229f77e5ea25b3dfd4b582f8d73d7e43c22320302b9ab128a2d3a0b38efe", size = 217356, upload-time = "2026-07-07T14:33:46.192Z" }, + { url = "https://files.pythonhosted.org/packages/86/7e/5ce0bba863470fd1902d5e5843968951bddf38abe4742fc97116ef4598b3/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:75286256590a6320cf106a0d28970d3560aad9ee09aa7b34fb40524792436d35", size = 209614, upload-time = "2026-07-07T14:33:47.705Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ef/2473d3c4d869155be4af1191111d59c4d5c4e0173026f7e85b176e23bf65/charset_normalizer-3.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:69b157c5d3292bcd443faca052f3096f637f1e074b98212a933c074ae23dc3b8", size = 224991, upload-time = "2026-07-07T14:33:49.238Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a3/53ddae3db108a088156aa8ddfafd411ebbc1340f48c5573f697b27f69a39/charset_normalizer-3.4.9-cp313-cp313-win32.whl", hash = "sha256:51307f5c71007673a2bf8232ad973483d281e74cb99c8c5a990af1eefa6277d9", size = 150622, upload-time = "2026-07-07T14:33:50.711Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ef/6953a77c7cf2c2ff9998e6f575ab3e380119f100223381565a4f94c1f836/charset_normalizer-3.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:fe2c7201c642b7c308f1675355ad7ff7b66acfe3541625efe5a3ad38f29d6115", size = 161947, upload-time = "2026-07-07T14:33:52.197Z" }, + { url = "https://files.pythonhosted.org/packages/6e/fb/d560d1d1555debbfe7849d9cac6145c1b537709d79576bf22557ed803b82/charset_normalizer-3.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:611057cc5d5c0afc743ba8be6bd828c17e0aaa8643f9d0a9b9bb7dea80eb8012", size = 152594, upload-time = "2026-07-07T14:33:53.486Z" }, + { url = "https://files.pythonhosted.org/packages/7e/8d/496817fa0944239ecae662dd57ea765cfeaec6a735f9f025d4b7b72e7143/charset_normalizer-3.4.9-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:0327fcd59a935777d83410750c50600ee9571af2846f71ce40f25b13da1ef380", size = 317253, upload-time = "2026-07-07T14:33:54.994Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/ef4a69ea338ad3c0deceea0f5f7d2380ae8b52132b06d652cb0d2cd86706/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a79d9f4d8001473a30c163556b3c3bfebec837495a412dde78b51672f6134f9", size = 215898, upload-time = "2026-07-07T14:33:56.334Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e7/5ddfd76fc061eb52de219658a4aa431cbacadf0a0219c8854f00da50d289/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:33bdcc2a32c0a0e861f60841a512c8acc658c87c2ac59d89e3a46dacf7d866e4", size = 236718, upload-time = "2026-07-07T14:33:57.9Z" }, + { url = "https://files.pythonhosted.org/packages/49/ba/768fa3f36048d81c477a0ce61f813bc1454d80917ccfe550abd9f44f5e24/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f840ed6d8ecba8255df8c42b87fadeda98ddfc6eeec05e2dc66e26d46dd6f58a", size = 232519, upload-time = "2026-07-07T14:33:59.811Z" }, + { url = "https://files.pythonhosted.org/packages/f4/c4/b3e049d2aa3766180c78507110543d9d50894cc97f57de543f1be521dcdc/charset_normalizer-3.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c25fe15c70c59eb7c5ce8c06a1f3fa1da0ecc5ea1e7a5922c40fd2fa9b0d5046", size = 223143, upload-time = "2026-07-07T14:34:01.517Z" }, + { url = "https://files.pythonhosted.org/packages/19/79/55c32d06d76ae4feafe053f061f3e3ab70bcf19f4007797ce8c3efda7830/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:f7fb7d750cfa0a070d2c24e831fd3481019a60dd317ea2b39acbcebc08b6ed81", size = 206742, upload-time = "2026-07-07T14:34:03.04Z" }, + { url = "https://files.pythonhosted.org/packages/10/e0/47c079dd82d217c807479cd59ffd30af56307ea31c108b75758970459ad3/charset_normalizer-3.4.9-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4d1c96a7a18b9690a4d46df09e3e3382406ae3213727cd1019ebade1c4a81917", size = 219191, upload-time = "2026-07-07T14:34:04.657Z" }, + { url = "https://files.pythonhosted.org/packages/42/ab/b9bc2e77d6b44a7e46ef62ec5cac1c9a6ba7b9135a5d560f002696ec9995/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a4cfde78a9f2880208d16a93b795726a3017d5977e08d1e162a7a31322479c41", size = 218328, upload-time = "2026-07-07T14:34:06.115Z" }, + { url = "https://files.pythonhosted.org/packages/f1/78/c9c71d599f5aa2d42bcdd35cbbd46d7f535351a57e40ff7d8e5a7e219401/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d4d6fcde76f94f5cb9e43e9e9a61f16dacefd228cbbf6f1a09bd9b219a92f1a1", size = 207406, upload-time = "2026-07-07T14:34:07.554Z" }, + { url = "https://files.pythonhosted.org/packages/f6/39/c914445c321a845097ce4f6ac7de9a18228a77b766272125a1ce00d851eb/charset_normalizer-3.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:898f0e9068ca27d37f8e83a5b962821df851532e6c4a7d615c1c033f9da6eedf", size = 225157, upload-time = "2026-07-07T14:34:09.061Z" }, + { url = "https://files.pythonhosted.org/packages/9b/f2/c0d4b8508565a36bc5c624e88ed297f5b0b1095011034d7f5b83a69908b5/charset_normalizer-3.4.9-cp314-cp314-win32.whl", hash = "sha256:c1c948747b03be832dceed96ca815cef7360de9aa19d37c730f8e3f6101aca48", size = 151095, upload-time = "2026-07-07T14:34:10.901Z" }, + { url = "https://files.pythonhosted.org/packages/49/fd/a1d26144398c67486422a72bf5812cda22cb4ccfcd95a290fb41ceb4b8e2/charset_normalizer-3.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:16b65ea0f2465b6fb52aa22de5eca612aa964ddfec00a912e26f4656cbef890b", size = 162796, upload-time = "2026-07-07T14:34:12.47Z" }, + { url = "https://files.pythonhosted.org/packages/20/95/d75e82f8ce9fd323ebf059c16c9aadefb22a1ecde13b7840b35835e4886c/charset_normalizer-3.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:40a126142a56b2dfc0aacbad1de8310cbf60da7656db0e6b16eebd48e3e93519", size = 153334, upload-time = "2026-07-07T14:34:14.044Z" }, + { url = "https://files.pythonhosted.org/packages/00/5e/17398df3a139985ba9d11ed072531986f408c8fca952835ef1ab1820c02b/charset_normalizer-3.4.9-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:609b3ba8fcc0fb5ab7af00719d0fb6ad0cb518e48e7712d12fd68f1327951198", size = 338848, upload-time = "2026-07-07T14:34:15.688Z" }, + { url = "https://files.pythonhosted.org/packages/cd/91/7253a32e86b7e1d1239b1b36ba6dd0f021a21107ab33054b53119cc083b9/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:51447e9aa2684679af07ca5021c3db526e0284347ebf4ffcec1154c3350cfe32", size = 223022, upload-time = "2026-07-07T14:34:17.248Z" }, + { url = "https://files.pythonhosted.org/packages/cb/32/2e64bd2be10e89c61e57ebe6a93fd98ae88eb7ebe414b5121f22c96c69eb/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cc1b0fff8ead343dae06305f954eb8468ba0ec1a97881f42489d198e4ce3c632", size = 241590, upload-time = "2026-07-07T14:34:18.813Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ef/d96ec496cfea0c21db43b0ad03891308b02388d054cc902cf0e5a1ad6a88/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fa36ec09ef71d158186bc79e359ff5fdd6e7996fe8ab638f00d6b93139ba4fcf", size = 239584, upload-time = "2026-07-07T14:34:20.52Z" }, + { url = "https://files.pythonhosted.org/packages/d4/ce/9af95f7876194bd7a14e3dfe4a4de2e0bff02666a3910d72beafd06cc297/charset_normalizer-3.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df115d4d83168fdf2cae48ef1ff6d1cb4c466364e30861b37121de0f3bf1b990", size = 230224, upload-time = "2026-07-07T14:34:22.189Z" }, + { url = "https://files.pythonhosted.org/packages/52/94/af74dde74a3996bd959c350709bfe50e297823d70a8c1cbd54b838880863/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f86c6358749bd4fda175388691e3ba8c46e24c5347d0afd20f9b7edfc9faf07d", size = 212667, upload-time = "2026-07-07T14:34:23.857Z" }, + { url = "https://files.pythonhosted.org/packages/ee/f0/f1c4fe746c395922961b5916ed1d7d6e7d4c84851d19ed43cc89980ec953/charset_normalizer-3.4.9-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:32286a2c8d167e897177b673176c1e3e00d4057caf5d2b64eef9a3666b03018e", size = 227179, upload-time = "2026-07-07T14:34:25.586Z" }, + { url = "https://files.pythonhosted.org/packages/e4/56/6c745619ac397e8871e2bcd3cea1eec86b877488f33888b3aef5c3ed506e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:83aed2c10721ddd90f68140685391b50811a880af20654c59af6b6c66c40513c", size = 225372, upload-time = "2026-07-07T14:34:27.212Z" }, + { url = "https://files.pythonhosted.org/packages/78/ad/98aae8630ac71f16711968e38a5acfecce41b778bf2f0312851020f565a8/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cd6c3d4b783c556fa00bf540854e42f135e2f256abd29669fcd0da0f2dec79c2", size = 215222, upload-time = "2026-07-07T14:34:28.774Z" }, + { url = "https://files.pythonhosted.org/packages/f7/40/9593d54209765207a7f11073c06494c1721e4ca4a0a426c597679bf7f91e/charset_normalizer-3.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ee2f2a527e3c1a6e6411eb4209642e138b544a2d72fe5d0d76daf77b24063534", size = 231958, upload-time = "2026-07-07T14:34:30.345Z" }, + { url = "https://files.pythonhosted.org/packages/b1/27/693ee5e8a18191eb38647360c51cd505013e2bd3b366aa43fd5344c21e3c/charset_normalizer-3.4.9-cp314-cp314t-win32.whl", hash = "sha256:0d861473f743244d349b50f850d10eb87aeb22bbdcc8e64f79273c94af5a8226", size = 155580, upload-time = "2026-07-07T14:34:31.884Z" }, + { url = "https://files.pythonhosted.org/packages/80/3f/bd97d3d9c613013d07cb7733d299385b41df37f0471310f5a73dc359f0b8/charset_normalizer-3.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:9b8e0f3107e2200b76f6054de99016eac3ee6762713587b36baaa7e4bd2ae177", size = 167620, upload-time = "2026-07-07T14:34:33.438Z" }, + { url = "https://files.pythonhosted.org/packages/3d/c6/eee9dca4439b1061f76373f06ea855678cc4a64c1c3c90b50e479edbb8eb/charset_normalizer-3.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:19ac87f93086ce37b86e098888555c4b4bc48102279bae3350098c0ed664b501", size = 158037, upload-time = "2026-07-07T14:34:35.018Z" }, + { url = "https://files.pythonhosted.org/packages/98/2b/f97f1c193fb855c345d678f5077d6926034db0722df74c8f057020e05a25/charset_normalizer-3.4.9-py3-none-any.whl", hash = "sha256:68e5f26a1ad57ded6d1cfb85331d1c1a195314756471d97758c48498bb4dcdf5", size = 64538, upload-time = "2026-07-07T14:34:56.993Z" }, ] [[package]] @@ -347,15 +366,15 @@ wheels = [ [[package]] name = "deno" -version = "2.9.0" +version = "2.9.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9f/96/098c84eff4b8ec7da8fb1b43d3371c22f021b7642d5b2bdad8d1780cd605/deno-2.9.0.tar.gz", hash = "sha256:7548d37528eaca993a2d4483ce092eca971fa206faadb0f4c8af8c3f039677d2", size = 8168, upload-time = "2026-06-25T15:03:34.872Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/3c/bf3a4e2c00f7eefd4ed2fd033a958cab3e92dcc539d3ab80351850cae99c/deno-2.9.2.tar.gz", hash = "sha256:3b77d7689c15fb2c47d5a3927dc8cf70dbd408bc92e20c70da079c6d78980e34", size = 8164, upload-time = "2026-07-08T14:38:54.267Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/64/ebd8bb0fc2ef57b0f8cff53cd9b43261c9c16dfaf0a07db37b9fcb2ec186/deno-2.9.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:f53bbb11a9a7eeae3865470baa1d5cb02da105ac073019953e9d59e0588bc10e", size = 42301281, upload-time = "2026-06-25T15:03:18.675Z" }, - { url = "https://files.pythonhosted.org/packages/63/3c/1cc1c8c731e5dada562640dc42c0603c017bc1ba731a69255cecd9b25be0/deno-2.9.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e58aa71c144dcd91ef9fd5b044dd465d1be571b6763c3a777924d4d54f7033e7", size = 37950190, upload-time = "2026-06-25T15:03:21.958Z" }, - { url = "https://files.pythonhosted.org/packages/30/86/e5487fe222840aca26c292cfa2532fb0ba4b080b6106f89cce530a7470a5/deno-2.9.0-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:3401337f5c3b75e8f7c1b07589612cddac66ddc17a0a18e7eff6e55f23a1b53b", size = 42017185, upload-time = "2026-06-25T15:03:25.328Z" }, - { url = "https://files.pythonhosted.org/packages/5a/d0/7f73a8d5f77947c327272147d89d005426888d4d5b3d6575181c1a7271e9/deno-2.9.0-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:63988d645e591bc171a574aa6b3ada2ddfc9bcf59f0178412a9fde1991bfc111", size = 43869469, upload-time = "2026-06-25T15:03:28.311Z" }, - { url = "https://files.pythonhosted.org/packages/01/34/ca326c53558f99792eb4e2c19ca88763e6890ce1ffb44b9536ecd7ae983f/deno-2.9.0-py3-none-win_amd64.whl", hash = "sha256:29f6f616b47e5babd9604bcd1a76ec3576895275f8696dedfb33ab6545f6ed85", size = 41589185, upload-time = "2026-06-25T15:03:32.002Z" }, + { url = "https://files.pythonhosted.org/packages/27/fe/4a02a256f19b3f31ac17ec37f6467ecf523601ed753e6c9177865dd2bbb2/deno-2.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fb4bb5362d02c193f8086668789076f366416980b79277c06ea6cff73866b668", size = 42341183, upload-time = "2026-07-08T14:38:36.384Z" }, + { url = "https://files.pythonhosted.org/packages/5a/3e/235530ac9b9c206ee8c098e2b215f711a71d915d8d7d2be77599c8c1db4b/deno-2.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f6f74fd8698a8a9a31af14eed5730f1964ab50eaa0129079d11d91fa92496f60", size = 37984508, upload-time = "2026-07-08T14:38:40.006Z" }, + { url = "https://files.pythonhosted.org/packages/7b/27/06a166fca538c06dfae4c45dbb92c2ebf3083341141966a3d649272ea734/deno-2.9.2-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:8b2fd9318d452b7e44c45968916670a0c5452d72796e1af9abfaa7a389e4aa6b", size = 42089854, upload-time = "2026-07-08T14:38:43.841Z" }, + { url = "https://files.pythonhosted.org/packages/09/b4/38338550a5ca0a7bcc39991e3d48e46ce3699413460dff32f383a611d822/deno-2.9.2-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:bf0471db7238e90810bca77a8d2c28646317b5a3fd9e5cdfdf73b251fde11b83", size = 43916921, upload-time = "2026-07-08T14:38:48.057Z" }, + { url = "https://files.pythonhosted.org/packages/a9/3d/c490c27e1f720baf014bb2c18b29d09b11c21d69fadfd69639e5904cdc95/deno-2.9.2-py3-none-win_amd64.whl", hash = "sha256:8e815d66b3e1314d028b2f0a05c75a331dd7025f4c825e12dd1f057207cee1f6", size = 41621982, upload-time = "2026-07-08T14:38:51.928Z" }, ] [[package]] @@ -1004,38 +1023,62 @@ wheels = [ [[package]] name = "websockets" -version = "16.0" +version = "16.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/02/b9a097e1e16fee4e2fd1ec8c39f6a9c5d6257bae8fa12640caf869f54436/websockets-16.1.tar.gz", hash = "sha256:299468cbe42e2b9981134c7c51d99387d8a7bf562b00183b3eec53f882846dad", size = 182530, upload-time = "2026-07-10T06:32:57.734Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, - { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, - { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, - { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, - { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, - { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, - { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, - { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, - { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, - { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, - { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, - { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, - { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, - { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, - { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, - { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, - { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, - { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, - { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, - { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, - { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, - { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, - { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, - { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, - { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, - { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, + { url = "https://files.pythonhosted.org/packages/d9/63/df158b155420b566f025e75613424ad9649a24bcb0e9f259321ab3d58bea/websockets-16.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:b0232ed141cec3df2af5a3959a071c51f40036336b0d37e17faf9ef52fc73e47", size = 179791, upload-time = "2026-07-10T06:31:33.108Z" }, + { url = "https://files.pythonhosted.org/packages/74/cf/00fe9414dfeafa6fe54eae9f5716c8c8e9ac59d192be3b893c096d395846/websockets-16.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a71b73d143991714144e159f767b698f03c4a70b8a65ae1733b650cff488045b", size = 177472, upload-time = "2026-07-10T06:31:34.522Z" }, + { url = "https://files.pythonhosted.org/packages/8b/76/b10633424d40681b4e892ffd08ca5226322b2426e62d4ab71eae484c3a32/websockets-16.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:187323204c3b2fc465e8fc2609e60437c521790cb9c1acb49c4c452a33e57f37", size = 177737, upload-time = "2026-07-10T06:31:35.964Z" }, + { url = "https://files.pythonhosted.org/packages/dc/61/d3bb03b2229bb1afd72008742d586cf1ea240dce64dd48c71c8c7fd3294c/websockets-16.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:9dba74233c8c3ce368850818c98354dad2570f57231b3fd3bd00d7aa57628881", size = 187403, upload-time = "2026-07-10T06:31:37.496Z" }, + { url = "https://files.pythonhosted.org/packages/26/16/cc2e80478f688fc3c39c67dc1fac6a0783858058914ebc2489917462cb42/websockets-16.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:63339bc8c63c86a463177775cb7c677691f5bcfac7b3b2f01b286d42acd41600", size = 188639, upload-time = "2026-07-10T06:31:38.86Z" }, + { url = "https://files.pythonhosted.org/packages/15/d6/ad87b2507e57de1cbf897a56c963f2925962ed5e85fbe06aaa83ced27acd/websockets-16.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:23e545ea8ae4263e37cdfd4e22a217f519e48e432728bc461185bbf585f38a83", size = 190078, upload-time = "2026-07-10T06:31:40.218Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1a/5b37b3fd335d5811f29fc829f2646a3e6d1463a4bf09c3100708684c766e/websockets-16.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2237081454846fb40403a80ba86d82e2038b9c45865ab96af0abe7d002a91045", size = 189267, upload-time = "2026-07-10T06:31:41.523Z" }, + { url = "https://files.pythonhosted.org/packages/42/98/06afc33e9450d4230f94c664db78875d90f5f6a5fb77f0bc6ec15ae74e1c/websockets-16.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5f5218de1ed047385ca53744caba9435d65f75d008364970a3fae95a05812cf9", size = 188022, upload-time = "2026-07-10T06:31:42.838Z" }, + { url = "https://files.pythonhosted.org/packages/8c/bf/42fef5d5887c18cf2d148b02debf56cecb9cfbffc68027cde9b12c8f432c/websockets-16.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75c98e3920039d0edff03b74478ada504b7ce3a1bc406db2cabfca84320f7baf", size = 185435, upload-time = "2026-07-10T06:31:44.219Z" }, + { url = "https://files.pythonhosted.org/packages/a0/9b/8021c133add5fe40ed40312553a6cd1408c069d7efe3444ad483d4973ed3/websockets-16.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1facd189d8190af30487a55b4c3688484dd50801628a3b5b2ccd26db08e67057", size = 188080, upload-time = "2026-07-10T06:31:45.986Z" }, + { url = "https://files.pythonhosted.org/packages/69/54/1e37384f395eaa127383aab15c1c45e200890a7d7b99db5c312233d193e0/websockets-16.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:cc0c6a6eef613c7da32d4fb068f82ef834b58134f6a16b54e6c1e5bf9529ab3d", size = 186678, upload-time = "2026-07-10T06:31:47.449Z" }, + { url = "https://files.pythonhosted.org/packages/68/79/1caeacab5bc2081e4519288d248bc8bd2de30652e6eaa94be6be09a1fe5b/websockets-16.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ad9411eded8988b879be6038206698bf7106c85a78f642c004485bcb95be17eb", size = 188554, upload-time = "2026-07-10T06:31:48.886Z" }, + { url = "https://files.pythonhosted.org/packages/ee/83/b3dca5fad71487b726e31cb0acf56f226792c1cc34e6ab18cbf146bd2d74/websockets-16.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:cd68f0914f3b64694895bc5e9b14e8b447e41d7bf5ffaf989bb8dcb5e2dfdce7", size = 186109, upload-time = "2026-07-10T06:31:50.508Z" }, + { url = "https://files.pythonhosted.org/packages/5b/0b/8f246c3712f07f207b52ea5fb47f3b2b66fafec7303162644c74aed51c6a/websockets-16.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fef2debfe7f7ebdda12176f26166f95b7af17af05ba06150fcf889032e0213e9", size = 187061, upload-time = "2026-07-10T06:31:51.861Z" }, + { url = "https://files.pythonhosted.org/packages/47/eb/27d6c92a01696b6495386af4fc941d7d0a13f2eab2bf9c336111d7321491/websockets-16.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a3cd6c9b798218798f4bb7b2e71c38f0e744bb94ca537b13376f88019d46384d", size = 187347, upload-time = "2026-07-10T06:31:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/6b/d5/eeee439921f55d5eaeabcea18d0f7ce32cdc39cb8fc1e185431a094c5c7b/websockets-16.1-cp313-cp313-win32.whl", hash = "sha256:84c170c6869633536921e4474b1cce7254c0c9b0053ef5725f966cee47e718e4", size = 180149, upload-time = "2026-07-10T06:31:55.058Z" }, + { url = "https://files.pythonhosted.org/packages/a3/03/971e98d4a4864cf263f9e94c5b2b7c9a9b7682d77bfbba4e732c55ee85a9/websockets-16.1-cp313-cp313-win_amd64.whl", hash = "sha256:bef52d327d70fa75dad93ee61ea2cb1d1489aca9f35c188833563f5a3b4df0a5", size = 180458, upload-time = "2026-07-10T06:31:56.767Z" }, + { url = "https://files.pythonhosted.org/packages/8d/e6/da1dc11507f8118145a81c751fe0c77e5e1c11b8554496addb39389e2dc2/websockets-16.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:f881fca0a45dd6789939bd6637cd98169b92f1c3fdc78262f2cb9ec2cb1f324e", size = 179833, upload-time = "2026-07-10T06:31:58.19Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ac/c0d46f62e31e232487b2c123bc3cfd9a4e45684ca7dc0c37f0987f29baae/websockets-16.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:30c379d5b207d3a7f0ba4c2e4602a895b0bcc63fb5f5371a4ae7fbddb03b672b", size = 177524, upload-time = "2026-07-10T06:31:59.563Z" }, + { url = "https://files.pythonhosted.org/packages/4a/33/abd966074b34a51e4f134e0aaed80f5a4a0a35163ea5ac58a1bc5a076d23/websockets-16.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:98ab58a4faa72b46da0127ccc1931dcbfc0985b0778892300a092185910c4cbe", size = 177743, upload-time = "2026-07-10T06:32:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/ea/30/646e47b8a8dff04e227bdab512e6dde60663a647eeac7bbd6edddd92bbc5/websockets-16.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8e9c4e369fc181b2d41a99e01477215cecdc8546a39f7d41a59cc0a7065a0b09", size = 187474, upload-time = "2026-07-10T06:32:02.54Z" }, + { url = "https://files.pythonhosted.org/packages/d2/72/890ab9d77494af93ea65268230bfbc0a90ba789401ed7a44356a44785644/websockets-16.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0704df094b2d5fa7f6f410925a594c2a5c9a09167731a76292e5410934208209", size = 188717, upload-time = "2026-07-10T06:32:04.156Z" }, + { url = "https://files.pythonhosted.org/packages/d5/aa/baedbbaa6bf9ed6029617ed5e8976535bd805f483ca9b3484e7ad9ee08bf/websockets-16.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b22b1f4950f6ab7126623329c3b47b3b90a14c05db517f2db2a026ad6c928352", size = 190090, upload-time = "2026-07-10T06:32:05.822Z" }, + { url = "https://files.pythonhosted.org/packages/52/4f/d813ec94e18002571ef4959d87a630eff6e01b72a51bcb0832b75ae8c51a/websockets-16.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1ae4a686a662964a6671069f84f7f908cc3475e782227726b0c622c715962105", size = 189320, upload-time = "2026-07-10T06:32:07.223Z" }, + { url = "https://files.pythonhosted.org/packages/b8/3c/8ec52a6662f3df64090fba28cd521d405d54759268d8e820477037e8c80d/websockets-16.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:856bdd638f8277f86465057bfdd4da097c73058fb0f9d2bd5baea29e2bf2d367", size = 188068, upload-time = "2026-07-10T06:32:08.586Z" }, + { url = "https://files.pythonhosted.org/packages/96/7f/f0ae6042b14f86fa5f996c6563ea4cf107adc036ccbedc9d4f418d0095f9/websockets-16.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9003a1fde1c21a322a3ca3fa0c4bda8c639da81dbc925162766086643b05ba87", size = 185493, upload-time = "2026-07-10T06:32:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/89/ad/5ffc53af9939c49fd653d147fa5b8f78ced1f6bce6c49a7446860945b0ce/websockets-16.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:39e947b1f5fdab045174306e3916785bf3ed537648acc1549827c08c33b10953", size = 188141, upload-time = "2026-07-10T06:32:11.434Z" }, + { url = "https://files.pythonhosted.org/packages/67/62/729206c0ee577a4db8eae6dd06e0eef725a1287c6df11b2ef831d003df31/websockets-16.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5dd0e666b5931c0509cf65714686a1c5126771e663a79ac5d40da4f58b1f9502", size = 186653, upload-time = "2026-07-10T06:32:12.845Z" }, + { url = "https://files.pythonhosted.org/packages/1b/86/e8806a99ec4589914f255e6b658853fe537bf359c05e6ba5762ad9c27917/websockets-16.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a0285df7925657ad65a65fb8dc330808bce082827538fd50ef45fa12d1fc5bca", size = 188614, upload-time = "2026-07-10T06:32:14.236Z" }, + { url = "https://files.pythonhosted.org/packages/89/38/ac554e2fc6ff0b8deeff9798b92e7abd8f99e2bd9731532e7033de208220/websockets-16.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:82d1c2cab3c133e9d059b3a5420bed9376bd30e21c185c63dda4ddadf6ddda47", size = 186165, upload-time = "2026-07-10T06:32:15.626Z" }, + { url = "https://files.pythonhosted.org/packages/6c/c5/4ef4d8e53342f94f3c49e1ae089b32c1e8b3878e15e0022c7708c647f351/websockets-16.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:c39907f1eaf11f6277def65aa02d68f30576b693d0c1ca332aafa3caa723ac6d", size = 187119, upload-time = "2026-07-10T06:32:17.114Z" }, + { url = "https://files.pythonhosted.org/packages/3a/33/4788b1dd417bd97eeb2698af3b9df6775ac656f96e9987da0419a067602f/websockets-16.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:45c5ea55446171949eb99fd34b771ceddd511ca21958d40d0197ced33159e5ee", size = 187411, upload-time = "2026-07-10T06:32:18.629Z" }, + { url = "https://files.pythonhosted.org/packages/30/38/00d37aad6dc3244ce349e2864815362e50b3cfc00cac28d216db20efe40f/websockets-16.1-cp314-cp314-win32.whl", hash = "sha256:b8ef8b1c8d6bd029a475ac432e730fba2dfd456715d26c473e2a82291024b99c", size = 179822, upload-time = "2026-07-10T06:32:20.233Z" }, + { url = "https://files.pythonhosted.org/packages/9d/37/2a8cb0eaddee5eaebda47a90a3ba0898d1ce3d866b02a4857fea17d82e5b/websockets-16.1-cp314-cp314-win_amd64.whl", hash = "sha256:7358ff21632b5d062707f73e859c824f1c3807e73d8ca25e71caca7c4cdcf145", size = 180167, upload-time = "2026-07-10T06:32:21.749Z" }, + { url = "https://files.pythonhosted.org/packages/07/5a/262ad5fcaef4198997b165060f09a63f861e76939b1786ab546ccc3f8120/websockets-16.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d0f38f4c3e9b359e257c339c2cc1967ccaeedb102e57c1c986bdce4bf4f32268", size = 180166, upload-time = "2026-07-10T06:32:23.278Z" }, + { url = "https://files.pythonhosted.org/packages/1a/c7/36377db690f4292826e4501a6dec2801dc55fd1cf0405923b04937e478df/websockets-16.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:3c3d2cbd1602593bad49bd86fa3fbb25407d87a3b4bf8857c0ac5ac4914e1901", size = 177697, upload-time = "2026-07-10T06:32:25.164Z" }, + { url = "https://files.pythonhosted.org/packages/fa/c7/07171abce1e39799a76f473608580fe98bd43a1230f5146159622c02bccf/websockets-16.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:36069b74671e7e667f48a7484249f84c45a825a134c8b1bdc01875d0daa10d79", size = 177902, upload-time = "2026-07-10T06:32:26.564Z" }, + { url = "https://files.pythonhosted.org/packages/14/17/c831f48e250bc4749f57c00dcce73337c41cd32f6d59a64567b84e782601/websockets-16.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:587f83c2ce8a5d628e166384d77fa7f0ac69b9007d515ab442123e6615aa8da3", size = 187766, upload-time = "2026-07-10T06:32:27.981Z" }, + { url = "https://files.pythonhosted.org/packages/2c/2e/4dfe63e245b0ecfaf470cf082d25c6ce35808159135fd88c82653a6b11ab/websockets-16.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a6db7972d52bc1b66cefe2246902e256cbaebc9ba8a45eac09343d7eb6671b2", size = 188939, upload-time = "2026-07-10T06:32:29.365Z" }, + { url = "https://files.pythonhosted.org/packages/ba/e5/5faf65aebd9562f6b4bc473d24ce38cc56f84eb5f5bee66ed9b86733f93c/websockets-16.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e7d6014888a0632e1ed7a4095248bb3095232999447f2d83bfb1900987dd9ed9", size = 191081, upload-time = "2026-07-10T06:32:30.868Z" }, + { url = "https://files.pythonhosted.org/packages/49/cd/2634f2f2c0556c1aae6501ed6840019cc569dd6fdbcac6494378daea4dc0/websockets-16.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9cb074d150e4ad2a77aa8a332c2be85f3f64f2681519d2570c1225c12c9821ff", size = 189513, upload-time = "2026-07-10T06:32:32.399Z" }, + { url = "https://files.pythonhosted.org/packages/59/bb/2c700b51196104f09715b326b1f092ed25326bdf79a03e00a4842e503743/websockets-16.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d19c9067e1fe9490f974bffbc0e443b80a7674c5efb4980c429cc00771f07c5a", size = 188240, upload-time = "2026-07-10T06:32:33.897Z" }, + { url = "https://files.pythonhosted.org/packages/f1/20/86283636e499a1a357fa9441f690ba34f255e731f2fea174132b3b762b57/websockets-16.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d440ff0c6c7469ad59c0a412c383c235935b43635e89425e3f6a0c36de90c31b", size = 185955, upload-time = "2026-07-10T06:32:35.279Z" }, + { url = "https://files.pythonhosted.org/packages/91/23/d7fb734b0095d43bc7f1c9f68afd50adb4176e7e513403e8c70ad7daa4fa/websockets-16.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8613129a2533f08de24505e69a3e403cedaadae49abdb043c4d170ca71b7e4bd", size = 188491, upload-time = "2026-07-10T06:32:36.673Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5e/168a192689db468405ecf3b8e4a2c18811936b0724d017ad7e6d252734f0/websockets-16.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:a5bf9c23f197b4ec88290fd5463f33db67362a1bb10f85fc2e8e7627f0ddab97", size = 186983, upload-time = "2026-07-10T06:32:38.207Z" }, + { url = "https://files.pythonhosted.org/packages/7e/9b/66795fa91ebe49019ebe4fa910282172252e37046b80e08fc52e0c365150/websockets-16.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:520b0fd0395f075febb283c76755af724ab9fd19dffa4f3bfd18cb4e622790a3", size = 188890, upload-time = "2026-07-10T06:32:39.545Z" }, + { url = "https://files.pythonhosted.org/packages/5a/32/126bbc844be5afb3613fd43211dac10a9645f4cf39741d04acaa2ec7030c/websockets-16.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:7143aa09a67e1c013be44e81a88dfe90fc6244198ab86c7edd064152cf619805", size = 186583, upload-time = "2026-07-10T06:32:41.038Z" }, + { url = "https://files.pythonhosted.org/packages/22/b9/0b5db9cbcf6e4970db4496893244a8d92e07f71a8ef27cf34b08aa02fef1/websockets-16.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:7acb811fad08e611755800d1560e395c67e11a6bd563598ea6abb319afb86938", size = 187353, upload-time = "2026-07-10T06:32:42.501Z" }, + { url = "https://files.pythonhosted.org/packages/99/2e/254b2131a10d831b76e2c18dfe7add9729c6292c674a8085bf8de01ad151/websockets-16.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c5cf88e3faa2f7931bc6baeee7599c97656a3f6ac7f831f4fccba233e141783a", size = 187784, upload-time = "2026-07-10T06:32:43.929Z" }, + { url = "https://files.pythonhosted.org/packages/21/dc/e7288aa8e3ac5a88a0924619984d663c1abf2a87d0ea98290c66fdaee0ec/websockets-16.1-cp314-cp314t-win32.whl", hash = "sha256:589f8842521c8307684ce0b40ce4ad70c5e0aa46484c6f1225a94ef4b8970341", size = 179947, upload-time = "2026-07-10T06:32:45.495Z" }, + { url = "https://files.pythonhosted.org/packages/d3/de/37edf1260ff0fbbd2f82433489c4cfbe799ac2ff21355331609879329fe6/websockets-16.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c0e0857c30bbbc2bb5c30687508f0b7ec19aa026cd9f2ff8424d0fee42dcc07", size = 180291, upload-time = "2026-07-10T06:32:47.119Z" }, + { url = "https://files.pythonhosted.org/packages/66/58/bd83247f39ddc26ffc2c24eb05087a3b749e00cb4509fc6d19daa23c8495/websockets-16.1-py3-none-any.whl", hash = "sha256:c5149dfe490ec7e5ee5dbf624c642fb725f93a5575c7f00ab594ca9eddb8dd81", size = 174031, upload-time = "2026-07-10T06:32:56.079Z" }, ] [[package]] From e2c777842e1e066e37a4f17284638f70be759504 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 10 Jul 2026 09:49:59 +0300 Subject: [PATCH 78/88] fix: honor OUTPUT_TEMPLATE for channel downloads (closes #1024) Detect YouTube channel tabs that yt-dlp reports as playlists so channel downloads use OUTPUT_TEMPLATE_CHANNEL and its empty fallback instead of OUTPUT_TEMPLATE_PLAYLIST. --- app/tests/test_download_queue.py | 97 ++++++++++++++++++++++++++++++++ app/ytdl.py | 22 +++++++- 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/app/tests/test_download_queue.py b/app/tests/test_download_queue.py index 695788f..2c0dceb 100644 --- a/app/tests/test_download_queue.py +++ b/app/tests/test_download_queue.py @@ -222,6 +222,103 @@ async def test_add_entry_queues_single_video_without_reextracting(dq_env): assert dq.pending.exists("https://example.com/watch?v=1") +@pytest.mark.asyncio +async def test_channel_download_uses_output_template_when_channel_template_empty(dq_env): + """Channel tabs reported as playlists must honor OUTPUT_TEMPLATE when OUTPUT_TEMPLATE_CHANNEL is empty.""" + notifier = AsyncMock() + dq_env.OUTPUT_TEMPLATE = "%(channel)s [YT]/%(title)s.%(ext)s" + dq_env.OUTPUT_TEMPLATE_CHANNEL = "" + dq_env.OUTPUT_TEMPLATE_PLAYLIST = "" + + channel_id = "UCabcd123" + + def fake_extract(self, url, ytdl_options_presets=None, ytdl_options_overrides=None): + return { + "_type": "playlist", + "id": channel_id, + "channel_id": channel_id, + "channel": "Odin", + "title": "Odin - Videos", + "entries": [ + { + "id": "vid1", + "title": "Salvia Plath - Pondering", + "url": "https://example.com/watch?v=1", + "webpage_url": "https://example.com/watch?v=1", + "channel": "Odin", + "upload_date": "20130804", + }, + ], + } + + dq = DownloadQueue(dq_env, notifier) + with patch.object(DownloadQueue, "_DownloadQueue__extract_info", fake_extract): + result = await dq.add( + "https://www.youtube.com/@odin/videos", + "video", + "auto", + "any", + "best", + "", + "", + 0, + auto_start=False, + ) + + assert result["status"] == "ok" + url = "https://example.com/watch?v=1" + assert dq.pending.exists(url) + download = dq.pending.get(url) + assert download.output_template.startswith("Odin [YT]/") + assert "Odin - Videos" not in download.output_template + + +@pytest.mark.asyncio +async def test_playlist_download_not_treated_as_channel(dq_env): + """Real playlists (id != channel_id) must not be promoted to channel downloads.""" + notifier = AsyncMock() + dq_env.OUTPUT_TEMPLATE = "%(channel)s [YT]/%(title)s.%(ext)s" + dq_env.OUTPUT_TEMPLATE_CHANNEL = "" + dq_env.OUTPUT_TEMPLATE_PLAYLIST = "%(playlist_title)s/%(title)s.%(ext)s" + + def fake_extract(self, url, ytdl_options_presets=None, ytdl_options_overrides=None): + return { + "_type": "playlist", + "id": "PLxyz789", + "channel_id": "UCabcd123", + "channel": "Odin", + "title": "My Playlist", + "entries": [ + { + "id": "vid1", + "title": "Test Video", + "url": "https://example.com/watch?v=1", + "webpage_url": "https://example.com/watch?v=1", + }, + ], + } + + dq = DownloadQueue(dq_env, notifier) + with patch.object(DownloadQueue, "_DownloadQueue__extract_info", fake_extract): + result = await dq.add( + "https://www.youtube.com/playlist?list=PLxyz789", + "video", + "auto", + "any", + "best", + "", + "", + 0, + auto_start=False, + ) + + assert result["status"] == "ok" + url = "https://example.com/watch?v=1" + assert dq.pending.exists(url) + download = dq.pending.get(url) + assert download.output_template.startswith("My Playlist/") + + @pytest.mark.asyncio async def test_add_merges_global_preset_and_override_options(dq_env): notifier = AsyncMock() diff --git a/app/ytdl.py b/app/ytdl.py index 78acc82..207d35e 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -799,6 +799,16 @@ class DownloadQueue: self._add_generation += 1 log.info('Playlist add operation canceled by user') + @staticmethod + def __is_channel_extraction(entry): + """Return True when yt-dlp reported a channel tab as a playlist. + + YouTube channel tabs are extracted with ``_type: 'playlist'`` but set + ``id`` equal to ``channel_id``; real playlists keep a distinct id. + """ + channel_id = entry.get('channel_id') + return bool(channel_id) and entry.get('id') == channel_id + async def __import_queue(self): for k, v in self.queue.saved_items(): await self.__add_download(v, True) @@ -1161,6 +1171,8 @@ class DownloadQueue: _add_gen, ) elif etype == 'playlist' or etype == 'channel': + if etype == 'playlist' and self.__is_channel_extraction(entry): + etype = 'channel' log.debug(f'Processing as a {etype}') entries = entry['entries'] # Convert generator to list if needed (for len() and slicing operations) @@ -1180,7 +1192,15 @@ class DownloadQueue: if "id" not in etr: etr["id"] = _entry_id(etr) etr["_type"] = "video" - etr[etype] = entry.get("id") or entry.get("channel_id") or entry.get("channel") + if etype == 'channel': + etr["channel"] = ( + entry.get("channel") + or entry.get("uploader") + or entry.get("title") + or entry.get("id") + ) + else: + etr["playlist"] = entry.get("id") or entry.get("channel_id") or entry.get("channel") etr[f"{etype}_index"] = '{{0:0{0:d}d}}'.format(index_digits).format(index) etr[f"{etype}_count"] = total_entries etr[f"{etype}_autonumber"] = index From 3ea4732c5d813a2902c8b6bda57e2f3f86d28e7f Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Sun, 12 Jul 2026 08:08:14 +0300 Subject: [PATCH 79/88] fix: harden download lifecycle, subscriptions, and UI robustness Addresses a full-project review. Backend correctness and availability: - ytdl: cancel() only SIGKILLs the child's process group when the child actually became its own group leader, so a race (or failed setpgrp) can no longer kill the whole server; kill the group on cancel and on shutdown to avoid orphaned ffmpeg children - ytdl: dedicated ThreadPoolExecutor for download supervision so active downloads can't starve extract_info / live probes on the default pool - ytdl/main/subscriptions: route fire-and-forget tasks through a bg_tasks helper that keeps a strong ref and logs failures - subscriptions: run flat-playlist extraction in an executor and check feeds with bounded concurrency so one slow feed can't block the loop; set last_checked on failure so broken feeds aren't retried every 60s - main: validate ids on /start & /delete and numeric env vars at startup; return 400 (not 500) on bad subscriptions/update input; serve /history from memory; move get_custom_dirs off the event loop; restrict t= stripping to YouTube hosts; drop double percent-decode in state guard - dl_formats/ytdl: enforce requested caption format via FFmpegSubtitlesConvertor and strip VTT header metadata only in the pre-cue region so real dialogue is preserved - ytdl: throttle progress events, dedup adds against pending, clear filename/size on error and reject out-of-dir trashcan deletes, pin fork start-method on Linux only Frontend: - retry deletes the done record only after a successful re-add - surface HTTP errors for delete/start and reset the deleting flag - ignore late 'updated' events for rows no longer in the queue - track table rows by map key; FileSizePipe uses base-1024 Also: HTTPS-aware Docker healthcheck, dead-code removal, and shared helpers for path-containment and yt-dlp option merging. Adds/updates unit tests throughout (250 backend tests passing). Co-Authored-By: Claude Opus 4.8 --- Dockerfile | 9 +- app/bg_tasks.py | 26 ++ app/dl_formats.py | 34 +- app/main.py | 66 +++- app/subscriptions.py | 121 +++++-- app/tests/test_api.py | 87 ++++- app/tests/test_bg_tasks.py | 55 ++++ app/tests/test_config.py | 29 ++ app/tests/test_dl_formats.py | 45 ++- app/tests/test_download_queue.py | 164 +++++++++- app/tests/test_main_helpers.py | 43 ++- app/tests/test_subscriptions.py | 233 +++++++++++++- app/tests/test_ytdl_utils.py | 173 ++++++++++ app/ytdl.py | 298 +++++++++++++----- ui/src/app/app.html | 4 +- ui/src/app/app.spec.ts | 6 + ui/src/app/app.ts | 52 ++- ui/src/app/pipes/file-size.pipe.spec.ts | 13 +- ui/src/app/pipes/file-size.pipe.ts | 5 +- ui/src/app/services/downloads.service.spec.ts | 64 ++++ ui/src/app/services/downloads.service.ts | 31 +- 21 files changed, 1392 insertions(+), 166 deletions(-) create mode 100644 app/bg_tasks.py create mode 100644 app/tests/test_bg_tasks.py diff --git a/Dockerfile b/Dockerfile index f9896bc..4a72f0e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,8 @@ -FROM node:lts-alpine AS builder +# Pinned to a major version rather than the lts-alpine floating tag: that tag +# has lagged behind and resolved to a Node patch older than the Angular CLI's +# minimum supported version, breaking the build. node:22-alpine currently +# satisfies @angular/cli's >=22.22.3 requirement. +FROM node:22-alpine AS builder WORKDIR /metube COPY ui ./ @@ -66,7 +70,8 @@ ENV TEMP_DIR=/downloads ENV PORT=8081 VOLUME /downloads EXPOSE 8081 -HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 CMD curl -fsS "http://localhost:${PORT}/" || exit 1 +HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ + CMD case "$HTTPS" in true|True|on|1) curl -fsSk "https://localhost:${PORT}/";; *) curl -fsS "http://localhost:${PORT}/";; esac || exit 1 # Add build-time argument for version ARG VERSION=dev diff --git a/app/bg_tasks.py b/app/bg_tasks.py new file mode 100644 index 0000000..2a1da6c --- /dev/null +++ b/app/bg_tasks.py @@ -0,0 +1,26 @@ +import asyncio +import logging + +log = logging.getLogger("bg_tasks") +_TASKS: set[asyncio.Task] = set() + + +def create_task(coro, *, name: str | None = None) -> asyncio.Task: + """create_task that keeps a strong reference and logs unexpected failures. + + A bare ``asyncio.create_task(...)`` is only weakly referenced by the event + loop; if nothing else holds the returned Task, it can be garbage collected + mid-flight. Keeping a module-level strong reference (removed once the task + finishes) avoids that, and the done-callback surfaces otherwise-silent + failures. + """ + task = asyncio.get_running_loop().create_task(coro, name=name) + _TASKS.add(task) + + def _done(t: asyncio.Task) -> None: + _TASKS.discard(t) + if not t.cancelled() and t.exception() is not None: + log.error("Background task %s failed", t.get_name(), exc_info=t.exception()) + + task.add_done_callback(_done) + return task diff --git a/app/dl_formats.py b/app/dl_formats.py index 7933451..9fcd779 100644 --- a/app/dl_formats.py +++ b/app/dl_formats.py @@ -3,6 +3,21 @@ import copy AUDIO_FORMATS = ("m4a", "mp3", "opus", "wav", "flac") CAPTION_MODES = ("auto_only", "manual_only", "prefer_manual", "prefer_auto") + +def merge_ytdl_option_layers(presets, overrides, presets_config) -> dict: + """Overlay named presets (in order) then per-item overrides onto a fresh dict. + + Does NOT include any base ``YTDL_OPTIONS`` β€” callers layer this on top of + their own base (a per-download build adds the global base; a subscription + scan relies on ``**config.YTDL_OPTIONS`` already being present in its + params). ``presets_config`` maps a preset name to its options dict. + """ + merged: dict = {} + for name in presets or []: + merged.update(presets_config.get(name, {})) + merged.update(overrides or {}) + return merged + CODEC_FILTER_MAP = { 'h264': "[vcodec~='^(h264|avc)']", 'h265': "[vcodec~='^(h265|hevc)']", @@ -43,6 +58,10 @@ def get_format(download_type: str, codec: str, format: str, quality: str) -> str quality = (quality or "best").strip().lower() if format.startswith("custom:"): + # Unreachable via the HTTP API (format is validated against a fixed + # set in main.py), but legacy persisted downloads may carry a + # custom: format from before that validation existed; removing this + # would crash PersistentQueue.load() for those records. return format[7:] if download_type == "thumbnail": @@ -137,7 +156,20 @@ def get_opts( requested_subtitle_format = (format or "srt").lower() if requested_subtitle_format == "txt": requested_subtitle_format = "srt" - opts["subtitlesformat"] = requested_subtitle_format + opts["subtitlesformat"] = f"{requested_subtitle_format}/best" + if requested_subtitle_format in ("srt", "vtt"): + # subtitlesformat above is only a preference: if the extractor + # doesn't natively offer this ext (e.g. YouTube has no native srt), + # yt-dlp silently falls back to whatever it has. ffmpeg can only + # convert to srt/vtt/ass/lrc, so only guarantee the requested + # container for those; other formats stay best-effort. + postprocessors.append( + { + "key": "FFmpegSubtitlesConvertor", + "format": requested_subtitle_format, + "when": "before_dl", + } + ) if mode == "manual_only": opts["writesubtitles"] = True opts["writeautomaticsub"] = False diff --git a/app/main.py b/app/main.py index 03a222c..fa575a9 100644 --- a/app/main.py +++ b/app/main.py @@ -16,9 +16,11 @@ import logging import json import pathlib import re -from urllib.parse import parse_qs, unquote, urlencode, urlparse, urlunparse +import time +from urllib.parse import parse_qs, urlencode, urlparse, urlunparse from watchfiles import DefaultFilter, Change, awatch +import bg_tasks from ytdl import DownloadQueueNotifier, DownloadQueue, Download from subscriptions import SubscriptionManager, SubscriptionNotifier, SubscriptionInfo, coerce_optional_bool from yt_dlp.version import __version__ as yt_dlp_version @@ -140,6 +142,10 @@ class Config: self._validate_int('MAX_CONCURRENT_DOWNLOADS', minimum=1) self._validate_int('PORT', minimum=1, maximum=65535) self._validate_int('CLEAR_COMPLETED_AFTER', minimum=0) + self._validate_int('DEFAULT_OPTION_PLAYLIST_ITEM_LIMIT', minimum=0) + self._validate_int('SUBSCRIPTION_DEFAULT_CHECK_INTERVAL', minimum=1) + self._validate_int('SUBSCRIPTION_SCAN_PLAYLIST_END', minimum=1) + self._validate_int('SUBSCRIPTION_MAX_SEEN_IDS', minimum=1) self._runtime_overrides = {} @@ -301,7 +307,10 @@ async def state_dir_guard(request, handler): (config.URL_PREFIX + 'audio_download/', config.AUDIO_DOWNLOAD_DIR), ): if request.path.startswith(prefix): - rel = unquote(request.path[len(prefix):]) + # request.path is already percent-decoded by aiohttp; decoding it + # again would mangle a download whose filename contains a literal + # '%' (e.g. "%" turning into a truncated escape) into a false 404. + rel = request.path[len(prefix):] target = os.path.realpath(os.path.join(base, rel)) if _is_within_state_dir(target): raise web.HTTPNotFound() @@ -425,12 +434,20 @@ def _clip_field_provided_in_post(raw) -> bool: def _extract_t_query_from_url(url: str) -> tuple[str, float | None]: - """If ``t=`` is present and parseable, return URL without ``t`` and start seconds.""" + """If ``t=`` is present and parseable, return URL without ``t`` and start seconds. + + Restricted to YouTube hosts: ``t`` is a generic query parameter name that + other sites may use for unrelated purposes, so rewriting it there would + silently mutate the URL and inject a bogus clip start. + """ try: parsed = urlparse(url) params = parse_qs(parsed.query) except Exception: return url, None + host = (parsed.hostname or '').lower() + if not (host in ('youtu.be', 'youtube.com') or host.endswith('.youtube.com')): + return url, None t_values = params.get('t') if not t_values: return url, None @@ -552,6 +569,7 @@ async def _download_queue_startup(app): async def _shutdown_download_manager(app): + dqueue.close() Download.shutdown_manager() @@ -607,7 +625,7 @@ async def _schedule_nightly_update() -> None: async def _start_nightly_update_schedule(app): - asyncio.create_task(_schedule_nightly_update()) + bg_tasks.create_task(_schedule_nightly_update(), name="nightly_update_schedule") app.on_startup.append(_start_nightly_update_schedule) @@ -656,7 +674,7 @@ async def watch_files(): await sio.emit('ytdl_options_changed', serializer.encode(result)) log.info(f'Starting Watch File: {config.YTDL_OPTIONS_FILE}') - asyncio.create_task(_watch_files()) + bg_tasks.create_task(_watch_files(), name="watch_ytdl_options_file") async def _watch_files_startup(app): await watch_files() @@ -970,13 +988,20 @@ async def subscriptions_check(request): result = await submgr.check_now([str(i) for i in ids] if ids else None) return web.Response(text=serializer.encode(result)) +def _require_id_list(post: dict) -> list: + ids = post.get('ids') + if not isinstance(ids, list) or not ids or not all(isinstance(i, str) for i in ids): + raise web.HTTPBadRequest(reason="'ids' must be a non-empty list of strings") + return ids + + @routes.post(config.URL_PREFIX + 'delete') async def delete(request): post = await _read_json_request(request) - ids = post.get('ids') + ids = _require_id_list(post) where = post.get('where') - if not ids or where not in ['queue', 'done']: - log.error("Bad request: missing 'ids' or incorrect 'where' value") + if where not in ['queue', 'done']: + log.error("Bad request: incorrect 'where' value") raise web.HTTPBadRequest() status = await (dqueue.cancel(ids) if where == 'queue' else dqueue.clear(ids)) log.info(f"Download delete request processed for ids: {ids}, where: {where}") @@ -985,7 +1010,7 @@ async def delete(request): @routes.post(config.URL_PREFIX + 'start') async def start(request): post = await _read_json_request(request) - ids = post.get('ids') + ids = _require_id_list(post) log.info(f"Received request to start pending downloads for ids: {ids}") status = await dqueue.start_pending(ids) return web.Response(text=serializer.encode(status)) @@ -1065,12 +1090,15 @@ async def cookie_status(request): async def history(request): history = { 'done': [], 'queue': [], 'pending': []} - for _, v in dqueue.queue.saved_items(): - history['queue'].append(v) - for _, v in dqueue.done.saved_items(): - history['done'].append(v) - for _, v in dqueue.pending.saved_items(): - history['pending'].append(v) + # Served from the in-memory queues (like the socket 'all' event) rather + # than saved_items(), which reloads and re-compacts the on-disk state on + # every call. + for _, v in dqueue.queue.items(): + history['queue'].append(v.info) + for _, v in dqueue.done.items(): + history['done'].append(v.info) + for _, v in dqueue.pending.items(): + history['pending'].append(v.info) log.info("Sending download history") return web.Response(text=serializer.encode(history)) @@ -1082,13 +1110,17 @@ async def connect(sid, environ): await sio.emit('subscriptions_all', serializer.encode([s.to_public_dict() for s in submgr.list_all()]), to=sid) await sio.emit('configuration', serializer.encode(config.frontend_safe()), to=sid) if config.CUSTOM_DIRS: - await sio.emit('custom_dirs', serializer.encode(get_custom_dirs()), to=sid) + # get_custom_dirs() can walk the whole download tree on a cache miss; + # keep that off the event loop so a large library doesn't stall every + # client's connect handshake. + dirs = await asyncio.get_running_loop().run_in_executor(None, get_custom_dirs) + await sio.emit('custom_dirs', serializer.encode(dirs), to=sid) if config.YTDL_OPTIONS_FILE: await sio.emit('ytdl_options_changed', serializer.encode(get_options_update_time()), to=sid) def get_custom_dirs(): cache_ttl_seconds = 5 - now = asyncio.get_running_loop().time() + now = time.monotonic() cache_key = ( config.DOWNLOAD_DIR, config.AUDIO_DOWNLOAD_DIR, diff --git a/app/subscriptions.py b/app/subscriptions.py index b2662d5..81e9513 100644 --- a/app/subscriptions.py +++ b/app/subscriptions.py @@ -11,14 +11,22 @@ import time import types import uuid from dataclasses import dataclass, field, fields +from functools import partial from typing import Any, Optional import yt_dlp import yt_dlp.networking.impersonate +import bg_tasks +from dl_formats import merge_ytdl_option_layers from state_store import AtomicJsonStore, read_legacy_shelf log = logging.getLogger("subscriptions") +# How many subscription feeds to scan at once. Bounded so one slow/hung feed +# doesn't serialize the rest, without bursting a large subscription list at the +# extractor (which risks rate-limiting / bot detection). +_MAX_CONCURRENT_CHECKS = 4 + VIDEO_ONLY_MSG = ( "This URL points to a single video, not a channel or playlist. Use Download instead." ) @@ -42,7 +50,9 @@ def _impersonate_opt(ytdl_options: dict) -> dict: return opts -def _build_ydl_params(config, *, playlistend: Optional[int] = None) -> dict: +def _build_ydl_params( + config, *, playlistend: Optional[int] = None, extra_opts: Optional[dict[str, Any]] = None +) -> dict: params: dict[str, Any] = { "quiet": not logging.getLogger().isEnabledFor(logging.DEBUG), "verbose": logging.getLogger().isEnabledFor(logging.DEBUG), @@ -52,6 +62,7 @@ def _build_ydl_params(config, *, playlistend: Optional[int] = None) -> dict: "lazy_playlist": True, "paths": {"home": config.DOWNLOAD_DIR, "temp": config.TEMP_DIR}, **config.YTDL_OPTIONS, + **(extra_opts or {}), } params = _impersonate_opt(params) if playlistend is not None and playlistend > 0: @@ -76,9 +87,11 @@ def _is_media_entry(entry: Any) -> bool: return True -def extract_flat_playlist(config, url: str, playlistend: int, *, _depth: int = 0): +def extract_flat_playlist( + config, url: str, playlistend: int, *, extra_opts: Optional[dict[str, Any]] = None, _depth: int = 0 +): """Return (info_dict, entries_list) for playlist/channel URLs.""" - params = _build_ydl_params(config, playlistend=playlistend) + params = _build_ydl_params(config, playlistend=playlistend, extra_opts=extra_opts) with yt_dlp.YoutubeDL(params=params) as ydl: info = ydl.extract_info(url, download=False) if not info: @@ -104,6 +117,7 @@ def extract_flat_playlist(config, url: str, playlistend: int, *, _depth: int = 0 config, nested_url, playlistend, + extra_opts=extra_opts, _depth=_depth + 1, ) if nested_entries: @@ -370,6 +384,23 @@ class SubscriptionManager: def _save_locked(self) -> None: self._store.save({"items": [_subscription_to_record(sub) for sub in self._subs.values()]}) + def _scan_extra_opts( + self, + ytdl_options_presets: Optional[list[str]], + ytdl_options_overrides: Optional[dict[str, Any]], + ) -> dict[str, Any]: + """Merge configured presets (in order) with per-subscription overrides. + + Applied on top of the global YTDL_OPTIONS when scanning a + subscription's feed, so cookies/impersonation/etc. configured via a + preset or override also take effect during the flat-playlist scan, + not just the eventual per-video download. (The global YTDL_OPTIONS base + is already spread into the scan params by ``_build_ydl_params``.) + """ + return merge_ytdl_option_layers( + ytdl_options_presets, ytdl_options_overrides, self.config.YTDL_OPTIONS_PRESETS + ) + async def _queue_subscription_entries( self, entries: list[dict], @@ -436,12 +467,8 @@ class SubscriptionManager: def start_background_loop(self) -> None: if self._loop_task is not None and not self._loop_task.done(): return - self._loop_task = asyncio.create_task(self._periodic_loop()) - self._loop_task.add_done_callback( - lambda t: log.error("Subscription loop failed: %s", t.exception()) - if not t.cancelled() and t.exception() - else None - ) + # bg_tasks.create_task already logs unexpected task failures with the name. + self._loop_task = bg_tasks.create_task(self._periodic_loop(), name="subscription_loop") async def _periodic_loop(self) -> None: while True: @@ -465,8 +492,30 @@ class SubscriptionManager: if now - sub.last_checked < interval_sec: continue due.append(sub) - for sub in due: - await self._check_one_unlocked(sub) + await self._check_many(due) + + async def _check_many(self, subs: list[SubscriptionInfo]) -> None: + """Check subscriptions with bounded concurrency so one slow feed does + not serialize the rest. Failures are isolated per subscription.""" + if not subs: + return + sem = asyncio.Semaphore(_MAX_CONCURRENT_CHECKS) + + async def _guarded(sub: SubscriptionInfo) -> None: + async with sem: + await self._check_one_unlocked(sub) + + results = await asyncio.gather( + *(_guarded(sub) for sub in subs), return_exceptions=True + ) + for sub, result in zip(subs, results): + if isinstance(result, Exception): + log.error( + "Subscription check crashed for %s: %s", + sub.name, + result, + exc_info=result, + ) async def add_subscription( self, @@ -513,8 +562,12 @@ class SubscriptionManager: try: scan_first = max(int(getattr(self.config, "SUBSCRIPTION_SCAN_PLAYLIST_END", 50)), 1) + scan_extra_opts = self._scan_extra_opts(ytdl_options_presets, ytdl_options_overrides) try: - info, entries = extract_flat_playlist(self.config, url, scan_first) + info, entries = await asyncio.get_running_loop().run_in_executor( + None, + partial(extract_flat_playlist, self.config, url, scan_first, extra_opts=scan_extra_opts), + ) except yt_dlp.utils.YoutubeDLError as exc: return {"status": "error", "msg": str(exc)} @@ -629,6 +682,24 @@ class SubscriptionManager: except ValueError as exc: return {"status": "error", "msg": str(exc)} + enabled_set = False + validated_enabled = False + if "enabled" in changes: + try: + validated_enabled = _coerce_bool(changes["enabled"]) + enabled_set = True + except ValueError as exc: + return {"status": "error", "msg": str(exc)} + + interval_set = False + validated_interval = 0 + if "check_interval_minutes" in changes: + try: + validated_interval = max(1, int(changes["check_interval_minutes"])) + except (TypeError, ValueError): + return {"status": "error", "msg": "check_interval_minutes must be an integer"} + interval_set = True + async with self._lock: sub = self._subs.get(sub_id) if not sub: @@ -636,10 +707,10 @@ class SubscriptionManager: previous = copy.deepcopy(sub) old_enabled = sub.enabled - if "enabled" in changes: - sub.enabled = _coerce_bool(changes["enabled"]) - if "check_interval_minutes" in changes: - sub.check_interval_minutes = max(1, int(changes["check_interval_minutes"])) + if enabled_set: + sub.enabled = validated_enabled + if interval_set: + sub.check_interval_minutes = validated_interval if "name" in changes and changes["name"]: sub.name = str(changes["name"]) if validated_tr is not None: @@ -673,8 +744,7 @@ class SubscriptionManager: "Manual subscription check requested for %d subscription(s)", len(targets), ) - for sub in targets: - await self._check_one_unlocked(sub) + await self._check_many(targets) return {"status": "ok"} async def _check_one_unlocked(self, sub: SubscriptionInfo) -> None: @@ -696,15 +766,23 @@ class SubscriptionManager: async def _check_one_inner(self, sub: SubscriptionInfo) -> None: sid = sub.id scan = int(getattr(self.config, "SUBSCRIPTION_SCAN_PLAYLIST_END", 50)) + # ytdl_options_presets/overrides are set at subscription creation and + # never mutated afterwards (update_subscription doesn't allow it), so + # reading them off `sub` here without holding the lock is safe. + scan_extra_opts = self._scan_extra_opts(sub.ytdl_options_presets, sub.ytdl_options_overrides) log.info("Checking subscription: %s", sub.name) try: - info, entries = extract_flat_playlist(self.config, sub.url, scan) + info, entries = await asyncio.get_running_loop().run_in_executor( + None, + partial(extract_flat_playlist, self.config, sub.url, scan, extra_opts=scan_extra_opts), + ) except yt_dlp.utils.YoutubeDLError as exc: async with self._lock: cur = self._subs.get(sid) if cur: previous = copy.deepcopy(cur) cur.error = str(exc) + cur.last_checked = time.time() try: self._save_locked() except Exception: @@ -723,6 +801,7 @@ class SubscriptionManager: if cur: previous = copy.deepcopy(cur) cur.error = VIDEO_ONLY_MSG + cur.last_checked = time.time() try: self._save_locked() except Exception: @@ -777,6 +856,10 @@ class SubscriptionManager: eid = _entry_id(ent) if not eid: continue + # Seen entries that are currently live are deliberately re-queued: + # a stream first seen as 'upcoming' must still be captured once it + # goes live. The download queue dedups by URL while a capture is + # in flight, so this can't double-queue an active capture. if eid in seen and ent.get("live_status") != "is_live": continue new_entries.append(ent) diff --git a/app/tests/test_api.py b/app/tests/test_api.py index 6481520..8ac86b3 100644 --- a/app/tests/test_api.py +++ b/app/tests/test_api.py @@ -6,6 +6,7 @@ import json import os from pathlib import Path from unittest.mock import AsyncMock, MagicMock +from urllib.parse import quote import pytest from aiohttp import web @@ -20,6 +21,7 @@ def mock_dqueue(monkeypatch): d.initialize = AsyncMock(return_value=None) d.add = AsyncMock(return_value={"status": "ok"}) d.cancel = AsyncMock(return_value={"status": "ok"}) + d.clear = AsyncMock(return_value={"status": "ok"}) d.start_pending = AsyncMock(return_value={"status": "ok"}) d.cancel_add = MagicMock() d.queue = MagicMock() @@ -28,6 +30,9 @@ def mock_dqueue(monkeypatch): d.queue.saved_items = MagicMock(return_value=[]) d.done.saved_items = MagicMock(return_value=[]) d.pending.saved_items = MagicMock(return_value=[]) + d.queue.items = MagicMock(return_value=[]) + d.done.items = MagicMock(return_value=[]) + d.pending.items = MagicMock(return_value=[]) d.get = MagicMock(return_value=([], [])) monkeypatch.setattr(main, "dqueue", d) return d @@ -215,11 +220,35 @@ async def test_start_pending(mock_dqueue): mock_dqueue.start_pending.assert_awaited_once_with(["a"]) +@pytest.mark.asyncio +@pytest.mark.parametrize("body", [{}, {"ids": "abc"}, {"ids": []}, {"ids": [1, 2]}]) +async def test_start_rejects_malformed_ids(mock_dqueue, body): + req = _json_request(body) + with pytest.raises(web.HTTPBadRequest): + await main.start(req) + mock_dqueue.start_pending.assert_not_awaited() + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "body", + [ + {"where": "queue"}, + {"where": "queue", "ids": "abc"}, + {"where": "queue", "ids": []}, + {"where": "queue", "ids": [1, 2]}, + ], +) +async def test_delete_rejects_malformed_ids(mock_dqueue, body): + req = _json_request(body) + with pytest.raises(web.HTTPBadRequest): + await main.delete(req) + mock_dqueue.cancel.assert_not_awaited() + mock_dqueue.clear.assert_not_awaited() + + @pytest.mark.asyncio async def test_history_shape(mock_dqueue): - mock_dqueue.queue.saved_items.return_value = [] - mock_dqueue.done.saved_items.return_value = [] - mock_dqueue.pending.saved_items.return_value = [] req = MagicMock(spec=web.Request) resp = await main.history(req) assert resp.status == 200 @@ -227,6 +256,30 @@ async def test_history_shape(mock_dqueue): assert set(data.keys()) == {"done", "queue", "pending"} +@pytest.mark.asyncio +async def test_history_reads_in_memory_queues_not_disk_state(mock_dqueue): + fake_queue_dl = MagicMock() + fake_queue_dl.info = {"id": "q1", "title": "Queued"} + fake_done_dl = MagicMock() + fake_done_dl.info = {"id": "d1", "title": "Done"} + fake_pending_dl = MagicMock() + fake_pending_dl.info = {"id": "p1", "title": "Pending"} + mock_dqueue.queue.items.return_value = [("q1", fake_queue_dl)] + mock_dqueue.done.items.return_value = [("d1", fake_done_dl)] + mock_dqueue.pending.items.return_value = [("p1", fake_pending_dl)] + + req = MagicMock(spec=web.Request) + resp = await main.history(req) + assert resp.status == 200 + data = json.loads(resp.text) + assert [item["id"] for item in data["queue"]] == ["q1"] + assert [item["id"] for item in data["done"]] == ["d1"] + assert [item["id"] for item in data["pending"]] == ["p1"] + mock_dqueue.queue.saved_items.assert_not_called() + mock_dqueue.done.saved_items.assert_not_called() + mock_dqueue.pending.saved_items.assert_not_called() + + @pytest.mark.asyncio async def test_version_json(mock_dqueue): req = MagicMock(spec=web.Request) @@ -311,6 +364,24 @@ async def test_subscribe_rejects_clip_options(mock_dqueue, monkeypatch): main.submgr.add_subscription.assert_not_awaited() +@pytest.mark.asyncio +async def test_subscriptions_update_invalid_enabled_returns_error_not_500(mock_dqueue): + req = _json_request({"id": "nonexistent", "enabled": "maybe"}) + resp = await main.subscriptions_update(req) + assert resp.status == 200 + body = json.loads(resp.text) + assert body["status"] == "error" + + +@pytest.mark.asyncio +async def test_subscriptions_update_invalid_interval_returns_error_not_500(mock_dqueue): + req = _json_request({"id": "nonexistent", "check_interval_minutes": "abc"}) + resp = await main.subscriptions_update(req) + assert resp.status == 200 + body = json.loads(resp.text) + assert body["status"] == "error" + + def test_is_within_state_dir_blocks_state_subtree(): state_dir = main._STATE_DIR_REAL assert main._is_within_state_dir(state_dir) @@ -331,6 +402,11 @@ async def test_download_blocks_state_dir_files(monkeypatch): state_dir.mkdir(parents=True, exist_ok=True) (state_dir / "cookies.txt").write_text("# Netscape HTTP Cookie File\n", encoding="utf-8") (download_dir / "video.mp4").write_bytes(b"video") + # request.path is already percent-decoded by aiohttp; state_dir_guard must + # not decode it a second time, or a filename containing a literal '%' + # gets mangled into a false 404. + percent_filename = "100% done.mp4" + (download_dir / percent_filename).write_bytes(b"percent video") monkeypatch.setattr(main.config, "STATE_DIR", str(state_dir)) monkeypatch.setattr(main, "_STATE_DIR_REAL", os.path.realpath(str(state_dir))) @@ -343,7 +419,12 @@ async def test_download_blocks_state_dir_files(monkeypatch): allowed = await client.get("/download/video.mp4") assert allowed.status == 200 assert await allowed.read() == b"video" + + percent_resp = await client.get("/download/" + quote(percent_filename)) + assert percent_resp.status == 200 + assert await percent_resp.read() == b"percent video" finally: (state_dir / "cookies.txt").unlink(missing_ok=True) (download_dir / "video.mp4").unlink(missing_ok=True) + (download_dir / percent_filename).unlink(missing_ok=True) state_dir.rmdir() diff --git a/app/tests/test_bg_tasks.py b/app/tests/test_bg_tasks.py new file mode 100644 index 0000000..a9c1237 --- /dev/null +++ b/app/tests/test_bg_tasks.py @@ -0,0 +1,55 @@ +"""Tests for the ``bg_tasks.create_task`` strong-reference/logging helper.""" + +from __future__ import annotations + +import asyncio +import logging + +import pytest + +import bg_tasks + + +@pytest.mark.asyncio +async def test_create_task_removes_itself_from_registry_on_success(): + async def _ok(): + return 42 + + task = bg_tasks.create_task(_ok(), name="ok_task") + assert task in bg_tasks._TASKS + result = await task + assert result == 42 + assert task not in bg_tasks._TASKS + + +@pytest.mark.asyncio +async def test_create_task_logs_unhandled_exception(caplog): + async def _boom(): + raise ValueError("kaboom") + + with caplog.at_level(logging.ERROR, logger="bg_tasks"): + task = bg_tasks.create_task(_boom(), name="boom_task") + with pytest.raises(ValueError): + await task + # Let the done-callback (scheduled via call_soon) run. + await asyncio.sleep(0) + + assert task not in bg_tasks._TASKS + assert any("boom_task" in record.message for record in caplog.records) + + +@pytest.mark.asyncio +async def test_create_task_does_not_log_on_cancellation(caplog): + async def _sleep_forever(): + await asyncio.sleep(10) + + with caplog.at_level(logging.ERROR, logger="bg_tasks"): + task = bg_tasks.create_task(_sleep_forever(), name="cancel_task") + await asyncio.sleep(0) + task.cancel() + with pytest.raises(asyncio.CancelledError): + await task + await asyncio.sleep(0) + + assert task not in bg_tasks._TASKS + assert not any("cancel_task" in record.message for record in caplog.records) diff --git a/app/tests/test_config.py b/app/tests/test_config.py index b918a93..5fed6fd 100644 --- a/app/tests/test_config.py +++ b/app/tests/test_config.py @@ -159,6 +159,35 @@ class ConfigTests(unittest.TestCase): c = Config() self.assertEqual(c.CLEAR_COMPLETED_AFTER, "0") + def test_invalid_default_option_playlist_item_limit_exits(self): + for bad in ("-1", "many"): + with patch.dict(os.environ, _base_env(DEFAULT_OPTION_PLAYLIST_ITEM_LIMIT=bad), clear=False): + with self.assertRaises(SystemExit): + Config() + + def test_default_option_playlist_item_limit_zero_allowed(self): + with patch.dict(os.environ, _base_env(DEFAULT_OPTION_PLAYLIST_ITEM_LIMIT="0"), clear=False): + c = Config() + self.assertEqual(c.DEFAULT_OPTION_PLAYLIST_ITEM_LIMIT, "0") + + def test_invalid_subscription_default_check_interval_exits(self): + for bad in ("0", "-1", "often"): + with patch.dict(os.environ, _base_env(SUBSCRIPTION_DEFAULT_CHECK_INTERVAL=bad), clear=False): + with self.assertRaises(SystemExit): + Config() + + def test_invalid_subscription_scan_playlist_end_exits(self): + for bad in ("0", "-1", "all"): + with patch.dict(os.environ, _base_env(SUBSCRIPTION_SCAN_PLAYLIST_END=bad), clear=False): + with self.assertRaises(SystemExit): + Config() + + def test_invalid_subscription_max_seen_ids_exits(self): + for bad in ("0", "-1", "unlimited"): + with patch.dict(os.environ, _base_env(SUBSCRIPTION_MAX_SEEN_IDS=bad), clear=False): + with self.assertRaises(SystemExit): + Config() + def test_runtime_override_roundtrip(self): with patch.dict(os.environ, _base_env(), clear=False): c = Config() diff --git a/app/tests/test_dl_formats.py b/app/tests/test_dl_formats.py index f1fbf48..07e26dd 100644 --- a/app/tests/test_dl_formats.py +++ b/app/tests/test_dl_formats.py @@ -10,6 +10,7 @@ from app.dl_formats import ( _normalize_subtitle_language, get_format, get_opts, + merge_ytdl_option_layers, ) @@ -118,7 +119,31 @@ class DlFormatsTests(unittest.TestCase): def test_get_opts_captions_txt_maps_to_srt_format(self): opts = get_opts("captions", "auto", "txt", "best", {}) - self.assertEqual(opts["subtitlesformat"], "srt") + self.assertEqual(opts["subtitlesformat"], "srt/best") + keys = [p["key"] for p in opts["postprocessors"]] + self.assertIn("FFmpegSubtitlesConvertor", keys) + convertor = next(p for p in opts["postprocessors"] if p["key"] == "FFmpegSubtitlesConvertor") + self.assertEqual(convertor["format"], "srt") + + def test_get_opts_captions_srt_guarantees_convertor(self): + opts = get_opts("captions", "auto", "srt", "best", {}) + self.assertEqual(opts["subtitlesformat"], "srt/best") + keys = [p["key"] for p in opts["postprocessors"]] + self.assertIn("FFmpegSubtitlesConvertor", keys) + + def test_get_opts_captions_vtt_guarantees_convertor(self): + opts = get_opts("captions", "auto", "vtt", "best", {}) + self.assertEqual(opts["subtitlesformat"], "vtt/best") + keys = [p["key"] for p in opts["postprocessors"]] + self.assertIn("FFmpegSubtitlesConvertor", keys) + convertor = next(p for p in opts["postprocessors"] if p["key"] == "FFmpegSubtitlesConvertor") + self.assertEqual(convertor["format"], "vtt") + + def test_get_opts_captions_ttml_has_no_convertor(self): + opts = get_opts("captions", "auto", "ttml", "best", {}) + self.assertEqual(opts["subtitlesformat"], "ttml/best") + keys = [p["key"] for p in opts["postprocessors"]] + self.assertNotIn("FFmpegSubtitlesConvertor", keys) def test_get_opts_merges_existing_postprocessors(self): opts = get_opts("audio", "auto", "opus", "best", {"postprocessors": [{"key": "SponsorBlock"}]}) @@ -135,5 +160,23 @@ class DlFormatsTests(unittest.TestCase): self.assertEqual(_normalize_subtitle_language(" "), "en") +class MergeYtdlOptionLayersTests(unittest.TestCase): + def test_presets_applied_in_order_then_overrides(self): + presets_config = { + "a": {"x": 1, "y": 1}, + "b": {"y": 2, "z": 2}, + } + merged = merge_ytdl_option_layers(["a", "b"], {"z": 3, "w": 4}, presets_config) + # b overrides a's y; explicit overrides win over presets. + self.assertEqual(merged, {"x": 1, "y": 2, "z": 3, "w": 4}) + + def test_no_base_options_included(self): + # The helper only produces the preset/override layer, never base opts. + self.assertEqual(merge_ytdl_option_layers(None, None, {}), {}) + + def test_unknown_preset_names_ignored(self): + self.assertEqual(merge_ytdl_option_layers(["missing"], {"a": 1}, {}), {"a": 1}) + + if __name__ == "__main__": unittest.main() diff --git a/app/tests/test_download_queue.py b/app/tests/test_download_queue.py index 2c0dceb..4b375f6 100644 --- a/app/tests/test_download_queue.py +++ b/app/tests/test_download_queue.py @@ -3,13 +3,14 @@ from __future__ import annotations import os +import re import tempfile from unittest.mock import AsyncMock, MagicMock, patch import pytest import time -from ytdl import DownloadInfo, DownloadQueue +from ytdl import Download, DownloadInfo, DownloadQueue @pytest.fixture @@ -46,6 +47,37 @@ def test_cancel_add_increments_generation(dq_env): assert dq._add_generation == before + 1 +def test_download_queue_has_dedicated_executor_sized_from_config(dq_env): + notifier = MagicMock() + dq = DownloadQueue(dq_env, notifier) + assert dq._download_executor is not None + assert dq._download_executor._max_workers == 2 * int(dq_env.MAX_CONCURRENT_DOWNLOADS) + 2 + dq.close() + + +def test_close_cancels_running_downloads_before_shutdown(dq_env): + notifier = MagicMock() + dq = DownloadQueue(dq_env, notifier) + + running = MagicMock() + running.started.return_value = True + running.running.return_value = True + idle = MagicMock() + idle.started.return_value = False + idle.running.return_value = False + + dq.queue.dict["u-running"] = running + dq.queue.dict["u-idle"] = idle + + dq.close() + + # The active download's subprocess group is killed; the not-started one is + # left alone. Executor is shut down afterwards. + running.cancel.assert_called_once() + idle.cancel.assert_not_called() + assert dq._download_executor._shutdown + + def test_get_returns_tuple_of_lists(dq_env): notifier = MagicMock() dq = DownloadQueue(dq_env, notifier) @@ -222,6 +254,58 @@ async def test_add_entry_queues_single_video_without_reextracting(dq_env): assert dq.pending.exists("https://example.com/watch?v=1") +@pytest.mark.asyncio +async def test_add_entry_duplicate_while_pending_is_skipped_not_clobbered(dq_env): + notifier = AsyncMock() + dq = DownloadQueue(dq_env, notifier) + entry = { + "_type": "video", + "id": "vid1", + "title": "Original Title", + "url": "https://example.com/watch?v=1", + "webpage_url": "https://example.com/watch?v=1", + } + + with patch.object(DownloadQueue, "_DownloadQueue__extract_info", side_effect=AssertionError("should not re-extract")): + first = await dq.add_entry(entry, "video", "auto", "any", "best", "", "", 0, auto_start=False) + assert first["status"] == "ok" + assert "msg" not in first + + dupe_entry = {**entry, "title": "Different Title"} + second = await dq.add_entry(dupe_entry, "audio", "auto", "mp3", "best", "", "", 0, auto_start=False) + + assert second["status"] == "ok" + assert "Already in queue" in second["msg"] + # The original pending download's options must survive untouched. + pending_dl = dq.pending.get("https://example.com/watch?v=1") + assert pending_dl.info.download_type == "video" + assert pending_dl.info.title == "Original Title" + + +@pytest.mark.asyncio +async def test_add_entry_duplicate_while_queued_is_skipped(dq_env): + notifier = AsyncMock() + dq = DownloadQueue(dq_env, notifier) + entry = { + "_type": "video", + "id": "vid1", + "title": "Test Video", + "url": "https://example.com/watch?v=1", + "webpage_url": "https://example.com/watch?v=1", + } + + with patch.object(DownloadQueue, "_DownloadQueue__extract_info", side_effect=AssertionError("should not re-extract")), \ + patch.object(DownloadQueue, "_DownloadQueue__start_download", new=AsyncMock()): + first = await dq.add_entry(entry, "video", "auto", "any", "best", "", "", 0, auto_start=True) + assert first["status"] == "ok" + assert dq.queue.exists("https://example.com/watch?v=1") + + second = await dq.add_entry(entry, "video", "auto", "any", "best", "", "", 0, auto_start=True) + + assert second["status"] == "ok" + assert "Already in queue" in second["msg"] + + @pytest.mark.asyncio async def test_channel_download_uses_output_template_when_channel_template_empty(dq_env): """Channel tabs reported as playlists must honor OUTPUT_TEMPLATE when OUTPUT_TEMPLATE_CHANNEL is empty.""" @@ -526,6 +610,9 @@ async def test_add_upcoming_stream_scheduled_without_starting(dq_env): assert download.info.live_release_timestamp is not None start_mock.assert_not_called() assert url in dq._scheduled_probe_at + # The "scheduled to start at ..." message must include a UTC offset + # (a naive datetime's %z would render as an empty string here). + assert re.search(r"[+-]\d{4}$", download.info.error) @pytest.mark.asyncio @@ -784,3 +871,78 @@ def test_download_info_to_public_dict_excludes_server_only_fields(): assert public["url"] == "https://example.com/watch?v=1" assert public["title"] == "Test Video" assert public["status"] == "pending" + + +def _make_download(dq_env, *, download_type="video", status="downloading", filename=None): + info = DownloadInfo( + id="id1", + title="t", + url="http://example.com/v", + quality="best", + download_type=download_type, + codec="auto", + format="any", + folder="", + custom_name_prefix="", + error=None, + entry=None, + playlist_item_limit=0, + split_by_chapters=False, + chapter_template="", + ) + info.status = status + info.filename = filename + info.size = 123 if filename else None + return Download( + dq_env.DOWNLOAD_DIR, dq_env.TEMP_DIR, "%(title)s.%(ext)s", "%(title)s.%(ext)s", "best", "any", {}, info + ) + + +@pytest.mark.asyncio +async def test_post_download_cleanup_clears_filename_on_error(dq_env): + notifier = AsyncMock() + dq = DownloadQueue(dq_env, notifier) + download = _make_download(dq_env, status="downloading", filename="../tmp/partial.mp4") + dq.queue.put(download) + + dq._post_download_cleanup(download) + + assert download.info.status == "error" + assert download.info.filename is None + assert download.info.size is None + + +@pytest.mark.asyncio +async def test_post_download_cleanup_keeps_captured_subtitles_on_error(dq_env): + notifier = AsyncMock() + dq = DownloadQueue(dq_env, notifier) + download = _make_download(dq_env, download_type="captions", status="downloading", filename="en.srt") + download.info.subtitle_files = [{"filename": "en.srt", "size": 42}] + dq.queue.put(download) + + dq._post_download_cleanup(download) + + assert download.info.status == "error" + assert download.info.filename == "en.srt" + + +@pytest.mark.asyncio +async def test_clear_skips_deletion_outside_download_directory(dq_env): + notifier = AsyncMock() + dq_env.DELETE_FILE_ON_TRASHCAN = True + dq = DownloadQueue(dq_env, notifier) + + outside_dir = tempfile.mkdtemp() + outside_file = os.path.join(outside_dir, "outside.txt") + with open(outside_file, "w") as f: + f.write("do not delete me") + + # A crafted/legacy relative filename that escapes DOWNLOAD_DIR via '..'. + escaping_filename = os.path.relpath(outside_file, dq_env.DOWNLOAD_DIR) + download = _make_download(dq_env, status="finished", filename=escaping_filename) + dq.done.put(download) + + await dq.clear([download.info.url]) + + assert os.path.exists(outside_file) + assert not dq.done.exists(download.info.url) diff --git a/app/tests/test_main_helpers.py b/app/tests/test_main_helpers.py index d39e54b..282e940 100644 --- a/app/tests/test_main_helpers.py +++ b/app/tests/test_main_helpers.py @@ -220,42 +220,61 @@ class ParseDownloadOptionsTests(unittest.TestCase): def test_clip_url_t_param_strips_query_and_sets_start(self): parsed = main.parse_download_options({ - "url": "https://example.com/watch?v=1&t=855s", + "url": "https://www.youtube.com/watch?v=1&t=855s", "download_type": "video", "codec": "auto", "format": "any", "quality": "best", }) - self.assertEqual(parsed["url"], "https://example.com/watch?v=1") + self.assertEqual(parsed["url"], "https://www.youtube.com/watch?v=1") self.assertEqual(parsed["clip_start"], 855.0) self.assertIsNone(parsed["clip_end"]) def test_clip_explicit_start_wins_over_url_t(self): parsed = main.parse_download_options({ - "url": "https://example.com/watch?v=1&t=100", + "url": "https://www.youtube.com/watch?v=1&t=100", "download_type": "video", "codec": "auto", "format": "any", "quality": "best", "clip_start": "50", }) - self.assertEqual(parsed["url"], "https://example.com/watch?v=1") + self.assertEqual(parsed["url"], "https://www.youtube.com/watch?v=1") self.assertEqual(parsed["clip_start"], 50.0) self.assertIsNone(parsed["clip_end"]) def test_clip_end_only_sets_start_zero_and_strips_url_t(self): parsed = main.parse_download_options({ - "url": "https://example.com/watch?v=1&t=999", + "url": "https://www.youtube.com/watch?v=1&t=999", "download_type": "video", "codec": "auto", "format": "any", "quality": "best", "clip_end": "60", }) - self.assertEqual(parsed["url"], "https://example.com/watch?v=1") + self.assertEqual(parsed["url"], "https://www.youtube.com/watch?v=1") self.assertEqual(parsed["clip_start"], 0.0) self.assertEqual(parsed["clip_end"], 60.0) + def test_clip_url_t_param_ignored_on_non_youtube_host(self): + # 't' is a generic query param name; only rewrite it on YouTube hosts + # so an unrelated site's URL isn't silently mutated with a bogus clip. + parsed = main.parse_download_options({ + "url": "https://example.com/watch?v=1&t=855s", + "download_type": "video", + "codec": "auto", + "format": "any", + "quality": "best", + }) + self.assertEqual(parsed["url"], "https://example.com/watch?v=1&t=855s") + self.assertIsNone(parsed["clip_start"]) + self.assertIsNone(parsed["clip_end"]) + + def test_extract_t_query_youtu_be_short_host(self): + cleaned, start = main._extract_t_query_from_url("https://youtu.be/abc123?t=90") + self.assertEqual(cleaned, "https://youtu.be/abc123") + self.assertEqual(start, 90.0) + def test_clip_rejects_end_before_start(self): with self.assertRaises(main.web.HTTPBadRequest): main.parse_download_options({ @@ -280,5 +299,17 @@ class ParseDownloadOptionsTests(unittest.TestCase): }) +class GetCustomDirsTests(unittest.TestCase): + def test_works_without_a_running_event_loop(self): + # get_custom_dirs() used to time its cache via + # asyncio.get_running_loop().time(), which raises RuntimeError outside + # a running loop (e.g. when called from a plain executor thread). It + # must work from a synchronous context too. + result = main.get_custom_dirs() + self.assertIn("download_dir", result) + self.assertIn("audio_download_dir", result) + self.assertIn("", result["download_dir"]) + + if __name__ == "__main__": unittest.main() diff --git a/app/tests/test_subscriptions.py b/app/tests/test_subscriptions.py index 525db11..0f97af1 100644 --- a/app/tests/test_subscriptions.py +++ b/app/tests/test_subscriptions.py @@ -1,10 +1,12 @@ from __future__ import annotations +import asyncio import json import os import shelve import sys import tempfile +import time import types import unittest from unittest.mock import patch @@ -29,6 +31,7 @@ sys.modules.setdefault("yt_dlp.networking", fake_networking) sys.modules.setdefault("yt_dlp.networking.impersonate", fake_impersonate) from subscriptions import ( + SubscriptionInfo, SubscriptionManager, _is_subscriber_only_entry, coerce_optional_bool, @@ -44,6 +47,7 @@ class _Config: self.DOWNLOAD_DIR = state_dir self.TEMP_DIR = state_dir self.YTDL_OPTIONS = {} + self.YTDL_OPTIONS_PRESETS = {} class _Queue: @@ -571,8 +575,16 @@ class SubscriptionPersistenceTests(unittest.IsolatedAsyncioTestCase): ) sub_id = result["subscription"]["id"] - with self.assertRaises(ValueError): - await mgr.update_subscription(sub_id, {"enabled": "maybe"}) + update_result = await mgr.update_subscription(sub_id, {"enabled": "maybe"}) + self.assertEqual(update_result["status"], "error") + stored = mgr.get(sub_id) + self.assertTrue(stored.enabled) + + update_result = await mgr.update_subscription( + sub_id, {"check_interval_minutes": "abc"} + ) + self.assertEqual(update_result["status"], "error") + self.assertEqual(mgr.get(sub_id).check_interval_minutes, 60) async def test_add_subscription_rejects_invalid_title_regex(self): with tempfile.TemporaryDirectory() as tmp: @@ -1012,6 +1024,223 @@ class ExtractFlatPlaylistTests(unittest.TestCase): self.assertEqual(info.get("_type"), "playlist") self.assertEqual([entry["webpage_url"] for entry in entries], ["https://example.com/v1"]) + def test_extra_opts_applied_on_top_of_config_options(self): + captured: dict = {} + + class _FakeYDL: + def __init__(self, params): + captured.update(params) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc, tb): + return False + + def extract_info(self, url, download=False): + return {"_type": "video"} + + cfg = _Config(tempfile.mkdtemp()) + with patch("subscriptions.yt_dlp.YoutubeDL", _FakeYDL, create=True): + extract_flat_playlist(cfg, "https://example.com/v1", 50, extra_opts={"cookiefile": "x"}) + + self.assertEqual(captured.get("cookiefile"), "x") + + +def _make_scan_capturing_fake_ydl(captured_params: list, entries: list[dict]): + class _FakeYDL: + def __init__(self, params): + captured_params.append(params) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc, tb): + return False + + def extract_info(self, url, download=False): + return {"_type": "channel", "title": "Channel", "entries": entries} + + return _FakeYDL + + +class SubscriptionScanExtraOptsTests(unittest.IsolatedAsyncioTestCase): + async def test_add_subscription_scan_applies_presets_and_overrides(self): + captured_params: list = [] + fake_ydl = _make_scan_capturing_fake_ydl( + captured_params, + [{"id": "v1", "title": "One", "webpage_url": "https://example.com/v1"}], + ) + + with tempfile.TemporaryDirectory() as tmp: + cfg = _Config(tmp) + cfg.YTDL_OPTIONS_PRESETS = {"mypreset": {"cookiefile": "preset.txt"}} + mgr = SubscriptionManager(cfg, _Queue(), _Notifier()) + + with patch("subscriptions.yt_dlp.YoutubeDL", fake_ydl, create=True): + await mgr.add_subscription( + "https://example.com/channel", + check_interval_minutes=60, + download_type="video", + codec="auto", + format="any", + quality="best", + folder="", + custom_name_prefix="", + auto_start=True, + playlist_item_limit=0, + split_by_chapters=False, + chapter_template="", + subtitle_language="en", + subtitle_mode="prefer_manual", + ytdl_options_presets=["mypreset"], + ytdl_options_overrides={"extra": "override"}, + ) + + self.assertTrue(captured_params) + self.assertEqual(captured_params[0].get("cookiefile"), "preset.txt") + self.assertEqual(captured_params[0].get("extra"), "override") + + async def test_check_now_scan_applies_stored_subscription_presets(self): + entries = [{"id": "v1", "title": "One", "webpage_url": "https://example.com/v1"}] + + with tempfile.TemporaryDirectory() as tmp: + cfg = _Config(tmp) + cfg.YTDL_OPTIONS_PRESETS = {"mypreset": {"cookiefile": "preset.txt"}} + mgr = SubscriptionManager(cfg, _Queue(), _Notifier()) + + add_captured: list = [] + with patch( + "subscriptions.yt_dlp.YoutubeDL", + _make_scan_capturing_fake_ydl(add_captured, entries), + create=True, + ): + result = await mgr.add_subscription( + "https://example.com/channel", + check_interval_minutes=60, + download_type="video", + codec="auto", + format="any", + quality="best", + folder="", + custom_name_prefix="", + auto_start=True, + playlist_item_limit=0, + split_by_chapters=False, + chapter_template="", + subtitle_language="en", + subtitle_mode="prefer_manual", + ytdl_options_presets=["mypreset"], + ) + sub_id = result["subscription"]["id"] + + check_captured: list = [] + with patch( + "subscriptions.yt_dlp.YoutubeDL", + _make_scan_capturing_fake_ydl(check_captured, entries), + create=True, + ): + await mgr.check_now([sub_id]) + + self.assertTrue(check_captured) + self.assertEqual(check_captured[0].get("cookiefile"), "preset.txt") + + +class SubscriptionEventLoopTests(unittest.IsolatedAsyncioTestCase): + async def test_check_now_does_not_block_event_loop(self): + with tempfile.TemporaryDirectory() as tmp: + queue = _Queue() + mgr = SubscriptionManager(_Config(tmp), queue, _Notifier()) + + with patch( + "subscriptions.extract_flat_playlist", + return_value=( + {"_type": "channel", "title": "Channel"}, + [{"id": "v1", "title": "One", "webpage_url": "https://example.com/v1"}], + ), + ): + result = await mgr.add_subscription( + "https://example.com/channel", + check_interval_minutes=60, + download_type="video", + codec="auto", + format="any", + quality="best", + folder="", + custom_name_prefix="", + auto_start=True, + playlist_item_limit=0, + split_by_chapters=False, + chapter_template="", + subtitle_language="en", + subtitle_mode="prefer_manual", + ) + sub_id = result["subscription"]["id"] + + def _slow_extract(config, url, playlistend, **kwargs): + time.sleep(0.3) + return ( + {"_type": "channel", "title": "Channel"}, + [{"id": "v1", "title": "One", "webpage_url": "https://example.com/v1"}], + ) + + with patch("subscriptions.extract_flat_playlist", side_effect=_slow_extract): + check_task = asyncio.ensure_future(mgr.check_now([sub_id])) + # If check_now() blocked the event loop, this would not complete + # until after the slow extraction finishes. + await asyncio.wait_for(asyncio.sleep(0.05), timeout=0.2) + self.assertFalse(check_task.done()) + await check_task + + async def test_check_many_isolates_a_crashing_subscription(self): + with tempfile.TemporaryDirectory() as tmp: + mgr = SubscriptionManager(_Config(tmp), _Queue(), _Notifier()) + + good = SubscriptionInfo(id="good", name="Good", url="https://example.com/good") + bad = SubscriptionInfo(id="bad", name="Bad", url="https://example.com/bad") + other = SubscriptionInfo(id="other", name="Other", url="https://example.com/other") + + checked: list[str] = [] + + async def fake_check(sub): + if sub.id == "bad": + raise RuntimeError("boom") + checked.append(sub.id) + + with patch.object(mgr, "_check_one_unlocked", side_effect=fake_check): + # The crashing subscription must not prevent the others running. + await mgr._check_many([good, bad, other]) + + self.assertIn("good", checked) + self.assertIn("other", checked) + + async def test_check_many_bounded_concurrency(self): + with tempfile.TemporaryDirectory() as tmp: + mgr = SubscriptionManager(_Config(tmp), _Queue(), _Notifier()) + subs = [ + SubscriptionInfo(id=str(i), name=str(i), url=f"https://example.com/{i}") + for i in range(10) + ] + + import subscriptions as subs_mod + + concurrent = 0 + peak = 0 + + async def fake_check(sub): + nonlocal concurrent, peak + concurrent += 1 + peak = max(peak, concurrent) + await asyncio.sleep(0.02) + concurrent -= 1 + + with patch.object(mgr, "_check_one_unlocked", side_effect=fake_check): + await mgr._check_many(subs) + + # Never exceed the configured bound, but do run more than one at once. + self.assertLessEqual(peak, subs_mod._MAX_CONCURRENT_CHECKS) + self.assertGreater(peak, 1) + if __name__ == "__main__": unittest.main() diff --git a/app/tests/test_ytdl_utils.py b/app/tests/test_ytdl_utils.py index 0f2e66d..36c2451 100644 --- a/app/tests/test_ytdl_utils.py +++ b/app/tests/test_ytdl_utils.py @@ -3,12 +3,14 @@ from __future__ import annotations import pickle +import signal import sys import tempfile import threading import types import unittest from pathlib import Path +from unittest.mock import MagicMock, patch fake_yt_dlp = types.ModuleType("yt_dlp") fake_networking = types.ModuleType("yt_dlp.networking") @@ -37,6 +39,7 @@ sys.modules.setdefault("yt_dlp.networking.impersonate", fake_impersonate) sys.modules.setdefault("yt_dlp.utils", fake_utils) from ytdl import ( + Download, DownloadInfo, _compact_persisted_entry, _convert_srt_to_txt_file, @@ -160,6 +163,105 @@ class SanitizeEntryForPickleTests(unittest.TestCase): self.assertEqual(out, {"z": 1, "a": 2}) +def _make_test_download() -> Download: + info = DownloadInfo( + id="id1", + title="t", + url="http://example.com/v", + quality="best", + download_type="video", + codec="auto", + format="any", + folder="", + custom_name_prefix="", + error=None, + entry=None, + playlist_item_limit=0, + split_by_chapters=False, + chapter_template="", + ) + return Download("/tmp", "/tmp", "%(title)s.%(ext)s", "%(title)s.%(ext)s", "best", "any", {}, info) + + +class ProgressThrottleTests(unittest.TestCase): + def test_downloading_ticks_are_throttled(self): + dl = _make_test_download() + forwarded = [] + dl.status_queue = types.SimpleNamespace(put=forwarded.append) + hook = dl._make_progress_hook() + + with patch("ytdl.time.monotonic", side_effect=[100.0, 100.1, 100.6, 100.7]): + hook({"status": "downloading", "downloaded_bytes": 1}) + hook({"status": "downloading", "downloaded_bytes": 2}) + hook({"status": "downloading", "downloaded_bytes": 3}) + hook({"status": "downloading", "downloaded_bytes": 4}) + + # Only the 1st and 3rd ticks are >= 0.5s apart from the last forwarded one. + self.assertEqual(len(forwarded), 2) + + def test_finished_and_error_statuses_always_forwarded(self): + dl = _make_test_download() + forwarded = [] + dl.status_queue = types.SimpleNamespace(put=forwarded.append) + hook = dl._make_progress_hook() + + with patch("ytdl.time.monotonic", side_effect=[200.0, 200.1]): + hook({"status": "downloading"}) + hook({"status": "finished"}) + hook({"status": "downloading"}) + hook({"status": "error", "msg": "boom"}) + + statuses = [item.get("status") for item in forwarded] + self.assertIn("finished", statuses) + self.assertIn("error", statuses) + + +class CancelProcessGroupTests(unittest.TestCase): + def test_cancel_kills_group_when_child_is_group_leader(self): + # Child successfully ran os.setpgrp(): its pgid equals its own pid. + dl = _make_test_download() + dl.proc = types.SimpleNamespace(pid=4321) + dl.status_queue = types.SimpleNamespace(put=lambda _item: None) + + with patch.object(Download, "running", return_value=True), \ + patch("ytdl.os.getpgid", return_value=4321) as mock_getpgid, \ + patch("ytdl.os.killpg") as mock_killpg: + dl.cancel() + + mock_getpgid.assert_called_once_with(4321) + mock_killpg.assert_called_once_with(4321, signal.SIGKILL) + self.assertTrue(dl.canceled) + + def test_cancel_does_not_killpg_parent_group_kills_child_only(self): + # Child has NOT become its own group leader yet (pgid != pid, e.g. it is + # still in the server's process group). killpg must NOT be called β€” that + # would SIGKILL the whole server β€” and we fall back to proc.kill(). + dl = _make_test_download() + dl.proc = types.SimpleNamespace(pid=4321, kill=MagicMock()) + dl.status_queue = types.SimpleNamespace(put=lambda _item: None) + + with patch.object(Download, "running", return_value=True), \ + patch("ytdl.os.getpgid", return_value=999), \ + patch("ytdl.os.killpg") as mock_killpg: + dl.cancel() + + mock_killpg.assert_not_called() + dl.proc.kill.assert_called_once() + self.assertTrue(dl.canceled) + + def test_cancel_falls_back_to_proc_kill_when_getpgid_unavailable(self): + dl = _make_test_download() + dl.proc = types.SimpleNamespace(pid=4321, kill=MagicMock()) + dl.status_queue = types.SimpleNamespace(put=lambda _item: None) + + with patch.object(Download, "running", return_value=True), \ + patch("ytdl.os.getpgid", side_effect=OSError("no such process")): + dl.cancel() + + dl.proc.kill.assert_called_once() + self.assertTrue(dl.canceled) + + class ConvertSrtToTxtTests(unittest.TestCase): def test_basic_conversion(self): srt = """1 @@ -180,6 +282,77 @@ Second line self.assertIn("Hello world", content) self.assertIn("Second line", content) + def test_vtt_input_strips_header_and_metadata(self): + # yt-dlp can fall back to VTT even when srt/txt was requested (the + # extractor may not offer a native srt track); the converter must not + # leak VTT-only header/metadata lines into the plain-text output. + vtt = """WEBVTT +Kind: captions +Language: en + +NOTE +This is a note block + +1 +00:00:01.000 --> 00:00:02.000 +Hello world + +2 +00:00:03.000 --> 00:00:04.000 +Second line +""" + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / "sub.vtt" + path.write_text(vtt, encoding="utf-8") + txt_path = _convert_srt_to_txt_file(str(path)) + self.assertIsNotNone(txt_path) + content = Path(txt_path).read_text(encoding="utf-8") + self.assertIn("Hello world", content) + self.assertIn("Second line", content) + self.assertNotIn("WEBVTT", content) + self.assertNotIn("Kind:", content) + self.assertNotIn("Language:", content) + self.assertNotIn("This is a note block", content) + + def test_vtt_standalone_header_block_is_stripped(self): + # Some VTT files put a blank line after WEBVTT, so Kind:/Language: form + # their own block. That header block (before the first timed cue) must + # still be stripped. + vtt = """WEBVTT + +Kind: captions +Language: en + +1 +00:00:01.000 --> 00:00:02.000 +Hello world +""" + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / "sub.vtt" + path.write_text(vtt, encoding="utf-8") + content = Path(_convert_srt_to_txt_file(str(path))).read_text(encoding="utf-8") + self.assertIn("Hello world", content) + self.assertNotIn("Kind:", content) + self.assertNotIn("Language:", content) + + def test_cue_text_starting_with_metadata_keyword_is_kept(self): + # A real caption line beginning with "Kind:"/"Language:" must NOT be + # dropped as if it were VTT header metadata. + srt = """1 +00:00:01,000 --> 00:00:02,000 +Kind: regards, everyone + +2 +00:00:03,000 --> 00:00:04,000 +Language: they spoke French +""" + with tempfile.TemporaryDirectory() as tmp: + path = Path(tmp) / "sub.srt" + path.write_text(srt, encoding="utf-8") + content = Path(_convert_srt_to_txt_file(str(path))).read_text(encoding="utf-8") + self.assertIn("Kind: regards, everyone", content) + self.assertIn("Language: they spoke French", content) + class DownloadInfoSetstateTests(unittest.TestCase): def _base_state(self, **kwargs): diff --git a/app/ytdl.py b/app/ytdl.py index 207d35e..43a7772 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -9,21 +9,42 @@ from collections import OrderedDict import time import asyncio import multiprocessing +from concurrent.futures import ThreadPoolExecutor from functools import partial import logging import re +import signal +import sys import types from typing import Any, Optional import yt_dlp.networking.impersonate from yt_dlp.utils import STR_FORMAT_RE_TMPL, STR_FORMAT_TYPES -from dl_formats import get_format, get_opts, AUDIO_FORMATS +import bg_tasks +from dl_formats import get_format, get_opts, AUDIO_FORMATS, merge_ytdl_option_layers from datetime import datetime from state_store import AtomicJsonStore, from_json_compatible, read_legacy_shelf, to_json_compatible from subscriptions import _entry_id log = logging.getLogger('ytdl') +# Python 3.14 switches the default multiprocessing start method on Linux +# (this app's only supported deployment target, per the Dockerfile) from fork +# to forkserver. Download._download relies on inheriting process state the +# way fork provides, so pin it back on Linux specifically. +# +# This must NOT be widened to "prefer fork wherever available": on macOS the +# platform default has long been spawn (never fork) precisely because forking +# a multi-threaded parent is hazardous β€” inherited locks held by threads that +# vanish in the child can deadlock it silently before it does any work. This +# app creates background threads (executors, notifier callbacks) well before +# any download starts, so forcing fork there reproduces exactly that hazard. +_MP_CTX = ( + multiprocessing.get_context("fork") + if sys.platform.startswith("linux") and "fork" in multiprocessing.get_all_start_methods() + else multiprocessing.get_context() +) + _LIVE_CHECK_INTERVAL = 60 _LIVE_MAX_CHECK_INTERVAL = 3600 # Consecutive probe failures (network blips, rate limits, transient extractor @@ -31,6 +52,22 @@ _LIVE_MAX_CHECK_INTERVAL = 3600 _LIVE_PROBE_MAX_FAILURES = 5 +def _is_within_directory(real_base: str, real_target: str) -> bool: + """True if ``real_target`` is inside (or equal to) ``real_base``. + + Both arguments must already be resolved with ``os.path.realpath``. Uses + ``commonpath`` rather than ``startswith`` so a sibling directory sharing a + name prefix (e.g. base ``/downloads`` vs ``/downloads-secret``) cannot be + reached via ``../downloads-secret``. + """ + try: + return os.path.commonpath([real_base, real_target]) == real_base + except ValueError: + # Raised when paths are on different drives (Windows) or mix + # absolute/relative; treat as outside the base directory. + return False + + # Characters that are invalid in Windows/NTFS path components. These are pre- # sanitised when substituting playlist/channel titles into output templates so # that downloads do not fail on NTFS-mounted volumes or Windows Docker hosts. @@ -133,10 +170,28 @@ def _convert_srt_to_txt_file(subtitle_path: str): # Normalize newlines so cue splitting is consistent across platforms. content = content.replace("\r\n", "\n").replace("\r", "\n") cues = [] + # The caption format may resolve to a VTT file even when srt/txt was + # requested (yt-dlp falls back when the extractor doesn't offer the + # requested ext). VTT header metadata (WEBVTT/NOTE/STYLE and + # "Kind:"/"Language:" fields) only ever appears BEFORE the first timed + # cue, so only strip it while still in that header region β€” otherwise a + # real caption line like "Kind: regards" would be dropped as metadata. + seen_cue = False for block in re.split(r"\n{2,}", content): lines = [line.strip() for line in block.split("\n") if line.strip()] if not lines: continue + has_timing = any("-->" in line for line in lines) + if not seen_cue and not has_timing: + # Still in the header region: drop metadata-only blocks + # (WEBVTT/NOTE/STYLE, or blocks made entirely of "Key: value" + # header fields such as a standalone "Kind:/Language:" block). + if re.match(r"^(WEBVTT|NOTE|STYLE)\b", lines[0]) or all( + re.match(r"^[A-Za-z][\w-]*:\s", line) for line in lines + ): + continue + if has_timing: + seen_cue = True if re.fullmatch(r"\d+", lines[0]): lines = lines[1:] if lines and "-->" in lines[0]: @@ -442,23 +497,50 @@ class Download: self.proc = None self.loop = None self.notifier = None + self._executor = None + + # Minimum interval between forwarded 'downloading' progress ticks. yt-dlp + # emits these many times per second; without throttling, each active + # download broadcasts hundreds of socket.io events/sec to every client. + _PROGRESS_THROTTLE_SECONDS = 0.5 + + def _make_progress_hook(self): + last_forward = 0.0 + + def put_status(st): + nonlocal last_forward + if st.get('status') == 'downloading': + now = time.monotonic() + if now - last_forward < self._PROGRESS_THROTTLE_SECONDS: + return + last_forward = now + self.status_queue.put({k: v for k, v in st.items() if k in ( + 'tmpfilename', + 'filename', + 'status', + 'msg', + 'total_bytes', + 'total_bytes_estimate', + 'downloaded_bytes', + 'speed', + 'eta', + )}) + + return put_status def _download(self): + # Run in our own process group so cancel() can SIGKILL the whole + # group (yt-dlp + any ffmpeg children it spawned for merge/postproc), + # instead of orphaning ffmpeg when only the yt-dlp process is killed. + if hasattr(os, 'setpgrp'): + try: + os.setpgrp() + except OSError: + pass log.info(f"Starting download for: {self.info.title} ({self.info.url})") try: debug_logging = logging.getLogger().isEnabledFor(logging.DEBUG) - def put_status(st): - self.status_queue.put({k: v for k, v in st.items() if k in ( - 'tmpfilename', - 'filename', - 'status', - 'msg', - 'total_bytes', - 'total_bytes_estimate', - 'downloaded_bytes', - 'speed', - 'eta', - )}) + put_status = self._make_progress_hook() def put_status_postprocessor(d): if d['postprocessor'] == 'MoveFiles' and d['status'] == 'finished': @@ -530,19 +612,20 @@ class Download: log.error(f"Download error for {self.info.title}: {str(exc)}") self.status_queue.put({'status': 'error', 'msg': str(exc)}) - async def start(self, notifier): + async def start(self, notifier, executor=None): log.info(f"Preparing download for: {self.info.title}") if Download.manager is None: - Download.manager = multiprocessing.Manager() + Download.manager = _MP_CTX.Manager() self.status_queue = Download.manager.Queue() - self.proc = multiprocessing.Process(target=self._download) + self.proc = _MP_CTX.Process(target=self._download) self.proc.start() self.loop = asyncio.get_running_loop() self.notifier = notifier + self._executor = executor self.info.status = 'preparing' await self.notifier.updated(self.info) self.status_task = asyncio.create_task(self.update_status()) - await self.loop.run_in_executor(None, self.proc.join) + await self.loop.run_in_executor(self._executor, self.proc.join) # Signal update_status to stop and wait for it to finish # so that all status updates (including MoveFiles with correct # file size) are processed before _post_download_cleanup runs. @@ -553,10 +636,26 @@ class Download: def cancel(self): log.info(f"Cancelling download: {self.info.title}") if self.running(): + killed_group = False try: - self.proc.kill() - except Exception as e: - log.error(f"Error killing process for {self.info.title}: {e}") + pgid = os.getpgid(self.proc.pid) + # Only kill the whole group (yt-dlp + any ffmpeg children) when + # the child actually became its own group leader via + # os.setpgrp() in _download() β€” that sets its pgid equal to its + # own pid. If it hasn't run setpgrp() yet, or setpgrp() failed, + # its pgid is still the SERVER's group and killpg would SIGKILL + # the entire MeTube process (PID 1 in Docker). Fall back to + # killing just the child process by pid in that case. + if pgid == self.proc.pid: + os.killpg(pgid, signal.SIGKILL) + killed_group = True + except (OSError, AttributeError): + pass + if not killed_group: + try: + self.proc.kill() + except Exception as e: + log.error(f"Error killing process for {self.info.title}: {e}") self.canceled = True if self.status_queue is not None: self.status_queue.put(None) @@ -577,7 +676,13 @@ class Download: async def update_status(self): while True: - status = await self.loop.run_in_executor(None, self.status_queue.get) + try: + status = await self.loop.run_in_executor(self._executor, self.status_queue.get) + except RuntimeError: + # The download executor was shut down (server shutting down); + # stop polling instead of raising a noisy background-task error. + log.info(f"Status polling stopped (executor shut down) for: {self.info.title}") + return if status is None: log.info(f"Status update finished for: {self.info.title}") return @@ -771,10 +876,6 @@ class PersistentQueue: self.dict[key] = old raise - def next(self): - k, v = next(iter(self.dict.items())) - return k, v - def empty(self): return not bool(self.dict) @@ -787,6 +888,13 @@ class DownloadQueue: self.pending = PersistentQueue("pending", self.config.STATE_DIR + '/pending') self.active_downloads = set() self.semaphore = asyncio.Semaphore(int(self.config.MAX_CONCURRENT_DOWNLOADS)) + # Each active download parks two threads for its whole duration + # (proc.join + status_queue.get). A dedicated pool keeps those from + # starving the default executor, which extract_info/live-probes also use. + self._download_executor = ThreadPoolExecutor( + max_workers=2 * int(self.config.MAX_CONCURRENT_DOWNLOADS) + 2, + thread_name_prefix="dl", + ) self.done.load() self._add_generation = 0 self._canceled_urls = set() # URLs canceled during current playlist add @@ -820,18 +928,14 @@ class DownloadQueue: async def initialize(self): log.info("Initializing DownloadQueue") self._start_live_monitor() - asyncio.create_task(self.__import_queue()) - asyncio.create_task(self.__import_pending()) + bg_tasks.create_task(self.__import_queue(), name="import_queue") + bg_tasks.create_task(self.__import_pending(), name="import_pending") def _start_live_monitor(self) -> None: if self._live_monitor_task is not None and not self._live_monitor_task.done(): return - self._live_monitor_task = asyncio.create_task(self._live_monitor_loop()) - self._live_monitor_task.add_done_callback( - lambda t: log.error("Live monitor loop failed: %s", t.exception()) - if not t.cancelled() and t.exception() - else None - ) + # bg_tasks.create_task already logs unexpected task failures with the name. + self._live_monitor_task = bg_tasks.create_task(self._live_monitor_loop(), name="live_monitor") def _register_scheduled(self, download: Download) -> None: self._scheduled_probe_at[download.info.url] = 0 @@ -964,7 +1068,7 @@ class DownloadQueue: info.error = None info.msg = None await self.notifier.updated(info) - asyncio.create_task(self.__start_download(download)) + bg_tasks.create_task(self.__start_download(download), name="start_download") def _schedule_upcoming_download(self, download: Download) -> None: download.info.status = 'scheduled' @@ -976,7 +1080,7 @@ class DownloadQueue: download.info.status = 'pending' download.info.error = None download.info.msg = None - asyncio.create_task(self.__start_download(download)) + bg_tasks.create_task(self.__start_download(download), name="start_download") async def __start_download(self, download): if download.canceled: @@ -986,7 +1090,7 @@ class DownloadQueue: if download.canceled: log.info(f"Download {download.info.title} was canceled, skipping start.") return - await download.start(self.notifier) + await download.start(self.notifier, self._download_executor) self._post_download_cleanup(download) def _post_download_cleanup(self, download): @@ -997,22 +1101,35 @@ class DownloadQueue: except OSError: pass download.info.status = 'error' + # A progress tick may have set filename to a temp-directory + # relative path before the error occurred; clear it so the UI + # doesn't render a broken link (or, worse, so a later trashcan + # delete doesn't act on a path outside the download directory). + # Captions downloads may still have captured valid subtitle + # files even when the overall status is 'error' β€” keep those. + has_captured_subtitles = bool(getattr(download.info, 'subtitle_files', None)) + if not (download.info.download_type == 'captions' and has_captured_subtitles): + download.info.filename = None + download.info.size = None download.close() if self.queue.exists(download.info.url): self.queue.delete(download.info.url) if download.canceled: - asyncio.create_task(self.notifier.canceled(download.info.url)) + bg_tasks.create_task(self.notifier.canceled(download.info.url), name="notify_canceled") else: self.done.put(download) - asyncio.create_task(self.notifier.completed(download.info)) + bg_tasks.create_task(self.notifier.completed(download.info), name="notify_completed") try: clear_after = int(self.config.CLEAR_COMPLETED_AFTER) except ValueError: log.error(f'CLEAR_COMPLETED_AFTER is set to an invalid value "{self.config.CLEAR_COMPLETED_AFTER}", expected an integer number of seconds') clear_after = 0 if clear_after > 0: - task = asyncio.create_task(self.__auto_clear_after_delay(download.info.url, clear_after)) - task.add_done_callback(lambda t: log.error(f'Auto-clear task failed: {t.exception()}') if not t.cancelled() and t.exception() else None) + # bg_tasks.create_task already logs unexpected task failures. + bg_tasks.create_task( + self.__auto_clear_after_delay(download.info.url, clear_after), + name="auto_clear", + ) async def __auto_clear_after_delay(self, url, delay_seconds): await asyncio.sleep(delay_seconds) @@ -1023,9 +1140,9 @@ class DownloadQueue: def _build_ytdl_options(self, ytdl_options_presets=None, ytdl_options_overrides=None): """Merge global options, presets (in order), and per-download overrides.""" opts = dict(self.config.YTDL_OPTIONS) - for preset_name in ytdl_options_presets or []: - opts.update(self.config.YTDL_OPTIONS_PRESETS.get(preset_name, {})) - opts.update(ytdl_options_overrides or {}) + opts.update(merge_ytdl_option_layers( + ytdl_options_presets, ytdl_options_overrides, self.config.YTDL_OPTIONS_PRESETS + )) return opts def __extract_info(self, url, ytdl_options_presets=None, ytdl_options_overrides=None): @@ -1053,16 +1170,7 @@ class DownloadQueue: return None, {'status': 'error', 'msg': 'A folder for the download was specified but CUSTOM_DIRS is not true in the configuration.'} dldirectory = os.path.realpath(os.path.join(base_directory, folder)) real_base_directory = os.path.realpath(base_directory) - # Use commonpath rather than startswith so that a sibling directory - # sharing a name prefix (e.g. base "/downloads" vs "/downloads-secret") - # cannot be reached via "../downloads-secret". - try: - inside_base = os.path.commonpath([real_base_directory, dldirectory]) == real_base_directory - except ValueError: - # Raised when paths are on different drives (Windows) or mix - # absolute/relative; treat as outside the base directory. - inside_base = False - if not inside_base: + if not _is_within_directory(real_base_directory, dldirectory): return None, {'status': 'error', 'msg': f'Folder "{folder}" must resolve inside the base download directory "{real_base_directory}"'} if not os.path.isdir(dldirectory): if not self.config.CREATE_CUSTOM_DIRS: @@ -1107,7 +1215,7 @@ class DownloadQueue: self._schedule_upcoming_download(download) else: self.queue.put(download) - asyncio.create_task(self.__start_download(download)) + bg_tasks.create_task(self.__start_download(download), name="start_download") else: self.pending.put(download) await self.notifier.added(dl) @@ -1139,7 +1247,9 @@ class DownloadQueue: error = None if "live_status" in entry and "release_timestamp" in entry and entry.get("live_status") == "is_upcoming": - dt_ts = datetime.fromtimestamp(entry.get("release_timestamp")).strftime('%Y-%m-%d %H:%M:%S %z') + # astimezone() makes this an aware datetime in the server's local + # zone; a naive datetime's %z renders as an empty string. + dt_ts = datetime.fromtimestamp(entry.get("release_timestamp")).astimezone().strftime('%Y-%m-%d %H:%M:%S %z') error = f"Live stream is scheduled to start at {dt_ts}" else: if "msg" in entry: @@ -1237,38 +1347,43 @@ class DownloadQueue: if any(res['status'] == 'error' for res in results): return {'status': 'error', 'msg': ', '.join(res['msg'] for res in results if res['status'] == 'error' and 'msg' in res)} return {'status': 'ok'} - elif etype == 'video' or (etype.startswith('url') and 'id' in entry and 'title' in entry): + elif etype == 'video': log.debug('Processing as a video') key = entry.get('webpage_url') or entry['url'] if key in self._canceled_urls: log.info(f'Skipping canceled URL: {entry.get("title") or key}') return {'status': 'ok'} - if not self.queue.exists(key): - dl = DownloadInfo( - id=entry['id'], - title=entry.get('title') or entry['id'], - url=key, - quality=quality, - download_type=download_type, - codec=codec, - format=format, - folder=folder, - custom_name_prefix=custom_name_prefix, - error=error, - entry=entry, - playlist_item_limit=playlist_item_limit, - split_by_chapters=split_by_chapters, - chapter_template=chapter_template, - subtitle_language=subtitle_language, - subtitle_mode=subtitle_mode, - ytdl_options_presets=ytdl_options_presets, - ytdl_options_overrides=ytdl_options_overrides, - clip_start=clip_start, - clip_end=clip_end, - live_status=entry.get('live_status'), - live_release_timestamp=entry.get('release_timestamp'), - ) - await self.__add_download(dl, auto_start) + if self.queue.exists(key) or self.pending.exists(key): + # Surface the skip instead of silently no-op'ing, and avoid + # clobbering an existing pending entry's options with a + # fresh DownloadInfo built from possibly-different args. + title = entry.get('title') or key + return {'status': 'ok', 'msg': f'Already in queue: {title}'} + dl = DownloadInfo( + id=entry['id'], + title=entry.get('title') or entry['id'], + url=key, + quality=quality, + download_type=download_type, + codec=codec, + format=format, + folder=folder, + custom_name_prefix=custom_name_prefix, + error=error, + entry=entry, + playlist_item_limit=playlist_item_limit, + split_by_chapters=split_by_chapters, + chapter_template=chapter_template, + subtitle_language=subtitle_language, + subtitle_mode=subtitle_mode, + ytdl_options_presets=ytdl_options_presets, + ytdl_options_overrides=ytdl_options_overrides, + clip_start=clip_start, + clip_end=clip_end, + live_status=entry.get('live_status'), + live_release_timestamp=entry.get('release_timestamp'), + ) + await self.__add_download(dl, auto_start) return {'status': 'ok'} return {'status': 'error', 'msg': f'Unsupported resource "{etype}"'} @@ -1394,7 +1509,7 @@ class DownloadQueue: self._schedule_upcoming_download(dl) else: self.queue.put(dl) - asyncio.create_task(self.__start_download(dl)) + bg_tasks.create_task(self.__start_download(dl), name="start_download") continue if self.queue.exists(id): dl = self.queue.get(id) @@ -1448,9 +1563,14 @@ class DownloadQueue: for extra in (getattr(dl.info, 'subtitle_files', None) or []): if isinstance(extra, dict) and extra.get('filename'): rel_names.append(extra['filename']) + real_base_directory = os.path.realpath(dldirectory) for rel_name in rel_names: + full_path = os.path.realpath(os.path.join(dldirectory, rel_name)) + if not _is_within_directory(real_base_directory, full_path): + log.warning(f'skipping deletion of "{rel_name}" for download {id}: resolves outside the download directory') + continue try: - os.remove(os.path.join(dldirectory, rel_name)) + os.remove(full_path) except FileNotFoundError: pass except OSError as e: @@ -1463,3 +1583,13 @@ class DownloadQueue: return (list((k, v.info) for k, v in self.queue.items()) + list((k, v.info) for k, v in self.pending.items()), list((k, v.info) for k, v in self.done.items())) + + def close(self): + # Kill any still-running download subprocesses (and their ffmpeg + # children) before tearing down the executor, so they aren't orphaned + # when the server exits. Their queue entries stay persisted and are + # re-imported/restarted on next startup. + for _key, download in list(self.queue.items()): + if download.started() and download.running(): + download.cancel() + self._download_executor.shutdown(wait=False, cancel_futures=True) diff --git a/ui/src/app/app.html b/ui/src/app/app.html index 1299b05..5e875f3 100644 --- a/ui/src/app/app.html +++ b/ui/src/app/app.html @@ -699,7 +699,7 @@ - @for (download of downloads.queue | keyvalue: asIsOrder; track download.value.id) { + @for (download of downloads.queue | keyvalue: asIsOrder; track download.key) { @@ -769,7 +769,7 @@ - @for (entry of cachedSortedDone; track entry[1].id) { + @for (entry of cachedSortedDone; track entry[0]) { diff --git a/ui/src/app/app.spec.ts b/ui/src/app/app.spec.ts index be19b65..c1f61a5 100644 --- a/ui/src/app/app.spec.ts +++ b/ui/src/app/app.spec.ts @@ -139,6 +139,12 @@ describe('App', () => { expect(app).toBeTruthy(); }); + it('asIsOrder returns a stable comparator value (insertion order preserved)', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + expect(app.asIsOrder()).toBe(0); + }); + it('hides manual override input when disabled', () => { const fixture = TestBed.createComponent(App); fixture.componentInstance.isAdvancedOpen = true; diff --git a/ui/src/app/app.ts b/ui/src/app/app.ts index e48161e..67bd702 100644 --- a/ui/src/app/app.ts +++ b/ui/src/app/app.ts @@ -351,13 +351,10 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.colorSchemeMediaQuery.removeEventListener('change', this.onColorSchemeChanged); } - // workaround to allow fetching of Map values in the order they were inserted - // https://github.com/angular/angular/issues/31420 - - - + // keyvalue comparator that preserves insertion order (Angular's keyvalue + // pipe sorts by key by default): https://github.com/angular/angular/issues/31420 asIsOrder() { - return 1; + return 0; } qualityChanged() { @@ -430,8 +427,8 @@ export class App implements AfterViewInit, OnInit, OnDestroy { this.downloads.configurationChanged.pipe(takeUntilDestroyed(this.destroyRef)).subscribe({ // eslint-disable-next-line @typescript-eslint/no-explicit-any next: (config: any) => { - const playlistItemLimit = config['DEFAULT_OPTION_PLAYLIST_ITEM_LIMIT']; - if (playlistItemLimit !== '0') { + const playlistItemLimit = parseInt(String(config['DEFAULT_OPTION_PLAYLIST_ITEM_LIMIT'] ?? '0'), 10); + if (!Number.isNaN(playlistItemLimit) && playlistItemLimit > 0) { this.playlistItemLimit = playlistItemLimit; } // Set chapter template from backend config if not already set by cookie @@ -526,6 +523,14 @@ export class App implements AfterViewInit, OnInit, OnDestroy { return status?.status === 'error' ? status.msg || null : null; } + private handleActionResult(res: unknown, fallbackMsg: string) { + const error = this.getStatusError(res); + if (error) { + this.toasts.error(error || fallbackMsg); + } + this.cdr.markForCheck(); + } + private refreshSubscriptionsWithAlert() { this.subscriptionsSvc.refreshList().pipe(takeUntilDestroyed(this.destroyRef)).subscribe((refreshRes) => { const error = this.getStatusError(refreshRes); @@ -1085,6 +1090,10 @@ export class App implements AfterViewInit, OnInit, OnDestroy { if (status.status === 'error' && !this.cancelRequested) { this.toasts.error(`Error adding URL: ${status.msg}`); } else if (status.status !== 'error') { + // e.g. "Already in queue: ..." when the backend skipped a duplicate. + if (status.msg) { + this.toasts.info(status.msg); + } this.addUrl = ''; } this.resetAddState(); @@ -1113,7 +1122,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { } downloadItemByKey(id: string) { - this.downloads.startById([id]).subscribe(); + this.downloads.startById([id]).subscribe((res) => this.handleActionResult(res, 'Start download failed')); } liveCountdownSeconds(download: Download): number | null { @@ -1137,7 +1146,7 @@ export class App implements AfterViewInit, OnInit, OnDestroy { } retryDownload(key: string, download: Download) { - this.addDownload({ + const payload = this.buildAddPayload({ url: download.url, downloadType: download.download_type, codec: download.codec, @@ -1158,27 +1167,38 @@ export class App implements AfterViewInit, OnInit, OnDestroy { clipStart: download.clip_start != null ? String(download.clip_start) : '', clipEnd: download.clip_end != null ? String(download.clip_end) : '', }); - this.downloads.delById('done', [key]).subscribe(); + // Only remove the done-list record once the retry is confirmed queued β€” + // deleting it eagerly would silently lose history if the re-add fails. + this.downloads.add(payload) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe((status: Status) => { + if (status.status === 'error') { + this.toasts.error(`Error retrying ${download.title}: ${status.msg}`); + this.cdr.markForCheck(); + return; + } + this.downloads.delById('done', [key]).subscribe(); + }); } delDownload(where: State, id: string) { - this.downloads.delById(where, [id]).subscribe(); + this.downloads.delById(where, [id]).subscribe((res) => this.handleActionResult(res, 'Delete failed')); } startSelectedDownloads(where: State){ - this.downloads.startByFilter(where, dl => !!dl.checked).subscribe(); + this.downloads.startByFilter(where, dl => !!dl.checked).subscribe((res) => this.handleActionResult(res, 'Start download failed')); } delSelectedDownloads(where: State) { - this.downloads.delByFilter(where, dl => !!dl.checked).subscribe(); + this.downloads.delByFilter(where, dl => !!dl.checked).subscribe((res) => this.handleActionResult(res, 'Delete failed')); } clearCompletedDownloads() { - this.downloads.delByFilter('done', dl => dl.status === 'finished').subscribe(); + this.downloads.delByFilter('done', dl => dl.status === 'finished').subscribe((res) => this.handleActionResult(res, 'Clear completed failed')); } clearFailedDownloads() { - this.downloads.delByFilter('done', dl => dl.status === 'error').subscribe(); + this.downloads.delByFilter('done', dl => dl.status === 'error').subscribe((res) => this.handleActionResult(res, 'Clear failed downloads failed')); } retryFailedDownloads() { diff --git a/ui/src/app/pipes/file-size.pipe.spec.ts b/ui/src/app/pipes/file-size.pipe.spec.ts index 06b649a..15d20b0 100644 --- a/ui/src/app/pipes/file-size.pipe.spec.ts +++ b/ui/src/app/pipes/file-size.pipe.spec.ts @@ -10,15 +10,16 @@ describe('FileSizePipe', () => { it('formats bytes and larger units', () => { const pipe = new FileSizePipe(); expect(pipe.transform(500)).toContain('Bytes'); - expect(pipe.transform(1000)).toContain('KB'); - expect(pipe.transform(1000 * 1000)).toContain('MB'); - expect(pipe.transform(1000 ** 3)).toContain('GB'); + expect(pipe.transform(1000)).toContain('Bytes'); + expect(pipe.transform(1024)).toContain('KB'); + expect(pipe.transform(1024 ** 2)).toContain('MB'); + expect(pipe.transform(1024 ** 3)).toContain('GB'); }); it('handles boundaries between units', () => { const pipe = new FileSizePipe(); - expect(pipe.transform(999)).toContain('Bytes'); - expect(pipe.transform(1000)).toContain('KB'); - expect(pipe.transform(1001)).toContain('KB'); + expect(pipe.transform(1023)).toContain('Bytes'); + expect(pipe.transform(1024)).toContain('KB'); + expect(pipe.transform(1025)).toContain('KB'); }); }); diff --git a/ui/src/app/pipes/file-size.pipe.ts b/ui/src/app/pipes/file-size.pipe.ts index 7e8e993..e87b23e 100644 --- a/ui/src/app/pipes/file-size.pipe.ts +++ b/ui/src/app/pipes/file-size.pipe.ts @@ -8,9 +8,10 @@ export class FileSizePipe implements PipeTransform { if (isNaN(value) || value === 0) return '0 Bytes'; const units = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; - const unitIndex = Math.floor(Math.log(value) / Math.log(1000)); // Use 1000 for common units + const k = 1024; // Matches SpeedPipe's base so file sizes and transfer speeds agree. + const unitIndex = Math.floor(Math.log(value) / Math.log(k)); - const unitValue = value / Math.pow(1000, unitIndex); + const unitValue = value / Math.pow(k, unitIndex); return `${unitValue.toFixed(2)} ${units[unitIndex]}`; } } \ No newline at end of file diff --git a/ui/src/app/services/downloads.service.spec.ts b/ui/src/app/services/downloads.service.spec.ts index 2fbc4be..897ce51 100644 --- a/ui/src/app/services/downloads.service.spec.ts +++ b/ui/src/app/services/downloads.service.spec.ts @@ -159,6 +159,61 @@ describe('DownloadsService', () => { req.flush({}); }); + it('delById resets deleting flag and emits error status on HTTP failure', () => { + const dl: Download = { + id: '1', + title: 't', + url: 'u1', + download_type: 'video', + quality: 'best', + format: 'any', + folder: '', + custom_name_prefix: '', + playlist_item_limit: 0, + status: 'finished', + msg: '', + percent: 0, + speed: 0, + eta: 0, + filename: '', + checked: false, + deleting: false, + }; + service.queue.set('u1', dl); + let queueChangedCount = 0; + service.queueChanged.subscribe(() => queueChangedCount++); + let result: unknown; + let threw = false; + + service.delById('queue', ['u1']).subscribe({ + next: (res) => { result = res; }, + error: () => { threw = true; }, + }); + expect(dl.deleting).toBe(true); + const req = httpMock.expectOne('delete'); + req.flush({ msg: 'boom' }, { status: 500, statusText: 'Server Error' }); + + expect(threw).toBe(false); + expect(dl.deleting).toBe(false); + expect(queueChangedCount).toBeGreaterThan(0); + expect((result as { status: string }).status).toBe('error'); + }); + + it('startById surfaces HTTP errors as a status object instead of throwing', () => { + let result: unknown; + let threw = false; + + service.startById(['a']).subscribe({ + next: (res) => { result = res; }, + error: () => { threw = true; }, + }); + const req = httpMock.expectOne('start'); + req.flush({ msg: 'nope' }, { status: 500, statusText: 'Server Error' }); + + expect(threw).toBe(false); + expect((result as { status: string }).status).toBe('error'); + }); + it('handleHTTPError extracts msg from object body', async () => { const err = new HttpErrorResponse({ error: { msg: 'bad' }, @@ -226,6 +281,15 @@ describe('DownloadsService', () => { expect(updated?.deleting).toBe(true); }); + it('socket updated ignores events for urls not already in the queue', () => { + expect(service.queue.has('unknown-url')).toBe(false); + socket.emit( + 'updated', + JSON.stringify({ url: 'unknown-url', title: 't', status: 'downloading' }), + ); + expect(service.queue.has('unknown-url')).toBe(false); + }); + it('socket completed moves entry to done', () => { service.queue.set('u1', { id: '1', diff --git a/ui/src/app/services/downloads.service.ts b/ui/src/app/services/downloads.service.ts index 60c3df6..8579cc8 100644 --- a/ui/src/app/services/downloads.service.ts +++ b/ui/src/app/services/downloads.service.ts @@ -69,8 +69,14 @@ export class DownloadsService { .subscribe((strdata: string) => { const data: Download = JSON.parse(strdata); const dl: Download | undefined = this.queue.get(data.url); - data.checked = !!dl?.checked; - data.deleting = !!dl?.deleting; + // An 'added' event always precedes legitimate updates. If the row is + // gone (canceled/completed already processed), this update is stale β€” + // applying it would resurrect a ghost row until the next full refresh. + if (!dl) { + return; + } + data.checked = !!dl.checked; + data.deleting = !!dl.deleting; this.queue.set(data.url, data); this.updated.next(); }); @@ -164,7 +170,9 @@ export class DownloadsService { } public startById(ids: string[]) { - return this.http.post('start', {ids: ids}); + return this.http.post('start', {ids: ids}).pipe( + catchError(this.handleHTTPError) + ); } public delById(where: State, ids: string[]) { @@ -177,7 +185,22 @@ export class DownloadsService { } } } - return this.http.post('delete', {where: where, ids: ids}); + return this.http.post('delete', {where: where, ids: ids}).pipe( + catchError((err: HttpErrorResponse) => { + // Request failed β€” the rows would otherwise stay disabled forever + // with no way to retry, since nothing ever clears `deleting`. + if (map) { + for (const id of ids) { + const obj = map.get(id); + if (obj) { + obj.deleting = false; + } + } + } + (where === 'queue' ? this.queueChanged : this.doneChanged).next(); + return this.handleHTTPError(err); + }) + ); } public startByFilter(where: State, filter: (dl: Download) => boolean) { From fdfbfed5e205e8bca50022633c4f690c414960d3 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Mon, 13 Jul 2026 23:07:39 +0300 Subject: [PATCH 80/88] fix: prevent playlist/channel title path traversal (closes GHSA-vh67-38x4-w8pc) Sanitize path separators and .. segments in playlist/channel titles before they are baked into yt-dlp output templates, and refuse downloads whose resolved output directory escapes DOWNLOAD_DIR. --- app/tests/test_ytdl_utils.py | 50 ++++++++++++++++++++++++++++++++++++ app/ytdl.py | 23 +++++++++++++++-- 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/app/tests/test_ytdl_utils.py b/app/tests/test_ytdl_utils.py index 36c2451..f529574 100644 --- a/app/tests/test_ytdl_utils.py +++ b/app/tests/test_ytdl_utils.py @@ -43,6 +43,7 @@ from ytdl import ( DownloadInfo, _compact_persisted_entry, _convert_srt_to_txt_file, + _output_dir_escapes, _resolve_outtmpl_fields, _sanitize_entry_for_pickle, _sanitize_path_component, @@ -61,6 +62,24 @@ class SanitizePathComponentTests(unittest.TestCase): self.assertIs(_sanitize_path_component(None), None) self.assertEqual(_sanitize_path_component(42), 42) + def test_strips_path_separators_and_traversal(self): + result = _sanitize_path_component('../../../../etc/x') + self.assertNotIn('..', result) + self.assertNotIn('/', result) + self.assertNotIn('\\', result) + + def test_strips_leading_absolute_path_separator(self): + result = _sanitize_path_component('/tmp/x') + self.assertFalse(result.startswith('/')) + self.assertFalse(result.startswith('\\')) + self.assertEqual(result, '_tmp_x') + + def test_collapses_slashes_in_legitimate_titles(self): + self.assertEqual(_sanitize_path_component('AC/DC'), 'AC_DC') + + def test_empty_after_strip_becomes_underscore(self): + self.assertEqual(_sanitize_path_component(' '), '_') + @unittest.skipUnless(_has_real_ytdlp, "requires real yt-dlp") class ResolveOuttmplFieldsTests(unittest.TestCase): @@ -125,6 +144,37 @@ class ResolveOuttmplFieldsTests(unittest.TestCase): ) self.assertEqual(result, "5 - %(title)s.%(ext)s") + def test_malicious_playlist_title_cannot_escape_via_template(self): + malicious_title = '/tmp/METUBE_ARBITRARY_WRITE_POC' + entry = { + 'playlist_title': malicious_title, + 'playlist_index': '1', + 'title': 'video', + 'ext': 'mp4', + } + sanitized = {k: _sanitize_path_component(v) for k, v in entry.items()} + template = '%(playlist_title)s/%(title)s.%(ext)s' + result = _resolve_outtmpl_fields(template, sanitized, ('playlist',)) + marker = result.find('%(') + literal_prefix = result[:marker] if marker != -1 else result + self.assertNotIn('..', literal_prefix) + self.assertFalse(literal_prefix.startswith('/')) + self.assertFalse(literal_prefix.startswith('\\')) + + +class OutputDirEscapesTests(unittest.TestCase): + def setUp(self): + self.base_dir = tempfile.mkdtemp() + + def test_relative_traversal_escapes(self): + self.assertTrue(_output_dir_escapes(self.base_dir, '../../tmp/x/%(title)s.%(ext)s')) + + def test_absolute_path_escapes(self): + self.assertTrue(_output_dir_escapes(self.base_dir, '/tmp/x/%(title)s.%(ext)s')) + + def test_normal_playlist_dir_stays_inside(self): + self.assertFalse(_output_dir_escapes(self.base_dir, 'Playlist/%(title)s.%(ext)s')) + class SanitizeEntryForPickleTests(unittest.TestCase): def test_nested(self): diff --git a/app/ytdl.py b/app/ytdl.py index 43a7772..8a2dc42 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -72,6 +72,7 @@ def _is_within_directory(real_base: str, real_target: str) -> bool: # sanitised when substituting playlist/channel titles into output templates so # that downloads do not fail on NTFS-mounted volumes or Windows Docker hosts. _WINDOWS_INVALID_PATH_CHARS = re.compile(r'[\\:*?"<>|]') +_PATH_SEP_OR_TRAVERSAL = re.compile(r'[\\/]|\.\.') def _sanitize_path_component(value: Any) -> Any: @@ -81,11 +82,27 @@ def _sanitize_path_component(value: Any) -> Any: that numeric format specs (e.g. ``%(playlist_index)02d``) still work. Only string values are sanitised because Windows-invalid characters are only a concern for human-readable strings (titles, channel names, etc.) - that may end up as directory names. + that may end up as directory names. Path separators and ``..`` segments + are also collapsed so attacker-controlled playlist/channel titles cannot + escape the download directory via the output template. """ if not isinstance(value, str): return value - return _WINDOWS_INVALID_PATH_CHARS.sub('_', value) + value = _WINDOWS_INVALID_PATH_CHARS.sub('_', value) + value = _PATH_SEP_OR_TRAVERSAL.sub('_', value) + return value.lstrip('.').strip() or '_' + + +def _output_dir_escapes(base_dir: str, output_template: str) -> bool: + """True when the literal directory prefix of *output_template* resolves outside *base_dir*.""" + marker = output_template.find('%(') + literal = output_template if marker == -1 else output_template[:marker] + dir_prefix = os.path.dirname(literal) + if not dir_prefix: + return False + real_base = os.path.realpath(base_dir) + real_target = os.path.realpath(os.path.join(base_dir, dir_prefix)) + return not _is_within_directory(real_base, real_target) # Regex matching yt-dlp output-template field references, e.g. ``%(title)s`` @@ -1205,6 +1222,8 @@ class DownloadQueue: if playlist_item_limit > 0: log.info(f'playlist limit is set. Processing only first {playlist_item_limit} entries') ytdl_options['playlistend'] = playlist_item_limit + if _output_dir_escapes(dldirectory, output): + return {'status': 'error', 'msg': 'Refusing download: resolved output path escapes the download directory'} download = Download(dldirectory, self.config.TEMP_DIR, output, output_chapter, dl.quality, dl.format, ytdl_options, dl) is_upcoming = ( getattr(dl, 'live_status', None) == 'is_upcoming' From c104e304513b41bbc88cab9d5af075f2ec60a8a1 Mon Sep 17 00:00:00 2001 From: Your GitHub Name Date: Wed, 15 Jul 2026 15:40:36 -0700 Subject: [PATCH 81/88] feat: add AlbumArtistPostProcessor to fill missing album-artist metadata --- app/tests/test_ytdl_utils.py | 104 +++++++++++++++++++++++++++++++++++ app/ytdl.py | 43 ++++++++++++++- 2 files changed, 146 insertions(+), 1 deletion(-) diff --git a/app/tests/test_ytdl_utils.py b/app/tests/test_ytdl_utils.py index f529574..73b40fe 100644 --- a/app/tests/test_ytdl_utils.py +++ b/app/tests/test_ytdl_utils.py @@ -15,6 +15,8 @@ from unittest.mock import MagicMock, patch fake_yt_dlp = types.ModuleType("yt_dlp") fake_networking = types.ModuleType("yt_dlp.networking") fake_impersonate = types.ModuleType("yt_dlp.networking.impersonate") +fake_postprocessor = types.ModuleType("yt_dlp.postprocessor") +fake_postprocessor_common = types.ModuleType("yt_dlp.postprocessor.common") fake_utils = types.ModuleType("yt_dlp.utils") @@ -24,18 +26,27 @@ class _ImpersonateTarget: return value +class _PostProcessor: + def __init__(self, downloader=None): + self._downloader = downloader + + fake_impersonate.ImpersonateTarget = _ImpersonateTarget fake_networking.impersonate = fake_impersonate +fake_postprocessor_common.PostProcessor = _PostProcessor # The inner ``key`` group mirrors the real ``STR_FORMAT_RE_TMPL`` so that # ``_OUTTMPL_FIELD_RE`` (compiled at import time) has the named group that # ``_resolve_outtmpl_fields`` reads via ``match.group('key')``. fake_utils.STR_FORMAT_RE_TMPL = r"(?P)%\((?P(?P{}))\)(?P[-0-9.]*{})" fake_utils.STR_FORMAT_TYPES = "diouxXeEfFgGcrsa" fake_yt_dlp.networking = fake_networking +fake_yt_dlp.postprocessor = fake_postprocessor fake_yt_dlp.utils = fake_utils sys.modules.setdefault("yt_dlp", fake_yt_dlp) sys.modules.setdefault("yt_dlp.networking", fake_networking) sys.modules.setdefault("yt_dlp.networking.impersonate", fake_impersonate) +sys.modules.setdefault("yt_dlp.postprocessor", fake_postprocessor) +sys.modules.setdefault("yt_dlp.postprocessor.common", fake_postprocessor_common) sys.modules.setdefault("yt_dlp.utils", fake_utils) from ytdl import ( @@ -43,6 +54,7 @@ from ytdl import ( DownloadInfo, _compact_persisted_entry, _convert_srt_to_txt_file, + _AlbumArtistPostProcessor, _output_dir_escapes, _resolve_outtmpl_fields, _sanitize_entry_for_pickle, @@ -54,6 +66,98 @@ from ytdl import ( _has_real_ytdlp = hasattr(sys.modules.get("yt_dlp"), "YoutubeDL") +class AlbumArtistPostProcessorTests(unittest.TestCase): + def setUp(self): + self.postprocessor = _AlbumArtistPostProcessor() + + def test_fills_album_artist_from_artist(self): + info = {'album': 'CrasH Talk', 'artist': 'ScHoolboy Q'} + + _, result = self.postprocessor.run(info) + + self.assertEqual(result['album_artist'], 'ScHoolboy Q') + + def test_uses_main_artist_for_featured_track(self): + info = { + 'album': 'CrasH Talk', + 'artists': ['ScHoolboy Q Β· Travis Scott'], + } + + _, result = self.postprocessor.run(info) + + self.assertEqual(result['album_artist'], 'ScHoolboy Q') + + def test_preserves_explicit_various_artists(self): + info = { + 'album': 'Revenge of the Dreamers III', + 'artist': 'J. Cole', + 'album_artist': 'Various Artists', + } + + _, result = self.postprocessor.run(info) + + self.assertEqual(result['album_artist'], 'Various Artists') + + def test_preserves_existing_album_artists_list(self): + info = { + 'album': 'Album', + 'artist': 'Track Artist', + 'album_artists': ['Album Artist'], + } + + _, result = self.postprocessor.run(info) + + self.assertEqual(result['album_artists'], ['Album Artist']) + self.assertNotIn('album_artist', result) + + def test_uses_first_artist_when_artist_list_has_multiple_entries(self): + info = {'album': 'Album', 'artists': ['Main Artist', 'Featured Artist']} + + _, result = self.postprocessor.run(info) + + self.assertEqual(result['album_artist'], 'Main Artist') + + def test_does_not_fill_without_album(self): + info = {'artist': 'Standalone Artist'} + + _, result = self.postprocessor.run(info) + + self.assertNotIn('album_artist', result) + self.assertNotIn('album_artists', result) + + def test_does_not_fill_without_artist(self): + info = {'album': 'Instrumental Album'} + + _, result = self.postprocessor.run(info) + + self.assertNotIn('album_artist', result) + self.assertNotIn('album_artists', result) + + +class AlbumArtistRegistrationTests(unittest.TestCase): + def test_audio_download_registers_pre_process_postprocessor(self): + download = _make_test_download() + download.info.download_type = 'audio' + fake_ydl = MagicMock() + + with patch('ytdl.yt_dlp.YoutubeDL', return_value=fake_ydl): + result = download._make_youtube_dl({'quiet': True}) + + self.assertIs(result, fake_ydl) + postprocessor, = fake_ydl.add_post_processor.call_args.args + self.assertIsInstance(postprocessor, _AlbumArtistPostProcessor) + self.assertEqual(fake_ydl.add_post_processor.call_args.kwargs, {'when': 'pre_process'}) + + def test_video_download_does_not_register_postprocessor(self): + download = _make_test_download() + fake_ydl = MagicMock() + + with patch('ytdl.yt_dlp.YoutubeDL', return_value=fake_ydl): + download._make_youtube_dl({'quiet': True}) + + fake_ydl.add_post_processor.assert_not_called() + + class SanitizePathComponentTests(unittest.TestCase): def test_replaces_windows_invalid_chars(self): self.assertEqual(_sanitize_path_component('a:b*c?d"eg|h'), "a_b_c_d_e_f_g_h") diff --git a/app/ytdl.py b/app/ytdl.py index 8a2dc42..daeb594 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -19,6 +19,7 @@ import types from typing import Any, Optional import yt_dlp.networking.impersonate +from yt_dlp.postprocessor.common import PostProcessor from yt_dlp.utils import STR_FORMAT_RE_TMPL, STR_FORMAT_TYPES import bg_tasks from dl_formats import get_format, get_opts, AUDIO_FORMATS, merge_ytdl_option_layers @@ -52,6 +53,40 @@ _LIVE_MAX_CHECK_INTERVAL = 3600 _LIVE_PROBE_MAX_FAILURES = 5 +class _AlbumArtistPostProcessor(PostProcessor): + """Fill missing album-artist metadata from yt-dlp's main track artist.""" + + @staticmethod + def _has_value(value: Any) -> bool: + if isinstance(value, str): + return bool(value.strip()) + if isinstance(value, (list, tuple)): + return any(_AlbumArtistPostProcessor._has_value(item) for item in value) + return value is not None + + @staticmethod + def _main_artist(info) -> Optional[str]: + artists = info.get('artists') + candidates = artists if isinstance(artists, list) else [info.get('artist')] + for candidate in candidates: + if not isinstance(candidate, str) or not candidate.strip(): + continue + # YouTube Music uses a spaced middle dot between credited artists. + # The first credit is the primary artist for normal albums. + return candidate.split(' Β· ', 1)[0].strip() + return None + + def run(self, info): + if not self._has_value(info.get('album')): + return [], info + if self._has_value(info.get('album_artist')) or self._has_value(info.get('album_artists')): + return [], info + + if artist := self._main_artist(info): + info['album_artist'] = artist + return [], info + + def _is_within_directory(real_base: str, real_target: str) -> bool: """True if ``real_target`` is inside (or equal to) ``real_base``. @@ -545,6 +580,12 @@ class Download: return put_status + def _make_youtube_dl(self, params): + ydl = yt_dlp.YoutubeDL(params=params) + if getattr(self.info, 'download_type', '') == 'audio': + ydl.add_post_processor(_AlbumArtistPostProcessor(ydl), when='pre_process') + return ydl + def _download(self): # Run in our own process group so cancel() can SIGKILL the whole # group (yt-dlp + any ffmpeg children it spawned for merge/postproc), @@ -622,7 +663,7 @@ class Download: [(start, end)], ) - ret = yt_dlp.YoutubeDL(params=ytdl_params).download([self.info.url]) + ret = self._make_youtube_dl(ytdl_params).download([self.info.url]) self.status_queue.put({'status': 'finished' if ret == 0 else 'error'}) log.info(f"Finished download for: {self.info.title}") except yt_dlp.utils.YoutubeDLError as exc: From 6d0528783c06ca4cc67ae2d9f31388257fa54664 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Thu, 16 Jul 2026 21:31:51 +0300 Subject: [PATCH 82/88] fix: block SSRF via user-submitted URLs User-submitted URLs were passed straight to yt-dlp's generic extractor, letting the server fetch internal endpoints (cloud metadata, loopback, RFC1918 hosts). Add a url_guard.validate_url check at every URL ingress (add, subscribe, and nested playlist recursion) that rejects non-http(s) schemes and hosts resolving to non-global addresses, while leaving bare video IDs and search prefixes untouched. --- app/subscriptions.py | 10 ++++ app/tests/test_url_guard.py | 102 ++++++++++++++++++++++++++++++++++++ app/url_guard.py | 89 +++++++++++++++++++++++++++++++ app/ytdl.py | 8 +++ 4 files changed, 209 insertions(+) create mode 100644 app/tests/test_url_guard.py create mode 100644 app/url_guard.py diff --git a/app/subscriptions.py b/app/subscriptions.py index 81e9513..8bfeae1 100644 --- a/app/subscriptions.py +++ b/app/subscriptions.py @@ -19,6 +19,7 @@ import yt_dlp.networking.impersonate import bg_tasks from dl_formats import merge_ytdl_option_layers from state_store import AtomicJsonStore, read_legacy_shelf +from url_guard import validate_url log = logging.getLogger("subscriptions") @@ -113,6 +114,9 @@ def extract_flat_playlist( nested_url = _entry_video_url(ent) if not nested_url: continue + # nested_url comes from remote playlist content; guard it too. + if validate_url(nested_url) is not None: + continue nested_info, nested_entries = extract_flat_playlist( config, nested_url, @@ -542,6 +546,12 @@ class SubscriptionManager: url = self._normalize_url(url) if not url: return {"status": "error", "msg": "Missing URL"} + # SSRF guard: block non-http(s) schemes and internal/metadata hosts + # before yt-dlp fetches the feed. May do a DNS lookup, so run off-loop. + url_error = await asyncio.get_running_loop().run_in_executor(None, validate_url, url) + if url_error is not None: + log.warning('Rejected subscription URL "%s": %s', url, url_error) + return {"status": "error", "msg": url_error} try: title_regex_stored = validate_title_regex(title_regex) except re.error as exc: diff --git a/app/tests/test_url_guard.py b/app/tests/test_url_guard.py new file mode 100644 index 0000000..1808f3e --- /dev/null +++ b/app/tests/test_url_guard.py @@ -0,0 +1,102 @@ +"""Tests for the SSRF URL guard (``url_guard.validate_url``).""" + +from __future__ import annotations + +import socket +import unittest +from unittest import mock + +from url_guard import validate_url + + +def _addrinfo(*addrs, family=socket.AF_INET): + return [(family, socket.SOCK_STREAM, socket.IPPROTO_TCP, "", (addr, 0)) for addr in addrs] + + +class NonUrlInputTests(unittest.TestCase): + """Bare IDs and yt-dlp search/extractor prefixes must pass untouched.""" + + def test_bare_video_id_allowed(self): + self.assertIsNone(validate_url("dQw4w9WgXcQ")) + + def test_ytsearch_prefix_allowed(self): + self.assertIsNone(validate_url("ytsearch:some song")) + + def test_empty_string_allowed(self): + self.assertIsNone(validate_url("")) + + def test_non_string_rejected(self): + self.assertIsNotNone(validate_url(None)) + + +class SchemeTests(unittest.TestCase): + def test_file_scheme_blocked(self): + self.assertIsNotNone(validate_url("file:///etc/passwd")) + + def test_ftp_scheme_blocked(self): + self.assertIsNotNone(validate_url("ftp://example.com/x")) + + def test_data_scheme_blocked(self): + self.assertIsNotNone(validate_url("data://text/plain;base64,AAAA")) + + +class HostnameBlocklistTests(unittest.TestCase): + def test_localhost_blocked_without_lookup(self): + with mock.patch("url_guard.socket.getaddrinfo") as gai: + self.assertIsNotNone(validate_url("http://localhost:8080/x")) + gai.assert_not_called() + + def test_localhost_subdomain_blocked(self): + self.assertIsNotNone(validate_url("http://foo.localhost/x")) + + def test_gcp_metadata_name_blocked(self): + self.assertIsNotNone(validate_url("http://metadata.google.internal/x")) + + +class AddressResolutionTests(unittest.TestCase): + def _validate_with_addrs(self, url, *addrs, family=socket.AF_INET): + with mock.patch("url_guard.socket.getaddrinfo", return_value=_addrinfo(*addrs, family=family)): + return validate_url(url) + + def test_public_https_allowed(self): + self.assertIsNone(self._validate_with_addrs("https://youtube.com/watch?v=x", "142.250.1.1")) + + def test_public_http_allowed(self): + self.assertIsNone(self._validate_with_addrs("http://example.com/x", "93.184.216.34")) + + def test_link_local_metadata_blocked(self): + self.assertIsNotNone(self._validate_with_addrs("http://metadata/x", "169.254.169.254")) + + def test_loopback_ipv4_blocked(self): + self.assertIsNotNone(self._validate_with_addrs("http://127.0.0.1/x", "127.0.0.1")) + + def test_private_rfc1918_blocked(self): + self.assertIsNotNone(self._validate_with_addrs("http://intranet/x", "10.0.0.5")) + + def test_decimal_ip_form_blocked(self): + # 2852039166 == 169.254.169.254; the OS resolver normalizes it. + self.assertIsNotNone(self._validate_with_addrs("http://2852039166/x", "169.254.169.254")) + + def test_ipv6_loopback_blocked(self): + self.assertIsNotNone( + self._validate_with_addrs("http://[::1]/x", "::1", family=socket.AF_INET6) + ) + + def test_ipv4_mapped_ipv6_metadata_blocked(self): + self.assertIsNotNone( + self._validate_with_addrs( + "http://evil/x", "::ffff:169.254.169.254", family=socket.AF_INET6 + ) + ) + + def test_mixed_public_and_private_blocked(self): + # If any resolved address is internal, reject the whole URL. + self.assertIsNotNone(self._validate_with_addrs("http://mixed/x", "142.250.1.1", "127.0.0.1")) + + def test_resolution_failure_defers_to_ytdlp(self): + with mock.patch("url_guard.socket.getaddrinfo", side_effect=socket.gaierror): + self.assertIsNone(validate_url("http://does-not-resolve.example/x")) + + +if __name__ == "__main__": + unittest.main() diff --git a/app/url_guard.py b/app/url_guard.py new file mode 100644 index 0000000..9d5adc7 --- /dev/null +++ b/app/url_guard.py @@ -0,0 +1,89 @@ +"""Lightweight SSRF guard for user-submitted URLs. + +MeTube hands user-submitted URLs to yt-dlp, whose generic extractor will fetch +any ``http(s)`` URL. Without a guard, an attacker can make the server fetch +internal endpoints (cloud metadata services, loopback, RFC1918 hosts, etc.) and +have the response saved to the download directory and served back. + +This module provides a single cheap validator applied at every URL ingress. It +intentionally does NOT attempt DNS-rebinding pinning, redirect-chain +re-validation, or validation of every media URL yt-dlp derives from remote +metadata β€” network isolation (e.g. Docker) remains the backstop for those. +""" + +import ipaddress +import logging +import socket +from urllib.parse import urlsplit + +log = logging.getLogger('url_guard') + +_ALLOWED_SCHEMES = ('http', 'https') + +# Hostnames that must be blocked without needing a lookup. ``localhost`` and any +# subdomain of it are conventionally loopback, and the GCP metadata name is a +# well-known SSRF target that may resolve via a resolver we don't control. +_BLOCKED_HOSTNAMES = ('localhost', 'metadata.google.internal') + + +def _hostname_is_blocked(hostname: str) -> bool: + host = hostname.rstrip('.').lower() + for blocked in _BLOCKED_HOSTNAMES: + if host == blocked or host.endswith('.' + blocked): + return True + return False + + +def _address_is_global(addr: str) -> bool: + try: + ip = ipaddress.ip_address(addr) + except ValueError: + return False + # Unwrap IPv4-mapped/compatible IPv6 (e.g. ::ffff:169.254.169.254) so the + # embedded IPv4 address is judged on its own merits. + if isinstance(ip, ipaddress.IPv6Address) and ip.ipv4_mapped is not None: + ip = ip.ipv4_mapped + return ip.is_global + + +def validate_url(url: str) -> str | None: + """Return an error message if the URL is disallowed, else ``None``. + + Inputs without a ``://`` scheme separator (bare video IDs, ``ytsearch:`` + and other yt-dlp search/extractor prefixes) are allowed unchanged so that + non-URL entries keep working. + """ + if not isinstance(url, str): + return 'Invalid URL' + + candidate = url.strip() + if '://' not in candidate: + # Not an absolute URL: bare video IDs, ytsearch: prefixes, etc. + return None + + parts = urlsplit(candidate) + scheme = parts.scheme.lower() + if scheme not in _ALLOWED_SCHEMES: + return f'URL scheme "{parts.scheme}" is not allowed (only http and https)' + + hostname = parts.hostname + if not hostname: + return 'URL is missing a host' + + if _hostname_is_blocked(hostname): + return f'Refusing to fetch internal host "{hostname}"' + + try: + addrinfo = socket.getaddrinfo(hostname, parts.port, proto=socket.IPPROTO_TCP) + except socket.gaierror: + # Let yt-dlp surface a normal resolution error rather than masking it. + return None + except (UnicodeError, ValueError): + return f'Invalid host "{hostname}"' + + for family, _type, _proto, _canonname, sockaddr in addrinfo: + addr = sockaddr[0] + if not _address_is_global(addr): + return f'Refusing to fetch internal address "{addr}" for host "{hostname}"' + + return None diff --git a/app/ytdl.py b/app/ytdl.py index 8a2dc42..dc76a79 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -25,6 +25,7 @@ from dl_formats import get_format, get_opts, AUDIO_FORMATS, merge_ytdl_option_la from datetime import datetime from state_store import AtomicJsonStore, from_json_compatible, read_legacy_shelf, to_json_compatible from subscriptions import _entry_id +from url_guard import validate_url log = logging.getLogger('ytdl') @@ -1444,6 +1445,13 @@ class DownloadQueue: return {'status': 'ok'} else: already.add(url) + # SSRF guard: reject non-http(s) schemes and hosts resolving to + # internal/loopback/link-local/metadata addresses before yt-dlp fetches + # anything. run_in_executor because validate_url may perform a DNS lookup. + url_error = await asyncio.get_running_loop().run_in_executor(None, validate_url, url) + if url_error is not None: + log.warning('Rejected URL "%s": %s', url, url_error) + return {'status': 'error', 'msg': url_error} try: entry = await asyncio.get_running_loop().run_in_executor( None, From 220f991fae31c53c9901435ad72a8b373893f573 Mon Sep 17 00:00:00 2001 From: Your GitHub Name Date: Thu, 16 Jul 2026 12:34:03 -0700 Subject: [PATCH 83/88] fix: prefer topic channel for album artist --- app/tests/test_ytdl_utils.py | 34 ++++++++++++++++++++++++++++++++++ app/ytdl.py | 16 ++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/app/tests/test_ytdl_utils.py b/app/tests/test_ytdl_utils.py index 73b40fe..72c716e 100644 --- a/app/tests/test_ytdl_utils.py +++ b/app/tests/test_ytdl_utils.py @@ -87,6 +87,40 @@ class AlbumArtistPostProcessorTests(unittest.TestCase): self.assertEqual(result['album_artist'], 'ScHoolboy Q') + def test_uses_topic_channel_artist_for_joint_album(self): + info = { + 'album': 'Watch the Throne', + 'artists': ['JAY-Z', 'Kanye West'], + 'channel': 'JAY-Z & Kanye West - Topic', + } + + _, result = self.postprocessor.run(info) + + self.assertEqual(result['album_artist'], 'JAY-Z & Kanye West') + + def test_uses_topic_uploader_and_strips_suffix_for_compilation(self): + info = { + 'album': 'Compilation', + 'artist': 'Track Artist', + 'channel': 'Regular Channel', + 'uploader': 'Various Artists - Topic', + } + + _, result = self.postprocessor.run(info) + + self.assertEqual(result['album_artist'], 'Various Artists') + + def test_regular_channel_falls_back_to_main_artist(self): + info = { + 'album': 'Album', + 'artist': 'Track Artist', + 'channel': 'Label Channel', + } + + _, result = self.postprocessor.run(info) + + self.assertEqual(result['album_artist'], 'Track Artist') + def test_preserves_explicit_various_artists(self): info = { 'album': 'Revenge of the Dreamers III', diff --git a/app/ytdl.py b/app/ytdl.py index daeb594..5c13b51 100644 --- a/app/ytdl.py +++ b/app/ytdl.py @@ -54,7 +54,9 @@ _LIVE_PROBE_MAX_FAILURES = 5 class _AlbumArtistPostProcessor(PostProcessor): - """Fill missing album-artist metadata from yt-dlp's main track artist.""" + """Fill missing album-artist metadata from yt-dlp's album-level signals.""" + + _TOPIC_SUFFIX = ' - Topic' @staticmethod def _has_value(value: Any) -> bool: @@ -76,13 +78,23 @@ class _AlbumArtistPostProcessor(PostProcessor): return candidate.split(' Β· ', 1)[0].strip() return None + @classmethod + def _topic_artist(cls, info) -> Optional[str]: + for field in ('channel', 'uploader'): + value = info.get(field) + if not isinstance(value, str) or not value.endswith(cls._TOPIC_SUFFIX): + continue + if artist := value[:-len(cls._TOPIC_SUFFIX)].strip(): + return artist + return None + def run(self, info): if not self._has_value(info.get('album')): return [], info if self._has_value(info.get('album_artist')) or self._has_value(info.get('album_artists')): return [], info - if artist := self._main_artist(info): + if artist := self._topic_artist(info) or self._main_artist(info): info['album_artist'] = artist return [], info From 8071611a843441572f8081deb8421fef9f27bfd9 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Thu, 16 Jul 2026 23:01:23 +0300 Subject: [PATCH 84/88] upgrade dependencies --- ui/package.json | 16 +- ui/pnpm-lock.yaml | 543 ++++++++++++++++++++++------------------------ uv.lock | 20 +- 3 files changed, 276 insertions(+), 303 deletions(-) diff --git a/ui/package.json b/ui/package.json index a5d75d5..2750d7c 100644 --- a/ui/package.json +++ b/ui/package.json @@ -33,10 +33,10 @@ "@angular/platform-browser-dynamic": "^22.0.6", "@angular/service-worker": "^22.0.6", "@fortawesome/angular-fontawesome": "~4.0.0", - "@fortawesome/fontawesome-svg-core": "^7.3.0", - "@fortawesome/free-brands-svg-icons": "^7.3.0", - "@fortawesome/free-regular-svg-icons": "^7.3.0", - "@fortawesome/free-solid-svg-icons": "^7.3.0", + "@fortawesome/fontawesome-svg-core": "^7.3.1", + "@fortawesome/free-brands-svg-icons": "^7.3.1", + "@fortawesome/free-regular-svg-icons": "^7.3.1", + "@fortawesome/free-solid-svg-icons": "^7.3.1", "@ng-bootstrap/ng-bootstrap": "^21.0.0", "@ng-select/ng-select": "^23.2.0", "@popperjs/core": "^2.11.8", @@ -49,13 +49,13 @@ }, "devDependencies": { "@angular-eslint/builder": "22.0.0", - "@angular/build": "^22.0.5", - "@angular/cli": "^22.0.5", + "@angular/build": "^22.0.7", + "@angular/cli": "^22.0.7", "@angular/compiler-cli": "^22.0.6", "@angular/localize": "^22.0.6", - "@eslint/js": "^9.39.4", + "@eslint/js": "^9.39.5", "angular-eslint": "22.0.0", - "eslint": "^9.39.4", + "eslint": "^9.39.5", "jsdom": "^27.4.0", "typescript": "~6.0.3", "typescript-eslint": "8.62.0", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index cbb013c..8e263c6 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -36,17 +36,17 @@ importers: specifier: ~4.0.0 version: 4.0.0(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)) '@fortawesome/fontawesome-svg-core': - specifier: ^7.3.0 - version: 7.3.0 + specifier: ^7.3.1 + version: 7.3.1 '@fortawesome/free-brands-svg-icons': - specifier: ^7.3.0 - version: 7.3.0 + specifier: ^7.3.1 + version: 7.3.1 '@fortawesome/free-regular-svg-icons': - specifier: ^7.3.0 - version: 7.3.0 + specifier: ^7.3.1 + version: 7.3.1 '@fortawesome/free-solid-svg-icons': - specifier: ^7.3.0 - version: 7.3.0 + specifier: ^7.3.1 + version: 7.3.1 '@ng-bootstrap/ng-bootstrap': specifier: ^21.0.0 version: 21.0.0(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/forms@22.0.6(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(rxjs@7.8.2))(@angular/localize@22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6))(@popperjs/core@2.11.8)(rxjs@7.8.2) @@ -77,13 +77,13 @@ importers: devDependencies: '@angular-eslint/builder': specifier: 22.0.0 - version: 22.0.0(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3) + version: 22.0.0(@angular/cli@22.0.7(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.5)(typescript@6.0.3) '@angular/build': - specifier: ^22.0.5 - version: 22.0.5(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6)(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@types/node@26.1.1)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(postcss@8.5.16)(tslib@2.8.1)(typescript@6.0.3)(vitest@4.1.10(@types/node@26.1.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0))) + specifier: ^22.0.7 + version: 22.0.7(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6)(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@types/node@26.1.1)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(postcss@8.5.19)(tslib@2.8.1)(typescript@6.0.3)(vitest@4.1.10(@types/node@26.1.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0))) '@angular/cli': - specifier: ^22.0.5 - version: 22.0.5(@types/node@26.1.1)(chokidar@5.0.0) + specifier: ^22.0.7 + version: 22.0.7(@types/node@26.1.1)(chokidar@5.0.0) '@angular/compiler-cli': specifier: ^22.0.6 version: 22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3) @@ -91,14 +91,14 @@ importers: specifier: ^22.0.6 version: 22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6) '@eslint/js': - specifier: ^9.39.4 - version: 9.39.4 + specifier: ^9.39.5 + version: 9.39.5 angular-eslint: specifier: 22.0.0 - version: 22.0.0(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.62.0(eslint@9.39.4)(typescript@6.0.3))(typescript@6.0.3) + version: 22.0.0(@angular/cli@22.0.7(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.5)(typescript-eslint@8.62.0(eslint@9.39.5)(typescript@6.0.3))(typescript@6.0.3) eslint: - specifier: ^9.39.4 - version: 9.39.4 + specifier: ^9.39.5 + version: 9.39.5 jsdom: specifier: ^27.4.0 version: 27.4.0 @@ -107,7 +107,7 @@ importers: version: 6.0.3 typescript-eslint: specifier: 8.62.0 - version: 8.62.0(eslint@9.39.4)(typescript@6.0.3) + version: 8.62.0(eslint@9.39.5)(typescript@6.0.3) vitest: specifier: ^4.1.10 version: 4.1.10(@types/node@26.1.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0)) @@ -177,13 +177,13 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.2200.5': - resolution: {integrity: sha512-Fbki7xEx37gn8uH5AFOh0EmfGV4y5xag2fTv1vn6KxY/h33L02KuAjiUX3h2YmSjnYSc1dXto3Q5c4lWs/I5Zw==} + '@angular-devkit/architect@0.2200.7': + resolution: {integrity: sha512-t03YYe5amlcpsX5gpMGi96kpBNTMKY7/rJsRDNO3v98mSy7kzNXlgzy/jyfdFev7AJusuwVM5TKVsUAgQxCgCw==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular-devkit/core@22.0.5': - resolution: {integrity: sha512-xxd3b9X0bbdGuYqDj1OgxtlGotrb/gsxQ5+4aqivWBHq4q/1RI8JfVB7gqcoYTvAfTq63Dwsk7Qd33hC4yJ1Wg==} + '@angular-devkit/core@22.0.7': + resolution: {integrity: sha512-r8XiflsVYcsvWp+zVvaNv5GsDoihaZ2OwWfn++N6YqTUZLcqDzqhsxeNk60sJ5V+Jn4ck1aKF4A9flmvSY+tpQ==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: chokidar: ^5.0.0 @@ -191,8 +191,8 @@ packages: chokidar: optional: true - '@angular-devkit/schematics@22.0.5': - resolution: {integrity: sha512-yKkImZuFLIbylmk1REyoW3EWQeqnr6qwtrzs4EPeaJgzjj+MZurq5slQ3fanK9lfX2q+W+dnSQFKBXHxlUGJ8Q==} + '@angular-devkit/schematics@22.0.7': + resolution: {integrity: sha512-bKgnBB0LPAj44uVXfW0UO1rQBb3HGXDZxa1bLtESr/KCK4j5iiaXlHqvJjc/z0em1Ds9WNQOfNXjV3IdJo9sSw==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@angular-eslint/builder@22.0.0': @@ -246,8 +246,8 @@ packages: peerDependencies: '@angular/core': 22.0.6 - '@angular/build@22.0.5': - resolution: {integrity: sha512-uVXf1cSKTMDvpiP5x0X8h7D7dICHC2XsbmvRYtxdyTRy+zJduqtCdZTwnwmCrwIc5hOF+bmwa5p17P6XirYVfw==} + '@angular/build@22.0.7': + resolution: {integrity: sha512-cm/QJmrIgsjzZJeBeqeNwgdgK6BGBKk7ca21jnSsrwlae3F9u1E8cImFbLeZSjEGghx4wFUacFDG+6lFEICntw==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler': ^22.0.0 @@ -257,7 +257,7 @@ packages: '@angular/platform-browser': ^22.0.0 '@angular/platform-server': ^22.0.0 '@angular/service-worker': ^22.0.0 - '@angular/ssr': ^22.0.5 + '@angular/ssr': ^22.0.7 istanbul-lib-instrument: ^6.0.0 karma: ^6.4.0 less: ^4.2.0 @@ -295,8 +295,8 @@ packages: vitest: optional: true - '@angular/cli@22.0.5': - resolution: {integrity: sha512-BYg2dWsbqdbpv3UVLNtDns1DoUbsROR8htPPIJEETj4Nfu7VF+g0nnM1CX2Ia+3002xtASgHp4YnRGQN/vAhWA==} + '@angular/cli@22.0.7': + resolution: {integrity: sha512-5r+fgP8ARnXZeylAGt5BuToVcR8Vn2QQZ6dPMe7V9o4NdvJqqOKmE7fEiRe7KaxIx5WSDhuAhgW9t+scLZQbvw==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true @@ -398,10 +398,6 @@ packages: resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} - '@babel/core@7.29.0': - resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} - engines: {node: '>=6.9.0'} - '@babel/core@7.29.7': resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} @@ -410,8 +406,8 @@ packages: resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.27.3': - resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + '@babel/helper-annotate-as-pure@7.29.7': + resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.29.7': @@ -839,12 +835,12 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.5': - resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + '@eslint/eslintrc@3.3.6': + resolution: {integrity: sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.4': - resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + '@eslint/js@9.39.5': + resolution: {integrity: sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -869,24 +865,24 @@ packages: peerDependencies: '@angular/core': ^21.0.0 - '@fortawesome/fontawesome-common-types@7.3.0': - resolution: {integrity: sha512-X/vND0Y1l9fVJ9O79UgtZnXSpz4aNF3bXlDxiJAEAm6kgeSftp9wjjBPgqzazJV8YlmxfRoeXNfSCJ48sf/Hhw==} + '@fortawesome/fontawesome-common-types@7.3.1': + resolution: {integrity: sha512-k0C0sdHmZtAo6dRDtd1Z/qcpyHbL0CKsjV8seMY/21xGhY5Wsv0XRmiI/xEEH4y2c9b1+jvgNs/3EqhV27yUEA==} engines: {node: '>=6'} - '@fortawesome/fontawesome-svg-core@7.3.0': - resolution: {integrity: sha512-MFbTNLDWkLJwbozDvHOZ7hwyDjQcBMBattlcOQ6ZmV5YD9bBrqdl1rNtmVjQ/lzqveXXX3sMz2Ew6fAgXoxmkw==} + '@fortawesome/fontawesome-svg-core@7.3.1': + resolution: {integrity: sha512-BoxVN3PKnMbgStHhjoaky/oWdxHomDqmBVA24IA3KEmssFGeI7u9YT/BJceOjIum/t6TpPa/vcMKaVYQeIQ/3Q==} engines: {node: '>=6'} - '@fortawesome/free-brands-svg-icons@7.3.0': - resolution: {integrity: sha512-W6C9ZbPWpwcUycq6U90lVbvWTrEnr01Td2x5jlO8fOtvww4kqDBMSmZqXQCc6FIIJD4kTH6G1MBRExAaSXz3yg==} + '@fortawesome/free-brands-svg-icons@7.3.1': + resolution: {integrity: sha512-8eiNzycuFhy3mZsi5EVO8JpO3H8l7/kSCl4Gk4iUew9hJuzYFqAAD6kqgeEup3+YZ9hL+5cjjorGzrRLkYuURg==} engines: {node: '>=6'} - '@fortawesome/free-regular-svg-icons@7.3.0': - resolution: {integrity: sha512-4675NiHzJJs0dLStFpp5G1JNfMYxqFSxZ2iCaiMfHptjlc8McLG9oqcd5pFEiPiqfiV2YG25cJ9EYWIEhUvp+A==} + '@fortawesome/free-regular-svg-icons@7.3.1': + resolution: {integrity: sha512-q1EsmL7Q8DDnkRBUjSvrxbq7c9oVwwVjCn/xa5apKmdp65YSgzUg2y0Ltnd5aDbT6GdAQQdXql5Ha90ArqIReQ==} engines: {node: '>=6'} - '@fortawesome/free-solid-svg-icons@7.3.0': - resolution: {integrity: sha512-YxI/CuwWeI3nPIoYU//vkDS+3ige/67DPZ6XwMATpYEFESzO9L8zfJOKllGRgIlpT/uebrZCcvAzp3peD7GmTw==} + '@fortawesome/free-solid-svg-icons@7.3.1': + resolution: {integrity: sha512-v0BLa0eqg7ubvVWeNSHVBs8fWH/GJicERZoJaxJ3FE/lj67VSqzoMg9pzfZVOfLMX10y0pGwQAuxoRVVH2patg==} engines: {node: '>=6'} '@gar/promise-retry@1.0.3': @@ -1568,8 +1564,8 @@ packages: cpu: [x64] os: [win32] - '@schematics/angular@22.0.5': - resolution: {integrity: sha512-e+1Ob5Kxt9QH2Gige0Bl56CXclG819RMl5Fso3kwO58+9AQnR2QxvjbetB7LDRl+YEG3TXCE4s3vdrV4gqRRAQ==} + '@schematics/angular@22.0.7': + resolution: {integrity: sha512-/GVLhB5zaxYGIaC4GFfsfk81SDq9ht0HbEHhHB+t1SqNPV8gnULDV2ZE0Cg+LGj+3YnPtSwpb7tS0vaHUcihVg==} engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@sigstore/bundle@4.0.0': @@ -1670,8 +1666,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.63.0': - resolution: {integrity: sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==} + '@typescript-eslint/project-service@8.64.0': + resolution: {integrity: sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1680,8 +1676,8 @@ packages: resolution: {integrity: sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.63.0': - resolution: {integrity: sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==} + '@typescript-eslint/scope-manager@8.64.0': + resolution: {integrity: sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.62.0': @@ -1690,8 +1686,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/tsconfig-utils@8.63.0': - resolution: {integrity: sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==} + '@typescript-eslint/tsconfig-utils@8.64.0': + resolution: {integrity: sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1707,8 +1703,8 @@ packages: resolution: {integrity: sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.63.0': - resolution: {integrity: sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==} + '@typescript-eslint/types@8.64.0': + resolution: {integrity: sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.62.0': @@ -1717,8 +1713,8 @@ packages: peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/typescript-estree@8.63.0': - resolution: {integrity: sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==} + '@typescript-eslint/typescript-estree@8.64.0': + resolution: {integrity: sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' @@ -1730,8 +1726,8 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.63.0': - resolution: {integrity: sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==} + '@typescript-eslint/utils@8.64.0': + resolution: {integrity: sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -1741,8 +1737,8 @@ packages: resolution: {integrity: sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.63.0': - resolution: {integrity: sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==} + '@typescript-eslint/visitor-keys@8.64.0': + resolution: {integrity: sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-basic-ssl@2.3.0': @@ -1881,8 +1877,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.10.42: - resolution: {integrity: sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==} + baseline-browser-mapping@2.10.43: + resolution: {integrity: sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -1912,8 +1908,8 @@ packages: resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} engines: {node: 18 || 20 || >=22} - browserslist@4.28.5: - resolution: {integrity: sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==} + browserslist@4.28.6: + resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1940,8 +1936,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - caniuse-lite@1.0.30001803: - resolution: {integrity: sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==} + caniuse-lite@1.0.30001805: + resolution: {integrity: sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -2099,8 +2095,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.389: - resolution: {integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==} + electron-to-chromium@1.5.392: + resolution: {integrity: sha512-1yQq3VQCZRwsnYc67Oc+1fge6Lwtn0hzi6zmEVkB61Zx21kTbwJAW4dFLadl5Rc1tKhG/kSpYXnfiAhu0f0a1g==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2148,8 +2144,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-module-lexer@2.3.0: - resolution: {integrity: sha512-KLdwQm2NvGLDkQDCGvmiQrhkd0JbMzXthwQAUgWjQuQdBLFa3eiBP5arXZyA+f8x+x7OXgud6bq2rxjGtHV2tw==} + es-module-lexer@2.3.1: + resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} es-object-atoms@1.1.2: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} @@ -2196,8 +2192,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@9.39.4: - resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + eslint@9.39.5: + resolution: {integrity: sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -2384,8 +2380,8 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} - hono@4.12.28: - resolution: {integrity: sha512-YwUvVpSF7m1yOblFPrU3Hbo8XhPheBoiyfGuII6z19LnOr6JpDnyyp7LFNrfV56wS8tpvtBFGRISHN02pDdLOA==} + hono@4.12.30: + resolution: {integrity: sha512-emn+JoJjrN9YTpRDS5it/UI2SO9BAE37T6I3d963RxcZ81G9A4pr2SZTEiiaiKbzx+NKRg5BZ89fCL7gCJCUog==} engines: {node: '>=16.9.0'} hosted-git-info@9.0.3: @@ -2434,6 +2430,10 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} + ignore@7.0.6: + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} + engines: {node: '>= 4'} + immutable@5.1.9: resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} @@ -2692,8 +2692,8 @@ packages: resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} engines: {node: ^20.17.0 || >=22.9.0} - nanoid@3.3.15: - resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -2736,8 +2736,8 @@ packages: engines: {node: ^20.17.0 || >=22.9.0} hasBin: true - node-releases@2.0.50: - resolution: {integrity: sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==} + node-releases@2.0.51: + resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} engines: {node: '>=18'} nopt@9.0.0: @@ -2890,8 +2890,8 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss@8.5.16: - resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} + postcss@8.5.19: + resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -3056,8 +3056,8 @@ packages: resolution: {integrity: sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==} engines: {node: '>=10.0.0'} - socket.io-parser@4.2.6: - resolution: {integrity: sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==} + socket.io-parser@4.2.7: + resolution: {integrity: sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==} engines: {node: '>=10.0.0'} socket.io@4.8.3: @@ -3137,8 +3137,8 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - tar@7.5.19: - resolution: {integrity: sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==} + tar@7.5.20: + resolution: {integrity: sha512-9FcyK4PA6+WbzlTM9WhQm6vB5W7cP7dUiPsv1g7YDwEQnQ1CGpK3MGlKk/ITVWMk05kHZuBhmVhiv8LZoy/PFQ==} engines: {node: '>=18'} tinybench@2.9.0: @@ -3160,11 +3160,11 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tldts-core@7.4.7: - resolution: {integrity: sha512-rNlAI8fKn/JckBMUSbNL/ES2kmDiurWaE49l+ikwEc9A6lFR7gMx9AhgQMQKBK4H5w4pKLH64JzZfB99uRsGNQ==} + tldts-core@7.4.8: + resolution: {integrity: sha512-c1P7u0EhACHj7lPy4MJm8iTFEU8+nB0LCtddH0fhP7noaVoXAqafMtOOeX+ulpuPBqnrRgRhw494RICT3mbhnw==} - tldts@7.4.7: - resolution: {integrity: sha512-56L0/9HELHSsG1bFCzay8UoLxzRL7kpFf7Wl5q/kSYwiSJGACvro61xnKzPNM+SadxllzdtXsKDSXE7HPeqIAw==} + tldts@7.4.8: + resolution: {integrity: sha512-htwgN/8KRB3z3vnC0BOETVh2m499g5GmyTK9Wq5JBLX3FNz6tSBveAd+fQhzy9hkjif8vy2jwDMR1sGhLtZl2A==} hasBin: true toidentifier@1.0.1: @@ -3378,8 +3378,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.21.0: - resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + ws@8.21.1: + resolution: {integrity: sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -3538,14 +3538,14 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.2200.5(chokidar@5.0.0)': + '@angular-devkit/architect@0.2200.7(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular-devkit/core': 22.0.7(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/core@22.0.5(chokidar@5.0.0)': + '@angular-devkit/core@22.0.7(chokidar@5.0.0)': dependencies: ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) @@ -3556,9 +3556,9 @@ snapshots: optionalDependencies: chokidar: 5.0.0 - '@angular-devkit/schematics@22.0.5(chokidar@5.0.0)': + '@angular-devkit/schematics@22.0.7(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular-devkit/core': 22.0.7(chokidar@5.0.0) jsonc-parser: 3.3.1 magic-string: 0.30.21 ora: 9.4.0 @@ -3566,46 +3566,46 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-eslint/builder@22.0.0(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3)': + '@angular-eslint/builder@22.0.0(@angular/cli@22.0.7(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.5)(typescript@6.0.3)': dependencies: - '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) - '@angular-devkit/core': 22.0.5(chokidar@5.0.0) - '@angular/cli': 22.0.5(@types/node@26.1.1)(chokidar@5.0.0) - eslint: 9.39.4 + '@angular-devkit/architect': 0.2200.7(chokidar@5.0.0) + '@angular-devkit/core': 22.0.7(chokidar@5.0.0) + '@angular/cli': 22.0.7(@types/node@26.1.1)(chokidar@5.0.0) + eslint: 9.39.5 typescript: 6.0.3 transitivePeerDependencies: - chokidar '@angular-eslint/bundled-angular-compiler@22.0.0': {} - '@angular-eslint/eslint-plugin-template@22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@typescript-eslint/types@8.63.0)(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': + '@angular-eslint/eslint-plugin-template@22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.5)(typescript@6.0.3))(@typescript-eslint/types@8.64.0)(@typescript-eslint/utils@8.64.0(eslint@9.39.5)(typescript@6.0.3))(eslint@9.39.5)(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.0.0 - '@angular-eslint/template-parser': 22.0.0(eslint@9.39.4)(typescript@6.0.3) - '@angular-eslint/utils': 22.0.0(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) - '@typescript-eslint/types': 8.63.0 - '@typescript-eslint/utils': 8.63.0(eslint@9.39.4)(typescript@6.0.3) + '@angular-eslint/template-parser': 22.0.0(eslint@9.39.5)(typescript@6.0.3) + '@angular-eslint/utils': 22.0.0(@typescript-eslint/utils@8.64.0(eslint@9.39.5)(typescript@6.0.3))(eslint@9.39.5)(typescript@6.0.3) + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/utils': 8.64.0(eslint@9.39.5)(typescript@6.0.3) aria-query: 5.3.2 axobject-query: 4.1.0 - eslint: 9.39.4 + eslint: 9.39.5 typescript: 6.0.3 - '@angular-eslint/eslint-plugin@22.0.0(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': + '@angular-eslint/eslint-plugin@22.0.0(@typescript-eslint/utils@8.64.0(eslint@9.39.5)(typescript@6.0.3))(eslint@9.39.5)(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.0.0 - '@angular-eslint/utils': 22.0.0(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) - '@typescript-eslint/utils': 8.63.0(eslint@9.39.4)(typescript@6.0.3) - eslint: 9.39.4 + '@angular-eslint/utils': 22.0.0(@typescript-eslint/utils@8.64.0(eslint@9.39.5)(typescript@6.0.3))(eslint@9.39.5)(typescript@6.0.3) + '@typescript-eslint/utils': 8.64.0(eslint@9.39.5)(typescript@6.0.3) + eslint: 9.39.5 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 - '@angular-eslint/schematics@22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(@typescript-eslint/types@8.63.0)(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3)': + '@angular-eslint/schematics@22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.5)(typescript@6.0.3))(@angular/cli@22.0.7(@types/node@26.1.1)(chokidar@5.0.0))(@typescript-eslint/types@8.64.0)(@typescript-eslint/utils@8.64.0(eslint@9.39.5)(typescript@6.0.3))(chokidar@5.0.0)(eslint@9.39.5)(typescript@6.0.3)': dependencies: - '@angular-devkit/core': 22.0.5(chokidar@5.0.0) - '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 22.0.0(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) - '@angular-eslint/eslint-plugin-template': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@typescript-eslint/types@8.63.0)(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) - '@angular/cli': 22.0.5(@types/node@26.1.1)(chokidar@5.0.0) + '@angular-devkit/core': 22.0.7(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.7(chokidar@5.0.0) + '@angular-eslint/eslint-plugin': 22.0.0(@typescript-eslint/utils@8.64.0(eslint@9.39.5)(typescript@6.0.3))(eslint@9.39.5)(typescript@6.0.3) + '@angular-eslint/eslint-plugin-template': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.5)(typescript@6.0.3))(@typescript-eslint/types@8.64.0)(@typescript-eslint/utils@8.64.0(eslint@9.39.5)(typescript@6.0.3))(eslint@9.39.5)(typescript@6.0.3) + '@angular/cli': 22.0.7(@types/node@26.1.1)(chokidar@5.0.0) ignore: 7.0.5 semver: 7.8.0 strip-json-comments: 3.1.1 @@ -3617,18 +3617,18 @@ snapshots: - eslint - typescript - '@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3)': + '@angular-eslint/template-parser@22.0.0(eslint@9.39.5)(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.0.0 - eslint: 9.39.4 + eslint: 9.39.5 eslint-scope: 9.1.2 typescript: 6.0.3 - '@angular-eslint/utils@22.0.0(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': + '@angular-eslint/utils@22.0.0(@typescript-eslint/utils@8.64.0(eslint@9.39.5)(typescript@6.0.3))(eslint@9.39.5)(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.0.0 - '@typescript-eslint/utils': 8.63.0(eslint@9.39.4)(typescript@6.0.3) - eslint: 9.39.4 + '@typescript-eslint/utils': 8.64.0(eslint@9.39.5)(typescript@6.0.3) + eslint: 9.39.5 typescript: 6.0.3 '@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))': @@ -3636,19 +3636,19 @@ snapshots: '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) tslib: 2.8.1 - '@angular/build@22.0.5(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6)(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@types/node@26.1.1)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(postcss@8.5.16)(tslib@2.8.1)(typescript@6.0.3)(vitest@4.1.10(@types/node@26.1.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0)))': + '@angular/build@22.0.7(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6)(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(@angular/localize@22.0.6(@angular/compiler-cli@22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3))(@angular/compiler@22.0.6))(@angular/platform-browser@22.0.6(@angular/animations@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/common@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0)))(@angular/service-worker@22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2))(@types/node@26.1.1)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(postcss@8.5.19)(tslib@2.8.1)(typescript@6.0.3)(vitest@4.1.10(@types/node@26.1.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0)))': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) + '@angular-devkit/architect': 0.2200.7(chokidar@5.0.0) '@angular/compiler': 22.0.6 '@angular/compiler-cli': 22.0.6(@angular/compiler@22.0.6)(typescript@6.0.3) - '@babel/core': 7.29.0 - '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/core': 7.29.7 + '@babel/helper-annotate-as-pure': 7.29.7 '@babel/helper-split-export-declaration': 7.24.7 '@inquirer/confirm': 6.0.12(@types/node@26.1.1) '@vitejs/plugin-basic-ssl': 2.3.0(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0)) beasties: 0.4.2 - browserslist: 4.28.5 + browserslist: 4.28.6 esbuild: 0.28.1 https-proxy-agent: 9.0.0 jsonc-parser: 3.3.1 @@ -3674,7 +3674,7 @@ snapshots: '@angular/service-worker': 22.0.6(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))(rxjs@7.8.2) istanbul-lib-instrument: 6.0.3 lmdb: 3.5.4 - postcss: 8.5.16 + postcss: 8.5.19 vitest: 4.1.10(@types/node@26.1.1)(jsdom@27.4.0)(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0)) transitivePeerDependencies: - '@types/node' @@ -3689,15 +3689,15 @@ snapshots: - tsx - yaml - '@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0)': + '@angular/cli@22.0.7(@types/node@26.1.1)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) - '@angular-devkit/core': 22.0.5(chokidar@5.0.0) - '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) + '@angular-devkit/architect': 0.2200.7(chokidar@5.0.0) + '@angular-devkit/core': 22.0.7(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.7(chokidar@5.0.0) '@inquirer/prompts': 8.4.2(@types/node@26.1.1) '@listr2/prompt-adapter-inquirer': 4.2.3(@inquirer/prompts@8.4.2(@types/node@26.1.1))(@types/node@26.1.1)(listr2@10.2.1) '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.2) - '@schematics/angular': 22.0.5(chokidar@5.0.0) + '@schematics/angular': 22.0.7(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 algoliasearch: 5.52.0 ini: 6.0.0 @@ -3818,26 +3818,6 @@ snapshots: '@babel/compat-data@7.29.7': {} - '@babel/core@7.29.0': - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/generator': 7.29.7 - '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.0) - '@babel/helpers': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/core@7.29.7': dependencies: '@babel/code-frame': 7.29.7 @@ -3866,7 +3846,7 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.27.3': + '@babel/helper-annotate-as-pure@7.29.7': dependencies: '@babel/types': 7.29.7 @@ -3874,7 +3854,7 @@ snapshots: dependencies: '@babel/compat-data': 7.29.7 '@babel/helper-validator-option': 7.29.7 - browserslist: 4.28.5 + browserslist: 4.28.6 lru-cache: 5.1.1 semver: 6.3.1 @@ -3887,15 +3867,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.0)': - dependencies: - '@babel/core': 7.29.0 - '@babel/helper-module-imports': 7.29.7 - '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.7 - transitivePeerDependencies: - - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -4127,9 +4098,9 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4)': + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.5)': dependencies: - eslint: 9.39.4 + eslint: 9.39.5 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -4150,7 +4121,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.5': + '@eslint/eslintrc@3.3.6': dependencies: ajv: 6.15.0 debug: 4.4.3 @@ -4164,7 +4135,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.39.4': {} + '@eslint/js@9.39.5': {} '@eslint/object-schema@2.1.7': {} @@ -4178,35 +4149,35 @@ snapshots: '@fortawesome/angular-fontawesome@4.0.0(@angular/core@22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0))': dependencies: '@angular/core': 22.0.6(@angular/compiler@22.0.6)(rxjs@7.8.2)(zone.js@0.15.0) - '@fortawesome/fontawesome-svg-core': 7.3.0 + '@fortawesome/fontawesome-svg-core': 7.3.1 tslib: 2.8.1 - '@fortawesome/fontawesome-common-types@7.3.0': {} + '@fortawesome/fontawesome-common-types@7.3.1': {} - '@fortawesome/fontawesome-svg-core@7.3.0': + '@fortawesome/fontawesome-svg-core@7.3.1': dependencies: - '@fortawesome/fontawesome-common-types': 7.3.0 + '@fortawesome/fontawesome-common-types': 7.3.1 - '@fortawesome/free-brands-svg-icons@7.3.0': + '@fortawesome/free-brands-svg-icons@7.3.1': dependencies: - '@fortawesome/fontawesome-common-types': 7.3.0 + '@fortawesome/fontawesome-common-types': 7.3.1 - '@fortawesome/free-regular-svg-icons@7.3.0': + '@fortawesome/free-regular-svg-icons@7.3.1': dependencies: - '@fortawesome/fontawesome-common-types': 7.3.0 + '@fortawesome/fontawesome-common-types': 7.3.1 - '@fortawesome/free-solid-svg-icons@7.3.0': + '@fortawesome/free-solid-svg-icons@7.3.1': dependencies: - '@fortawesome/fontawesome-common-types': 7.3.0 + '@fortawesome/fontawesome-common-types': 7.3.1 '@gar/promise-retry@1.0.3': {} '@harperfast/extended-iterable@1.0.3': optional: true - '@hono/node-server@1.19.14(hono@4.12.28)': + '@hono/node-server@1.19.14(hono@4.12.30)': dependencies: - hono: 4.12.28 + hono: 4.12.30 '@humanfs/core@0.19.2': dependencies: @@ -4407,7 +4378,7 @@ snapshots: '@modelcontextprotocol/sdk@1.29.0(zod@4.4.2)': dependencies: - '@hono/node-server': 1.19.14(hono@4.12.28) + '@hono/node-server': 1.19.14(hono@4.12.30) ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) content-type: 1.0.5 @@ -4417,7 +4388,7 @@ snapshots: eventsource-parser: 3.1.0 express: 5.2.1 express-rate-limit: 8.5.2(express@5.2.1) - hono: 4.12.28 + hono: 4.12.30 jose: 6.2.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -4728,10 +4699,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.60.2': optional: true - '@schematics/angular@22.0.5(chokidar@5.0.0)': + '@schematics/angular@22.0.7(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 22.0.5(chokidar@5.0.0) - '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) + '@angular-devkit/core': 22.0.7(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.7(chokidar@5.0.0) jsonc-parser: 3.3.1 typescript: 6.0.3 transitivePeerDependencies: @@ -4828,30 +4799,30 @@ snapshots: dependencies: '@types/node': 26.1.1 - '@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.5)(typescript@6.0.3))(eslint@9.39.5)(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.62.0(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/parser': 8.62.0(eslint@9.39.5)(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.62.0 - '@typescript-eslint/type-utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) - '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/type-utils': 8.62.0(eslint@9.39.5)(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.0(eslint@9.39.5)(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.62.0 - eslint: 9.39.4 - ignore: 7.0.5 + eslint: 9.39.5 + ignore: 7.0.6 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.62.0(eslint@9.39.4)(typescript@6.0.3)': + '@typescript-eslint/parser@8.62.0(eslint@9.39.5)(typescript@6.0.3)': dependencies: '@typescript-eslint/scope-manager': 8.62.0 '@typescript-eslint/types': 8.62.0 '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.62.0 debug: 4.4.3 - eslint: 9.39.4 + eslint: 9.39.5 typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -4865,10 +4836,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.63.0(typescript@6.0.3)': + '@typescript-eslint/project-service@8.64.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.63.0(typescript@6.0.3) - '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@6.0.3) + '@typescript-eslint/types': 8.64.0 debug: 4.4.3 typescript: 6.0.3 transitivePeerDependencies: @@ -4879,26 +4850,26 @@ snapshots: '@typescript-eslint/types': 8.62.0 '@typescript-eslint/visitor-keys': 8.62.0 - '@typescript-eslint/scope-manager@8.63.0': + '@typescript-eslint/scope-manager@8.64.0': dependencies: - '@typescript-eslint/types': 8.63.0 - '@typescript-eslint/visitor-keys': 8.63.0 + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/visitor-keys': 8.64.0 '@typescript-eslint/tsconfig-utils@8.62.0(typescript@6.0.3)': dependencies: typescript: 6.0.3 - '@typescript-eslint/tsconfig-utils@8.63.0(typescript@6.0.3)': + '@typescript-eslint/tsconfig-utils@8.64.0(typescript@6.0.3)': dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.62.0(eslint@9.39.4)(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.62.0(eslint@9.39.5)(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.62.0 '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.0(eslint@9.39.5)(typescript@6.0.3) debug: 4.4.3 - eslint: 9.39.4 + eslint: 9.39.5 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: @@ -4906,7 +4877,7 @@ snapshots: '@typescript-eslint/types@8.62.0': {} - '@typescript-eslint/types@8.63.0': {} + '@typescript-eslint/types@8.64.0': {} '@typescript-eslint/typescript-estree@8.62.0(typescript@6.0.3)': dependencies: @@ -4923,12 +4894,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.63.0(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.64.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.63.0(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.63.0(typescript@6.0.3) - '@typescript-eslint/types': 8.63.0 - '@typescript-eslint/visitor-keys': 8.63.0 + '@typescript-eslint/project-service': 8.64.0(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.64.0(typescript@6.0.3) + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/visitor-keys': 8.64.0 debug: 4.4.3 minimatch: 10.2.5 semver: 7.8.5 @@ -4938,24 +4909,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.62.0(eslint@9.39.4)(typescript@6.0.3)': + '@typescript-eslint/utils@8.62.0(eslint@9.39.5)(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5) '@typescript-eslint/scope-manager': 8.62.0 '@typescript-eslint/types': 8.62.0 '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) - eslint: 9.39.4 + eslint: 9.39.5 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3)': + '@typescript-eslint/utils@8.64.0(eslint@9.39.5)(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) - '@typescript-eslint/scope-manager': 8.63.0 - '@typescript-eslint/types': 8.63.0 - '@typescript-eslint/typescript-estree': 8.63.0(typescript@6.0.3) - eslint: 9.39.4 + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5) + '@typescript-eslint/scope-manager': 8.64.0 + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/typescript-estree': 8.64.0(typescript@6.0.3) + eslint: 9.39.5 typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -4965,9 +4936,9 @@ snapshots: '@typescript-eslint/types': 8.62.0 eslint-visitor-keys: 5.0.1 - '@typescript-eslint/visitor-keys@8.63.0': + '@typescript-eslint/visitor-keys@8.64.0': dependencies: - '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/types': 8.64.0 eslint-visitor-keys: 5.0.1 '@vitejs/plugin-basic-ssl@2.3.0(vite@7.3.5(@types/node@26.1.1)(sass@1.99.0))': @@ -5074,21 +5045,21 @@ snapshots: '@algolia/requester-fetch': 5.52.0 '@algolia/requester-node-http': 5.52.0 - angular-eslint@22.0.0(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript-eslint@8.62.0(eslint@9.39.4)(typescript@6.0.3))(typescript@6.0.3): + angular-eslint@22.0.0(@angular/cli@22.0.7(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.5)(typescript-eslint@8.62.0(eslint@9.39.5)(typescript@6.0.3))(typescript@6.0.3): dependencies: - '@angular-devkit/core': 22.0.5(chokidar@5.0.0) - '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) - '@angular-eslint/builder': 22.0.0(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3) - '@angular-eslint/eslint-plugin': 22.0.0(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) - '@angular-eslint/eslint-plugin-template': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@typescript-eslint/types@8.63.0)(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) - '@angular-eslint/schematics': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.4)(typescript@6.0.3))(@angular/cli@22.0.5(@types/node@26.1.1)(chokidar@5.0.0))(@typescript-eslint/types@8.63.0)(@typescript-eslint/utils@8.63.0(eslint@9.39.4)(typescript@6.0.3))(chokidar@5.0.0)(eslint@9.39.4)(typescript@6.0.3) - '@angular-eslint/template-parser': 22.0.0(eslint@9.39.4)(typescript@6.0.3) - '@angular/cli': 22.0.5(@types/node@26.1.1)(chokidar@5.0.0) - '@typescript-eslint/types': 8.63.0 - '@typescript-eslint/utils': 8.63.0(eslint@9.39.4)(typescript@6.0.3) - eslint: 9.39.4 + '@angular-devkit/core': 22.0.7(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.7(chokidar@5.0.0) + '@angular-eslint/builder': 22.0.0(@angular/cli@22.0.7(@types/node@26.1.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.5)(typescript@6.0.3) + '@angular-eslint/eslint-plugin': 22.0.0(@typescript-eslint/utils@8.64.0(eslint@9.39.5)(typescript@6.0.3))(eslint@9.39.5)(typescript@6.0.3) + '@angular-eslint/eslint-plugin-template': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.5)(typescript@6.0.3))(@typescript-eslint/types@8.64.0)(@typescript-eslint/utils@8.64.0(eslint@9.39.5)(typescript@6.0.3))(eslint@9.39.5)(typescript@6.0.3) + '@angular-eslint/schematics': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@9.39.5)(typescript@6.0.3))(@angular/cli@22.0.7(@types/node@26.1.1)(chokidar@5.0.0))(@typescript-eslint/types@8.64.0)(@typescript-eslint/utils@8.64.0(eslint@9.39.5)(typescript@6.0.3))(chokidar@5.0.0)(eslint@9.39.5)(typescript@6.0.3) + '@angular-eslint/template-parser': 22.0.0(eslint@9.39.5)(typescript@6.0.3) + '@angular/cli': 22.0.7(@types/node@26.1.1)(chokidar@5.0.0) + '@typescript-eslint/types': 8.64.0 + '@typescript-eslint/utils': 8.64.0(eslint@9.39.5)(typescript@6.0.3) + eslint: 9.39.5 typescript: 6.0.3 - typescript-eslint: 8.62.0(eslint@9.39.4)(typescript@6.0.3) + typescript-eslint: 8.62.0(eslint@9.39.5)(typescript@6.0.3) transitivePeerDependencies: - chokidar - supports-color @@ -5119,7 +5090,7 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.10.42: {} + baseline-browser-mapping@2.10.43: {} beasties@0.4.2: dependencies: @@ -5129,9 +5100,9 @@ snapshots: domhandler: 5.0.3 htmlparser2: 10.1.0 picocolors: 1.1.1 - postcss: 8.5.16 + postcss: 8.5.19 postcss-media-query-parser: 0.2.3 - postcss-safe-parser: 7.0.1(postcss@8.5.16) + postcss-safe-parser: 7.0.1(postcss@8.5.19) bidi-js@1.0.3: dependencies: @@ -5166,13 +5137,13 @@ snapshots: dependencies: balanced-match: 4.0.4 - browserslist@4.28.5: + browserslist@4.28.6: dependencies: - baseline-browser-mapping: 2.10.42 - caniuse-lite: 1.0.30001803 - electron-to-chromium: 1.5.389 - node-releases: 2.0.50 - update-browserslist-db: 1.2.3(browserslist@4.28.5) + baseline-browser-mapping: 2.10.43 + caniuse-lite: 1.0.30001805 + electron-to-chromium: 1.5.392 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.6) buffer-from@1.1.2: {} @@ -5203,7 +5174,7 @@ snapshots: callsites@3.1.0: {} - caniuse-lite@1.0.30001803: {} + caniuse-lite@1.0.30001805: {} chai@6.2.2: {} @@ -5346,7 +5317,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.389: {} + electron-to-chromium@1.5.392: {} emoji-regex@10.6.0: {} @@ -5357,7 +5328,7 @@ snapshots: '@socket.io/component-emitter': 3.1.2 debug: 4.4.3 engine.io-parser: 5.2.3 - ws: 8.21.0 + ws: 8.21.1 xmlhttprequest-ssl: 2.1.2 transitivePeerDependencies: - bufferutil @@ -5377,7 +5348,7 @@ snapshots: cors: 2.8.6 debug: 4.4.3 engine.io-parser: 5.2.3 - ws: 8.21.0 + ws: 8.21.1 transitivePeerDependencies: - bufferutil - supports-color @@ -5397,7 +5368,7 @@ snapshots: es-errors@1.3.0: {} - es-module-lexer@2.3.0: {} + es-module-lexer@2.3.1: {} es-object-atoms@1.1.2: dependencies: @@ -5485,15 +5456,15 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@9.39.4: + eslint@9.39.5: dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4) + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.5) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.2 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.5 - '@eslint/js': 9.39.4 + '@eslint/eslintrc': 3.3.6 + '@eslint/js': 9.39.5 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 @@ -5714,7 +5685,7 @@ snapshots: dependencies: function-bind: 1.1.2 - hono@4.12.28: {} + hono@4.12.30: {} hosted-git-info@9.0.3: dependencies: @@ -5776,6 +5747,8 @@ snapshots: ignore@7.0.5: {} + ignore@7.0.6: {} + immutable@5.1.9: {} import-fresh@3.3.1: @@ -5857,7 +5830,7 @@ snapshots: webidl-conversions: 8.0.1 whatwg-mimetype: 4.0.0 whatwg-url: 15.1.0 - ws: 8.21.0 + ws: 8.21.1 xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' @@ -6053,7 +6026,7 @@ snapshots: mute-stream@3.0.0: {} - nanoid@3.3.15: {} + nanoid@3.3.16: {} natural-compare@1.4.0: {} @@ -6101,12 +6074,12 @@ snapshots: nopt: 9.0.0 proc-log: 6.1.0 semver: 7.7.4 - tar: 7.5.19 + tar: 7.5.20 tinyglobby: 0.2.17 undici: 6.27.0 which: 6.0.1 - node-releases@2.0.50: {} + node-releases@2.0.51: {} nopt@9.0.0: dependencies: @@ -6227,7 +6200,7 @@ snapshots: proc-log: 6.1.0 sigstore: 4.1.1 ssri: 13.0.1 - tar: 7.5.19 + tar: 7.5.20 transitivePeerDependencies: - supports-color @@ -6278,13 +6251,13 @@ snapshots: postcss-media-query-parser@0.2.3: {} - postcss-safe-parser@7.0.1(postcss@8.5.16): + postcss-safe-parser@7.0.1(postcss@8.5.19): dependencies: - postcss: 8.5.16 + postcss: 8.5.19 - postcss@8.5.16: + postcss@8.5.19: dependencies: - nanoid: 3.3.15 + nanoid: 3.3.16 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -6488,7 +6461,7 @@ snapshots: socket.io-adapter@2.5.8: dependencies: debug: 4.4.3 - ws: 8.21.0 + ws: 8.21.1 transitivePeerDependencies: - bufferutil - supports-color @@ -6499,13 +6472,13 @@ snapshots: '@socket.io/component-emitter': 3.1.2 debug: 4.4.3 engine.io-client: 6.6.6 - socket.io-parser: 4.2.6 + socket.io-parser: 4.2.7 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - socket.io-parser@4.2.6: + socket.io-parser@4.2.7: dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.4.3 @@ -6520,7 +6493,7 @@ snapshots: debug: 4.4.3 engine.io: 6.6.9 socket.io-adapter: 2.5.8 - socket.io-parser: 4.2.6 + socket.io-parser: 4.2.7 transitivePeerDependencies: - bufferutil - supports-color @@ -6594,7 +6567,7 @@ snapshots: symbol-tree@3.2.4: {} - tar@7.5.19: + tar@7.5.20: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 @@ -6618,17 +6591,17 @@ snapshots: tinyrainbow@3.1.0: {} - tldts-core@7.4.7: {} + tldts-core@7.4.8: {} - tldts@7.4.7: + tldts@7.4.8: dependencies: - tldts-core: 7.4.7 + tldts-core: 7.4.8 toidentifier@1.0.1: {} tough-cookie@6.0.2: dependencies: - tldts: 7.4.7 + tldts: 7.4.8 tr46@6.0.0: dependencies: @@ -6658,13 +6631,13 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.2 - typescript-eslint@8.62.0(eslint@9.39.4)(typescript@6.0.3): + typescript-eslint@8.62.0(eslint@9.39.5)(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.4)(typescript@6.0.3))(eslint@9.39.4)(typescript@6.0.3) - '@typescript-eslint/parser': 8.62.0(eslint@9.39.4)(typescript@6.0.3) + '@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.5)(typescript@6.0.3))(eslint@9.39.5)(typescript@6.0.3) + '@typescript-eslint/parser': 8.62.0(eslint@9.39.5)(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.62.0(eslint@9.39.4)(typescript@6.0.3) - eslint: 9.39.4 + '@typescript-eslint/utils': 8.62.0(eslint@9.39.5)(typescript@6.0.3) + eslint: 9.39.5 typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -6677,9 +6650,9 @@ snapshots: unpipe@1.0.0: {} - update-browserslist-db@1.2.3(browserslist@4.28.5): + update-browserslist-db@1.2.3(browserslist@4.28.6): dependencies: - browserslist: 4.28.5 + browserslist: 4.28.6 escalade: 3.2.0 picocolors: 1.1.1 @@ -6696,7 +6669,7 @@ snapshots: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.16 + postcss: 8.5.19 rollup: 4.60.2 tinyglobby: 0.2.16 optionalDependencies: @@ -6713,7 +6686,7 @@ snapshots: '@vitest/snapshot': 4.1.10 '@vitest/spy': 4.1.10 '@vitest/utils': 4.1.10 - es-module-lexer: 2.3.0 + es-module-lexer: 2.3.1 expect-type: 1.4.0 magic-string: 0.30.21 obug: 2.1.3 @@ -6784,7 +6757,7 @@ snapshots: wrappy@1.0.2: {} - ws@8.21.0: {} + ws@8.21.1: {} xml-name-validator@5.0.0: {} diff --git a/uv.lock b/uv.lock index afaba4b..cd0dd80 100644 --- a/uv.lock +++ b/uv.lock @@ -106,14 +106,14 @@ wheels = [ [[package]] name = "anyio" -version = "4.14.1" +version = "4.14.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/3b/72/5562aabb8dd7181e8e860622a38bea08d17842b99ecd4c91f84ac95251b0/anyio-4.14.1.tar.gz", hash = "sha256:8d648a3544c1a700e3ff78615cd679e4c5c3f149904287e73687b2596963629e", size = 254831, upload-time = "2026-06-24T20:56:06.017Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/7b/90df4a0a816d98d6ea26f559d87836d494a2cf1fcf063be67df50a7bcc30/anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72", size = 124875, upload-time = "2026-06-24T20:56:04.413Z" }, + { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, ] [[package]] @@ -366,15 +366,15 @@ wheels = [ [[package]] name = "deno" -version = "2.9.2" +version = "2.9.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/dc/3c/bf3a4e2c00f7eefd4ed2fd033a958cab3e92dcc539d3ab80351850cae99c/deno-2.9.2.tar.gz", hash = "sha256:3b77d7689c15fb2c47d5a3927dc8cf70dbd408bc92e20c70da079c6d78980e34", size = 8164, upload-time = "2026-07-08T14:38:54.267Z" } +sdist = { url = "https://files.pythonhosted.org/packages/98/ab/638749d76881f74d100a079414745f0a0fd20bba38e27ab4a3a6495d8264/deno-2.9.3.tar.gz", hash = "sha256:73268cbac7f7c4ff1983e49420a93f3f3f2f2e4e4372436f0942d04d02c9e943", size = 8166, upload-time = "2026-07-15T15:37:50.565Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/fe/4a02a256f19b3f31ac17ec37f6467ecf523601ed753e6c9177865dd2bbb2/deno-2.9.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fb4bb5362d02c193f8086668789076f366416980b79277c06ea6cff73866b668", size = 42341183, upload-time = "2026-07-08T14:38:36.384Z" }, - { url = "https://files.pythonhosted.org/packages/5a/3e/235530ac9b9c206ee8c098e2b215f711a71d915d8d7d2be77599c8c1db4b/deno-2.9.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f6f74fd8698a8a9a31af14eed5730f1964ab50eaa0129079d11d91fa92496f60", size = 37984508, upload-time = "2026-07-08T14:38:40.006Z" }, - { url = "https://files.pythonhosted.org/packages/7b/27/06a166fca538c06dfae4c45dbb92c2ebf3083341141966a3d649272ea734/deno-2.9.2-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:8b2fd9318d452b7e44c45968916670a0c5452d72796e1af9abfaa7a389e4aa6b", size = 42089854, upload-time = "2026-07-08T14:38:43.841Z" }, - { url = "https://files.pythonhosted.org/packages/09/b4/38338550a5ca0a7bcc39991e3d48e46ce3699413460dff32f383a611d822/deno-2.9.2-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:bf0471db7238e90810bca77a8d2c28646317b5a3fd9e5cdfdf73b251fde11b83", size = 43916921, upload-time = "2026-07-08T14:38:48.057Z" }, - { url = "https://files.pythonhosted.org/packages/a9/3d/c490c27e1f720baf014bb2c18b29d09b11c21d69fadfd69639e5904cdc95/deno-2.9.2-py3-none-win_amd64.whl", hash = "sha256:8e815d66b3e1314d028b2f0a05c75a331dd7025f4c825e12dd1f057207cee1f6", size = 41621982, upload-time = "2026-07-08T14:38:51.928Z" }, + { url = "https://files.pythonhosted.org/packages/07/fa/b4e1e2b3b894ed992c78a6796a43dc775edff895239d83c0e9a10457f4f7/deno-2.9.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:1deef9fe97ab28d5d4fea07693075e9ccc62f4ea97a653047a770beb01ecd307", size = 42354026, upload-time = "2026-07-15T15:37:34.977Z" }, + { url = "https://files.pythonhosted.org/packages/3d/cc/4090ef7370005b740efe57ec6ff75a3471393abe9816f1459e73dbbf0429/deno-2.9.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:78635cd9f0802ce7125aa3ba6feb5775460f55d432611374f16b454ffb8a1308", size = 37997909, upload-time = "2026-07-15T15:37:38.291Z" }, + { url = "https://files.pythonhosted.org/packages/14/07/70b7c965bbca5f3d158859acda0e90c041f1fa98b8c9ac8eff3fafd8b96c/deno-2.9.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:7c1c669827a480ef9e2710dd762560811da35dca04a9e90b23c0518587dadb8a", size = 42100873, upload-time = "2026-07-15T15:37:41.539Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f5/0a07cc19a27476011719dd017a98950c7ccc33058c2ffc07019ba3ad9a5e/deno-2.9.3-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:3e724d5c8df13a90f6b50b16cc8df1acfe64c61a7736a774e11d61f3bca3a3b1", size = 43928967, upload-time = "2026-07-15T15:37:45.175Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9a/030847bd4ea6cefbb471cae2b63c9ab29a3ab6c1809484447752bbcbcd52/deno-2.9.3-py3-none-win_amd64.whl", hash = "sha256:e2ebe2b5c1a7ee9daeaf8caf14ebc33fda96b4c2399da259e761fe2f82d22fa4", size = 41630201, upload-time = "2026-07-15T15:37:48.344Z" }, ] [[package]] From fa02717e1283bc3faf3982cd124f4c0cf205d4b1 Mon Sep 17 00:00:00 2001 From: tjelite1986 Date: Thu, 16 Jul 2026 22:38:52 +0200 Subject: [PATCH 85/88] docs: document pairing MeTube with a music tagger Short section pointing beets / Picard / Lidarr at AUDIO_DOWNLOAD_DIR, as suggested in #1027. Co-Authored-By: Claude Fable 5 --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 3de99d0..0f84924 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,14 @@ In case you need to use your browser's cookies with MeTube, for example to downl * After upload, the cookie indicator should show as active. * Use **Delete Cookies** in the same section to remove uploaded cookies. +## 🎡 Pairing with a music tagger + +MeTube deliberately stops once the file is written β€” tagging and library organization belong to dedicated tools. Point one at your audio download folder (`AUDIO_DOWNLOAD_DIR`): + +* [beets](https://beets.io) β€” `beet import` matches tracks against MusicBrainz, fixes tags, and files them into an Artist/Album library; headless and scriptable. +* [MusicBrainz Picard](https://picard.musicbrainz.org) β€” GUI tagger with acoustic fingerprinting. +* [Lidarr](https://lidarr.audio) β€” full music library manager; add the folder as an import path. + ## πŸ”Œ Browser extensions Browser extensions allow right-clicking videos and sending them directly to MeTube. If you're on an HTTPS page, your MeTube instance must be behind an HTTPS reverse proxy (see below) for extensions to work. From 8fe81dea18d7ffb01a9b108f5fcacf218fcdb791 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 17 Jul 2026 15:08:33 +0300 Subject: [PATCH 86/88] docs: encode project scope boundary in AGENTS.md Document the line between in-scope work (improving the download-time write, surfacing yt-dlp built-ins) and out-of-scope work (post-download tag editing, external metadata lookups, library organization), so agent-assisted contributions can check their plans against it before writing code. Follows the decisions on #1025, #1026/#1027, #1028, #1031. Co-Authored-By: Claude Fable 5 --- AGENTS.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 4a27f4d..39e5c65 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,53 @@ # Agent Guidelines +## Project scope β€” read this before planning a feature + +MeTube's contract is: give it a URL, it runs yt-dlp well, and correct files appear. +The maintainer holds a deliberate line on what belongs inside that contract, and PRs +on the wrong side of it are declined **regardless of code quality**. Check your plan +against this line before writing any code. + +**In scope β€” improving the write itself:** + +- Features that make the file yt-dlp writes at download time come out more correct, + using only data the extractor already provides (e.g. filling a missing album-artist + tag from the extractor's own metadata). +- Surfacing functionality yt-dlp itself owns and maintains as first-class UI options + (e.g. a SponsorBlock toggle that just passes yt-dlp postprocessor params). +- Download queue, subscriptions, output templates, and UI improvements to the + download workflow. + +**Out of scope β€” managing files after they exist:** + +- Tag editors, metadata dialogs, or any workflow that rewrites files after the + download has finished. This holds even for slimmed-down versions. +- Lookups against external metadata services (iTunes, Deezer, MusicBrainz, etc.). + More broadly: any new dependency on a third-party API, or new network egress from + self-hosted instances, beyond what yt-dlp itself performs. +- Library organization: moving/renaming existing files into Artist/Album layouts, + watch-folder processing, and similar media-manager features. Dedicated tools + (beets, MusicBrainz Picard, Lidarr) do this properly; the README points users + to them. + +**Corollaries that shape borderline PRs:** + +- Site-specific intelligence (parsing playlist-ID prefixes, URL path conventions, + and other platform internals) is extractor work and belongs upstream in yt-dlp, + not re-implemented here β€” it silently breaks when the platform changes and + MeTube would own the breakage. +- Prefer enriching yt-dlp's info dict and letting its existing pipeline + (FFmpegMetadata etc.) do the writing, over adding custom per-format tag-writing + code to MeTube. +- Supplemental processing must never fail a download that otherwise succeeded: + warn and continue, don't raise. +- Keep feature scope minimal on first submission. A hardcoded sensible default + beats a configuration surface; follow-ups can add options when users actually + ask. PRs that bundle several "reasonable next steps" invite rejection of the + whole. + +If a feature idea fails this test, the accepted alternative is usually a README +section documenting how to pair MeTube with the right dedicated tool. + ## README.md size constraint The README.md is synced to Docker Hub, which has a **25,000 character limit**. From 1eafecd1ccb3a3e6027ad44797cac3847c943c59 Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 17 Jul 2026 15:14:33 +0300 Subject: [PATCH 87/88] docs: add test gotchas, option checklist, and security invariants to AGENTS.md - Test-running traps: repo-root cwd requirement, frontend-build ordering, the yt_dlp stub quirk in test_ytdl_utils.py - Note that master is continuously released - Checklist for adding a per-download option (the split_by_chapters pattern, including the commonly missed pieces) - Security invariants: SSRF guard for URLs, path helpers for anything derived from untrusted metadata - Conventions: OnPush/markForCheck, compact persisted state, yt-dlp postprocessor list ordering Co-Authored-By: Claude Fable 5 --- AGENTS.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 39e5c65..6ec2596 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -78,6 +78,24 @@ uv run pytest app/tests/ All of these run in CI (`.github/workflows/main.yml`) on every push to master and must pass. +Gotchas: + +- Backend tests must run **from the repo root**: `main.py` resolves the static-assets + path relative to the cwd, and several test modules import `main`. Running from + `app/` makes five test modules fail to import. +- The frontend must be **built before** running backend tests (same reason β€” the + assets at `ui/dist/metube/browser` must exist). The command order above is + load-bearing. +- `app/tests/test_ytdl_utils.py` stubs `yt_dlp` at import time. Run standalone, + two tests fail with `AttributeError: does not have the + attribute 'YoutubeDL'`; under the full suite the real module is imported first + and they pass. This is a known quirk, not a bug to fix in the code under test. + +Every non-markdown push to master builds multi-arch Docker images and cuts a dated +release the same day. **Master is continuously released** β€” a PR must be +release-ready exactly as merged; there is no stabilization window for follow-up +fixes. + ## Code style Follow `.editorconfig`: @@ -104,5 +122,36 @@ ui/src/app/ β€” Angular standalone components (no NgModules) - Backend configuration lives in the `Config` class in `app/main.py` with env-var defaults in `_DEFAULTS`. New env vars go there. - Real-time communication uses Socket.IO events, not REST polling. - Frontend uses standalone Angular components with `inject()` for DI, RxJS Subjects for state, and `takeUntilDestroyed()` for cleanup. +- Frontend components use OnPush change detection: subscribe callbacks must call `cdr.markForCheck()`. - State is persisted as JSON files via `AtomicJsonStore` in `app/state_store.py`. +- Persisted state stays compact: the completed queue deliberately drops bulky entry data (see `_compact_persisted_entry` in `app/ytdl.py`). Don't expand what gets persisted without discussion. +- Custom yt-dlp postprocessors added to `ytdl_params['postprocessors']` run in **list order** within a stage. When combining postprocessors, mirror the ordering the yt-dlp CLI would produce (e.g. sponsor-segment removal before chapter splitting). - No pre-commit hooks β€” linting and tests are enforced in CI only. + +## Checklist: adding a per-download option + +New options on the download form (the `split_by_chapters` pattern) need **all** of +these pieces β€” the last three are the ones commonly missed: + +1. `parse_download_options` in `app/main.py`. +2. A field on `DownloadInfo` in `app/ytdl.py`. +3. A `hasattr` backfill in `DownloadInfo.__setstate__` for old persisted records. +4. The safe-deserialization field list in `app/ytdl.py`. +5. UI form control + cookie persistence in `ui/src/app/app.ts` / `app.html`, and + the payload in `downloads.service.ts` (plus its spec). +6. The redownload path in `app.ts`, so retries carry the option. +7. If the option makes sense for unattended downloads: threading through + `app/subscriptions.py` (`SubscriptionInfo` field, serializer, add/update + routes, the enqueue call) β€” or a note in the PR that it's deliberately + direct-downloads-only. + +## Security invariants + +User input and extractor-provided metadata (titles, playlist names, URLs) are +untrusted. Use the existing guards instead of hand-rolling: + +- User-submitted URLs go through the SSRF guard (see `test_url_guard.py` for the + expected behavior). +- Anything that becomes a filesystem path goes through `_is_within_directory` and + `_sanitize_path_component` in `app/ytdl.py` β€” including values that arrive via + yt-dlp metadata, which sites can influence. From 96f5ffe34a9d9a130164d99a377a0a7647d742db Mon Sep 17 00:00:00 2001 From: Alex Shnitman Date: Fri, 17 Jul 2026 15:39:20 +0300 Subject: [PATCH 88/88] docs: restructure README around the wiki as companion documentation - Move bookmarklet code and Apache/Caddy/swag reverse-proxy configs to the wiki; link them - Group browser extensions, bookmarklets, iOS Shortcut, and Raycast under one 'Sending links to MeTube' section with shared CORS/HTTPS prerequisites stated once - Move Runtime & Permissions (PUID/PGID/UMASK) to the top of the env-var reference; slim the CORS_ALLOWED_ORIGINS entry - Link the new Subscriptions guide and Troubleshooting FAQ wiki pages - State the project scope line in 'Submitting feature requests' - Docker Compose naming, multi-arch note, trailing whitespace 24,830 -> 21,571 chars against the 25k Docker Hub limit. Co-Authored-By: Claude Fable 5 --- README.md | 132 ++++++++++++++---------------------------------------- 1 file changed, 34 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 0f84924..a73b8b4 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,12 @@ ![Build Status](https://github.com/alexta69/metube/actions/workflows/main.yml/badge.svg) ![Docker Pulls](https://img.shields.io/docker/pulls/alexta69/metube.svg) -MeTube is a self-hosted web UI for `yt-dlp`, for downloading media from YouTube and [dozens of other sites](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md). +MeTube is a self-hosted web UI for `yt-dlp`, for downloading media from YouTube and [dozens of other sites](https://github.com/yt-dlp/yt-dlp/blob/master/supportedsites.md). Docker images are multi-arch (amd64/arm64). Key capabilities: * Download videos, audio, captions, and thumbnails from a browser UI. * Download playlists and channels, with configurable output and download options. -* Subscribe to channels and playlists, periodically check for new items, and queue new uploads automatically. +* [Subscribe](https://github.com/alexta69/metube/wiki/Subscriptions) to channels and playlists, periodically check for new items, and queue new uploads automatically. ![screenshot1](https://github.com/alexta69/metube/raw/master/screenshot.gif) @@ -18,7 +18,7 @@ Key capabilities: docker run -d -p 8081:8081 -v /path/to/downloads:/downloads ghcr.io/alexta69/metube ``` -## 🐳 Run using docker-compose +## 🐳 Run using Docker Compose ```yaml services: @@ -34,11 +34,20 @@ services: ## βš™οΈ Configuration via environment variables -Certain values can be set via environment variables, using the `-e` parameter on the docker command line, or the `environment:` section in docker-compose. +Certain values can be set via environment variables, using the `-e` parameter on the docker command line, or the `environment:` section in Docker Compose. + +### 🏠 Runtime & Permissions + +* __PUID__: User under which MeTube will run. Defaults to `1000` (legacy `UID` also supported). +* __PGID__: Group under which MeTube will run. Defaults to `1000` (legacy `GID` also supported). +* __UMASK__: Umask value used by MeTube. Defaults to `022`. +* __DEFAULT_THEME__: Default theme to use for the UI, can be set to `light`, `dark`, or `auto`. Defaults to `auto`. +* __LOGLEVEL__: Log level, can be set to `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`, or `NONE`. Defaults to `INFO`. +* __ENABLE_ACCESSLOG__: Whether to enable access log. Defaults to `false`. ### ⬇️ Download Behavior -* __MAX_CONCURRENT_DOWNLOADS__: Maximum number of simultaneous downloads allowed. For example, if set to `5`, then at most five downloads will run concurrently, and any additional downloads will wait until one of the active downloads completes. Defaults to `3`. +* __MAX_CONCURRENT_DOWNLOADS__: Maximum number of simultaneous downloads allowed. For example, if set to `5`, then at most five downloads will run concurrently, and any additional downloads will wait until one of the active downloads completes. Defaults to `3`. * __DELETE_FILE_ON_TRASHCAN__: if `true`, downloaded files are deleted on the server, when they are trashed from the "Completed" section of the UI. Defaults to `false`. * __DEFAULT_OPTION_PLAYLIST_ITEM_LIMIT__: Maximum number of playlist items that can be downloaded. Defaults to `0` (no limit). * __SUBSCRIPTION_DEFAULT_CHECK_INTERVAL__: Default minutes between automatic checks for each subscription. Defaults to `60`. @@ -83,18 +92,9 @@ Certain values can be set via environment variables, using the `-e` parameter on * __HTTPS__: Use `https` instead of `http` (__CERTFILE__ and __KEYFILE__ required). Defaults to `false`. * __CERTFILE__: HTTPS certificate file path. * __KEYFILE__: HTTPS key file path. -* __CORS_ALLOWED_ORIGINS__: Comma-separated list of origins permitted to make cross-origin requests to the MeTube API. When unset or empty, all cross-origin requests are denied. Set to `*` to allow all origins. This must be configured for [browser extensions](#-browser-extensions), [bookmarklets](#-bookmarklet), and any other browser-based tools that contact MeTube from a different origin. For browser extensions use `*` (see below); for bookmarklets you can list specific sites, e.g. `https://www.youtube.com,https://www.vimeo.com`. +* __CORS_ALLOWED_ORIGINS__: Comma-separated list of origins permitted to make cross-origin requests to the MeTube API; `*` allows all. When unset or empty, all cross-origin requests are denied. Required for browser extensions and bookmarklets β€” see [Sending links to MeTube](#-sending-links-to-metube). * __ROBOTS_TXT__: A path to a `robots.txt` file mounted in the container. -### 🏠 Basic Setup - -* __PUID__: User under which MeTube will run. Defaults to `1000` (legacy `UID` also supported). -* __PGID__: Group under which MeTube will run. Defaults to `1000` (legacy `GID` also supported). -* __UMASK__: Umask value used by MeTube. Defaults to `022`. -* __DEFAULT_THEME__: Default theme to use for the UI, can be set to `light`, `dark`, or `auto`. Defaults to `auto`. -* __LOGLEVEL__: Log level, can be set to `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`, or `NONE`. Defaults to `INFO`. -* __ENABLE_ACCESSLOG__: Whether to enable access log. Defaults to `false`. - ## πŸŽ›οΈ Configuring yt-dlp options MeTube lets you customize how [yt-dlp](https://github.com/yt-dlp/yt-dlp) behaves at three levels, from broadest to most specific: @@ -229,6 +229,20 @@ In case you need to use your browser's cookies with MeTube, for example to downl * After upload, the cookie indicator should show as active. * Use **Delete Cookies** in the same section to remove uploaded cookies. +## πŸ”— Sending links to MeTube + +Several integrations let you send URLs to MeTube from wherever you are, instead of pasting them into the UI. The browser-based ones make cross-origin requests, so they require `CORS_ALLOWED_ORIGINS` to be set; and if you're on an HTTPS page, your MeTube instance must be served over HTTPS too (with `HTTPS=true` or behind an HTTPS reverse proxy β€” see below). + +__Browser extensions__ allow right-clicking videos and sending them directly to MeTube. Since extensions request from their own origin, set `CORS_ALLOWED_ORIGINS=*`. +* __Chrome:__ contributed by [Rpsl](https://github.com/rpsl) β€” install from the [Chrome Webstore](https://chrome.google.com/webstore/detail/metube-downloader/fbmkmdnlhacefjljljlbhkodfmfkijdh) or [from sources](https://github.com/Rpsl/metube-browser-extension). +* __Firefox:__ contributed by [nanocortex](https://github.com/nanocortex) β€” install from [Firefox Addons](https://addons.mozilla.org/en-US/firefox/addon/metube-downloader) or get sources [here](https://github.com/nanocortex/metube-firefox-addon). + +__Bookmarklets__ send the currently open page to MeTube with one click. Add the origins of the sites where you use them to `CORS_ALLOWED_ORIGINS`, e.g. `https://www.youtube.com,https://www.vimeo.com`. The code (Chrome and Firefox variants, contributed by [kushfest](https://github.com/kushfest) and [shoonya75](https://github.com/shoonya75)) is in the [Bookmarklets wiki page](https://github.com/alexta69/metube/wiki/Bookmarklets). + +__iOS Shortcut:__ [rithask](https://github.com/rithask) created an [iOS shortcut](https://www.icloud.com/shortcuts/66627a9f334c467baabdb2769763a1a6) for sending URLs to MeTube from Safari's share menu; it prompts for your instance address on first use. + +__Raycast:__ [dotvhs](https://github.com/dotvhs) has created an [extension for Raycast](https://www.raycast.com/dot/metube) for adding videos to MeTube directly from Raycast. + ## 🎡 Pairing with a music tagger MeTube deliberately stops once the file is written β€” tagging and library organization belong to dedicated tools. Point one at your audio download folder (`AUDIO_DOWNLOAD_DIR`): @@ -237,48 +251,6 @@ MeTube deliberately stops once the file is written β€” tagging and library organ * [MusicBrainz Picard](https://picard.musicbrainz.org) β€” GUI tagger with acoustic fingerprinting. * [Lidarr](https://lidarr.audio) β€” full music library manager; add the folder as an import path. -## πŸ”Œ Browser extensions - -Browser extensions allow right-clicking videos and sending them directly to MeTube. If you're on an HTTPS page, your MeTube instance must be behind an HTTPS reverse proxy (see below) for extensions to work. - -Since browser extensions make requests from their own origin (`chrome-extension://...` or `moz-extension://...`), you must set `CORS_ALLOWED_ORIGINS=*` for them to work. - -__Chrome:__ contributed by [Rpsl](https://github.com/rpsl). You can install it from [Google Chrome Webstore](https://chrome.google.com/webstore/detail/metube-downloader/fbmkmdnlhacefjljljlbhkodfmfkijdh) or use developer mode and install [from sources](https://github.com/Rpsl/metube-browser-extension). - -__Firefox:__ contributed by [nanocortex](https://github.com/nanocortex). You can install it from [Firefox Addons](https://addons.mozilla.org/en-US/firefox/addon/metube-downloader) or get sources from [here](https://github.com/nanocortex/metube-firefox-addon). - -## πŸ“± iOS Shortcut - -[rithask](https://github.com/rithask) created an iOS shortcut to send URLs to MeTube from Safari. Enter the MeTube instance address when prompted which will be saved for later use. You can run the shortcut from Safari’s share menu. The shortcut can be downloaded from [this iCloud link](https://www.icloud.com/shortcuts/66627a9f334c467baabdb2769763a1a6). - -## πŸ”– Bookmarklet - -[kushfest](https://github.com/kushfest) has created a Chrome bookmarklet for sending the currently open webpage to MeTube. Please note that if you're on an HTTPS page, your MeTube instance must be configured with `HTTPS` as `true` in the environment, or be behind an HTTPS reverse proxy (see below) for the bookmarklet to work. - -Since bookmarklets run in the context of the current page (e.g. youtube.com), the requests they make to MeTube are cross-origin. You must add the origins of sites where you use the bookmarklet to the __CORS_ALLOWED_ORIGINS__ environment variable, otherwise the browser will block the requests. For example, to use the bookmarklet on YouTube and Vimeo: `CORS_ALLOWED_ORIGINS=https://www.youtube.com,https://www.vimeo.com`. - -GitHub doesn't allow embedding JavaScript as a link, so the bookmarklet has to be created manually by copying the following code to a new bookmark you create on your bookmarks bar. Change the hostname in the URL below to point to your MeTube instance. - -```javascript -javascript:!function(){xhr=new XMLHttpRequest();xhr.open("POST","https://metube.domain.com/add");xhr.withCredentials=true;xhr.send(JSON.stringify({"url":document.location.href,"quality":"best"}));xhr.onload=function(){if(xhr.status==200){alert("Sent to metube!")}else{alert("Send to metube failed. Check the javascript console for clues.")}}}(); -``` - -[shoonya75](https://github.com/shoonya75) has contributed a Firefox version: - -```javascript -javascript:(function(){xhr=new XMLHttpRequest();xhr.open("POST","https://metube.domain.com/add");xhr.send(JSON.stringify({"url":document.location.href,"quality":"best"}));xhr.onload=function(){if(xhr.status==200){alert("Sent to metube!")}else{alert("Send to metube failed. Check the javascript console for clues.")}}})(); -``` - -The above bookmarklets use `alert()` for notifications. This variant shows a toast instead (Chrome β€” for Firefox, replace the `!function(){...}()` wrapper with `(function(){...})()`): - -```javascript -javascript:!function(){function notify(msg) {var sc = document.scrollingElement.scrollTop; var text = document.createElement('span');text.innerHTML=msg;var ts = text.style;ts.all = 'revert';ts.color = '#000';ts.fontFamily = 'Verdana, sans-serif';ts.fontSize = '15px';ts.backgroundColor = 'white';ts.padding = '15px';ts.border = '1px solid gainsboro';ts.boxShadow = '3px 3px 10px';ts.zIndex = '100';document.body.appendChild(text);ts.position = 'absolute'; ts.top = 50 + sc + 'px'; ts.left = (window.innerWidth / 2)-(text.offsetWidth / 2) + 'px'; setTimeout(function () { text.style.visibility = "hidden"; }, 1500);}xhr=new XMLHttpRequest();xhr.open("POST","https://metube.domain.com/add");xhr.send(JSON.stringify({"url":document.location.href,"quality":"best"}));xhr.onload=function() { if(xhr.status==200){notify("Sent to metube!")}else {notify("Send to metube failed. Check the javascript console for clues.")}}}(); -``` - -## ⚑ Raycast extension - -[dotvhs](https://github.com/dotvhs) has created an [extension for Raycast](https://www.raycast.com/dot/metube) for adding videos to MeTube directly from Raycast. - ## πŸ”’ HTTPS support, and running behind a reverse proxy It's possible to configure MeTube to listen in HTTPS mode. `docker-compose` example: @@ -301,11 +273,7 @@ services: - KEYFILE=/ssl/key.pem ``` -MeTube can also run behind a reverse proxy for HTTPS termination or authentication. When serving under a subdirectory, set `URL_PREFIX` accordingly. - -The [linuxserver/swag](https://docs.linuxserver.io/general/swag) image includes ready-made snippets for MeTube in [subfolder](https://github.com/linuxserver/reverse-proxy-confs/blob/master/metube.subfolder.conf.sample) and [subdomain](https://github.com/linuxserver/reverse-proxy-confs/blob/master/metube.subdomain.conf.sample) modes, plus Authelia for authentication. - -### 🌐 NGINX +MeTube can also run behind a reverse proxy for HTTPS termination or authentication. When serving under a subdirectory, set `URL_PREFIX` accordingly. MeTube uses WebSocket for real-time updates, so the proxy must pass the `Upgrade`/`Connection` headers, as in this NGINX example: ```nginx location /metube/ { @@ -317,41 +285,7 @@ location /metube/ { } ``` -Note: the extra `proxy_set_header` directives are there to make WebSocket work. - -### 🌐 Apache - -Contributed by [PIE-yt](https://github.com/PIE-yt). Source [here](https://gist.github.com/PIE-yt/29e7116588379032427f5bd446b2cac4). - -```apache -# For putting in your Apache sites site.conf -# Serves MeTube under a /metube/ subdir (http://yourdomain.com/metube/) - - ProxyPass http://localhost:8081/ retry=0 timeout=30 - ProxyPassReverse http://localhost:8081/ - - - - RewriteEngine On - RewriteCond %{QUERY_STRING} transport=websocket [NC] - RewriteRule /(.*) ws://localhost:8081/socket.io/$1 [P,L] - ProxyPass http://localhost:8081/socket.io retry=0 timeout=30 - ProxyPassReverse http://localhost:8081/socket.io - -``` - -### 🌐 Caddy - -The following example Caddyfile gets a reverse proxy going behind [caddy](https://caddyserver.com). - -```caddyfile -example.com { - route /metube/* { - uri strip_prefix metube - reverse_proxy metube:8081 - } -} -``` +Apache, Caddy, and [linuxserver/swag](https://docs.linuxserver.io/general/swag) (with Authelia) examples are in the [Reverse proxy configurations wiki page](https://github.com/alexta69/metube/wiki/Reverse-proxy-configurations). ## πŸ”„ Updating yt-dlp @@ -366,9 +300,11 @@ docker exec -ti metube sh cd /downloads ``` +Common issues and their fixes are collected in the [Troubleshooting FAQ](https://github.com/alexta69/metube/wiki/Troubleshooting-FAQ) on the wiki. + ## πŸ’‘ Submitting feature requests -MeTube development relies on community contributions. If you need additional features, please submit a PR. Create an issue first to discuss the implementation β€” some PRs may not be accepted to reduce bloat. Feature requests without an accompanying PR are unlikely to be fulfilled. +MeTube development relies on community contributions. If you need additional features, please submit a PR. Create an issue first to discuss the implementation before writing code β€” MeTube's scope is deliberately narrow: it downloads well and stops once the file is written. Features that improve the download itself are welcome; post-download file management (tag editing, metadata lookups, library organization) is out of scope regardless of implementation quality β€” see [AGENTS.md](AGENTS.md) for the full policy. Feature requests without an accompanying PR are unlikely to be fulfilled. ## πŸ› οΈ Building and running locally