From d9c8ba4d0bd442d0056c611da9b37dbfafaa03b0 Mon Sep 17 00:00:00 2001 From: 4gray <4gray@users.noreply.github.com> Date: Sun, 13 Sep 2026 17:25:53 +0200 Subject: [PATCH 01/43] fix(epg): keep the import panel free of horizontal scrolling (#1599) --- .changes/epg-import-panel-polish.md | 6 ++ apps/electron-backend-e2e/src/epg.e2e.ts | 19 +++++ apps/web/src/assets/i18n/ar.json | 4 + apps/web/src/assets/i18n/ary.json | 4 + apps/web/src/assets/i18n/by.json | 4 + apps/web/src/assets/i18n/de.json | 4 + apps/web/src/assets/i18n/el.json | 4 + apps/web/src/assets/i18n/en.json | 4 + apps/web/src/assets/i18n/es.json | 4 + apps/web/src/assets/i18n/fr.json | 4 + apps/web/src/assets/i18n/hu.json | 4 + apps/web/src/assets/i18n/it.json | 4 + apps/web/src/assets/i18n/ja.json | 4 + apps/web/src/assets/i18n/ko.json | 4 + apps/web/src/assets/i18n/nl.json | 4 + apps/web/src/assets/i18n/pl.json | 4 + apps/web/src/assets/i18n/pt.json | 4 + apps/web/src/assets/i18n/ru.json | 4 + apps/web/src/assets/i18n/tr.json | 4 + apps/web/src/assets/i18n/zh.json | 4 + apps/web/src/assets/i18n/zhtw.json | 4 + .../epg-import-display-url.spec.ts | 53 +++++++++++++ .../epg-import-display-url.ts | 48 ++++++++++++ .../epg-progress-panel.component.html | 28 +++++-- .../epg-progress-panel.component.scss | 74 +++++++++++-------- .../epg-progress-panel.component.ts | 16 +++- 26 files changed, 280 insertions(+), 40 deletions(-) create mode 100644 .changes/epg-import-panel-polish.md create mode 100644 libs/ui/epg/src/lib/epg-progress-panel/epg-import-display-url.spec.ts create mode 100644 libs/ui/epg/src/lib/epg-progress-panel/epg-import-display-url.ts diff --git a/.changes/epg-import-panel-polish.md b/.changes/epg-import-panel-polish.md new file mode 100644 index 000000000..7e102563d --- /dev/null +++ b/.changes/epg-import-panel-polish.md @@ -0,0 +1,6 @@ +--- +type: fix +area: epg +--- + +The EPG import panel no longer shows a horizontal scrollbar while sources download. Source names now read as host/file instead of running the two together, channel and programme counts use your locale's digit grouping, and the minimize button and summary are translated. diff --git a/apps/electron-backend-e2e/src/epg.e2e.ts b/apps/electron-backend-e2e/src/epg.e2e.ts index 6fa53db09..aacfd7ccb 100644 --- a/apps/electron-backend-e2e/src/epg.e2e.ts +++ b/apps/electron-backend-e2e/src/epg.e2e.ts @@ -334,6 +334,25 @@ test.describe('Electron EPG', () => { .first() ).toBeVisible(); + // The row must fit the 300px panel: Material icon buttons carry a + // 48px touch target that used to poke past the row padding and + // turn into a horizontal scrollbar. + const importsList = app.mainWindow.locator( + '.epg-progress-panel .imports-list' + ); + await expect + .poll(() => + importsList.evaluate( + (element) => element.scrollWidth - element.clientWidth + ) + ) + .toBe(0); + await expect( + app.mainWindow.locator('.epg-progress-panel .item-url') + ).toHaveText( + `${new URL(epgServer.resourceUrl).hostname}/guide.xml` + ); + await saveSettings(app.mainWindow); await app.mainWindow diff --git a/apps/web/src/assets/i18n/ar.json b/apps/web/src/assets/i18n/ar.json index c6c699add..bfce7ab2c 100644 --- a/apps/web/src/assets/i18n/ar.json +++ b/apps/web/src/assets/i18n/ar.json @@ -857,6 +857,10 @@ "PROGRAMS": "برامج", "LOADING": "جارٍ التحميل", "QUEUED": "في قائمة الانتظار", + "IMPORT_PANEL_MINIMIZE": "تصغير لوحة استيراد دليل البرامج", + "IMPORT_PANEL_EXPAND": "توسيع لوحة استيراد دليل البرامج", + "IMPORT_SUMMARY_LOADING": "{{count}} قيد التحميل", + "IMPORT_SUMMARY_QUEUED": "{{count}} في قائمة الانتظار", "TOTAL_CHANNELS": "إجمالي القنوات", "TOTAL_PROGRAMS": "إجمالي البرامج", "UP_TO_DATE": "EPG محدث، لا حاجة للمزامنة", diff --git a/apps/web/src/assets/i18n/ary.json b/apps/web/src/assets/i18n/ary.json index 3ea8f4604..034be9e4c 100644 --- a/apps/web/src/assets/i18n/ary.json +++ b/apps/web/src/assets/i18n/ary.json @@ -857,6 +857,10 @@ "PROGRAMS": "برامج", "LOADING": "جاري التشريج", "QUEUED": "في الطابور", + "IMPORT_PANEL_MINIMIZE": "صغّر لوحة استيراد دليل البرامج", + "IMPORT_PANEL_EXPAND": "كبّر لوحة استيراد دليل البرامج", + "IMPORT_SUMMARY_LOADING": "{{count}} كيتحمّل", + "IMPORT_SUMMARY_QUEUED": "{{count}} فالصف", "TOTAL_CHANNELS": "مجموع القنوات", "TOTAL_PROGRAMS": "مجموع البرامج", "UP_TO_DATE": "EPG محدث، ما خاصش مزامنة", diff --git a/apps/web/src/assets/i18n/by.json b/apps/web/src/assets/i18n/by.json index c15c7d871..fd7656417 100644 --- a/apps/web/src/assets/i18n/by.json +++ b/apps/web/src/assets/i18n/by.json @@ -857,6 +857,10 @@ "PROGRAMS": "перадач", "LOADING": "Загрузка", "QUEUED": "У чарзе", + "IMPORT_PANEL_MINIMIZE": "Згарнуць панэль імпарту EPG", + "IMPORT_PANEL_EXPAND": "Разгарнуць панэль імпарту EPG", + "IMPORT_SUMMARY_LOADING": "Загружаецца: {{count}}", + "IMPORT_SUMMARY_QUEUED": "у чарзе: {{count}}", "TOTAL_CHANNELS": "Усяго каналаў", "TOTAL_PROGRAMS": "Усяго перадач", "UP_TO_DATE": "EPG актуальны, сінхранізацыя не патрэбна", diff --git a/apps/web/src/assets/i18n/de.json b/apps/web/src/assets/i18n/de.json index 418a73e69..5c196d68e 100644 --- a/apps/web/src/assets/i18n/de.json +++ b/apps/web/src/assets/i18n/de.json @@ -857,6 +857,10 @@ "PROGRAMS": "Programme", "LOADING": "Wird geladen", "QUEUED": "In Warteschlange", + "IMPORT_PANEL_MINIMIZE": "EPG-Importpanel minimieren", + "IMPORT_PANEL_EXPAND": "EPG-Importpanel ausklappen", + "IMPORT_SUMMARY_LOADING": "{{count}} wird geladen", + "IMPORT_SUMMARY_QUEUED": "{{count}} in Warteschlange", "TOTAL_CHANNELS": "Sender insgesamt", "TOTAL_PROGRAMS": "Programme insgesamt", "UP_TO_DATE": "EPG ist aktuell, keine Synchronisation nötig", diff --git a/apps/web/src/assets/i18n/el.json b/apps/web/src/assets/i18n/el.json index ad8d6501d..5e88b86d2 100644 --- a/apps/web/src/assets/i18n/el.json +++ b/apps/web/src/assets/i18n/el.json @@ -857,6 +857,10 @@ "PROGRAMS": "προγράμματα", "LOADING": "Φόρτωση", "QUEUED": "Σε ουρά", + "IMPORT_PANEL_MINIMIZE": "Ελαχιστοποίηση πίνακα εισαγωγής EPG", + "IMPORT_PANEL_EXPAND": "Ανάπτυξη πίνακα εισαγωγής EPG", + "IMPORT_SUMMARY_LOADING": "{{count}} σε φόρτωση", + "IMPORT_SUMMARY_QUEUED": "{{count}} σε αναμονή", "TOTAL_CHANNELS": "Σύνολο καναλιών", "TOTAL_PROGRAMS": "Σύνολο προγραμμάτων", "UP_TO_DATE": "Το EPG είναι ενημερωμένο, δεν απαιτείται συγχρονισμός", diff --git a/apps/web/src/assets/i18n/en.json b/apps/web/src/assets/i18n/en.json index e95bb7c70..fd207686b 100644 --- a/apps/web/src/assets/i18n/en.json +++ b/apps/web/src/assets/i18n/en.json @@ -857,6 +857,10 @@ "PROGRAMS": "programs", "LOADING": "Loading", "QUEUED": "Queued", + "IMPORT_PANEL_MINIMIZE": "Minimize EPG import panel", + "IMPORT_PANEL_EXPAND": "Expand EPG import panel", + "IMPORT_SUMMARY_LOADING": "{{count}} loading", + "IMPORT_SUMMARY_QUEUED": "{{count}} queued", "TOTAL_CHANNELS": "Total channels", "TOTAL_PROGRAMS": "Total programs", "UP_TO_DATE": "EPG is up-to-date, no sync needed", diff --git a/apps/web/src/assets/i18n/es.json b/apps/web/src/assets/i18n/es.json index 028e86667..307268b04 100644 --- a/apps/web/src/assets/i18n/es.json +++ b/apps/web/src/assets/i18n/es.json @@ -857,6 +857,10 @@ "PROGRAMS": "programas", "LOADING": "Cargando", "QUEUED": "En cola", + "IMPORT_PANEL_MINIMIZE": "Minimizar el panel de importación de EPG", + "IMPORT_PANEL_EXPAND": "Expandir el panel de importación de EPG", + "IMPORT_SUMMARY_LOADING": "{{count}} cargando", + "IMPORT_SUMMARY_QUEUED": "{{count}} en cola", "TOTAL_CHANNELS": "Total de canales", "TOTAL_PROGRAMS": "Total de programas", "UP_TO_DATE": "EPG actualizada, no se necesita sincronización", diff --git a/apps/web/src/assets/i18n/fr.json b/apps/web/src/assets/i18n/fr.json index e9975c103..2732734d4 100644 --- a/apps/web/src/assets/i18n/fr.json +++ b/apps/web/src/assets/i18n/fr.json @@ -857,6 +857,10 @@ "PROGRAMS": "programmes", "LOADING": "Chargement", "QUEUED": "En file d'attente", + "IMPORT_PANEL_MINIMIZE": "Réduire le panneau d’import EPG", + "IMPORT_PANEL_EXPAND": "Agrandir le panneau d’import EPG", + "IMPORT_SUMMARY_LOADING": "{{count}} en cours", + "IMPORT_SUMMARY_QUEUED": "{{count}} en attente", "TOTAL_CHANNELS": "Total des chaînes", "TOTAL_PROGRAMS": "Total des programmes", "UP_TO_DATE": "L'EPG est à jour, aucune synchronisation nécessaire", diff --git a/apps/web/src/assets/i18n/hu.json b/apps/web/src/assets/i18n/hu.json index 9b76312fe..a85807d0a 100644 --- a/apps/web/src/assets/i18n/hu.json +++ b/apps/web/src/assets/i18n/hu.json @@ -857,6 +857,10 @@ "PROGRAMS": "műsor", "LOADING": "Betöltés", "QUEUED": "Várólistán", + "IMPORT_PANEL_MINIMIZE": "EPG-importálási panel összecsukása", + "IMPORT_PANEL_EXPAND": "EPG-importálási panel kibontása", + "IMPORT_SUMMARY_LOADING": "{{count}} betöltés alatt", + "IMPORT_SUMMARY_QUEUED": "{{count}} várakozik", "TOTAL_CHANNELS": "Csatornák összesen", "TOTAL_PROGRAMS": "Műsorok összesen", "UP_TO_DATE": "Az EPG naprakész, nincs szükség szinkronizálásra", diff --git a/apps/web/src/assets/i18n/it.json b/apps/web/src/assets/i18n/it.json index 543494654..fc277a738 100644 --- a/apps/web/src/assets/i18n/it.json +++ b/apps/web/src/assets/i18n/it.json @@ -857,6 +857,10 @@ "PROGRAMS": "programmi", "LOADING": "Caricamento", "QUEUED": "In coda", + "IMPORT_PANEL_MINIMIZE": "Riduci il pannello di importazione EPG", + "IMPORT_PANEL_EXPAND": "Espandi il pannello di importazione EPG", + "IMPORT_SUMMARY_LOADING": "{{count}} in caricamento", + "IMPORT_SUMMARY_QUEUED": "{{count}} in coda", "TOTAL_CHANNELS": "Totale canali", "TOTAL_PROGRAMS": "Totale programmi", "UP_TO_DATE": "L'EPG è aggiornato, nessuna sincronizzazione necessaria", diff --git a/apps/web/src/assets/i18n/ja.json b/apps/web/src/assets/i18n/ja.json index 42d98c2f9..b25ea4953 100644 --- a/apps/web/src/assets/i18n/ja.json +++ b/apps/web/src/assets/i18n/ja.json @@ -857,6 +857,10 @@ "PROGRAMS": "番組", "LOADING": "読み込み中", "QUEUED": "待機中", + "IMPORT_PANEL_MINIMIZE": "EPG インポートパネルを最小化", + "IMPORT_PANEL_EXPAND": "EPG インポートパネルを展開", + "IMPORT_SUMMARY_LOADING": "{{count}} 件読み込み中", + "IMPORT_SUMMARY_QUEUED": "{{count}} 件待機中", "TOTAL_CHANNELS": "総チャンネル数", "TOTAL_PROGRAMS": "総番組数", "UP_TO_DATE": "EPGは最新です。同期は不要です", diff --git a/apps/web/src/assets/i18n/ko.json b/apps/web/src/assets/i18n/ko.json index d3d472070..fd330bd2c 100644 --- a/apps/web/src/assets/i18n/ko.json +++ b/apps/web/src/assets/i18n/ko.json @@ -857,6 +857,10 @@ "PROGRAMS": "프로그램", "LOADING": "불러오는 중", "QUEUED": "대기 중", + "IMPORT_PANEL_MINIMIZE": "EPG 가져오기 패널 최소화", + "IMPORT_PANEL_EXPAND": "EPG 가져오기 패널 펼치기", + "IMPORT_SUMMARY_LOADING": "{{count}}개 불러오는 중", + "IMPORT_SUMMARY_QUEUED": "{{count}}개 대기 중", "TOTAL_CHANNELS": "전체 채널", "TOTAL_PROGRAMS": "전체 프로그램", "UP_TO_DATE": "EPG가 최신 상태이며 동기화가 필요하지 않습니다", diff --git a/apps/web/src/assets/i18n/nl.json b/apps/web/src/assets/i18n/nl.json index 11b4d7ef7..3f59c18d2 100644 --- a/apps/web/src/assets/i18n/nl.json +++ b/apps/web/src/assets/i18n/nl.json @@ -857,6 +857,10 @@ "PROGRAMS": "programma's", "LOADING": "Laden", "QUEUED": "In wachtrij", + "IMPORT_PANEL_MINIMIZE": "EPG-importpaneel minimaliseren", + "IMPORT_PANEL_EXPAND": "EPG-importpaneel uitvouwen", + "IMPORT_SUMMARY_LOADING": "{{count}} wordt geladen", + "IMPORT_SUMMARY_QUEUED": "{{count}} in de wachtrij", "TOTAL_CHANNELS": "Totaal aantal kanalen", "TOTAL_PROGRAMS": "Totaal aantal programma's", "UP_TO_DATE": "EPG is up-to-date, synchronisatie niet nodig", diff --git a/apps/web/src/assets/i18n/pl.json b/apps/web/src/assets/i18n/pl.json index 213a834e9..79d769a46 100644 --- a/apps/web/src/assets/i18n/pl.json +++ b/apps/web/src/assets/i18n/pl.json @@ -857,6 +857,10 @@ "PROGRAMS": "programów", "LOADING": "Ładowanie", "QUEUED": "W kolejce", + "IMPORT_PANEL_MINIMIZE": "Zwiń panel importu EPG", + "IMPORT_PANEL_EXPAND": "Rozwiń panel importu EPG", + "IMPORT_SUMMARY_LOADING": "Wczytywanie: {{count}}", + "IMPORT_SUMMARY_QUEUED": "w kolejce: {{count}}", "TOTAL_CHANNELS": "Wszystkie kanały", "TOTAL_PROGRAMS": "Wszystkie programy", "UP_TO_DATE": "EPG jest aktualne, synchronizacja nie jest potrzebna", diff --git a/apps/web/src/assets/i18n/pt.json b/apps/web/src/assets/i18n/pt.json index 6b3702806..89e2c286f 100644 --- a/apps/web/src/assets/i18n/pt.json +++ b/apps/web/src/assets/i18n/pt.json @@ -857,6 +857,10 @@ "PROGRAMS": "programas", "LOADING": "Carregando", "QUEUED": "Na fila", + "IMPORT_PANEL_MINIMIZE": "Minimizar o painel de importação do EPG", + "IMPORT_PANEL_EXPAND": "Expandir o painel de importação do EPG", + "IMPORT_SUMMARY_LOADING": "{{count}} a carregar", + "IMPORT_SUMMARY_QUEUED": "{{count}} na fila", "TOTAL_CHANNELS": "Total de canais", "TOTAL_PROGRAMS": "Total de programas", "UP_TO_DATE": "EPG está atualizado, sincronização não é necessária", diff --git a/apps/web/src/assets/i18n/ru.json b/apps/web/src/assets/i18n/ru.json index 71956f8a8..668bdd0ce 100644 --- a/apps/web/src/assets/i18n/ru.json +++ b/apps/web/src/assets/i18n/ru.json @@ -857,6 +857,10 @@ "PROGRAMS": "программ", "LOADING": "Загрузка", "QUEUED": "В очереди", + "IMPORT_PANEL_MINIMIZE": "Свернуть панель импорта EPG", + "IMPORT_PANEL_EXPAND": "Развернуть панель импорта EPG", + "IMPORT_SUMMARY_LOADING": "Загружается: {{count}}", + "IMPORT_SUMMARY_QUEUED": "в очереди: {{count}}", "TOTAL_CHANNELS": "Всего каналов", "TOTAL_PROGRAMS": "Всего программ", "UP_TO_DATE": "EPG актуален, синхронизация не требуется", diff --git a/apps/web/src/assets/i18n/tr.json b/apps/web/src/assets/i18n/tr.json index 2e2e4a90c..6ceb0db40 100644 --- a/apps/web/src/assets/i18n/tr.json +++ b/apps/web/src/assets/i18n/tr.json @@ -857,6 +857,10 @@ "PROGRAMS": "programlar", "LOADING": "Yükleniyor", "QUEUED": "Sırada", + "IMPORT_PANEL_MINIMIZE": "EPG içe aktarma panelini küçült", + "IMPORT_PANEL_EXPAND": "EPG içe aktarma panelini genişlet", + "IMPORT_SUMMARY_LOADING": "{{count}} yükleniyor", + "IMPORT_SUMMARY_QUEUED": "{{count}} sırada", "TOTAL_CHANNELS": "Toplam kanal", "TOTAL_PROGRAMS": "Toplam program", "UP_TO_DATE": "EPG güncel, senkronizasyona gerek yok", diff --git a/apps/web/src/assets/i18n/zh.json b/apps/web/src/assets/i18n/zh.json index f3dc37731..241da46f5 100644 --- a/apps/web/src/assets/i18n/zh.json +++ b/apps/web/src/assets/i18n/zh.json @@ -857,6 +857,10 @@ "PROGRAMS": "节目", "LOADING": "加载中", "QUEUED": "排队中", + "IMPORT_PANEL_MINIMIZE": "最小化 EPG 导入面板", + "IMPORT_PANEL_EXPAND": "展开 EPG 导入面板", + "IMPORT_SUMMARY_LOADING": "{{count}} 个加载中", + "IMPORT_SUMMARY_QUEUED": "{{count}} 个排队中", "TOTAL_CHANNELS": "频道总数", "TOTAL_PROGRAMS": "节目总数", "UP_TO_DATE": "EPG 已为最新,无需同步", diff --git a/apps/web/src/assets/i18n/zhtw.json b/apps/web/src/assets/i18n/zhtw.json index 2c2ea6e9a..267d161ba 100644 --- a/apps/web/src/assets/i18n/zhtw.json +++ b/apps/web/src/assets/i18n/zhtw.json @@ -857,6 +857,10 @@ "PROGRAMS": "節目", "LOADING": "載入中", "QUEUED": "佇列中", + "IMPORT_PANEL_MINIMIZE": "最小化 EPG 匯入面板", + "IMPORT_PANEL_EXPAND": "展開 EPG 匯入面板", + "IMPORT_SUMMARY_LOADING": "{{count}} 個載入中", + "IMPORT_SUMMARY_QUEUED": "{{count}} 個排隊中", "TOTAL_CHANNELS": "頻道總數", "TOTAL_PROGRAMS": "節目總數", "UP_TO_DATE": "EPG 已是最新,無需同步", diff --git a/libs/ui/epg/src/lib/epg-progress-panel/epg-import-display-url.spec.ts b/libs/ui/epg/src/lib/epg-progress-panel/epg-import-display-url.spec.ts new file mode 100644 index 000000000..465ece4f6 --- /dev/null +++ b/libs/ui/epg/src/lib/epg-progress-panel/epg-import-display-url.spec.ts @@ -0,0 +1,53 @@ +import { formatEpgImportDisplayUrl } from './epg-import-display-url'; + +describe('formatEpgImportDisplayUrl', () => { + it('separates the host from the file name', () => { + expect( + formatEpgImportDisplayUrl('http://epg.it999.ru/edem.xml.gz') + ).toBe('epg.it999.ru/edem.xml.gz'); + }); + + it('collapses intermediate path segments', () => { + expect( + formatEpgImportDisplayUrl( + 'https://iptvx.one/static/epg/epg_lite.xml.gz?token=secret' + ) + ).toBe('iptvx.one/…/epg_lite.xml.gz'); + }); + + it('decodes a percent-encoded remote file name', () => { + expect( + formatEpgImportDisplayUrl('https://example.org/guide%20v2.xml') + ).toBe('example.org/guide v2.xml'); + expect( + formatEpgImportDisplayUrl('https://example.org/epg/guide%ZZ.xml') + ).toBe('example.org/…/guide%ZZ.xml'); + }); + + it('falls back to the host when the path has no file name', () => { + expect(formatEpgImportDisplayUrl('https://example.org/')).toBe( + 'example.org' + ); + expect(formatEpgImportDisplayUrl('https://example.org')).toBe( + 'example.org' + ); + }); + + it('shows the file name of a local file URL or path', () => { + expect( + formatEpgImportDisplayUrl('file:///home/user/epg/guide%20v2.xml.gz') + ).toBe('…/guide v2.xml.gz'); + expect(formatEpgImportDisplayUrl('/home/user/epg/guide.xml')).toBe( + '…/guide.xml' + ); + expect(formatEpgImportDisplayUrl('C:\\epg\\guide.xml.gz')).toBe( + '…/guide.xml.gz' + ); + }); + + it('truncates unparsable values instead of throwing', () => { + const long = 'x'.repeat(60); + expect(formatEpgImportDisplayUrl(long)).toBe(`${'x'.repeat(40)}…`); + expect(formatEpgImportDisplayUrl('short')).toBe('short'); + }); +}); diff --git a/libs/ui/epg/src/lib/epg-progress-panel/epg-import-display-url.ts b/libs/ui/epg/src/lib/epg-progress-panel/epg-import-display-url.ts new file mode 100644 index 000000000..fb55a3f84 --- /dev/null +++ b/libs/ui/epg/src/lib/epg-progress-panel/epg-import-display-url.ts @@ -0,0 +1,48 @@ +const MAX_PLAIN_LENGTH = 40; + +/** + * Compact single-line label for an EPG source: the host, an ellipsis for + * any intermediate path, and the file name — `host/…/file.xml.gz`. A local + * file (a `file:` URL or a filesystem path) shows `…/file.xml.gz`, since the + * host slot would be empty. Query strings and credentials are never + * rendered; the full value belongs in the tooltip only. + */ +export function formatEpgImportDisplayUrl(url: string): string { + let parsed: URL | null = null; + try { + parsed = new URL(url); + } catch { + parsed = null; + } + + if (parsed && parsed.protocol !== 'file:' && parsed.hostname) { + const segments = parsed.pathname.split('/').filter(Boolean); + const file = segments[segments.length - 1]; + if (!file) { + return parsed.hostname; + } + const name = safeDecode(file); + return segments.length > 1 + ? `${parsed.hostname}/…/${name}` + : `${parsed.hostname}/${name}`; + } + + const localPath = parsed?.protocol === 'file:' ? parsed.pathname : url; + const segments = localPath.split(/[\\/]/).filter(Boolean); + const file = segments[segments.length - 1]; + if (file && segments.length > 1) { + return `…/${safeDecode(file)}`; + } + + return url.length > MAX_PLAIN_LENGTH + ? `${url.substring(0, MAX_PLAIN_LENGTH)}…` + : url; +} + +function safeDecode(value: string): string { + try { + return decodeURIComponent(value); + } catch { + return value; + } +} diff --git a/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.html b/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.html index a767ffc1d..5f8e2f8d0 100644 --- a/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.html +++ b/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.html @@ -10,9 +10,16 @@ {{ 'EPG.IMPORT_PROGRESS' | translate }} } @else { - {{ loadingCount() }} loading + {{ + 'EPG.IMPORT_SUMMARY_LOADING' + | translate: { count: loadingCount() } + }} @if (queuedCount() > 0) { - , {{ queuedCount() }} queued + , + {{ + 'EPG.IMPORT_SUMMARY_QUEUED' + | translate: { count: queuedCount() } + }} } } @@ -22,7 +29,18 @@ mat-icon-button class="minimize-btn" (click)="toggleMinimize()" - [attr.aria-label]="minimized() ? 'Expand' : 'Minimize'" + [attr.aria-label]=" + (minimized() + ? 'EPG.IMPORT_PANEL_EXPAND' + : 'EPG.IMPORT_PANEL_MINIMIZE' + ) | translate + " + [matTooltip]=" + (minimized() + ? 'EPG.IMPORT_PANEL_EXPAND' + : 'EPG.IMPORT_PANEL_MINIMIZE' + ) | translate + " > {{ minimized() ? 'unfold_more' : 'unfold_less' }} @@ -99,7 +117,7 @@ }}" > tv - {{ item.stats.totalChannels }} + {{ formatCount(item.stats.totalChannels) }} event - {{ item.stats.totalPrograms }} + {{ formatCount(item.stats.totalPrograms) }} } @else if (item.error) { {{ item.error }} diff --git a/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.scss b/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.scss index 61914a45c..66c053a5f 100644 --- a/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.scss +++ b/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.scss @@ -20,6 +20,34 @@ --epg-queued: #a78bfa; } +// Material icon buttons keep a 48px absolutely positioned touch target and +// a 48px state layer regardless of the width/height they are given. Inside +// the scrollable list that 48px box pokes past the row padding and turns +// into a horizontal scrollbar. Compact buttons shrink the state layer to the +// visible size and keep a touch target that is still larger than the button +// but fits inside the row padding (6px on each side, the tightest row has +// 8px), so touch and motor-impaired users keep a forgiving hit area. +@mixin compact-icon-button($size, $icon-size) { + --mdc-icon-button-state-layer-size: #{$size}; + --mdc-icon-button-icon-size: #{$icon-size}; + width: $size; + height: $size; + padding: 0; + line-height: $size; + flex-shrink: 0; + + ::ng-deep .mat-mdc-button-touch-target { + width: $size + 12px; + height: $size + 12px; + } + + mat-icon { + font-size: $icon-size; + width: $icon-size; + height: $icon-size; + } +} + .epg-progress-panel { position: fixed; bottom: 20px; @@ -90,18 +118,9 @@ .minimize-btn, .dismiss-all-btn { - width: 28px; - height: 28px; - padding: 0; - line-height: 28px; + @include compact-icon-button(28px, 18px); border-radius: 8px; color: var(--epg-text-tertiary); - - mat-icon { - font-size: 18px; - width: 18px; - height: 18px; - } } } @@ -117,7 +136,9 @@ .imports-list { max-height: 320px; - overflow-y: auto; + // Never scroll sideways: every row truncates or wraps instead. + overflow: hidden auto; + overscroll-behavior: contain; padding: 4px 0; scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--app-muted-color) 55%, transparent) @@ -152,6 +173,7 @@ .item-url { flex: 1; + min-width: 0; font-size: 0.78rem; font-weight: 500; white-space: nowrap; @@ -162,22 +184,13 @@ .dismiss-btn, .retry-btn { - width: 24px; - height: 24px; - padding: 0; - line-height: 24px; + @include compact-icon-button(24px, 16px); border-radius: 6px; opacity: 0; transition: opacity 0.15s ease, color 0.15s ease; color: var(--epg-text-tertiary); - - mat-icon { - font-size: 16px; - width: 16px; - height: 16px; - } } // Retry is a deliberate action — keep it discoverable, not hover-only, @@ -207,7 +220,8 @@ } } - &:hover .dismiss-btn { + &:hover .dismiss-btn, + .dismiss-btn:focus-visible { opacity: 1; } @@ -260,8 +274,11 @@ } .error-text { + min-width: 0; font-size: 0.72rem; + line-height: 1.35; color: var(--epg-error); + overflow-wrap: anywhere; } } @@ -322,6 +339,7 @@ .queue-url { flex: 1; + min-width: 0; font-size: 0.72rem; white-space: nowrap; overflow: hidden; @@ -329,17 +347,9 @@ } .dismiss-btn { - width: 22px; - height: 22px; - padding: 0; - line-height: 22px; + @include compact-icon-button(22px, 14px); + border-radius: 6px; color: var(--epg-text-tertiary); - - mat-icon { - font-size: 14px; - width: 14px; - height: 14px; - } } } diff --git a/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.ts b/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.ts index b83619288..37c55b5ee 100644 --- a/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.ts +++ b/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.ts @@ -14,6 +14,8 @@ import { EpgProgressService, } from '@iptvnator/epg/data-access'; import { ELECTRON_BRIDGE_SECURITY_ERROR_CODES } from '@iptvnator/shared/interfaces'; +import { normalizeDateLocale } from '@iptvnator/pipes'; +import { formatEpgImportDisplayUrl } from './epg-import-display-url'; interface EpgTrustConfirmDialogData { confirmLabel: string; @@ -106,11 +108,19 @@ export class EpgProgressPanelComponent { } getDisplayUrl(url: string): string { + return formatEpgImportDisplayUrl(url); + } + + formatCount(value: number): string { + // App language codes are not all BCP 47 tags (`zhtw`, `ary`, `by`); + // the date pipes already map those aliases for Intl. + const locale = normalizeDateLocale( + this.translate.currentLang || this.translate.defaultLang + ); try { - const urlObject = new URL(url); - return urlObject.hostname + urlObject.pathname.split('/').pop(); + return new Intl.NumberFormat(locale).format(value); } catch { - return url.length > 40 ? `${url.substring(0, 40)}...` : url; + return String(value); } } From d438f5c6550bf8e360a1da69bc438fe75888c4d5 Mon Sep 17 00:00:00 2001 From: 4gray <4gray@users.noreply.github.com> Date: Sun, 13 Sep 2026 21:04:41 +0200 Subject: [PATCH 02/43] feat(epg): accept local XMLTV files as EPG sources (#1600) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(epg): accept local XMLTV files as EPG sources Settings → EPG and the playlist dialog accepted `file://` in their form pattern, but the main process rejected everything except http(s), so a local XMLTV entry saved fine and then failed on import. Both surfaces now take a remote link, a `file:` URL, an absolute POSIX path or a Windows drive/UNC path (`classifyEpgSourceReference` in shared/interfaces), and the settings section spells out the accepted formats with examples. The EPG worker opens every source through `openEpgSourceStream`: remote links keep the validated-redirect client and trust policy, local files are read from disk behind the signature-sniffing optional gunzip stage, so .xml, .xml.gz and extension-less gzip all parse. Only hand-typed sources may be local: `extractM3uEpgUrls` harvests http(s) links only from M3U headers, since the local branch bypasses `validateRemoteUrl`. Co-Authored-By: Claude Fable 5.1 * feat(epg): pick local XMLTV files with a native file dialog A folder button beside each EPG source row (Settings → EPG and the playlist dialog) opens the native open-file dialog and writes the chosen absolute path into the row. New `EPG_OPEN_FILE_DIALOG` IPC behind `ElectronBridgeApi.openEpgFileDialog`, gated in the renderer by `RuntimeCapabilitiesService.supportsEpgFilePicker`. The row's refresh/remove buttons carry `data-test-id`s now, and the EPG e2e suites address them by id instead of index, since the folder button became the first button in a row. Co-Authored-By: Claude Fable 5.1 * fix(epg): authorize local XMLTV files in the main process Review follow-up (Greptile P1, Codex P1). The renderer hands source strings to FETCH_EPG/EPG_FORCE_FETCH unchanged, so the form validator alone could not enforce the provenance rule: a compromised renderer, or a legacy `file://` entry an older version stored from an M3U header, could name any file on disk. `EpgWorkerService.startFetch` now asks a main-process `EpgLocalSourceAuthorizer` before a local path reaches the worker: a path the native picker returned is trusted at once, a hand-typed path is confirmed once in a native message box the renderer cannot fake, and a refusal is reported in the progress panel. Allowed paths persist under TRUSTED_LOCAL_EPG_SOURCES in the main-process config. The worker opens its local branch only when main set `allowLocalFile`; the service defaults to deny-all until epg.events installs the persisted authorizer. `resolvePlaylistEpgSourceState` and `filterPlaylistEpgUrlsForFetch` drop a stored non-remote entry unless it is also in `manualEpgUrls`. Co-Authored-By: Claude Fable 5.1 * fix(epg): fail a refused local EPG fetch instead of resolving it Review follow-up (Codex P2). A denied native confirmation now rejects the fetch after reporting the error row, so handleFetchEpg and the renderer's fetch result cannot claim the file was read. Also restores the unrelated CLAUDE.md paragraph an earlier formatter pass had reflowed into a list. Co-Authored-By: Claude Fable 5.1 * fix(epg): cancel a local source retired during its authorization prompt Review follow-up (Codex P2). startFetch keeps the request generation captured before awaiting the native confirmation and rechecks it afterwards: a source retired meanwhile ends as cancelled instead of starting an import that a pending clear would then have to await. Co-Authored-By: Claude Fable 5.1 * test(epg): leave the local XMLTV e2e with a pristine settings form The local-file test ended with the EPG source field still dirty, which arms the main-process close guard: the app then waited for the unsaved changes dialog instead of closing, the close timeout killed it, and on Windows the killed process kept iptvnator.db busy (EBUSY on the data-dir cleanup) and hung the Playwright worker teardown. Discarding the form before the app closes takes the test from 15 s to 4 s locally. Co-Authored-By: Claude Fable 5.1 --------- Co-authored-by: 4gray Co-authored-by: Claude Fable 5.1 --- .changes/epg-local-xmltv-sources.md | 7 + CLAUDE.md | 2 +- apps/electron-backend-e2e/src/epg.e2e.ts | 142 +++++++++++---- .../src/xtream-epg.e2e.ts | 4 +- .../src/app/api/main.preload.spec-data.ts | 6 + .../src/app/api/main.preload.ts | 1 + .../src/app/events/epg-guide.events.spec.ts | 5 + .../epg-local-source-authorizer.spec.ts | 106 +++++++++++ .../app/events/epg-local-source-authorizer.ts | 96 ++++++++++ .../src/app/events/epg-mapping.events.spec.ts | 5 + .../src/app/events/epg-worker.service.spec.ts | 111 ++++++++++++ .../src/app/events/epg-worker.service.ts | 76 +++++++- .../src/app/events/epg.events.spec.ts | 5 + .../src/app/events/epg.events.ts | 41 ++++- .../src/app/services/store.service.ts | 9 + .../app/util/epg-local-source-path.spec.ts | 24 +++ .../src/app/util/epg-local-source-path.ts | 25 +++ .../src/app/workers/epg-parser.worker.ts | 81 +-------- .../src/app/workers/epg-source-stream.spec.ts | 164 ++++++++++++++++++ .../src/app/workers/epg-source-stream.ts | 151 ++++++++++++++++ .../settings-epg-section.component.html | 36 +++- .../settings-epg-section.component.ts | 2 + .../app/settings/settings-epg.facade.spec.ts | 46 +++++ .../src/app/settings/settings-epg.facade.ts | 23 +++ .../app/settings/settings-form.utils.spec.ts | 22 +++ .../src/app/settings/settings-form.utils.ts | 10 +- .../src/app/settings/settings.component.html | 2 + .../src/app/settings/settings.component.scss | 16 ++ .../test-stubs/settings-test-harness.stub.ts | 2 + apps/web/src/assets/i18n/ar.json | 4 + apps/web/src/assets/i18n/ary.json | 4 + apps/web/src/assets/i18n/by.json | 4 + apps/web/src/assets/i18n/de.json | 14 +- apps/web/src/assets/i18n/el.json | 4 + apps/web/src/assets/i18n/en.json | 14 +- apps/web/src/assets/i18n/es.json | 4 + apps/web/src/assets/i18n/fr.json | 4 + apps/web/src/assets/i18n/hu.json | 4 + apps/web/src/assets/i18n/it.json | 4 + apps/web/src/assets/i18n/ja.json | 4 + apps/web/src/assets/i18n/ko.json | 4 + apps/web/src/assets/i18n/nl.json | 4 + apps/web/src/assets/i18n/pl.json | 4 + apps/web/src/assets/i18n/pt.json | 4 + apps/web/src/assets/i18n/ru.json | 14 +- apps/web/src/assets/i18n/tr.json | 4 + apps/web/src/assets/i18n/zh.json | 4 + apps/web/src/assets/i18n/zhtw.json | 4 + docs/architecture/m3u-playlist-module.md | 57 ++++++ .../lib/epg-runtime-bridge.service.spec.ts | 19 ++ .../src/lib/epg-runtime-bridge.service.ts | 14 ++ libs/m3u-state/src/lib/effects.ts | 2 +- .../playlist-scoped-epg-fetch.util.spec.ts | 19 ++ .../src/lib/playlist-scoped-epg-fetch.util.ts | 11 +- .../playlist-info.component.html | 20 +++ .../playlist-info.component.spec.ts | 21 +++ .../playlist-info/playlist-info.component.ts | 24 ++- .../lib/runtime-capabilities.service.spec.ts | 3 + .../src/lib/runtime-capabilities.service.ts | 5 + libs/shared/interfaces/src/index.ts | 1 + .../src/lib/electron-api.interface.ts | 2 + .../src/lib/epg-source-reference.util.spec.ts | 80 +++++++++ .../src/lib/epg-source-reference.util.ts | 72 ++++++++ .../m3u-utils/src/lib/playlist.utils.spec.ts | 59 +++++++ .../m3u-utils/src/lib/playlist.utils.ts | 35 +++- 65 files changed, 1621 insertions(+), 149 deletions(-) create mode 100644 .changes/epg-local-xmltv-sources.md create mode 100644 apps/electron-backend/src/app/events/epg-local-source-authorizer.spec.ts create mode 100644 apps/electron-backend/src/app/events/epg-local-source-authorizer.ts create mode 100644 apps/electron-backend/src/app/util/epg-local-source-path.spec.ts create mode 100644 apps/electron-backend/src/app/util/epg-local-source-path.ts create mode 100644 apps/electron-backend/src/app/workers/epg-source-stream.spec.ts create mode 100644 apps/electron-backend/src/app/workers/epg-source-stream.ts create mode 100644 libs/shared/interfaces/src/lib/epg-source-reference.util.spec.ts create mode 100644 libs/shared/interfaces/src/lib/epg-source-reference.util.ts diff --git a/.changes/epg-local-xmltv-sources.md b/.changes/epg-local-xmltv-sources.md new file mode 100644 index 000000000..e609a2840 --- /dev/null +++ b/.changes/epg-local-xmltv-sources.md @@ -0,0 +1,7 @@ +--- +type: feature +area: epg +highlight: Local XMLTV files as EPG sources +--- + +EPG sources no longer have to be URLs: point Settings → EPG or a playlist's EPG list at an XMLTV file on your computer, by absolute path, `file://` link, or the new folder button. Plain `.xml` and gzip `.xml.gz` both work. A typed path is confirmed once in a native dialog before it is read; the settings page spells out the accepted formats with examples. diff --git a/CLAUDE.md b/CLAUDE.md index d02196c94..abd136b93 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1583,7 +1583,7 @@ stream_id`); it drops `series_id`/`movie_id`, so the builder pins the **EPG (Electronic Program Guide)**: -- XMLTV format support +- XMLTV format support, from `http(s)` links or local files (Electron only): a `file:` URL, an absolute POSIX path, or a Windows drive/UNC path, plain `.xml` or gzip (detected by signature). A folder button beside each row opens the native picker (`EPG_OPEN_FILE_DIALOG`, `RuntimeCapabilitiesService.supportsEpgFilePicker`). Shape rules: `classifyEpgSourceReference` in `libs/shared/interfaces`; the worker opens both kinds through `openEpgSourceStream` (`workers/epg-source-stream.ts`). Only hand-chosen sources may be local: `extractM3uEpgUrls` harvests only remote links from M3U headers (legacy stored non-remote entries are dropped unless manual), and `EpgWorkerService.startFetch` asks the main-process `EpgLocalSourceAuthorizer` before a local path reaches the worker — picker results are trusted, a typed path is confirmed once in a native message box, allowed paths persist under `TRUSTED_LOCAL_EPG_SOURCES`, and the worker's local branch requires the main-set `allowLocalFile` flag (deny-all until wired). Contract: `docs/architecture/m3u-playlist-module.md` ("Local XMLTV files") - Background parsing in worker thread; HTTP/file gzip compatibility follows `docs/architecture/m3u-playlist-module.md` ("XMLTV response compression"). - Stored in database for quick lookup - Global display-time offset (`Settings.epgOffsetMinutes`, Settings → EPG, ±720 min, Electron only): display-only, provider data is never rewritten. Two equivalent forms in `libs/shared/interfaces/src/lib/epg-display-offset.util.ts` — `epgDisplayTimeMs` (shift the programme; `ui/epg` rendering via the `offsetMinutes` input, channel rows, dashboard/recording labels; the programme dialog and the programme guide read the store themselves) and `epgProviderClockMs` (shift "now"; every "currently airing" decision: the `GET_CURRENT_PROGRAMS_BATCH` lookup takes an explicit `nowMs` and `EpgService` tags its cache with the offset, Xtream/Stalker/M3U current-programme selection and previews, the unified collection resolver, dashboard progress, recording overlap). A consumer applies exactly one form per comparison. Contract: `docs/architecture/m3u-playlist-module.md` ("EPG display offset") diff --git a/apps/electron-backend-e2e/src/epg.e2e.ts b/apps/electron-backend-e2e/src/epg.e2e.ts index aacfd7ccb..58525a4af 100644 --- a/apps/electron-backend-e2e/src/epg.e2e.ts +++ b/apps/electron-backend-e2e/src/epg.e2e.ts @@ -1,3 +1,4 @@ +import { writeFileSync } from 'node:fs'; import { readFile } from 'node:fs/promises'; import { createServer } from 'node:http'; import { join } from 'node:path'; @@ -233,10 +234,7 @@ test.describe('Electron EPG', () => { removedUrls: [], })); }); - await app.mainWindow - .locator('.epg-source-row button') - .nth(1) - .click(); + await app.mainWindow.getByTestId('epg-source-remove').click(); await saveSettings(app.mainWindow); await expect( app.mainWindow.getByTestId('settings-unsaved-bar') @@ -296,6 +294,106 @@ test.describe('Electron EPG', () => { } }); + test('@epg @electron imports a local gzipped XMLTV file given as an absolute path', async ({ + dataDir, + }) => { + const localGuide = join(dataDir, 'local guide.xml.gz'); + writeFileSync(localGuide, gzipSync(epgFixtureXml)); + const app = await launchElectronApp(dataDir); + + try { + await openSettings(app.mainWindow); + await openSettingsSection(app.mainWindow, 'epg'); + await expect( + app.mainWindow.getByTestId('epg-source-formats') + ).toContainText('file://'); + await app.mainWindow + .getByRole('button', { name: 'Add EPG source' }) + .click(); + const field = app.mainWindow + .locator('.epg-source-row input') + .first(); + await field.fill('guide.xml'); + // Material reveals errors once the control is touched. + await field.blur(); + await expect( + app.mainWindow.locator('.epg-source-row mat-error') + ).toBeVisible(); + await field.fill(localGuide); + await expect( + app.mainWindow.locator('.epg-source-row mat-error') + ).toHaveCount(0); + + // Native dialogs cannot be driven from Playwright: stub them in + // the main process. A hand-typed path is confirmed once through + // the message box; a refused path is reported, never read. + await app.electronApp.evaluate(({ dialog }) => { + dialog.showMessageBox = (async () => ({ + response: 1, + checkboxChecked: false, + })) as typeof dialog.showMessageBox; + }); + await app.mainWindow.getByTestId('epg-source-refresh').click(); + await expect( + app.mainWindow.locator( + '.epg-progress-panel .import-item.status-error' + ) + ).toContainText('was not allowed'); + expect(await getEpgChannelCount(app.mainWindow)).toBe(0); + + await app.electronApp.evaluate(({ dialog }) => { + dialog.showMessageBox = (async () => ({ + response: 0, + checkboxChecked: false, + })) as typeof dialog.showMessageBox; + }); + await app.mainWindow + .locator('.epg-progress-panel .retry-btn') + .click(); + await expect + .poll(() => getEpgChannelCount(app.mainWindow), { + timeout: 30000, + }) + .toBeGreaterThan(0); + await expect( + app.mainWindow.locator( + '.epg-progress-panel .import-item.status-complete' + ) + ).toHaveCount(1); + + // A file chosen in the native picker is trusted without a prompt. + const browsedGuide = join(dataDir, 'browsed guide.xml.gz'); + writeFileSync(browsedGuide, gzipSync(epgFixtureXml)); + await app.electronApp.evaluate(({ dialog }, filePath) => { + dialog.showOpenDialog = (async () => ({ + canceled: false, + filePaths: [filePath], + })) as typeof dialog.showOpenDialog; + dialog.showMessageBox = (async () => { + throw new Error('picked files must not prompt'); + }) as typeof dialog.showMessageBox; + }, browsedGuide); + await app.mainWindow.getByTestId('epg-source-browse').click(); + await expect(field).toHaveValue(browsedGuide); + await app.mainWindow.getByTestId('epg-source-refresh').click(); + await expect( + app.mainWindow.locator( + '.epg-progress-panel .import-item.status-complete' + ) + ).toHaveCount(2); + + // Leave with a pristine form: a dirty settings form arms the + // main-process close guard, and the app would then wait for a + // confirmation dialog instead of closing. + await app.mainWindow.getByTestId('discard-settings').click(); + await expect( + app.mainWindow.getByTestId('settings-unsaved-bar') + ).toHaveCount(0); + } finally { + await closeElectronApp(app); + } + }); + test('@epg @electron adds an EPG source, fetches guide data, removes its stored EPG data on save', async ({ dataDir, }) => { @@ -316,10 +414,7 @@ test.describe('Electron EPG', () => { .first() .fill(epgServer.resourceUrl); - await app.mainWindow - .locator('.epg-source-row button') - .first() - .click(); + await app.mainWindow.getByTestId('epg-source-refresh').click(); await expect( app.mainWindow.locator('.epg-progress-panel') ).toBeVisible(); @@ -355,10 +450,7 @@ test.describe('Electron EPG', () => { await saveSettings(app.mainWindow); - await app.mainWindow - .locator('.epg-source-row button') - .nth(1) - .click(); + await app.mainWindow.getByTestId('epg-source-remove').click(); await expect(app.mainWindow.locator('.epg-source-row')).toHaveCount( 0 ); @@ -436,10 +528,7 @@ test.describe('Electron EPG', () => { '.epg-progress-panel .import-item.status-error' ) ).toHaveCount(1); - await app.mainWindow - .locator('.epg-source-row button') - .nth(1) - .click(); + await app.mainWindow.getByTestId('epg-source-remove').click(); await saveSettings(app.mainWindow); await expect( app.mainWindow.locator('.epg-progress-panel .import-item') @@ -502,8 +591,7 @@ test.describe('Electron EPG', () => { await app.mainWindow .locator('.epg-source-row') .first() - .locator('button') - .nth(1) + .getByTestId('epg-source-remove') .click(); // A staged removal must not delete data before Save. expect(await programs()).toContain('Removed Bulletin'); @@ -523,10 +611,7 @@ test.describe('Electron EPG', () => { await expect.poll(programs).toEqual(['Retained Bulletin']); await openSettings(app.mainWindow); await openSettingsSection(app.mainWindow, 'epg'); - await app.mainWindow - .locator('.epg-source-row button') - .nth(1) - .click(); + await app.mainWindow.getByTestId('epg-source-remove').click(); await saveSettings(app.mainWindow); await expect.poll(programs).toEqual([]); await expect.poll(() => getEpgChannelCount(app.mainWindow)).toBe(0); @@ -592,8 +677,7 @@ test.describe('Electron EPG', () => { await app.mainWindow .locator('.epg-source-row') .nth(1) - .locator('button') - .nth(1) + .getByTestId('epg-source-remove') .click(); await saveSettings(app.mainWindow); const firstMetadata = { @@ -701,10 +785,7 @@ test.describe('Electron EPG', () => { .locator('.epg-source-row input') .fill(epgServer.resourceUrl); await saveSettings(app.mainWindow); - await app.mainWindow - .locator('.epg-source-row button') - .nth(1) - .click(); + await app.mainWindow.getByTestId('epg-source-remove').click(); await saveSettings(app.mainWindow); // The same URL still belongs to the saved M3U playlist. const retained = await app.mainWindow.evaluate(async () => @@ -771,10 +852,7 @@ test.describe('Electron EPG', () => { .locator('.epg-source-row input') .first() .fill(epgServer.resourceUrl); - await app.mainWindow - .locator('.epg-source-row button') - .first() - .click(); + await app.mainWindow.getByTestId('epg-source-refresh').click(); await expect .poll(() => getEpgChannelCount(app.mainWindow), { diff --git a/apps/electron-backend-e2e/src/xtream-epg.e2e.ts b/apps/electron-backend-e2e/src/xtream-epg.e2e.ts index 02eeb28e3..dec87b886 100644 --- a/apps/electron-backend-e2e/src/xtream-epg.e2e.ts +++ b/apps/electron-backend-e2e/src/xtream-epg.e2e.ts @@ -96,7 +96,7 @@ test('@epg @xtream @electron removes uploaded guide data and restores provider E .toContain('Temporary XMLTV Bulletin'); await openSettings(app.mainWindow); await openSettingsSection(app.mainWindow, 'epg'); - await app.mainWindow.locator('.epg-source-row button').nth(1).click(); + await app.mainWindow.getByTestId('epg-source-remove').click(); await saveSettings(app.mainWindow); await openWorkspaceSection(app.mainWindow, 'Live TV'); await clickCategoryByNameExact(app.mainWindow, fixture.categoryName); @@ -190,7 +190,7 @@ test('@epg @stalker @electron invalidates a loaded manual XMLTV mapping after so .toContain('Retired Stalker Bulletin'); await openSettings(app.mainWindow); await openSettingsSection(app.mainWindow, 'epg'); - await app.mainWindow.locator('.epg-source-row button').nth(1).click(); + await app.mainWindow.getByTestId('epg-source-remove').click(); await saveSettings(app.mainWindow); await openWorkspaceSection(app.mainWindow, 'Live TV'); await clickCategoryByNameExact(app.mainWindow, fixture.categoryName); diff --git a/apps/electron-backend/src/app/api/main.preload.spec-data.ts b/apps/electron-backend/src/app/api/main.preload.spec-data.ts index a3e895386..c9c597bb3 100644 --- a/apps/electron-backend/src/app/api/main.preload.spec-data.ts +++ b/apps/electron-backend/src/app/api/main.preload.spec-data.ts @@ -550,6 +550,12 @@ export const epgPreloadCases: PreloadInvokeCase[] = [ }, ], }, + { + method: 'openEpgFileDialog', + args: [], + channel: 'EPG_OPEN_FILE_DIALOG', + forwardedArgs: [], + }, { method: 'clearEpgData', args: [], diff --git a/apps/electron-backend/src/app/api/main.preload.ts b/apps/electron-backend/src/app/api/main.preload.ts index 580167b15..ea4ad442e 100644 --- a/apps/electron-backend/src/app/api/main.preload.ts +++ b/apps/electron-backend/src/app/api/main.preload.ts @@ -680,6 +680,7 @@ const electronApi: ElectronBridgeApi = { ipcRenderer.invoke('EPG_GET_PROGRAM_COVERAGE', window), forceFetchEpg: (url: string, options?: ElectronBridgeTrustOptions) => ipcRenderer.invoke('EPG_FORCE_FETCH', { url, options }), + openEpgFileDialog: () => ipcRenderer.invoke('EPG_OPEN_FILE_DIALOG'), clearEpgData: () => ipcRenderer.invoke('EPG_CLEAR_ALL'), reconcileEpgSources: (urls: string[]) => ipcRenderer.invoke('EPG_RECONCILE_SOURCES', { urls }), diff --git a/apps/electron-backend/src/app/events/epg-guide.events.spec.ts b/apps/electron-backend/src/app/events/epg-guide.events.spec.ts index ccf491003..cc8f7eff9 100644 --- a/apps/electron-backend/src/app/events/epg-guide.events.spec.ts +++ b/apps/electron-backend/src/app/events/epg-guide.events.spec.ts @@ -9,6 +9,11 @@ const ipcHandlers = new Map< (event: unknown, args: unknown) => Promise >(); +jest.mock('../services/store.service', () => ({ + TRUSTED_LOCAL_EPG_SOURCES: 'TRUSTED_LOCAL_EPG_SOURCES', + store: { get: jest.fn(), set: jest.fn() }, +})); + jest.mock('electron', () => ({ app: { isPackaged: false, diff --git a/apps/electron-backend/src/app/events/epg-local-source-authorizer.spec.ts b/apps/electron-backend/src/app/events/epg-local-source-authorizer.spec.ts new file mode 100644 index 000000000..253cd01cb --- /dev/null +++ b/apps/electron-backend/src/app/events/epg-local-source-authorizer.spec.ts @@ -0,0 +1,106 @@ +jest.mock('electron', () => ({ + BrowserWindow: { getFocusedWindow: () => null, getAllWindows: () => [] }, + dialog: { showMessageBox: jest.fn() }, +})); + +import { dialog } from 'electron'; +import { + PersistedEpgLocalSourceAuthorizer, + promptForLocalEpgSource, +} from './epg-local-source-authorizer'; + +describe('PersistedEpgLocalSourceAuthorizer', () => { + let stored: string[]; + let prompt: jest.Mock; + let authorizer: PersistedEpgLocalSourceAuthorizer; + + beforeEach(() => { + stored = []; + prompt = jest.fn(); + authorizer = new PersistedEpgLocalSourceAuthorizer( + { + load: () => [...stored], + save: (paths) => { + stored = [...paths]; + }, + }, + prompt + ); + }); + + it('trusts a picked file without prompting and persists it once', async () => { + authorizer.authorize('/epg/guide.xml'); + authorizer.authorize('/epg/guide.xml'); + + await expect(authorizer.ensureAllowed('/epg/guide.xml')).resolves.toBe( + true + ); + expect(prompt).not.toHaveBeenCalled(); + expect(stored).toEqual(['/epg/guide.xml']); + }); + + it('persists a hand-typed path only after the user allowed it', async () => { + prompt.mockResolvedValueOnce(false).mockResolvedValueOnce(true); + + await expect(authorizer.ensureAllowed('/epg/typed.xml')).resolves.toBe( + false + ); + expect(stored).toEqual([]); + + await expect(authorizer.ensureAllowed('/epg/typed.xml')).resolves.toBe( + true + ); + expect(stored).toEqual(['/epg/typed.xml']); + expect(prompt).toHaveBeenCalledTimes(2); + + await expect(authorizer.ensureAllowed('/epg/typed.xml')).resolves.toBe( + true + ); + expect(prompt).toHaveBeenCalledTimes(2); + }); + + it('shares one prompt between concurrent requests for the same file', async () => { + let resolvePrompt: (allowed: boolean) => void = () => undefined; + prompt.mockReturnValue( + new Promise((resolve) => { + resolvePrompt = resolve; + }) + ); + + const first = authorizer.ensureAllowed('/epg/shared.xml'); + const second = authorizer.ensureAllowed('/epg/shared.xml'); + expect(prompt).toHaveBeenCalledTimes(1); + + resolvePrompt(true); + await expect(Promise.all([first, second])).resolves.toEqual([ + true, + true, + ]); + expect(stored).toEqual(['/epg/shared.xml']); + }); +}); + +describe('promptForLocalEpgSource', () => { + it('maps the Allow button to true and anything else to false', async () => { + (dialog.showMessageBox as jest.Mock).mockResolvedValueOnce({ + response: 0, + }); + await expect(promptForLocalEpgSource('/epg/guide.xml')).resolves.toBe( + true + ); + expect(dialog.showMessageBox).toHaveBeenCalledWith( + expect.objectContaining({ + buttons: ['Allow', 'Cancel'], + cancelId: 1, + detail: expect.stringContaining('/epg/guide.xml'), + }) + ); + + (dialog.showMessageBox as jest.Mock).mockResolvedValueOnce({ + response: 1, + }); + await expect(promptForLocalEpgSource('/epg/guide.xml')).resolves.toBe( + false + ); + }); +}); diff --git a/apps/electron-backend/src/app/events/epg-local-source-authorizer.ts b/apps/electron-backend/src/app/events/epg-local-source-authorizer.ts new file mode 100644 index 000000000..63cf56fbe --- /dev/null +++ b/apps/electron-backend/src/app/events/epg-local-source-authorizer.ts @@ -0,0 +1,96 @@ +import { BrowserWindow, dialog } from 'electron'; + +/** + * Main-process gate for local XMLTV files. + * + * The renderer hands EPG source strings to `FETCH_EPG`/`EPG_FORCE_FETCH` + * unchanged, so a compromised renderer could name any file on disk. Like + * `save-file-dialog` → `write-file` for playlist exports, the decision + * therefore lives here: a path is readable only after the native picker + * returned it or the user allowed it in a native confirmation the renderer + * cannot fake. Allowed paths persist in the main-process config so startup + * refreshes need no prompt. + */ +export interface EpgLocalSourceAuthorizer { + /** A file the user chose in the native picker is trusted right away. */ + authorize(filePath: string): void; + /** Whether `filePath` may be read; prompts once for a hand-typed path. */ + ensureAllowed(filePath: string): Promise; +} + +export interface EpgLocalSourcePersistence { + load(): string[]; + save(filePaths: string[]): void; +} + +export const EPG_LOCAL_SOURCE_REFUSED_MESSAGE = + 'Reading this local file was not allowed. Retry to be asked again.'; + +/** Refuses every local file; the real authorizer is wired at registration. */ +export const DENY_ALL_EPG_LOCAL_SOURCES: EpgLocalSourceAuthorizer = { + authorize: () => undefined, + ensureAllowed: () => Promise.resolve(false), +}; + +export class PersistedEpgLocalSourceAuthorizer implements EpgLocalSourceAuthorizer { + private readonly pendingPrompts = new Map>(); + + constructor( + private readonly persistence: EpgLocalSourcePersistence, + private readonly prompt: (filePath: string) => Promise + ) {} + + authorize(filePath: string): void { + const allowed = this.persistence.load(); + if (allowed.includes(filePath)) { + return; + } + this.persistence.save([...allowed, filePath]); + } + + ensureAllowed(filePath: string): Promise { + if (this.persistence.load().includes(filePath)) { + return Promise.resolve(true); + } + // Concurrent fetches of the same file (settings save plus a startup + // refresh) share one prompt instead of stacking dialogs. + const pending = this.pendingPrompts.get(filePath); + if (pending) { + return pending; + } + const decision = this.prompt(filePath) + .then((allowed) => { + if (allowed) { + this.authorize(filePath); + } + return allowed; + }) + .finally(() => { + this.pendingPrompts.delete(filePath); + }); + this.pendingPrompts.set(filePath, decision); + return decision; + } +} + +/** Native confirmation for a hand-typed local EPG path. */ +export async function promptForLocalEpgSource( + filePath: string +): Promise { + const options: Electron.MessageBoxOptions = { + type: 'question', + title: 'Allow local EPG file?', + message: 'Read this file as an EPG source?', + detail: `${filePath}\n\nIPTVnator will read the XMLTV guide data in this file. Only allow files you added yourself.`, + buttons: ['Allow', 'Cancel'], + defaultId: 0, + cancelId: 1, + noLink: true, + }; + const window = + BrowserWindow.getFocusedWindow() ?? BrowserWindow.getAllWindows()[0]; + const { response } = window + ? await dialog.showMessageBox(window, options) + : await dialog.showMessageBox(options); + return response === 0; +} diff --git a/apps/electron-backend/src/app/events/epg-mapping.events.spec.ts b/apps/electron-backend/src/app/events/epg-mapping.events.spec.ts index 0af8125fe..e0110f6b8 100644 --- a/apps/electron-backend/src/app/events/epg-mapping.events.spec.ts +++ b/apps/electron-backend/src/app/events/epg-mapping.events.spec.ts @@ -11,6 +11,11 @@ const ipcHandlers = new Map< (event: unknown, args: unknown) => Promise >(); +jest.mock('../services/store.service', () => ({ + TRUSTED_LOCAL_EPG_SOURCES: 'TRUSTED_LOCAL_EPG_SOURCES', + store: { get: jest.fn(), set: jest.fn() }, +})); + jest.mock('electron', () => ({ app: { isPackaged: false, diff --git a/apps/electron-backend/src/app/events/epg-worker.service.spec.ts b/apps/electron-backend/src/app/events/epg-worker.service.spec.ts index 34f501a66..ecb0f278d 100644 --- a/apps/electron-backend/src/app/events/epg-worker.service.spec.ts +++ b/apps/electron-backend/src/app/events/epg-worker.service.spec.ts @@ -38,6 +38,7 @@ jest.mock('../workers/worker-runtime-paths', () => ({ mockResolveWorkerRuntimeBootstrap(...args), })); +import { retireEpgSource } from './epg-source-generation'; import { EpgWorkerService } from './epg-worker.service'; describe('EpgWorkerService worker lifecycle', () => { @@ -166,6 +167,116 @@ describe('EpgWorkerService worker lifecycle', () => { expect(service.hasFetchedUrl(url)).toBe(true); }); + it('refuses a local file the authorizer denies without spawning a worker', async () => { + const localPath = '/home/user/epg/guide.xml.gz'; + const progressSpy = jest.spyOn(service, 'sendProgressToRenderer'); + service.localSourceAuthorizer = { + authorize: jest.fn(), + ensureAllowed: jest.fn().mockResolvedValue(false), + }; + + await expect( + service.fetchEpgFromUrl(localPath, { + allowLocalFile: true, + } as any) + ).rejects.toThrow('was not allowed'); + + expect( + service.localSourceAuthorizer.ensureAllowed + ).toHaveBeenCalledWith(localPath); + expect(mockWorkerInstances).toHaveLength(0); + expect(progressSpy).toHaveBeenCalledWith( + localPath, + 'error', + undefined, + expect.stringContaining('was not allowed') + ); + expect(service.hasFetchedUrl(localPath)).toBe(false); + }); + + it('cancels a local source retired while its authorization prompt is open', async () => { + const localPath = '/home/user/epg/retired.xml.gz'; + const progressSpy = jest.spyOn(service, 'sendProgressToRenderer'); + let answer: (allowed: boolean) => void = () => undefined; + service.localSourceAuthorizer = { + authorize: jest.fn(), + ensureAllowed: jest.fn( + () => + new Promise((resolve) => { + answer = resolve; + }) + ), + }; + + const fetchPromise = service.fetchEpgFromUrl(localPath); + await Promise.resolve(); + retireEpgSource(localPath); + answer(true); + + await expect(fetchPromise).resolves.toBeUndefined(); + expect(mockWorkerInstances).toHaveLength(0); + expect(progressSpy).toHaveBeenCalledWith( + localPath, + 'cancelled', + undefined, + undefined, + undefined, + undefined, + undefined, + 0 + ); + }); + + it('unlocks the worker local branch only for an allowed file', async () => { + const localPath = '/home/user/epg/guide.xml.gz'; + service.localSourceAuthorizer = { + authorize: jest.fn(), + ensureAllowed: jest.fn().mockResolvedValue(true), + }; + + const fetchPromise = service.fetchEpgFromUrl(localPath, { + trustedInsecureTlsHosts: ['example.com'], + }); + await Promise.resolve(); + const worker = mockWorkerInstances[0]; + worker.emit('message', { type: 'READY' }); + + expect(worker.postMessage).toHaveBeenCalledWith({ + type: 'FETCH_EPG', + url: localPath, + options: { + trustedInsecureTlsHosts: ['example.com'], + allowLocalFile: true, + }, + }); + + worker.emit('message', { + type: 'EPG_COMPLETE', + stats: { totalChannels: 1, totalPrograms: 1 }, + }); + await expect(fetchPromise).resolves.toBeUndefined(); + }); + + it('never forwards a renderer-supplied allowLocalFile flag for remote sources', async () => { + const fetchPromise = service.fetchEpgFromUrl(url, { + allowLocalFile: true, + } as any); + await Promise.resolve(); + const worker = mockWorkerInstances[0]; + worker.emit('message', { type: 'READY' }); + + expect(worker.postMessage).toHaveBeenCalledWith({ + type: 'FETCH_EPG', + url, + options: {}, + }); + worker.emit('message', { + type: 'EPG_COMPLETE', + stats: { totalChannels: 1, totalPrograms: 1 }, + }); + await expect(fetchPromise).resolves.toBeUndefined(); + }); + it('rejects when the worker emits an error event', async () => { const fetchPromise = service.fetchEpgFromUrl(url); const worker = mockWorkerInstances[0]; diff --git a/apps/electron-backend/src/app/events/epg-worker.service.ts b/apps/electron-backend/src/app/events/epg-worker.service.ts index 0df02d09e..9e6827071 100644 --- a/apps/electron-backend/src/app/events/epg-worker.service.ts +++ b/apps/electron-backend/src/app/events/epg-worker.service.ts @@ -7,6 +7,12 @@ import { import { BrowserWindow } from 'electron'; import { EpgWorkerRuntime } from './epg-worker-runtime'; import { runEpgFetch } from './epg-fetch-operation'; +import { + DENY_ALL_EPG_LOCAL_SOURCES, + EPG_LOCAL_SOURCE_REFUSED_MESSAGE, + EpgLocalSourceAuthorizer, +} from './epg-local-source-authorizer'; +import { resolveLocalEpgSourcePath } from '../util/epg-local-source-path'; import { Worker } from 'worker_threads'; import { ElectronBridgeSecurityErrorCode, @@ -118,18 +124,76 @@ export class EpgWorkerService { return; } - const fetchPromise = this.startFetch(url, options).finally(() => { - this.inFlightFetches.delete(url); - }); + const fetchPromise = this.startFetch(url, options, generation).finally( + () => { + this.inFlightFetches.delete(url); + } + ); this.inFlightFetches.set(url, fetchPromise); return fetchPromise; } - private startFetch( + /** + * Gate for local XMLTV files; `epg.events.ts` installs the persisted, + * prompting authorizer at registration. Until then every local path is + * refused, so a missing wiring fails closed rather than open. + */ + localSourceAuthorizer: EpgLocalSourceAuthorizer = + DENY_ALL_EPG_LOCAL_SOURCES; + + private async startFetch( url: string, - options: ElectronBridgeTrustOptions + options: ElectronBridgeTrustOptions, + generation: number ): Promise { - return runEpgFetch(url, options, { + // The renderer's options must not be able to unlock the worker's + // local branch: the flag is set here, only for an allowed path. + const { allowLocalFile: _rendererFlag, ...trustOptions } = + options as ElectronBridgeTrustOptions & { + allowLocalFile?: unknown; + }; + let workerOptions: ElectronBridgeTrustOptions & { + allowLocalFile?: boolean; + } = trustOptions; + const localPath = resolveLocalEpgSourcePath(url); + if (localPath) { + const allowed = + await this.localSourceAuthorizer.ensureAllowed(localPath); + // The prompt can stay open for a while; a source retired + // meanwhile (settings save, reconciliation) must end as + // cancelled rather than start an import the clear then awaits. + if (generation !== epgSourceGeneration(url)) { + this.sendProgressToRenderer( + url, + 'cancelled', + undefined, + undefined, + undefined, + undefined, + undefined, + generation + ); + return; + } + if (!allowed) { + epgLogger.log( + this.loggerLabel, + 'Local EPG file was not allowed by the user' + ); + this.sendProgressToRenderer( + url, + 'error', + undefined, + EPG_LOCAL_SOURCE_REFUSED_MESSAGE + ); + // A refusal is a failed fetch, not a completed one: the + // renderer's fetch result and EPG availability must not + // claim the file was read. + throw new Error(EPG_LOCAL_SOURCE_REFUSED_MESSAGE); + } + workerOptions = { ...trustOptions, allowLocalFile: true }; + } + return runEpgFetch(url, workerOptions, { runtime: this.runtime, workers: this.workers, fetchedUrls: this.fetchedUrls, diff --git a/apps/electron-backend/src/app/events/epg.events.spec.ts b/apps/electron-backend/src/app/events/epg.events.spec.ts index c1c2cc226..b39bdfe1a 100644 --- a/apps/electron-backend/src/app/events/epg.events.spec.ts +++ b/apps/electron-backend/src/app/events/epg.events.spec.ts @@ -39,6 +39,11 @@ jest.mock('../workers/worker-runtime-paths', () => ({ resolveWorkerRuntimeBootstrap(...args), })); +jest.mock('../services/store.service', () => ({ + TRUSTED_LOCAL_EPG_SOURCES: 'TRUSTED_LOCAL_EPG_SOURCES', + store: { get: jest.fn(), set: jest.fn() }, +})); + jest.mock('../database/connection', () => ({ getDatabase: (...args: unknown[]) => getDatabase(...args), })); diff --git a/apps/electron-backend/src/app/events/epg.events.ts b/apps/electron-backend/src/app/events/epg.events.ts index 91801b10b..c84c6db11 100644 --- a/apps/electron-backend/src/app/events/epg.events.ts +++ b/apps/electron-backend/src/app/events/epg.events.ts @@ -1,5 +1,10 @@ import { reconcileEpgSources } from './epg-source-settings.service'; -import { ipcMain } from 'electron'; +import { dialog, ipcMain } from 'electron'; +import { store, TRUSTED_LOCAL_EPG_SOURCES } from '../services/store.service'; +import { + PersistedEpgLocalSourceAuthorizer, + promptForLocalEpgSource, +} from './epg-local-source-authorizer'; import { ElectronBridgeCurrentProgramsOptions, ElectronBridgeEpgGuideWindow, @@ -131,6 +136,24 @@ export default class EpgEvents { } ); + epgWorkerService.localSourceAuthorizer = + new PersistedEpgLocalSourceAuthorizer( + { + load: () => store.get(TRUSTED_LOCAL_EPG_SOURCES) ?? [], + save: (filePaths) => + store.set(TRUSTED_LOCAL_EPG_SOURCES, filePaths), + }, + promptForLocalEpgSource + ); + + ipcMain.handle('EPG_OPEN_FILE_DIALOG', async () => { + const filePath = await this.pickEpgSourceFile(); + if (filePath) { + epgWorkerService.localSourceAuthorizer.authorize(filePath); + } + return filePath; + }); + ipcMain.handle('EPG_CLEAR_ALL', async () => { await this.clearEpgData(); return { success: true }; @@ -207,6 +230,22 @@ export default class EpgEvents { return ipcMain; } + /** + * Native picker for a local XMLTV file. Resolves the absolute path the + * user chose, or null on cancel; the renderer stores it as the source + * value and the worker reads it through `openEpgSourceStream`. + */ + private static async pickEpgSourceFile(): Promise { + const { canceled, filePaths } = await dialog.showOpenDialog({ + properties: ['openFile'], + filters: [ + { name: 'XMLTV', extensions: ['xml', 'gz', 'xmltv'] }, + { name: 'All Files', extensions: ['*'] }, + ], + }); + return canceled || filePaths.length === 0 ? null : filePaths[0]; + } + private static async checkEpgFreshness( urls: string[], maxAgeHours: number diff --git a/apps/electron-backend/src/app/services/store.service.ts b/apps/electron-backend/src/app/services/store.service.ts index d53338efb..9bdb10d30 100644 --- a/apps/electron-backend/src/app/services/store.service.ts +++ b/apps/electron-backend/src/app/services/store.service.ts @@ -28,6 +28,14 @@ export const STARTUP_WINDOW_MODE = 'STARTUP_WINDOW_MODE'; /** Desktop portal request cooldown; absent means enabled. */ export const PORTAL_CONNECTIVITY_GUARD = 'PORTAL_CONNECTIVITY_GUARD'; +/** + * Local XMLTV files the EPG importer may read: every path the native picker + * returned plus every hand-typed path the user allowed in the main-process + * confirmation (`epg-local-source-authorizer.ts`). Owned by main, never by + * the renderer, because the renderer is what this list defends against. + */ +export const TRUSTED_LOCAL_EPG_SOURCES = 'TRUSTED_LOCAL_EPG_SOURCES'; + /** * Extra libmpv options for embedded sessions, one "key=value" per line, as * typed in Settings > Playback. Mirrored here by the SETTINGS_UPDATE handler @@ -56,6 +64,7 @@ export type StoreType = { [EMBEDDED_MPV_AUTO_RECONNECT]: boolean; [STARTUP_WINDOW_MODE]: StartupWindowMode; [PORTAL_CONNECTIVITY_GUARD]: boolean; + [TRUSTED_LOCAL_EPG_SOURCES]: string[]; }; // Export singleton store instance diff --git a/apps/electron-backend/src/app/util/epg-local-source-path.spec.ts b/apps/electron-backend/src/app/util/epg-local-source-path.spec.ts new file mode 100644 index 000000000..3ed9b7b39 --- /dev/null +++ b/apps/electron-backend/src/app/util/epg-local-source-path.spec.ts @@ -0,0 +1,24 @@ +import { join } from 'path'; +import { pathToFileURL } from 'url'; +import { resolveLocalEpgSourcePath } from './epg-local-source-path'; + +describe('resolveLocalEpgSourcePath', () => { + const file = join(process.cwd(), 'epg', 'guide v2.xml.gz'); + + it('maps a file URL and the plain path to the same normalized path', () => { + expect(resolveLocalEpgSourcePath(pathToFileURL(file).href)).toBe(file); + expect(resolveLocalEpgSourcePath(` ${file} `)).toBe(file); + expect( + resolveLocalEpgSourcePath(join(file, '..', 'guide v2.xml.gz')) + ).toBe(file); + }); + + it('returns null for remote links, relative paths and empty values', () => { + expect( + resolveLocalEpgSourcePath('https://epg.example.org/guide.xml') + ).toBeNull(); + expect(resolveLocalEpgSourcePath('epg/guide.xml')).toBeNull(); + expect(resolveLocalEpgSourcePath('')).toBeNull(); + expect(resolveLocalEpgSourcePath('file://')).toBeNull(); + }); +}); diff --git a/apps/electron-backend/src/app/util/epg-local-source-path.ts b/apps/electron-backend/src/app/util/epg-local-source-path.ts new file mode 100644 index 000000000..97977bd16 --- /dev/null +++ b/apps/electron-backend/src/app/util/epg-local-source-path.ts @@ -0,0 +1,25 @@ +import { isAbsolute, resolve } from 'path'; +import { fileURLToPath } from 'url'; +import { classifyEpgSourceReference } from '@iptvnator/shared/interfaces'; + +/** + * The filesystem path behind an EPG source reference, or null when the + * reference is not a local file (a remote link, an empty value or a relative + * path). Shared by the main-process authorizer and the worker so both agree + * on which string names which file: a `file:` URL and the plain path it + * encodes normalize to the same value. + */ +export function resolveLocalEpgSourcePath(reference: string): string | null { + const trimmed = reference.trim(); + if (classifyEpgSourceReference(trimmed) !== 'local') { + return null; + } + if (/^file:/i.test(trimmed)) { + try { + return resolve(fileURLToPath(trimmed)); + } catch { + return null; + } + } + return isAbsolute(trimmed) ? resolve(trimmed) : null; +} diff --git a/apps/electron-backend/src/app/workers/epg-parser.worker.ts b/apps/electron-backend/src/app/workers/epg-parser.worker.ts index c2567f652..1ab2aa661 100644 --- a/apps/electron-backend/src/app/workers/epg-parser.worker.ts +++ b/apps/electron-backend/src/app/workers/epg-parser.worker.ts @@ -5,30 +5,20 @@ import { ElectronBridgeSecurityErrorCode, ElectronBridgeTrustOptions, } from '@iptvnator/shared/interfaces'; -import { Readable } from 'stream'; import { parentPort, workerData } from 'worker_threads'; import { EpgDatabase, EpgDatabaseClearOperation, EpgDatabaseSourceClearOperation, } from './epg-database'; -import { createDecodedEpgStream } from './epg-stream-decoder'; +import { openEpgSourceStream } from './epg-source-stream'; import { StreamingEpgParser } from './epg-streaming-parser'; -import { - getEpgResponseContentEncoding, - shouldGunzipEpgResponse, -} from './epg-response-utils'; -import { - isPrivateNetworkUrlAccessAllowed, - UnsafeUrlError, -} from '../events/url-safety'; -import { createPlaylistAgentFactory } from '../util/secure-https'; +import { UnsafeUrlError } from '../events/url-safety'; import { getHostnameFromErrorUrl, getHostnameFromUrl, isInvalidTlsCertificateError, } from '../util/security-errors'; -import { requestWithValidatedRedirects } from '../util/validated-axios'; import { getNativeModuleSearchPaths, getWorkerDataNativeModuleSearchPaths, @@ -115,54 +105,7 @@ async function fetchAndParseEpgStreaming( let hasClearedSource = false; try { - // EPG URLs can originate from an untrusted M3U `url-tvg` attribute. - // Validate every redirect and require an explicit operator opt-in for - // private/LAN sources. - const response = await requestWithValidatedRedirects( - url.trim(), - { - agentFactory: createPlaylistAgentFactory({ - trustedInsecureTlsHosts: options.trustedInsecureTlsHosts, - }), - decompress: false, - method: 'GET', - responseType: 'stream', - }, - { - allowPrivateNetworks: - isPrivateNetworkUrlAccessAllowed() || - isTrustedPrivateNetworkEpgSource(url, options), - } - ); - const responseUrl = response.config.url; - const isGzipped = shouldGunzipEpgResponse(url, { - headers: response.headers, - url: responseUrl, - }); - const contentEncoding = getEpgResponseContentEncoding(response.headers); - - if (responseUrl && responseUrl !== url) { - epgLogger.log(loggerLabel, 'Resolved EPG redirect'); - } - - epgLogger.log( - loggerLabel, - `EPG response detected as gzipped: ${isGzipped}` - ); - if (contentEncoding) { - epgLogger.log( - loggerLabel, - `EPG response content-encoding: ${contentEncoding}` - ); - } - - if (response.status < 200 || response.status >= 300) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - if (!response.data) { - throw new Error('Response body is null'); - } + const decodedStream = await openEpgSourceStream(url, options); const parser = new StreamingEpgParser( (channels) => { @@ -188,11 +131,7 @@ async function fetchAndParseEpgStreaming( ); return new Promise((resolve, reject) => { - const dataStream = createDecodedEpgStream( - response.data, - response.headers, - isGzipped - ); + const dataStream = decodedStream; dataStream.on('data', (chunk: Buffer) => { try { @@ -260,18 +199,6 @@ async function fetchAndParseEpgStreaming( } } -function isTrustedPrivateNetworkEpgSource( - url: string, - options: ElectronBridgeTrustOptions -): boolean { - const normalizedUrl = url.trim(); - return ( - options.trustedPrivateNetworkEpgUrls?.some( - (trustedUrl) => trustedUrl.trim() === normalizedUrl - ) ?? false - ); -} - function toEpgFetchError( error: unknown, url: string diff --git a/apps/electron-backend/src/app/workers/epg-source-stream.spec.ts b/apps/electron-backend/src/app/workers/epg-source-stream.spec.ts new file mode 100644 index 000000000..f5a4b338e --- /dev/null +++ b/apps/electron-backend/src/app/workers/epg-source-stream.spec.ts @@ -0,0 +1,164 @@ +import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'fs'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import { Readable } from 'stream'; +import { pathToFileURL } from 'url'; +import { gzipSync } from 'zlib'; + +const requestWithValidatedRedirects = jest.fn(); + +jest.mock('../util/validated-axios', () => ({ + requestWithValidatedRedirects: (...args: unknown[]) => + requestWithValidatedRedirects(...args), +})); +jest.mock('../util/secure-https', () => ({ + createPlaylistAgentFactory: () => ({}), +})); +jest.mock('../events/url-safety', () => ({ + isPrivateNetworkUrlAccessAllowed: () => false, +})); +jest.mock('../util/epg-logger', () => ({ + epgLogger: { log: jest.fn(), error: jest.fn(), warn: jest.fn() }, +})); + +import { openEpgSourceStream } from './epg-source-stream'; + +const LOCAL = { allowLocalFile: true }; + +const xmltv = + '' + + 'One'; + +async function readAll(stream: Readable): Promise { + const chunks: Buffer[] = []; + for await (const chunk of stream) { + chunks.push(Buffer.from(chunk)); + } + return Buffer.concat(chunks).toString('utf-8'); +} + +describe('openEpgSourceStream', () => { + let dir: string; + + beforeEach(() => { + dir = mkdtempSync(join(tmpdir(), 'iptvnator-epg-source-')); + requestWithValidatedRedirects.mockReset(); + }); + + afterEach(() => { + rmSync(dir, { recursive: true, force: true }); + }); + + it('reads a plain XML file from an absolute path', async () => { + const file = join(dir, 'guide.xml'); + writeFileSync(file, xmltv); + + await expect( + readAll(await openEpgSourceStream(file, LOCAL)) + ).resolves.toBe(xmltv); + expect(requestWithValidatedRedirects).not.toHaveBeenCalled(); + }); + + it('gunzips a .xml.gz file', async () => { + const file = join(dir, 'guide.xml.gz'); + writeFileSync(file, gzipSync(xmltv)); + + await expect( + readAll(await openEpgSourceStream(file, LOCAL)) + ).resolves.toBe(xmltv); + }); + + it('detects gzip by signature when the extension is missing', async () => { + const file = join(dir, 'guide'); + writeFileSync(file, gzipSync(xmltv)); + + await expect( + readAll(await openEpgSourceStream(file, LOCAL)) + ).resolves.toBe(xmltv); + }); + + it('accepts a file: URL with percent-encoded characters', async () => { + const file = join(dir, 'guide v2.xml'); + writeFileSync(file, xmltv); + + await expect( + readAll(await openEpgSourceStream(pathToFileURL(file).href, LOCAL)) + ).resolves.toBe(xmltv); + }); + + it('tolerates surrounding whitespace in the stored value', async () => { + const file = join(dir, 'guide.xml'); + writeFileSync(file, xmltv); + + await expect( + readAll(await openEpgSourceStream(` ${file} `, LOCAL)) + ).resolves.toBe(xmltv); + }); + + it('reports a missing file with its path instead of an ENOENT code', async () => { + const file = join(dir, 'missing.xml'); + + await expect(openEpgSourceStream(file, LOCAL)).rejects.toThrow( + `EPG file not found: ${file}` + ); + }); + + it('refuses a directory', async () => { + const folder = join(dir, 'epg'); + mkdirSync(folder); + + await expect(openEpgSourceStream(folder, LOCAL)).rejects.toThrow( + `EPG source is not a file: ${folder}` + ); + }); + + it('fails a truncated gzip file instead of parsing it as XML', async () => { + const file = join(dir, 'guide.xml.gz'); + writeFileSync(file, gzipSync(xmltv).subarray(0, 20)); + + await expect( + readAll(await openEpgSourceStream(file, LOCAL)) + ).rejects.toThrow(); + }); + + it('routes remote links through the validated HTTP client', async () => { + requestWithValidatedRedirects.mockResolvedValue({ + config: { url: 'https://epg.example.org/guide.xml' }, + data: Readable.from([Buffer.from(xmltv)]), + headers: {}, + status: 200, + }); + + const stream = await openEpgSourceStream( + 'https://epg.example.org/guide.xml', + { trustedPrivateNetworkEpgUrls: [] } + ); + + await expect(readAll(stream)).resolves.toBe(xmltv); + expect(requestWithValidatedRedirects).toHaveBeenCalledWith( + 'https://epg.example.org/guide.xml', + expect.objectContaining({ responseType: 'stream' }), + { allowPrivateNetworks: false } + ); + }); + + it('refuses a local file the main process did not authorize', async () => { + const file = join(dir, 'guide.xml'); + writeFileSync(file, xmltv); + + await expect(openEpgSourceStream(file)).rejects.toThrow( + 'Local EPG file was not authorized' + ); + await expect( + openEpgSourceStream(file, { + allowLocalFile: 'yes' as unknown as boolean, + }) + ).rejects.toThrow('Local EPG file was not authorized'); + expect(requestWithValidatedRedirects).not.toHaveBeenCalled(); + }); + + it('never reads a relative path from disk', async () => { + await expect(openEpgSourceStream('guide.xml')).rejects.toThrow(); + expect(requestWithValidatedRedirects).toHaveBeenCalledTimes(1); + }); +}); diff --git a/apps/electron-backend/src/app/workers/epg-source-stream.ts b/apps/electron-backend/src/app/workers/epg-source-stream.ts new file mode 100644 index 000000000..6e3eadc3e --- /dev/null +++ b/apps/electron-backend/src/app/workers/epg-source-stream.ts @@ -0,0 +1,151 @@ +import { createReadStream } from 'fs'; +import { stat } from 'fs/promises'; +import { PassThrough, Readable, pipeline } from 'stream'; +import { ElectronBridgeTrustOptions } from '@iptvnator/shared/interfaces'; +import { isPrivateNetworkUrlAccessAllowed } from '../events/url-safety'; +import { epgLogger } from '../util/epg-logger'; +import { resolveLocalEpgSourcePath } from '../util/epg-local-source-path'; +import { createPlaylistAgentFactory } from '../util/secure-https'; +import { requestWithValidatedRedirects } from '../util/validated-axios'; +import { createOptionalEpgGunzip } from './epg-optional-gunzip'; +import { + getEpgResponseContentEncoding, + shouldGunzipEpgResponse, +} from './epg-response-utils'; +import { createDecodedEpgStream } from './epg-stream-decoder'; + +const loggerLabel = '[EPG Worker]'; + +/** Trust options plus the main-process verdict on a local file. */ +export type EpgWorkerFetchOptions = ElectronBridgeTrustOptions & { + /** + * Set by `EpgWorkerService.startFetch` only after the main-process + * authorizer allowed the path (native picker or native confirmation). + * Never taken from the renderer. + */ + allowLocalFile?: boolean; +}; + +/** + * Opens the decoded XMLTV byte stream for an EPG source. + * + * Remote sources go through the validated-redirect HTTP client with the + * private-network and TLS trust policy. Local sources — a `file:` URL or an + * absolute path — are read from disk; gzip is detected from the file's own + * signature, so `guide.xml`, `guide.xml.gz` and a gzip file without the + * extension all work. + * + * The local branch bypasses `validateRemoteUrl`, which only knows http(s). + * Two things make that safe: header-declared M3U sources are filtered to + * remote links before they are stored (`extractM3uEpgUrls`), and the main + * process opens the branch only for a path its authorizer allowed + * (`allowLocalFile`), so neither a downloaded playlist nor a compromised + * renderer can name an arbitrary file. + */ +export async function openEpgSourceStream( + url: string, + options: EpgWorkerFetchOptions = {} +): Promise { + const localPath = resolveLocalEpgSourcePath(url); + if (localPath) { + if (options.allowLocalFile !== true) { + throw new Error('Local EPG file was not authorized'); + } + return openLocalEpgSource(localPath); + } + return openRemoteEpgSource(url, options); +} + +async function openLocalEpgSource(filePath: string): Promise { + let info: Awaited>; + try { + info = await stat(filePath); + } catch (error) { + const code = (error as NodeJS.ErrnoException).code; + if (code === 'ENOENT' || code === 'ENOTDIR') { + throw new Error(`EPG file not found: ${filePath}`); + } + throw error; + } + if (!info.isFile()) { + throw new Error(`EPG source is not a file: ${filePath}`); + } + + epgLogger.log(loggerLabel, 'Reading local EPG file'); + const output = new PassThrough(); + pipeline( + createReadStream(filePath), + createOptionalEpgGunzip(), + output, + (error) => { + if (error) output.destroy(error); + } + ); + return output; +} + +async function openRemoteEpgSource( + url: string, + options: ElectronBridgeTrustOptions +): Promise { + // EPG URLs can originate from an untrusted M3U `url-tvg` attribute. + // Validate every redirect and require an explicit operator opt-in for + // private/LAN sources. + const response = await requestWithValidatedRedirects( + url.trim(), + { + agentFactory: createPlaylistAgentFactory({ + trustedInsecureTlsHosts: options.trustedInsecureTlsHosts, + }), + decompress: false, + method: 'GET', + responseType: 'stream', + }, + { + allowPrivateNetworks: + isPrivateNetworkUrlAccessAllowed() || + isTrustedPrivateNetworkEpgSource(url, options), + } + ); + const responseUrl = response.config.url; + const isGzipped = shouldGunzipEpgResponse(url, { + headers: response.headers, + url: responseUrl, + }); + const contentEncoding = getEpgResponseContentEncoding(response.headers); + + if (responseUrl && responseUrl !== url) { + epgLogger.log(loggerLabel, 'Resolved EPG redirect'); + } + epgLogger.log( + loggerLabel, + `EPG response detected as gzipped: ${isGzipped}` + ); + if (contentEncoding) { + epgLogger.log( + loggerLabel, + `EPG response content-encoding: ${contentEncoding}` + ); + } + + if (response.status < 200 || response.status >= 300) { + throw new Error(`HTTP error! status: ${response.status}`); + } + if (!response.data) { + throw new Error('Response body is null'); + } + + return createDecodedEpgStream(response.data, response.headers, isGzipped); +} + +function isTrustedPrivateNetworkEpgSource( + url: string, + options: ElectronBridgeTrustOptions +): boolean { + const normalizedUrl = url.trim(); + return ( + options.trustedPrivateNetworkEpgUrls?.some( + (trustedUrl) => trustedUrl.trim() === normalizedUrl + ) ?? false + ); +} diff --git a/apps/web/src/app/settings/settings-epg-section.component.html b/apps/web/src/app/settings/settings-epg-section.component.html index c3b7bb875..c8a96c1c1 100644 --- a/apps/web/src/app/settings/settings-epg-section.component.html +++ b/apps/web/src/app/settings/settings-epg-section.component.html @@ -70,6 +70,19 @@
+
+

{{ 'SETTINGS.EPG_SOURCE_LIST_TITLE' | translate }}

+

{{ 'SETTINGS.EPG_SOURCE_FORMATS_HINT' | translate }}

+

+ {{ + 'SETTINGS.EPG_SOURCE_FORMATS_EXAMPLES' | translate + }} + https://example.org/guide.xml.gz + /home/you/epg/guide.xml + C:\epg\guide.xml.gz + file:///…/guide.xml +

+
@if (epgUrl().length === 0) { @@ -102,7 +115,8 @@ }} @@ -115,12 +129,31 @@ [url]="epgField.value" /> } + @if (canBrowseFiles()) { + + } + }
+
+
+

+ {{ 'SETTINGS.COVER_TITLES' | translate }} +

+

+ {{ 'SETTINGS.COVER_TITLES_DESCRIPTION' | translate }} +

+
+
+ +
+
+

{{ 'SETTINGS.STARTUP_BEHAVIOR' | translate }}

diff --git a/apps/web/src/app/settings/settings.component.form.spec.ts b/apps/web/src/app/settings/settings.component.form.spec.ts index 1e0eb7478..e7b05bdbf 100644 --- a/apps/web/src/app/settings/settings.component.form.spec.ts +++ b/apps/web/src/app/settings/settings.component.form.spec.ts @@ -81,6 +81,29 @@ describe('SettingsComponent form', () => { ); }); + it('stages the posters-only cover wall until Save', async () => { + const checkbox = (fixture.nativeElement as HTMLElement).querySelector( + '[data-test-id="cover-titles-toggle"] input' + ) as HTMLInputElement; + expect(checkbox).not.toBeNull(); + expect(checkbox.checked).toBe(true); + + checkbox.click(); + fixture.detectChanges(); + + expect(component.settingsForm.get('showCoverTitles')?.value).toBe( + false + ); + expect(settingsStore.updateSettings).not.toHaveBeenCalled(); + + await component.form.save(() => undefined); + + expect(settingsStore.updateSettings).toHaveBeenCalledWith( + expect.objectContaining({ showCoverTitles: false }), + expect.anything() + ); + }); + it('hides the portal pause setting when the desktop bridge is unavailable', () => { fixture.destroy(); window.electron = undefined; diff --git a/apps/web/src/app/settings/test-stubs/settings-test-harness.stub.ts b/apps/web/src/app/settings/test-stubs/settings-test-harness.stub.ts index f1f851648..736f7cafe 100644 --- a/apps/web/src/app/settings/test-stubs/settings-test-harness.stub.ts +++ b/apps/web/src/app/settings/test-stubs/settings-test-harness.stub.ts @@ -115,6 +115,7 @@ export const DEFAULT_SETTINGS = { embeddedMpvAutoReconnect: true, portalConnectivityGuard: true, coverSize: 'medium', + showCoverTitles: true, dashboardRails: DEFAULT_DASHBOARD_RAILS, preferUploadedEpgOverXtream: false, epgViewMode: 'timeline', diff --git a/apps/web/src/assets/i18n/ar.json b/apps/web/src/assets/i18n/ar.json index 89ce53221..7dcd6a825 100644 --- a/apps/web/src/assets/i18n/ar.json +++ b/apps/web/src/assets/i18n/ar.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "صغير", "COVER_SIZE_MEDIUM": "متوسط", "COVER_SIZE_LARGE": "كبير", + "COVER_TITLES": "عرض العناوين أسفل الأغلفة", + "COVER_TITLES_DESCRIPTION": "عطّل هذا الخيار للحصول على جدار ملصقات فقط في شبكات الأفلام والمسلسلات: يظهر العنوان على الغلاف عند تمرير المؤشر فوقه أو التركيز عليه. تحتفظ القنوات المباشرة ونتائج البحث وأشرطة لوحة التحكم بعناوينها.", "EPG_VIEW_MODE": "عرض الدليل", "EPG_OFFSET_TITLE": "إزاحة وقت دليل البرامج", "EPG_OFFSET_HINT": "اضبط أوقات البرامج المعروضة دون تحديث الدليل. استخدم الدقائق من -720 إلى 720.", diff --git a/apps/web/src/assets/i18n/ary.json b/apps/web/src/assets/i18n/ary.json index 4cda264ba..05cbda747 100644 --- a/apps/web/src/assets/i18n/ary.json +++ b/apps/web/src/assets/i18n/ary.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "صغير", "COVER_SIZE_MEDIUM": "متوسط", "COVER_SIZE_LARGE": "كبير", + "COVER_TITLES": "وري العناوين تحت الأغلفة", + "COVER_TITLES_DESCRIPTION": "طفي هاد الخيار باش تولي شبكات الأفلام والسلاسل حيط غير بالأغلفة: العنوان كيبان فوق الغلاف ملي تقرب بالماوس عليه ولا تفوكيسي عليه بالكيبورد. القنوات المباشرة، نتايج البحث وقضبان لوحة التحكم كيبقاو عناوينهم ظاهرين.", "EPG_VIEW_MODE": "عرض الدليل", "EPG_OFFSET_TITLE": "إزاحة وقت دليل البرامج", "EPG_OFFSET_HINT": "اضبط أوقات البرامج المعروضة دون تحديث الدليل. استخدم الدقائق من -720 إلى 720.", diff --git a/apps/web/src/assets/i18n/by.json b/apps/web/src/assets/i18n/by.json index c858ca3b1..56c58b83f 100644 --- a/apps/web/src/assets/i18n/by.json +++ b/apps/web/src/assets/i18n/by.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "Малы", "COVER_SIZE_MEDIUM": "Сярэдні", "COVER_SIZE_LARGE": "Вялікі", + "COVER_TITLES": "Паказаць назвы пад вокладкамі", + "COVER_TITLES_DESCRIPTION": "Выключыце, каб атрымаць сцяну толькі з постараў у сетках фільмаў і серыялаў: назва з'яўляецца на вокладцы пры навядзенні курсора або пераводзе фокусу на яе. Жывыя каналы, вынікі пошуку і стужкі панэлі заўсёды паказваюць свае назвы.", "EPG_VIEW_MODE": "Выгляд праграмы перадач", "EPG_OFFSET_TITLE": "Зрушэнне часу праграмы", "EPG_OFFSET_HINT": "Змяняйце час паказу праграм без абнаўлення даведніка. Выкарыстоўвайце хвіліны ад -720 да 720.", diff --git a/apps/web/src/assets/i18n/de.json b/apps/web/src/assets/i18n/de.json index d7eb551c9..0503d6cac 100644 --- a/apps/web/src/assets/i18n/de.json +++ b/apps/web/src/assets/i18n/de.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "Klein", "COVER_SIZE_MEDIUM": "Mittel", "COVER_SIZE_LARGE": "Groß", + "COVER_TITLES": "Titel unter Covern anzeigen", + "COVER_TITLES_DESCRIPTION": "Deaktivieren Sie diese Option für eine reine Poster-Wand in Film- und Serienrastern: Der Titel erscheint auf dem Cover, wenn Sie mit der Maus darauf zeigen oder es per Tastatur fokussieren. Live-Kanäle, Suchergebnisse und Dashboard-Reihen behalten ihre Beschriftungen.", "EPG_VIEW_MODE": "Programmführer-Ansicht", "EPG_OFFSET_TITLE": "EPG-Zeitverschiebung", "EPG_OFFSET_HINT": "Angezeigte Sendezeiten verschieben, ohne den Programmführer zu aktualisieren. Minuten von -720 bis 720 verwenden.", diff --git a/apps/web/src/assets/i18n/el.json b/apps/web/src/assets/i18n/el.json index 05e0aa73e..c20094823 100644 --- a/apps/web/src/assets/i18n/el.json +++ b/apps/web/src/assets/i18n/el.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "Μικρό", "COVER_SIZE_MEDIUM": "Μεσαίο", "COVER_SIZE_LARGE": "Μεγάλο", + "COVER_TITLES": "Εμφάνιση τίτλων κάτω από τα εξώφυλλα", + "COVER_TITLES_DESCRIPTION": "Απενεργοποιήστε το για έναν τοίχο μόνο με αφίσες στα πλέγματα ταινιών και σειρών: ο τίτλος εμφανίζεται πάνω στο εξώφυλλο όταν περάσετε το ποντίκι από πάνω του ή το εστιάσετε. Τα ζωντανά κανάλια, τα αποτελέσματα αναζήτησης και οι σειρές του πίνακα διατηρούν τις ετικέτες τους.", "EPG_VIEW_MODE": "Προβολή οδηγού", "EPG_OFFSET_TITLE": "Μετατόπιση ώρας EPG", "EPG_OFFSET_HINT": "Μετατοπίστε τις εμφανιζόμενες ώρες προγραμμάτων χωρίς ανανέωση του οδηγού. Χρησιμοποιήστε λεπτά από -720 έως 720.", diff --git a/apps/web/src/assets/i18n/en.json b/apps/web/src/assets/i18n/en.json index 089c3d23d..a9fe5a77b 100644 --- a/apps/web/src/assets/i18n/en.json +++ b/apps/web/src/assets/i18n/en.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "Small", "COVER_SIZE_MEDIUM": "Medium", "COVER_SIZE_LARGE": "Large", + "COVER_TITLES": "Show titles under covers", + "COVER_TITLES_DESCRIPTION": "Turn off for a posters-only wall in movie and series grids: the title appears on the cover when you point at it or focus it. Live channels, search results and dashboard rails keep their labels.", "EPG_VIEW_MODE": "Guide view", "EPG_OFFSET_TITLE": "EPG time offset", "EPG_OFFSET_HINT": "Shift displayed programme times without refreshing the guide. Use minutes, from -720 to 720.", diff --git a/apps/web/src/assets/i18n/es.json b/apps/web/src/assets/i18n/es.json index 0dd3485a5..1cced59af 100644 --- a/apps/web/src/assets/i18n/es.json +++ b/apps/web/src/assets/i18n/es.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "Pequeño", "COVER_SIZE_MEDIUM": "Mediano", "COVER_SIZE_LARGE": "Grande", + "COVER_TITLES": "Mostrar títulos debajo de las portadas", + "COVER_TITLES_DESCRIPTION": "Desactívalo para lograr un muro de solo pósteres en las cuadrículas de películas y series: el título aparece sobre la portada al pasar el cursor por encima o al enfocarla. Los canales en vivo, los resultados de búsqueda y los carriles del panel mantienen sus etiquetas.", "EPG_VIEW_MODE": "Vista de la guía", "EPG_OFFSET_TITLE": "Desfase horario de la EPG", "EPG_OFFSET_HINT": "Desplaza las horas mostradas de los programas sin actualizar la guía. Usa minutos, de -720 a 720.", diff --git a/apps/web/src/assets/i18n/fr.json b/apps/web/src/assets/i18n/fr.json index d6ff9d1f8..843ec3e85 100644 --- a/apps/web/src/assets/i18n/fr.json +++ b/apps/web/src/assets/i18n/fr.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "Petite", "COVER_SIZE_MEDIUM": "Moyenne", "COVER_SIZE_LARGE": "Grande", + "COVER_TITLES": "Afficher les titres sous les affiches", + "COVER_TITLES_DESCRIPTION": "Désactivez cette option pour un mur d'affiches uniquement dans les grilles de films et de séries : le titre apparaît sur l'affiche lorsque vous la survolez ou lui donnez le focus. Les chaînes en direct, les résultats de recherche et les rangées du tableau de bord conservent leurs libellés.", "EPG_VIEW_MODE": "Vue du guide", "EPG_OFFSET_TITLE": "Décalage horaire de l’EPG", "EPG_OFFSET_HINT": "Décalez les horaires affichés des programmes sans actualiser le guide. Utilisez des minutes de -720 à 720.", diff --git a/apps/web/src/assets/i18n/hu.json b/apps/web/src/assets/i18n/hu.json index 0d31c50b5..2fb55d30f 100644 --- a/apps/web/src/assets/i18n/hu.json +++ b/apps/web/src/assets/i18n/hu.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "Kicsi", "COVER_SIZE_MEDIUM": "Közepes", "COVER_SIZE_LARGE": "Nagy", + "COVER_TITLES": "Borítók alatti címek megjelenítése", + "COVER_TITLES_DESCRIPTION": "Kapcsolja ki, ha csak borítókból álló falat szeretne a film- és sorozatrácsokban: a cím a borítón jelenik meg, amikor rámutat az egérrel vagy arra fókuszál. Az élő csatornák, a keresési találatok és az irányítópulti sávok megtartják a feliratukat.", "EPG_VIEW_MODE": "Műsorújság nézete", "EPG_OFFSET_TITLE": "EPG-időeltolás", "EPG_OFFSET_HINT": "A műsorok megjelenített idejének eltolása a műsorújság frissítése nélkül. Percben adja meg, -720 és 720 között.", diff --git a/apps/web/src/assets/i18n/it.json b/apps/web/src/assets/i18n/it.json index 1b2ad6bb3..3bb7a3d33 100644 --- a/apps/web/src/assets/i18n/it.json +++ b/apps/web/src/assets/i18n/it.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "Piccola", "COVER_SIZE_MEDIUM": "Media", "COVER_SIZE_LARGE": "Grande", + "COVER_TITLES": "Mostra i titoli sotto le copertine", + "COVER_TITLES_DESCRIPTION": "Disattiva per un muro di sole copertine nelle griglie di film e serie: il titolo appare sulla copertina quando ci passi sopra o la selezioni con la tastiera. I canali in diretta, i risultati di ricerca e le fasce della dashboard mantengono comunque le loro etichette.", "EPG_VIEW_MODE": "Vista guida", "EPG_OFFSET_TITLE": "Scostamento orario EPG", "EPG_OFFSET_HINT": "Sposta gli orari dei programmi visualizzati senza aggiornare la guida. Usa minuti da -720 a 720.", diff --git a/apps/web/src/assets/i18n/ja.json b/apps/web/src/assets/i18n/ja.json index f45969134..f4c8ec8df 100644 --- a/apps/web/src/assets/i18n/ja.json +++ b/apps/web/src/assets/i18n/ja.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "小", "COVER_SIZE_MEDIUM": "中", "COVER_SIZE_LARGE": "大", + "COVER_TITLES": "カバーの下にタイトルを表示", + "COVER_TITLES_DESCRIPTION": "オフにすると、映画・シリーズのグリッドがポスターのみの表示になります。マウスを合わせるか、フォーカスするとカバーにタイトルが表示されます。ライブチャンネル、検索結果、ダッシュボードのレールでは常にラベルが表示されます。", "EPG_VIEW_MODE": "番組表の表示", "EPG_OFFSET_TITLE": "EPG 時刻の補正", "EPG_OFFSET_HINT": "番組表を更新せずに表示時刻をずらします。-720 から 720 までの分単位で指定してください。", diff --git a/apps/web/src/assets/i18n/ko.json b/apps/web/src/assets/i18n/ko.json index dbac6b39c..3429e5ae6 100644 --- a/apps/web/src/assets/i18n/ko.json +++ b/apps/web/src/assets/i18n/ko.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "작게", "COVER_SIZE_MEDIUM": "중간", "COVER_SIZE_LARGE": "크게", + "COVER_TITLES": "커버 아래에 제목 표시", + "COVER_TITLES_DESCRIPTION": "끄면 영화 및 시리즈 그리드가 포스터만 표시되는 화면이 됩니다. 마우스로 가리키거나 키보드로 포커스하면 커버 위에 제목이 나타납니다. 라이브 채널, 검색 결과, 대시보드 레일은 항상 레이블을 유지합니다.", "EPG_VIEW_MODE": "가이드 보기", "EPG_OFFSET_TITLE": "EPG 시간 오프셋", "EPG_OFFSET_HINT": "가이드를 새로 고치지 않고 표시되는 프로그램 시간을 조정합니다. -720에서 720 사이의 분 단위로 입력하세요.", diff --git a/apps/web/src/assets/i18n/nl.json b/apps/web/src/assets/i18n/nl.json index 12738a0cd..d65ea1ab4 100644 --- a/apps/web/src/assets/i18n/nl.json +++ b/apps/web/src/assets/i18n/nl.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "Klein", "COVER_SIZE_MEDIUM": "Gemiddeld", "COVER_SIZE_LARGE": "Groot", + "COVER_TITLES": "Titels onder hoezen tonen", + "COVER_TITLES_DESCRIPTION": "Schakel dit uit voor een muur met alleen posters in rasters met films en reeksen: de titel verschijnt op de hoes wanneer u de muis erover beweegt of hem focust. Livekanalen, zoekresultaten en dashboardrails behouden hun labels.", "EPG_VIEW_MODE": "Gidsweergave", "EPG_OFFSET_TITLE": "EPG-tijdverschuiving", "EPG_OFFSET_HINT": "Verschuif weergegeven programmatijden zonder de gids te vernieuwen. Gebruik minuten van -720 tot 720.", diff --git a/apps/web/src/assets/i18n/pl.json b/apps/web/src/assets/i18n/pl.json index ff5859027..52275f216 100644 --- a/apps/web/src/assets/i18n/pl.json +++ b/apps/web/src/assets/i18n/pl.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "Mały", "COVER_SIZE_MEDIUM": "Średni", "COVER_SIZE_LARGE": "Duży", + "COVER_TITLES": "Pokaż tytuły pod okładkami", + "COVER_TITLES_DESCRIPTION": "Wyłącz, aby uzyskać ścianę samych plakatów w siatkach filmów i seriali: tytuł pojawia się na okładce po najechaniu na nią kursorem lub przejściu do niej klawiaturą. Kanały na żywo, wyniki wyszukiwania i sekcje pulpitu nadal pokazują tytuły.", "EPG_VIEW_MODE": "Widok przewodnika", "EPG_OFFSET_TITLE": "Przesunięcie czasu EPG", "EPG_OFFSET_HINT": "Przesuń wyświetlane godziny programów bez odświeżania przewodnika. Użyj minut od -720 do 720.", diff --git a/apps/web/src/assets/i18n/pt.json b/apps/web/src/assets/i18n/pt.json index 1f6500029..364582bcb 100644 --- a/apps/web/src/assets/i18n/pt.json +++ b/apps/web/src/assets/i18n/pt.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "Pequeno", "COVER_SIZE_MEDIUM": "Médio", "COVER_SIZE_LARGE": "Grande", + "COVER_TITLES": "Mostrar títulos sob as capas", + "COVER_TITLES_DESCRIPTION": "Desative para um mural apenas de pôsteres nas grades de filmes e séries: o título aparece sobre a capa ao passar o cursor ou focar nela. Canais ao vivo, resultados de pesquisa e os trilhos do dashboard mantêm os seus rótulos.", "EPG_VIEW_MODE": "Visualização do guia", "EPG_OFFSET_TITLE": "Deslocamento de horário do EPG", "EPG_OFFSET_HINT": "Desloque os horários dos programas exibidos sem atualizar o guia. Use minutos de -720 a 720.", diff --git a/apps/web/src/assets/i18n/ru.json b/apps/web/src/assets/i18n/ru.json index 690616e10..bdfe7dc3d 100644 --- a/apps/web/src/assets/i18n/ru.json +++ b/apps/web/src/assets/i18n/ru.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "Маленький", "COVER_SIZE_MEDIUM": "Средний", "COVER_SIZE_LARGE": "Большой", + "COVER_TITLES": "Показывать названия под обложками", + "COVER_TITLES_DESCRIPTION": "Отключите, чтобы в сетках фильмов и сериалов отображались только обложки: название появляется на обложке при наведении курсора или получении фокуса. Live-каналы, результаты поиска и ленты панели по-прежнему показывают подписи.", "EPG_VIEW_MODE": "Вид программы передач", "EPG_OFFSET_TITLE": "Смещение времени телепрограммы", "EPG_OFFSET_HINT": "Смещайте время отображения программ без обновления телепрограммы. Укажите минуты от -720 до 720.", diff --git a/apps/web/src/assets/i18n/tr.json b/apps/web/src/assets/i18n/tr.json index 626df797c..5e883964d 100644 --- a/apps/web/src/assets/i18n/tr.json +++ b/apps/web/src/assets/i18n/tr.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "Küçük", "COVER_SIZE_MEDIUM": "Orta", "COVER_SIZE_LARGE": "Büyük", + "COVER_TITLES": "Kapakların altında başlıkları göster", + "COVER_TITLES_DESCRIPTION": "Film ve dizi ızgaralarında yalnızca poster gösteren bir görünüm için kapatın: üzerine geldiğinizde veya odaklandığınızda başlık kapağın üzerinde görünür. Canlı kanallar, arama sonuçları ve pano bölümleri etiketlerini korur.", "EPG_VIEW_MODE": "Rehber görünümü", "EPG_OFFSET_TITLE": "EPG zaman kayması", "EPG_OFFSET_HINT": "Rehberi yenilemeden görüntülenen program saatlerini kaydırın. -720 ile 720 arasında dakika kullanın.", diff --git a/apps/web/src/assets/i18n/zh.json b/apps/web/src/assets/i18n/zh.json index e10ba4496..5d3595771 100644 --- a/apps/web/src/assets/i18n/zh.json +++ b/apps/web/src/assets/i18n/zh.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "小", "COVER_SIZE_MEDIUM": "中", "COVER_SIZE_LARGE": "大", + "COVER_TITLES": "在封面下方显示标题", + "COVER_TITLES_DESCRIPTION": "关闭后,电影和剧集网格将呈现纯海报墙:将指针悬停在封面上或将其聚焦时才会显示标题。直播频道、搜索结果和仪表盘栏目将始终显示标签。", "EPG_VIEW_MODE": "节目指南视图", "EPG_OFFSET_TITLE": "EPG 时间偏移", "EPG_OFFSET_HINT": "无需刷新节目指南即可调整显示的节目时间。请输入 -720 到 720 之间的分钟数。", diff --git a/apps/web/src/assets/i18n/zhtw.json b/apps/web/src/assets/i18n/zhtw.json index 308a2dba2..0afdb9eb0 100644 --- a/apps/web/src/assets/i18n/zhtw.json +++ b/apps/web/src/assets/i18n/zhtw.json @@ -385,6 +385,8 @@ "COVER_SIZE_SMALL": "小", "COVER_SIZE_MEDIUM": "中", "COVER_SIZE_LARGE": "大", + "COVER_TITLES": "在封面下方顯示標題", + "COVER_TITLES_DESCRIPTION": "停用後,電影和影集網格會變成純海報牆:滑鼠指向或聚焦封面時才會顯示標題。直播頻道、搜尋結果和儀表板欄位仍會保留標籤。", "EPG_VIEW_MODE": "節目表檢視方式", "EPG_OFFSET_TITLE": "EPG 時間偏移", "EPG_OFFSET_HINT": "無需重新整理節目表即可調整顯示的節目時間。請輸入 -720 到 720 之間的分鐘數。", diff --git a/docs/architecture/iptvnator-ui-guidelines.md b/docs/architecture/iptvnator-ui-guidelines.md index 27b5f5e69..a07005111 100644 --- a/docs/architecture/iptvnator-ui-guidelines.md +++ b/docs/architecture/iptvnator-ui-guidelines.md @@ -234,6 +234,62 @@ minimum dimensions and flexible columns over fixed row widths. - Do not render placeholder and real logo at the same time - Keep logos contained with `object-fit: contain` +## Cover Grids + +Movie and series covers render in three surfaces: the catalog grid +(`app-grid-list`, `libs/portal/shared/ui/.../grid-list/`), the favorites / +recent card (`app-content-card`, same lib) and the dashboard rails. All of +them size from the `--cover-grid-min-width` / `--cover-rail-width` / +`--cover-gap` tokens that `Settings.coverSize` writes onto `` as +`data-cover-size` (`apps/web/src/_cover-size.scss`). + +### Posters-only wall + +`Settings.showCoverTitles` (Settings > General, default on, only an explicit +`false` opts out — coerced like `webPlayerSharedControls`) removes the title +row under VOD and series covers so the grid shows more rows per screen. + +- **Resolution.** `CoverTitlesService.postersOnly` (`libs/portal/shared/ui`) + is the single source: the opt-out AND a hover-capable pointer + (`(any-hover: hover)` media query, tracked live). On touch-only devices the + preference is ignored and titles stay under the covers, because a tap + already opens the item and there is no gesture left to peek at a hidden + name. +- **Scope.** Catalog grids (Xtream/Stalker VOD and series), unified + favorites/recent grids and the portal favorites tab. Exempt, regardless of + the setting: live channel grids (`type` `live`/`itv`/`radio` or the + `logo` variant — logos are too often missing to identify a channel), + search results and "recently added" rails (they answer by name; hosts + pass `[allowPostersOnly]="false"` to `app-content-card`; `app-grid-list` + and `app-unified-grid-tab` drop the wall themselves while their + `searchTerm` input is non-blank, i.e. an in-section search is filtering + the list), and + the dashboard rails (their meta rows do not fit an overlay). +- **Reveal.** The title is a `.cover-title-overlay` inside the poster + wrapper: bottom gradient scrim, two clamped lines, 150 ms ease-out + opacity, shown on `:hover` and `:focus-visible` of the card, none under + `prefers-reduced-motion`. It is `aria-hidden`; the card itself carries the + accessible name. +- **Pinned caption.** When the item has no cover to identify it — no + poster URL, or the image failed and the default poster / placeholder is + showing — the overlay is pinned open (`--pinned`). Both components track + failed URLs so the fallback branch re-renders instead of swapping `src` + in place. +- **Layout hints.** The grid's `contain-intrinsic-size` drops from 270 px to + 222 px (bare 2/3 poster) under `.grid-list--posters-only`, and the skeleton + hides its text lines so loading matches the cards it precedes. +- **Keyboard.** Both cards expose a `role="button"`, `tabindex="0"` surface + labelled by the title, activated by Enter and Space (Space prevents the + page scroll) and carrying a `:focus-visible` ring (`card-focus-ring` + mixin in `libs/ui/styles/_content-grid.scss`). On `app-content-card` that + surface is the inner `.content-card__activation` element, and the Remove + control (labelled by `removeTooltip`) is a SIBLING positioned over the + poster corner — an interactive control nested inside a `role="button"` + is an invalid accessibility structure. Its ring is drawn on the OUTER + `.content-card` via `:has(> .content-card__activation:focus-visible)`, + because the card's `overflow: hidden` would clip an outline on the inner + surface on every edge. Poster `alt` is the title, not a literal. + ## EPG Views The shared timeline and list still contain local dark surfaces, blue selection diff --git a/libs/portal/shared/ui/src/index.ts b/libs/portal/shared/ui/src/index.ts index f0133438e..1ad3105bb 100644 --- a/libs/portal/shared/ui/src/index.ts +++ b/libs/portal/shared/ui/src/index.ts @@ -1,6 +1,7 @@ export * from './lib/components/category-view/category-view.component'; export * from './lib/directives/infinite-scroll.directive'; export * from './lib/components/content-card/content-card.component'; +export * from './lib/cover-titles/cover-titles.service'; export * from './lib/components/content-rail-shell/content-rail-shell.component'; export * from './lib/components/favorites-layout/favorites-layout.component'; export * from './lib/components/global-favorites-list/global-favorites-list.component'; diff --git a/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.html b/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.html index a89a09cce..bd00ff363 100644 --- a/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.html +++ b/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.html @@ -1,52 +1,84 @@ -
-
- @if (showRemoveButton()) { - - } +
+ +
+
+ @if (posterUrl() && !artworkFailed()) { + + } @else if (showPlaceholder()) { +
+ {{ getPlaceholderIcon() }} +
+ } @else { + + } - @if (posterUrl()) { - - } @else if (showPlaceholder()) { -
- {{ getPlaceholderIcon() }} + @if (postersOnly()) { + + + } + + @if (type() && showTypeBadge()) { + + {{ type() }} + + } +
+ + @if (!postersOnly()) { +
+

{{ title() }}

+ @if (date()) { +

+ {{ + date() + | date: 'd MMM, HH:mm' : undefined : currentLocale() + }} +

+ }
- } @else { - - } - - @if (type() && showTypeBadge()) { - - {{ type() }} - }
-
-

{{ title() }}

- @if (date()) { -

- {{ date() | date: 'd MMM, HH:mm' : undefined : currentLocale() }} -

- } -
+ @if (showRemoveButton()) { + + }
diff --git a/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.scss b/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.scss index 6cc36dea1..00e628bd8 100644 --- a/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.scss +++ b/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.scss @@ -2,12 +2,30 @@ .content-card { @include grid.content-card; + position: relative; + + // Keyboard/pointer activation surface; the Remove button is a sibling + // absolutely positioned over the poster's corner. The focus ring is + // drawn on this outer card: `overflow: hidden` here would clip an + // outline on the inner surface on every edge. + .content-card__activation { + border-radius: inherit; + outline: none; + } + + &:has(> .content-card__activation:focus-visible) { + @include grid.focus-ring-declarations; + } + + .remove-button { + @include grid.remove-button; + } .thumbnail-wrapper { @include grid.thumbnail-wrapper; - .remove-button { - @include grid.remove-button; + .cover-title-overlay { + @include grid.cover-title-overlay; } .type-badge { @@ -19,3 +37,8 @@ @include grid.card-info; } } + +.content-card:hover .cover-title-overlay, +.content-card__activation:focus-visible .cover-title-overlay { + opacity: 1; +} diff --git a/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.spec.ts b/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.spec.ts new file mode 100644 index 000000000..7609b010c --- /dev/null +++ b/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.spec.ts @@ -0,0 +1,196 @@ +import { signal } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { TranslateService } from '@ngx-translate/core'; +import { of } from 'rxjs'; +import { SettingsStore } from '@iptvnator/services'; +import { ContentCardComponent } from './content-card.component'; + +describe('ContentCardComponent', () => { + let fixture: ComponentFixture; + let showCoverTitles: ReturnType>; + + beforeEach(async () => { + showCoverTitles = signal(false); + await TestBed.configureTestingModule({ + imports: [ContentCardComponent], + providers: [ + { provide: SettingsStore, useValue: { showCoverTitles } }, + { + provide: TranslateService, + useValue: { + onLangChange: of(), + currentLang: 'en', + defaultLang: 'en', + }, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(ContentCardComponent); + fixture.componentRef.setInput('title', 'Blade Runner'); + fixture.componentRef.setInput('posterUrl', 'blade-runner.jpg'); + fixture.componentRef.setInput('type', 'movie'); + }); + + const overlay = () => + fixture.debugElement.query(By.css('.cover-title-overlay')); + const info = () => fixture.debugElement.query(By.css('.card-info')); + + it('joins the posters-only wall by default: overlay instead of the info row', () => { + fixture.detectChanges(); + + expect(fixture.nativeElement.classList).toContain( + 'content-card--posters-only' + ); + expect(info()).toBeNull(); + expect(overlay().nativeElement.textContent.trim()).toBe( + 'Blade Runner' + ); + }); + + it('keeps the info row when the host opts the card out (search results)', () => { + fixture.componentRef.setInput('allowPostersOnly', false); + + fixture.detectChanges(); + + expect(info().nativeElement.textContent).toContain('Blade Runner'); + expect(overlay()).toBeNull(); + }); + + it.each(['live', 'radio'])( + 'keeps the info row for %s cards whose logos rarely identify them', + (type) => { + fixture.componentRef.setInput('type', type); + + fixture.detectChanges(); + + expect(info()).not.toBeNull(); + expect(overlay()).toBeNull(); + } + ); + + it('pins the overlay open for a missing or broken cover', () => { + fixture.componentRef.setInput('posterUrl', undefined); + fixture.componentRef.setInput('showPlaceholder', false); + fixture.detectChanges(); + expect(overlay().nativeElement.classList).toContain( + 'cover-title-overlay--pinned' + ); + + fixture.componentRef.setInput('posterUrl', 'broken.jpg'); + fixture.detectChanges(); + expect(overlay().nativeElement.classList).not.toContain( + 'cover-title-overlay--pinned' + ); + + fixture.debugElement + .query(By.css('.poster')) + .nativeElement.dispatchEvent(new Event('error')); + fixture.detectChanges(); + + expect( + fixture.debugElement + .query(By.css('.poster')) + .nativeElement.getAttribute('src') + ).toContain('default-poster.png'); + expect(overlay().nativeElement.classList).toContain( + 'cover-title-overlay--pinned' + ); + }); + + it('gives a new poster URL a fresh chance after a failed one', () => { + fixture.componentRef.setInput('posterUrl', 'broken.jpg'); + fixture.detectChanges(); + fixture.debugElement + .query(By.css('.poster')) + .nativeElement.dispatchEvent(new Event('error')); + fixture.detectChanges(); + + fixture.componentRef.setInput('posterUrl', 'fixed.jpg'); + fixture.detectChanges(); + + expect( + fixture.debugElement + .query(By.css('.poster')) + .nativeElement.getAttribute('src') + ).toBe('fixed.jpg'); + }); + + it('renders the info row while titles are enabled', () => { + showCoverTitles.set(true); + + fixture.detectChanges(); + + expect(fixture.nativeElement.classList).not.toContain( + 'content-card--posters-only' + ); + expect(info().nativeElement.textContent).toContain('Blade Runner'); + expect(overlay()).toBeNull(); + }); + + it('is a keyboard-activatable button named after the item', () => { + fixture.detectChanges(); + const clicked = jest.fn(); + fixture.componentInstance.cardClick.subscribe(clicked); + const card = fixture.debugElement.query( + By.css('.content-card__activation') + ); + + expect(card.nativeElement.getAttribute('role')).toBe('button'); + expect(card.nativeElement.getAttribute('tabindex')).toBe('0'); + expect(card.nativeElement.getAttribute('aria-label')).toBe( + 'Blade Runner' + ); + + const cardElement = card.nativeElement as HTMLElement; + cardElement.dispatchEvent( + new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }) + ); + const space = new KeyboardEvent('keydown', { + key: ' ', + bubbles: true, + cancelable: true, + }); + cardElement.dispatchEvent(space); + + expect(clicked).toHaveBeenCalledTimes(2); + expect(space.defaultPrevented).toBe(true); + }); + + it('keeps the Remove control outside the card button so its keys never open the item', () => { + fixture.componentRef.setInput('showRemoveButton', true); + fixture.componentRef.setInput('removeTooltip', 'Remove from favorites'); + fixture.detectChanges(); + const clicked = jest.fn(); + const removed = jest.fn(); + fixture.componentInstance.cardClick.subscribe(clicked); + fixture.componentInstance.remove.subscribe(removed); + const removeButton = fixture.debugElement.query( + By.css('.remove-button') + ).nativeElement as HTMLElement; + + // No interactive control nested inside a role="button". + expect(removeButton.closest('[role="button"]')).toBeNull(); + expect(removeButton.getAttribute('aria-label')).toBe( + 'Remove from favorites' + ); + + removeButton.dispatchEvent( + new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }) + ); + const space = new KeyboardEvent('keydown', { + key: ' ', + bubbles: true, + cancelable: true, + }); + removeButton.dispatchEvent(space); + removeButton.click(); + + // The button's own activation removes the item; the card must + // neither open it nor swallow the Space the button relies on. + expect(clicked).not.toHaveBeenCalled(); + expect(space.defaultPrevented).toBe(false); + expect(removed).toHaveBeenCalledTimes(1); + }); +}); diff --git a/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.ts b/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.ts index 6c8ae46de..3e8316ae9 100644 --- a/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.ts +++ b/libs/portal/shared/ui/src/lib/components/content-card/content-card.component.ts @@ -5,6 +5,7 @@ import { computed, inject, input, + linkedSignal, output, } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; @@ -14,6 +15,10 @@ import { MatTooltip } from '@angular/material/tooltip'; import { normalizeDateLocale } from '@iptvnator/pipes'; import { TranslateService } from '@ngx-translate/core'; import { startWith } from 'rxjs'; +import { CoverTitlesService } from '../../cover-titles/cover-titles.service'; + +/** Channel-like cards always keep their label: logos rarely identify them. */ +const LABELLED_CONTENT_TYPES: ReadonlySet = new Set(['live', 'radio']); @Component({ selector: 'app-content-card', @@ -21,10 +26,12 @@ import { startWith } from 'rxjs'; imports: [DatePipe, MatIcon, MatIconButton, MatTooltip], templateUrl: './content-card.component.html', styleUrl: './content-card.component.scss', + host: { '[class.content-card--posters-only]': 'postersOnly()' }, changeDetection: ChangeDetectionStrategy.OnPush, }) export class ContentCardComponent { private readonly translate = inject(TranslateService); + private readonly coverTitles = inject(CoverTitlesService); private readonly languageTick = toSignal( this.translate.onLangChange.pipe(startWith(null)), { initialValue: null } @@ -54,11 +61,33 @@ export class ContentCardComponent { /** Whether to render the type badge (live/movie/series) on the poster */ readonly showTypeBadge = input(true); + /** + * Whether this card may join the posters-only wall + * (`Settings.showCoverTitles === false`). Hosts whose grids answer by + * name — search results, "recently added" rails — pass false so their + * labels stay put regardless of the preference. + */ + readonly allowPostersOnly = input(true); + /** Emitted when the card is clicked */ readonly cardClick = output(); /** Emitted when the remove button is clicked */ readonly remove = output(); + /** A failed poster URL falls back per card; a new URL gets a fresh try. */ + protected readonly artworkFailed = linkedSignal({ + source: this.posterUrl, + computation: () => false, + }); + protected readonly artworkMissing = computed( + () => !this.posterUrl() || this.artworkFailed() + ); + protected readonly postersOnly = computed( + () => + this.allowPostersOnly() && + this.coverTitles.postersOnly() && + !LABELLED_CONTENT_TYPES.has(this.type() ?? '') + ); readonly currentLocale = computed(() => { this.languageTick(); return normalizeDateLocale( @@ -84,13 +113,22 @@ export class ContentCardComponent { this.cardClick.emit(); } + /** + * Enter/Space activate the card like a click; Space also prevents the + * page scroll. The Remove control is a sibling of the activation + * surface, never a descendant, so its keys cannot reach this handler. + */ + onCardKey(event: Event): void { + event.preventDefault(); + this.cardClick.emit(); + } + onRemoveClick(event: Event): void { event.stopPropagation(); this.remove.emit(); } - onImageError(event: Event): void { - (event.target as HTMLImageElement).src = - './assets/images/default-poster.png'; + onImageError(): void { + this.artworkFailed.set(true); } } diff --git a/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.scss b/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.scss index ab0852775..3a8ef2f6d 100644 --- a/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.scss +++ b/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.scss @@ -1,4 +1,5 @@ @use '../../styles/skeleton'; +@use '../../styles/content-grid' as grid; // ─── Card base ─────────────────────────────────────────────────────────────── mat-card { @@ -15,6 +16,10 @@ mat-card:hover { box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12); } +mat-card { + @include grid.card-focus-ring; +} + // ─── Grid layout ───────────────────────────────────────────────────────────── .grid-list__grid { display: grid; @@ -90,6 +95,15 @@ mat-card:hover { } } + .cover-title-overlay { + @include grid.cover-title-overlay; + } + + &:hover .cover-title-overlay, + &:focus-visible .cover-title-overlay { + opacity: 1; + } + .title { margin-top: 8px; padding: 0 2px; @@ -211,6 +225,19 @@ mat-card:hover { contain-intrinsic-size: auto 270px; } +// ─── Posters-only wall ─────────────────────────────────────────────────────── +// No title row: the hint shrinks to the bare 2/3 poster, and the skeleton +// drops its text lines so the loading state matches the cards it precedes. +:host.grid-list--posters-only { + .grid-list__grid mat-card { + contain-intrinsic-size: auto 222px; + } + + .grid-skeleton-title { + display: none; + } +} + .grid-list__tail { display: flex; align-items: center; diff --git a/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.spec.ts b/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.spec.ts index 441991135..c24e8a2be 100644 --- a/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.spec.ts +++ b/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.spec.ts @@ -311,3 +311,218 @@ describe('GridListComponent with strip country prefix enabled', () => { expect(renderedTitle()).toBe('US | Some Movie'); }); }); + +describe('GridListComponent posters-only wall', () => { + let fixture: ComponentFixture; + let showCoverTitles: ReturnType>; + + beforeEach(async () => { + showCoverTitles = signal(false); + await TestBed.configureTestingModule({ + imports: [GridListComponent], + providers: [ + { + provide: SettingsStore, + useValue: { showCoverTitles }, + }, + ], + }) + .overrideComponent(GridListComponent, { + remove: { imports: [TranslatePipe] }, + add: { + imports: [ + MockPipe( + TranslatePipe, + (value: string | null | undefined) => value ?? '' + ), + ], + }, + }) + .compileComponents(); + + fixture = TestBed.createComponent(GridListComponent); + }); + + const overlay = () => + fixture.debugElement.query(By.css('.cover-title-overlay')); + + it('replaces the title row with a hover overlay on VOD covers', () => { + fixture.componentRef.setInput('items', [ + { title: 'Blade Runner', poster_url: 'blade-runner.jpg' }, + ]); + fixture.componentRef.setInput('type', 'vod'); + + fixture.detectChanges(); + + expect(fixture.nativeElement.classList).toContain( + 'grid-list--posters-only' + ); + expect(fixture.debugElement.query(By.css('.title'))).toBeNull(); + expect(overlay().nativeElement.textContent.trim()).toBe( + 'Blade Runner' + ); + expect(overlay().nativeElement.classList).not.toContain( + 'cover-title-overlay--pinned' + ); + }); + + it('pins the overlay open when the item has no cover to identify it', () => { + fixture.componentRef.setInput('items', [{ title: 'No Poster Film' }]); + fixture.componentRef.setInput('type', 'vod'); + + fixture.detectChanges(); + + expect(overlay().nativeElement.classList).toContain( + 'cover-title-overlay--pinned' + ); + }); + + it('pins the overlay once the cover fails to load', () => { + fixture.componentRef.setInput('items', [ + { title: 'Broken Poster Film', poster_url: 'broken.jpg' }, + ]); + fixture.componentRef.setInput('type', 'vod'); + fixture.detectChanges(); + expect(overlay().nativeElement.classList).not.toContain( + 'cover-title-overlay--pinned' + ); + + fixture.debugElement + .query(By.css('.stream-icon')) + .nativeElement.dispatchEvent(new Event('error')); + fixture.detectChanges(); + + // The template re-renders the default poster, and the overlay + // stays open because that poster identifies nothing. + expect( + fixture.debugElement + .query(By.css('.stream-icon')) + .nativeElement.getAttribute('src') + ).toContain('default-poster.png'); + expect(overlay().nativeElement.classList).toContain( + 'cover-title-overlay--pinned' + ); + }); + + it('keeps the title row on live channel grids regardless of the setting', () => { + fixture.componentRef.setInput('items', [ + { name: 'CNN', stream_icon: 'cnn.png' }, + ]); + fixture.componentRef.setInput('type', 'live'); + fixture.componentRef.setInput('variant', 'logo'); + + fixture.detectChanges(); + + expect(fixture.nativeElement.classList).not.toContain( + 'grid-list--posters-only' + ); + expect( + fixture.debugElement.query(By.css('.title')).nativeElement + .textContent + ).toContain('CNN'); + expect(overlay()).toBeNull(); + }); + + it('keeps the title row while an in-section search filters the grid', () => { + fixture.componentRef.setInput('items', [ + { title: 'Blade Runner', poster_url: 'blade-runner.jpg' }, + ]); + fixture.componentRef.setInput('type', 'vod'); + fixture.componentRef.setInput('searchTerm', 'blade'); + + fixture.detectChanges(); + + expect(fixture.nativeElement.classList).not.toContain( + 'grid-list--posters-only' + ); + expect( + fixture.debugElement.query(By.css('.title')).nativeElement + .textContent + ).toContain('Blade Runner'); + expect(overlay()).toBeNull(); + + fixture.componentRef.setInput('searchTerm', ' '); + fixture.detectChanges(); + + expect(fixture.debugElement.query(By.css('.title'))).toBeNull(); + expect(overlay()).not.toBeNull(); + }); + + it('restores the title row as soon as the setting is switched back on', () => { + fixture.componentRef.setInput('items', [ + { title: 'Blade Runner', poster_url: 'blade-runner.jpg' }, + ]); + fixture.componentRef.setInput('type', 'vod'); + fixture.detectChanges(); + expect(fixture.debugElement.query(By.css('.title'))).toBeNull(); + + showCoverTitles.set(true); + fixture.detectChanges(); + + expect( + fixture.debugElement.query(By.css('.title')).nativeElement + .textContent + ).toContain('Blade Runner'); + expect(overlay()).toBeNull(); + }); +}); + +describe('GridListComponent keyboard access', () => { + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [GridListComponent], + providers: [{ provide: SettingsStore, useValue: {} }], + }) + .overrideComponent(GridListComponent, { + remove: { imports: [TranslatePipe] }, + add: { + imports: [ + MockPipe( + TranslatePipe, + (value: string | null | undefined) => value ?? '' + ), + ], + }, + }) + .compileComponents(); + + fixture = TestBed.createComponent(GridListComponent); + fixture.componentRef.setInput('items', [ + { title: 'Blade Runner', poster_url: 'blade-runner.jpg' }, + ]); + fixture.componentRef.setInput('type', 'vod'); + fixture.detectChanges(); + }); + + it('exposes each card as a focusable button named after the item', () => { + const card = fixture.debugElement.query(By.css('mat-card')) + .nativeElement as HTMLElement; + + expect(card.getAttribute('role')).toBe('button'); + expect(card.getAttribute('tabindex')).toBe('0'); + expect(card.getAttribute('aria-label')).toBe('Blade Runner'); + expect( + fixture.debugElement + .query(By.css('.stream-icon')) + .nativeElement.getAttribute('alt') + ).toBe('Blade Runner'); + }); + + it('activates a card with Enter and Space without scrolling the grid', () => { + const clicked = jest.fn(); + fixture.componentInstance.itemClicked.subscribe(clicked); + const card = fixture.debugElement.query(By.css('mat-card')); + + card.triggerEventHandler('keydown.enter', new KeyboardEvent('keydown')); + const space = new KeyboardEvent('keydown', { + key: ' ', + cancelable: true, + }); + card.triggerEventHandler('keydown.space', space); + + expect(clicked).toHaveBeenCalledTimes(2); + expect(space.defaultPrevented).toBe(true); + }); +}); diff --git a/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.ts b/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.ts index ab32cc20a..dcd42b66b 100644 --- a/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.ts +++ b/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.ts @@ -23,6 +23,7 @@ import { ProgressCapsuleComponent, WatchedBadgeComponent, } from '@iptvnator/ui/components'; +import { CoverTitlesService } from '../../cover-titles/cover-titles.service'; import { PlaylistErrorViewComponent } from '../playlist-error-view/playlist-error-view.component'; interface GridListItem { @@ -107,19 +108,26 @@ function normalizeArtworkUrl(value: string | undefined): string | undefined { } @else { @for (item of items(); track $index) { @let i = $any(item); + @let title = channelTitle(i); @let poster = resolvePoster(i); + @let artworkMissing = !poster || hasArtworkFailed(poster);
- @if (poster && !hasArtworkFailed(poster)) { + @if (!artworkMissing) { logo } @else if ( shouldRenderArtworkPlaceholder(poster) @@ -137,9 +145,25 @@ function normalizeArtworkUrl(value: string | undefined): string | undefined { class="stream-icon" src="./assets/images/default-poster.png" loading="lazy" - alt="logo" + [alt]="title" /> } + @if (postersOnly()) { + + + } @if (i.progress && i.progress > 0) { } @@ -182,11 +206,11 @@ function normalizeArtworkUrl(value: string | undefined): string | undefined { star{{ rating }}
} - -
- {{ channelTitle(i) }} -
-
+ @if (!postersOnly()) { + +
{{ title }}
+
+ }
} @empty {
@@ -250,11 +274,13 @@ function normalizeArtworkUrl(value: string | undefined): string | undefined { ProgressCapsuleComponent, WatchedBadgeComponent, ], + host: { '[class.grid-list--posters-only]': 'postersOnly()' }, changeDetection: ChangeDetectionStrategy.OnPush, }) export class GridListComponent { private readonly failedArtworkUrls = signal>(new Set()); private readonly settingsStore = inject(SettingsStore); + private readonly coverTitles = inject(CoverTitlesService); readonly items = input([]); readonly isLoading = input(false); @@ -282,6 +308,20 @@ export class GridListComponent { private readonly isLiveGrid = computed(() => ['live', 'itv', 'radio'].includes(this.type()) ); + /** + * Posters-only wall (`Settings.showCoverTitles === false`) applies to + * VOD/series covers only: channel logos are too often missing or + * generic to identify a channel without its name. A grid filtered by + * an in-section search keeps its titles too — those results are + * identified by the name the user just typed. + */ + protected readonly postersOnly = computed( + () => + this.coverTitles.postersOnly() && + !this.isLiveGrid() && + this.variant() !== 'logo' && + !this.hasActiveSearch() + ); protected readonly catchupDays = getXtreamCatchupDays; /** Catch-up badge is live-grid only; VOD/series rows never carry it. */ protected showCatchupBadge(item: GridListItem): boolean { @@ -329,23 +369,28 @@ export class GridListComponent { } } + /** Space activates like a click but must not scroll the grid. */ + protected onSpaceKey(event: Event, item: GridListItem): void { + event.preventDefault(); + this.itemClicked.emit(item); + } + + /** + * A failed poster is remembered per URL so the template re-renders the + * fallback branch (placeholder or default poster) — and the posters-only + * overlay can pin the title, since a default poster identifies nothing. + */ protected onImageError(event: Event, poster: string): void { - if (this.usesArtworkPlaceholder()) { - this.failedArtworkUrls.update((failedUrls) => { - const nextFailedUrls = new Set(failedUrls); - nextFailedUrls.add(poster); + this.failedArtworkUrls.update((failedUrls) => { + const nextFailedUrls = new Set(failedUrls); + nextFailedUrls.add(poster); - return nextFailedUrls; - }); - (event.target as HTMLImageElement | null)?.style.setProperty( - 'display', - 'none' - ); - return; - } - - (event.target as HTMLImageElement).src = - './assets/images/default-poster.png'; + return nextFailedUrls; + }); + (event.target as HTMLImageElement | null)?.style.setProperty( + 'display', + 'none' + ); } private usesArtworkPlaceholder(): boolean { diff --git a/libs/portal/shared/ui/src/lib/components/unified-collection/unified-grid-tab.component.html b/libs/portal/shared/ui/src/lib/components/unified-collection/unified-grid-tab.component.html index 73b9928ef..445e8f5de 100644 --- a/libs/portal/shared/ui/src/lib/components/unified-collection/unified-grid-tab.component.html +++ b/libs/portal/shared/ui/src/lib/components/unified-collection/unified-grid-tab.component.html @@ -20,6 +20,7 @@
@for (item of filteredItems(); track trackByUid($index, item)) { { + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [UnifiedGridTabComponent], + providers: [ + { + provide: SettingsStore, + useValue: { showCoverTitles: signal(false) }, + }, + { + provide: TranslateService, + useValue: { + onLangChange: of(), + currentLang: 'en', + defaultLang: 'en', + }, + }, + ], + }) + .overrideComponent(UnifiedGridTabComponent, { + remove: { imports: [TranslatePipe] }, + add: { + imports: [ + MockPipe( + TranslatePipe, + (value: string | null | undefined) => value ?? '' + ), + ], + }, + }) + .compileComponents(); + + fixture = TestBed.createComponent(UnifiedGridTabComponent); + fixture.componentRef.setInput('items', ITEMS); + }); + + const cardInfos = () => fixture.debugElement.queryAll(By.css('.card-info')); + const overlays = () => + fixture.debugElement.queryAll(By.css('.cover-title-overlay')); + + it('hides titles behind the overlay while nothing is being searched', () => { + fixture.detectChanges(); + + expect(cardInfos()).toHaveLength(0); + expect(overlays()).toHaveLength(2); + }); + + it('keeps titles visible while a search term filters the collection', () => { + fixture.componentRef.setInput('searchTerm', 'blade'); + fixture.detectChanges(); + + expect(cardInfos()).toHaveLength(1); + expect(cardInfos()[0].nativeElement.textContent).toContain( + 'Blade Runner' + ); + expect(overlays()).toHaveLength(0); + + fixture.componentRef.setInput('searchTerm', ' '); + fixture.detectChanges(); + + expect(cardInfos()).toHaveLength(0); + expect(overlays()).toHaveLength(2); + }); +}); diff --git a/libs/portal/shared/ui/src/lib/components/unified-collection/unified-grid-tab.component.ts b/libs/portal/shared/ui/src/lib/components/unified-collection/unified-grid-tab.component.ts index 7a73b229e..ff88648f5 100644 --- a/libs/portal/shared/ui/src/lib/components/unified-collection/unified-grid-tab.component.ts +++ b/libs/portal/shared/ui/src/lib/components/unified-collection/unified-grid-tab.component.ts @@ -29,8 +29,19 @@ export class UnifiedGridTabComponent { readonly removeItem = output(); readonly itemSelected = output(); + private readonly normalizedSearchTerm = computed(() => + this.searchTerm().trim().toLowerCase() + ); + /** + * Search results are identified by the name the user typed, so a + * filtered grid keeps its titles even under the posters-only wall. + */ + readonly hasActiveSearch = computed( + () => this.normalizedSearchTerm().length > 0 + ); + readonly filteredItems = computed(() => { - const term = this.searchTerm().trim().toLowerCase(); + const term = this.normalizedSearchTerm(); const all = this.items(); return term ? all.filter((i) => i.name.toLowerCase().includes(term)) diff --git a/libs/portal/shared/ui/src/lib/cover-titles/cover-titles.service.spec.ts b/libs/portal/shared/ui/src/lib/cover-titles/cover-titles.service.spec.ts new file mode 100644 index 000000000..73da1a4b3 --- /dev/null +++ b/libs/portal/shared/ui/src/lib/cover-titles/cover-titles.service.spec.ts @@ -0,0 +1,98 @@ +import { Injector, signal } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { SettingsStore } from '@iptvnator/services'; +import { CoverTitlesService } from './cover-titles.service'; + +type ChangeListener = (event: { matches: boolean }) => void; + +describe('CoverTitlesService', () => { + const originalMatchMedia = window.matchMedia; + let showCoverTitles: ReturnType>; + let hoverMatches: boolean; + let listeners: ChangeListener[]; + let removeListener: jest.Mock; + + beforeEach(() => { + showCoverTitles = signal(true); + hoverMatches = true; + listeners = []; + removeListener = jest.fn(); + window.matchMedia = jest.fn((query: string) => ({ + matches: query === '(any-hover: hover)' ? hoverMatches : false, + addEventListener: (_type: string, listener: ChangeListener) => { + listeners.push(listener); + }, + removeEventListener: removeListener, + })) as unknown as typeof window.matchMedia; + + TestBed.configureTestingModule({ + providers: [ + CoverTitlesService, + { provide: SettingsStore, useValue: { showCoverTitles } }, + ], + }); + }); + + afterEach(() => { + window.matchMedia = originalMatchMedia; + }); + + it('keeps titles while the setting is on or absent', () => { + const service = TestBed.inject(CoverTitlesService); + expect(service.postersOnly()).toBe(false); + + showCoverTitles.set(undefined); + expect(service.postersOnly()).toBe(false); + }); + + it('switches to the posters-only wall only on an explicit opt-out', () => { + const service = TestBed.inject(CoverTitlesService); + + showCoverTitles.set(false); + + expect(service.postersOnly()).toBe(true); + }); + + it('ignores the opt-out on touch-only devices where nothing can hover', () => { + hoverMatches = false; + const service = TestBed.inject(CoverTitlesService); + showCoverTitles.set(false); + + expect(service.postersOnly()).toBe(false); + }); + + it('reads any-hover, not hover, so a touch-first device with a mouse keeps the wall', () => { + TestBed.inject(CoverTitlesService); + + expect(window.matchMedia).toHaveBeenCalledWith('(any-hover: hover)'); + }); + + it('follows the hover capability when a pointer is attached or removed', () => { + hoverMatches = false; + const service = TestBed.inject(CoverTitlesService); + showCoverTitles.set(false); + expect(service.postersOnly()).toBe(false); + + listeners.forEach((listener) => listener({ matches: true })); + expect(service.postersOnly()).toBe(true); + + listeners.forEach((listener) => listener({ matches: false })); + expect(service.postersOnly()).toBe(false); + }); + + it('unsubscribes from the media query when its injector is destroyed', () => { + const injector = Injector.create({ + providers: [ + { provide: CoverTitlesService }, + { provide: SettingsStore, useValue: { showCoverTitles } }, + ], + parent: TestBed.inject(Injector), + }); + injector.get(CoverTitlesService); + expect(listeners).toHaveLength(1); + + (injector as unknown as { destroy(): void }).destroy(); + + expect(removeListener).toHaveBeenCalledWith('change', listeners[0]); + }); +}); diff --git a/libs/portal/shared/ui/src/lib/cover-titles/cover-titles.service.ts b/libs/portal/shared/ui/src/lib/cover-titles/cover-titles.service.ts new file mode 100644 index 000000000..797b7d7c5 --- /dev/null +++ b/libs/portal/shared/ui/src/lib/cover-titles/cover-titles.service.ts @@ -0,0 +1,61 @@ +import { + computed, + DestroyRef, + inject, + Injectable, + signal, + Signal, +} from '@angular/core'; +import { SettingsStore } from '@iptvnator/services'; + +const HOVER_CAPABLE_QUERY = '(any-hover: hover)'; + +/** + * Resolves whether cover grids should render as a posters-only wall. + * + * The wall hides the title row under every cover and reveals the title as a + * hover/focus overlay instead. That overlay needs a pointer that can hover, + * so on touch-only devices the preference is ignored and the titles stay + * under the covers — a tap already opens the item, leaving no gesture to + * peek at a hidden name. `any-hover` rather than `hover`: the latter + * describes only the PRIMARY pointer, so a touch-first tablet with a mouse + * or hover-capable stylus attached would wrongly lose the wall. + * + * Consumers still apply their own exemptions (live grids, search results) + * on top of this signal; it only answers "does the user want the wall AND + * can this device show it". + */ +@Injectable({ providedIn: 'root' }) +export class CoverTitlesService { + private readonly settingsStore = inject(SettingsStore); + private readonly hoverCapable = createHoverCapabilitySignal( + inject(DestroyRef) + ); + + /** True when covers render without a title row. */ + readonly postersOnly = computed( + () => + this.settingsStore.showCoverTitles?.() === false && + this.hoverCapable() + ); +} + +function createHoverCapabilitySignal(destroyRef: DestroyRef): Signal { + // No `matchMedia` (SSR, some test hosts) means we cannot prove a hover + // pointer exists; default to true so desktop keeps the feature in those + // environments and the touch fallback only triggers on real evidence. + if (typeof window === 'undefined' || !('matchMedia' in window)) { + return signal(true).asReadonly(); + } + + const query = window.matchMedia(HOVER_CAPABLE_QUERY); + const hoverCapable = signal(query.matches); + const onChange = (event: MediaQueryListEvent): void => { + hoverCapable.set(event.matches); + }; + + query.addEventListener('change', onChange); + destroyRef.onDestroy(() => query.removeEventListener('change', onChange)); + + return hoverCapable.asReadonly(); +} diff --git a/libs/portal/stalker/feature/src/lib/stalker-search/stalker-search.component.html b/libs/portal/stalker/feature/src/lib/stalker-search/stalker-search.component.html index 426ac5ad1..51fcbfa97 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-search/stalker-search.component.html +++ b/libs/portal/stalker/feature/src/lib/stalker-search/stalker-search.component.html @@ -56,6 +56,7 @@
@for (item of searchResults(); track item.id) { @for (item of recentlyAddedVod(); track item.xtream_id) { @for (item of recentlyAddedSeries(); track item.xtream_id) { @for (item of recentlyAddedLive(); track item.xtream_id) { @for (item of group.items; track item.id) { @for (item of xtreamStore.searchResults(); track item.id) { @for (item of visibleInPortalResults(); track item.id) { { + it('resolves every default-on flag, treating only explicit false as an opt-out', () => { + expect( + resolveDefaultOnSettings({ + webPlayerSharedControls: false, + portalConnectivityGuard: 'false' as unknown as boolean, + }) + ).toEqual({ + webPlayerSharedControls: false, + portalConnectivityGuard: true, + embeddedMpvAutoReconnect: true, + showCoverTitles: true, + }); + }); + + it('coerces only the flags an update actually carries', () => { + expect( + coerceProvidedDefaultOnSettings({ + showCoverTitles: false, + embeddedMpvAutoReconnect: undefined, + language: undefined, + }) + ).toEqual({ showCoverTitles: false }); + expect(coerceProvidedDefaultOnSettings({})).toEqual({}); + }); +}); diff --git a/libs/services/src/lib/settings-opt-out.util.ts b/libs/services/src/lib/settings-opt-out.util.ts new file mode 100644 index 000000000..bb159e011 --- /dev/null +++ b/libs/services/src/lib/settings-opt-out.util.ts @@ -0,0 +1,47 @@ +import { Settings } from '@iptvnator/shared/interfaces'; + +/** + * Boolean settings that default ON and are stored as an explicit opt-out. + * + * Absent in settings stored before the default flip means "never chose" — + * those users get the default; only an explicit `false` opts out, so junk + * such as the string `'false'` falls back to the default too. + */ +export const DEFAULT_ON_SETTINGS = [ + 'webPlayerSharedControls', + 'portalConnectivityGuard', + 'embeddedMpvAutoReconnect', + 'showCoverTitles', +] as const satisfies readonly (keyof Settings)[]; + +export type DefaultOnSetting = (typeof DEFAULT_ON_SETTINGS)[number]; + +/** Every default-on flag resolved: `true` unless stored as exactly `false`. */ +export function resolveDefaultOnSettings( + stored: Partial +): Record { + // `reduce` rather than `Object.fromEntries`: the web build lib target + // predates ES2019. + return DEFAULT_ON_SETTINGS.reduce( + (resolved, key) => { + resolved[key] = stored[key] !== false; + return resolved; + }, + {} as Record + ); +} + +/** Only the default-on flags the caller supplied, coerced the same way. */ +export function coerceProvidedDefaultOnSettings( + settings: Partial +): Partial> { + return DEFAULT_ON_SETTINGS.reduce( + (coerced, key) => { + if (settings[key] !== undefined) { + coerced[key] = settings[key] !== false; + } + return coerced; + }, + {} as Partial> + ); +} diff --git a/libs/services/src/lib/settings-store.service.spec.ts b/libs/services/src/lib/settings-store.service.spec.ts index b2ff5c20e..029d2aaca 100644 --- a/libs/services/src/lib/settings-store.service.spec.ts +++ b/libs/services/src/lib/settings-store.service.spec.ts @@ -270,6 +270,36 @@ describe('SettingsStore dashboard rail settings', () => { expect(store.getSettings().webPlayerSharedControls).toBe(false); }); + it('defaults cover titles to shown when the stored field is missing', async () => { + storedSettings = {}; + const store = injector.get(SettingsStore); + + await store.loadSettings(); + + expect(store.getSettings().showCoverTitles).toBe(true); + }); + + it('restores a persisted posters-only opt-out and persists it back', async () => { + storedSettings = { showCoverTitles: false }; + const store = injector.get(SettingsStore); + + await store.loadSettings(); + expect(store.getSettings().showCoverTitles).toBe(false); + + await store.updateSettings({ showCoverTitles: true }); + expect(store.showCoverTitles?.()).toBe(true); + expect(storage.set).toHaveBeenLastCalledWith( + STORE_KEY.Settings, + expect.objectContaining({ showCoverTitles: true }) + ); + + await store.updateSettings({ + showCoverTitles: 'false' as unknown as boolean, + }); + // Junk never opts out: only an explicit boolean false does. + expect(store.showCoverTitles?.()).toBe(true); + }); + it('defaults embedded MPV auto-reconnect to true when the stored field is missing', async () => { storedSettings = {}; const store = injector.get(SettingsStore); diff --git a/libs/services/src/lib/settings-store.service.ts b/libs/services/src/lib/settings-store.service.ts index d230cfc91..bc7ba2708 100644 --- a/libs/services/src/lib/settings-store.service.ts +++ b/libs/services/src/lib/settings-store.service.ts @@ -2,6 +2,10 @@ import { EpgSourceSettingsService, epgSourceUrlsChanged, } from './epg-source-settings.service'; +import { + coerceProvidedDefaultOnSettings, + resolveDefaultOnSettings, +} from './settings-opt-out.util'; import { computed, inject } from '@angular/core'; import { patchState, @@ -64,6 +68,7 @@ const DEFAULT_SETTINGS: Settings = { embeddedMpvAutoReconnect: true, portalConnectivityGuard: true, coverSize: 'medium', + showCoverTitles: true, epgViewMode: 'timeline', epgOffsetMinutes: 0, dashboardRails: DEFAULT_DASHBOARD_RAILS_SETTINGS, @@ -169,19 +174,7 @@ export const SettingsStore = signalStore( epgOffsetMinutes: normalizeEpgOffsetMinutes( storedSettings.epgOffsetMinutes ), - - // Absent in settings stored before the default - // flip means "never chose" — those users get the - // new default; only an explicit false opts out. - webPlayerSharedControls: - storedSettings.webPlayerSharedControls !== - false, - portalConnectivityGuard: - storedSettings.portalConnectivityGuard !== - false, - embeddedMpvAutoReconnect: - storedSettings.embeddedMpvAutoReconnect !== - false, + ...resolveDefaultOnSettings(storedSettings), dashboardRails: normalizeDashboardRailsSettings( storedSettings.dashboardRails ), @@ -222,24 +215,7 @@ export const SettingsStore = signalStore( const previousEpgUrls = store.epgUrl(); patchState(store, { ...settings, - ...(settings.webPlayerSharedControls !== undefined - ? { - webPlayerSharedControls: - settings.webPlayerSharedControls !== false, - } - : {}), - ...(settings.portalConnectivityGuard !== undefined - ? { - portalConnectivityGuard: - settings.portalConnectivityGuard !== false, - } - : {}), - ...(settings.embeddedMpvAutoReconnect !== undefined - ? { - embeddedMpvAutoReconnect: - settings.embeddedMpvAutoReconnect !== false, - } - : {}), + ...coerceProvidedDefaultOnSettings(settings), ...(settings.dashboardRails !== undefined ? { dashboardRails: normalizeDashboardRailsSettings( @@ -344,6 +320,7 @@ export const SettingsStore = signalStore( store.embeddedMpvAutoReconnect?.() !== false, coverSize: store.coverSize?.() ?? DEFAULT_SETTINGS.coverSize, + showCoverTitles: store.showCoverTitles?.() !== false, epgViewMode: store.epgViewMode?.() ?? DEFAULT_SETTINGS.epgViewMode, epgOffsetMinutes: normalizeEpgOffsetMinutes( diff --git a/libs/shared/interfaces/src/lib/settings.interface.ts b/libs/shared/interfaces/src/lib/settings.interface.ts index b70b24634..c3c01c902 100644 --- a/libs/shared/interfaces/src/lib/settings.interface.ts +++ b/libs/shared/interfaces/src/lib/settings.interface.ts @@ -241,6 +241,15 @@ export interface Settings { portalConnectivityGuard?: boolean; /** Cover/poster sizing preset applied across grids and rails */ coverSize?: CoverSize; + /** + * Titles under movie/series covers. Default on; only an explicit false + * switches VOD, series, favorites and recent grids to a posters-only + * wall whose title appears as an overlay on hover/focus. Live channel + * grids, search results and the dashboard rails always keep their + * labels: logos are often missing, a search answers by name, and rails + * carry meta rows the overlay cannot hold. + */ + showCoverTitles?: boolean; /** Live EPG panel layout: horizontal timeline (default) or vertical list */ epgViewMode?: EpgViewMode; /** diff --git a/libs/ui/styles/_content-grid.scss b/libs/ui/styles/_content-grid.scss index 8906db632..110373d9d 100644 --- a/libs/ui/styles/_content-grid.scss +++ b/libs/ui/styles/_content-grid.scss @@ -106,6 +106,67 @@ } } +// Hover/focus caption for the posters-only wall (Settings > General > +// "Show titles under covers" off). Placed inside the poster wrapper, which +// must be `position: relative` with `overflow: hidden`; the consumer reveals +// it on `:hover`/`:focus-visible` and pins it with `--pinned` whenever the +// cover cannot identify the item (default poster, placeholder). +@mixin cover-title-overlay { + position: absolute; + inset: auto 0 0 0; + box-sizing: border-box; + padding: 28px 8px 8px; + color: #fff; + font-size: 0.78rem; + font-weight: 500; + line-height: 1.3; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); + background: linear-gradient( + 180deg, + rgba(0, 0, 0, 0) 0%, + rgba(0, 0, 0, 0.72) 50%, + rgba(0, 0, 0, 0.88) 100% + ); + opacity: 0; + pointer-events: none; + // Entrance under 300ms, ease-out: the caption answers a hover, it is + // not a state change worth choreographing. + transition: opacity 150ms ease-out; + + // The clamp lives on a child: `overflow: hidden` clips at the padding + // edge, so a clamped element with its own padding paints the third + // line into that padding. + .cover-title-overlay__text { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + word-break: break-word; + } + + &.cover-title-overlay--pinned { + opacity: 1; + } + + @media (prefers-reduced-motion: reduce) { + transition: none; + } +} + +// Draw the ring on the element that owns `overflow: hidden` (or on one of +// its ancestors): an outline on a clipped descendant is cut on every edge. +@mixin focus-ring-declarations { + outline: 2px solid var(--app-selection-color, #2f7bff); + outline-offset: 3px; +} + +@mixin card-focus-ring { + &:focus-visible { + @include focus-ring-declarations; + } +} + @mixin card-info { padding: 8px 10px; From e9eca1c38674113aa5413c980563077437fe180b Mon Sep 17 00:00:00 2001 From: 4gray <4gray@users.noreply.github.com> Date: Mon, 14 Sep 2026 19:02:40 +0200 Subject: [PATCH 05/43] chore(deps): upgrade Angular to 22.1 and Nx to 23.2 (#1603) * chore(deps): upgrade Angular to 22.1 and Nx to 23.2 * fix(deps): complete Angular migrations after rebasing on master * fix(ci): use the Node pin for Windows runtime refresh * docs(deps): synchronize the workspace-shell Node requirements --- .changes/deps-angular-22-nx-23.md | 6 + .github/workflows/build-and-make.yaml | 4 +- .github/workflows/ci.yml | 4 +- .github/workflows/deploy-website.yml | 2 +- .github/workflows/e2e-tests.yaml | 4 +- .../refresh-windows-embedded-mpv-runtime.yaml | 2 +- .nvmrc | 1 + AGENTS.md | 17 +- CLAUDE.md | 17 +- README.md | 9 +- apps/electron-backend-e2e/tsconfig.json | 3 +- apps/electron-backend/tsconfig.json | 3 +- apps/remote-control-web/src/app/app.config.ts | 4 +- apps/remote-control-web/src/app/app.ts | 9 +- apps/remote-control-web/src/test-setup.ts | 7 +- apps/remote-control-web/tsconfig.app.json | 10 +- apps/remote-control-web/tsconfig.json | 3 +- apps/stalker-mock-server/tsconfig.json | 5 +- apps/web-backend/project.json | 21 +- apps/web-backend/tsconfig.json | 3 +- apps/web-e2e/src/basic.e2e.ts | 171 +- apps/web-e2e/src/dash-clearkey.e2e.ts | 24 + apps/web-e2e/tsconfig.json | 3 +- apps/web/jest.config.ts | 2 +- ...app-update-notification-panel.component.ts | 3 + apps/web/src/app/app.component.ts | 3 + apps/web/src/app/app.config.ts | 3 +- ...p-update-release-notes-dialog.component.ts | 16 +- .../settings-about-section.component.ts | 9 +- .../settings-backup-section.component.ts | 10 +- .../settings-dashboard-section.component.ts | 9 +- ...s-delete-all-playlists-dialog.component.ts | 9 +- .../settings-epg-section.component.ts | 10 +- .../settings-general-section.component.ts | 10 +- .../settings-playback-section.component.ts | 10 +- ...ttings-remote-control-section.component.ts | 10 +- .../settings-reset-section.component.ts | 10 +- .../settings-tmdb-section.component.ts | 3 + ...ttings-unsaved-changes-dialog.component.ts | 4 +- .../settings/settings.component.form.spec.ts | 4 + .../app/settings/settings.component.spec.ts | 12 +- .../src/app/settings/settings.component.ts | 3 + apps/web/src/test-setup.ts | 7 +- apps/web/tsconfig.app.json | 10 +- apps/web/tsconfig.json | 3 +- apps/website/tsconfig.json | 16 +- apps/xtream-mock-server/tsconfig.json | 5 +- apps/xtream-mock-server/tsconfig.spec.json | 3 +- docker/Dockerfile | 2 +- docs/architecture/nx-workspace-boundaries.md | 75 +- docs/architecture/workspace-shell.md | 53 +- jest.web-esm.workspace.ts | 2 +- libs/epg/data-access/src/test-setup.ts | 7 +- libs/epg/data-access/tsconfig.json | 3 +- libs/m3u-state/src/test-setup.ts | 7 +- libs/m3u-state/tsconfig.json | 3 +- libs/playback/util/tsconfig.json | 3 +- .../add-playlist-dialog.component.ts | 7 +- .../lib/auto-import/auto-import.component.ts | 25 +- .../lib/file-upload/file-upload.component.ts | 10 +- .../stalker-portal-import.component.ts | 9 +- .../lib/text-import/text-import.component.ts | 8 +- .../lib/url-upload/url-upload.component.ts | 8 +- .../xtream-code-import.component.ts | 9 +- .../playlist/import/feature/src/test-setup.ts | 7 +- libs/playlist/import/feature/tsconfig.json | 3 +- .../m3u-vod-detail.component.ts | 2 + .../video-player/video-player.component.html | 4 +- .../video-player/video-player.component.ts | 2 + .../empty-state/empty-state.component.ts | 13 +- .../playlist-info.component.spec.ts | 12 +- .../playlist-info/playlist-info.component.ts | 9 +- .../playlist-item/playlist-item.component.ts | 2 + .../source-cleanup-dialog.component.ts | 19 +- .../source-health-indicator.component.ts | 2 + libs/playlist/shared/ui/src/test-setup.ts | 7 +- libs/playlist/shared/ui/tsconfig.json | 3 +- libs/playlist/shared/util/src/test-setup.ts | 7 +- libs/playlist/shared/util/tsconfig.json | 3 +- .../category-content-view.component.spec.ts | 11 +- libs/portal/catalog/feature/src/test-setup.ts | 7 +- libs/portal/catalog/feature/tsconfig.json | 3 +- .../download-offline-detail.component.html | 21 +- .../downloads/feature/src/test-setup.ts | 7 +- libs/portal/downloads/feature/tsconfig.json | 3 +- .../shared/data-access/src/test-setup.ts | 7 +- libs/portal/shared/data-access/tsconfig.json | 3 +- .../favorites-layout.component.ts | 9 +- .../playlist-error-view.component.ts | 8 +- .../search-form/search-form.component.ts | 2 + .../infinite-scroll.directive.spec.ts | 3 +- .../navigation/portal-rail-links.component.ts | 17 +- libs/portal/shared/ui/src/test-setup.ts | 7 +- libs/portal/shared/ui/tsconfig.json | 3 +- libs/portal/shared/util/src/test-setup.ts | 7 +- libs/portal/shared/util/tsconfig.json | 3 +- .../stalker/data-access/src/test-setup.ts | 7 +- libs/portal/stalker/data-access/tsconfig.json | 3 +- .../stalker-catalog-detail.component.spec.ts | 20 +- .../stalker-catalog-detail.component.ts | 2 + ...talker-collection-detail.component.spec.ts | 18 +- .../stalker-favorites-button.component.ts | 8 +- .../stalker-inline-detail.component.spec.ts | 19 +- ...alker-live-stream-layout.component.spec.ts | 4 +- .../stalker-live-stream-layout.spec-stubs.ts | 13 +- .../stalker-search.component.spec.ts | 9 +- .../stalker-series-view.component.html | 6 +- .../stalker-series-view.component.spec.ts | 65 +- .../stalker-series-view.component.ts | 2 + .../stalker-series-view.test-helpers.ts | 70 + libs/portal/stalker/feature/src/test-setup.ts | 7 +- libs/portal/stalker/feature/tsconfig.json | 3 +- .../xtream/data-access/src/test-setup.ts | 7 +- libs/portal/xtream/data-access/tsconfig.json | 3 +- .../global-search-results.component.ts | 3 +- .../live-stream-layout-stubs.spec-data.ts | 13 +- .../live-stream-layout.sidebar-levels.spec.ts | 19 +- ...am-live-channel-navigation.service.spec.ts | 9 +- .../portal-channels-list.component.spec.ts | 6 +- .../serial-details.component.spec.ts | 19 +- .../serial-details.component.ts | 2 + .../vod-details-route.actions.spec.ts | 9 +- .../lib/xtream-content-gate.component.spec.ts | 27 +- libs/portal/xtream/feature/src/test-setup.ts | 7 +- libs/portal/xtream/feature/tsconfig.json | 3 +- libs/services/src/test-setup.ts | 7 +- libs/services/tsconfig.json | 3 +- libs/shared/database/tsconfig.json | 3 +- libs/shared/host-health/tsconfig.json | 3 +- libs/shared/interfaces/package.json | 2 +- libs/shared/interfaces/tsconfig.json | 3 +- libs/shared/logging/tsconfig.json | 3 +- libs/shared/m3u-utils/tsconfig.json | 3 +- libs/shared/marketing-fixtures/tsconfig.json | 3 +- .../all-channels-view.component.html | 5 +- .../favorites-view.component.html | 5 +- .../groups-view/groups-view.component.html | 6 +- .../confirm-dialog.component.ts | 4 +- .../content-hero/content-hero.component.ts | 3 + .../expandable-text.component.ts | 3 + .../content-about.component.ts | 9 +- .../portal-detail-shell.component.ts | 3 + .../progress-capsule.component.ts | 9 +- .../episode-info-dialog.component.ts | 8 +- .../watched-badge/watched-badge.component.ts | 4 +- libs/ui/components/src/test-setup.ts | 7 +- libs/ui/components/tsconfig.json | 3 +- .../epg-item-description.component.ts | 8 +- .../epg-progress-panel.component.ts | 10 +- .../epg-source-status.component.ts | 17 +- libs/ui/epg/tsconfig.json | 3 +- libs/ui/pipes/src/test-setup.ts | 7 +- libs/ui/pipes/tsconfig.json | 3 +- .../lib/art-player/art-player.component.ts | 2 + .../audio-player/audio-player.component.ts | 7 +- .../embedded-mpv-player.component.html | 6 +- .../external-player-info-dialog.component.ts | 3 +- .../html-video-player.component.ts | 2 + .../video-player/sidebar/sidebar.component.ts | 10 +- .../lib/vjs-player/vjs-player.component.ts | 2 + .../lib/vod-details/vod-details.component.ts | 2 + .../web-player-shared-controls.ts | 10 + .../web-player-view.component.ts | 10 +- libs/ui/playback/tsconfig.json | 3 +- .../remote-control.component.html | 6 +- .../remote-control.component.ts | 10 +- libs/ui/remote-control/src/test-setup.ts | 7 +- libs/ui/remote-control/tsconfig.json | 3 +- libs/ui/shared-portals/src/test-setup.ts | 7 +- libs/ui/shared-portals/tsconfig.json | 55 +- .../dashboard/data-access/src/test-setup.ts | 7 +- .../dashboard/data-access/tsconfig.json | 3 +- .../dashboard/feature/src/test-setup.ts | 7 +- .../workspace/dashboard/feature/tsconfig.json | 3 +- .../workspace-command-palette.component.ts | 2 + ...pace-collection-context-panel.component.ts | 17 +- .../workspace-context-panel.component.ts | 2 + ...kspace-settings-context-panel.component.ts | 12 +- ...ace-keyboard-shortcuts-dialog.component.ts | 3 +- ...ce-live-categories-popover.service.spec.ts | 7 +- ...ce-shell-context-sidebar.component.spec.ts | 16 +- .../workspace-shell-header.component.spec.ts | 8 +- .../workspace-shell-rail.component.spec.ts | 7 +- .../workspace-shell.component.spec.ts | 15 +- .../workspace-shell.component.ts | 12 +- .../workspace-sources.component.spec.ts | 17 +- .../workspace-sources.component.ts | 8 +- .../workspace/shell/feature/src/test-setup.ts | 7 +- libs/workspace/shell/feature/tsconfig.json | 3 +- libs/workspace/shell/util/src/test-setup.ts | 7 +- libs/workspace/shell/util/tsconfig.json | 3 +- package.json | 123 +- patches/nx-electron@22.0.0.patch | 12 + patches/vite@7.3.6.patch | 54 - patches/vite@8.1.5.patch | 46 + pnpm-lock.yaml | 8304 ++++------------- pnpm-workspace.yaml | 30 +- .../vite-transform-filter.test.mjs | 15 +- .../electron-package-identity.test.mjs | 11 + tsconfig.base.json | 6 +- 200 files changed, 3515 insertions(+), 6890 deletions(-) create mode 100644 .changes/deps-angular-22-nx-23.md create mode 100644 .nvmrc create mode 100644 libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.test-helpers.ts create mode 100644 libs/ui/playback/src/lib/web-player-view/web-player-shared-controls.ts delete mode 100644 patches/vite@7.3.6.patch create mode 100644 patches/vite@8.1.5.patch diff --git a/.changes/deps-angular-22-nx-23.md b/.changes/deps-angular-22-nx-23.md new file mode 100644 index 000000000..586a84061 --- /dev/null +++ b/.changes/deps-angular-22-nx-23.md @@ -0,0 +1,6 @@ +--- +type: internal +area: deps +--- + +The application uses Angular 22 and Nx 23 with updated build and test tooling. diff --git a/.github/workflows/build-and-make.yaml b/.github/workflows/build-and-make.yaml index fc7e8bb27..1cd46c20d 100644 --- a/.github/workflows/build-and-make.yaml +++ b/.github/workflows/build-and-make.yaml @@ -61,7 +61,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v7 with: - node-version: '22' + node-version-file: '.nvmrc' - name: Resolve Linux runtime toolchain cache key id: linux-runtime-cache-key @@ -379,7 +379,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v7 with: - node-version: '22' + node-version-file: '.nvmrc' cache: 'pnpm' - name: Install Linux system dependencies diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b0434924..db7634ba9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,7 +103,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v7 with: - node-version: '22' + node-version-file: '.nvmrc' cache: 'pnpm' - name: Install dependencies @@ -141,7 +141,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v7 with: - node-version: '22' + node-version-file: '.nvmrc' cache: 'pnpm' - name: Install dependencies diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml index f7e1b2c52..a8cf6f076 100644 --- a/.github/workflows/deploy-website.yml +++ b/.github/workflows/deploy-website.yml @@ -32,7 +32,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v7 with: - node-version: '22' + node-version-file: '.nvmrc' cache: 'pnpm' - name: Install dependencies diff --git a/.github/workflows/e2e-tests.yaml b/.github/workflows/e2e-tests.yaml index b2f4e2582..5879a477f 100644 --- a/.github/workflows/e2e-tests.yaml +++ b/.github/workflows/e2e-tests.yaml @@ -59,7 +59,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v7 with: - node-version: '22' + node-version-file: '.nvmrc' cache: 'pnpm' - name: Install Dependencies @@ -113,7 +113,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v7 with: - node-version: '22' + node-version-file: '.nvmrc' cache: 'pnpm' - name: Install Dependencies diff --git a/.github/workflows/refresh-windows-embedded-mpv-runtime.yaml b/.github/workflows/refresh-windows-embedded-mpv-runtime.yaml index fafdab266..a944acea0 100644 --- a/.github/workflows/refresh-windows-embedded-mpv-runtime.yaml +++ b/.github/workflows/refresh-windows-embedded-mpv-runtime.yaml @@ -35,7 +35,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 with: - node-version: '22' + node-version-file: '.nvmrc' - name: Refresh pin when it approaches upstream retention id: refresh diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 000000000..c94711948 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22.23.2 diff --git a/AGENTS.md b/AGENTS.md index 504ea352c..c8b5c0de4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,10 +19,12 @@ This file provides guidance to coding agents working in this repository. - See `docs/architecture/nx-workspace-boundaries.md` for the current Nx tag and alias policy. - Keep `nx` and every official `@nx/*` package on the same exact version; run `pnpm run deps:nx:validate` after dependency updates. -- Vite `7.3.6`, resolved through Angular's build tooling, is patched with - bounded transform prefilters and the upstream precise matchers in - `patches/vite@7.3.6.patch`. Keep the patch until supported Angular tooling - resolves a Vite version containing the fix, and run `pnpm run deps:vite:test` +- Use the Node version in `.nvmrc` for development and CI. Angular 22 requires + Node `^22.22.3 || ^24.15.0` and TypeScript `>=6.0 <6.1` in this workspace. +- Vite `8.1.5`, resolved through Angular's build tooling, retains upstream + precise matchers and adds bounded raw-code prefilters through + `patches/vite@8.1.5.patch`. Keep the patch until upstream also preserves + comment-bearing asset/worker expressions; run `pnpm run deps:vite:test` after related dependency updates. - `app-builder-lib` `26.15.7` (electron-builder's macOS signing) is patched in `patches/app-builder-lib@26.15.7.patch` with the upstream backport @@ -30,11 +32,16 @@ This file provides guidance to coding agents working in this repository. must receive the temporary keychain's own password, not the `.p12` import password. macOS runner images since `macos-26-arm64` 20260831 verify that password, and `Build on macos arm64` failed with `SecKeychainUnlock: The user -name or passphrase you entered is not correct`. Keep the patch until + name or passphrase you entered is not correct`. Keep the patch until electron-builder resolves an `app-builder-lib` containing the fix (26.16.1+), and run `pnpm run deps:electron-builder:test` after related dependency updates — the test fails when the patched version no longer matches the installed one. +- `nx-electron@22.0.0` uses a local Nx 23 export-path patch and an explicit + `webpack-node-externals` package extension. Scoped peer allowances for it + and `ngx-indexed-db@22.0.0` live in `pnpm-workspace.yaml`; they are project + compatibility bridges, not upstream support declarations. See + `docs/architecture/nx-workspace-boundaries.md` before removing them. - A directory holding files consumed by other projects must be an Nx project. Nx builds its graph from TypeScript imports only, so a relative SCSS `@use` across project roots creates no edge and the imported file lands in no task diff --git a/CLAUDE.md b/CLAUDE.md index 77bdba3c8..1acd3a9b9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -97,10 +97,12 @@ pnpm nx show projects - See `docs/architecture/nx-workspace-boundaries.md` for the current Nx tag and alias policy. - Keep `nx` and every official `@nx/*` package on the same exact version; run `pnpm run deps:nx:validate` after dependency updates. -- Vite `7.3.6`, resolved through Angular's build tooling, is patched with - bounded transform prefilters and the upstream precise matchers in - `patches/vite@7.3.6.patch`. Keep the patch until supported Angular tooling - resolves a Vite version containing the fix, and run `pnpm run deps:vite:test` +- Use the Node version in `.nvmrc` for development and CI. Angular 22 requires + Node `^22.22.3 || ^24.15.0` and TypeScript `>=6.0 <6.1` in this workspace. +- Vite `8.1.5`, resolved through Angular's build tooling, retains upstream + precise matchers and adds bounded raw-code prefilters through + `patches/vite@8.1.5.patch`. Keep the patch until upstream also preserves + comment-bearing asset/worker expressions; run `pnpm run deps:vite:test` after related dependency updates. - `app-builder-lib` `26.15.7` (electron-builder's macOS signing) is patched in `patches/app-builder-lib@26.15.7.patch` with the upstream backport @@ -108,11 +110,16 @@ pnpm nx show projects must receive the temporary keychain's own password, not the `.p12` import password. macOS runner images since `macos-26-arm64` 20260831 verify that password, and `Build on macos arm64` failed with `SecKeychainUnlock: The user -name or passphrase you entered is not correct`. Keep the patch until + name or passphrase you entered is not correct`. Keep the patch until electron-builder resolves an `app-builder-lib` containing the fix (26.16.1+), and run `pnpm run deps:electron-builder:test` after related dependency updates — the test fails when the patched version no longer matches the installed one. +- `nx-electron@22.0.0` uses a local Nx 23 export-path patch and an explicit + `webpack-node-externals` package extension. Scoped peer allowances for it + and `ngx-indexed-db@22.0.0` live in `pnpm-workspace.yaml`; they are project + compatibility bridges, not upstream support declarations. See + `docs/architecture/nx-workspace-boundaries.md` before removing them. - A directory holding files consumed by other projects must be an Nx project. Nx builds its graph from TypeScript imports only, so a relative SCSS `@use` across project roots creates no edge and the imported file lands in no task diff --git a/README.md b/README.md index d593d19b6..89869f174 100644 --- a/README.md +++ b/README.md @@ -272,11 +272,18 @@ needed. Requirements: -- Node.js 22.13–22.x or 24 and newer with pnpm (via Corepack) +- Node.js 22.22.3 or newer within 22.x, or 24.15.0 or newer within 24.x +- pnpm 10.33.0 (via Corepack) + +The repository's `.nvmrc` pins the Node.js version used by CI. With nvm, +run `nvm install` and `nvm use` from the repository root to use the same version +for local development. 1. Clone this repository and install project dependencies: ``` + $ nvm install + $ nvm use $ corepack enable $ pnpm install ``` diff --git a/apps/electron-backend-e2e/tsconfig.json b/apps/electron-backend-e2e/tsconfig.json index dcc6815b6..05ce18d2b 100644 --- a/apps/electron-backend-e2e/tsconfig.json +++ b/apps/electron-backend-e2e/tsconfig.json @@ -9,7 +9,8 @@ "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + "ignoreDeprecations": "6.0" }, "include": [ "**/*.ts", diff --git a/apps/electron-backend/tsconfig.json b/apps/electron-backend/tsconfig.json index 16f3e16fd..522574b2b 100644 --- a/apps/electron-backend/tsconfig.json +++ b/apps/electron-backend/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "types": ["node"], "moduleResolution": "nodenext", - "module": "NodeNext" + "module": "NodeNext", + "ignoreDeprecations": "6.0" }, "include": ["**/*.ts"] } diff --git a/apps/remote-control-web/src/app/app.config.ts b/apps/remote-control-web/src/app/app.config.ts index ad40068af..a8bd09a77 100644 --- a/apps/remote-control-web/src/app/app.config.ts +++ b/apps/remote-control-web/src/app/app.config.ts @@ -4,7 +4,7 @@ import { provideBrowserGlobalErrorListeners, provideZoneChangeDetection, } from '@angular/core'; -import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClient, withXhr } from '@angular/common/http'; import { TranslateModule, TranslateLoader } from '@ngx-translate/core'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; import { HttpClient } from '@angular/common/http'; @@ -17,7 +17,7 @@ export const appConfig: ApplicationConfig = { providers: [ provideBrowserGlobalErrorListeners(), provideZoneChangeDetection({ eventCoalescing: true }), - provideHttpClient(), + provideHttpClient(withXhr()), importProvidersFrom( TranslateModule.forRoot({ defaultLanguage: 'en', diff --git a/apps/remote-control-web/src/app/app.ts b/apps/remote-control-web/src/app/app.ts index c5cf9f9de..0e688e292 100644 --- a/apps/remote-control-web/src/app/app.ts +++ b/apps/remote-control-web/src/app/app.ts @@ -1,4 +1,9 @@ -import { Component, OnInit, inject } from '@angular/core'; +import { + Component, + OnInit, + inject, + ChangeDetectionStrategy, +} from '@angular/core'; import { RemoteControlComponent } from '@iptvnator/ui/remote-control'; import { TranslateService } from '@ngx-translate/core'; @@ -6,6 +11,8 @@ import { TranslateService } from '@ngx-translate/core'; imports: [RemoteControlComponent], selector: 'app-root', templateUrl: './app.html', + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styleUrl: './app.scss', }) export class App implements OnInit { diff --git a/apps/remote-control-web/src/test-setup.ts b/apps/remote-control-web/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/apps/remote-control-web/src/test-setup.ts +++ b/apps/remote-control-web/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/apps/remote-control-web/tsconfig.app.json b/apps/remote-control-web/tsconfig.app.json index 231df7c01..4e79fa9b6 100644 --- a/apps/remote-control-web/tsconfig.app.json +++ b/apps/remote-control-web/tsconfig.app.json @@ -10,5 +10,13 @@ "src/test-setup.ts", "src/**/*.test.ts", "src/**/*.spec.ts" - ] + ], + "angularCompilerOptions": { + "extendedDiagnostics": { + "checks": { + "nullishCoalescingNotNullable": "suppress", + "optionalChainNotNullable": "suppress" + } + } + } } diff --git a/apps/remote-control-web/tsconfig.json b/apps/remote-control-web/tsconfig.json index b81a1f7df..d1bb37e85 100644 --- a/apps/remote-control-web/tsconfig.json +++ b/apps/remote-control-web/tsconfig.json @@ -10,7 +10,8 @@ "moduleResolution": "bundler", "isolatedModules": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/apps/stalker-mock-server/tsconfig.json b/apps/stalker-mock-server/tsconfig.json index 2406aa3d0..11ab3479f 100644 --- a/apps/stalker-mock-server/tsconfig.json +++ b/apps/stalker-mock-server/tsconfig.json @@ -9,7 +9,10 @@ "strict": true, "esModuleInterop": true, "skipLibCheck": true, - "resolveJsonModule": true + "resolveJsonModule": true, + "noUncheckedSideEffectImports": false, + "types": ["*"], + "ignoreDeprecations": "6.0" }, "include": ["src/**/*.ts"], "exclude": ["node_modules", "dist"] diff --git a/apps/web-backend/project.json b/apps/web-backend/project.json index 075b3c30f..b637b2e89 100644 --- a/apps/web-backend/project.json +++ b/apps/web-backend/project.json @@ -18,7 +18,26 @@ "bundle": true, "thirdParty": true, "generatePackageJson": true - } + }, + "inputs": [ + "default", + "^default", + "{workspaceRoot}/pnpm-workspace.yaml", + { + "json": "{workspaceRoot}/package.json", + "fields": [ + "packageManager", + "pnpm.onlyBuiltDependencies", + "pnpm.neverBuiltDependencies", + "pnpm.allowBuilds", + "pnpm.supportedArchitectures", + "pnpm.patchedDependencies" + ] + }, + { + "runtime": "node -e \"try{console.log('pnpm major '+require('child_process').execSync('pnpm --version',{stdio:['ignore','pipe','ignore']}).toString().trim().split('.')[0])}catch{console.log('pnpm major unavailable')}\"" + } + ] }, "serve": { "continuous": true, diff --git a/apps/web-backend/tsconfig.json b/apps/web-backend/tsconfig.json index 357916b0d..4b6d15081 100644 --- a/apps/web-backend/tsconfig.json +++ b/apps/web-backend/tsconfig.json @@ -8,7 +8,8 @@ "lib": ["ES2022"], "strict": true, "esModuleInterop": true, - "skipLibCheck": true + "skipLibCheck": true, + "ignoreDeprecations": "6.0" }, "include": ["src/**/*.ts"] } diff --git a/apps/web-e2e/src/basic.e2e.ts b/apps/web-e2e/src/basic.e2e.ts index c8f63bff6..2b8d08b66 100644 --- a/apps/web-e2e/src/basic.e2e.ts +++ b/apps/web-e2e/src/basic.e2e.ts @@ -4,6 +4,12 @@ import { expect, test } from './fixtures'; const fixturePath = join(__dirname, 'fixtures/test.m3u'); test('@web @m3u basic playlist import flow', async ({ page }) => { + // The fixture's placeholder media hosts must not delay navigation or make + // persistence coverage depend on external DNS and asset availability. + await page.route( + /^https?:\/\/(?:channel\.icons\.url|example\.channels|xml-url)\//, + (route) => route.abort() + ); await page.goto('/'); // Basic checks @@ -29,6 +35,15 @@ test('@web @m3u basic playlist import flow', async ({ page }) => { await expect(page.getByText('4 channels')).toBeVisible(); await expect(page.getByText('1. Channel 1')).toBeVisible(); await expect(page.getByText('4. HappyKids TV')).toBeVisible(); + + const importedPlaylistUrl = page.url(); + await page.reload({ waitUntil: 'domcontentloaded' }); + + await expect(page).toHaveURL(importedPlaylistUrl); + await expect(page.getByText('test', { exact: true })).toBeVisible(); + await expect(page.getByText('4 channels')).toBeVisible(); + await expect(page.getByText('1. Channel 1')).toBeVisible(); + await expect(page.getByText('4. HappyKids TV')).toBeVisible(); }); test('@web @auto-detect pasted provider message prefills the Xtream form', async ({ @@ -110,7 +125,161 @@ test('@web @auto-detect keeps the pasted message when switching methods', async ); }); -test('@web keyboard shortcuts help opens from question mark', async ({ page }) => { +test('@web @m3u reads an existing v1 IndexedDB playlist after application upgrade', async ({ + page, +}) => { + const playlistId = 'legacy-indexeddb-playlist'; + // Seed before Angular boots so the application must open the old database. + await page.route('**/__indexeddb-seed__', (route) => + route.fulfill({ contentType: 'text/html', body: '' }) + ); + await page.route('https://legacy.example.invalid/**', (route) => + route.abort() + ); + await page.goto('/__indexeddb-seed__'); + await page.evaluate( + (id) => + new Promise((resolve, reject) => { + const request = indexedDB.open('iptvnator', 1); + request.onerror = () => reject(request.error); + request.onupgradeneeded = () => { + const store = request.result.createObjectStore( + 'playlists', + { + keyPath: '_id', + autoIncrement: false, + } + ); + // Freeze the historical schema independently of app config. + for (const name of [ + '_id', + 'filename', + 'title', + 'count', + 'playlist', + 'importDate', + 'lastUsage', + 'favorites', + 'recentlyViewed', + 'autoRefresh', + 'url', + 'filePath', + ]) { + store.createIndex(name, name, { unique: false }); + } + }; + request.onsuccess = () => { + const db = request.result; + const transaction = db.transaction( + 'playlists', + 'readwrite' + ); + transaction.onabort = () => { + db.close(); + reject(transaction.error); + }; + transaction.oncomplete = () => { + db.close(); + resolve(); + }; + transaction.objectStore('playlists').add({ + _id: id, + title: 'Legacy playlist', + filename: 'legacy.m3u', + importDate: '2025-01-01T00:00:00.000Z', + lastUsage: '2025-01-01T00:00:00.000Z', + count: 1, + autoRefresh: false, + favorites: [], + recentlyViewed: [], + playlist: { + header: { attrs: {}, raw: '#EXTM3U' }, + items: [ + { + id: 'legacy-channel', + name: 'Legacy channel', + url: 'https://legacy.example.invalid/live.m3u8', + group: { title: 'News' }, + tvg: { + id: '', + name: '', + url: '', + logo: '', + rec: '', + }, + http: { + referrer: '', + 'user-agent': '', + origin: '', + }, + radio: 'false', + }, + ], + }, + }); + }; + }), + playlistId + ); + + const playlistPath = `/workspace/playlists/${playlistId}/all`; + await page.goto(playlistPath, { waitUntil: 'domcontentloaded' }); + await expect( + page.getByText('Legacy playlist', { exact: true }) + ).toBeVisible(); + await expect( + page.getByText('1. Legacy channel', { exact: true }) + ).toBeVisible(); + + await page.reload({ waitUntil: 'domcontentloaded' }); + await expect(page).toHaveURL(new RegExp(`${playlistPath}$`)); + await expect( + page.getByText('Legacy playlist', { exact: true }) + ).toBeVisible(); + await expect( + page.getByText('1. Legacy channel', { exact: true }) + ).toBeVisible(); + + const persisted = await page.evaluate( + (id) => + new Promise((resolve, reject) => { + const request = indexedDB.open('iptvnator'); + request.onerror = () => reject(request.error); + request.onsuccess = () => { + const db = request.result; + const transaction = db.transaction('playlists', 'readonly'); + const store = transaction.objectStore('playlists'); + const record = store.get(id); + transaction.onabort = () => { + db.close(); + reject(transaction.error); + }; + transaction.oncomplete = () => { + resolve({ + version: db.version, + keyPath: store.keyPath, + autoIncrement: store.autoIncrement, + id: record.result?._id, + channel: record.result?.playlist.items[0].name, + }); + db.close(); + }; + }; + }), + playlistId + ); + expect(persisted).toEqual({ + version: 1, + keyPath: '_id', + autoIncrement: false, + id: playlistId, + channel: 'Legacy channel', + }); +}); + +test('@web keyboard shortcuts help opens from question mark', async ({ + page, +}) => { await page.goto('/'); await page.getByRole('button', { name: 'Open keyboard shortcuts' }).focus(); diff --git a/apps/web-e2e/src/dash-clearkey.e2e.ts b/apps/web-e2e/src/dash-clearkey.e2e.ts index 59330584b..854974976 100644 --- a/apps/web-e2e/src/dash-clearkey.e2e.ts +++ b/apps/web-e2e/src/dash-clearkey.e2e.ts @@ -66,6 +66,18 @@ test.skip( 'DASH ClearKey coverage targets Chromium' ); +test.beforeEach(async ({ page }) => { + await page.addInitScript(() => { + const play = HTMLMediaElement.prototype.play; + HTMLMediaElement.prototype.play = function () { + // Keep real decoding while avoiding the host's audio-output clock. + // Muting must happen before play, including replacement players. + this.muted = true; + return play.call(this); + }; + }); +}); + async function serveDashFixtures(page: Page): Promise { await page.route(`${FIXTURE_HOST}/**`, async (route) => { const url = new URL(route.request().url()); @@ -150,7 +162,19 @@ test('@web @m3u @dash ClearKey and clear DASH channels play inline', async ({ await page.getByText('1. ClearKey DASH').click(); await expectVideoPlaying(page); + const clearKeyVideo = await page + .locator('app-web-player-view video') + .first() + .elementHandle(); + if (!clearKeyVideo) { + throw new Error('Missing playing ClearKey video'); + } await page.getByText('2. Clear DASH').click(); + // The new source mounts asynchronously; never accept the old video's time. + await page.waitForFunction( + (previous) => !previous.isConnected, + clearKeyVideo + ); await expectVideoPlaying(page); }); diff --git a/apps/web-e2e/tsconfig.json b/apps/web-e2e/tsconfig.json index d35c2908f..28a4fb775 100644 --- a/apps/web-e2e/tsconfig.json +++ b/apps/web-e2e/tsconfig.json @@ -9,7 +9,8 @@ "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + "ignoreDeprecations": "6.0" }, "include": [ "**/*.ts", diff --git a/apps/web/jest.config.ts b/apps/web/jest.config.ts index d218a0165..15277199e 100644 --- a/apps/web/jest.config.ts +++ b/apps/web/jest.config.ts @@ -1,4 +1,4 @@ -import { nxPreset } from '@nx/jest/preset.js'; +import { nxPreset } from '@nx/jest/preset'; import { createEsmPreset } from 'jest-preset-angular/presets/index.js'; const angularEsmPreset = createEsmPreset({ diff --git a/apps/web/src/app/app-update-notification-panel.component.ts b/apps/web/src/app/app-update-notification-panel.component.ts index 6045d01dc..d10b02117 100644 --- a/apps/web/src/app/app-update-notification-panel.component.ts +++ b/apps/web/src/app/app-update-notification-panel.component.ts @@ -5,6 +5,7 @@ import { OnInit, signal, inject, + ChangeDetectionStrategy, } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; @@ -101,6 +102,8 @@ import { AppUpdateReleaseNotesDialogComponent } from './settings/app-update-rele } `, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ` .app-update-notification { diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 1ad524812..07a5d6d77 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -5,6 +5,7 @@ import { inject, OnInit, signal, + ChangeDetectionStrategy, } from '@angular/core'; import { MatSnackBar } from '@angular/material/snack-bar'; import { Router, RouterOutlet } from '@angular/router'; @@ -48,6 +49,8 @@ const debugAppComponent = createDevLogger('AppComponent'); @Component({ selector: 'app-root', templateUrl: './app.component.html', + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, imports: [ AppStartupStatusComponent, AppUpdateNotificationPanelComponent, diff --git a/apps/web/src/app/app.config.ts b/apps/web/src/app/app.config.ts index 8aa62f5f1..0f08a66bf 100644 --- a/apps/web/src/app/app.config.ts +++ b/apps/web/src/app/app.config.ts @@ -2,6 +2,7 @@ import { HttpClient, provideHttpClient, withInterceptorsFromDi, + withXhr, } from '@angular/common/http'; import { FullscreenOverlayContainer, @@ -114,7 +115,7 @@ export const appConfig: ApplicationConfig = { // fullscreen channel panel's sort/context menus and the controls' // tooltips render while the player is fullscreen. { provide: OverlayContainer, useClass: FullscreenOverlayContainer }, - provideHttpClient(withInterceptorsFromDi()), + provideHttpClient(withXhr(), withInterceptorsFromDi()), provideStore({ router: routerReducer, playlistState: playlistReducer, diff --git a/apps/web/src/app/settings/app-update-release-notes-dialog.component.ts b/apps/web/src/app/settings/app-update-release-notes-dialog.component.ts index 32232f60a..2078703f1 100644 --- a/apps/web/src/app/settings/app-update-release-notes-dialog.component.ts +++ b/apps/web/src/app/settings/app-update-release-notes-dialog.component.ts @@ -6,6 +6,7 @@ import { SecurityContext, signal, ViewEncapsulation, + ChangeDetectionStrategy, } from '@angular/core'; import { DatePipe } from '@angular/common'; import { MatButtonModule } from '@angular/material/button'; @@ -76,7 +77,9 @@ function decorateReleaseNotesHtml(html: string): string {
- {{ notes()?.releaseName || notes()?.tagName }} + {{ + notes()?.releaseName || notes()?.tagName + }} @if (notes()?.publishedAt; as publishedAt) { {{ publishedAt | date: 'mediumDate' }} } @@ -120,6 +123,8 @@ function decorateReleaseNotesHtml(html: string): string { `, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ` .release-notes-dialog { @@ -210,9 +215,8 @@ function decorateReleaseNotesHtml(html: string): string { ], }) export class AppUpdateReleaseNotesDialogComponent implements OnInit { - private readonly data = inject( - MAT_DIALOG_DATA - ); + private readonly data = + inject(MAT_DIALOG_DATA); private readonly dialogRef = inject( MatDialogRef ); @@ -271,7 +275,9 @@ export class AppUpdateReleaseNotesDialogComponent implements OnInit { await window.electron.getAppUpdateReleaseNotes(request) ); } catch (error) { - this.error.set(error instanceof Error ? error.message : String(error)); + this.error.set( + error instanceof Error ? error.message : String(error) + ); } finally { this.loading.set(false); } diff --git a/apps/web/src/app/settings/settings-about-section.component.ts b/apps/web/src/app/settings/settings-about-section.component.ts index 849803476..f23eb6e92 100644 --- a/apps/web/src/app/settings/settings-about-section.component.ts +++ b/apps/web/src/app/settings/settings-about-section.component.ts @@ -4,6 +4,7 @@ import { input, output, ViewEncapsulation, + ChangeDetectionStrategy, } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; @@ -18,6 +19,8 @@ import { imports: [MatButtonModule, MatIconModule, TranslateModule], templateUrl: './settings-about-section.component.html', encapsulation: ViewEncapsulation.None, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ':host { display: contents; }', '.version-block .build-commit { opacity: 0.65; font-size: 0.85em; }', @@ -28,7 +31,9 @@ export class SettingsAboutSectionComponent { readonly version = input(); readonly buildCommit = input(); readonly updateMessage = input(); - readonly appUpdateStatus = input(null); + readonly appUpdateStatus = input( + null + ); readonly buildCommitShort = computed(() => { const commit = this.buildCommit()?.trim(); @@ -74,7 +79,7 @@ export class SettingsAboutSectionComponent { return Boolean( status?.currentVersion && - status.status !== ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Checking + status.status !== ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Checking ); }); diff --git a/apps/web/src/app/settings/settings-backup-section.component.ts b/apps/web/src/app/settings/settings-backup-section.component.ts index 5b092ca8b..f383c6163 100644 --- a/apps/web/src/app/settings/settings-backup-section.component.ts +++ b/apps/web/src/app/settings/settings-backup-section.component.ts @@ -1,4 +1,10 @@ -import { Component, input, output, ViewEncapsulation } from '@angular/core'; +import { + Component, + input, + output, + ViewEncapsulation, + ChangeDetectionStrategy, +} from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; @@ -14,6 +20,8 @@ import { TranslateModule } from '@ngx-translate/core'; ], templateUrl: './settings-backup-section.component.html', encapsulation: ViewEncapsulation.None, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [':host { display: contents; }'], }) export class SettingsBackupSectionComponent { diff --git a/apps/web/src/app/settings/settings-dashboard-section.component.ts b/apps/web/src/app/settings/settings-dashboard-section.component.ts index bba422dea..3cb1739b6 100644 --- a/apps/web/src/app/settings/settings-dashboard-section.component.ts +++ b/apps/web/src/app/settings/settings-dashboard-section.component.ts @@ -1,5 +1,10 @@ import { CommonModule } from '@angular/common'; -import { Component, input, ViewEncapsulation } from '@angular/core'; +import { + Component, + input, + ViewEncapsulation, + ChangeDetectionStrategy, +} from '@angular/core'; import { FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatIconModule } from '@angular/material/icon'; @@ -16,6 +21,8 @@ import { TranslateModule } from '@ngx-translate/core'; ], templateUrl: './settings-dashboard-section.component.html', encapsulation: ViewEncapsulation.None, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [':host { display: contents; }'], }) export class SettingsDashboardSectionComponent { diff --git a/apps/web/src/app/settings/settings-delete-all-playlists-dialog.component.ts b/apps/web/src/app/settings/settings-delete-all-playlists-dialog.component.ts index 79ef4f649..759812dd1 100644 --- a/apps/web/src/app/settings/settings-delete-all-playlists-dialog.component.ts +++ b/apps/web/src/app/settings/settings-delete-all-playlists-dialog.component.ts @@ -1,5 +1,10 @@ import { CommonModule } from '@angular/common'; -import { Component, computed, inject } from '@angular/core'; +import { + Component, + computed, + inject, + ChangeDetectionStrategy, +} from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatDialogModule, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; @@ -25,6 +30,8 @@ type SettingsDeleteSummaryItem = { selector: 'app-settings-delete-all-playlists-dialog', templateUrl: './settings-delete-all-playlists-dialog.component.html', styleUrls: ['./settings-delete-all-playlists-dialog.component.scss'], + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, imports: [ CommonModule, MatButtonModule, diff --git a/apps/web/src/app/settings/settings-epg-section.component.ts b/apps/web/src/app/settings/settings-epg-section.component.ts index e785bde05..8f07d4124 100644 --- a/apps/web/src/app/settings/settings-epg-section.component.ts +++ b/apps/web/src/app/settings/settings-epg-section.component.ts @@ -1,4 +1,10 @@ -import { Component, input, output, ViewEncapsulation } from '@angular/core'; +import { + Component, + input, + output, + ViewEncapsulation, + ChangeDetectionStrategy, +} from '@angular/core'; import { FormArray, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -28,6 +34,8 @@ import { EpgViewModeOption } from './settings.models'; ], templateUrl: './settings-epg-section.component.html', encapsulation: ViewEncapsulation.None, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [':host { display: contents; }'], }) export class SettingsEpgSectionComponent { diff --git a/apps/web/src/app/settings/settings-general-section.component.ts b/apps/web/src/app/settings/settings-general-section.component.ts index ae6a9a4a8..b24d2afbc 100644 --- a/apps/web/src/app/settings/settings-general-section.component.ts +++ b/apps/web/src/app/settings/settings-general-section.component.ts @@ -1,5 +1,11 @@ import { CommonModule } from '@angular/common'; -import { Component, input, output, ViewEncapsulation } from '@angular/core'; +import { + Component, + input, + output, + ViewEncapsulation, + ChangeDetectionStrategy, +} from '@angular/core'; import { FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatFormFieldModule } from '@angular/material/form-field'; @@ -27,6 +33,8 @@ import { ], templateUrl: './settings-general-section.component.html', encapsulation: ViewEncapsulation.None, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [':host { display: contents; }'], }) export class SettingsGeneralSectionComponent { diff --git a/apps/web/src/app/settings/settings-playback-section.component.ts b/apps/web/src/app/settings/settings-playback-section.component.ts index 755b5ac3d..c6d980340 100644 --- a/apps/web/src/app/settings/settings-playback-section.component.ts +++ b/apps/web/src/app/settings/settings-playback-section.component.ts @@ -1,5 +1,11 @@ import { CommonModule } from '@angular/common'; -import { Component, input, output, ViewEncapsulation } from '@angular/core'; +import { + Component, + input, + output, + ViewEncapsulation, + ChangeDetectionStrategy, +} from '@angular/core'; import { FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -30,6 +36,8 @@ import { SettingsPlayerOption } from './settings.models'; ], templateUrl: './settings-playback-section.component.html', encapsulation: ViewEncapsulation.None, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [':host { display: contents; }'], }) export class SettingsPlaybackSectionComponent { diff --git a/apps/web/src/app/settings/settings-remote-control-section.component.ts b/apps/web/src/app/settings/settings-remote-control-section.component.ts index 0d4d6d51d..37b6408ef 100644 --- a/apps/web/src/app/settings/settings-remote-control-section.component.ts +++ b/apps/web/src/app/settings/settings-remote-control-section.component.ts @@ -1,4 +1,10 @@ -import { Component, input, output, ViewEncapsulation } from '@angular/core'; +import { + Component, + input, + output, + ViewEncapsulation, + ChangeDetectionStrategy, +} from '@angular/core'; import { FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -24,6 +30,8 @@ import { QRCodeComponent } from 'angularx-qrcode'; ], templateUrl: './settings-remote-control-section.component.html', encapsulation: ViewEncapsulation.None, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [':host { display: contents; }'], }) export class SettingsRemoteControlSectionComponent { diff --git a/apps/web/src/app/settings/settings-reset-section.component.ts b/apps/web/src/app/settings/settings-reset-section.component.ts index f9376a28b..431e0e252 100644 --- a/apps/web/src/app/settings/settings-reset-section.component.ts +++ b/apps/web/src/app/settings/settings-reset-section.component.ts @@ -1,4 +1,10 @@ -import { Component, input, output, ViewEncapsulation } from '@angular/core'; +import { + Component, + input, + output, + ViewEncapsulation, + ChangeDetectionStrategy, +} from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; @@ -15,6 +21,8 @@ import { SettingsPlaylistDeleteSummary } from './settings.models'; ], templateUrl: './settings-reset-section.component.html', encapsulation: ViewEncapsulation.None, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [':host { display: contents; }'], }) export class SettingsResetSectionComponent { diff --git a/apps/web/src/app/settings/settings-tmdb-section.component.ts b/apps/web/src/app/settings/settings-tmdb-section.component.ts index 172fbd26b..4b57acfba 100644 --- a/apps/web/src/app/settings/settings-tmdb-section.component.ts +++ b/apps/web/src/app/settings/settings-tmdb-section.component.ts @@ -4,6 +4,7 @@ import { input, signal, ViewEncapsulation, + ChangeDetectionStrategy, } from '@angular/core'; import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; @@ -32,6 +33,8 @@ type TmdbKeyTestState = 'idle' | 'testing' | 'success' | 'error'; ], templateUrl: './settings-tmdb-section.component.html', encapsulation: ViewEncapsulation.None, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ` app-settings-tmdb-section { diff --git a/apps/web/src/app/settings/settings-unsaved-changes-dialog.component.ts b/apps/web/src/app/settings/settings-unsaved-changes-dialog.component.ts index 37b8624d5..cdd877ee3 100644 --- a/apps/web/src/app/settings/settings-unsaved-changes-dialog.component.ts +++ b/apps/web/src/app/settings/settings-unsaved-changes-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, inject } from '@angular/core'; +import { Component, inject, ChangeDetectionStrategy } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; @@ -38,6 +38,8 @@ export interface SettingsUnsavedChangesDialogData { } `, ], + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, template: `

{{ 'SETTINGS.UNSAVED_DIALOG_TITLE' | translate }} diff --git a/apps/web/src/app/settings/settings.component.form.spec.ts b/apps/web/src/app/settings/settings.component.form.spec.ts index e7b05bdbf..ce785b452 100644 --- a/apps/web/src/app/settings/settings.component.form.spec.ts +++ b/apps/web/src/app/settings/settings.component.form.spec.ts @@ -472,7 +472,11 @@ describe('SettingsComponent form', () => { const webFixture = TestBed.createComponent(SettingsComponent); const webComponent = webFixture.componentInstance; stubSettingsSideEffects(webComponent); + const loadSettings = jest.spyOn(webComponent.form, 'loadSettings'); webFixture.detectChanges(); + expect(loadSettings).toHaveBeenCalledTimes(1); + // Await hydration before editing; Zone cannot track native awaits. + await loadSettings.mock.results[0].value; await webFixture.whenStable(); webComponent.settingsForm.patchValue({ theme: Theme.DarkTheme }); diff --git a/apps/web/src/app/settings/settings.component.spec.ts b/apps/web/src/app/settings/settings.component.spec.ts index cc643487d..837d4646e 100644 --- a/apps/web/src/app/settings/settings.component.spec.ts +++ b/apps/web/src/app/settings/settings.component.spec.ts @@ -78,6 +78,10 @@ describe('SettingsComponent', () => { const lifecycleFixture = TestBed.createComponent(SettingsComponent); const lifecycleComponent = lifecycleFixture.componentInstance; stubSettingsSideEffects(lifecycleComponent); + const loadSettings = jest.spyOn( + lifecycleComponent.form, + 'loadSettings' + ); const appUpdateInit = jest.spyOn(lifecycleComponent.appUpdate, 'init'); const appUpdateDispose = jest.spyOn( lifecycleComponent.appUpdate, @@ -89,6 +93,9 @@ describe('SettingsComponent', () => { ); lifecycleFixture.detectChanges(); + expect(loadSettings).toHaveBeenCalledTimes(1); + // Let ngOnInit resume after its native promise, which Zone cannot track. + await loadSettings.mock.results[0].value; await lifecycleFixture.whenStable(); expect(appUpdateInit).toHaveBeenCalledTimes(1); @@ -197,7 +204,10 @@ describe('SettingsComponent', () => { it('save-and-leave persists before allowing the navigation', async () => { component.settingsForm.markAsDirty(); - jest.spyOn(component.epg, 'fetchConfiguredEpg').mockImplementation(); + jest.spyOn( + component.epg, + 'fetchConfiguredEpg' + ).mockImplementation(); answerDialogWith('save'); await expect( diff --git a/apps/web/src/app/settings/settings.component.ts b/apps/web/src/app/settings/settings.component.ts index e5b6f4868..8390b6127 100644 --- a/apps/web/src/app/settings/settings.component.ts +++ b/apps/web/src/app/settings/settings.component.ts @@ -7,6 +7,7 @@ import { OnDestroy, OnInit, ViewEncapsulation, + ChangeDetectionStrategy, } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { ReactiveFormsModule } from '@angular/forms'; @@ -93,6 +94,8 @@ export const SETTINGS_DEFAULT_SECTION = 'general'; SettingsResetSectionComponent, SettingsTmdbSectionComponent, ], + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, providers: [ SettingsAppUpdateFacade, SettingsBackupFacade, diff --git a/apps/web/src/test-setup.ts b/apps/web/src/test-setup.ts index c28563d25..4bb680fcf 100644 --- a/apps/web/src/test-setup.ts +++ b/apps/web/src/test-setup.ts @@ -1,4 +1,7 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone/index.mjs'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless/index.mjs'; import { installDuplicateVideoJsQualityLevelsWarnFilter } from '@iptvnator/shared/testing'; Object.defineProperty(globalThis, 'jest', { @@ -25,7 +28,7 @@ if (!Element.prototype.animate) { installDuplicateVideoJsQualityLevelsWarnFilter(); -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/apps/web/tsconfig.app.json b/apps/web/tsconfig.app.json index 2f8957c43..3c21d3523 100644 --- a/apps/web/tsconfig.app.json +++ b/apps/web/tsconfig.app.json @@ -12,5 +12,13 @@ "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.stub.ts" - ] + ], + "angularCompilerOptions": { + "extendedDiagnostics": { + "checks": { + "nullishCoalescingNotNullable": "suppress", + "optionalChainNotNullable": "suppress" + } + } + } } diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 83086f821..79baea4e2 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -15,7 +15,8 @@ "resolveJsonModule": true, "strict": true, "skipLibCheck": true, - "useDefineForClassFields": false + "useDefineForClassFields": false, + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/apps/website/tsconfig.json b/apps/website/tsconfig.json index 990abae73..f3946efd2 100644 --- a/apps/website/tsconfig.json +++ b/apps/website/tsconfig.json @@ -1,11 +1,11 @@ { - "extends": "astro/tsconfigs/strict", - "include": [".astro/types.d.ts", "**/*"], - "exclude": ["dist"], - "compilerOptions": { - "baseUrl": ".", - "paths": { - "@/*": ["src/*"] + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"], + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + }, + "ignoreDeprecations": "6.0" } - } } diff --git a/apps/xtream-mock-server/tsconfig.json b/apps/xtream-mock-server/tsconfig.json index b94d5fe8b..d4c8e6b89 100644 --- a/apps/xtream-mock-server/tsconfig.json +++ b/apps/xtream-mock-server/tsconfig.json @@ -9,7 +9,10 @@ "strict": true, "esModuleInterop": true, "skipLibCheck": true, - "resolveJsonModule": true + "resolveJsonModule": true, + "noUncheckedSideEffectImports": false, + "types": ["*"], + "ignoreDeprecations": "6.0" }, "include": ["src/**/*.ts"], "exclude": ["node_modules", "dist"] diff --git a/apps/xtream-mock-server/tsconfig.spec.json b/apps/xtream-mock-server/tsconfig.spec.json index f1a044453..6c0800193 100644 --- a/apps/xtream-mock-server/tsconfig.spec.json +++ b/apps/xtream-mock-server/tsconfig.spec.json @@ -6,7 +6,8 @@ "module": "commonjs", "moduleResolution": "node10", "types": ["jest", "node"], - "strict": true + "strict": true, + "ignoreDeprecations": "6.0" }, "include": [ "jest.config.ts", diff --git a/docker/Dockerfile b/docker/Dockerfile index 86da3f98d..c5502fa8d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -8,7 +8,7 @@ RUN apk add --no-cache python3 make g++ git WORKDIR /usr/src/app COPY .npmrc ./ -COPY package.json pnpm-lock.yaml ./ +COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ COPY patches ./patches # Node 25 unbundled Corepack, so `corepack enable` cannot be the way pnpm gets diff --git a/docs/architecture/nx-workspace-boundaries.md b/docs/architecture/nx-workspace-boundaries.md index 5dbb8d670..0ca059aa1 100644 --- a/docs/architecture/nx-workspace-boundaries.md +++ b/docs/architecture/nx-workspace-boundaries.md @@ -56,23 +56,70 @@ Omit `pnpm nx migrate --run-migrations` when the first command reports that no migrations exist. Major Nx updates always use this manual workflow and the resulting PR runs the full CI pipeline. +## Angular 22 Toolchain Compatibility + +Angular framework/Material 22.1 and CLI/build 22.1 use Nx 23.2 and TypeScript +6.0. Framework and CLI patch numbers may differ; keep framework packages aligned +in both dependency sections and all official Nx packages on one exact version. +Use `.nvmrc` for development/CI; supported LTS Node ranges are +`^22.22.3 || ^24.15.0`. Docker uses the supported Node 24 line. + +The Angular 22 migration explicitly preserves the old default change detection +with `ChangeDetectionStrategy.Eager`. Existing OnPush components and +`provideZoneChangeDetection` remain in their existing modes. +Existing eager components under Angular ESLint carry a line-scoped explanation +for the newly recommended OnPush rule; new components keep that rule enabled. +Adopting OnPush in those components requires a separate behavior review. +TypeScript 6 migration settings preserve compiler defaults and per-project output roots; +`ignoreDeprecations: "6.0"` temporarily supports legacy Node test configs and +`baseUrl`, rather than changing the whole workspace to browser resolution. + +Two third-party packages need scoped compatibility bridges in +`pnpm-workspace.yaml`: + +- `nx-electron@22.0.0` still declares Nx 22 peers. Its existing package-layout + patch also changes the removed `@nx/js/src/utils/buildable-libs-utils` import + to `@nx/js/internal`. A package extension supplies its undeclared runtime + dependency `webpack-node-externals`. The packaging suite loads every executor + and checks the single application manifest. Remove these bridges only after + the upstream package declares and implements the current Nx APIs. +- `ngx-indexed-db@22.0.0` declares Angular `<22`, but its used API remains + compatible. Its allowance is scoped to the exact Angular version and must be + revalidated on upgrades. `apps/web-e2e/src/basic.e2e.ts` opens a historical + `iptvnator` v1 database, retains the `_id` key path, and checks reload + persistence. The shared interfaces package declares the same library version. + Remove the allowance when upstream includes the installed Angular version. + +Jest uses root Babel 7 while Angular build retains its own Babel 8. The website +uses Astro 7 with the root TypeScript 6; no Astro-specific compiler package +extension is needed. Keep the website build in toolchain validation. +The website's alias uses a relative `./src/*` target without the deprecated +`baseUrl` option. + +Electron's webpack build resolves `ts-loader` 9.6.2 through the existing +`nx-electron` version range. Keep at least 9.5.7: earlier versions clear +`rootDir` in transpile-only mode and fail with TypeScript 6 (TS5011/TS6059). + +Angular unit-test setup explicitly uses `setupZonelessTestEnv` and separately +imports `zone.js/testing` for existing `fakeAsync` helpers. This preserves the +Angular 21 scheduler used by jest-preset-angular 15: its `setupZoneTestEnv` loaded +Zone but did not provide zone-based change detection on Angular 20+. Preset 17 +does provide it, which disables automatic fixture rendering and changes +`whenStable()` behavior. Production applications continue to use their existing +`provideZoneChangeDetection`; browser and Electron E2E validate that runtime. + ## Vite Dev-Server Patch -Angular's development builder currently resolves Vite `7.3.6`. That release's -asset and worker transform prefilters can catastrophically backtrack on a large -generated chunk containing unrelated `new URL...` expressions while searching -for a valid `new URL(..., import.meta.url)` construct. The Electron development -server can then fail a lazy chunk request with `Maximum call stack size -exceeded`, even though static builds succeed because they do not pass emitted -chunks through Vite's request-time plugin container. +Angular's development builder resolves Vite `8.1.5`. It contains upstream precise +asset/worker URL matchers, but also uses them as raw-code prefilters. Those +prefilters reject valid expressions containing comments before the handlers can +strip the comments and apply the precise matchers. -`patches/vite@7.3.6.patch` backports Vite's upstream precise-matcher fix from -[vitejs/vite#21800](https://github.com/vitejs/vite/pull/21800). Bounded -prefilters keep the request-time scan linear while allowing comment-bearing -asset and worker expressions to reach the precise matcher after Vite strips -comments. Keep the patch while the supported Angular toolchain resolves Vite -`7.3.6`; remove it only after the resolved Vite contains the upstream fix. Run -the regression check after any related manifest or lockfile update: +`patches/vite@8.1.5.patch` adds bounded asset/worker prefilters and uses the asset +prefilter for bundled URL rewriting too. The precise handler matchers remain +unchanged. This preserves the earlier protection against expensive scanning of +large false-positive chunks and retains comment-bearing expressions. Remove the +patch only when the resolved upstream Vite passes the same behavior checks: ```bash pnpm run deps:vite:test diff --git a/docs/architecture/workspace-shell.md b/docs/architecture/workspace-shell.md index a53ca1215..fd7315666 100644 --- a/docs/architecture/workspace-shell.md +++ b/docs/architecture/workspace-shell.md @@ -304,27 +304,28 @@ handlers in `apps/electron-backend/src/app/events/window.events.ts`): snap, F11). The controls hide themselves while the window is fullscreen. - Window state is **never re-read at event time**. `attachWindowStateEvents` - seeds `{ isMaximized, isFullScreen }` once at window creation and each - event patches only the flag it names; every push carries a copy of that - tracked state. On Windows both getters can still report the - pre-transition value while the matching event fires — `isFullScreen()` - stays `true` during an HTML fullscreen exit, and `isMaximized()` reads - `false` while the window is fullscreen. Because the renderer replaces - both flags on every push and no later event corrects a stale one, - polling left the controls hidden forever after leaving fullscreen and - stuck the maximize/restore glyph on the wrong icon. Regression coverage: - `app-window-state.spec.ts` and `window-controls.e2e.ts`. + Window state is **never re-read at event time**. `attachWindowStateEvents` + seeds `{ isMaximized, isFullScreen }` once at window creation and each + event patches only the flag it names; every push carries a copy of that + tracked state. On Windows both getters can still report the + pre-transition value while the matching event fires — `isFullScreen()` + stays `true` during an HTML fullscreen exit, and `isMaximized()` reads + `false` while the window is fullscreen. Because the renderer replaces + both flags on every push and no later event corrects a stale one, + polling left the controls hidden forever after leaving fullscreen and + stuck the maximize/restore glyph on the wrong icon. Regression coverage: + `app-window-state.spec.ts` and `window-controls.e2e.ts`. + + The pushed `isFullScreen` is the OR of two flags tracked apart: native + (OS-level, fed by `enter/leave-full-screen`) and HTML-element (fed by + the `*-html-*` pair). Electron remembers when the window was already + natively fullscreen before the player entered HTML fullscreen and then + leaves ONLY the HTML state on exit — no `leave-full-screen` fires and the + window stays fullscreen — so a single flag cleared by + `leave-html-full-screen` would un-hide the controls over a window that + is still fullscreen. A fullscreen launch (below) or F11 followed by the + player's `F` → `Esc` makes that path routine on Windows/Linux. - The pushed `isFullScreen` is the OR of two flags tracked apart: native - (OS-level, fed by `enter/leave-full-screen`) and HTML-element (fed by - the `*-html-*` pair). Electron remembers when the window was already - natively fullscreen before the player entered HTML fullscreen and then - leaves ONLY the HTML state on exit — no `leave-full-screen` fires and the - window stays fullscreen — so a single flag cleared by - `leave-html-full-screen` would un-hide the controls over a window that - is still fullscreen. A fullscreen launch (below) or F11 followed by the - player's `F` → `Esc` makes that path routine on Windows/Linux. 3. `WINDOW:TOGGLE_FULLSCREEN` toggles OS-level fullscreen (`setFullScreen`) and, like the maximize toggle, reports the requested state and leaves the `WINDOW:STATE_CHANGED` push authoritative. Because the transition is @@ -439,15 +440,15 @@ Toolchain notes for the Electron 43 baseline: implicit `node-gyp rebuild` bypasses those binaries and makes installation depend on the host compiler toolchain. The old `node-abi` override belonged to v12's removed `prebuild-install` path and is no longer required. -2. Local development supports **Node 22.13–22.x or Node >= 24**, declared in - `engines` as `^22.13.0 || >=24.0.0`. - The direct `@faker-js/faker` dependency and current lint tooling require - that floor. `electron-builder` 26.15.7 also pulls `@electron/rebuild` 4, +2. Local development supports **Node 22.22.3–22.x or 24.15.0–24.x**, declared in + `engines` as `^22.22.3 || ^24.15.0`. Use `.nvmrc` (currently 22.23.2) for + development and CI. Angular 22 sets this supported LTS range and requires + TypeScript `>=6.0 <6.1`. `electron-builder` 26.15.7 also pulls `@electron/rebuild` 4, which requires Node 22.12 or newer, and the root `postinstall` runs `install-app-deps` on every `pnpm install`. The 26.15.7 minimum also carries the v26 backport that fully extracts the Snap template's `.tar.7z` payload; 26.15.0–26.15.6 can - produce a Snap that is missing `desktop-init.sh`. CI already runs Node 22. + produce a Snap that is missing `desktop-init.sh`. 3. Dependabot keeps Electron, native database, packaging, EPG parser, and version-locked Shaka/mpegts updates out of the shared npm minor/patch group. Those dependencies require standalone PRs so their dedicated package, @@ -457,7 +458,7 @@ Toolchain notes for the Electron 43 baseline: Electron command instead of during package `postinstall`, so `electron` must not remain in pnpm's `onlyBuiltDependencies` allowlist. The first local `pnpm run serve:backend` can include a one-time download; use `pnpm exec - electron --version` to prewarm it before an offline run. +electron --version` to prewarm it before an offline run. Known caveats: diff --git a/jest.web-esm.workspace.ts b/jest.web-esm.workspace.ts index 0bb8da563..ed6badaa6 100644 --- a/jest.web-esm.workspace.ts +++ b/jest.web-esm.workspace.ts @@ -1,4 +1,4 @@ -import { nxPreset } from '@nx/jest/preset.js'; +import { nxPreset } from '@nx/jest/preset'; import { createEsmPreset } from 'jest-preset-angular/presets/index.js'; const angularEsmPreset = createEsmPreset({ diff --git a/libs/epg/data-access/src/test-setup.ts b/libs/epg/data-access/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/epg/data-access/src/test-setup.ts +++ b/libs/epg/data-access/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/epg/data-access/tsconfig.json b/libs/epg/data-access/tsconfig.json index f7fcb0d81..8fcb75804 100644 --- a/libs/epg/data-access/tsconfig.json +++ b/libs/epg/data-access/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/m3u-state/src/test-setup.ts b/libs/m3u-state/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/m3u-state/src/test-setup.ts +++ b/libs/m3u-state/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/m3u-state/tsconfig.json b/libs/m3u-state/tsconfig.json index 70cbd99dc..c8a852d6f 100644 --- a/libs/m3u-state/tsconfig.json +++ b/libs/m3u-state/tsconfig.json @@ -9,7 +9,8 @@ "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/playback/util/tsconfig.json b/libs/playback/util/tsconfig.json index 41d58cc4b..88490bc63 100644 --- a/libs/playback/util/tsconfig.json +++ b/libs/playback/util/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "files": [], "include": [], diff --git a/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts index 7db950b8e..2a8efc34d 100644 --- a/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts +++ b/libs/playlist/import/feature/src/lib/add-playlist-dialog/add-playlist-dialog.component.ts @@ -6,6 +6,7 @@ import { signal, ViewEncapsulation, viewChild, + ChangeDetectionStrategy, } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { @@ -70,6 +71,7 @@ const METHOD_BY_CANDIDATE_KIND: Record< selector: 'app-add-playlist', templateUrl: './add-playlist-dialog.component.html', styleUrl: './add-playlist-dialog.component.scss', + changeDetection: ChangeDetectionStrategy.Eager, encapsulation: ViewEncapsulation.None, }) export class AddPlaylistDialogComponent { @@ -98,8 +100,9 @@ export class AddPlaylistDialogComponent { * the prefill is applied by an effect that re-runs once the child's * viewChild signal resolves, then clears this. */ - private readonly pendingPrefill = - signal(null); + private readonly pendingPrefill = signal( + null + ); /** * Survives the auto-detect surface being destroyed by a method switch, so diff --git a/libs/playlist/import/feature/src/lib/auto-import/auto-import.component.ts b/libs/playlist/import/feature/src/lib/auto-import/auto-import.component.ts index 59641610e..c8b7f6cbd 100644 --- a/libs/playlist/import/feature/src/lib/auto-import/auto-import.component.ts +++ b/libs/playlist/import/feature/src/lib/auto-import/auto-import.component.ts @@ -1,4 +1,11 @@ -import { Component, computed, input, OnInit, output } from '@angular/core'; +import { + Component, + computed, + input, + OnInit, + output, + ChangeDetectionStrategy, +} from '@angular/core'; import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; import { FormControl, ReactiveFormsModule } from '@angular/forms'; import { MatButtonModule } from '@angular/material/button'; @@ -139,6 +146,7 @@ function maskUrlQueryPasswords(url: string): string { selector: 'app-auto-import', templateUrl: './auto-import.component.html', styleUrl: './auto-import.component.scss', + changeDetection: ChangeDetectionStrategy.Eager, imports: [ MatButtonModule, MatFormFieldModule, @@ -228,10 +236,7 @@ export class AutoImportComponent implements OnInit { ); break; case 'xtream': - push( - 'HOME.XTREAM_PLAYLIST.SERVER_URL', - candidate.serverUrl - ); + push('HOME.XTREAM_PLAYLIST.SERVER_URL', candidate.serverUrl); push('HOME.XTREAM_PLAYLIST.USERNAME', candidate.username); push( 'HOME.XTREAM_PLAYLIST.PASSWORD', @@ -239,14 +244,8 @@ export class AutoImportComponent implements OnInit { ); break; case 'stalker': - push( - 'HOME.STALKER_PORTAL.SERVER_URL', - candidate.portalUrl - ); - push( - 'HOME.STALKER_PORTAL.MAC_ADDRESS', - candidate.macAddress - ); + push('HOME.STALKER_PORTAL.SERVER_URL', candidate.portalUrl); + push('HOME.STALKER_PORTAL.MAC_ADDRESS', candidate.macAddress); push( 'HOME.STALKER_PORTAL.SERIAL_NUMBER', candidate.serialNumber diff --git a/libs/playlist/import/feature/src/lib/file-upload/file-upload.component.ts b/libs/playlist/import/feature/src/lib/file-upload/file-upload.component.ts index e51714762..ae51aa9c2 100644 --- a/libs/playlist/import/feature/src/lib/file-upload/file-upload.component.ts +++ b/libs/playlist/import/feature/src/lib/file-upload/file-upload.component.ts @@ -1,4 +1,11 @@ -import { Component, EventEmitter, Output, inject, signal } from '@angular/core'; +import { + Component, + EventEmitter, + Output, + inject, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; import { TranslatePipe } from '@ngx-translate/core'; import { PlaylistFileImportService } from '@iptvnator/playlist/shared/util'; @@ -11,6 +18,7 @@ const KB = 1024; imports: [DragDropFileUploadDirective, MatIconModule, TranslatePipe], selector: 'app-file-upload', templateUrl: './file-upload.component.html', + changeDetection: ChangeDetectionStrategy.Eager, styleUrls: ['./file-upload.component.scss'], }) export class FileUploadComponent { diff --git a/libs/playlist/import/feature/src/lib/stalker-portal-import/stalker-portal-import.component.ts b/libs/playlist/import/feature/src/lib/stalker-portal-import/stalker-portal-import.component.ts index bcfb41477..bd39ff666 100644 --- a/libs/playlist/import/feature/src/lib/stalker-portal-import/stalker-portal-import.component.ts +++ b/libs/playlist/import/feature/src/lib/stalker-portal-import/stalker-portal-import.component.ts @@ -1,4 +1,10 @@ -import { Component, inject, output, signal } from '@angular/core'; +import { + Component, + inject, + output, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { FormControl, FormGroup, @@ -59,6 +65,7 @@ interface StalkerSettledIdentity { ], selector: 'app-stalker-portal-import', templateUrl: './stalker-portal-import.component.html', + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ` :host { diff --git a/libs/playlist/import/feature/src/lib/text-import/text-import.component.ts b/libs/playlist/import/feature/src/lib/text-import/text-import.component.ts index 3aaa414a9..5eb4f7fd5 100644 --- a/libs/playlist/import/feature/src/lib/text-import/text-import.component.ts +++ b/libs/playlist/import/feature/src/lib/text-import/text-import.component.ts @@ -1,4 +1,9 @@ -import { Component, EventEmitter, Output } from '@angular/core'; +import { + Component, + EventEmitter, + Output, + ChangeDetectionStrategy, +} from '@angular/core'; import { FormControl, FormGroup, @@ -12,6 +17,7 @@ import { TranslatePipe } from '@ngx-translate/core'; selector: 'app-text-import', templateUrl: './text-import.component.html', styleUrls: ['./text-import.component.scss'], + changeDetection: ChangeDetectionStrategy.Eager, imports: [MatInputModule, ReactiveFormsModule, TranslatePipe], }) export class TextImportComponent { diff --git a/libs/playlist/import/feature/src/lib/url-upload/url-upload.component.ts b/libs/playlist/import/feature/src/lib/url-upload/url-upload.component.ts index 15d756d6d..ce06b6834 100644 --- a/libs/playlist/import/feature/src/lib/url-upload/url-upload.component.ts +++ b/libs/playlist/import/feature/src/lib/url-upload/url-upload.component.ts @@ -1,4 +1,9 @@ -import { Component, inject, OnInit } from '@angular/core'; +import { + Component, + inject, + OnInit, + ChangeDetectionStrategy, +} from '@angular/core'; import { FormBuilder, FormControl, @@ -14,6 +19,7 @@ import { RuntimeCapabilitiesService } from '@iptvnator/services'; @Component({ selector: 'app-url-upload', templateUrl: './url-upload.component.html', + changeDetection: ChangeDetectionStrategy.Eager, imports: [ MatFormFieldModule, MatInputModule, diff --git a/libs/playlist/import/feature/src/lib/xtream-code-import/xtream-code-import.component.ts b/libs/playlist/import/feature/src/lib/xtream-code-import/xtream-code-import.component.ts index 2a4f56502..c445c35b9 100644 --- a/libs/playlist/import/feature/src/lib/xtream-code-import/xtream-code-import.component.ts +++ b/libs/playlist/import/feature/src/lib/xtream-code-import/xtream-code-import.component.ts @@ -1,4 +1,10 @@ -import { Component, EventEmitter, Output, inject } from '@angular/core'; +import { + Component, + EventEmitter, + Output, + inject, + ChangeDetectionStrategy, +} from '@angular/core'; import { AbstractControl, FormControl, @@ -47,6 +53,7 @@ function xtreamServerUrlValidator( ], selector: 'app-xtream-code-import', templateUrl: './xtream-code-import.component.html', + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ` :host { diff --git a/libs/playlist/import/feature/src/test-setup.ts b/libs/playlist/import/feature/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/playlist/import/feature/src/test-setup.ts +++ b/libs/playlist/import/feature/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/playlist/import/feature/tsconfig.json b/libs/playlist/import/feature/tsconfig.json index 7d33479d8..4853e7663 100644 --- a/libs/playlist/import/feature/tsconfig.json +++ b/libs/playlist/import/feature/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/playlist/m3u/feature-player/src/lib/m3u-vod-detail/m3u-vod-detail.component.ts b/libs/playlist/m3u/feature-player/src/lib/m3u-vod-detail/m3u-vod-detail.component.ts index 253b91bb6..3ed046a76 100644 --- a/libs/playlist/m3u/feature-player/src/lib/m3u-vod-detail/m3u-vod-detail.component.ts +++ b/libs/playlist/m3u/feature-player/src/lib/m3u-vod-detail/m3u-vod-detail.component.ts @@ -7,6 +7,7 @@ import { output, signal, untracked, + ChangeDetectionStrategy, } from '@angular/core'; import { MatIcon } from '@angular/material/icon'; import { TranslatePipe } from '@ngx-translate/core'; @@ -59,6 +60,7 @@ import { M3uVodMetadataService } from './m3u-vod-metadata.service'; TranslatePipe, ], templateUrl: './m3u-vod-detail.component.html', + changeDetection: ChangeDetectionStrategy.Eager, styleUrls: ['./m3u-vod-detail.component.scss'], }) export class M3uVodDetailComponent { diff --git a/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.html b/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.html index a6c3cf737..aa4710be4 100644 --- a/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.html +++ b/libs/playlist/m3u/feature-player/src/lib/video-player/video-player.component.html @@ -105,7 +105,9 @@ @if (activeChannel.radio === 'true') { { currentLang: 'en', get: jest.fn((key: string) => of(key)), instant: jest.fn((key: string) => key), - onDefaultLangChange: of({ - lang: 'en', - translations: {}, - }), - onLangChange: of({ lang: 'en', translations: {} }), - onTranslationChange: of({ - lang: 'en', - translations: {}, - }), + onDefaultLangChange: new Subject(), + onLangChange: new Subject(), + onTranslationChange: new Subject(), }, }, { diff --git a/libs/playlist/shared/ui/src/lib/recent-playlists/playlist-info/playlist-info.component.ts b/libs/playlist/shared/ui/src/lib/recent-playlists/playlist-info/playlist-info.component.ts index 379705f25..233911464 100644 --- a/libs/playlist/shared/ui/src/lib/recent-playlists/playlist-info/playlist-info.component.ts +++ b/libs/playlist/shared/ui/src/lib/recent-playlists/playlist-info/playlist-info.component.ts @@ -1,6 +1,12 @@ import { Clipboard, ClipboardModule } from '@angular/cdk/clipboard'; import { DatePipe } from '@angular/common'; -import { Component, DestroyRef, inject, signal } from '@angular/core'; +import { + Component, + DestroyRef, + inject, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { FormControl, ReactiveFormsModule, @@ -159,6 +165,7 @@ type DesktopFileSaveBridge = Pick< `, ], providers: [DatePipe], + changeDetection: ChangeDetectionStrategy.Eager, imports: [ ClipboardModule, MatButton, diff --git a/libs/playlist/shared/ui/src/lib/recent-playlists/playlist-item/playlist-item.component.ts b/libs/playlist/shared/ui/src/lib/recent-playlists/playlist-item/playlist-item.component.ts index deb30ea91..1ab9a3f84 100644 --- a/libs/playlist/shared/ui/src/lib/recent-playlists/playlist-item/playlist-item.component.ts +++ b/libs/playlist/shared/ui/src/lib/recent-playlists/playlist-item/playlist-item.component.ts @@ -12,6 +12,7 @@ import { inject, input, output, + ChangeDetectionStrategy, } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { MatIconButton } from '@angular/material/button'; @@ -33,6 +34,7 @@ import { PlaylistMeta } from '@iptvnator/shared/interfaces'; selector: 'app-playlist-item', templateUrl: './playlist-item.component.html', styleUrls: ['./playlist-item.component.scss'], + changeDetection: ChangeDetectionStrategy.Eager, imports: [ SourceHealthIndicatorComponent, DatePipe, diff --git a/libs/playlist/shared/ui/src/lib/source-health/source-cleanup-dialog.component.ts b/libs/playlist/shared/ui/src/lib/source-health/source-cleanup-dialog.component.ts index d61019e16..347cefaac 100644 --- a/libs/playlist/shared/ui/src/lib/source-health/source-cleanup-dialog.component.ts +++ b/libs/playlist/shared/ui/src/lib/source-health/source-cleanup-dialog.component.ts @@ -1,4 +1,11 @@ -import { Component, DestroyRef, computed, effect, inject } from '@angular/core'; +import { + Component, + DestroyRef, + computed, + effect, + inject, + ChangeDetectionStrategy, +} from '@angular/core'; import { DatePipe } from '@angular/common'; import { MAT_DIALOG_DATA, @@ -107,7 +114,9 @@ export interface SourceCleanupDialogData extends SourceCleanupContext { @if (entry.status === 'ready') { {{ 'SOURCE_HEALTH.REASON.' + - entry.health?.reason | translate + $safeNavigationMigration( + entry.health?.reason + ) | translate }} } @else { {{ @@ -117,8 +126,9 @@ export interface SourceCleanupDialogData extends SourceCleanupContext { } @if (entry.health?.checkedAt) { } @if (entry.warning) { @@ -170,6 +180,7 @@ export interface SourceCleanupDialogData extends SourceCleanupContext { } `, + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ` :host { diff --git a/libs/playlist/shared/ui/src/lib/source-health/source-health-indicator.component.ts b/libs/playlist/shared/ui/src/lib/source-health/source-health-indicator.component.ts index eee37e0c5..3637c056a 100644 --- a/libs/playlist/shared/ui/src/lib/source-health/source-health-indicator.component.ts +++ b/libs/playlist/shared/ui/src/lib/source-health/source-health-indicator.component.ts @@ -6,6 +6,7 @@ import { inject, input, untracked, + ChangeDetectionStrategy, } from '@angular/core'; import { MatTooltip } from '@angular/material/tooltip'; import { TranslateService } from '@ngx-translate/core'; @@ -28,6 +29,7 @@ import { PlaylistMeta, sourceHealthType } from '@iptvnator/shared/interfaces'; role="img" > }`, + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ` :host { diff --git a/libs/playlist/shared/ui/src/test-setup.ts b/libs/playlist/shared/ui/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/playlist/shared/ui/src/test-setup.ts +++ b/libs/playlist/shared/ui/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/playlist/shared/ui/tsconfig.json b/libs/playlist/shared/ui/tsconfig.json index 7d33479d8..4853e7663 100644 --- a/libs/playlist/shared/ui/tsconfig.json +++ b/libs/playlist/shared/ui/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/playlist/shared/util/src/test-setup.ts b/libs/playlist/shared/util/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/playlist/shared/util/src/test-setup.ts +++ b/libs/playlist/shared/util/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/playlist/shared/util/tsconfig.json b/libs/playlist/shared/util/tsconfig.json index 7d33479d8..4853e7663 100644 --- a/libs/playlist/shared/util/tsconfig.json +++ b/libs/playlist/shared/util/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/portal/catalog/feature/src/lib/category-content-view/category-content-view.component.spec.ts b/libs/portal/catalog/feature/src/lib/category-content-view/category-content-view.component.spec.ts index a2e4e4918..f925a698c 100644 --- a/libs/portal/catalog/feature/src/lib/category-content-view/category-content-view.component.spec.ts +++ b/libs/portal/catalog/feature/src/lib/category-content-view/category-content-view.component.spec.ts @@ -1,4 +1,10 @@ -import { Component, input, output, signal } from '@angular/core'; +import { + Component, + input, + output, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { NgComponentOutlet } from '@angular/common'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; @@ -21,6 +27,7 @@ import { CategoryContentViewComponent } from './category-content-view.component' @Component({ selector: 'app-grid-list', standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: '', }) class MockGridListComponent { @@ -37,6 +44,7 @@ class MockGridListComponent { @Component({ selector: 'app-playlist-error-view', standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: '', }) class MockPlaylistErrorViewComponent { @@ -48,6 +56,7 @@ class MockPlaylistErrorViewComponent { @Component({ standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: '', }) class MockDetailComponent { diff --git a/libs/portal/catalog/feature/src/test-setup.ts b/libs/portal/catalog/feature/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/portal/catalog/feature/src/test-setup.ts +++ b/libs/portal/catalog/feature/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/portal/catalog/feature/tsconfig.json b/libs/portal/catalog/feature/tsconfig.json index 7d33479d8..4853e7663 100644 --- a/libs/portal/catalog/feature/tsconfig.json +++ b/libs/portal/catalog/feature/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/portal/downloads/feature/src/lib/offline-detail/download-offline-detail.component.html b/libs/portal/downloads/feature/src/lib/offline-detail/download-offline-detail.component.html index b5caee4b4..2af873533 100644 --- a/libs/portal/downloads/feature/src/lib/offline-detail/download-offline-detail.component.html +++ b/libs/portal/downloads/feature/src/lib/offline-detail/download-offline-detail.component.html @@ -312,7 +312,10 @@ {{ episodeTitle( episode.item, - episode.episodeMetadata?.title + $safeNavigationMigration( + episode.episodeMetadata + ?.title + ) ) }} @@ -360,7 +363,10 @@ ' ' + episodeTitle( episode.item, - episode.episodeMetadata?.title + $safeNavigationMigration( + episode.episodeMetadata + ?.title + ) ) " (click)=" @@ -390,7 +396,10 @@ ': ' + episodeTitle( episode.item, - episode.episodeMetadata?.title + $safeNavigationMigration( + episode.episodeMetadata + ?.title + ) ) " > @@ -418,8 +427,10 @@ ' ' + episodeTitle( episode.item, - episode.episodeMetadata - ?.title + $safeNavigationMigration( + episode.episodeMetadata + ?.title + ) ) " (click)=" diff --git a/libs/portal/downloads/feature/src/test-setup.ts b/libs/portal/downloads/feature/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/portal/downloads/feature/src/test-setup.ts +++ b/libs/portal/downloads/feature/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/portal/downloads/feature/tsconfig.json b/libs/portal/downloads/feature/tsconfig.json index 7d33479d8..4853e7663 100644 --- a/libs/portal/downloads/feature/tsconfig.json +++ b/libs/portal/downloads/feature/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/portal/shared/data-access/src/test-setup.ts b/libs/portal/shared/data-access/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/portal/shared/data-access/src/test-setup.ts +++ b/libs/portal/shared/data-access/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/portal/shared/data-access/tsconfig.json b/libs/portal/shared/data-access/tsconfig.json index dd63ae991..a5e6d5039 100644 --- a/libs/portal/shared/data-access/tsconfig.json +++ b/libs/portal/shared/data-access/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/portal/shared/ui/src/lib/components/favorites-layout/favorites-layout.component.ts b/libs/portal/shared/ui/src/lib/components/favorites-layout/favorites-layout.component.ts index 8437887af..52c4e285d 100644 --- a/libs/portal/shared/ui/src/lib/components/favorites-layout/favorites-layout.component.ts +++ b/libs/portal/shared/ui/src/lib/components/favorites-layout/favorites-layout.component.ts @@ -1,4 +1,10 @@ -import { Component, inject, input, output } from '@angular/core'; +import { + Component, + inject, + input, + output, + ChangeDetectionStrategy, +} from '@angular/core'; import { MatIconButton } from '@angular/material/button'; import { MatIcon } from '@angular/material/icon'; import { MatTooltip } from '@angular/material/tooltip'; @@ -50,6 +56,7 @@ export interface FavoriteLayoutItem { './favorites-layout.component.scss', '../../styles/portal-sidebar.scss', ], + changeDetection: ChangeDetectionStrategy.Eager, imports: [ CategoryViewComponent, ContentCardComponent, diff --git a/libs/portal/shared/ui/src/lib/components/playlist-error-view/playlist-error-view.component.ts b/libs/portal/shared/ui/src/lib/components/playlist-error-view/playlist-error-view.component.ts index 864100c3a..aa12df8d7 100644 --- a/libs/portal/shared/ui/src/lib/components/playlist-error-view/playlist-error-view.component.ts +++ b/libs/portal/shared/ui/src/lib/components/playlist-error-view/playlist-error-view.component.ts @@ -1,4 +1,9 @@ -import { Component, inject, input } from '@angular/core'; +import { + Component, + inject, + input, + ChangeDetectionStrategy, +} from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatDialog } from '@angular/material/dialog'; import { MatIconModule } from '@angular/material/icon'; @@ -14,6 +19,7 @@ import { PlaylistActions } from '@iptvnator/m3u-state'; selector: 'app-playlist-error-view', templateUrl: './playlist-error-view.component.html', styleUrls: ['./playlist-error-view.component.scss'], + changeDetection: ChangeDetectionStrategy.Eager, imports: [MatButtonModule, MatIconModule, RouterLink, TranslateModule], }) export class PlaylistErrorViewComponent { diff --git a/libs/portal/shared/ui/src/lib/components/search-form/search-form.component.ts b/libs/portal/shared/ui/src/lib/components/search-form/search-form.component.ts index fb1f59e7d..5028d424d 100644 --- a/libs/portal/shared/ui/src/lib/components/search-form/search-form.component.ts +++ b/libs/portal/shared/ui/src/lib/components/search-form/search-form.component.ts @@ -6,6 +6,7 @@ import { model, Output, ViewChild, + ChangeDetectionStrategy, } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatCheckboxModule } from '@angular/material/checkbox'; @@ -38,6 +39,7 @@ export interface SearchFilter { TranslatePipe, ], templateUrl: './search-form.component.html', + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ` .search-container { diff --git a/libs/portal/shared/ui/src/lib/directives/infinite-scroll.directive.spec.ts b/libs/portal/shared/ui/src/lib/directives/infinite-scroll.directive.spec.ts index bd82fe389..599e62511 100644 --- a/libs/portal/shared/ui/src/lib/directives/infinite-scroll.directive.spec.ts +++ b/libs/portal/shared/ui/src/lib/directives/infinite-scroll.directive.spec.ts @@ -1,4 +1,4 @@ -import { Component, signal } from '@angular/core'; +import { Component, signal, ChangeDetectionStrategy } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { InfiniteScrollDirective } from './infinite-scroll.directive'; @@ -13,6 +13,7 @@ import { InfiniteScrollDirective } from './infinite-scroll.directive'; [infiniteResetKey]="resetKey()" (infiniteLoadMore)="onLoadMore()" >

`, + changeDetection: ChangeDetectionStrategy.Eager, imports: [InfiniteScrollDirective], }) class TestHostComponent { diff --git a/libs/portal/shared/ui/src/lib/navigation/portal-rail-links.component.ts b/libs/portal/shared/ui/src/lib/navigation/portal-rail-links.component.ts index c52c784f1..2976a0cee 100644 --- a/libs/portal/shared/ui/src/lib/navigation/portal-rail-links.component.ts +++ b/libs/portal/shared/ui/src/lib/navigation/portal-rail-links.component.ts @@ -1,4 +1,9 @@ -import { Component, inject, input } from '@angular/core'; +import { + Component, + inject, + input, + ChangeDetectionStrategy, +} from '@angular/core'; import { MatIcon } from '@angular/material/icon'; import { MatListModule } from '@angular/material/list'; import { MatTooltip } from '@angular/material/tooltip'; @@ -8,12 +13,16 @@ import { RouterLink, RouterLinkActive, } from '@angular/router'; -import { PortalRailLink, PortalRailSection } from '@iptvnator/portal/shared/util'; +import { + PortalRailLink, + PortalRailSection, +} from '@iptvnator/portal/shared/util'; @Component({ selector: 'app-portal-rail-links', imports: [MatIcon, MatListModule, MatTooltip, RouterLink, RouterLinkActive], templateUrl: './portal-rail-links.component.html', + changeDetection: ChangeDetectionStrategy.Eager, styleUrl: './portal-rail-links.component.scss', }) export class PortalRailLinksComponent { @@ -27,7 +36,9 @@ export class PortalRailLinksComponent { readonly variant = input<'list' | 'rail'>('list'); resolveIcon(link: PortalRailLink): string { - const normalizedPath = link.path.map((segment) => String(segment)).join('/'); + const normalizedPath = link.path + .map((segment) => String(segment)) + .join('/'); if (normalizedPath.includes('/workspace/sources')) { return 'playlist_play'; } diff --git a/libs/portal/shared/ui/src/test-setup.ts b/libs/portal/shared/ui/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/portal/shared/ui/src/test-setup.ts +++ b/libs/portal/shared/ui/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/portal/shared/ui/tsconfig.json b/libs/portal/shared/ui/tsconfig.json index 7d33479d8..4853e7663 100644 --- a/libs/portal/shared/ui/tsconfig.json +++ b/libs/portal/shared/ui/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/portal/shared/util/src/test-setup.ts b/libs/portal/shared/util/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/portal/shared/util/src/test-setup.ts +++ b/libs/portal/shared/util/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/portal/shared/util/tsconfig.json b/libs/portal/shared/util/tsconfig.json index dd63ae991..a5e6d5039 100644 --- a/libs/portal/shared/util/tsconfig.json +++ b/libs/portal/shared/util/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/portal/stalker/data-access/src/test-setup.ts b/libs/portal/stalker/data-access/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/portal/stalker/data-access/src/test-setup.ts +++ b/libs/portal/stalker/data-access/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/portal/stalker/data-access/tsconfig.json b/libs/portal/stalker/data-access/tsconfig.json index dd63ae991..a5e6d5039 100644 --- a/libs/portal/stalker/data-access/tsconfig.json +++ b/libs/portal/stalker/data-access/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.spec.ts b/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.spec.ts index 041d23ab1..38af4f584 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.spec.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.spec.ts @@ -1,5 +1,11 @@ import { Location } from '@angular/common'; -import { Component, input, output, signal } from '@angular/core'; +import { + Component, + input, + output, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { MatSnackBar } from '@angular/material/snack-bar'; @@ -33,7 +39,11 @@ function deferred() { return { promise, reject, resolve }; } -@Component({ selector: 'app-vod-details', template: '' }) +@Component({ + selector: 'app-vod-details', + changeDetection: ChangeDetectionStrategy.Eager, + template: '', +}) class StubVodDetailsComponent { readonly playbackSessionKey = input.required(); readonly item = input.required(); @@ -53,7 +63,11 @@ class StubVodDetailsComponent { readonly inlineExternalFallbackRequested = output(); } -@Component({ selector: 'app-stalker-series-view', template: '' }) +@Component({ + selector: 'app-stalker-series-view', + changeDetection: ChangeDetectionStrategy.Eager, + template: '', +}) class StubStalkerSeriesViewComponent { readonly vodWithSeries = input(null); readonly providerOnly = input(false); diff --git a/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.ts b/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.ts index 98fa25f03..96fffc7dd 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.ts @@ -6,6 +6,7 @@ import { inject, input, signal, + ChangeDetectionStrategy, } from '@angular/core'; import { Location } from '@angular/common'; import { MatSnackBar } from '@angular/material/snack-bar'; @@ -55,6 +56,7 @@ import { createPlaybackSessionKey } from '@iptvnator/playback/util'; selector: 'app-stalker-catalog-detail', imports: [StalkerSeriesViewComponent, VodDetailsComponent], templateUrl: './stalker-catalog-detail.component.html', + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ` :host { diff --git a/libs/portal/stalker/feature/src/lib/stalker-collection-detail.component.spec.ts b/libs/portal/stalker/feature/src/lib/stalker-collection-detail.component.spec.ts index dc50a34ca..86d328a7f 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-collection-detail.component.spec.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-collection-detail.component.spec.ts @@ -1,4 +1,10 @@ -import { Component, input, output, signal } from '@angular/core'; +import { + Component, + input, + output, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { MatSnackBar } from '@angular/material/snack-bar'; @@ -22,7 +28,7 @@ import { VodDetailsItem, createStalkerVodItem, } from '@iptvnator/shared/interfaces'; -import { of } from 'rxjs'; +import { of, Subject } from 'rxjs'; import { StalkerCollectionDetailComponent } from './stalker-collection-detail.component'; import { StalkerInlineDetailComponent } from './stalker-inline-detail/stalker-inline-detail.component'; import { createPlaybackSessionKey } from '@iptvnator/playback/util'; @@ -30,6 +36,7 @@ import { createPlaybackSessionKey } from '@iptvnator/playback/util'; @Component({ selector: 'app-content-hero', standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: '', }) class StubContentHeroComponent { @@ -39,6 +46,7 @@ class StubContentHeroComponent { @Component({ selector: 'app-stalker-inline-detail', standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: '', }) class StubStalkerInlineDetailComponent { @@ -200,9 +208,9 @@ describe('StalkerCollectionDetailComponent', () => { instant: (key: string) => key, get: (key: string) => of(key), stream: (key: string) => of(key), - onLangChange: of(null), - onTranslationChange: of(null), - onDefaultLangChange: of(null), + onLangChange: new Subject(), + onTranslationChange: new Subject(), + onDefaultLangChange: new Subject(), }, }, { diff --git a/libs/portal/stalker/feature/src/lib/stalker-favorites-button/stalker-favorites-button.component.ts b/libs/portal/stalker/feature/src/lib/stalker-favorites-button/stalker-favorites-button.component.ts index 315f93494..7c4ca1c57 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-favorites-button/stalker-favorites-button.component.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-favorites-button/stalker-favorites-button.component.ts @@ -1,5 +1,10 @@ import { AsyncPipe } from '@angular/common'; -import { Component, inject, input } from '@angular/core'; +import { + Component, + inject, + input, + ChangeDetectionStrategy, +} from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; import { TranslateModule } from '@ngx-translate/core'; import { BehaviorSubject, map, of, switchMap } from 'rxjs'; @@ -21,6 +26,7 @@ type FavoriteButtonItem = StalkerSelectedVodItem & { selector: 'app-favorites-button', templateUrl: './stalker-favorites-button.component.html', styleUrls: ['./stalker-favorites-button.component.scss'], + changeDetection: ChangeDetectionStrategy.Eager, imports: [AsyncPipe, MatIconModule, TranslateModule], }) export class FavoritesButtonComponent { diff --git a/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.spec.ts b/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.spec.ts index 4d21d905a..f892b3598 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.spec.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.spec.ts @@ -1,4 +1,9 @@ -import { Component, input, output } from '@angular/core'; +import { + Component, + input, + output, + ChangeDetectionStrategy, +} from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { createStalkerVodItem } from '@iptvnator/shared/interfaces'; @@ -6,7 +11,11 @@ import { VodDetailsComponent } from '@iptvnator/ui/playback'; import { StalkerSeriesViewComponent } from '../stalker-series-view/stalker-series-view.component'; import { StalkerInlineDetailComponent } from './stalker-inline-detail.component'; -@Component({ selector: 'app-vod-details', template: '' }) +@Component({ + selector: 'app-vod-details', + changeDetection: ChangeDetectionStrategy.Eager, + template: '', +}) class StubVodDetailsComponent { readonly playbackSessionKey = input.required(); readonly item = input.required(); @@ -25,7 +34,11 @@ class StubVodDetailsComponent { readonly inlineExternalFallbackRequested = output(); } -@Component({ selector: 'app-stalker-series-view', template: '' }) +@Component({ + selector: 'app-stalker-series-view', + changeDetection: ChangeDetectionStrategy.Eager, + template: '', +}) class StubStalkerSeriesViewComponent { readonly vodWithSeries = input(null); readonly providerOnly = input(false); diff --git a/libs/portal/stalker/feature/src/lib/stalker-live-stream-layout/stalker-live-stream-layout.component.spec.ts b/libs/portal/stalker/feature/src/lib/stalker-live-stream-layout/stalker-live-stream-layout.component.spec.ts index 3070f72ac..f58110ea6 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-live-stream-layout/stalker-live-stream-layout.component.spec.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-live-stream-layout/stalker-live-stream-layout.component.spec.ts @@ -998,10 +998,12 @@ describe('StalkerLiveStreamLayoutComponent', () => { // of only growing the client-side render window. itvFullListActive.set(true); itvSelectedCategoryFromCache.set(false); - hasMoreChannels.set(true); fixture.detectChanges(); await fixture.whenStable(); + // Enable pagination after the initial viewport-fill timer has settled + // so this assertion observes the explicit loadMore call below. + hasMoreChannels.set(true); page.set(0); stalkerStore.setPage.mockClear(); component.loadMore(); diff --git a/libs/portal/stalker/feature/src/lib/stalker-live-stream-layout/stalker-live-stream-layout.spec-stubs.ts b/libs/portal/stalker/feature/src/lib/stalker-live-stream-layout/stalker-live-stream-layout.spec-stubs.ts index 420ac1b62..52b6d2e05 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-live-stream-layout/stalker-live-stream-layout.spec-stubs.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-live-stream-layout/stalker-live-stream-layout.spec-stubs.ts @@ -1,4 +1,10 @@ -import { Component, Directive, input, output } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Directive, + input, + output, +} from '@angular/core'; import { type PlaybackFallbackRequest } from '@iptvnator/ui/playback'; import { EpgProgram, @@ -13,6 +19,7 @@ import { */ @Component({ + changeDetection: ChangeDetectionStrategy.Eager, selector: 'app-channel-list-item', standalone: true, template: '', @@ -36,6 +43,7 @@ export class StubChannelListItemComponent { } @Component({ + changeDetection: ChangeDetectionStrategy.Eager, selector: 'app-web-player-view', standalone: true, template: '', @@ -51,6 +59,7 @@ export class StubWebPlayerViewComponent { } @Component({ + changeDetection: ChangeDetectionStrategy.Eager, selector: 'app-audio-player', standalone: true, template: '', @@ -66,6 +75,7 @@ export class StubAudioPlayerComponent { // Matches both live-panel selectors so the host's timeline ↔ list swap can be // asserted by tag name; both branches share the identical contract. @Component({ + changeDetection: ChangeDetectionStrategy.Eager, selector: 'app-epg-timeline, app-epg-list-view', standalone: true, template: ` @@ -97,6 +107,7 @@ export class StubEpgTimelineComponent { } @Component({ + changeDetection: ChangeDetectionStrategy.Eager, selector: 'app-portal-empty-state', standalone: true, template: '', diff --git a/libs/portal/stalker/feature/src/lib/stalker-search/stalker-search.component.spec.ts b/libs/portal/stalker/feature/src/lib/stalker-search/stalker-search.component.spec.ts index 3f957d545..70b02ab47 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-search/stalker-search.component.spec.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-search/stalker-search.component.spec.ts @@ -1,5 +1,11 @@ import { Location } from '@angular/common'; -import { Component, input, output, signal } from '@angular/core'; +import { + Component, + input, + output, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { ActivatedRoute, convertToParamMap } from '@angular/router'; @@ -36,6 +42,7 @@ function deferred() { @Component({ selector: 'app-stalker-inline-detail', standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: '', }) class StubStalkerInlineDetailComponent { diff --git a/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.component.html b/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.component.html index e7aed956f..0d01f950f 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.component.html +++ b/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.component.html @@ -244,7 +244,11 @@ [downloadAdapter]="episodeDownloadAdapter()" [isLoading]=" isVodSeriesSeasonsLoading() || - isCurrentSeasonLoading(seasonContainer?.selectedSeason()) + isCurrentSeasonLoading( + $safeNavigationMigration( + seasonContainer?.selectedSeason() + ) + ) " [seasonWatchBatchRunning]="seasonWatchBatchRunning()" [hasUnloadedSeasons]="hasUnloadedVodSeasons()" diff --git a/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.component.spec.ts b/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.component.spec.ts index 2854361c7..6909d20c2 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.component.spec.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.component.spec.ts @@ -1,4 +1,4 @@ -import { Component, input, output, signal } from '@angular/core'; +import { signal } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { Router } from '@angular/router'; @@ -6,7 +6,6 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { TranslatePipe, TranslateService } from '@ngx-translate/core'; import { MockPipe } from 'ng-mocks'; import { SeasonContainerComponent } from '@iptvnator/ui/components'; -import type { SeasonEpisodeDownloadAdapter } from '@iptvnator/portal/shared/data-access'; import { PORTAL_EXTERNAL_PLAYBACK, PORTAL_PLAYBACK_POSITIONS, @@ -23,63 +22,11 @@ import { EMPTY, of } from 'rxjs'; import { FavoritesButtonComponent } from '../stalker-favorites-button/stalker-favorites-button.component'; import { StalkerSeriesViewComponent } from './stalker-series-view.component'; -@Component({ - selector: 'app-season-container', - template: '
', -}) -class StubSeasonContainerComponent { - readonly seasons = input(null); - readonly hasUnloadedSeasons = input(false); - readonly seriesId = input(null); - readonly playlistId = input(''); - readonly seriesTitle = input(undefined); - readonly playbackPositions = input(null); - readonly openingEpisodeId = input(null); - readonly activeEpisodeId = input(null); - readonly playingEpisodeId = input(null); - readonly seasonDescriptions = input(null); - readonly isLoading = input(false); - readonly downloadsEnabled = input(true); - readonly downloadAdapter = input(null); - readonly seasonWatchBatchRunning = input(false); - readonly seasonSelected = output(); - readonly episodeClicked = output(); - readonly playbackToggleRequested = output(); - readonly seasonPlaybackToggleRequested = output(); - readonly selectedSeason = signal(undefined); -} - -@Component({ - selector: 'app-portal-inline-player', - standalone: true, - template: '', -}) -class StubPortalInlinePlayerComponent { - readonly playbackSessionKey = input.required(); - readonly playback = input(null); - readonly episodeMetadata = input(null); - readonly seriesTitle = input(null); - readonly seriesNavigation = input(null); - readonly upNextEpisodes = input(null); - readonly timeUpdate = output(); - readonly closed = output(); - readonly streamUrlCopied = output(); - readonly externalFallbackRequested = output(); - readonly playbackEnded = output(); - readonly previousEpisodeRequested = output(); - readonly nextEpisodeRequested = output(); - readonly upNextEpisodeSelected = output(); -} - -@Component({ - selector: 'app-favorites-button', - standalone: true, - template: '', -}) -class StubFavoritesButtonComponent { - readonly itemId = input(undefined); - readonly item = input(null); -} +import { + StubFavoritesButtonComponent, + StubPortalInlinePlayerComponent, + StubSeasonContainerComponent, +} from './stalker-series-view.test-helpers'; describe('StalkerSeriesViewComponent', () => { let fixture: ComponentFixture; diff --git a/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.component.ts b/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.component.ts index d9a13ae33..6ff0ff722 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.component.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.component.ts @@ -8,6 +8,7 @@ import { output, signal, untracked, + ChangeDetectionStrategy, } from '@angular/core'; import { Location } from '@angular/common'; import { MatIcon } from '@angular/material/icon'; @@ -169,6 +170,7 @@ interface StalkerSeriesPlaybackRequestContext { SeasonContainerComponent, MatIcon, ], + changeDetection: ChangeDetectionStrategy.Eager, providers: [StalkerSeriesTmdbSeasonsService], }) export class StalkerSeriesViewComponent implements OnDestroy { diff --git a/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.test-helpers.ts b/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.test-helpers.ts new file mode 100644 index 000000000..c3d7beaf5 --- /dev/null +++ b/libs/portal/stalker/feature/src/lib/stalker-series-view/stalker-series-view.test-helpers.ts @@ -0,0 +1,70 @@ +import { + ChangeDetectionStrategy, + Component, + input, + output, + signal, +} from '@angular/core'; +import type { SeasonEpisodeDownloadAdapter } from '@iptvnator/portal/shared/data-access'; + +@Component({ + selector: 'app-season-container', + standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, + template: '
', +}) +export class StubSeasonContainerComponent { + readonly seasons = input(null); + readonly hasUnloadedSeasons = input(false); + readonly seriesId = input(null); + readonly playlistId = input(''); + readonly seriesTitle = input(undefined); + readonly playbackPositions = input(null); + readonly openingEpisodeId = input(null); + readonly activeEpisodeId = input(null); + readonly playingEpisodeId = input(null); + readonly seasonDescriptions = input(null); + readonly isLoading = input(false); + readonly downloadsEnabled = input(true); + readonly downloadAdapter = input(null); + readonly seasonWatchBatchRunning = input(false); + readonly seasonSelected = output(); + readonly episodeClicked = output(); + readonly playbackToggleRequested = output(); + readonly seasonPlaybackToggleRequested = output(); + readonly selectedSeason = signal(undefined); +} + +@Component({ + selector: 'app-portal-inline-player', + standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, + template: '', +}) +export class StubPortalInlinePlayerComponent { + readonly playbackSessionKey = input.required(); + readonly playback = input(null); + readonly episodeMetadata = input(null); + readonly seriesTitle = input(null); + readonly seriesNavigation = input(null); + readonly upNextEpisodes = input(null); + readonly timeUpdate = output(); + readonly closed = output(); + readonly streamUrlCopied = output(); + readonly externalFallbackRequested = output(); + readonly playbackEnded = output(); + readonly previousEpisodeRequested = output(); + readonly nextEpisodeRequested = output(); + readonly upNextEpisodeSelected = output(); +} + +@Component({ + selector: 'app-favorites-button', + standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, + template: '', +}) +export class StubFavoritesButtonComponent { + readonly itemId = input(undefined); + readonly item = input(null); +} diff --git a/libs/portal/stalker/feature/src/test-setup.ts b/libs/portal/stalker/feature/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/portal/stalker/feature/src/test-setup.ts +++ b/libs/portal/stalker/feature/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/portal/stalker/feature/tsconfig.json b/libs/portal/stalker/feature/tsconfig.json index 7d33479d8..4853e7663 100644 --- a/libs/portal/stalker/feature/tsconfig.json +++ b/libs/portal/stalker/feature/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/portal/xtream/data-access/src/test-setup.ts b/libs/portal/xtream/data-access/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/portal/xtream/data-access/src/test-setup.ts +++ b/libs/portal/xtream/data-access/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/portal/xtream/data-access/tsconfig.json b/libs/portal/xtream/data-access/tsconfig.json index 7d33479d8..4853e7663 100644 --- a/libs/portal/xtream/data-access/tsconfig.json +++ b/libs/portal/xtream/data-access/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/portal/xtream/feature/src/lib/global-search-results/global-search-results.component.ts b/libs/portal/xtream/feature/src/lib/global-search-results/global-search-results.component.ts index 61dca7165..1b0185345 100644 --- a/libs/portal/xtream/feature/src/lib/global-search-results/global-search-results.component.ts +++ b/libs/portal/xtream/feature/src/lib/global-search-results/global-search-results.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, ChangeDetectionStrategy } from '@angular/core'; import { XtreamStore } from '@iptvnator/portal/xtream/data-access'; import { SearchResultsComponent } from '../search-results/search-results.component'; @@ -6,6 +6,7 @@ import { SearchResultsComponent } from '../search-results/search-results.compone selector: 'app-global-search-results', imports: [SearchResultsComponent], providers: [XtreamStore], + changeDetection: ChangeDetectionStrategy.Eager, template: '', }) export class GlobalSearchResultsComponent {} diff --git a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout-stubs.spec-data.ts b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout-stubs.spec-data.ts index 05411eff2..8da6ec9fa 100644 --- a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout-stubs.spec-data.ts +++ b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout-stubs.spec-data.ts @@ -1,4 +1,11 @@ -import { Component, Directive, input, output, signal } from '@angular/core'; +import { + ChangeDetectionStrategy, + Component, + Directive, + input, + output, + signal, +} from '@angular/core'; import { EpgProgram, RecordingStartMetadata, @@ -11,6 +18,7 @@ import { import { PlaybackFallbackRequest } from '@iptvnator/ui/playback'; @Component({ + changeDetection: ChangeDetectionStrategy.Eager, selector: 'app-portal-channels-list', standalone: true, template: '
', @@ -26,6 +34,7 @@ export class StubPortalChannelsListComponent { } @Component({ + changeDetection: ChangeDetectionStrategy.Eager, selector: 'app-grid-list', standalone: true, template: '
', @@ -43,6 +52,7 @@ export class StubGridListComponent { } @Component({ + changeDetection: ChangeDetectionStrategy.Eager, selector: 'app-web-player-view', standalone: true, template: '', @@ -60,6 +70,7 @@ export class StubWebPlayerViewComponent { // Matches both live-panel selectors so the host's timeline ↔ list swap can be // asserted by tag name; both branches share the identical contract. @Component({ + changeDetection: ChangeDetectionStrategy.Eager, selector: 'app-epg-timeline, app-epg-list-view', standalone: true, template: ` diff --git a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.sidebar-levels.spec.ts b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.sidebar-levels.spec.ts index 009de836b..523dfbc9e 100644 --- a/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.sidebar-levels.spec.ts +++ b/libs/portal/xtream/feature/src/lib/live-stream-layout/live-stream-layout.sidebar-levels.spec.ts @@ -1,6 +1,13 @@ import { EpgArchiveDownloadService } from '@iptvnator/ui/epg'; import { EpgArchiveCopyService } from '@iptvnator/ui/epg'; -import { Component, Directive, input, output, signal } from '@angular/core'; +import { + Component, + Directive, + input, + output, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { ActivatedRoute, Router, convertToParamMap } from '@angular/router'; @@ -40,6 +47,7 @@ import { LiveStreamLayoutComponent } from './live-stream-layout.component'; @Component({ selector: 'app-portal-channels-list', standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: '', }) class StubPortalChannelsListComponent { @@ -56,13 +64,19 @@ class StubPortalChannelsListComponent { @Component({ selector: 'app-channel-list-hidden-state', standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: '
', }) class StubChannelListHiddenStateComponent { readonly restore = output(); } -@Component({ selector: 'app-grid-list', standalone: true, template: '' }) +@Component({ + selector: 'app-grid-list', + standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, + template: '', +}) class StubGridListComponent { readonly items = input([]); readonly isLoading = input(false); @@ -75,6 +89,7 @@ class StubGridListComponent { @Component({ selector: 'app-web-player-view, app-epg-timeline, app-epg-list-view', standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: '', }) class StubPassiveComponent {} diff --git a/libs/portal/xtream/feature/src/lib/live-stream-layout/xtream-live-channel-navigation.service.spec.ts b/libs/portal/xtream/feature/src/lib/live-stream-layout/xtream-live-channel-navigation.service.spec.ts index c3df716e3..88bf0f9e0 100644 --- a/libs/portal/xtream/feature/src/lib/live-stream-layout/xtream-live-channel-navigation.service.spec.ts +++ b/libs/portal/xtream/feature/src/lib/live-stream-layout/xtream-live-channel-navigation.service.spec.ts @@ -1,4 +1,10 @@ -import { Component, computed, inject, signal } from '@angular/core'; +import { + Component, + computed, + inject, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { TestBed } from '@angular/core/testing'; import { ActivatedRoute, @@ -19,6 +25,7 @@ const other = { xtream_id: 3, category_id: '20', title: 'Other' }; @Component({ template: '', + changeDetection: ChangeDetectionStrategy.Eager, providers: [XtreamLiveChannelNavigationService], }) class NavigationHostComponent { diff --git a/libs/portal/xtream/feature/src/lib/portal-channels-list/portal-channels-list.component.spec.ts b/libs/portal/xtream/feature/src/lib/portal-channels-list/portal-channels-list.component.spec.ts index 573994d4a..1c40d00b3 100644 --- a/libs/portal/xtream/feature/src/lib/portal-channels-list/portal-channels-list.component.spec.ts +++ b/libs/portal/xtream/feature/src/lib/portal-channels-list/portal-channels-list.component.spec.ts @@ -114,9 +114,9 @@ describe('PortalChannelsListComponent', () => { ? 'Loading channels...' : key ), - onLangChange: of(null), - onTranslationChange: of(null), - onDefaultLangChange: of(null), + onLangChange: new Subject(), + onTranslationChange: new Subject(), + onDefaultLangChange: new Subject(), currentLang: 'en', defaultLang: 'en', }, diff --git a/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.spec.ts b/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.spec.ts index ecb46bb7c..17808cb76 100644 --- a/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.spec.ts +++ b/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.spec.ts @@ -1,4 +1,10 @@ -import { Component, input, output, signal } from '@angular/core'; +import { + Component, + input, + output, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { Location } from '@angular/common'; @@ -31,6 +37,7 @@ import { createPlaybackSessionKey } from '@iptvnator/playback/util'; @Component({ selector: 'app-season-container', standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: '
', }) class StubSeasonContainerComponent { @@ -54,6 +61,7 @@ class StubSeasonContainerComponent { @Component({ selector: 'app-portal-inline-player', standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: '', }) class StubPortalInlinePlayerComponent { @@ -76,6 +84,7 @@ class StubPortalInlinePlayerComponent { @Component({ selector: 'mat-icon', standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: '', }) class StubMatIconComponent {} @@ -460,12 +469,8 @@ describe('SerialDetailsComponent', () => { '2': 'TMDB season 2 overview', }, episodes: { - '1': [ - { id: '1001', episode_num: 1, title: 'E1', season: 1 }, - ], - '2': [ - { id: '2001', episode_num: 1, title: 'E1', season: 2 }, - ], + '1': [{ id: '1001', episode_num: 1, title: 'E1', season: 1 }], + '2': [{ id: '2001', episode_num: 1, title: 'E1', season: 2 }], }, }); diff --git a/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.ts b/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.ts index da97b2282..f647f3785 100644 --- a/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.ts +++ b/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.ts @@ -8,6 +8,7 @@ import { OnInit, signal, untracked, + ChangeDetectionStrategy, } from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { MatIcon } from '@angular/material/icon'; @@ -85,6 +86,7 @@ import { createSerialPlaybackSessionKey } from './serial-playback-session-key'; SerialDetailsSeasonWatchService, SerialDetailsSimilarService, ], + changeDetection: ChangeDetectionStrategy.Eager, imports: [ DetailActionsTemplateDirective, DetailMetaTemplateDirective, diff --git a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.actions.spec.ts b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.actions.spec.ts index 478150ee7..618a9b972 100644 --- a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.actions.spec.ts +++ b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.actions.spec.ts @@ -1,5 +1,11 @@ import { Location } from '@angular/common'; -import { Component, input, output, signal } from '@angular/core'; +import { + Component, + input, + output, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { MatSnackBar } from '@angular/material/snack-bar'; @@ -32,6 +38,7 @@ import { createPlaybackSessionKey } from '@iptvnator/playback/util'; @Component({ selector: 'app-portal-inline-player', standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: '
', }) class StubPortalInlinePlayerComponent { diff --git a/libs/portal/xtream/feature/src/lib/xtream-content-gate.component.spec.ts b/libs/portal/xtream/feature/src/lib/xtream-content-gate.component.spec.ts index 255833334..cd5ffceca 100644 --- a/libs/portal/xtream/feature/src/lib/xtream-content-gate.component.spec.ts +++ b/libs/portal/xtream/feature/src/lib/xtream-content-gate.component.spec.ts @@ -1,4 +1,9 @@ -import { Component, input, signal } from '@angular/core'; +import { + Component, + input, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; @@ -15,6 +20,7 @@ import { XtreamContentGateComponent } from './xtream-content-gate.component'; @Component({ selector: 'app-playlist-error-view', standalone: true, + changeDetection: ChangeDetectionStrategy.Eager, template: `
{{ title() }} @@ -29,13 +35,14 @@ class MockPlaylistErrorViewComponent { describe('XtreamContentGateComponent', () => { let fixture: ComponentFixture; - const contentInitBlockReason = - signal(null); + const contentInitBlockReason = signal( + null + ); const isContentInitialized = signal(false); const isPendingRestoreBlocked = signal(false); - const portalStatus = signal<'active' | 'inactive' | 'expired' | 'unavailable'>( - 'active' - ); + const portalStatus = signal< + 'active' | 'inactive' | 'expired' | 'unavailable' + >('active'); const retryContentInitialization = jest.fn().mockResolvedValue(undefined); beforeEach(async () => { @@ -114,7 +121,9 @@ describe('XtreamContentGateComponent', () => { fixture.detectChanges(); expect(fixture.nativeElement.querySelector('.mock-error')).toBeNull(); - expect(fixture.nativeElement.querySelector('router-outlet')).not.toBeNull(); + expect( + fixture.nativeElement.querySelector('router-outlet') + ).not.toBeNull(); }); it('keeps the child outlet unavailable while parked state is pending', () => { @@ -140,7 +149,9 @@ describe('XtreamContentGateComponent', () => { expect(warning?.textContent).toContain( 'PORTALS.ERROR_VIEW.PORTAL_UNAVAILABLE.TITLE' ); - expect(fixture.nativeElement.querySelector('router-outlet')).not.toBeNull(); + expect( + fixture.nativeElement.querySelector('router-outlet') + ).not.toBeNull(); }); it('retries content initialization from the blocked state', () => { diff --git a/libs/portal/xtream/feature/src/test-setup.ts b/libs/portal/xtream/feature/src/test-setup.ts index 119f7a954..d23de2742 100644 --- a/libs/portal/xtream/feature/src/test-setup.ts +++ b/libs/portal/xtream/feature/src/test-setup.ts @@ -1,9 +1,12 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; import { installDuplicateVideoJsQualityLevelsWarnFilter } from '@iptvnator/shared/testing'; installDuplicateVideoJsQualityLevelsWarnFilter(); -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/portal/xtream/feature/tsconfig.json b/libs/portal/xtream/feature/tsconfig.json index 7d33479d8..4853e7663 100644 --- a/libs/portal/xtream/feature/tsconfig.json +++ b/libs/portal/xtream/feature/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/services/src/test-setup.ts b/libs/services/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/services/src/test-setup.ts +++ b/libs/services/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/services/tsconfig.json b/libs/services/tsconfig.json index 7cac292e3..e8bfd4b89 100644 --- a/libs/services/tsconfig.json +++ b/libs/services/tsconfig.json @@ -8,7 +8,8 @@ "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/shared/database/tsconfig.json b/libs/shared/database/tsconfig.json index 106e941d7..727a983bd 100644 --- a/libs/shared/database/tsconfig.json +++ b/libs/shared/database/tsconfig.json @@ -9,7 +9,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "noPropertyAccessFromIndexSignature": true, - "esModuleInterop": true + "esModuleInterop": true, + "ignoreDeprecations": "6.0" }, "files": [], "include": [], diff --git a/libs/shared/host-health/tsconfig.json b/libs/shared/host-health/tsconfig.json index ab8e5af25..26fb021ed 100644 --- a/libs/shared/host-health/tsconfig.json +++ b/libs/shared/host-health/tsconfig.json @@ -8,7 +8,8 @@ "noImplicitOverride": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "noPropertyAccessFromIndexSignature": true + "noPropertyAccessFromIndexSignature": true, + "ignoreDeprecations": "6.0" }, "files": [], "include": [], diff --git a/libs/shared/interfaces/package.json b/libs/shared/interfaces/package.json index 519b83807..6d1e149d5 100644 --- a/libs/shared/interfaces/package.json +++ b/libs/shared/interfaces/package.json @@ -6,7 +6,7 @@ "main": "./src/index.js", "types": "./src/index.d.ts", "dependencies": { - "ngx-indexed-db": "^22.0.0", + "ngx-indexed-db": "22.0.0", "tslib": "^2.3.0" } } diff --git a/libs/shared/interfaces/tsconfig.json b/libs/shared/interfaces/tsconfig.json index c0906eb5f..0538f5bfe 100644 --- a/libs/shared/interfaces/tsconfig.json +++ b/libs/shared/interfaces/tsconfig.json @@ -8,7 +8,8 @@ "noImplicitOverride": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "noPropertyAccessFromIndexSignature": true + "noPropertyAccessFromIndexSignature": true, + "ignoreDeprecations": "6.0" }, "files": [], "include": [], diff --git a/libs/shared/logging/tsconfig.json b/libs/shared/logging/tsconfig.json index ab8e5af25..26fb021ed 100644 --- a/libs/shared/logging/tsconfig.json +++ b/libs/shared/logging/tsconfig.json @@ -8,7 +8,8 @@ "noImplicitOverride": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "noPropertyAccessFromIndexSignature": true + "noPropertyAccessFromIndexSignature": true, + "ignoreDeprecations": "6.0" }, "files": [], "include": [], diff --git a/libs/shared/m3u-utils/tsconfig.json b/libs/shared/m3u-utils/tsconfig.json index c0906eb5f..0538f5bfe 100644 --- a/libs/shared/m3u-utils/tsconfig.json +++ b/libs/shared/m3u-utils/tsconfig.json @@ -8,7 +8,8 @@ "noImplicitOverride": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "noPropertyAccessFromIndexSignature": true + "noPropertyAccessFromIndexSignature": true, + "ignoreDeprecations": "6.0" }, "files": [], "include": [], diff --git a/libs/shared/marketing-fixtures/tsconfig.json b/libs/shared/marketing-fixtures/tsconfig.json index c0906eb5f..0538f5bfe 100644 --- a/libs/shared/marketing-fixtures/tsconfig.json +++ b/libs/shared/marketing-fixtures/tsconfig.json @@ -8,7 +8,8 @@ "noImplicitOverride": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "noPropertyAccessFromIndexSignature": true + "noPropertyAccessFromIndexSignature": true, + "ignoreDeprecations": "6.0" }, "files": [], "include": [], diff --git a/libs/ui/components/src/lib/channel-list-container/all-channels-view/all-channels-view.component.html b/libs/ui/components/src/lib/channel-list-container/all-channels-view/all-channels-view.component.html index e15234637..63087f210 100644 --- a/libs/ui/components/src/lib/channel-list-container/all-channels-view/all-channels-view.component.html +++ b/libs/ui/components/src/lib/channel-list-container/all-channels-view/all-channels-view.component.html @@ -86,7 +86,10 @@ [showDetailsContextMenu]="true" [isRadio]="channel?.radio === 'true'" [epgProgram]=" - epgMetadataMap().get(getChannelEpgKey(channel))?.epgProgram + $safeNavigationMigration( + epgMetadataMap().get(getChannelEpgKey(channel)) + ?.epgProgram + ) " [progressPercentage]=" epgMetadataMap().get(getChannelEpgKey(channel)) diff --git a/libs/ui/components/src/lib/channel-list-container/favorites-view/favorites-view.component.html b/libs/ui/components/src/lib/channel-list-container/favorites-view/favorites-view.component.html index 9712df0af..04853e29b 100644 --- a/libs/ui/components/src/lib/channel-list-container/favorites-view/favorites-view.component.html +++ b/libs/ui/components/src/lib/channel-list-container/favorites-view/favorites-view.component.html @@ -25,7 +25,10 @@ [showEpg]="shouldShowEpg()" [isRadio]="channel?.radio === 'true'" [epgProgram]=" - epgMetadataMap().get(getChannelEpgKey(channel))?.epgProgram + $safeNavigationMigration( + epgMetadataMap().get(getChannelEpgKey(channel)) + ?.epgProgram + ) " [progressPercentage]=" epgMetadataMap().get(getChannelEpgKey(channel)) diff --git a/libs/ui/components/src/lib/channel-list-container/groups-view/groups-view.component.html b/libs/ui/components/src/lib/channel-list-container/groups-view/groups-view.component.html index bc69bdae0..94947c878 100644 --- a/libs/ui/components/src/lib/channel-list-container/groups-view/groups-view.component.html +++ b/libs/ui/components/src/lib/channel-list-container/groups-view/groups-view.component.html @@ -269,8 +269,10 @@ [showDetailsContextMenu]="true" [isRadio]="channel?.radio === 'true'" [epgProgram]=" - epgMetadataMap().get(getChannelEpgKey(channel)) - ?.epgProgram + $safeNavigationMigration( + epgMetadataMap().get(getChannelEpgKey(channel)) + ?.epgProgram + ) " [progressPercentage]=" epgMetadataMap().get(getChannelEpgKey(channel)) diff --git a/libs/ui/components/src/lib/confirm-dialog/confirm-dialog.component.ts b/libs/ui/components/src/lib/confirm-dialog/confirm-dialog.component.ts index ffd13e5ab..28278b175 100644 --- a/libs/ui/components/src/lib/confirm-dialog/confirm-dialog.component.ts +++ b/libs/ui/components/src/lib/confirm-dialog/confirm-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, inject } from '@angular/core'; +import { Component, inject, ChangeDetectionStrategy } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; import { TranslateModule } from '@ngx-translate/core'; @@ -16,6 +16,8 @@ export interface ConfirmDialogData { @Component({ imports: [MatButtonModule, MatDialogModule, TranslateModule], + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, template: `

{{ dialogData.title }} diff --git a/libs/ui/components/src/lib/content-hero/content-hero.component.ts b/libs/ui/components/src/lib/content-hero/content-hero.component.ts index b89927779..68f774f47 100644 --- a/libs/ui/components/src/lib/content-hero/content-hero.component.ts +++ b/libs/ui/components/src/lib/content-hero/content-hero.component.ts @@ -9,6 +9,7 @@ import { untracked, viewChild, ElementRef, + ChangeDetectionStrategy, } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; @@ -25,6 +26,8 @@ import { NgxSkeletonLoaderComponent } from 'ngx-skeleton-loader'; TranslateModule, ], templateUrl: './content-hero.component.html', + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styleUrls: ['./content-hero.component.scss'], }) export class ContentHeroComponent { diff --git a/libs/ui/components/src/lib/expandable-text/expandable-text.component.ts b/libs/ui/components/src/lib/expandable-text/expandable-text.component.ts index 6d372ef94..a35f18b95 100644 --- a/libs/ui/components/src/lib/expandable-text/expandable-text.component.ts +++ b/libs/ui/components/src/lib/expandable-text/expandable-text.component.ts @@ -8,6 +8,7 @@ import { signal, untracked, viewChild, + ChangeDetectionStrategy, } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; import { TranslateModule } from '@ngx-translate/core'; @@ -46,6 +47,8 @@ import { TranslateModule } from '@ngx-translate/core'; } `, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ` :host { diff --git a/libs/ui/components/src/lib/portal-detail-shell/content-about.component.ts b/libs/ui/components/src/lib/portal-detail-shell/content-about.component.ts index 5bedb7d8e..449a89385 100644 --- a/libs/ui/components/src/lib/portal-detail-shell/content-about.component.ts +++ b/libs/ui/components/src/lib/portal-detail-shell/content-about.component.ts @@ -1,5 +1,10 @@ import { NgTemplateOutlet } from '@angular/common'; -import { Component, TemplateRef, input } from '@angular/core'; +import { + Component, + TemplateRef, + input, + ChangeDetectionStrategy, +} from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; /** @@ -48,6 +53,8 @@ import { TranslateModule } from '@ngx-translate/core';

`, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styleUrls: ['./content-about.component.scss'], }) export class ContentAboutComponent { diff --git a/libs/ui/components/src/lib/portal-detail-shell/portal-detail-shell.component.ts b/libs/ui/components/src/lib/portal-detail-shell/portal-detail-shell.component.ts index 85d592d15..1ec536f29 100644 --- a/libs/ui/components/src/lib/portal-detail-shell/portal-detail-shell.component.ts +++ b/libs/ui/components/src/lib/portal-detail-shell/portal-detail-shell.component.ts @@ -11,6 +11,7 @@ import { input, output, viewChild, + ChangeDetectionStrategy, } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; import { TranslateModule } from '@ngx-translate/core'; @@ -47,6 +48,8 @@ import { ], templateUrl: './portal-detail-shell.component.html', styleUrls: ['./portal-detail-shell.component.scss'], + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, host: { tabindex: '0', role: 'region', diff --git a/libs/ui/components/src/lib/progress-capsule/progress-capsule.component.ts b/libs/ui/components/src/lib/progress-capsule/progress-capsule.component.ts index f921028d9..943f9a878 100644 --- a/libs/ui/components/src/lib/progress-capsule/progress-capsule.component.ts +++ b/libs/ui/components/src/lib/progress-capsule/progress-capsule.component.ts @@ -1,4 +1,9 @@ -import { Component, computed, input } from '@angular/core'; +import { + Component, + computed, + input, + ChangeDetectionStrategy, +} from '@angular/core'; @Component({ selector: 'app-progress-capsule', @@ -14,6 +19,8 @@ import { Component, computed, input } from '@angular/core'; >
`, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ` .progress-capsule { diff --git a/libs/ui/components/src/lib/season-container/episode-info-dialog.component.ts b/libs/ui/components/src/lib/season-container/episode-info-dialog.component.ts index b8815ad86..59c78ca4c 100644 --- a/libs/ui/components/src/lib/season-container/episode-info-dialog.component.ts +++ b/libs/ui/components/src/lib/season-container/episode-info-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, inject } from '@angular/core'; +import { Component, inject, ChangeDetectionStrategy } from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MAT_DIALOG_DATA, @@ -25,9 +25,7 @@ export function buildEpisodeInfoDialogData( fallbackSeasonKey: string | undefined ): EpisodeInfoDialogData { const info = - !episode.info || Array.isArray(episode.info) - ? undefined - : episode.info; + !episode.info || Array.isArray(episode.info) ? undefined : episode.info; const seasonNumber = Number(episode.season || fallbackSeasonKey || 1); const episodeNumber = Number(episode.episode_num || 1); return { @@ -107,6 +105,8 @@ export const EPISODE_INFO_PLAY = 'play' as const;
`, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ` :host { diff --git a/libs/ui/components/src/lib/watched-badge/watched-badge.component.ts b/libs/ui/components/src/lib/watched-badge/watched-badge.component.ts index 91f1806bd..cba3fe79a 100644 --- a/libs/ui/components/src/lib/watched-badge/watched-badge.component.ts +++ b/libs/ui/components/src/lib/watched-badge/watched-badge.component.ts @@ -1,4 +1,4 @@ -import { Component, input } from '@angular/core'; +import { Component, input, ChangeDetectionStrategy } from '@angular/core'; import { MatIcon } from '@angular/material/icon'; @Component({ @@ -12,6 +12,8 @@ import { MatIcon } from '@angular/material/icon';
} `, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection -- Preserve pre-Angular 22 eager checking during the framework upgrade. + changeDetection: ChangeDetectionStrategy.Eager, styles: [ ` .watched-badge { diff --git a/libs/ui/components/src/test-setup.ts b/libs/ui/components/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/ui/components/src/test-setup.ts +++ b/libs/ui/components/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/ui/components/tsconfig.json b/libs/ui/components/tsconfig.json index e05028a82..36f9cac37 100644 --- a/libs/ui/components/tsconfig.json +++ b/libs/ui/components/tsconfig.json @@ -8,7 +8,8 @@ "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/ui/epg/src/lib/epg-item-description/epg-item-description.component.ts b/libs/ui/epg/src/lib/epg-item-description/epg-item-description.component.ts index 6cbf7e3e0..de527bda8 100644 --- a/libs/ui/epg/src/lib/epg-item-description/epg-item-description.component.ts +++ b/libs/ui/epg/src/lib/epg-item-description/epg-item-description.component.ts @@ -1,5 +1,10 @@ import { DatePipe } from '@angular/common'; -import { Component, computed, inject } from '@angular/core'; +import { + Component, + computed, + inject, + ChangeDetectionStrategy, +} from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { MatIcon } from '@angular/material/icon'; import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; @@ -33,6 +38,7 @@ export type EpgItemDialogData = EpgProgram & { selector: 'app-epg-item-description', templateUrl: './epg-item-description.component.html', styleUrls: ['./epg-item-description.component.scss'], + changeDetection: ChangeDetectionStrategy.Eager, imports: [DatePipe, MatDialogModule, MatIcon, TranslatePipe], }) export class EpgItemDescriptionComponent { diff --git a/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.ts b/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.ts index 37c55b5ee..9b633a84f 100644 --- a/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.ts +++ b/libs/ui/epg/src/lib/epg-progress-panel/epg-progress-panel.component.ts @@ -1,4 +1,10 @@ -import { Component, computed, inject, signal } from '@angular/core'; +import { + Component, + computed, + inject, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { MatButtonModule } from '@angular/material/button'; import { MAT_DIALOG_DATA, @@ -26,6 +32,7 @@ interface EpgTrustConfirmDialogData { @Component({ selector: 'app-epg-trust-confirm-dialog', imports: [MatButtonModule, MatDialogModule, TranslatePipe], + changeDetection: ChangeDetectionStrategy.Eager, template: `

{{ data.title }}

@@ -57,6 +64,7 @@ class EpgTrustConfirmDialogComponent { TranslatePipe, ], templateUrl: './epg-progress-panel.component.html', + changeDetection: ChangeDetectionStrategy.Eager, styleUrl: './epg-progress-panel.component.scss', }) export class EpgProgressPanelComponent { diff --git a/libs/ui/epg/src/lib/epg-source-status/epg-source-status.component.ts b/libs/ui/epg/src/lib/epg-source-status/epg-source-status.component.ts index 6c36d4137..e5fb3777d 100644 --- a/libs/ui/epg/src/lib/epg-source-status/epg-source-status.component.ts +++ b/libs/ui/epg/src/lib/epg-source-status/epg-source-status.component.ts @@ -6,6 +6,7 @@ import { inject, input, signal, + ChangeDetectionStrategy, } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; @@ -17,12 +18,7 @@ import { } from '@iptvnator/epg/data-access'; type BadgeStatus = - | 'loading' - | 'queued' - | 'fresh' - | 'stale' - | 'error' - | 'unknown'; + 'loading' | 'queued' | 'fresh' | 'stale' | 'error' | 'unknown'; @Component({ selector: 'app-epg-source-status', @@ -34,6 +30,7 @@ type BadgeStatus = TranslatePipe, ], templateUrl: './epg-source-status.component.html', + changeDetection: ChangeDetectionStrategy.Eager, styleUrl: './epg-source-status.component.scss', }) export class EpgSourceStatusComponent implements OnInit { @@ -61,10 +58,10 @@ export class EpgSourceStatusComponent implements OnInit { return this.isFresh() ? 'fresh' : 'stale'; }); - readonly errorMessage = computed(() => - this.epgProgress - .imports() - .find((item) => item.url === this.url())?.error + readonly errorMessage = computed( + () => + this.epgProgress.imports().find((item) => item.url === this.url()) + ?.error ); constructor() { diff --git a/libs/ui/epg/tsconfig.json b/libs/ui/epg/tsconfig.json index f7fcb0d81..8fcb75804 100644 --- a/libs/ui/epg/tsconfig.json +++ b/libs/ui/epg/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/ui/pipes/src/test-setup.ts b/libs/ui/pipes/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/ui/pipes/src/test-setup.ts +++ b/libs/ui/pipes/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/ui/pipes/tsconfig.json b/libs/ui/pipes/tsconfig.json index e05028a82..36f9cac37 100644 --- a/libs/ui/pipes/tsconfig.json +++ b/libs/ui/pipes/tsconfig.json @@ -8,7 +8,8 @@ "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/ui/playback/src/lib/art-player/art-player.component.ts b/libs/ui/playback/src/lib/art-player/art-player.component.ts index 79903205f..1dc49464a 100644 --- a/libs/ui/playback/src/lib/art-player/art-player.component.ts +++ b/libs/ui/playback/src/lib/art-player/art-player.component.ts @@ -10,6 +10,7 @@ import { signal, SimpleChanges, viewChild, + ChangeDetectionStrategy, } from '@angular/core'; import Artplayer from 'artplayer'; import { Channel, createDevLogger } from '@iptvnator/shared/interfaces'; @@ -46,6 +47,7 @@ Artplayer.AUTO_PLAYBACK_TIMEOUT = 10000; ], providers: [WebVideoControlsAdapter], templateUrl: './art-player.component.html', + changeDetection: ChangeDetectionStrategy.Eager, styleUrls: ['./art-player.component.scss'], }) export class ArtPlayerComponent implements OnInit, OnDestroy, OnChanges { diff --git a/libs/ui/playback/src/lib/audio-player/audio-player.component.ts b/libs/ui/playback/src/lib/audio-player/audio-player.component.ts index 45a1bdfa4..e49cc7ef7 100644 --- a/libs/ui/playback/src/lib/audio-player/audio-player.component.ts +++ b/libs/ui/playback/src/lib/audio-player/audio-player.component.ts @@ -11,6 +11,7 @@ import { signal, untracked, viewChild, + ChangeDetectionStrategy, } from '@angular/core'; import { extractDominantColor } from './extract-color'; import { FormsModule } from '@angular/forms'; @@ -159,6 +160,7 @@ import { ChannelActions } from '@iptvnator/m3u-state';
`, styleUrls: ['./audio-player.component.scss'], + changeDetection: ChangeDetectionStrategy.Eager, imports: [ FormsModule, MatButtonModule, @@ -280,10 +282,7 @@ export class AudioPlayerComponent { this.playState.set('paused'); } - setVolume( - value: number, - options: { emitChange?: boolean } = {} - ) { + setVolume(value: number, options: { emitChange?: boolean } = {}) { const clamped = Math.round(Math.max(0, Math.min(1, value)) * 100) / 100; this.volume.set(clamped); const audio = this.audioRef()?.nativeElement; diff --git a/libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-player.component.html b/libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-player.component.html index 376fce965..0e2d44191 100644 --- a/libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-player.component.html +++ b/libs/ui/playback/src/lib/embedded-mpv-player/embedded-mpv-player.component.html @@ -253,7 +253,11 @@ {{ formatTime(timelineValue()) }} @if (canSeek()) { {{ - formatTime(session()?.durationSeconds) + formatTime( + $safeNavigationMigration( + session()?.durationSeconds + ) + ) }} } @else if (isLivePlayback()) {
`, + changeDetection: ChangeDetectionStrategy.Eager, styleUrl: './workspace-context-panel.component.scss', }) export class WorkspaceCollectionContextPanelComponent { readonly ctx = inject(PortalCollectionContextService); // Root-provided; optional keeps standalone unit tests light. Closes the phone // drawer after a selection, which never navigates on this panel. - private readonly contextDrawer = inject(WorkspaceShellContextDrawerService, { - optional: true, - }); + private readonly contextDrawer = inject( + WorkspaceShellContextDrawerService, + { + optional: true, + } + ); readonly selectedCategory = computed(() => { const categories = this.ctx.categories(); diff --git a/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-context-panel.component.ts b/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-context-panel.component.ts index 91924dd44..d505eb0aa 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-context-panel.component.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-context-panel.component.ts @@ -9,6 +9,7 @@ import { signal, viewChild, ElementRef, + ChangeDetectionStrategy, } from '@angular/core'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { MatIconButton } from '@angular/material/button'; @@ -65,6 +66,7 @@ interface WorkspaceCategoryLike { WorkspaceContextErrorViewComponent, ], templateUrl: './workspace-context-panel.component.html', + changeDetection: ChangeDetectionStrategy.Eager, styleUrl: './workspace-context-panel.component.scss', }) export class WorkspaceContextPanelComponent { diff --git a/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.ts b/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.ts index 568695847..2c736855f 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-context-panel/workspace-settings-context-panel.component.ts @@ -1,5 +1,5 @@ import { Location } from '@angular/common'; -import { Component, inject } from '@angular/core'; +import { Component, inject, ChangeDetectionStrategy } from '@angular/core'; import { MatIconModule } from '@angular/material/icon'; import { RouterLink, RouterLinkActive } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; @@ -12,6 +12,7 @@ import { selector: 'app-workspace-settings-context-panel', imports: [MatIconModule, RouterLink, RouterLinkActive, TranslateModule], styleUrls: ['./workspace-settings-context-panel.component.scss'], + changeDetection: ChangeDetectionStrategy.Eager, template: `

{{ 'SETTINGS.TITLE' | translate }}

@@ -54,9 +55,12 @@ export class WorkspaceSettingsContextPanelComponent { // links are real navigations now, so the phone drawer's NavigationEnd // auto-close fires too — the explicit close just makes the drawer react // immediately instead of waiting for the navigation to settle. - private readonly contextDrawer = inject(WorkspaceShellContextDrawerService, { - optional: true, - }); + private readonly contextDrawer = inject( + WorkspaceShellContextDrawerService, + { + optional: true, + } + ); onSectionClicked() { this.contextDrawer?.close(); diff --git a/libs/workspace/shell/feature/src/lib/workspace-keyboard-shortcuts/workspace-keyboard-shortcuts-dialog.component.ts b/libs/workspace/shell/feature/src/lib/workspace-keyboard-shortcuts/workspace-keyboard-shortcuts-dialog.component.ts index 1496f4c33..6140c36b0 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-keyboard-shortcuts/workspace-keyboard-shortcuts-dialog.component.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-keyboard-shortcuts/workspace-keyboard-shortcuts-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, inject } from '@angular/core'; +import { Component, inject, ChangeDetectionStrategy } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogModule, @@ -19,6 +19,7 @@ export interface WorkspaceKeyboardShortcutsDialogData { selector: 'app-workspace-keyboard-shortcuts-dialog', imports: [MatButtonModule, MatDialogModule, MatIconModule, TranslatePipe], templateUrl: './workspace-keyboard-shortcuts-dialog.component.html', + changeDetection: ChangeDetectionStrategy.Eager, styleUrl: './workspace-keyboard-shortcuts-dialog.component.scss', }) export class WorkspaceKeyboardShortcutsDialogComponent { diff --git a/libs/workspace/shell/feature/src/lib/workspace-live-categories-popover/workspace-live-categories-popover.service.spec.ts b/libs/workspace/shell/feature/src/lib/workspace-live-categories-popover/workspace-live-categories-popover.service.spec.ts index f5ed4947c..23f787678 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-live-categories-popover/workspace-live-categories-popover.service.spec.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-live-categories-popover/workspace-live-categories-popover.service.spec.ts @@ -5,6 +5,7 @@ import { input, output, signal, + ChangeDetectionStrategy, } from '@angular/core'; import { TestBed } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; @@ -29,6 +30,7 @@ import { WorkspaceLiveCategoriesPopoverService } from './workspace-live-categori `, + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class StubWorkspaceContextPanelComponent { @@ -82,7 +84,10 @@ describe('WorkspaceLiveCategoriesPopoverService', () => { provide: LIVE_CATEGORIES_POPOVER, useExisting: WorkspaceLiveCategoriesPopoverService, }, - { provide: WorkspaceShellContextDrawerService, useValue: drawer }, + { + provide: WorkspaceShellContextDrawerService, + useValue: drawer, + }, { provide: Router, useValue: { events: routerEvents.asObservable() }, diff --git a/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-context-sidebar/workspace-shell-context-sidebar.component.spec.ts b/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-context-sidebar/workspace-shell-context-sidebar.component.spec.ts index 7ee5a116b..6b42f37fb 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-context-sidebar/workspace-shell-context-sidebar.component.spec.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-context-sidebar/workspace-shell-context-sidebar.component.spec.ts @@ -1,4 +1,10 @@ -import { Component, Directive, input, signal } from '@angular/core'; +import { + Component, + Directive, + input, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { MatIcon } from '@angular/material/icon'; @@ -24,6 +30,7 @@ class MockResizableDirective { @Component({ selector: 'app-workspace-context-panel', template: '', + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class MockWorkspaceContextPanelComponent { @@ -35,6 +42,7 @@ class MockWorkspaceContextPanelComponent { @Component({ selector: 'app-workspace-collection-context-panel', template: '', + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class MockWorkspaceCollectionContextPanelComponent {} @@ -42,6 +50,7 @@ class MockWorkspaceCollectionContextPanelComponent {} @Component({ selector: 'app-workspace-settings-context-panel', template: '', + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class MockWorkspaceSettingsContextPanelComponent {} @@ -49,6 +58,7 @@ class MockWorkspaceSettingsContextPanelComponent {} @Component({ selector: 'app-workspace-sources-filters-panel', template: '', + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class MockWorkspaceSourcesFiltersPanelComponent {} @@ -111,7 +121,9 @@ describe('WorkspaceShellContextSidebarComponent', () => { }) .compileComponents(); - fixture = TestBed.createComponent(WorkspaceShellContextSidebarComponent); + fixture = TestBed.createComponent( + WorkspaceShellContextSidebarComponent + ); liveSidebarService = TestBed.inject(LiveLayoutSidebarStateService); liveSidebarService.setState('portal', 'expanded'); }); diff --git a/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-header/workspace-shell-header.component.spec.ts b/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-header/workspace-shell-header.component.spec.ts index 7e0c64835..46d31d9e9 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-header/workspace-shell-header.component.spec.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-header/workspace-shell-header.component.spec.ts @@ -1,6 +1,11 @@ import { readFileSync } from 'node:fs'; import { join } from 'node:path'; -import { Component, input, output } from '@angular/core'; +import { + Component, + input, + output, + ChangeDetectionStrategy, +} from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { MatIconButton } from '@angular/material/button'; @@ -14,6 +19,7 @@ import { WorkspaceShellHeaderComponent } from './workspace-shell-header.componen @Component({ selector: 'app-playlist-switcher', template: '', + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class MockPlaylistSwitcherComponent { diff --git a/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-rail/workspace-shell-rail.component.spec.ts b/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-rail/workspace-shell-rail.component.spec.ts index 8c8a7dbe0..607c362a9 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-rail/workspace-shell-rail.component.spec.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-rail/workspace-shell-rail.component.spec.ts @@ -1,4 +1,4 @@ -import { Component, input } from '@angular/core'; +import { Component, input, ChangeDetectionStrategy } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { MatIcon } from '@angular/material/icon'; import { MatTooltip } from '@angular/material/tooltip'; @@ -12,6 +12,7 @@ import { WorkspaceShellRailComponent } from './workspace-shell-rail.component'; @Component({ selector: 'app-workspace-shell-rail-links', template: '', + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class MockWorkspaceShellRailLinksComponent { @@ -87,9 +88,7 @@ describe('WorkspaceShellRailComponent', () => { fixture.nativeElement.querySelector('.rail-shortcut.is-active') ).not.toBeNull(); expect( - fixture.nativeElement - .querySelector('.brand') - ?.getAttribute('href') + fixture.nativeElement.querySelector('.brand')?.getAttribute('href') ).toContain('/workspace/sources'); expect( fixture.nativeElement diff --git a/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.spec.ts b/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.spec.ts index b1a170b38..5729af1b2 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.spec.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.spec.ts @@ -1,5 +1,12 @@ import { CdkTrapFocus } from '@angular/cdk/a11y'; -import { Component, Directive, input, output, signal } from '@angular/core'; +import { + Component, + Directive, + input, + output, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { TranslatePipe, TranslateService } from '@ngx-translate/core'; import { of, Subject } from 'rxjs'; import { EmbeddedMpvOverlayVisibilityService } from '@iptvnator/ui/playback'; @@ -21,6 +28,7 @@ import { WorkspaceKeyboardShortcutsService } from '../workspace-keyboard-shortcu @Component({ selector: 'app-workspace-shell-rail', template: '', + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class MockWorkspaceShellRailComponent { @@ -39,6 +47,7 @@ class MockWorkspaceShellRailComponent { @Component({ selector: 'app-workspace-shell-header', template: '', + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class MockWorkspaceShellHeaderComponent { @@ -87,6 +96,7 @@ class MockWorkspaceShellHeaderComponent { @Component({ selector: 'app-workspace-shell-context-sidebar', template: '', + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class MockWorkspaceShellContextSidebarComponent { @@ -99,6 +109,7 @@ class MockWorkspaceShellContextSidebarComponent { @Component({ selector: 'app-external-playback-dock', template: '', + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class MockExternalPlaybackDockComponent { @@ -110,6 +121,7 @@ class MockExternalPlaybackDockComponent { @Component({ selector: 'app-playlist-drop-overlay', template: '', + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class MockPlaylistDropOverlayComponent { @@ -128,6 +140,7 @@ class MockPlaylistDropZoneDirective { @Component({ selector: 'app-workspace-shell-import-overlay', template: '', + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class MockWorkspaceShellImportOverlayComponent {} diff --git a/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.ts b/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.ts index cc585f9cb..6e88514f9 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-shell/workspace-shell.component.ts @@ -7,6 +7,7 @@ import { inject, untracked, viewChild, + ChangeDetectionStrategy, } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { TranslatePipe } from '@ngx-translate/core'; @@ -48,6 +49,7 @@ import { LIVE_CATEGORIES_POPOVER } from '@iptvnator/portal/shared/util'; ], templateUrl: './workspace-shell.component.html', styleUrl: './workspace-shell.component.scss', + changeDetection: ChangeDetectionStrategy.Eager, providers: [ WorkspaceShellFacade, WorkspaceShellRouteStateService, @@ -72,12 +74,10 @@ export class WorkspaceShellComponent { private readonly mpvOverlayVisibility = inject( EmbeddedMpvOverlayVisibilityService ); - private readonly header = viewChild( - 'workspaceHeader' - ); - private readonly workspaceContent = viewChild>( - 'workspaceContent' - ); + private readonly header = + viewChild('workspaceHeader'); + private readonly workspaceContent = + viewChild>('workspaceContent'); constructor() { // Focus restoration is the closing half of the drawer's focus diff --git a/libs/workspace/shell/feature/src/lib/workspace-sources/workspace-sources.component.spec.ts b/libs/workspace/shell/feature/src/lib/workspace-sources/workspace-sources.component.spec.ts index 3c94882bf..06aff44ac 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-sources/workspace-sources.component.spec.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-sources/workspace-sources.component.spec.ts @@ -2,7 +2,13 @@ import { PORTAL_EXTERNAL_PLAYBACK } from '@iptvnator/portal/shared/util'; import { SourceActivityService } from '@iptvnator/services'; import { MatDialog } from '@angular/material/dialog'; import { PlaylistRefreshActionService } from '@iptvnator/playlist/shared/ui'; -import { Component, input, output, signal } from '@angular/core'; +import { + Component, + input, + output, + signal, + ChangeDetectionStrategy, +} from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { MatButtonModule } from '@angular/material/button'; @@ -11,7 +17,7 @@ import { MatMenuModule } from '@angular/material/menu'; import { ActivatedRoute, convertToParamMap } from '@angular/router'; import { provideMockStore } from '@ngrx/store/testing'; import { TranslatePipe, TranslateService } from '@ngx-translate/core'; -import { of } from 'rxjs'; +import { of, Subject } from 'rxjs'; import { selectActiveTypeFilters, selectAllPlaylistsMeta, @@ -23,6 +29,7 @@ import { WorkspaceSourcesComponent } from './workspace-sources.component'; @Component({ selector: 'app-recent-playlists', template: '', + changeDetection: ChangeDetectionStrategy.Eager, standalone: true, }) class MockRecentPlaylistsComponent { @@ -116,9 +123,9 @@ describe('WorkspaceSourcesComponent', () => { }, get: (key: string) => of(key), stream: (key: string) => of(key), - onLangChange: of(null), - onTranslationChange: of(null), - onDefaultLangChange: of(null), + onLangChange: new Subject(), + onTranslationChange: new Subject(), + onDefaultLangChange: new Subject(), currentLang: 'en', defaultLang: 'en', }, diff --git a/libs/workspace/shell/feature/src/lib/workspace-sources/workspace-sources.component.ts b/libs/workspace/shell/feature/src/lib/workspace-sources/workspace-sources.component.ts index 785b2b6ed..515577abd 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-sources/workspace-sources.component.ts +++ b/libs/workspace/shell/feature/src/lib/workspace-sources/workspace-sources.component.ts @@ -18,7 +18,12 @@ import { sourceHealthType, PlaylistUpdateState, } from '@iptvnator/shared/interfaces'; -import { Component, computed, inject } from '@angular/core'; +import { + Component, + computed, + inject, + ChangeDetectionStrategy, +} from '@angular/core'; import { toSignal } from '@angular/core/rxjs-interop'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; @@ -55,6 +60,7 @@ interface SortOption { TranslatePipe, ], templateUrl: './workspace-sources.component.html', + changeDetection: ChangeDetectionStrategy.Eager, styleUrl: './workspace-sources.component.scss', }) export class WorkspaceSourcesComponent { diff --git a/libs/workspace/shell/feature/src/test-setup.ts b/libs/workspace/shell/feature/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/workspace/shell/feature/src/test-setup.ts +++ b/libs/workspace/shell/feature/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/workspace/shell/feature/tsconfig.json b/libs/workspace/shell/feature/tsconfig.json index dd63ae991..a5e6d5039 100644 --- a/libs/workspace/shell/feature/tsconfig.json +++ b/libs/workspace/shell/feature/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/libs/workspace/shell/util/src/test-setup.ts b/libs/workspace/shell/util/src/test-setup.ts index 0d88648e3..1ca0136b9 100644 --- a/libs/workspace/shell/util/src/test-setup.ts +++ b/libs/workspace/shell/util/src/test-setup.ts @@ -1,6 +1,9 @@ -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone'; +// Keep Zone's fakeAsync helpers while preserving the Angular 21 test scheduler. +import 'zone.js'; +import 'zone.js/testing'; +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless'; -setupZoneTestEnv({ +setupZonelessTestEnv({ errorOnUnknownElements: true, errorOnUnknownProperties: true, }); diff --git a/libs/workspace/shell/util/tsconfig.json b/libs/workspace/shell/util/tsconfig.json index dd63ae991..a5e6d5039 100644 --- a/libs/workspace/shell/util/tsconfig.json +++ b/libs/workspace/shell/util/tsconfig.json @@ -10,7 +10,8 @@ "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "emitDecoratorMetadata": false, - "module": "preserve" + "module": "preserve", + "ignoreDeprecations": "6.0" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/package.json b/package.json index 54995798a..6847a3d1f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "iptvnator", "version": "0.23.0", "engines": { - "node": "^22.13.0 || >=24.0.0" + "node": "^22.22.3 || ^24.15.0" }, "license": "MIT", "description": "IPTV player application.", @@ -92,25 +92,25 @@ "private": true, "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319", "dependencies": { - "@angular/animations": "21.2.19", - "@angular/cdk": "21.2.14", - "@angular/common": "21.2.19", - "@angular/compiler": "21.2.19", - "@angular/core": "21.2.19", - "@angular/forms": "21.2.19", - "@angular/material": "21.2.14", - "@angular/platform-browser": "21.2.19", - "@angular/platform-browser-dynamic": "21.2.19", - "@angular/router": "21.2.19", - "@ngrx/effects": "21.1.1", - "@ngrx/entity": "21.1.1", - "@ngrx/router-store": "21.1.1", - "@ngrx/signals": "21.1.1", - "@ngrx/store": "21.1.1", - "@ngrx/store-devtools": "21.1.1", - "@ngx-pwa/local-storage": "21.0.0", + "@angular/animations": "22.1.6", + "@angular/cdk": "22.1.6", + "@angular/common": "22.1.6", + "@angular/compiler": "22.1.6", + "@angular/core": "22.1.6", + "@angular/forms": "22.1.6", + "@angular/material": "22.1.6", + "@angular/platform-browser": "22.1.6", + "@angular/platform-browser-dynamic": "22.1.6", + "@angular/router": "22.1.6", + "@ngrx/effects": "22.0.1", + "@ngrx/entity": "22.0.1", + "@ngrx/router-store": "22.0.1", + "@ngrx/signals": "22.0.1", + "@ngrx/store": "22.0.1", + "@ngrx/store-devtools": "22.0.1", + "@ngx-pwa/local-storage": "22.0.0", "@yangkghjh/videojs-aspect-ratio-panel": "0.0.1", - "angularx-qrcode": "21.0.5", + "angularx-qrcode": "22.0.1", "artplayer": "5.4.0", "axios": "1.20.0", "better-sqlite3": "13.0.3", @@ -133,28 +133,28 @@ "video.js": "8.24.0", "videojs-contrib-quality-levels": "4.1.0", "videojs-quality-selector-hls": "1.1.1", - "zone.js": "~0.16.2" + "zone.js": "0.16.3" }, "devDependencies": { - "@angular-devkit/core": "21.2.19", - "@angular-devkit/schematics": "21.2.21", - "@angular-eslint/builder": "21.3.1", - "@angular-eslint/eslint-plugin": "21.3.1", - "@angular-eslint/eslint-plugin-template": "21.3.1", - "@angular-eslint/schematics": "21.3.1", - "@angular-eslint/template-parser": "21.3.1", - "@angular/build": "21.2.19", - "@angular/cli": "21.2.21", - "@angular/common": "21.2.19", - "@angular/compiler": "21.2.19", - "@angular/compiler-cli": "21.2.19", - "@angular/core": "21.2.19", - "@angular/forms": "21.2.19", - "@angular/language-service": "21.2.19", - "@angular/platform-browser": "21.2.19", - "@angular/platform-browser-dynamic": "21.2.19", - "@angular/router": "21.2.19", - "@angular/service-worker": "21.2.19", + "@angular-devkit/core": "22.1.8", + "@angular-devkit/schematics": "22.1.8", + "@angular-eslint/builder": "22.5.0", + "@angular-eslint/eslint-plugin": "22.5.0", + "@angular-eslint/eslint-plugin-template": "22.5.0", + "@angular-eslint/schematics": "22.5.0", + "@angular-eslint/template-parser": "22.5.0", + "@angular/build": "22.1.8", + "@angular/cli": "22.1.8", + "@angular/common": "22.1.6", + "@angular/compiler": "22.1.6", + "@angular/compiler-cli": "22.1.6", + "@angular/core": "22.1.6", + "@angular/forms": "22.1.6", + "@angular/language-service": "22.1.6", + "@angular/platform-browser": "22.1.6", + "@angular/platform-browser-dynamic": "22.1.6", + "@angular/router": "22.1.6", + "@angular/service-worker": "22.1.6", "@astrojs/mdx": "7.0.7", "@astrojs/sitemap": "3.7.4", "@electron/asar": "3.4.1", @@ -167,21 +167,21 @@ "@fontsource/ibm-plex-mono": "5.3.0", "@fontsource/jetbrains-mono": "5.3.0", "@fontsource/roboto": "5.3.0", - "@ngrx/eslint-plugin": "^21.1.1", + "@ngrx/eslint-plugin": "22.0.1", "@ngx-translate/core": "16.0.4", "@ngx-translate/http-loader": "16.0.1", - "@nx/angular": "22.7.8", - "@nx/devkit": "22.7.8", - "@nx/esbuild": "22.7.8", - "@nx/eslint": "22.7.8", - "@nx/eslint-plugin": "22.7.8", - "@nx/jest": "22.7.8", - "@nx/js": "22.7.8", - "@nx/playwright": "22.7.8", - "@nx/web": "22.7.8", - "@nx/workspace": "22.7.8", + "@nx/angular": "23.2.1", + "@nx/devkit": "23.2.1", + "@nx/esbuild": "23.2.1", + "@nx/eslint": "23.2.1", + "@nx/eslint-plugin": "23.2.1", + "@nx/jest": "23.2.1", + "@nx/js": "23.2.1", + "@nx/playwright": "23.2.1", + "@nx/web": "23.2.1", + "@nx/workspace": "23.2.1", "@playwright/test": "^1.62.1", - "@schematics/angular": "21.2.19", + "@schematics/angular": "22.1.8", "@swc-node/register": "1.12.1", "@swc/core": "1.16.1", "@swc/helpers": "0.5.23", @@ -196,7 +196,7 @@ "@typescript-eslint/eslint-plugin": "^8.69.0", "@typescript-eslint/parser": "^8.69.0", "@typescript-eslint/utils": "^8.69.0", - "angular-eslint": "21.4.0", + "angular-eslint": "22.5.0", "astro": "7.2.4", "autoprefixer": "10.5.4", "cookie": "2.0.1", @@ -219,13 +219,13 @@ "jest": "^30.5.1", "jest-environment-jsdom": "^30.5.1", "jest-environment-node": "^30.5.1", - "jest-preset-angular": "~15.0.0", + "jest-preset-angular": "17.0.0", "jest-util": "^30.5.1", "jsonc-eslint-parser": "^2.1.0", "material-design-icons-iconfont": "6.7.0", "mrmime": "2.0.1", - "ng-mocks": "14.17.3", - "nx": "22.7.8", + "ng-mocks": "14.17.6", + "nx": "23.2.1", "nx-electron": "22.0.0", "prettier": "^3.9.6", "sharp": "0.35.4", @@ -234,10 +234,11 @@ "ts-node": "10.9.2", "tslib": "^2.8.1", "tsx": "4.23.13", - "typescript": "5.9.3", + "typescript": "6.0.3", "typescript-eslint": "^8.69.0", "yaml": "2.9.0", - "zod": "4.5.4" + "zod": "4.5.4", + "@babel/core": "7.29.7" }, "pnpm": { "overrides": { @@ -246,7 +247,7 @@ "@humanfs/node@0.16.7": "0.16.8", "@xmldom/xmldom@0.8.11": "0.8.15", "ajv@6.12.6": "6.14.0", - "ajv@8.17.1": "8.18.0", + "ajv@8.17.1": "8.20.0", "brace-expansion@1.1.12": "1.1.18", "browserslist@4.28.1": "4.28.8", "defu@6.1.4": "6.1.6", @@ -268,7 +269,7 @@ "node-forge@1.3.3": "1.4.0", "path-to-regexp@0.1.12": "0.1.13", "picomatch@2.3.1": "2.3.2", - "picomatch@4.0.3": "4.0.5", + "picomatch@4.0.3": "4.0.7", "postcss-selector-parser@6.1.2": "6.1.4", "postcss@8.5.6": "8.5.26", "qs@6.14.1": "6.15.3", @@ -283,8 +284,8 @@ }, "patchedDependencies": { "nx-electron@22.0.0": "patches/nx-electron@22.0.0.patch", - "vite@7.3.6": "patches/vite@7.3.6.patch", - "app-builder-lib@26.15.7": "patches/app-builder-lib@26.15.7.patch" + "app-builder-lib@26.15.7": "patches/app-builder-lib@26.15.7.patch", + "vite@8.1.5": "patches/vite@8.1.5.patch" } } } diff --git a/patches/nx-electron@22.0.0.patch b/patches/nx-electron@22.0.0.patch index 13092f21e..ed9980df6 100644 --- a/patches/nx-electron@22.0.0.patch +++ b/patches/nx-electron@22.0.0.patch @@ -10,3 +10,15 @@ index e36f61a825a280c26ec5064f1ba086ac170fb1b6..17a351dd4b91bf3b72743036f79c8300 { from: (0, path_1.resolve)(options.sourcePath, options.name), to: options.name, +diff --git a/src/executors/build/executor.js b/src/executors/build/executor.js +--- a/src/executors/build/executor.js ++++ b/src/executors/build/executor.js +@@ -13,7 +13,7 @@ + const workspace_1 = require("../../utils/workspace"); + const config_1 = require("../../utils/config"); + const js_1 = require("@nx/js"); +-const buildable_libs_utils_1 = require("@nx/js/src/utils/buildable-libs-utils"); ++const buildable_libs_utils_1 = require("@nx/js/internal"); + function executor(rawOptions, context) { + const { sourceRoot, projectRoot } = (0, workspace_1.getSourceRoot)(context); + const normalizedOptions = (0, normalize_1.normalizeBuildOptions)(rawOptions, context.root, sourceRoot, projectRoot); diff --git a/patches/vite@7.3.6.patch b/patches/vite@7.3.6.patch deleted file mode 100644 index 7990eb340..000000000 --- a/patches/vite@7.3.6.patch +++ /dev/null @@ -1,54 +0,0 @@ -diff --git a/dist/node/chunks/config.js b/dist/node/chunks/config.js ---- a/dist/node/chunks/config.js -+++ b/dist/node/chunks/config.js -@@ -27667,7 +27667,8 @@ async function getWorkerType(raw, clean, i$1) { - if (workerOpts.type && (workerOpts.type === "module" || workerOpts.type === "classic")) return workerOpts.type; - return "classic"; - } --const workerImportMetaUrlRE = /new\s+(?:Worker|SharedWorker)\s*\(\s*new\s+URL.+?import\.meta\.url/s; -+const workerImportMetaUrlFilterRE = /\bnew\s+(?:Worker|SharedWorker)\b/; -+const workerImportMetaUrlRE = /\bnew\s+(?:Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\))/dg; - function workerImportMetaUrlPlugin(config$2) { - const isBuild = config$2.command === "build"; - let workerResolver; -@@ -27688,10 +27689,10 @@ function workerImportMetaUrlPlugin(config$2) { -- filter: { code: workerImportMetaUrlRE }, -+ filter: { code: workerImportMetaUrlFilterRE }, - async handler(code, id) { - let s; -+ const re = new RegExp(workerImportMetaUrlRE); - const cleanString = stripLiteral(code); -- const workerImportMetaUrlRE$1 = /\bnew\s+(?:Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/dg; - let match; -- while (match = workerImportMetaUrlRE$1.exec(cleanString)) { -+ while (match = re.exec(cleanString)) { - const [[, endIndex], [expStart, expEnd], [urlStart, urlEnd]] = match.indices; - const rawUrl = code.slice(urlStart, urlEnd); - if (rawUrl[0] === "`" && rawUrl.includes("${")) this.error(`\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, expStart); -@@ -27733,6 +27734,8 @@ function workerImportMetaUrlPlugin(config$2) { - } - - //#endregion -+const assetImportMetaUrlFilterRE = /\bnew\s+URL\b/; -+const assetImportMetaUrlRE = /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\)/dg; - //#region src/node/plugins/assetImportMetaUrl.ts - /** - * Convert `new URL('./foo.png', import.meta.url)` to its resolved built URL -@@ -27763,14 +27765,14 @@ function assetImportMetaUrlPlugin(config$2) { - transform: { - filter: { - id: { exclude: [exactRegex(preloadHelperId), exactRegex(CLIENT_ENTRY)] }, -- code: /new\s+URL.+import\.meta\.url/s -+ code: assetImportMetaUrlFilterRE - }, - async handler(code, id) { - let s; -- const assetImportMetaUrlRE = /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\)/dg; -+ const re = new RegExp(assetImportMetaUrlRE); - const cleanString = stripLiteral(code); - let match; -- while (match = assetImportMetaUrlRE.exec(cleanString)) { -+ while (match = re.exec(cleanString)) { - const [[startIndex, endIndex], [urlStart, urlEnd]] = match.indices; - if (hasViteIgnoreRE.test(code.slice(startIndex, urlStart))) continue; - const rawUrl = code.slice(urlStart, urlEnd); diff --git a/patches/vite@8.1.5.patch b/patches/vite@8.1.5.patch new file mode 100644 index 000000000..9f58a6578 --- /dev/null +++ b/patches/vite@8.1.5.patch @@ -0,0 +1,46 @@ +diff --git a/dist/node/chunks/node.js b/dist/node/chunks/node.js +--- a/dist/node/chunks/node.js ++++ b/dist/node/chunks/node.js +@@ -28026,6 +28026,7 @@ + if (workerOpts.type && (workerOpts.type === "module" || workerOpts.type === "classic")) return workerOpts.type; + return "classic"; + } ++const workerImportMetaUrlFilterRE = /\bnew\s+(?:Worker|SharedWorker)\b/; + const workerImportMetaUrlRE = /\bnew\s+(?:Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\))/dg; + function workerImportMetaUrlPlugin(config) { + let workerResolver; +@@ -28043,7 +28044,7 @@ + return environment.config.consumer === "client"; + }, + transform: { +- filter: { code: workerImportMetaUrlRE }, ++ filter: { code: workerImportMetaUrlFilterRE }, + async handler(code, id) { + const isBundled = this.environment.config.isBundled; + let s; +@@ -28455,6 +28456,7 @@ + * import.meta.glob('./dir/**.png', { eager: true, import: 'default' })[`./dir/${name}.png`] + * ``` + */ ++const assetImportMetaUrlFilterRE = /\bnew\s+URL\b/; + const assetImportMetaUrlRE = /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\)/dg; + function assetImportMetaUrlPlugin(config) { + const { publicDir } = config; +@@ -28475,7 +28477,7 @@ + transform: { + filter: { + id: { exclude: [exactRegex(preloadHelperId), exactRegex(CLIENT_ENTRY)] }, +- code: assetImportMetaUrlRE ++ code: assetImportMetaUrlFilterRE + }, + async handler(code, id) { + let s; +@@ -31269,7 +31271,7 @@ + } + }, + transform: { +- filter: { code: assetImportMetaUrlRE }, ++ filter: { code: assetImportMetaUrlFilterRE }, + handler(code, id) { + let s; + const re = new RegExp(assetImportMetaUrlRE); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1051e1de2..247db21cc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ overrides: '@humanfs/node@0.16.7': 0.16.8 '@xmldom/xmldom@0.8.11': 0.8.15 ajv@6.12.6: 6.14.0 - ajv@8.17.1: 8.18.0 + ajv@8.17.1: 8.20.0 brace-expansion@1.1.12: 1.1.18 browserslist@4.28.1: 4.28.8 defu@6.1.4: 6.1.6 @@ -32,7 +32,7 @@ overrides: node-forge@1.3.3: 1.4.0 path-to-regexp@0.1.12: 0.1.13 picomatch@2.3.1: 2.3.2 - picomatch@4.0.3: 4.0.5 + picomatch@4.0.3: 4.0.7 postcss-selector-parser@6.1.2: 6.1.4 postcss@8.5.6: 8.5.26 qs@6.14.1: 6.15.3 @@ -45,78 +45,80 @@ overrides: uuid@8.3.2: 11.1.1 yaml@1.10.2: 1.10.3 +packageExtensionsChecksum: sha256-6DjYvAdT+Lv/4pu048t+fxYoAtP25Fr1lEoYfZ6HV38= + patchedDependencies: app-builder-lib@26.15.7: hash: 6114f0c543079e090166568e1ff0c28e406abb22bded6030663b7a3470dce83b path: patches/app-builder-lib@26.15.7.patch nx-electron@22.0.0: - hash: f4bbde1778360c4c462b255bec47a337c0465d59cdcab14ecb601161f3467002 + hash: 4d5ac9c5b10268dcc40998d7a2b166d004105ae7875fa182130d6810871a1e84 path: patches/nx-electron@22.0.0.patch - vite@7.3.6: - hash: 215b6585a9c92fa68c096d500f4676983200307b1e64c1321e20ccbd50bbc8f3 - path: patches/vite@7.3.6.patch + vite@8.1.5: + hash: 469ec4025880fc7b86d3d8b3b4e2028572d6acf65bdfb93973db18fdb3c00ad9 + path: patches/vite@8.1.5.patch importers: .: dependencies: '@angular/animations': - specifier: 21.2.19 - version: 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) + specifier: 22.1.6 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) '@angular/cdk': - specifier: 21.2.14 - version: 21.2.14(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) + specifier: 22.1.6 + version: 22.1.6(d84494623d562fa967d8dbca5e8215e7) '@angular/common': - specifier: 21.2.19 - version: 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + specifier: 22.1.6 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) '@angular/compiler': - specifier: 21.2.19 - version: 21.2.19 + specifier: 22.1.6 + version: 22.1.6 '@angular/core': - specifier: 21.2.19 - version: 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) + specifier: 22.1.6 + version: 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) '@angular/forms': - specifier: 21.2.19 - version: 21.2.19(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) + specifier: 22.1.6 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) '@angular/material': - specifier: 21.2.14 - version: 21.2.14(e62c7210bef08c270f76f8032e5d0530) + specifier: 22.1.6 + version: 22.1.6(42f78374544ce4ecfab2a610740b5d20) '@angular/platform-browser': - specifier: 21.2.19 - version: 21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) + specifier: 22.1.6 + version: 22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) '@angular/platform-browser-dynamic': - specifier: 21.2.19 - version: 21.2.19(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/compiler@21.2.19)(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))) + specifier: 22.1.6 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/compiler@22.1.6)(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))) '@angular/router': - specifier: 21.2.19 - version: 21.2.19(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) + specifier: 22.1.6 + version: 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) '@ngrx/effects': - specifier: 21.1.1 - version: 21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@ngrx/store@21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(rxjs@7.8.2) + specifier: 22.0.1 + version: 22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@ngrx/store@22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(rxjs@7.8.2) '@ngrx/entity': - specifier: 21.1.1 - version: 21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@ngrx/store@21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(rxjs@7.8.2) + specifier: 22.0.1 + version: 22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@ngrx/store@22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(rxjs@7.8.2) '@ngrx/router-store': - specifier: 21.1.1 - version: 21.1.1(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/router@21.2.19(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(@ngrx/store@21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(rxjs@7.8.2) + specifier: 22.0.1 + version: 22.0.1(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/router@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2))(@ngrx/store@22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(rxjs@7.8.2) '@ngrx/signals': - specifier: 21.1.1 - version: 21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + specifier: 22.0.1 + version: 22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) '@ngrx/store': - specifier: 21.1.1 - version: 21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + specifier: 22.0.1 + version: 22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) '@ngrx/store-devtools': - specifier: 21.1.1 - version: 21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@ngrx/store@21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(rxjs@7.8.2) + specifier: 22.0.1 + version: 22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@ngrx/store@22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(rxjs@7.8.2) '@ngx-pwa/local-storage': - specifier: 21.0.0 - version: 21.0.0(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + specifier: 22.0.0 + version: 22.0.0(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) '@yangkghjh/videojs-aspect-ratio-panel': specifier: 0.0.1 version: 0.0.1 angularx-qrcode: - specifier: 21.0.5 - version: 21.0.5(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) + specifier: 22.0.1 + version: 22.0.1(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) artplayer: specifier: 5.4.0 version: 5.4.0 @@ -161,10 +163,10 @@ importers: version: 2.1.3 ngx-indexed-db: specifier: 22.0.0 - version: 22.0.0(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) + version: 22.0.0(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) ngx-skeleton-loader: specifier: 11.3.0 - version: 11.3.0(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) + version: 11.3.0(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) rxjs: specifier: 7.8.2 version: 7.8.2 @@ -184,51 +186,54 @@ importers: specifier: 1.1.1 version: 1.1.1 zone.js: - specifier: ~0.16.2 - version: 0.16.2 + specifier: 0.16.3 + version: 0.16.3 devDependencies: '@angular-devkit/core': - specifier: 21.2.19 - version: 21.2.19(chokidar@5.0.0) + specifier: 22.1.8 + version: 22.1.8(chokidar@5.0.0) '@angular-devkit/schematics': - specifier: 21.2.21 - version: 21.2.21(chokidar@5.0.0) + specifier: 22.1.8 + version: 22.1.8(chokidar@5.0.0) '@angular-eslint/builder': - specifier: 21.3.1 - version: 21.3.1(@angular/cli@21.2.21(@types/node@20.19.9)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + specifier: 22.5.0 + version: 22.5.0(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) '@angular-eslint/eslint-plugin': - specifier: 21.3.1 - version: 21.3.1(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + specifier: 22.5.0 + version: 22.5.0(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) '@angular-eslint/eslint-plugin-template': - specifier: 21.3.1 - version: 21.3.1(@angular-eslint/template-parser@21.3.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@typescript-eslint/types@8.69.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + specifier: 22.5.0 + version: 22.5.0(@angular-eslint/template-parser@22.5.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(@typescript-eslint/types@8.69.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) '@angular-eslint/schematics': - specifier: 21.3.1 - version: 21.3.1(@angular-eslint/template-parser@21.3.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@angular/cli@21.2.21(@types/node@20.19.9)(chokidar@5.0.0))(@typescript-eslint/types@8.69.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + specifier: 22.5.0 + version: 22.5.0(@angular-eslint/template-parser@22.5.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(@typescript-eslint/types@8.69.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) '@angular-eslint/template-parser': - specifier: 21.3.1 - version: 21.3.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + specifier: 22.5.0 + version: 22.5.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) '@angular/build': - specifier: 21.2.19 - version: 21.2.19(@angular/compiler-cli@21.2.19(@angular/compiler@21.2.19)(typescript@5.9.3))(@angular/compiler@21.2.19)(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/service-worker@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@20.19.9)(chokidar@5.0.0)(jiti@1.21.7)(less@4.5.1)(lightningcss@1.33.0)(postcss@8.5.26)(sass-embedded@1.100.0)(tailwindcss@3.4.19(tsx@4.23.13)(yaml@2.9.0))(terser@5.49.2)(tslib@2.8.1)(tsx@4.23.13)(typescript@5.9.3)(yaml@2.9.0) + specifier: 22.1.8 + version: 22.1.8(@angular/compiler-cli@22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3))(@angular/compiler@22.1.6)(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/service-worker@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@types/node@20.19.9)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(jiti@1.21.7)(less@4.5.1)(postcss@8.5.26)(rollup@4.59.0)(sass-embedded@1.100.0)(tailwindcss@3.4.19(tsx@4.23.13)(yaml@2.9.0))(terser@5.49.2)(tslib@2.8.1)(tsx@4.23.13)(typescript@6.0.3)(yaml@2.9.0) '@angular/cli': - specifier: 21.2.21 - version: 21.2.21(@types/node@20.19.9)(chokidar@5.0.0) + specifier: 22.1.8 + version: 22.1.8(@types/node@20.19.9)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: 21.2.19 - version: 21.2.19(@angular/compiler@21.2.19)(typescript@5.9.3) + specifier: 22.1.6 + version: 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) '@angular/language-service': - specifier: 21.2.19 - version: 21.2.19 + specifier: 22.1.6 + version: 22.1.6 '@angular/service-worker': - specifier: 21.2.19 - version: 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + specifier: 22.1.6 + version: 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) '@astrojs/mdx': specifier: 7.0.7 - version: 7.0.7(@astrojs/markdown-satteri@0.3.7)(astro@7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.102.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0)) + version: 7.0.7(@astrojs/markdown-satteri@0.3.7)(astro@7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0)) '@astrojs/sitemap': specifier: 3.7.4 version: 3.7.4 + '@babel/core': + specifier: 7.29.7 + version: 7.29.7 '@electron/asar': specifier: 3.4.1 version: 3.4.1 @@ -260,53 +265,53 @@ importers: specifier: 5.3.0 version: 5.3.0 '@ngrx/eslint-plugin': - specifier: ^21.1.1 - version: 21.1.1(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript-eslint@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(typescript@5.9.3) + specifier: 22.0.1 + version: 22.0.1(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript-eslint@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(typescript@6.0.3) '@ngx-translate/core': specifier: 16.0.4 - version: 16.0.4(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) + version: 16.0.4(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) '@ngx-translate/http-loader': specifier: 16.0.1 - version: 16.0.1(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) + version: 16.0.1(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) '@nx/angular': - specifier: 22.7.8 - version: 22.7.8(9da22baaa942ea628a4531574667d706) + specifier: 23.2.1 + version: 23.2.1(e86c4a4fb085aa7a209327ce2b2f688a) '@nx/devkit': - specifier: 22.7.8 - version: 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + specifier: 23.2.1 + version: 23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) '@nx/esbuild': - specifier: 22.7.8 - version: 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + specifier: 23.2.1 + version: 23.2.1(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) '@nx/eslint': - specifier: 22.7.8 - version: 22.7.8(c6268304c3e993c057e90202eca80e3c) + specifier: 23.2.1 + version: 23.2.1(@babel/traverse@7.29.8)(@nx/jest@23.2.1(72e9877c63da59ac93e1f6ba5cf055e8))(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.2(jiti@1.21.7))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) '@nx/eslint-plugin': - specifier: 22.7.8 - version: 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(typescript@5.9.3) + specifier: 23.2.1 + version: 23.2.1(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(typescript@6.0.3) '@nx/jest': - specifier: 22.7.8 - version: 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3))(typescript@5.9.3) + specifier: 23.2.1 + version: 23.2.1(72e9877c63da59ac93e1f6ba5cf055e8) '@nx/js': - specifier: 22.7.8 - version: 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + specifier: 23.2.1 + version: 23.2.1(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) '@nx/playwright': - specifier: 22.7.8 - version: 22.7.8(b0b327483e86e3b4312dfd51a3eacfc2) + specifier: 23.2.1 + version: 23.2.1(@babel/traverse@7.29.8)(@nx/jest@23.2.1(72e9877c63da59ac93e1f6ba5cf055e8))(@playwright/test@1.62.1)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.2(jiti@1.21.7))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) '@nx/web': - specifier: 22.7.8 - version: 22.7.8(e36557eae963c5bd5b3bad2c1c9ad54a) + specifier: 23.2.1 + version: 23.2.1(f806492ec758094179990070b023c41c) '@nx/workspace': - specifier: 22.7.8 - version: 22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)) + specifier: 23.2.1 + version: 23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)) '@playwright/test': specifier: ^1.62.1 version: 1.62.1 '@schematics/angular': - specifier: 21.2.19 - version: 21.2.19(chokidar@5.0.0) + specifier: 22.1.8 + version: 22.1.8(chokidar@5.0.0) '@swc-node/register': specifier: 1.12.1 - version: 1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3) + version: 1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3) '@swc/core': specifier: 1.16.1 version: 1.16.1(@swc/helpers@0.5.23) @@ -339,19 +344,19 @@ importers: version: 7.3.58 '@typescript-eslint/eslint-plugin': specifier: ^8.69.0 - version: 8.69.0(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + version: 8.69.0(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) '@typescript-eslint/parser': specifier: ^8.69.0 - version: 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + version: 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) '@typescript-eslint/utils': specifier: ^8.69.0 - version: 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + version: 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) angular-eslint: - specifier: 21.4.0 - version: 21.4.0(@angular/cli@21.2.21(@types/node@20.19.9)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript-eslint@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(typescript@5.9.3) + specifier: 22.5.0 + version: 22.5.0(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript-eslint@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(typescript@6.0.3) astro: specifier: 7.2.4 - version: 7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.102.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0) + version: 7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0) autoprefixer: specifier: 10.5.4 version: 10.5.4(postcss@8.5.26) @@ -384,7 +389,7 @@ importers: version: 10.1.8(eslint@9.39.2(jiti@1.21.7)) eslint-plugin-import: specifier: 2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7)) + version: 2.32.0(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7)) eslint-plugin-playwright: specifier: ^1.6.2 version: 1.8.3(eslint@9.39.2(jiti@1.21.7)) @@ -408,7 +413,7 @@ importers: version: 3.2.0 jest: specifier: ^30.5.1 - version: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)) + version: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)) jest-environment-jsdom: specifier: ^30.5.1 version: 30.5.1 @@ -416,8 +421,8 @@ importers: specifier: ^30.5.1 version: 30.5.1 jest-preset-angular: - specifier: ~15.0.0 - version: 15.0.3(49d964010ff732709c8b8b9285288d9d) + specifier: 17.0.0 + version: 17.0.0(@angular/compiler-cli@22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@babel/core@7.29.7)(@jest/transform@30.5.1)(@jest/types@30.5.1)(@types/jsdom@21.1.7)(babel-jest@30.5.1(@babel/core@7.29.7))(jest@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)))(jsdom@26.1.0)(typescript@6.0.3) jest-util: specifier: ^30.5.1 version: 30.5.1 @@ -431,14 +436,14 @@ importers: specifier: 2.0.1 version: 2.0.1 ng-mocks: - specifier: 14.17.3 - version: 14.17.3(b285834fe0000b6ade3305b52068a4d2) + specifier: 14.17.6 + version: 14.17.6(c5ca909b0ec8f50b848b5bca41287c00) nx: - specifier: 22.7.8 - version: 22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)) + specifier: 23.2.1 + version: 23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)) nx-electron: specifier: 22.0.0 - version: 22.0.0(patch_hash=f4bbde1778360c4c462b255bec47a337c0465d59cdcab14ecb601161f3467002)(@nx/devkit@22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))))(@nx/workspace@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(@swc/core@1.16.1(@swc/helpers@0.5.23))(electron-builder-squirrel-windows@26.15.7)(electron@43.3.0)(esbuild@0.28.2)(rxjs@7.8.2)(typescript@5.9.3) + version: 22.0.0(patch_hash=4d5ac9c5b10268dcc40998d7a2b166d004105ae7875fa182130d6810871a1e84)(@nx/devkit@23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))))(@nx/workspace@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(@swc/core@1.16.1(@swc/helpers@0.5.23))(electron-builder-squirrel-windows@26.15.7)(electron@43.3.0)(esbuild@0.28.2)(rxjs@7.8.2)(typescript@6.0.3) prettier: specifier: ^3.9.6 version: 3.9.6 @@ -450,10 +455,10 @@ importers: version: 3.4.19(tsx@4.23.13)(yaml@2.9.0) ts-jest: specifier: ^29.4.12 - version: 29.4.12(@babel/core@7.29.7)(@jest/transform@30.5.1)(@jest/types@30.5.1)(babel-jest@30.5.1(@babel/core@7.29.7))(esbuild@0.28.2)(jest-util@30.5.1)(jest@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.12(@babel/core@7.29.7)(@jest/transform@30.5.1)(@jest/types@30.5.1)(babel-jest@30.5.1(@babel/core@7.29.7))(esbuild@0.28.2)(jest-util@30.5.1)(jest@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)))(typescript@6.0.3) ts-node: specifier: 10.9.2 - version: 10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3) + version: 10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3) tslib: specifier: ^2.8.1 version: 2.8.1 @@ -461,11 +466,11 @@ importers: specifier: 4.23.13 version: 4.23.13 typescript: - specifier: 5.9.3 - version: 5.9.3 + specifier: 6.0.3 + version: 6.0.3 typescript-eslint: specifier: ^8.69.0 - version: 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + version: 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) yaml: specifier: 2.9.0 version: 2.9.0 @@ -475,62 +480,6 @@ importers: packages: - '@algolia/abtesting@1.14.1': - resolution: {integrity: sha512-Dkj0BgPiLAaim9sbQ97UKDFHJE/880wgStAM18U++NaJ/2Cws34J5731ovJifr6E3Pv4T2CqvMXf8qLCC417Ew==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-abtesting@5.48.1': - resolution: {integrity: sha512-LV5qCJdj+/m9I+Aj91o+glYszrzd7CX6NgKaYdTOj4+tUYfbS62pwYgUfZprYNayhkQpVFcrW8x8ZlIHpS23Vw==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-analytics@5.48.1': - resolution: {integrity: sha512-/AVoMqHhPm14CcHq7mwB+bUJbfCv+jrxlNvRjXAuO+TQa+V37N8k1b0ijaRBPdmSjULMd8KtJbQyUyabXOu6Kg==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-common@5.48.1': - resolution: {integrity: sha512-VXO+qu2Ep6ota28ktvBm3sG53wUHS2n7bgLWmce5jTskdlCD0/JrV4tnBm1l7qpla1CeoQb8D7ShFhad+UoSOw==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-insights@5.48.1': - resolution: {integrity: sha512-zl+Qyb0nLg+Y5YvKp1Ij+u9OaPaKg2/EPzTwKNiVyOHnQJlFxmXyUZL1EInczAZsEY8hVpPCLtNfhMhfxluXKQ==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-personalization@5.48.1': - resolution: {integrity: sha512-r89Qf9Oo9mKWQXumRu/1LtvVJAmEDpn8mHZMc485pRfQUMAwSSrsnaw1tQ3sszqzEgAr1c7rw6fjBI+zrAXTOw==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-query-suggestions@5.48.1': - resolution: {integrity: sha512-TPKNPKfghKG/bMSc7mQYD9HxHRUkBZA4q1PEmHgICaSeHQscGqL4wBrKkhfPlDV1uYBKW02pbFMUhsOt7p4ZpA==} - engines: {node: '>= 14.0.0'} - - '@algolia/client-search@5.48.1': - resolution: {integrity: sha512-4Fu7dnzQyQmMFknYwTiN/HxPbH4DyxvQ1m+IxpPp5oslOgz8m6PG5qhiGbqJzH4HiT1I58ecDiCAC716UyVA8Q==} - engines: {node: '>= 14.0.0'} - - '@algolia/ingestion@1.48.1': - resolution: {integrity: sha512-/RFq3TqtXDUUawwic/A9xylA2P3LDMO8dNhphHAUOU51b1ZLHrmZ6YYJm3df1APz7xLY1aht6okCQf+/vmrV9w==} - engines: {node: '>= 14.0.0'} - - '@algolia/monitoring@1.48.1': - resolution: {integrity: sha512-Of0jTeAZRyRhC7XzDSjJef0aBkgRcvRAaw0ooYRlOw57APii7lZdq+layuNdeL72BRq1snaJhoMMwkmLIpJScw==} - engines: {node: '>= 14.0.0'} - - '@algolia/recommend@5.48.1': - resolution: {integrity: sha512-bE7JcpFXzxF5zHwj/vkl2eiCBvyR1zQ7aoUdO+GDXxGp0DGw7nI0p8Xj6u8VmRQ+RDuPcICFQcCwRIJT5tDJFw==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-browser-xhr@5.48.1': - resolution: {integrity: sha512-MK3wZ2koLDnvH/AmqIF1EKbJlhRS5j74OZGkLpxI4rYvNi9Jn/C7vb5DytBnQ4KUWts7QsmbdwHkxY5txQHXVw==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-fetch@5.48.1': - resolution: {integrity: sha512-2oDT43Y5HWRSIQMPQI4tA/W+TN/N2tjggZCUsqQV440kxzzoPGsvv9QP1GhQ4CoDa+yn6ygUsGp6Dr+a9sPPSg==} - engines: {node: '>= 14.0.0'} - - '@algolia/requester-node-http@5.48.1': - resolution: {integrity: sha512-xcaCqbhupVWhuBP1nwbk1XNvwrGljozutEiLx06mvqDf3o8cHyEgQSHS4fKJM+UAggaWVnnFW+Nne5aQ8SUJXg==} - engines: {node: '>= 14.0.0'} - '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} @@ -539,170 +488,93 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.2101.4': - resolution: {integrity: sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==} - 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.2201.8': + resolution: {integrity: sha512-EUQo8RDS1my2Bo5FRS+gBYgz1/klfIp9XESfMpTBO04nBkjF6DkPCeOxeAf1CYjWy9nCXcWn968eSdEhV5jXiA==} + 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/architect@0.2102.19': - resolution: {integrity: sha512-cj4tzUMiloLTg5rNf17E8MsvIxCWYoBiBsaj7ns6dgXqT9XCeG+J0TA2t1M+N9uuqfeLd22U/rYoCkADmcircQ==} - 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/architect@0.2102.21': - resolution: {integrity: sha512-WqITVviALevNpCQoI50e3YY9qNOQT+gqTow/4FhWsxlAJFMb5uyilFpVz2hGYWPgosF3OAKmT1h9r+Jm9S6SUQ==} - 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.1.4': - resolution: {integrity: sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==} - 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.1.8': + resolution: {integrity: sha512-34lsgg2FwMVBX7nR/ZqzRUcdvdYPvSB8XAEr2GudXAyZwLI9ehzfQlagAWR/gEb8p4uCFZW0r7uU0toxydq4Rw==} + 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/core@21.2.19': - resolution: {integrity: sha512-dtpJMQBz5nhkcIogPmXP/aT2Ak8m/wLRPOSTI/g4vSJSuGiI53PgtWq4/wfQga6E6wdM2XWsblAE89d8w5heQQ==} - 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.1.8': + resolution: {integrity: sha512-Pv3cPa/44kvEwYcqwx4Ns5dhIDmcFNSHhbpheqiEG1Z/u4e2t4zHKDtE3eHZB+8o+IcC7xJj+d+AqGR44RoZDA==} + 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.5.0': + resolution: {integrity: sha512-bChGIHOApVk3eXaAfCOxxb54tToIttSzADEUpuBN6cs7Jp7OO96lNpMktv9IPvxfyVrhbriocwNy33jhpRSt0Q==} peerDependencies: - chokidar: ^5.0.0 - peerDependenciesMeta: - chokidar: - optional: true - - '@angular-devkit/core@21.2.21': - resolution: {integrity: sha512-xOr6mZ00M6hgM/xltAVkPVc/Yd1a/Wa0CGecV64JTITaVaSH8p8+wXe62Xavdr3pcc9cLKKUUB4mAup4k7Kkyw==} - 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 - peerDependenciesMeta: - chokidar: - optional: true - - '@angular-devkit/schematics@21.2.19': - resolution: {integrity: sha512-AG3Fzh9wJCmKBfxUQOUWaEHMj5Gq2O+Msf1z52aDSxbVhs5/iSQcXGPv/DLdAXu7d4xmQhLouNe9Glaq2omDyw==} - 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@21.2.21': - resolution: {integrity: sha512-LgTd/0CpyWSxMJLLk0X85F5RyDlJekssLlapU1C8fKqOUdWONYncTXYvZUQ+zbRAGXHcSZrst1Vm8qdXbo11Mg==} - 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.3.1': - resolution: {integrity: sha512-1f1Lyp5e7OH6txiV224HaY3G1uRCj91OSKq7hT2Vw9NRw6zWFc1anBpDeLVjpL9ptUxzUGIQR5jEV54hOPayoQ==} - peerDependencies: - '@angular/cli': '>= 21.0.0 < 22.0.0' - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular-eslint/builder@21.4.0': - resolution: {integrity: sha512-3kgGmrVaCYbLtDjC8g4BmMBbdz4thsOB8/NYly8JtXM8EuDZEk5Pz6VTRpJR02ARprwayraTTmhyvq6OGBlQ9w==} + '@angular-eslint/bundled-angular-compiler@22.5.0': + resolution: {integrity: sha512-AdvtopHjjenM02LqsgqQbpTWTbE2DqFS4ZJT8YxEIzUKUdLNwkGUgZn9WpKHBHGBvaiA3hj2BLWRCJs/vJmV3g==} + + '@angular-eslint/eslint-plugin-template@22.5.0': + resolution: {integrity: sha512-qHi292XBe5TIFpPjEKY+NtiFGEZLrYzD8GPVzBuIruaKQz1Y//eqPp3Ve/MYYBbwdj0gD8rPZRjDIXAcsRUMVg==} peerDependencies: - '@angular/cli': '>= 21.0.0 < 22.0.0' - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + '@angular-eslint/template-parser': 22.5.0 + '@typescript-eslint/types': ^8.0.0 + '@typescript-eslint/utils': ^8.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular-eslint/bundled-angular-compiler@21.3.1': - resolution: {integrity: sha512-jjbnJPUXQeQBJ8RM+ahlbt4GH2emVN8JvG3AhFbPci1FrqXi9cOOfkbwLmvpoyTli4LF8gy7g4ctFqnlRgqryw==} - - '@angular-eslint/bundled-angular-compiler@21.4.0': - resolution: {integrity: sha512-/3H4BPbQ1BHJkkrUsfusZtmHc+qiFWBBZ9UDPWah4xZMjflexOK9U4GYeH7nMjcuyqFnIlMMeJJNwNLGt/hmdg==} - - '@angular-eslint/eslint-plugin-template@21.3.1': - resolution: {integrity: sha512-ndPWJodkcEOu2PVUxlUwyz4D2u3r9KO7veWmStVNOLeNrICJA+nQvrz2BWCu0l48rO0K5ezsy0JFcQDVwE/5mw==} + '@angular-eslint/eslint-plugin@22.5.0': + resolution: {integrity: sha512-3MooyC+F156Aa0XmNLwNMeQFVmSPhJYqPdX8FditvzSYuo5qANZTSbBABMU8XepvmxXcpPmEsj5sQUpbTdRl9Q==} peerDependencies: - '@angular-eslint/template-parser': 21.3.1 - '@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 || ^10.0.0 + '@typescript-eslint/utils': ^8.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular-eslint/eslint-plugin-template@21.4.0': - resolution: {integrity: sha512-sJEHx2WYnvOgPpzP1eHnUdRS06zgKmRxbiIR0JiCcaSen5iv1HlsMieXy//FS9TtNW+abHOy4UtDuGuSPflPFA==} + '@angular-eslint/schematics@22.5.0': + resolution: {integrity: sha512-4B7Bpph/H6vNI0Wfi3p4w4AKw6VJrTG9IGNbSx6NKbPzcwBSaFSuJUpIUxiuzlsaFy6csbRWU2E3UPyXjEfFew==} + + '@angular-eslint/template-parser@22.5.0': + resolution: {integrity: sha512-p0C48htmzd1/Qum3TIQz3FZMWivbkm7x8z3Q6GY3CzNOIzVRKQU2MwZtpocXPkctJGjYnC5wJ58d8Ow4E5LVbA==} peerDependencies: - '@angular-eslint/template-parser': 21.4.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 || ^10.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular-eslint/eslint-plugin@21.3.1': - resolution: {integrity: sha512-08NNTxwawRLTWPLl8dg1BnXMwimx93y4wMEwx2aWQpJbIt4pmNvwJzd+NgoD/Ag2VdLS/gOMadhJH5fgaYKsPQ==} + '@angular-eslint/utils@22.5.0': + resolution: {integrity: sha512-i106BkmVnXM3ub5nsaXmAZNk1bnajzVOwrGrEVt9U0mX22zcVlunLMWC49z2dqGjl91Ppw0Xr5ed+QrKWm9Ldg==} peerDependencies: - '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + '@typescript-eslint/utils': ^8.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular-eslint/eslint-plugin@21.4.0': - resolution: {integrity: sha512-mow2DMj+xBvGl5t7jzC34R8YfbHbaGNyCNFzpovtl9qc0JbuqLyg6htmt8xb05f8ZjATOr4nz0ESt6HV4c51hw==} - peerDependencies: - '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '*' - - '@angular-eslint/schematics@21.3.1': - resolution: {integrity: sha512-1U2u4ZsZvwT30aXRLsIJf6tULIiioo9BtASNsldpYecU3/m/1+F61lCYG79qt7YWbif9KABPYZlFTJUFGN8HWA==} - peerDependencies: - '@angular/cli': '>= 21.0.0 < 22.0.0' - - '@angular-eslint/schematics@21.4.0': - resolution: {integrity: sha512-crD6Hfxs7x5bN9FCqTZI7uVSiGvprfCS3MCPOpyIQl87bRr/9aNhnicJ3ROUHv+2A713BgPHIgiCII/bxzrfPw==} - peerDependencies: - '@angular/cli': '>= 21.0.0 < 22.0.0' - - '@angular-eslint/template-parser@21.3.1': - resolution: {integrity: sha512-moERVCTekQKOvR8RMuEOtWSO3VS1qrzA3keI1dPto/JVB8Nqp9w3R5ZpEoXHzh4zgEryosxmPgdi6UczJe2ouQ==} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '*' - - '@angular-eslint/template-parser@21.4.0': - resolution: {integrity: sha512-BaUSLSyS+43fzDoJkTMkGqNdCXq3fGnUZsfXTmrlZPJf5AYFbgAlAPGZXDJyoNWw43fux+DafdlrlKcYUSgSIw==} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '*' - - '@angular-eslint/utils@21.3.1': - resolution: {integrity: sha512-Q3SGA1/36phZhmsp1mYrKzp/jcmqofRr861MYn46FaWIKSYXBYRzl+H3FIJKBu5CE36Bggu6hbNpwGPuUp+MCg==} - peerDependencies: - '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '*' - - '@angular-eslint/utils@21.4.0': - resolution: {integrity: sha512-7pi+Ga7QmdH5Ig/diau6fR5L4yubgKr9TOjdCg7OeuE/zo0O3osTCNT6JOodzS/iQM1kSCJFDoIBKFeUOttiNw==} - peerDependencies: - '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '*' - - '@angular/animations@21.2.19': - resolution: {integrity: sha512-qC0mviselvpKq2ID9bT+USnCsHKfwZAifkUr5A4h/WCzXvUvpVNWp/RPhGcKeOR+seg3EO5GtIZyblwiRlhzrA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/animations@22.1.6': + resolution: {integrity: sha512-o3tad5WSlcq80aFvZGQ1VrNtY2fieJWbYn1h2AxJXE1Mj0h3atvw4zgQ+qx9ZRCYsZvsluW88YxA1Zk0bCuZvQ==} + 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.19 + '@angular/core': 22.1.6 - '@angular/build@21.2.19': - resolution: {integrity: sha512-emy9mqrTXAwhZzcvx8MaHyz+cUR06PVGxnqy91+bpDxPP9S5x67sPoOkY9y/ETFFhRpB5ULlUxyq0eN/pi6QOg==} - 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.1.8': + resolution: {integrity: sha512-tw+Evk0EITb8p8dTu743ONoFztCEiyDkdRbtZsh7C0vBS7Q9ElsOcFeg2Z9OAAwrmtZMIBuW3jrdPRQp7OCSdw==} + 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.19 + '@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.1.8 + 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.5.26 + rollup: 4.59.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': @@ -717,6 +589,8 @@ packages: optional: true '@angular/ssr': optional: true + istanbul-lib-instrument: + optional: true karma: optional: true less: @@ -725,51 +599,54 @@ packages: optional: true postcss: optional: true + rollup: + optional: true tailwindcss: optional: true vitest: optional: true - '@angular/cdk@21.2.14': - resolution: {integrity: sha512-806REq/CLf37nEhmmd8Q+ILN8z/RVG2vk2n8YZ/4TdHpcBCi5ux4AxLbpMmduLwGPOzPagJ6ggRzE5fnX0rmcQ==} + '@angular/cdk@22.1.6': + resolution: {integrity: sha512-4eHSs/sIIp1mesNKFDkDTYDhfB/3/Bpfjzv/PbybsKVd642FTa3IAbjWf3uqHS/V7mpucF3Q5gSpirZHDgxwrg==} peerDependencies: - '@angular/common': ^21.0.0 || ^22.0.0 - '@angular/core': ^21.0.0 || ^22.0.0 - '@angular/platform-browser': ^21.0.0 || ^22.0.0 + '@angular/common': ^22.0.0 || ^23.0.0 + '@angular/core': ^22.0.0 || ^23.0.0 + '@angular/forms': ^22.0.0 || ^23.0.0 + '@angular/platform-browser': ^22.0.0 || ^23.0.0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/cli@21.2.21': - resolution: {integrity: sha512-9TqWGpguYGpAsycSYX/Ji6csuPrEntdd+D2iK0TWIVQ1APCma7UmBgI1ltcATQNezxWiLTiJIMIOEuPG+cIVnw==} - 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.1.8': + resolution: {integrity: sha512-MFN/FydqDndai8vF12hDxG5RlBt1HUUheeJgJNmuHYIne4bOAfOT+xuMpIK8+gLSRwCemdqR3OMM38pnsFJ/nA==} + 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.19': - resolution: {integrity: sha512-Rvo/VXI0kUmfQT7+OeAjv526OJlf/WrLnJq1Tz84Jkyv9bs9SOMTCT6m4+boo8gxVNdrcxvGU3Q0o2jZzKceSg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/common@22.1.6': + resolution: {integrity: sha512-giuH+jJvo6YbBxbKofJCXvq6k8g1Z/xCAvh4piNFSSS6/toXTLCeZ+snr6Stw5b2wRbArM5Q5nDeuto3NqVPnQ==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/core': 21.2.19 + '@angular/core': 22.1.6 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@21.2.19': - resolution: {integrity: sha512-QxWqUvhTWgyYPPkfpupOUIEa3Y5cbzMilaFgRNpkvFy6teARw5Izsd7RxUbj3Tp3xJOi1MtumNmkxLxmv3iv7A==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/compiler-cli@22.1.6': + resolution: {integrity: sha512-C1fQuaSLnibhfbb7Im/vurdBEcfQk+/GqPkY4+dgEKd4EPleO0xWlD+k5DwyNFX8a9w7HebWfo0Zl66HuaEwOQ==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.19 - typescript: '>=5.9 <6.1' + '@angular/compiler': 22.1.6 + typescript: '>=6.0 <6.1' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@21.2.19': - resolution: {integrity: sha512-vuF5i1t14ftJiHXVVLgDYLWkT99QuajphcUHy1VZaMX3FiqSGXRV62g+R2RMxL0OJ5C1ai8xTHKPx9n1lQFyFg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/compiler@22.1.6': + resolution: {integrity: sha512-JjOUm/qD338+fGfZvxSNn/vTUiVqNwOiPzacInVUq1eVp7Jev+cnvEwXA2cFJkYZoy3Imz6wHoMvTUZNN8cbKQ==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} - '@angular/core@21.2.19': - resolution: {integrity: sha512-PVoXD1kBexOJLkFzKx2zBY/0oZJXGru0eGn2hu0q5n3vkZlYsR1yRolfGenK1gZE48Qibiw/ttg/X/pAIPEZGg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/core@22.1.6': + resolution: {integrity: sha512-3Ln9YYOhsaU2vPufnpcu6C4dlmX4e/nJTlggVcKMT7bGZH5KlEtw3h0uh9YfANv8YhQCEk1AcuRI7KpBnh5ing==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/compiler': 21.2.19 + '@angular/compiler': 22.1.6 rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 || ~0.16.0 peerDependenciesMeta: @@ -778,65 +655,65 @@ packages: zone.js: optional: true - '@angular/forms@21.2.19': - resolution: {integrity: sha512-tEw8cz2UU6VSB+ReJN86k87nRdLRXGi+8SZhoRl2dFu2iReIO3S6kJAVTH7Y9kdrokqTPhWG+KNEzWgqhdjXOg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/forms@22.1.6': + resolution: {integrity: sha512-rfV4G4UB4l69yXSRvhaHPzTMIruvBlRO+ak9NtTcYMnsoj8O5ZCyPv0ledGIrLTBwGSI3X1j4nr9aoV+Rj/n+Q==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/common': 21.2.19 - '@angular/core': 21.2.19 - '@angular/platform-browser': 21.2.19 + '@angular/common': 22.1.6 + '@angular/core': 22.1.6 + '@angular/platform-browser': 22.1.6 rxjs: ^6.5.3 || ^7.4.0 - '@angular/language-service@21.2.19': - resolution: {integrity: sha512-ockDnVB9drvrx1WXPd3n+0mDT0iBAMJsmjBDUijGTOCy4oc+iXPeC2Q+bpiRuM2+rtpo6Z939ZLgvNdh7KrRzQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/language-service@22.1.6': + resolution: {integrity: sha512-qsOgDUGR62lsdyJF+hoLx1kTSZtBoc+r2GJ64o6O+HqlsTPw0Q6aI6Y1qQkAurQbGa0s7qfhuXEHtKmG9MFiXA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} - '@angular/material@21.2.14': - resolution: {integrity: sha512-fMQca8VRtei93JRRG9qQ+u08DCb0nga59Esoakq5yx3+A1NfdpFeUS1tBns56U04o8KAaIAwZK3NBqXz8ZKNqg==} + '@angular/material@22.1.6': + resolution: {integrity: sha512-NpURtI21B7D6+DVltihVLxQWOm/cshlbJKqlUuSmyoGGO8Fvf4RxMtx6IZ5YAfVSQsuZvCvdXPc4BKUlJrqnHw==} peerDependencies: - '@angular/cdk': 21.2.14 - '@angular/common': ^21.0.0 || ^22.0.0 - '@angular/core': ^21.0.0 || ^22.0.0 - '@angular/forms': ^21.0.0 || ^22.0.0 - '@angular/platform-browser': ^21.0.0 || ^22.0.0 + '@angular/cdk': 22.1.6 + '@angular/common': ^22.0.0 || ^23.0.0 + '@angular/core': ^22.0.0 || ^23.0.0 + '@angular/forms': ^22.0.0 || ^23.0.0 + '@angular/platform-browser': ^22.0.0 || ^23.0.0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/platform-browser-dynamic@21.2.19': - resolution: {integrity: sha512-Ns+0D4S8A6Bypxgeba0QSDdIIVosOUfSfxDxiC0oN/VrgMBiLAbOROTnoNfOaFRgkV2gUzPNPFdHD2Ciflq+bQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/platform-browser-dynamic@22.1.6': + resolution: {integrity: sha512-ixLGQoRkF1ctIN01opG8XaT84TR2/RngMvA0DKsbCjoz0+ECCq3RtsrXUB5Qdny7AXzPvsJchxQ+ZubtOX9MYQ==} + 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.19 - '@angular/compiler': 21.2.19 - '@angular/core': 21.2.19 - '@angular/platform-browser': 21.2.19 + '@angular/common': 22.1.6 + '@angular/compiler': 22.1.6 + '@angular/core': 22.1.6 + '@angular/platform-browser': 22.1.6 - '@angular/platform-browser@21.2.19': - resolution: {integrity: sha512-YMguYVhdkV8tr9MvbN+VpMjbdmsYq6g12M9WPvAYM51BkL2iREbWeoXDGmfCw9G0it6+0pMdgrSPFFUFuOqpAQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/platform-browser@22.1.6': + resolution: {integrity: sha512-jrRi6zpdz+jOle5l0OW7QL0a8xPgdnxWT5FrJabbiNKfYzQfqKcaAu02l8uJoJxtGb8fLQ8pbjkPpZEvKO2z+w==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/animations': 21.2.19 - '@angular/common': 21.2.19 - '@angular/core': 21.2.19 + '@angular/animations': 22.1.6 + '@angular/common': 22.1.6 + '@angular/core': 22.1.6 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/router@21.2.19': - resolution: {integrity: sha512-cKO/aq1xEMvgS29jFUc/Y3KsgEzHnwOw6sEL+UQZkZTmGD5YrYv8Yvj05GDMEUYbvDxBd5DixVVEbH38lyQKqA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/router@22.1.6': + resolution: {integrity: sha512-ex0vrkcVyJJdn7NzZXun+5ctaPPLTZJ/gE7A3g1dun7BM3g4Z6zT738mODFeJgtPtJNf4bTs2gpgvyegvzWH7w==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/common': 21.2.19 - '@angular/core': 21.2.19 - '@angular/platform-browser': 21.2.19 + '@angular/common': 22.1.6 + '@angular/core': 22.1.6 + '@angular/platform-browser': 22.1.6 rxjs: ^6.5.3 || ^7.4.0 - '@angular/service-worker@21.2.19': - resolution: {integrity: sha512-GG9SoZgY5FRi6VlRZYU5lNue1WT+6CoTka7FLpRkdOJs3OG+ivkgAxPExHNXxvuPbHrOyN1cQoRKaTgDBIvIiw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/service-worker@22.1.6': + resolution: {integrity: sha512-l++I/g8Bic8DwTgvIXNxrU0rIzfs2taep8+EgIdbUCGcqIl+Y4oFgCafJBYMBBjH6PMIiEcp7y8xUu5ZFz4muA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/core': 21.2.19 + '@angular/core': 22.1.6 rxjs: ^6.5.3 || ^7.4.0 '@asamuzakjp/css-color@3.2.0': @@ -945,30 +822,50 @@ packages: resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} + '@babel/code-frame@8.0.0': + resolution: {integrity: sha512-dYYg153EyN2Ekbqw2zAsbd6/JR+9N2SEoC7YV2GyyqMM7x9bLDTjBD6XBhSMLH0wtIVyJj03jWNriQhaN+eoCw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/compat-data@7.29.7': resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} + '@babel/compat-data@8.0.5': + resolution: {integrity: sha512-YLsYoQMvL8l8WrGpN3Zj7O1wK5LEBN+cQtux7BcuHyxIXve724XG+zuJ1n3U1cUweRtTzQOA4IHbuQw3N34SZw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/core@7.29.7': resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} + '@babel/core@8.0.1': + resolution: {integrity: sha512-5FgxM4dLQpMJHSiVATk8foW263dVHQHBVpXYiimNECVWG01f4nFyEbQixeT6Mwvg7TayREJ2gpKl3o2RoMdnqw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/generator@7.29.8': resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} 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/generator@8.0.5': + resolution: {integrity: sha512-f/TuhuMAxJqhwxEGNsJrswuG9VHmh0oNFoQoo6TbpgtFAz9wYZXcTAcWZMHfp7ljesr0RG04bp3Aos9GI59L7w==} + engines: {node: ^22.18.0 || >=24.11.0} '@babel/helper-annotate-as-pure@7.29.7': resolution: {integrity: sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==} engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@8.0.0': + resolution: {integrity: sha512-NSpMkMsvvZqzThJ0p1B02cbtA2ObEyfBvq950bmNkyxsxvcxwhvvCB036rKhlEnuBBo30bOrk13u3FzlKSoRrw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-compilation-targets@7.29.7': resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@8.0.5': + resolution: {integrity: sha512-Qk8ahMGooH5mz6uuhoDvfZGkUf/Mf3RTBucVVl4MKx4LKMTv872TeW8O92h15iVtlN8wAROBIpI1aV6x1z0LCQ==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-create-class-features-plugin@7.29.7': resolution: {integrity: sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==} engines: {node: '>=6.9.0'} @@ -990,6 +887,10 @@ packages: resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} + '@babel/helper-globals@8.0.0': + resolution: {integrity: sha512-lLozHOM6sWWlxNo8CYqHy4MBZeTvHXNgVPBfPOGsjPKUzHC2Az9QwB6gxdQmpwHl6GlQtbGgS+lj5887guDiLw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-member-expression-to-functions@7.29.7': resolution: {integrity: sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==} engines: {node: '>=6.9.0'} @@ -1036,6 +937,10 @@ packages: resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@8.0.0': + resolution: {integrity: sha512-6mJgmFFFIIO82vvoLt9XtRC7/TkzXfts1t/SpRX4IHSzMgqoPYCWesVu1udUPUWioAE/2fcG6WuI8zrkE1gwrg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} @@ -1044,10 +949,18 @@ packages: resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@8.0.4': + resolution: {integrity: sha512-4wFaiLd0bVo4cIoTXI3zKI038NIWE/cr3jvBjejOVYVxV/m8Ltav1USiGzG1fmS5J2RhgEOgXNNK46cRPnRsrg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-option@7.29.7': resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@8.0.0': + resolution: {integrity: sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-wrap-function@7.29.7': resolution: {integrity: sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==} engines: {node: '>=6.9.0'} @@ -1056,11 +969,20 @@ packages: resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} + '@babel/helpers@8.0.5': + resolution: {integrity: sha512-fQtPOXjYOYv85PIdwotp2TJGVYOycX0PQq+l844fFAxOULtBy8BVF35GyeueX0r4KvDthqPH5xAI1clQPk/2uA==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/parser@7.29.8': resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@8.0.5': + resolution: {integrity: sha512-51RXvQNFakaS0bTpYiGkxNbUVwkPO4kONv6EVLorZABxsx+KZ6Z7uSYvi/wmKS/+X+rfj9RvOw0/ZNh+cmI0Rw==} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7': resolution: {integrity: sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==} engines: {node: '>=6.9.0'} @@ -1561,14 +1483,26 @@ packages: resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} + '@babel/template@8.0.0': + resolution: {integrity: sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/traverse@7.29.8': resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} engines: {node: '>=6.9.0'} + '@babel/traverse@8.0.5': + resolution: {integrity: sha512-XFfnuvapSc/vJOcUO7kwORSvpBIvraofKEZ2dhT0PjiF21BRCD7YbAFC8UEeDJNeLoQz82/gVqzgX5hCzkCbdg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/types@7.29.8': resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} + '@babel/types@8.0.5': + resolution: {integrity: sha512-eVdMqi3ej5aHhyQ2Si6yD2cAWeV8FJK9UrhK5aL0Sd8hu5GhT+YswhVNbVheOGVYMg8kuGuMaUpkB3stjj4z8A==} + engines: {node: ^22.18.0 || >=24.11.0} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -1621,8 +1555,8 @@ packages: cpu: [x64] os: [win32] - '@bufbuild/protobuf@2.13.0': - resolution: {integrity: sha512-acq7c49vxfm1ggJ95P70TX7ABDM0vxr1SYD3BB0o0jnBLB4OAqeHyKuN+cD3w80gXEDQ2zxHpR6CUeA+O/aU9g==} + '@bufbuild/protobuf@2.15.0': + resolution: {integrity: sha512-DAheWUkVr/SJTWCc+lg9dhY0eN4SaWlf4+bG1KzHeXbnqt0AfB/NX0Z+VunGlM1ki1B4zVvye27MpKh/svySUA==} '@capsizecss/unpack@4.0.0': resolution: {integrity: sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==} @@ -1636,9 +1570,6 @@ packages: resolution: {integrity: sha512-y7/yvZ2TPAnR9+jnc00klvNNLkJiXFFrQA/hlLCcxA9a2A4zQIOimyFQ9XfwYKiGD1fb5GY8vbKIIgO8d5Tb2A==} engines: {node: '>= 20.12.0'} - '@colordx/core@5.5.0': - resolution: {integrity: sha512-3PxTH8itZzltK0U9jTwVVnjLXvnDYuq3m+QXsHkENxWiPRh4WaoLcs1SQjqgZ55kS+QyirpH5BVwzP2gMVG6EQ==} - '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} @@ -1724,6 +1655,9 @@ packages: '@emnapi/core@1.11.1': resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + '@emnapi/core@1.11.2': + resolution: {integrity: sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==} + '@emnapi/core@1.11.3': resolution: {integrity: sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==} @@ -1736,6 +1670,9 @@ packages: '@emnapi/runtime@1.11.1': resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + '@emnapi/runtime@1.11.2': + resolution: {integrity: sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==} + '@emnapi/runtime@1.11.3': resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} @@ -1768,12 +1705,6 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.28.1': - resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.28.2': resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} engines: {node: '>=18'} @@ -1786,12 +1717,6 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.28.1': - resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.28.2': resolution: {integrity: sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==} engines: {node: '>=18'} @@ -1804,12 +1729,6 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.28.1': - resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.28.2': resolution: {integrity: sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==} engines: {node: '>=18'} @@ -1822,12 +1741,6 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.28.1': - resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.28.2': resolution: {integrity: sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==} engines: {node: '>=18'} @@ -1840,12 +1753,6 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.28.1': - resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.28.2': resolution: {integrity: sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==} engines: {node: '>=18'} @@ -1858,12 +1765,6 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.28.1': - resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.28.2': resolution: {integrity: sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==} engines: {node: '>=18'} @@ -1876,12 +1777,6 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.28.1': - resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.28.2': resolution: {integrity: sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==} engines: {node: '>=18'} @@ -1894,12 +1789,6 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.28.1': - resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.28.2': resolution: {integrity: sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==} engines: {node: '>=18'} @@ -1912,12 +1801,6 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.28.1': - resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.28.2': resolution: {integrity: sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==} engines: {node: '>=18'} @@ -1930,12 +1813,6 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.28.1': - resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.28.2': resolution: {integrity: sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==} engines: {node: '>=18'} @@ -1948,12 +1825,6 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.28.1': - resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.28.2': resolution: {integrity: sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==} engines: {node: '>=18'} @@ -1966,12 +1837,6 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.28.1': - resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.28.2': resolution: {integrity: sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==} engines: {node: '>=18'} @@ -1984,12 +1849,6 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.28.1': - resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.28.2': resolution: {integrity: sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==} engines: {node: '>=18'} @@ -2002,12 +1861,6 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.28.1': - resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.28.2': resolution: {integrity: sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==} engines: {node: '>=18'} @@ -2020,12 +1873,6 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.28.1': - resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.28.2': resolution: {integrity: sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==} engines: {node: '>=18'} @@ -2038,12 +1885,6 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.28.1': - resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.28.2': resolution: {integrity: sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==} engines: {node: '>=18'} @@ -2056,12 +1897,6 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.28.1': - resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.28.2': resolution: {integrity: sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==} engines: {node: '>=18'} @@ -2074,12 +1909,6 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.28.1': - resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-arm64@0.28.2': resolution: {integrity: sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==} engines: {node: '>=18'} @@ -2092,12 +1921,6 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.28.1': - resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.28.2': resolution: {integrity: sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==} engines: {node: '>=18'} @@ -2110,12 +1933,6 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.28.1': - resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.28.2': resolution: {integrity: sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==} engines: {node: '>=18'} @@ -2128,12 +1945,6 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.28.1': - resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.28.2': resolution: {integrity: sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==} engines: {node: '>=18'} @@ -2146,12 +1957,6 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.28.1': - resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - '@esbuild/openharmony-arm64@0.28.2': resolution: {integrity: sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==} engines: {node: '>=18'} @@ -2164,12 +1969,6 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.28.1': - resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.28.2': resolution: {integrity: sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==} engines: {node: '>=18'} @@ -2182,12 +1981,6 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.28.1': - resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.28.2': resolution: {integrity: sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==} engines: {node: '>=18'} @@ -2200,12 +1993,6 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.28.1': - resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.28.2': resolution: {integrity: sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==} engines: {node: '>=18'} @@ -2218,12 +2005,6 @@ packages: 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] - '@esbuild/win32-x64@0.28.2': resolution: {integrity: sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==} engines: {node: '>=18'} @@ -2296,10 +2077,6 @@ packages: '@fontsource/roboto@5.3.0': resolution: {integrity: sha512-BapRJOWYP+LZ21zp+wBQjfpPYKRoxc4LspJ/RLuI+HSMBD5u/X4O+ESDrSvEqDSy0rAl7GwBJ+09mdc16cVQ1Q==} - '@gar/promise-retry@1.0.3': - resolution: {integrity: sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==} - engines: {node: ^20.17.0 || >=22.9.0} - '@harperfast/extended-iterable@1.0.3': resolution: {integrity: sha512-sSAYhQca3rDWtQUHSAPeO7axFIUJOI6hn1gjRC5APVE1a90tuyT8f5WIgRsFhhWA7htNkju2veB9eWL6YHi/Lw==} @@ -2491,134 +2268,152 @@ packages: cpu: [x64] os: [win32] - '@inquirer/ansi@1.0.2': - resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} - engines: {node: '>=18'} + '@inquirer/ansi@2.0.8': + resolution: {integrity: sha512-WpQM+Ti6Z40EFwwt+uL2p4UabT+W179zHp6HhLVOzfbwnVn05IPO/eXIZXGNqcT1jbQ15SujNLzQ39k4QPPxBQ==} + 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.5': + resolution: {integrity: sha512-bRt8J8m+Fot9CXv+zNQGXUq2ET0MggR1fPz7v6edN6MFYmsbfGnMmkmWZJEegMKqrAC8ej/o1sqisHZXZJMAfQ==} + 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.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/core@10.3.2': - resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} - engines: {node: '>=18'} + '@inquirer/confirm@6.3.2': + resolution: {integrity: sha512-Xvr/0HggjddPtGppuqVmxhTw+Hr8PvsZ/k0HmOEaAqQEt80OITNkFWnsdNmyT0/eM4Ab+iJLx2R8rctlEyfSVg==} + 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/core@12.0.3': + resolution: {integrity: sha512-wsSy0sznmXwkty+2PzZwx00Cazc/E0r0B7mAzdGROz2Ct+DFZXaK7WDjGZvgjRldxH5ZhFVfF2lgkYrqgOw2KA==} + 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/editor@5.3.3': + resolution: {integrity: sha512-YsKkS2q63IiLtaDK/9nqzdComN97SDQrmKiyNggN+ceP4ty+Z6VwyTz3FpjeUWeW1Efss2xHFKCC9sx7hnrsxg==} + 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/expand@5.1.5': + resolution: {integrity: sha512-uHuXLmXW+TtIfT/9vSBotypAkqn1n34Ul+CLGPos/xANyO4Ff5xZzkYhbKR4NEcfVK4a9mHQOpwVZzluSHFRGw==} + 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/external-editor@3.0.5': + resolution: {integrity: sha512-f3QQJRIX5ZEneBHNUIuPjmbdzHnmRFJA8r2dkcb8q+OM5Uv5KtnuAttQumnrjcBVBM3mcTX1CkmtAkU58VRZxg==} + 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/figures@2.0.9': + resolution: {integrity: sha512-EAWgUTGQ/Umgga51dE3B2PUHbufuXarDfg86uVgoSgNHNNQnyFKcOrQLWVqYMghuSyHh8+2HUH0Js9cTC1WAdg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/input@5.1.6': + resolution: {integrity: sha512-HtcJhB2QFVXbLuJ5S3syhNbTUVxYvwqV4VRBDkQceBloC9bmTViUoRFP5PbSaDZb3HzfPmpuU/gG4ybVBz4FHA==} + 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/number@4.2.3': + resolution: {integrity: sha512-6Yuwh1NGSbu1Lo4N1EWjXs1jKRntLg/ZCwhmeorEHde90v1XxAozdbd4Iu30eOQLW+6h1hp2O9ujNfLSbTPJnA==} + 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/password@5.2.2': + resolution: {integrity: sha512-W9zYdyzogK+6110mqwaSJWCBu2yA5Q/OfnGSjjZB1bNpHlmUozXxTl0+QOZBNeVd6Qo81/qT75gW05gLAtITxw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.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/prompts@8.5.2': + resolution: {integrity: sha512-IYR/3C/paEVVQYQvdDlFZVjRCJVYHHON0XXMH91KO9GSxs0TdKYWlUdvfQl2EfAHDxUaN3IBffkE/BDTh5nJ6g==} + 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/rawlist@5.3.5': + resolution: {integrity: sha512-1oHky1ONfCOwNrnkQGDE1oaSij/3fI6HFMSf2H/WsGO2lEyDX9My82iggITSy9ddSZ8yk8j9v41OI0fVoSIoaA==} + 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/search@4.3.3': + resolution: {integrity: sha512-fyuIU1Nbpvwlikjg3gXwJFDI11+EFjqQ7P+iByfmivIKQ1vmaykNrD/vy5unHuUqUpsOsnvJ25//tPF7E/RBRA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@5.2.5': + resolution: {integrity: sha512-9kc15hr8r/kI+3DO/xLog5nOzTz1jqsHXa6JBFzmQKhkoJ8Slda1I1L/uD8ZSZ9tF1yp79wwXe7mclvX1rqR2Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@4.1.1': + resolution: {integrity: sha512-yJoHYrMnxIsJZCY+0Vb66Dy3he3kL3e2wOBKhoSwWWAzZAY82emlxwgprCtp6yRixvNRNq9ztfRWQYPNr3Go7A==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -2641,10 +2436,6 @@ packages: resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} engines: {node: '>=8'} - '@jest/console@30.4.1': - resolution: {integrity: sha512-v3bhyxUh9Hgmo5p6hAOXe14/R3ZxZDOsvHleh4B07z3m/x4/ngPUXEm9XwK4sF4u+f+P2ORb0Ge+MgpaqRMVDA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/console@30.5.1': resolution: {integrity: sha512-u5Ncuc+gXVUwjNMFQOnphHo2Qx2DxyC8Dvpmf4HCx4y0kvSkRRNiQYRixrvOP4V7y52JcSuNxqochCt0PpdHVg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2662,24 +2453,10 @@ packages: resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/diff-sequences@30.4.0': - resolution: {integrity: sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/diff-sequences@30.5.0': resolution: {integrity: sha512-OsqBjHXCn8cadasoAZBP6nWYvMsRhpMzGXTpxJ5aO04NlbdhIz+FVe3q49l0AwVhsz/cEmIpBes6gAFl1/dWQg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/environment-jsdom-abstract@30.2.0': - resolution: {integrity: sha512-kazxw2L9IPuZpQ0mEt9lu9Z98SqR74xcagANmMBU16X0lS23yPc0+S6hGLUz8kVRlomZEs/5S/Zlpqwf5yu6OQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - canvas: ^3.0.0 - jsdom: '*' - peerDependenciesMeta: - canvas: - optional: true - '@jest/environment-jsdom-abstract@30.5.1': resolution: {integrity: sha512-J395vmP3Fb2Te0JmF7pe4si4jpfbXef1YsY4UpHYL6OOxS2molu9Dsie1VmIiUalXdtmz1P5QRgc+5hBD+ssBg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2691,14 +2468,6 @@ packages: canvas: optional: true - '@jest/environment@30.2.0': - resolution: {integrity: sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/environment@30.4.1': - resolution: {integrity: sha512-AK9yNRqgKxiabqMoe4oW+3/TSSeV8vkdC7BGaxZdU0AFXfOpofTLqdru2GXKZghP3sdgwE9XXpnVwfZ8JnFV4w==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/environment@30.5.1': resolution: {integrity: sha512-eYJAkOsrpwDXPcoLNEG6lN9Zo3Cy35pxnVQD74vyIfsi/Q8wB/lZFsEjU4wrecOgT4ZviQDZaX/cFIJyMdMxTw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2707,30 +2476,14 @@ packages: resolution: {integrity: sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/expect-utils@30.4.1': - resolution: {integrity: sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/expect-utils@30.5.1': resolution: {integrity: sha512-WcRWhHQdTMRDpyWKZ/6MINBmovI7zeD+bL8wFjCncRV3NQOwKy1X45IfyblfHR4k/XciIlNEdFL9QjFO+HNKOg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/expect@30.4.1': - resolution: {integrity: sha512-ginrj6TMgh2GshLUGCjO94Ptx9HhdZA/I6A9iUfyeLKFtdAjnKzHDgzgP9HYQgbxM1lbXScQ2eUBz2lGeVDPWA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/expect@30.5.1': resolution: {integrity: sha512-uOGd40P/COyUp9xHf5jeGiGJC2/ANg+2+Tk9/xN5/LxmlY/r/gxsPrx3DTEtaRZsLAX4wgNSLEDELZ5bmVs2bA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/fake-timers@30.2.0': - resolution: {integrity: sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - - '@jest/fake-timers@30.4.1': - resolution: {integrity: sha512-iW5umdmfPeWzehrVhugFQZqCchSCud5S1l2YT0O9ZhjRR0ExclANDZkiSBwzqtnlOn0J1JXvO+HZ6rkuyOVOgQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/fake-timers@30.5.1': resolution: {integrity: sha512-rEkV6YzpBXo/L9cnj2ibyuYZuyGiNWaPUsyCu3HYJbqCV4jnEiKmf7hId20z8eKjZ0JQ9jtIYKxRSmYB/OYSsA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2743,10 +2496,6 @@ packages: resolution: {integrity: sha512-9/2VUPitAjmBzbvDvqrxmvB7BzWsBW0WmkkojX1ODuxX1NLGxx9gfaZpHB0z8DtJ9uhGNmZG/VXBhf8uO0OV8Q==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/globals@30.4.1': - resolution: {integrity: sha512-ZbuY4cmXC8DkxYjfvT2DbcHWL2T6vmsMhXCDcmTB2T0y0gaezBI77ufq5ZAIdcRkYZ7NEQEDg1xFeKbxUJ5v5Q==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/globals@30.5.1': resolution: {integrity: sha512-VhqvQ251XIC7pk46YymI3HCeBLOdvriDw9zibekodAHEwoVvbVVgpU5H13GvmyOAzxtZCfsm1uvzqkaqJf2I+g==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2755,23 +2504,10 @@ packages: resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/pattern@30.4.0': - resolution: {integrity: sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/pattern@30.5.0': resolution: {integrity: sha512-HdNQYSdRTEBNrginaqzQtTjG0HRMfrra/z6Ok7uL3S87vSlarIVohEsJsSj5edu3MiHoHjAkvPROz5ZjoKai+w==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/reporters@30.4.1': - resolution: {integrity: sha512-/SnkPCzEQpUaBH81kjdEdDdo2WZl5hxw+BmLDGWjRkm8o7XlhjwsU36cqwe5PGBE5WYpBvDzRSdXx9rbGuJtNA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true - '@jest/reporters@30.5.1': resolution: {integrity: sha512-RbUXIfv85KxitJn4l3MpAoMilkvXe2QCO5lXxHWftywo/VdZ5vkEoHRDgphcxP6qmoIkUaN8/UZj6NhdkIFdJg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2785,50 +2521,26 @@ packages: resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/schemas@30.4.1': - resolution: {integrity: sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/schemas@30.5.0': resolution: {integrity: sha512-/hunigyNpc4RCjC0VaW3f5RCUZVM2+WQ65qP7z083Gmvac7or2LI50XVNOtE4YPgBpV0yxYiAgorAPGniCoJmg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/snapshot-utils@30.4.1': - resolution: {integrity: sha512-ObY4ljvQ95mt6iwKtVLetR/4yXiAgl3H4nJxhztr0MTjrN97TwDYrnCp/kF60Ec9HdhkWTHSu+Hg05aXfngpOA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/snapshot-utils@30.5.1': resolution: {integrity: sha512-V3wnxNtiVmw5PPVg433Cn3VdXnsOeu/ofLw3KC04Bn2y1wIlU5kozXQn48rhrgj/02LrCVtntTEF1yBha0XSUw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/source-map@30.0.1': - resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/source-map@30.5.0': resolution: {integrity: sha512-xWpTJP9D0bDFGbPGT8XuWSwwha/iHADyyKzUnMx4UbdgnHugxrDaQFO4RZ8x4ZsFzRP6pNii8uvlgKCDxCIuDg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/test-result@30.4.1': - resolution: {integrity: sha512-/ZG7pgEiOmmWkN9TplKbOu4id2N5lh7FHwRwlkgBVAzGdRH+OkkQ8wX/kIxg4zmd3ZQvAL1RwL2yWsvNYYECTw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/test-result@30.5.1': resolution: {integrity: sha512-A/1S6ZBdpic50E0pxLgvaB9XNPL4k7AksmG69OO2oiotxciWZwHkbOec+qbIi+uUtIIByOVlXJUl97oZUsz+Jw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/test-sequencer@30.4.1': - resolution: {integrity: sha512-PeYE+4td5rKjoRPxztObrXU+H8hsjZfxKMXOcmrr34JerSyB/ROOxbbicz8B7A5j9R9VayDnVPvBmedqCsFCdw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/test-sequencer@30.5.1': resolution: {integrity: sha512-SHcPnrjdVRYJv6y6l4JUTy4Jccu7zmO6BmiOfOA34UiVBto22s19WiDC0A/2qfM7MWB/qdcoqfSIRMitpjTT4A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/transform@30.4.1': - resolution: {integrity: sha512-Wz0LyktlTvRefoymh+n64hQ84KNXsRGcwdoZ8CSa0Ea+fgYcHZlnk+hDP7v2MS7il2bQ5uTEIxf4/NNfhMN4KQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/transform@30.5.1': resolution: {integrity: sha512-EDnDhn0jleU9ZhpVoA4gvqw+Ev0iw/r5upNT2b79RiwiaTiYAMMhvNJP3lmocjIe5J2ZkoNr0B3K/J6O5GIm4Q==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2837,10 +2549,6 @@ packages: resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/types@30.4.1': - resolution: {integrity: sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/types@30.5.1': resolution: {integrity: sha512-LvVYn83nnXPl+Rg98nvcFgjx6nRMTArhSn6RAX/w3ELn54S8A42TYZvsCMdGUqTM8S0wyXbtlQdU6Hi6dykj9g==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2848,6 +2556,9 @@ packages: '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/gen-mapping@0.4.0-beta.0': + resolution: {integrity: sha512-JdGNkbE4GlNPYQhM0L95fBQr7ctLZJ276QXQLTad4t1oSdnnCI3fDq9DW3BqYAWv8Wc3+HS+4Gsii1oPMCfz1w==} + '@jridgewell/remapping@2.3.5': resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} @@ -2861,174 +2572,54 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@jsonjoy.com/base64@1.1.2': - resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} - engines: {node: '>=10.0'} + '@listr2/prompt-adapter-inquirer@4.2.5': + resolution: {integrity: sha512-pYGy9dTdTwXdasPgyohkr0HoQ4FrkAzFnsUZl/gcnadDArbpZ8e+fgr+F9WBdNEl2y00mb9bCM4WgmoBkZJ27A==} + engines: {node: '>=22.13.0'} peerDependencies: - tslib: '2' + '@inquirer/prompts': '>= 3 < 9' + listr2: 11.0.0 - '@jsonjoy.com/base64@17.67.0': - resolution: {integrity: sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/buffers@1.2.1': - resolution: {integrity: sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/buffers@17.67.0': - resolution: {integrity: sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/codegen@1.0.0': - resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/codegen@17.67.0': - resolution: {integrity: sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/fs-core@4.68.0': - resolution: {integrity: sha512-OAioDU3UGV34ECVTB4pt641OfUzuyDuyUmgrlVM+Fp9g4YyavZrB4xxWuupYrU66+81+um9D0DAzYhjXto5ysQ==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/fs-fsa@4.68.0': - resolution: {integrity: sha512-dC6VeW8uqXMF9Hkqb7qYST/t165XCHG+bNwwMzRetoNZRxJC8anr5XCJSAk5B+YLHGn6SP+noNltU6OFg/oa7A==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/fs-node-builtins@4.68.0': - resolution: {integrity: sha512-j3bb+k4NuqpqXQzinCLeagS7LGLCUvPqA6TnpaBkBqhnPjbzETYiQFpD7pWaV8cGOhhZcAv9dmpzk5mxNb1qsg==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/fs-node-to-fsa@4.68.0': - resolution: {integrity: sha512-dtLpmLQxw3IBcGxoK9tVvftwmQLl9qmQyivt8DZpeEwDUcQkxk2CLMZERXtmOlF1mWXfT9CqtjjMiswkb03rKw==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/fs-node-utils@4.68.0': - resolution: {integrity: sha512-Q84pogSfRaz0WNrBBy+HzBmhNu4m6tWoM0po8bLwW4pKvrwrSadOgFpm1sku08sfg0QAtUubkWuZEBDpNI3toQ==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/fs-node@4.68.0': - resolution: {integrity: sha512-xYDlpSk3UDHjcI0kiz6kNJ/oVTEzlS7kUzAoAsgJh2/+yOtYV/eZW7M8O9Fj+ED3BDONYKt7+8OiPlWl6xpFeA==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/fs-print@4.68.0': - resolution: {integrity: sha512-sdy9F6N9QEcyAcU8OxEQmB91mlgiyuMphTFeijQ+qFgigCOeT16lW3X1dCWNzD7wwKLOKXnnYqsFyeq4LWSlYQ==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/fs-snapshot@4.68.0': - resolution: {integrity: sha512-eV44t9KY9LH47aPR1a4Nv4xYxM1vQ+OKEw9Mx/3zkuxGOILwmtFddlh5HakdMFpFHwuFNPw1s+NGq2eVYFpQRA==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/json-pack@1.21.0': - resolution: {integrity: sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/json-pack@17.67.0': - resolution: {integrity: sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/json-pointer@1.0.2': - resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/json-pointer@17.67.0': - resolution: {integrity: sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/util@1.9.0': - resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@jsonjoy.com/util@17.67.0': - resolution: {integrity: sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - - '@leichtgewicht/ip-codec@2.0.5': - resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} - - '@listr2/prompt-adapter-inquirer@3.0.5': - resolution: {integrity: sha512-WELs+hj6xcilkloBXYf9XXK8tYEnKsgLj01Xl5ONUJpKjmT5hGVUzNUS5tooUxs7pGMrw+jFD/41WpqW4V3LDA==} - engines: {node: '>=20.0.0'} - peerDependencies: - '@inquirer/prompts': '>= 3 < 8' - listr2: 9.0.5 - - '@lmdb/lmdb-darwin-arm64@3.5.1': - resolution: {integrity: sha512-tpfN4kKrrMpQ+If1l8bhmoNkECJi0iOu6AEdrTJvWVC+32sLxTARX5Rsu579mPImRP9YFWfWgeRQ5oav7zApQQ==} + '@lmdb/lmdb-darwin-arm64@3.5.6': + resolution: {integrity: sha512-mY5FG4TjPAkY4P0w+OhHaUka5mDh2TX2WKYIwuKzJ1zeW3VvRgxdam/lGJTquI+bthTx5CSHDW+BAQCnNAzkEA==} cpu: [arm64] os: [darwin] - '@lmdb/lmdb-darwin-x64@3.5.1': - resolution: {integrity: sha512-+a2tTfc3rmWhLAolFUWRgJtpSuu+Fw/yjn4rF406NMxhfjbMuiOUTDRvRlMFV+DzyjkwnokisskHbCWkS3Ly5w==} + '@lmdb/lmdb-darwin-x64@3.5.6': + resolution: {integrity: sha512-foa+pwitysO8k+xhs7psBFfTKnVgR69NlZRRTHaFVDqphh7AdGpLeyRzKw/ofatr/sN6TiHRRW6mmop0ZrrppQ==} cpu: [x64] os: [darwin] - '@lmdb/lmdb-linux-arm64@3.5.1': - resolution: {integrity: sha512-aoERa5B6ywXdyFeYGQ1gbQpkMkDbEo45qVoXE5QpIRavqjnyPwjOulMkmkypkmsbJ5z4Wi0TBztON8agCTG0Vg==} + '@lmdb/lmdb-linux-arm64@3.5.6': + resolution: {integrity: sha512-HmiyFFdJa38s1heCMSooSPaBSFTHJ3C+ERPp28xAPlDX1YiALJVOgbry065nXd8Y7KISWjnw05zpG1RX8IfftA==} cpu: [arm64] os: [linux] - '@lmdb/lmdb-linux-arm@3.5.1': - resolution: {integrity: sha512-0EgcE6reYr8InjD7V37EgXcYrloqpxVPINy3ig1MwDSbl6LF/vXTYRH9OE1Ti1D8YZnB35ZH9aTcdfSb5lql2A==} + '@lmdb/lmdb-linux-arm@3.5.6': + resolution: {integrity: sha512-QR4YRyR5h5Z8eGXrNQjiyo2NNDfqi3tCc9dQG5Is1blCt+qWw1ZoBWhlWAr5d+jshkifMIJjVHzHGKbkKzF8Tw==} cpu: [arm] os: [linux] - '@lmdb/lmdb-linux-x64@3.5.1': - resolution: {integrity: sha512-SqNDY1+vpji7bh0sFH5wlWyFTOzjbDOl0/kB5RLLYDAFyd/uw3n7wyrmas3rYPpAW7z18lMOi1yKlTPv967E3g==} + '@lmdb/lmdb-linux-x64@3.5.6': + resolution: {integrity: sha512-ADzCuCF2cTNiX9kDScqcz1fjnAkxPpQNneV3KFTdV3wWtVlI2sTGzySoMTgDpinkMMFj1NTJlxA6XR8fwc4hlA==} cpu: [x64] os: [linux] - '@lmdb/lmdb-win32-arm64@3.5.1': - resolution: {integrity: sha512-50v0O1Lt37cwrmR9vWZK5hRW0Aw+KEmxJJ75fge/zIYdvNKB/0bSMSVR5Uc2OV9JhosIUyklOmrEvavwNJ8D6w==} + '@lmdb/lmdb-win32-arm64@3.5.6': + resolution: {integrity: sha512-J7A9aEQsQiv0TYtBGL7NDIPp2lOS8nnl+zm4sWZm1xlsTTaQ4PgD096Adzdrk27rw3UxCkDXdCUa4ax41oztBQ==} cpu: [arm64] os: [win32] - '@lmdb/lmdb-win32-x64@3.5.1': - resolution: {integrity: sha512-qwosvPyl+zpUlp3gRb7UcJ3H8S28XHCzkv0Y0EgQToXjQP91ZD67EHSCDmaLjtKhe+GVIW5om1KUpzVLA0l6pg==} + '@lmdb/lmdb-win32-x64@3.5.6': + resolution: {integrity: sha512-1g7G0knRX2iV/voDu54yxrGqw5Dk0w2oIYb7dgJq8IkOi+m7wbD8Q3QpPFjh0C01G58S88dqGn03len6UPCXsg==} cpu: [x64] os: [win32] @@ -3053,108 +2644,6 @@ packages: '@cfworker/json-schema': optional: true - '@module-federation/bridge-react-webpack-plugin@2.8.2': - resolution: {integrity: sha512-cEhnpCsWHqUndQC6WKtwat5BGz+IU0UdCjzyXrZtx1UqHX1jRB0+DZxB4DKYKtxTko8fsV0FUCJ/0FjKZY6z8g==} - - '@module-federation/cli@2.8.2': - resolution: {integrity: sha512-SrRe2UOzjYux/9Zf7AYymGGsYpJgrIHUPF+T9JQ+rZ7MC9Uiy5rNUtSYdxNrdFpVaRZYXMrI4b82iUy66CU6UA==} - engines: {node: '>=16.0.0'} - hasBin: true - - '@module-federation/dts-plugin@2.8.2': - resolution: {integrity: sha512-pwZFW8b2LZTymMMC+o2M9xMXDIQKAHGtCRqj/IkOp0jHRYrKjK9cma9xoUNst8/R3sEn878/i9wgv/43BnCEyg==} - peerDependencies: - typescript: ^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - vue-tsc: '>=1.0.24' - peerDependenciesMeta: - vue-tsc: - optional: true - - '@module-federation/enhanced@2.8.2': - resolution: {integrity: sha512-XVCp1dz7ADd2YgjuvGVsS7IVJ8ViuAUCEz9gAb4M0qMJCjcPfzXQ1CzV1/Me1lKaPPwNj/cJ3NXTRvXjMHanGg==} - hasBin: true - peerDependencies: - typescript: ^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - vue-tsc: '>=1.0.24' - webpack: ^5.0.0 - peerDependenciesMeta: - typescript: - optional: true - vue-tsc: - optional: true - webpack: - optional: true - - '@module-federation/error-codes@0.21.6': - resolution: {integrity: sha512-MLJUCQ05KnoVl8xd6xs9a5g2/8U+eWmVxg7xiBMeR0+7OjdWUbHwcwgVFatRIwSZvFgKHfWEiI7wsU1q1XbTRQ==} - - '@module-federation/error-codes@2.8.2': - resolution: {integrity: sha512-8inlDv48QOjA//CLQ3epjoHEiMQGsz1Pmtu2N+s7gQVggn6AYHpjnMe8AsyGxtpaPg3wbX0HmBZtRFggpXUB9A==} - - '@module-federation/inject-external-runtime-core-plugin@2.8.2': - resolution: {integrity: sha512-RetbaupJGiT2FtmA3WWGm72xZkNSJ6Xyy53jSgtu9HRnR/5xt/wiQl+Pe/iEfzgfLsUnUAkOsSfiPBXrDtNizw==} - peerDependencies: - '@module-federation/runtime-tools': 2.8.2 - - '@module-federation/managers@2.8.2': - resolution: {integrity: sha512-OQfnoUwy1IUfn6DaI2S/DPKgnElkYlP+gG8mbUQlJVEW1Zg/8V/dZbNgJUKikTJly6yR1r08PK59g7239ITYMw==} - - '@module-federation/manifest@2.8.2': - resolution: {integrity: sha512-mJUZo7QFL46NXoEWNby3CfPFFm2235J7LO6bXAAMrEIT4qF8QHLiqfoo5dZmrisRgM6e+muriWtGvPFMprnXyA==} - - '@module-federation/node@2.7.49': - resolution: {integrity: sha512-xNGYfhA2aqFpogb/uq6lwBeEbnmDLV6PwHzSe97mRrSSr00eUKAMwlLG6PcQP6ynbkPeDG86RYj/YUC7EWgLMA==} - peerDependencies: - webpack: ^5.40.0 - peerDependenciesMeta: - webpack: - optional: true - - '@module-federation/rspack@2.8.2': - resolution: {integrity: sha512-HEDirYhVYvx7IzP9jes6KLPMqSoSQwuLfBzPSOgBqY7sIH/e9zRSRO1qh5C8OXYKHi23WcQpGY+EeecIK9wxWw==} - peerDependencies: - '@rspack/core': ^0.7.0 || ^1.0.0 || ^2.0.0-0 - typescript: ^4.9.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - vue-tsc: '>=1.0.24' - peerDependenciesMeta: - typescript: - optional: true - vue-tsc: - optional: true - - '@module-federation/runtime-core@0.21.6': - resolution: {integrity: sha512-5Hd1Y5qp5lU/aTiK66lidMlM/4ji2gr3EXAtJdreJzkY+bKcI5+21GRcliZ4RAkICmvdxQU5PHPL71XmNc7Lsw==} - - '@module-federation/runtime-core@2.8.2': - resolution: {integrity: sha512-PEkkK9MUp+nUCeQMS4ox3QGZfwwxgfjGA7P4umEnr5c3y8DLNDR+26tyHf/Gkjen2VsjlcyL+mEAQo1Zi4IE3g==} - - '@module-federation/runtime-tools@0.21.6': - resolution: {integrity: sha512-fnP+ZOZTFeBGiTAnxve+axGmiYn2D60h86nUISXjXClK3LUY1krUfPgf6MaD4YDJ4i51OGXZWPekeMe16pkd8Q==} - - '@module-federation/runtime-tools@2.8.2': - resolution: {integrity: sha512-eW/yPvZB2LbpbyPXPTnOeF1ieWl165D9QcPV0y5Bj1QYGDjL2cb+dnzrBp0fmFtJhCYqmAdsVNoYItVa3yuJ3g==} - - '@module-federation/runtime@0.21.6': - resolution: {integrity: sha512-+caXwaQqwTNh+CQqyb4mZmXq7iEemRDrTZQGD+zyeH454JAYnJ3s/3oDFizdH6245pk+NiqDyOOkHzzFQorKhQ==} - - '@module-federation/runtime@2.8.2': - resolution: {integrity: sha512-SUoP+PD5EjSPSi6FxEPGIZoRkFifxdeYcVQbJE9mO0VEjF51gAk3/TgX8k0vzUryOBPmXekLr9SfQXU6DqUtvA==} - - '@module-federation/sdk@0.21.6': - resolution: {integrity: sha512-x6hARETb8iqHVhEsQBysuWpznNZViUh84qV2yE7AD+g7uIzHKiYdoWqj10posbo5XKf/147qgWDzKZoKoEP2dw==} - - '@module-federation/sdk@2.8.2': - resolution: {integrity: sha512-OPS/lbQjraLXoWniQpCwQ/vqgURHTrhsackSNcOPmcJHM3LyR+DabxUc0pl8jAqExsW2l+uepQq7+/Gkei871w==} - - '@module-federation/third-party-dts-extractor@2.8.2': - resolution: {integrity: sha512-Xf3iZ4iDi972XMOMbmUm/c5Vwwb6cTsU+Jpoz96lUom6Yps9FQX48elIdhgcQsL7/K64PqdOONsrlZVo8zphKA==} - - '@module-federation/webpack-bundler-runtime@0.21.6': - resolution: {integrity: sha512-7zIp3LrcWbhGuFDTUMLJ2FJvcwjlddqhWGxi/MW3ur1a+HaO8v5tF2nl+vElKmbG1DFLU/52l3PElVcWf/YcsQ==} - - '@module-federation/webpack-bundler-runtime@2.8.2': - resolution: {integrity: sha512-g4xQgfgMMCKgJjVMBh7nIYGjLGNDYwSZ4lfpTdkVyWDnxmR3SL6VPQTJEZHRYPyqvrTtZE1zdDhDd++yNRvLdA==} - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==} cpu: [arm64] @@ -3304,16 +2793,6 @@ packages: '@napi-rs/wasm-runtime@0.2.4': resolution: {integrity: sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ==} - '@napi-rs/wasm-runtime@1.0.7': - resolution: {integrity: sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==} - - '@napi-rs/wasm-runtime@1.2.2': - resolution: {integrity: sha512-JfB4kuJQjaoHuCTseIINHtHWeJnvgEcxjwA5t/Y00ZgaOO1Crz3fjT/p8kT28zA/Caz7oiUMn3d6H2yOVCVwuw==} - engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} - peerDependencies: - '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.3 - '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.3 - '@napi-rs/wasm-runtime@1.2.3': resolution: {integrity: sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==} engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} @@ -3321,64 +2800,64 @@ packages: '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 - '@ngrx/effects@21.1.1': - resolution: {integrity: sha512-JQUv2tYyjkbdsE40WYtJDsiI+3qtn4V5m0Ybr2CYw9zKpVQshDV5MLLAni+SuijlHAWJkw8x4wwnYmNyG/Rmng==} + '@ngrx/effects@22.0.1': + resolution: {integrity: sha512-SRI6Q04rJfrf/qUwphzoJU8IJsPAPzP0MmmXK5qbMUuZ24V1JbAyI5+c0bEVzIFMMOooAU7wkO6iuJg5TVjmsw==} peerDependencies: - '@angular/core': ^21.0.0 - '@ngrx/store': 21.1.1 + '@angular/core': ^22.0.0 + '@ngrx/store': 22.0.1 rxjs: ^6.5.3 || ^7.5.0 - '@ngrx/entity@21.1.1': - resolution: {integrity: sha512-yDHjjw63XjKWRXimIAloxo+Ws5AK2IPUIankRJZGw+IRCinZCO6gzhHmNamfqfS2vlcUgCS1r/7ZphRunK3WAg==} + '@ngrx/entity@22.0.1': + resolution: {integrity: sha512-oag8ziTUA2tBOZWI5quhWImCWXIaRjAHdj4rsoAe8fNiFdIa9k+fvpSk8dQXDlXZARRvN8jLRA5O26frx1qJJg==} peerDependencies: - '@angular/core': ^21.0.0 - '@ngrx/store': 21.1.1 + '@angular/core': ^22.0.0 + '@ngrx/store': 22.0.1 rxjs: ^6.5.3 || ^7.5.0 - '@ngrx/eslint-plugin@21.1.1': - resolution: {integrity: sha512-I2RzaBmEPuSDOE/TCwG8m0mMi7ETKo1j+1GRZ8JuhNDj+sEpzj5hQMnbLoVcRjcz2ctGJF6rFI9qADztzvg5wg==} + '@ngrx/eslint-plugin@22.0.1': + resolution: {integrity: sha512-jBhZaLaFJZh0UWFSBx54Hkoog05akw6ovQFARkk65QAf/2BgN8EZOnAS3zUavFPHX90m4RYAkMzj9bRZI7iL8g==} peerDependencies: '@typescript-eslint/utils': ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' typescript-eslint: ^8.0.0 - '@ngrx/router-store@21.1.1': - resolution: {integrity: sha512-niO2J0aYgMcplHRgg3Nf1DPjHpg4ehZjqnA282DtLOXlYtHWoP2ePZcvnPxhK1K77Am5hSGOLa7U2DjjgwlSdQ==} + '@ngrx/router-store@22.0.1': + resolution: {integrity: sha512-g7F8bGXhe1THdNdUC4xySObd3q2Rkfu0lkrlR4s8/nm0nDsJoqY/q9Jm/m438QXVyiRC1dV42W0v6Gg37eaFRw==} peerDependencies: - '@angular/common': ^21.0.0 - '@angular/core': ^21.0.0 - '@angular/router': ^21.0.0 - '@ngrx/store': 21.1.1 + '@angular/common': ^22.0.0 + '@angular/core': ^22.0.0 + '@angular/router': ^22.0.0 + '@ngrx/store': 22.0.1 rxjs: ^6.5.3 || ^7.5.0 - '@ngrx/signals@21.1.1': - resolution: {integrity: sha512-AXxsvO39cJ4gu2GMvLyUHwx+3qHi35Bn7OUqyIfJW0fffi/Af7ML6h0MPMWDHVROo55FBgxzJrYRswwzdew9VA==} + '@ngrx/signals@22.0.1': + resolution: {integrity: sha512-R62VsPrnH54LgA3LAqw+GWA9FHii526CH1aqah8aiK1RZocp+3N9rS3g5wrrXU655eVH65jhN4k/k+EFyxO/RA==} peerDependencies: - '@angular/core': ^21.0.0 + '@angular/core': ^22.0.0 rxjs: ^6.5.3 || ^7.4.0 peerDependenciesMeta: rxjs: optional: true - '@ngrx/store-devtools@21.1.1': - resolution: {integrity: sha512-gNRDPCZZCO2uzdamco6xJ7292cZdVumHqOIAqmJsYaHkRQ4sZIKBjwFyYXAtPvS6a+Y9bnkGkHetxkF1iZzDvA==} + '@ngrx/store-devtools@22.0.1': + resolution: {integrity: sha512-mnsz+k0+07dUvThTehbKvlV8lyVWRRy6GTQuo30EW+40PSBvUkdcqhb8u63K2zIdV43SjsZOWEjDSjMJJSCCmg==} peerDependencies: - '@angular/core': ^21.0.0 - '@ngrx/store': 21.1.1 + '@angular/core': ^22.0.0 + '@ngrx/store': 22.0.1 rxjs: ^6.5.3 || ^7.5.0 - '@ngrx/store@21.1.1': - resolution: {integrity: sha512-B0/IyBTqBExmwPAmrpcJrhlrn+FeeitvOW8ltf7O3kJb2vRKduV5Oi34GLiwYQ8eAukTbKyWQv/VV02gxLeEMg==} + '@ngrx/store@22.0.1': + resolution: {integrity: sha512-CWCqqG+qepatkH9HE4+a17x/uyU/sTDO75IQWEntJi0xGTeE++iZH/QUm4MWB7mmb43Y+PnwydI3515DzfINtg==} peerDependencies: - '@angular/core': ^21.0.0 + '@angular/core': ^22.0.0 rxjs: ^6.5.3 || ^7.5.0 - '@ngx-pwa/local-storage@21.0.0': - resolution: {integrity: sha512-MOsgo5G2HvWHOzWus868K/CLHtHmaOPvIkOharYKic6ze/+w8vz6J7KEcIcZ6zmGniEvdefNRWO+XIaah+av3w==} + '@ngx-pwa/local-storage@22.0.0': + resolution: {integrity: sha512-k5ZamLx8YTjAkvS6x+RHYBBGbcXrR0EnvI6Xgxaf29yKcwXXA61iQ8VRpFXeLY+0B/qT/Su2auXlJ8RLaIrG9g==} peerDependencies: - '@angular/common': ^21.0.0 - '@angular/core': ^21.0.0 + '@angular/common': ^22.0.0 + '@angular/core': ^22.0.0 rxjs: ^7.6.0 '@ngx-translate/core@16.0.4': @@ -3504,186 +2983,171 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@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': - resolution: {integrity: sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@npmcli/git@7.0.2': - resolution: {integrity: sha512-oeolHDjExNAJAnlYP2qzNjMX/Xi9bmu78C9dIGr4xjobrSKbuMYCph8lTzn4vnW3NjIqVmw/f8BCfouqyJXlRg==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@npmcli/installed-package-contents@4.0.0': - resolution: {integrity: sha512-yNyAdkBxB72gtZ4GrwXCM0ZUedo9nIbOMKfGjt6Cu6DXf0p8y1PViZAKDC8q8kv/fufx0WTjRBdSlyrvnP7hmA==} - engines: {node: ^20.17.0 || >=22.9.0} - hasBin: true - - '@npmcli/node-gyp@5.0.0': - resolution: {integrity: sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@npmcli/package-json@7.0.5': - resolution: {integrity: sha512-iVuTlG3ORq2iaVa1IWUxAO/jIp77tUKBhoMjuzYW2kL4MLN1bi/ofqkZ7D7OOwh8coAx1/S2ge0rMdGv8sLSOQ==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@npmcli/promise-spawn@9.0.1': - resolution: {integrity: sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@npmcli/redact@4.0.0': - resolution: {integrity: sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@npmcli/run-script@10.0.4': - resolution: {integrity: sha512-mGUWr1uMnf0le2TwfOZY4SFxZGXGfm4Jtay/nwAa2FLNAKXUoUwaGwBMNH36UHPtinWfTSJ3nqFQr0091CxVGg==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@nx/angular@22.7.8': - resolution: {integrity: sha512-7vcaw+ooJDFgTugYstmYFXm6X9bOaR6KsIDUIOIlhtb36KgKSQRDgCx0FNQqYELhf5i/Vs+7ETPru2mwBqzFSw==} + '@nx/angular@23.2.1': + resolution: {integrity: sha512-94TpS2u4rcHkPyoa6lE0J2aMaAVrOnSRyrmF0dTck9ImX5bPnBRB6Ml9QehHM6cQ5SERRvgPZd0L2yIa5DQxWA==} peerDependencies: - '@angular-devkit/build-angular': '>= 19.0.0 < 22.0.0' - '@angular-devkit/core': '>= 19.0.0 < 22.0.0' - '@angular-devkit/schematics': '>= 19.0.0 < 22.0.0' - '@angular/build': '>= 19.0.0 < 22.0.0' - '@schematics/angular': '>= 19.0.0 < 22.0.0' - ng-packagr: '>= 19.0.0 < 22.0.0' + '@angular-devkit/build-angular': '>= 20.0.0 < 23.0.0' + '@angular-devkit/core': '>= 20.0.0 < 23.0.0' + '@angular-devkit/schematics': '>= 20.0.0 < 23.0.0' + '@angular/build': '>= 20.0.0 < 23.0.0' + '@nx/cypress': 23.2.1 + '@nx/module-federation': 23.2.1 + '@nx/playwright': 23.2.1 + '@nx/rspack': 23.2.1 + '@nx/webpack': 23.2.1 + '@schematics/angular': '>= 20.0.0 < 23.0.0' + ng-packagr: '>= 20.0.0 < 23.0.0' rxjs: ^6.5.3 || ^7.5.0 + webpack-merge: ^5.8.0 peerDependenciesMeta: '@angular-devkit/build-angular': optional: true '@angular/build': optional: true + '@nx/cypress': + optional: true + '@nx/module-federation': + optional: true + '@nx/playwright': + optional: true + '@nx/rspack': + optional: true + '@nx/webpack': + optional: true ng-packagr: optional: true + webpack-merge: + optional: true - '@nx/devkit@22.7.8': - resolution: {integrity: sha512-aQe6wSJY7eIsJXE+DSHEgCMf4UBLeRbW0o1WNoF2TlLxE7N3Navb6aWiebe2wv/eaQ2IDKJXLuNxdJJJK/6zCg==} + '@nx/devkit@23.2.1': + resolution: {integrity: sha512-uMGbMJpCi+y7eHBEWqygA8JITNgzFZdzVmcKX17EXnFreu14aWZ1kyo6Qh9pKIcbJigO3F46w6ANlYp6wgCovQ==} peerDependencies: - nx: '>= 21 <= 23 || ^22.0.0-0' + nx: '>= 22 <= 24 || ^23.0.0-0' - '@nx/esbuild@22.7.8': - resolution: {integrity: sha512-Ppwm0IyFa+yhyS/2h7IOK86YzfKHk5+ywe8nwJjNIW8F6LWGzfJ13pR6NJnWZgdvXG+YssmmaDtoOIfK8FHDwA==} + '@nx/esbuild@23.2.1': + resolution: {integrity: sha512-TpN8jf4R2r/e7a+qlHLTDikuMteL8xFV2zMWHWz4/6/IrfvBIrBm8EHmpivXhrY5008rc50YR0klmMSYMTeyVg==} peerDependencies: esbuild: '>=0.19.2 <1.0.0' peerDependenciesMeta: esbuild: optional: true - '@nx/eslint-plugin@22.7.8': - resolution: {integrity: sha512-HpudlvefqMOLWX/UOaXd4Btx0GMLJjf7BEfHz/N3/77scq+X8tArq+auvg4sNkFC6AGdLNQ0exYuY7AaBRuZiQ==} + '@nx/eslint-plugin@23.2.1': + resolution: {integrity: sha512-hvGaCALUQ5t/I8Loexrk5/Ggwikh1Z38iuf+9Eh37HfiedCYtXnW+QeZH+yZkVDQ44ytJGRQ7jJKqDv/ghhTtg==} peerDependencies: - '@typescript-eslint/parser': ^6.13.2 || ^7.0.0 || ^8.0.0 + '@typescript-eslint/parser': ^8.0.0 eslint-config-prettier: ^10.0.0 peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true eslint-config-prettier: optional: true - '@nx/eslint@22.7.8': - resolution: {integrity: sha512-b+KETcZnUOCV6Mb9mhI+SsWfkA+kkFSFY4mtIlDF2xpajA9H2yzoeLcoIDfEfjivrhCmPcpd4cmUyPySW3uNig==} + '@nx/eslint@23.2.1': + resolution: {integrity: sha512-cqvudRspelZJVxASZIImeGlBXMrZ+83T1IVadFSOwShSNvJiwfpzPs+7jDduspTvgJkOgndxAasawbZ8L6Qxaw==} peerDependencies: - '@nx/jest': 22.7.8 + '@nx/jest': 23.2.1 '@zkochan/js-yaml': 0.0.7 - eslint: ^8.0.0 || ^9.0.0 || ^10.0.0 + eslint: ^9.0.0 || ^10.0.0 peerDependenciesMeta: '@nx/jest': optional: true '@zkochan/js-yaml': optional: true - '@nx/jest@22.7.8': - resolution: {integrity: sha512-dP1lly5MxTbzHi31VutprgftmMmvInro99Kop1ya0ww55MYdb5xZzzAaCN13MJykIJQbEY3fZgux4kgIgwLaiQ==} - - '@nx/js@22.7.8': - resolution: {integrity: sha512-OBXoHIm1mMiyXyd/3xtEnOfaDzDrGtACvAqhhKAUUi+VXGlHyvUdXBQkAamIzvjCyxo+7p2JtdYG2ca8G4pKDA==} + '@nx/jest@23.2.1': + resolution: {integrity: sha512-iMxP4FDNLihk665gz5bdeRNsFuSKEeVYE4wJPibp+ynAUB6dooh/75tpSnoeRl9bE22shBWreN07nW3eiLOwdw==} peerDependencies: + jest: ^29.0.0 || ^30.0.0 + ts-jest: ^29.0.0 + peerDependenciesMeta: + jest: + optional: true + ts-jest: + optional: true + + '@nx/js@23.2.1': + resolution: {integrity: sha512-52vAau7gmL4oFIohwAQ+rhnbbKeIAir6bKam0+lURvwSFHpkHPzMa0m8L/IlCO9mbRp2vx0gYONsPtL1k7jbqA==} + peerDependencies: + '@swc/cli': '>=0.6.0 <0.9.0' verdaccio: ^6.0.5 peerDependenciesMeta: + '@swc/cli': + optional: true verdaccio: optional: true - '@nx/module-federation@22.7.8': - resolution: {integrity: sha512-mAs4Acjjq3paxRL54UsbCLhbW0OdxSlD2QOWGCwVcZ6CASTHrykE166ByW8oDumQiYiptB3S6kUdGNsh+KsJXA==} - - '@nx/nx-darwin-arm64@22.7.8': - resolution: {integrity: sha512-IM1geDyWPFsS565de9dByYNZ5I3j8FQZvNUp9LIYw1dNu70sCWbAT0glw0anholOwlAb7JWEscoUeV7ouRBW0A==} + '@nx/nx-darwin-arm64@23.2.1': + resolution: {integrity: sha512-majzvZlhlWL8MSDEKbQoGv3qM1u7v8Pk7X86srWsySbGDnCJ5BpMFyqMeroQ3AY4lxRjLYWACX5YXVPy2ttRtQ==} cpu: [arm64] os: [darwin] - '@nx/nx-darwin-x64@22.7.8': - resolution: {integrity: sha512-X/AyooJCmAwHyp9f//bspkAmtaPsv2lPEKe6OiOScsyxJITP4nw+rOfIAJ4Ar64WQcMAY8WLP+ys4ah0K6DZSw==} + '@nx/nx-darwin-x64@23.2.1': + resolution: {integrity: sha512-C+KkJwGOZ3h2RjEoQk2SWBdMlKq4BdMfTtxCJ9YgCxjPKqT8V/57tn1Dzz9fnw76P8+PocsfRdjIS0cj9r7isA==} cpu: [x64] os: [darwin] - '@nx/nx-freebsd-x64@22.7.8': - resolution: {integrity: sha512-mgdqiFag8txon4XugDtNj+hq+X9Whn8LBMuqwJSez93L1fralzpQFSUip7XnE7ejQRr5Zewg3BFscGlsk8Cy3A==} + '@nx/nx-freebsd-x64@23.2.1': + resolution: {integrity: sha512-GxxMtUlZWy74b15iyHVw+c4a0V2pSHR6RKbvmaSJQ3BXJrJarIqaWys/JrvcV2I/AjtrN7Tq7cFxCEaxkwCsEA==} cpu: [x64] os: [freebsd] - '@nx/nx-linux-arm-gnueabihf@22.7.8': - resolution: {integrity: sha512-g7ojIloHGFI7wuMnUdg7io42EL7C7ae4zAolNVj7eXZM54amn3qoNjwbyqaVbBQvUNRiAKJizGyn1IhKpzvG9A==} + '@nx/nx-linux-arm-gnueabihf@23.2.1': + resolution: {integrity: sha512-Wr/VLZkPhEsDN7GGx0wOmvZ6v7yMrMf9WpLfrcTVe10NPUB/y6xYaoTDo8teKOBNLsPJ73C40bj2ZBmWtGPg7Q==} cpu: [arm] os: [linux] - '@nx/nx-linux-arm64-gnu@22.7.8': - resolution: {integrity: sha512-Kws8e7W4epfqpTWYaV7KLKaj33o+9JtJa2rErx/XFTtMXhfVzOs5hC4oIrZsYpgk1DuT0APp7UDvX06q2kdAVQ==} + '@nx/nx-linux-arm64-gnu@23.2.1': + resolution: {integrity: sha512-lGPhJYkJbIlTWj38FrHZ3qnaGkAfafPrK+6KjGdt+ubDOt9fp0lyUbEhfdg9JuIL++hOgcTHvz0FOA6S+7k3nA==} cpu: [arm64] os: [linux] libc: [glibc] - '@nx/nx-linux-arm64-musl@22.7.8': - resolution: {integrity: sha512-fpnyVFL+mSqLdKBPk8+n/rHUEXiem7Xwr9cIOd2Dd5zxQ5wcwj4qSYTNRsBPI2qDFo3esHliwaoFJZULbTHldw==} + '@nx/nx-linux-arm64-musl@23.2.1': + resolution: {integrity: sha512-HrHBmBjh++NBE1p0DBjnc53Ech86/4gy4V1bAUJRViG1/FSJAopKE8qQqanYfShOUsHXk6ohPIhf6nlC8LsFFQ==} cpu: [arm64] os: [linux] libc: [musl] - '@nx/nx-linux-x64-gnu@22.7.8': - resolution: {integrity: sha512-KRbkSthClEwkbpt/LLJmBJhIOvOsyrp9OICisF9p3mOODjaxAuYmyOgrVreg09BT2F4hXN3JXpvt9eIZpTCRsw==} + '@nx/nx-linux-x64-gnu@23.2.1': + resolution: {integrity: sha512-D1mSU/kdC1N8aeo7RpmuXWOl77GR4QIbam4dpUmezAwdNkIHlIq7hwqec+4HDWum9nlNmQX/dZn0nZaBem0rRA==} cpu: [x64] os: [linux] libc: [glibc] - '@nx/nx-linux-x64-musl@22.7.8': - resolution: {integrity: sha512-pdPMWko1yZI5ZNI6/t2Y5rQPGgV/ah5DW+mlHo2aFKav4lnxvgisEh9e4HtOsYBfK/9PyYZ5u3M9hLSaMiJ75w==} + '@nx/nx-linux-x64-musl@23.2.1': + resolution: {integrity: sha512-p8Lc4RLu7cj4iP7NCnivLv4Qw+RorrLRO6s2J0LNIMH4nI1PAu5p5IztsCb3lsRo0f/ERCQ8DGwBn2BM1J8Blg==} cpu: [x64] os: [linux] libc: [musl] - '@nx/nx-win32-arm64-msvc@22.7.8': - resolution: {integrity: sha512-yYDu3pj7AXu7LtN/T/bA4TMWWWbmvEE4wa8UW8ZU5JeWYblyXQA7HyYpPAb4fLzCtHb/dIrNDghVBRIeAAcnSw==} + '@nx/nx-win32-arm64-msvc@23.2.1': + resolution: {integrity: sha512-XWYG+6P368xOZXU1z3QH4jXlaajQDxlXVGzYXogp+i09twEAhE7LV6fmv8gQptBta67EZy610KcKu38IVn6x7Q==} cpu: [arm64] os: [win32] - '@nx/nx-win32-x64-msvc@22.7.8': - resolution: {integrity: sha512-cSr0qMt/GgM2aOBFsapxllnIv55j0gAz/6eWvqEOx5sS8d3X1G0IgazZnPbjW2c8gfSYaBZ9UmYnfapWIO/jqg==} + '@nx/nx-win32-x64-msvc@23.2.1': + resolution: {integrity: sha512-qZQBNB4L/yljM81Fm5M/LoyCMoGJc7yc//KePQ5xiyhzZjJENooHsFkB8ps/pSv4cfHKfYJbvgBAMPOLXeHqqA==} cpu: [x64] os: [win32] - '@nx/playwright@22.7.8': - resolution: {integrity: sha512-GwUtz0faMp32bZgfw80efXFG3Q0vKTrSiPB1/ivqWneX8epqvtffgqy9RJLL41+27arhQi9csk5WM8bq5n/XLQ==} + '@nx/playwright@23.2.1': + resolution: {integrity: sha512-lfUqS8gGXWvvPAlUs9DhE4y50jowh+xqdnpWbfcp2RrWRH7eXjAYmm1kHPX6d8ZNtS8wvq7PVkWlIarJxr31IA==} peerDependencies: '@playwright/test': ^1.36.0 peerDependenciesMeta: '@playwright/test': optional: true - '@nx/rspack@22.7.8': - resolution: {integrity: sha512-xthkJ6w8AYXDAtsiivPBXZPeddYRG+FHGmc+1BVVa02PCHHpaub0ReC5lPZ3ptI2nN+DiUKp0CfOsxyznXeNVQ==} + '@nx/web@23.2.1': + resolution: {integrity: sha512-XmlnwbNP4cjNHmD9G7C/aWigxujmDhIB6mTFw9qCq27c7q52baDRFVQhQsS9JZxla44Z4oUhc9Jgf0Rvn35qMA==} peerDependencies: - '@module-federation/enhanced': ^2.3.3 - '@module-federation/node': ^2.7.21 - - '@nx/web@22.7.8': - resolution: {integrity: sha512-j52F9u1s5nO/yCPcJX2a4bd93FUvf5blhrBdVs8nT4DpPzTMTi3lWE/0lNZdryTDa1Gll7KpFFt5Iy5dOJzSIQ==} - peerDependencies: - '@nx/cypress': 22.7.8 - '@nx/eslint': 22.7.8 - '@nx/jest': 22.7.8 - '@nx/playwright': 22.7.8 - '@nx/vite': 22.7.8 - '@nx/webpack': 22.7.8 + '@nx/cypress': 23.2.1 + '@nx/eslint': 23.2.1 + '@nx/jest': 23.2.1 + '@nx/playwright': 23.2.1 + '@nx/vite': 23.2.1 + '@nx/webpack': 23.2.1 peerDependenciesMeta: '@nx/cypress': optional: true @@ -3698,17 +3162,147 @@ packages: '@nx/webpack': optional: true - '@nx/webpack@22.7.8': - resolution: {integrity: sha512-EfOKtVKSRALgixo0OljKOjBI8KtOssJiH1vKaaXwXAmou+tTxBoZ4Mbc5l0mENefYWB5OakkCcfblRJwizkTNQ==} - - '@nx/workspace@22.7.8': - resolution: {integrity: sha512-VTe1hq+Bilm5LpjTiJK7eSNNRIn2/spy1vKOIVDKNox6VjygMVzZDrlobykbK7clLwL/1nAURsQWI1rC/i/zJw==} + '@nx/workspace@23.2.1': + resolution: {integrity: sha512-lC5NdIQUK7tmz4SeJE0zJ+DTDTAYvbs88gZJQZBwAqkky20ZlRnJiebrOXHlhhVn6Cn35XVLmEkhEsZVda1R0A==} '@oslojs/encoding@1.1.0': resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} - '@oxc-project/types@0.113.0': - resolution: {integrity: sha512-Tp3XmgxwNQ9pEN9vxgJBAqdRamHibi76iowQ38O2I4PMpcvNRQNVsU2n1x1nv9yh0XoTrGFzf7cZSGxmixxrhA==} + '@oxc-parser/binding-android-arm-eabi@0.142.0': + resolution: {integrity: sha512-ZiRGDutGsv1G6bL/ozy/koC0Sv39T1DqyoC4KD1DOy9ZoACm1O5UWhEK2c02Qdk+4lfLVkvFa/mQ0fm/4h1BtQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.142.0': + resolution: {integrity: sha512-WZkvGRLNQTz8lR9zP5nLjUdlroRCopBu3g9zF1p/laE6DzT1UbQo8Rdz5MWhaJUPYg/6gp+jo7HUgsyKaN1FtQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.142.0': + resolution: {integrity: sha512-l4khS8LQOOVYsGRVARo1gSaCT/aBSceUVXgtovWc2+drnxVuDr082WA3OCHVdVzIz5JIrP/y9CWsSKxBDNmYGg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.142.0': + resolution: {integrity: sha512-QBsNF3nqlXmcH2B1YOPqQYmCJoy4HuIjUxGbBO/k5JAJUl68ghU2psRY2zPk+RyBaWqKP/qfL4oaFgEMCdwskA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.142.0': + resolution: {integrity: sha512-b7Q7m4Cqc6XqNhri3R+QhU+GVy646Pn+bkdhrDdWym/Fdi0ZUa+d73H9dm5H91JtbtAQ/z1d8XKMW3oOV8a4tQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.142.0': + resolution: {integrity: sha512-3riVS5IhdH3uCZj1Y9ftDQlR0dvLsIlw/edrRqk8JhgNd5K0XSs+UBtgh50N13CAlW9/TXj6sVGXaKNBocd0Yg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.142.0': + resolution: {integrity: sha512-NmXUOpgpTSkhl795TiXmWppTwmSJ92RC1qvD6e4XOF+slgmo3e6Ah+kEu+6AN8s7NAOEwqGmir58MgSQSWmBSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.142.0': + resolution: {integrity: sha512-gc0EXsKtXgerujmU2Bql3u1L1HsSQ2774R83idq/FoNMPVV/RY/1ErFsvnit7KoiP/sLvzQixeUo4Ut0ic0wmw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-arm64-musl@0.142.0': + resolution: {integrity: sha512-F2XvmWSE0uWpie+jHKKIFgdVOe9ypGhkEZxKx5DuW215K6cbAC274yYaPkcM7EqY4Df3Weyhpcz3lsURyH2LVg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-ppc64-gnu@0.142.0': + resolution: {integrity: sha512-wLMbT21U/QxknQsk+VvNF0b9D2/aGWhcaQQQ+VYlE8FwD5+GoWZIPPXNzyHmkYyhm0KB3itL+TBavjMatqNnYA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-gnu@0.142.0': + resolution: {integrity: sha512-+G8F/4ckwT7FCJV4H2bt09xEzJbjNCfuL4Sp1AYNaFtFMVtgIGMuJlteT82U+K0UIZ/DzAR/LDlMFnEuajG7Kw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-musl@0.142.0': + resolution: {integrity: sha512-hTsHtTLxMAfCo+rpF5K3qZJKW2NpPN/CHd4mYB3y7XlSdspHkd2gehDIofP64AacA9nWQw2tY3O7wR6UY8IVOA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-s390x-gnu@0.142.0': + resolution: {integrity: sha512-6y7qYY3TCUDYjqswImdTGl92y+KA/80twALegQPN27kfY+bG7Ib1+L3jbmrCZQx6wrVnai9IPsEZp07I0hx7JQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-gnu@0.142.0': + resolution: {integrity: sha512-i69kAWU+2LgoH5bR+zWiiu+UzAw7Oxkwv7COeJTeY19pn4e70nKQcr9Pm6cL2Z0Z54d+gl9qADlK/0yyuCPiBA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-musl@0.142.0': + resolution: {integrity: sha512-4SQs678MmjYVrmhAgCWD4o0vpaFszXw9xLX5p2Z9MMFcltxiLkA88wQjh80YHjPrXtpyZ2CWI5m+1yNKM0m2Pw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-openharmony-arm64@0.142.0': + resolution: {integrity: sha512-YHpx9N7Ln3a++Tc8rv+H7mrK1zyJQOAwCFg8LZ3lTs1T5afGWeZrLPhPT9HLnIwSjCyJqPWVMIrMxbjcmBr2oQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-wasm32-wasi@0.142.0': + resolution: {integrity: sha512-3pLDyY3+oogW73RM5uehNgAiR/Xfb7fvO2Q1Z1gIqZ2+50XDVQmBVlRkHXZTU4gKnQHpwETNsYQVsJ3joVB2iA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.142.0': + resolution: {integrity: sha512-Had/VeVY28Oyb0K+Q4FV8KCzoBycIh93oDK6pCbya9lkzdq+ikMHMgBubsdqqlybjJmQRawCQRrnBRHyQwYvcQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.142.0': + resolution: {integrity: sha512-GGi3+YphVHavvgs6gum2UXoNCqzHAmPt/nXkn8ZQZstV2Q1qZD1Mn8fz/nWrDkefHQtrG/+1/XrbMxsBTo6Svw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.142.0': + resolution: {integrity: sha512-Ny/Wv4Us1LGC/ljwNTp+Hx3r/pH15EFfeDF0p+n898gt+TtRd6C9SccHcuUhDiNTb8s5tt7jdeAMDRQZ4Vq6hg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.139.0': + resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} + + '@oxc-project/types@0.140.0': + resolution: {integrity: sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ==} + + '@oxc-project/types@0.142.0': + resolution: {integrity: sha512-7W+2q5AKQVU36fkaryontrHn3YDt1RyUYXatw9i5H8ocYe2sPKSFB6eS8WNPeRKiN1qAWWZUPm7gwFzJGrccqQ==} '@oxc-project/types@0.146.0': resolution: {integrity: sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==} @@ -3903,36 +3497,9 @@ packages: resolution: {integrity: sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==} engines: {node: '>= 10.0.0'} - '@peculiar/asn1-cms@2.8.0': - resolution: {integrity: sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==} - - '@peculiar/asn1-csr@2.8.0': - resolution: {integrity: sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg==} - - '@peculiar/asn1-ecc@2.8.0': - resolution: {integrity: sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ==} - - '@peculiar/asn1-pfx@2.8.0': - resolution: {integrity: sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg==} - - '@peculiar/asn1-pkcs8@2.8.0': - resolution: {integrity: sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA==} - - '@peculiar/asn1-pkcs9@2.8.0': - resolution: {integrity: sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ==} - - '@peculiar/asn1-rsa@2.8.0': - resolution: {integrity: sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg==} - '@peculiar/asn1-schema@2.8.0': resolution: {integrity: sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==} - '@peculiar/asn1-x509-attr@2.8.0': - resolution: {integrity: sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA==} - - '@peculiar/asn1-x509@2.8.0': - resolution: {integrity: sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg==} - '@peculiar/json-schema@1.1.12': resolution: {integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==} engines: {node: '>=8.0.0'} @@ -3944,10 +3511,6 @@ packages: resolution: {integrity: sha512-ODOov0sGMJMf3jPonOkgGqPknTsu+DdQ7kD++gz8aI+aFMOMHFbWAA2taqXXVTdP+OTOQR/znGvSpmkeI0WTYQ==} engines: {node: '>=14.18.0'} - '@peculiar/x509@1.14.3': - resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==} - engines: {node: '>=20.0.0'} - '@phenomnomnominal/tsquery@6.2.0': resolution: {integrity: sha512-Vo9nkhfZxDB/sBiqIY3pjDC4mOSyure+AFlEW5hcy/tRE82MqCXjRN4InnVNMldinRt0dLYqg4HAU2XPq5e1LA==} peerDependencies: @@ -3972,8 +3535,14 @@ packages: cpu: [arm] os: [android] - '@rolldown/binding-android-arm64@1.0.0-rc.4': - resolution: {integrity: sha512-vRq9f4NzvbdZavhQbjkJBx7rRebDKYR9zHfO/Wg486+I7bSecdUapzCm5cyXoK+LHokTxgSq7A5baAXUZkIz0w==} + '@rolldown/binding-android-arm64@1.1.5': + resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-android-arm64@1.2.0': + resolution: {integrity: sha512-9yB1l95IrJuNGDFdOYe79vdApdz6WWBCObE+rQ2LUliYUlcyFwSYIb2xb5/Ifw7dAtMy2ZqNyd8QTSOc7duAKw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] @@ -3984,8 +3553,14 @@ packages: cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-rc.4': - resolution: {integrity: sha512-kFgEvkWLqt3YCgKB5re9RlIrx9bRsvyVUnaTakEpOPuLGzLpLapYxE9BufJNvPg8GjT6mB1alN4yN1NjzoeM8Q==} + '@rolldown/binding-darwin-arm64@1.1.5': + resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-arm64@1.2.0': + resolution: {integrity: sha512-pexNaW9ACLUOaBITOpU6qVu4VrsOFIjTv6bzgu0YUATo4eUJx0V605PxwZfndpPOn0ilqGqvGQ0M8UW0IE24jg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] @@ -3996,8 +3571,14 @@ packages: cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-rc.4': - resolution: {integrity: sha512-JXmaOJGsL/+rsmMfutcDjxWM2fTaVgCHGoXS7nE8Z3c9NAYjGqHvXrAhMUZvMpHS/k7Mg+X7n/MVKb7NYWKKww==} + '@rolldown/binding-darwin-x64@1.1.5': + resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.0': + resolution: {integrity: sha512-NqKYaq0355ZmNMG4QGpxtEDxsc7tGDhjhCm4PpE0cwnBW+5Il95LJyq414niEiaKLVjnVHBEjSo1wngKxJNiFw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] @@ -4008,8 +3589,14 @@ packages: cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-rc.4': - resolution: {integrity: sha512-ep3Catd6sPnHTM0P4hNEvIv5arnDvk01PfyJIJ+J3wVCG1eEaPo09tvFqdtcaTrkwQy0VWR24uz+cb4IsK53Qw==} + '@rolldown/binding-freebsd-x64@1.1.5': + resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-freebsd-x64@1.2.0': + resolution: {integrity: sha512-3vPoHzh6eBTz9IbB0/qZdSr0Qeks2echn+I4cHu2joV74VriPDdldswksEDzrl1mBB+oPRi+67+3Ib59paxIPQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] @@ -4020,8 +3607,14 @@ packages: cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.4': - resolution: {integrity: sha512-LwA5ayKIpnsgXJEwWc3h8wPiS33NMIHd9BhsV92T8VetVAbGe2qXlJwNVDGHN5cOQ22R9uYvbrQir2AB+ntT2w==} + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': + resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.0': + resolution: {integrity: sha512-E6NNefZ1bUVmKJq2tJkf45J4Zyczj7qm9rUT7NY+Xo2474Y13qWAwc2tvBt0BAVbmtXR1llkxXg0Ou1jbDf2SQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] @@ -4032,8 +3625,15 @@ packages: cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.4': - resolution: {integrity: sha512-AC1WsGdlV1MtGay/OQ4J9T7GRadVnpYRzTcygV1hKnypbYN20Yh4t6O1Sa2qRBMqv1etulUknqXjc3CTIsBu6A==} + '@rolldown/binding-linux-arm64-gnu@1.1.5': + resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-gnu@1.2.0': + resolution: {integrity: sha512-D+TgkdgM1vu+7/Fpf8+v0ARW+RXEP9Ccazgm8zQ4JFFd9Q7SrYQ2TakU5S5ihazQDgpKyAgZDOcIFsvoHmTZ8w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -4046,8 +3646,15 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.4': - resolution: {integrity: sha512-lU+6rgXXViO61B4EudxtVMXSOfiZONR29Sys5VGSetUY7X8mg9FCKIIjcPPj8xNDeYzKl+H8F/qSKOBVFJChCQ==} + '@rolldown/binding-linux-arm64-musl@1.1.5': + resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-arm64-musl@1.2.0': + resolution: {integrity: sha512-wUqdwJBbAv0APN87GecstdMUtLjjNTs0hBALpxETD73mccFxdmt/XeizXDtN5RAlBwNKmI+Tg+blect2G+8IeQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] @@ -4060,6 +3667,20 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-ppc64-gnu@1.1.5': + resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-ppc64-gnu@1.2.0': + resolution: {integrity: sha512-9DtF35qR9/NrfhM4oxLplCzVVjE+KKm8Pjemi0i/sdhAWkUasjmSo8WTTubNJClhSHCfyk2yeyoXDQEDPtDAAw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-ppc64-gnu@1.2.5': resolution: {integrity: sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4067,6 +3688,20 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.1.5': + resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.0': + resolution: {integrity: sha512-RzuHrBh8X8Hntd2N4VR02QGEciq/9JhcZoTpR/Cee6otRrlILGCf3cg2ygHuih+ZebUnWmMrDX6ITI85btO6rQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-s390x-gnu@1.2.5': resolution: {integrity: sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4074,8 +3709,15 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.4': - resolution: {integrity: sha512-DZaN1f0PGp/bSvKhtw50pPsnln4T13ycDq1FrDWRiHmWt1JeW+UtYg9touPFf8yt993p8tS2QjybpzKNTxYEwg==} + '@rolldown/binding-linux-x64-gnu@1.1.5': + resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.0': + resolution: {integrity: sha512-MK7L0018jjh1jR3mh21G2j1zAVcpscJBlPo2z19pRjv2XOYGRhaV4LyiD8HO6nCDdZln9IFgCMIV5yt4E3klGQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -4088,8 +3730,15 @@ packages: os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.0-rc.4': - resolution: {integrity: sha512-RnGxwZLN7fhMMAItnD6dZ7lvy+TI7ba+2V54UF4dhaWa/p8I/ys1E73KO6HmPmgz92ZkfD8TXS1IMV8+uhbR9g==} + '@rolldown/binding-linux-x64-musl@1.1.5': + resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-x64-musl@1.2.0': + resolution: {integrity: sha512-gyrxLQ9NfGb/9LoVnC4kb9miUghw1mghnkfYvNHSnVIXriabnfgGPUP4RLcJm87q3KgYz4FYUG8IDiWUT+CpSw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] @@ -4102,8 +3751,14 @@ packages: os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.0-rc.4': - resolution: {integrity: sha512-6lcI79+X8klGiGd8yHuTgQRjuuJYNggmEml+RsyN596P23l/zf9FVmJ7K0KVKkFAeYEdg0iMUKyIxiV5vebDNQ==} + '@rolldown/binding-openharmony-arm64@1.1.5': + resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-openharmony-arm64@1.2.0': + resolution: {integrity: sha512-/6VFMQGRmrhP77KXDC+StIxGzcNp5JOIyYtw0CQ8gPlzhpiIRucYfoM5FaFamHd5BJYIdH86yfP46l1p3WdrFA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] @@ -4114,13 +3769,24 @@ packages: 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'} + '@rolldown/binding-wasm32-wasi@1.1.5': + resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.4': - resolution: {integrity: sha512-cfiMrfuWCIgsFmcVG0IPuO6qTRHvF7NuG3wngX1RZzc6dU8FuBFb+J3MIR5WrdTNozlumfgL4cvz+R4ozBCvsQ==} + '@rolldown/binding-wasm32-wasi@1.2.0': + resolution: {integrity: sha512-rwdbUL465kisF24WEJLvP3JrEG6E5GRuIHt5wpMwHGERtHe4Wm2CIvtf5gTBgr2tGOHKh5NdKEAFS2VkOPE91g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.1.5': + resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-arm64-msvc@1.2.0': + resolution: {integrity: sha512-+5suHwRiKGmhwyUaNT8a5QbrBvLFh2DbO910TEmGRH1aSxwrCezodvGQnulv4uiWEIv1Kq4ypRsJ5+O+ry1DiA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] @@ -4131,8 +3797,14 @@ packages: cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.4': - resolution: {integrity: sha512-p6UeR9y7ht82AH57qwGuFYn69S6CZ7LLKdCKy/8T3zS9VTrJei2/CGsTUV45Da4Z9Rbhc7G4gyWQ/Ioamqn09g==} + '@rolldown/binding-win32-x64-msvc@1.1.5': + resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.0': + resolution: {integrity: sha512-WfFv6/qGufotqBSBzBYwgpCkJBk8Nj7697LL9vTz/XWc67e0r3oewu8iMRwQj3AUL45GVD7wVsPjCsAAtW66Wg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -4143,9 +3815,6 @@ packages: cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-rc.4': - resolution: {integrity: sha512-1BrrmTu0TWfOP1riA8uakjFc9bpIUGzVKETsOtzY39pPga8zELGDl8eu1Dx7/gjM5CAz14UknsUMpBO8L+YntQ==} - '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} @@ -4287,102 +3956,12 @@ packages: cpu: [x64] os: [win32] - '@rspack/binding-darwin-arm64@1.6.8': - resolution: {integrity: sha512-e8CTQtzaeGnf+BIzR7wRMUwKfIg0jd/sxMRc1Vd0bCMHBhSN9EsGoMuJJaKeRrSmy2nwMCNWHIG+TvT1CEKg+A==} - cpu: [arm64] - os: [darwin] - - '@rspack/binding-darwin-x64@1.6.8': - resolution: {integrity: sha512-ku1XpTEPt6Za11zhpFWhfwrTQogcgi9RJrOUVC4FESiPO9aKyd4hJ+JiPgLY0MZOqsptK6vEAgOip+uDVXrCpg==} - cpu: [x64] - os: [darwin] - - '@rspack/binding-linux-arm64-gnu@1.6.8': - resolution: {integrity: sha512-fvZX6xZPvBT8qipSpvkKMX5M7yd2BSpZNCZXcefw6gA3uC7LI3gu+er0LrDXY1PtPzVuHTyDx+abwWpagV3PiQ==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rspack/binding-linux-arm64-musl@1.6.8': - resolution: {integrity: sha512-++XMKcMNrt59HcFBLnRaJcn70k3X0GwkAegZBVpel8xYIAgvoXT5+L8P1ExId/yTFxqedaz8DbcxQnNmMozviw==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rspack/binding-linux-x64-gnu@1.6.8': - resolution: {integrity: sha512-tv3BWkTE1TndfX+DsE1rSTg8fBevCxujNZ3MlfZ22Wfy9x1FMXTJlWG8VIOXmaaJ1wUHzv8S7cE2YUUJ2LuiCg==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rspack/binding-linux-x64-musl@1.6.8': - resolution: {integrity: sha512-DCGgZ5/in1O3FjHWqXnDsncRy+48cMhfuUAAUyl0yDj1NpsZu9pP+xfGLvGcQTiYrVl7IH9Aojf1eShP/77WGA==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rspack/binding-wasm32-wasi@1.6.8': - resolution: {integrity: sha512-VUwdhl/lI4m6o1OGCZ9JwtMjTV/yLY5VZTQdEPKb40JMTlmZ5MBlr5xk7ByaXXYHr6I+qnqEm73iMKQvg6iknw==} - cpu: [wasm32] - - '@rspack/binding-win32-arm64-msvc@1.6.8': - resolution: {integrity: sha512-23YX7zlOZlub+nPGDBUzktb4D5D6ETUAluKjXEeHIZ9m7fSlEYBnGL66YE+3t1DHXGd0OqsdwlvrNGcyo6EXDQ==} - cpu: [arm64] - os: [win32] - - '@rspack/binding-win32-ia32-msvc@1.6.8': - resolution: {integrity: sha512-cFgRE3APxrY4AEdooVk2LtipwNNT/9mrnjdC5lVbsIsz+SxvGbZR231bxDJEqP15+RJOaD07FO1sIjINFqXMEg==} - cpu: [ia32] - os: [win32] - - '@rspack/binding-win32-x64-msvc@1.6.8': - resolution: {integrity: sha512-cIuhVsZYd3o3Neo1JSAhJYw6BDvlxaBoqvgwRkG1rs0ExFmEmgYyG7ip9pFKnKNWph/tmW3rDYypmEfjs1is7g==} - cpu: [x64] - os: [win32] - - '@rspack/binding@1.6.8': - resolution: {integrity: sha512-lUeL4mbwGo+nqRKqFDCm9vH2jv9FNMVt1X8jqayWRcOCPlj/2UVMEFgqjR7Pp2vlvnTKq//31KbDBJmDZq31RQ==} - - '@rspack/core@1.6.8': - resolution: {integrity: sha512-FolcIAH5FW4J2FET+qwjd1kNeFbCkd0VLuIHO0thyolEjaPSxw5qxG67DA7BZGm6PVcoiSgPLks1DL6eZ8c+fA==} - engines: {node: '>=18.12.0'} - peerDependencies: - '@swc/helpers': '>=0.5.1' - peerDependenciesMeta: - '@swc/helpers': - optional: true - - '@rspack/dev-server@1.2.1': - resolution: {integrity: sha512-e/ARvskYn2Qdd02qLvc0i6H9BnOmzP0xGHS2XCr7GZ3t2k5uC5ZlLkeN1iEebU0FkAW+6ot89NahFo3nupKuww==} - engines: {node: '>= 18.12.0'} - peerDependencies: - '@rspack/core': '*' - - '@rspack/lite-tapable@1.1.0': - resolution: {integrity: sha512-E2B0JhYFmVAwdDiG14+DW0Di4Ze4Jg10Pc4/lILUrd5DRCaklduz2OvJ5HYQ6G+hd+WTzqQb3QnDNfK4yvAFYw==} - - '@rspack/lite-tapable@1.1.5': - resolution: {integrity: sha512-uzB782zJbFTM3ta+e2Glikx36dca/6Y+DXyvFN+wb0Tx5ItIW+g03A0t3amP3LGzPHSkb0k81VHCm4jxLQwfag==} - - '@rspack/plugin-react-refresh@1.6.2': - resolution: {integrity: sha512-k+/VrfTNgo+KirjI6V+8CWRj6y+DH9jOUWv8JorYY4vKf/9xfnZ8xHzuB4iqCwTtoZl9YnxOaOuoyjJipc2tiQ==} - peerDependencies: - react-refresh: '>=0.10.0 <1.0.0' - webpack-hot-middleware: 2.x - peerDependenciesMeta: - webpack-hot-middleware: - optional: true - '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - '@schematics/angular@21.2.19': - resolution: {integrity: sha512-eL+UU9eizoadhDB4YEctRmmo0A5iwrSmGzeuEa6akrq8nLGVWM8zO91HTJutkPqGQjelF+UOiOShsQSZAU9SIQ==} - 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@21.2.21': - resolution: {integrity: sha512-ZiR5CcDMBI+0TkP4WeazhJmu1SdIq81VvO9CbXEHBO9KQWTtuE0EQCnzQkpIo4Dyh6jDRpA6sA3gkh79vW4aNQ==} - 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.1.8': + resolution: {integrity: sha512-V37T9uHOQVHyxxOqwcJ9xjSIW/mW9UuSfjOc7WJE4V8+3zj0abDJLHuoxDZKe0icYGapgOcvHyYjtNOjSeSivw==} + 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'} '@shikijs/core@4.4.3': resolution: {integrity: sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==} @@ -4415,30 +3994,6 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - '@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.1': - resolution: {integrity: sha512-qRsxPnCrbC/puegGxKuynfnxgLiHqWStrSjxkoB4YKqq3Z3s4cyZyj42ZdWFAEblNP65C+rBH8EuREHIXoi83g==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@sigstore/protobuf-specs@0.5.1': - resolution: {integrity: sha512-/ScWUhhoFasJsSRGTVBwId1loQjjnjAfE4djL6ZhrXRpNCmPTnUKF5Jokd58ILseOMjzET3UrMOtJPS9sYeI0g==} - engines: {node: ^18.17.0 || >=20.5.0} - - '@sigstore/sign@4.1.1': - resolution: {integrity: sha512-Hf4xglukg0XXQ2RiD5vSoLjdPe8OBUPA8XeVjUObheuDcWdYWrnH/BNmxZCzkAy68MzmNCxXLeurJvs6hcP2OQ==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@sigstore/tuf@4.0.2': - resolution: {integrity: sha512-TCAzTy0xzdP79EnxSjq9KQ3eaR7+FmudLC6eRKknVKZbV7ZNlGLClAAQb/HMNJ5n2OBNk2GT1tEmU0xuPr+SLQ==} - engines: {node: ^20.17.0 || >=22.9.0} - - '@sigstore/verify@3.1.1': - resolution: {integrity: sha512-qv7+G3J2cc6wwFj3yKvXOamzqhMwSk1ogPGmhpS8iXllcPrJaIIBA+4HbttlHVu1pqWTdmaCH/WE7UOC51kdoA==} - engines: {node: ^20.17.0 || >=22.9.0} - '@sinclair/typebox@0.34.46': resolution: {integrity: sha512-kiW7CtS/NkdvTUjkjUJo7d5JsFfbJ14YjdhDk9KoEgK6nFjKNXZPrX0jfLA8ZlET4cFLHxOZ/0vFKOP+bOxIOQ==} @@ -4456,9 +4011,6 @@ packages: '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - '@sinonjs/fake-timers@13.0.5': - resolution: {integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==} - '@sinonjs/fake-timers@15.4.0': resolution: {integrity: sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==} @@ -4598,14 +4150,6 @@ packages: '@tsconfig/node16@1.0.4': resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - '@tufjs/canonical-json@2.0.0': - resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} - engines: {node: ^16.14.0 || >=18.0.0} - - '@tufjs/models@4.1.0': - 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==} @@ -4630,15 +4174,9 @@ packages: '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} - '@types/bonjour@3.5.13': - resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} - '@types/cacheable-request@6.0.3': resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} - '@types/connect-history-api-fallback@1.5.4': - resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==} - '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -4669,21 +4207,18 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} - '@types/express-serve-static-core@4.19.9': - resolution: {integrity: sha512-QP2ESEe/ImWY0HDwNAnK9PvEffUyhLTnWkk7KXzHfyeWAnlrDe1fN77bXl6ia8KT3wPlmA7t9/VPRpnf4Ex9sg==} - '@types/express-serve-static-core@5.1.1': resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} - '@types/express@4.17.25': - resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==} - '@types/express@5.0.6': resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} '@types/fs-extra@9.0.13': resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + '@types/gensync@1.0.5': + resolution: {integrity: sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg==} + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -4696,9 +4231,6 @@ packages: '@types/http-errors@2.0.5': resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} - '@types/http-proxy@1.17.17': - resolution: {integrity: sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==} - '@types/istanbul-lib-coverage@2.0.6': resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} @@ -4714,6 +4246,9 @@ packages: '@types/jsdom@21.1.7': resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==} + '@types/jsesc@2.5.1': + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -4729,9 +4264,6 @@ packages: '@types/mdx@2.0.13': resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} - '@types/mime@1.3.5': - resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - '@types/mocha@9.0.0': resolution: {integrity: sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==} @@ -4741,9 +4273,6 @@ packages: '@types/nlcst@2.0.3': resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} - '@types/node-forge@1.3.14': - resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} - '@types/node@20.19.9': resolution: {integrity: sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==} @@ -4756,39 +4285,21 @@ packages: '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} - '@types/qs@6.15.1': - resolution: {integrity: sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==} - '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} '@types/responselike@1.0.3': resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} - '@types/retry@0.12.2': - resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} - '@types/sax@1.2.7': resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} - '@types/send@0.17.6': - resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==} - '@types/send@1.2.1': resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} - '@types/serve-index@1.9.4': - resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} - - '@types/serve-static@1.15.10': - resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==} - '@types/serve-static@2.2.0': resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} - '@types/sockjs@0.3.36': - resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} - '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -4804,9 +4315,6 @@ packages: '@types/video.js@7.3.58': resolution: {integrity: sha512-1CQjuSrgbv1/dhmcfQ83eVyYbvGyqhTvb2Opxr0QCV+iJ4J6/J+XWQ3Om59WiwCd1MN3rDUHasx5XRrpUtewYQ==} - '@types/ws@8.18.1': - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -4828,45 +4336,22 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.66.0': - resolution: {integrity: sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.69.0': resolution: {integrity: sha512-yi4obFrHMmnsesWehHbkg9zMA7Jt8cXT+mKM08G999pH1yT6nqgsHx7MYm0uY1wAj8CqiBXYRJ7WAT0QdQHQXg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.66.0': - resolution: {integrity: sha512-8TGcH25j9zqJ/IULB/ppyhRvxA8QYfFEZ7nfbg6/BN9spDgb8fPWQXlE5l8TWBL50EtUx007uZ1o9VOwrq2/9g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.69.0': resolution: {integrity: sha512-ewfspqWvSxKSOaplqAUNbaSFO0eB6w1EtQ+esfYFRm3614Ty4uNtExkcbgd6nWsXphbqKyf9ZYdbZdv2xEoWEQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.66.0': - resolution: {integrity: sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/tsconfig-utils@8.69.0': resolution: {integrity: sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.66.0': - resolution: {integrity: sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==} - 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/type-utils@8.69.0': resolution: {integrity: sha512-ZfoJAVg3JZndQEpEl9petVlxau3lRuElc4HRMuAlLCf8to04/iHz692RUSNmXKDjEuJmIL+KZ2/BsOcBc16dsA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4874,33 +4359,16 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.66.0': - resolution: {integrity: sha512-H6gcYaSDOyvL3AD/jHUtUFo2jqGgn/F6nuyuZSu0QTesxL+cP4dQoIMrODRofuJC09g64+WgZ6tE19Y1N2YIFQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.69.0': resolution: {integrity: sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.66.0': - resolution: {integrity: sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/typescript-estree@8.69.0': resolution: {integrity: sha512-AdFkgqck3Vudb/kWnxlyafU/4aBhHrbQ9locP2N4psXTy5mOBg0SHJumnLvx7r6g1gV4DKvUFwV2nJZBoqOD8w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.66.0': - resolution: {integrity: sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==} - 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/utils@8.69.0': resolution: {integrity: sha512-tUbx60BBqQa31kXF5MCsOOLL5E/WzUuxIn7YpAvq+eaUlqvk8/NXnXMBNAdLCr0icjkzem7iUA5QqWHe/hJ1aw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4908,10 +4376,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.66.0': - resolution: {integrity: sha512-dkKR8q+lKciskj1Y3vthHktl+3cMLWGyVUP23bRiPZ5O9BRT++4EqDDV+TVeIKBL1VXVEqrJlz8MYbcnvJcAlg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.69.0': resolution: {integrity: sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -5068,11 +4532,11 @@ packages: '@videojs/xhr@2.7.0': resolution: {integrity: sha512-giab+EVRanChIupZK7gXjHy90y3nncA2phIOyG3Ne5fvpiMJzvqYwiTOnEVW2S4CoYcuKJkomat7bMXA/UoUZQ==} - '@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 '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -5144,10 +4608,6 @@ packages: resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==} engines: {node: ^20.17.0 || >=22.9.0} - accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} - accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -5181,10 +4641,6 @@ packages: resolution: {integrity: sha512-XNAb/a6TCqou+TufU8/u11HCu9x1gYvOoxLwtlXgIqmkrYQADVv6ljyW2zwiPhHz9R1gItAWpuDrdJMmrOBFEA==} engines: {node: '>= 16.0.0'} - adm-zip@0.6.0: - resolution: {integrity: sha512-XleryMhbuksdKtofnWZ9Sk+4CUTbms4Mb/EU32SZwToAyZ5RgVos/ki8n+yr0LWHOGKuakbXTuuYNHLQjhddgg==} - engines: {node: '>=14.0'} - aes-decrypter@3.1.3: resolution: {integrity: sha512-VkG9g4BbhMBy+N5/XodDeV6F02chEk9IpgRTq/0bS80y4dzy79VH2Gtms02VXomf3HmyRe3yyJYkJ990ns+d6A==} @@ -5199,10 +4655,14 @@ 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@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: - ajv: 8.18.0 + ajv: 8.20.0 peerDependenciesMeta: ajv: optional: true @@ -5210,7 +4670,7 @@ packages: ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} peerDependencies: - ajv: 8.18.0 + ajv: 8.20.0 peerDependenciesMeta: ajv: optional: true @@ -5223,7 +4683,7 @@ packages: ajv-keywords@5.1.0: resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} peerDependencies: - ajv: 8.18.0 + ajv: 8.20.0 ajv@6.14.0: resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} @@ -5231,37 +4691,25 @@ 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==} - engines: {node: '>= 14.0.0'} - am-i-vibing@0.4.0: resolution: {integrity: sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==} hasBin: true - angular-eslint@21.4.0: - resolution: {integrity: sha512-LH7bWmtJvsubzwPoztnl1pWgI5X0VrfGTUITGSYcwn2J+SXuN/avzrKrxJmhUiIrNvLtfV+18GG6xZS1IGZdKg==} + angular-eslint@22.5.0: + resolution: {integrity: sha512-qDJVh/YLYiZ7k2qZCBGsmfO+qSiHcdyQp1UFdm3eHHc3YRNlW/Xvj1wp1oDKxuHSE//aqmbVFHlkcqdwJyymdg==} peerDependencies: - '@angular/cli': '>= 21.0.0 < 22.0.0' - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' typescript-eslint: ^8.0.0 - angularx-qrcode@21.0.5: - resolution: {integrity: sha512-9Kv/Mi5tKzAkBL2xhYZyPaJEoq+b24mIpUAntp16kIBiozVnpGI6DbigTPJiSDkV7EpYHdjBJNGj8igFAHEp6g==} + angularx-qrcode@22.0.1: + resolution: {integrity: sha512-a5fqGAerl2+SPUj6KeuH8POBaX+CTg17t+tBMNWQslZ1Tln2kz3/HLdGHlqdSHQOq9sMY8M/rZo5aXpM9w/4Kw==} peerDependencies: - '@angular/common': ^21.0.0 - '@angular/core': ^21.0.0 - - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} + '@angular/common': '>=22.0.0 <23.0.0' + '@angular/core': '>=22.0.0 <23.0.0' ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} @@ -5271,11 +4719,6 @@ packages: resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} engines: {node: '>=18'} - ansi-html-community@0.0.8: - resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} - engines: {'0': node >= 0.8.0} - hasBin: true - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -5334,9 +4777,6 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} - array-flatten@1.1.1: - resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - array-includes@3.1.9: resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} engines: {node: '>= 0.4'} @@ -5423,42 +4863,21 @@ packages: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} - babel-jest@30.4.1: - resolution: {integrity: sha512-fATAbM8piYxkiXQp3RBXmZHxZVNJZAVXXfyeyCN2Tida3+qJ8ea9UxhiJ2y4fLO90ZImKt6k9FlcH2+rLkJGhw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - '@babel/core': ^7.11.0 || ^8.0.0-0 - babel-jest@30.5.1: resolution: {integrity: sha512-ge1xUVZS91ml09YRMgRGgeKJ4YJpcOiuwteAxFBYLugQyp7cRw+hHej6Ho0vPjvLrjq60bb7JPHH9LAMA1/krA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-0 - babel-loader@9.2.1: - resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==} - engines: {node: '>= 14.15.0'} - peerDependencies: - '@babel/core': ^7.12.0 - webpack: '>=5' - babel-plugin-const-enum@1.2.0: resolution: {integrity: sha512-o1m/6iyyFnp9MRsK1dHF3bneqyf3AlM2q3A/YbgQr2pCat6B6XJVDv2TXqzfY2RYUi4mak6WAksSBPlyYGx9dg==} peerDependencies: '@babel/core': ^7.0.0-0 - babel-plugin-istanbul@7.0.1: - resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} - engines: {node: '>=12'} - babel-plugin-istanbul@8.0.0: resolution: {integrity: sha512-18wCskrN3DgbuBmp1gr7LBGT8xdz5xhQQqFvFhVxbkl8VBCrMKQ2YtqBWtUal1Zrc1HTuX0011+Brjw78TCFkg==} engines: {node: '>=18'} - babel-plugin-jest-hoist@30.4.0: - resolution: {integrity: sha512-9EdtWM/sSfXLOGLwSn+GS6pIXyBnL07/8gyJlwFXjWy4DxMOyItqyUT29d4lQiS380EZwYlX7/At4PgBS+m2aA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - babel-plugin-jest-hoist@30.5.0: resolution: {integrity: sha512-gtGo1B+u14jrZQv6TdSWIWkTqclboo7Qn+dFAGUOIuXLFuJKAdg3U5MIWzZnW4vfUb4dX9skmAMiby86e/SF4A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5501,12 +4920,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0 || ^8.0.0-0 - babel-preset-jest@30.4.0: - resolution: {integrity: sha512-lBY4jxsNmCnSiu7kquw8ZC9F4+XLMOKypT3RnNHPvU2Kpd4W0xaPuLr5ZkRyOsvLYAY4yaW1ZwTW4xB7NIiZzg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - '@babel/core': ^7.11.0 || ^8.0.0-beta.1 - babel-preset-jest@30.5.0: resolution: {integrity: sha512-ZGPn5ClP4lBDpuOK8W1yQIOy359HmbnZv3suucAlIe+SEE9yDsxV4S2PjSbH2Vc97U+WmzYD7vw3kr8NsQ/i6w==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -5539,20 +4952,14 @@ packages: resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} engines: {node: '>= 0.8'} - batch@0.6.1: - resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} - - beasties@0.4.1: - resolution: {integrity: sha512-2Imdcw3LznDuxAbJM26RHniOLAzE6WgrK8OuvVXCQtNBS8rsnD9zsSEa3fHl4hHpUY7BYTlrpvtPVbvu9G6neg==} + beasties@0.4.3: + resolution: {integrity: sha512-fIIeLOcbAB/K1kb1HBVJoiq1alHL4RCYBSo5e7HzrNkkgMggXR1Vqt/Z9JWnkfe/qdCo66Ux3QRwZioAIBdWRA==} engines: {node: '>=18.0.0'} better-sqlite3@13.0.3: resolution: {integrity: sha512-RbOBxmLBG8uvFUc15X9+9SFemKcQ0WBuISBVkpuiaUB2qblC8UWlHEjdWVoZ8AdhSwmoEgsiXKfopX0CQxaACQ==} engines: {node: '>=22'} - big.js@5.2.2: - resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} - binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -5563,17 +4970,10 @@ packages: bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - body-parser@1.20.6: - resolution: {integrity: sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@2.2.1: resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} engines: {node: '>=18'} - bonjour-service@1.4.4: - resolution: {integrity: sha512-jCZcVv7eoc4QesRscwEZtSROBen+6LpKAmBIsQYQrsAeVHLyMXWX/t6eIV5KiRZYNUBl8eVqImEEMQ8L5+c/Kw==} - boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} @@ -5587,10 +4987,6 @@ packages: brace-expansion@2.1.4: resolution: {integrity: sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==} - brace-expansion@5.0.8: - resolution: {integrity: sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==} - engines: {node: 20 || >=22} - brace-expansion@5.0.9: resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} engines: {node: 20 || >=22} @@ -5637,10 +5033,6 @@ packages: resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} engines: {node: '>=6.0.0'} - cacache@20.0.4: - resolution: {integrity: sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==} - engines: {node: ^20.17.0 || >=22.9.0} - cacheable-lookup@5.0.4: resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} engines: {node: '>=10.6.0'} @@ -5677,9 +5069,6 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-api@3.0.0: - resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - caniuse-lite@1.0.30001809: resolution: {integrity: sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==} @@ -5763,9 +5152,9 @@ packages: resolution: {integrity: sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==} engines: {node: '>=18.20'} - cli-truncate@5.2.0: - resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} - engines: {node: '>=20'} + cli-truncate@6.1.1: + resolution: {integrity: sha512-06p9vyLahLa4zkGcgsGxU6iEkSOiuI4fhCH6Emhe2lPAcoUv73n72DnODsnHA+5wwXGnV0n9M9/qOQJSjYhFhw==} + engines: {node: '>=22'} cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} @@ -5858,35 +5247,16 @@ packages: resolution: {integrity: sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng==} engines: {node: '>= 18'} - common-path-prefix@3.0.0: - resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} - compare-version@0.1.2: resolution: {integrity: sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==} engines: {node: '>=0.10.0'} - compressible@2.0.18: - resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} - engines: {node: '>= 0.6'} - - compression@1.8.1: - resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} - engines: {node: '>= 0.8.0'} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} confusing-browser-globals@1.0.11: resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} - connect-history-api-fallback@2.0.0: - resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} - engines: {node: '>=0.8'} - - content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} - content-disposition@1.0.1: resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} engines: {node: '>=18'} @@ -5904,9 +5274,6 @@ packages: cookie-es@1.2.2: resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} - cookie-signature@1.0.7: - resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} - cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} @@ -5928,12 +5295,6 @@ packages: peerDependencies: webpack: ^5.1.0 - copy-webpack-plugin@14.0.0: - resolution: {integrity: sha512-3JLW90aBGeaTLpM7mYQKpnVdgsUZRExY55giiZgLuX/xTQRUs1dOCwbBnWnvY6Q6rfZoXMNwzOQJCSZPppfqXA==} - engines: {node: '>= 20.9.0'} - peerDependencies: - webpack: ^5.1.0 - core-js-compat@3.50.0: resolution: {integrity: sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==} engines: {node: '>=6.4.0'} @@ -5962,15 +5323,6 @@ packages: typescript: optional: true - cosmiconfig@9.0.2: - resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true - create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} @@ -5984,49 +5336,6 @@ packages: crossws@0.3.5: resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} - css-declaration-sorter@7.4.0: - resolution: {integrity: sha512-LTuzjPoyA2vMGKKcaOqKSp7Ub2eGrNfKiZH4LpezxpNrsICGCSFvsQOI29psISxNZtaXibkC2CXzrQ5enMeGGw==} - engines: {node: ^14 || ^16 || >=18} - peerDependencies: - postcss: 8.5.26 - - css-loader@6.11.0: - resolution: {integrity: sha512-CTJ+AEQJjq5NzLga5pE39qdiSV56F8ywCIsqNIRF0r7BDgWsN25aazToqAFg7ZrtA/U016xudB3ffgweORxX7g==} - engines: {node: '>= 12.13.0'} - peerDependencies: - '@rspack/core': 0.x || 1.x - webpack: ^5.0.0 - peerDependenciesMeta: - '@rspack/core': - optional: true - webpack: - optional: true - - css-minimizer-webpack-plugin@8.0.0: - resolution: {integrity: sha512-9bEpzHs8gEq6/cbEj418jXL/YWjBUD2YTLLk905Npt2JODqnRITin0+So5Vx4Dp5vyi2Lpt9pp2QHzQ7fdxNrw==} - engines: {node: '>= 20.9.0'} - peerDependencies: - '@parcel/css': '*' - '@swc/css': '*' - clean-css: '*' - csso: '*' - esbuild: '*' - lightningcss: '*' - webpack: ^5.0.0 - peerDependenciesMeta: - '@parcel/css': - optional: true - '@swc/css': - optional: true - clean-css: - optional: true - csso: - optional: true - esbuild: - optional: true - lightningcss: - optional: true - css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} @@ -6054,24 +5363,6 @@ packages: engines: {node: '>=4'} hasBin: true - cssnano-preset-default@7.0.17: - resolution: {integrity: sha512-11qO63A+czwguQFJCaTdICvbaxn0pJzz/XghLlv+OT7WyToDxAMR0Xb3/26/l0y0hQJywwNbj/SLSQlGBHE1OA==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - cssnano-utils@5.0.3: - resolution: {integrity: sha512-ynIREMICLxkxm7e9bCR9sh75s4Q5drICi0ua1yxo5jH2XPBqSKkl4dOh4EbFqtUmnTMhRffHgYL0EKKkMjtJTg==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - cssnano@7.1.9: - resolution: {integrity: sha512-uPR75+5Dk/WJ/YSPR1/YDHdwMM9c5FsaARljfKWgeCKLKOtJ0we21xy/RcCjn53fZnD/f6yYEIZ8pu18+GnbNQ==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - csso@5.0.5: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -6102,14 +5393,6 @@ packages: dayjs@1.11.21: resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} - debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -6156,10 +5439,18 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} + default-browser-id@5.0.0: + resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} + engines: {node: '>=18'} + default-browser-id@5.0.1: resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} engines: {node: '>=18'} + default-browser@5.2.1: + resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} + engines: {node: '>=18'} + default-browser@5.5.0: resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} engines: {node: '>=18'} @@ -6179,10 +5470,6 @@ packages: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} - define-lazy-prop@2.0.0: - resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} - engines: {node: '>=8'} - define-lazy-prop@3.0.0: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} @@ -6198,10 +5485,6 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - depd@1.1.2: - resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} - engines: {node: '>= 0.6'} - depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -6213,10 +5496,6 @@ packages: destr@2.0.5: resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -6262,10 +5541,6 @@ packages: dmg-builder@26.15.7: resolution: {integrity: sha512-rfo1YyAWO0L3cZLKCqKQiLYbW6ZXebRUfK0kWp4oXxO7dDFLrf7alRkWImNuXvZVQhs6Idzy++cwOk8I+xPDhw==} - dns-packet@5.6.1: - resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} - engines: {node: '>=6'} - doctrine@2.1.0: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} @@ -6472,17 +5747,14 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - emojis-list@3.0.0: - resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} - engines: {node: '>= 4'} + empathic@2.0.1: + resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} + engines: {node: '>=14'} encodeurl@2.0.0: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} - encoding@0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} @@ -6490,14 +5762,6 @@ packages: resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} engines: {node: '>=10.13.0'} - enhanced-resolve@5.24.5: - resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==} - engines: {node: '>=10.13.0'} - - enquirer@2.3.6: - resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} - engines: {node: '>=8.6'} - entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -6539,9 +5803,6 @@ packages: error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - error-stack-parser@2.1.4: - resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} - es-abstract@1.24.1: resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} @@ -6600,8 +5861,8 @@ packages: peerDependencies: esbuild: '>=0.12 <1' - esbuild-wasm@0.27.2: - resolution: {integrity: sha512-eUTnl8eh+v8UZIZh4MrMOKDAc8Lm7+NqP3pyuTORGFY1s/o9WoiJgKnwXy+te2J3hX7iRbFSHEyig7GsPeeJyw==} + esbuild-wasm@0.28.2: + resolution: {integrity: sha512-GccVwhv3mmOUVQHCQm2Ox/rby8n/EqUwvZxE6Pjfikrq/lWw9g9WX/u9EykWnpot3Ko6j426DgQdea2xWKIAQA==} engines: {node: '>=18'} hasBin: true @@ -6610,11 +5871,6 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.28.1: - resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.28.2: resolution: {integrity: sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==} engines: {node: '>=18'} @@ -6816,10 +6072,6 @@ packages: resolution: {integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - expect@30.4.1: - resolution: {integrity: sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - expect@30.5.1: resolution: {integrity: sha512-m8YrYgvKe9+9gEnWEuKz+qCGfHqkrff7PPfyDnOFkjsfYRKqiYyOxDFMFieCGAuhtk/VNm63tvNgKZVzVy+Hvg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -6833,10 +6085,6 @@ packages: peerDependencies: express: '>= 4.11' - express@4.22.2: - resolution: {integrity: sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==} - engines: {node: '>= 0.10.0'} - express@5.2.1: resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} @@ -6866,16 +6114,15 @@ packages: fast-uri@3.1.6: resolution: {integrity: sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==} + fast-wrap-ansi@0.2.0: + resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - faye-websocket@0.11.4: - resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} - engines: {node: '>=0.8.0'} - fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -6883,7 +6130,7 @@ packages: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} peerDependencies: - picomatch: 4.0.5 + picomatch: 4.0.7 peerDependenciesMeta: picomatch: optional: true @@ -6903,18 +6150,10 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - finalhandler@1.3.2: - resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} - engines: {node: '>= 0.8'} - finalhandler@2.1.1: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} - find-cache-dir@4.0.0: - resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} - engines: {node: '>=14.16'} - find-process@2.1.1: resolution: {integrity: sha512-SrQDx3QhlmHM90iqn9rdjCQcw/T+WlpOkHFsjoRgB+zTpDfltNA1VSNYeYELwhUTJy12UFxqjWhmhOrJc+o4sA==} hasBin: true @@ -6927,10 +6166,6 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - find-up@6.3.0: - resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - fix-path@5.0.0: resolution: {integrity: sha512-erEWGGCN7RIu1bXTCfNVpVBdm0f5mwcbeja+4QXiEZzIQukP401sbpu8gd3Ny1vS34YNeswyMO0TdT2tP5OlHA==} engines: {node: '>=20'} @@ -6992,10 +6227,6 @@ packages: fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} - fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - fresh@2.0.0: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} @@ -7027,10 +6258,6 @@ packages: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} - fs-minipass@3.0.3: - resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - fs-monkey@1.1.0: resolution: {integrity: sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==} @@ -7115,12 +6342,6 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob-to-regex.js@1.2.0: - resolution: {integrity: sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} @@ -7182,9 +6403,6 @@ packages: h3@1.15.10: resolution: {integrity: sha512-YzJeWSkDZxAhvmp8dexjRK5hxziRO7I9m0N53WhvYL5NiWfkUkzssVzY9jvGu0HBoLFW6+duYmNSn6MaZBCCtg==} - handle-thing@2.0.1: - resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} - handlebars@4.7.9: resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} engines: {node: '>=0.4.7'} @@ -7271,17 +6489,14 @@ packages: resolution: {integrity: sha512-GqXJqY/xJkJmuloTrnV1ZEXG3fqte+VjkUqoRNZXcrUidiUOP4fMSIHHY4tsqZBK++kVyWmt/AAfSUuy57/eSA==} engines: {node: '>=16.9.0'} + hosted-git-info@10.1.1: + resolution: {integrity: sha512-DeOnSPAvOndYKfw075gt8yZzQ7S2hNztw34zBTfhIzLhmBTswIBg5/y+pqu/VD5cYWm5goAFTusDmUEmKZ0PEQ==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} + hosted-git-info@4.1.0: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} - hosted-git-info@9.0.3: - resolution: {integrity: sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==} - engines: {node: ^20.17.0 || >=22.9.0} - - hpack.js@2.1.6: - resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} - html-encoding-sniffer@3.0.0: resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} engines: {node: '>=12'} @@ -7305,37 +6520,14 @@ packages: http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} - http-deceiver@1.2.7: - resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==} - - http-errors@1.8.1: - resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} - engines: {node: '>= 0.6'} - http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} - http-parser-js@0.5.10: - resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} - http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} - http-proxy-middleware@2.0.10: - resolution: {integrity: sha512-RKzRWNPxUZqbuk3BC5mGVJbBnWgr+diEnjJexIOytFbBzDy88Fbh/YvBr3DsNrl1jYAfjWfpATEv0NO35FDuPQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/express': ^4.17.13 - peerDependenciesMeta: - '@types/express': - optional: true - - http-proxy-middleware@3.0.7: - resolution: {integrity: sha512-iwbQltVlx8bCrqePUM8C+hllHvdawVhQJaLrj1X7qllkvFQdXFsr16pW/mo9+JDVjN+QO2XUx9jd8SmoFkE5qw==} - engines: {node: ^14.18.0 || ^16.10.0 || >=18.0.0} - http-proxy@1.18.1: resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} engines: {node: '>=8.0.0'} @@ -7357,18 +6549,14 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + https-proxy-agent@9.1.0: + resolution: {integrity: sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==} + engines: {node: '>= 20'} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - hyperdyperid@1.2.0: - resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} - engines: {node: '>=10.18'} - - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -7381,12 +6569,6 @@ packages: resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} engines: {node: '>=0.10.0'} - icss-utils@5.1.0: - resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: 8.5.26 - identity-obj-proxy@3.0.0: resolution: {integrity: sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==} engines: {node: '>=4'} @@ -7394,10 +6576,6 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore-walk@8.0.0: - resolution: {integrity: sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==} - engines: {node: ^20.17.0 || >=22.9.0} - ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -7406,10 +6584,6 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - ignore@7.0.6: - resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} - engines: {node: '>= 4'} - ignore@7.0.8: resolution: {integrity: sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==} engines: {node: '>= 4'} @@ -7431,6 +6605,9 @@ packages: engines: {node: '>=8'} hasBin: true + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -7445,10 +6622,6 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - ini@6.0.0: - resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} - engines: {node: ^20.17.0 || >=22.9.0} - inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -7464,10 +6637,6 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - ipaddr.js@2.5.0: - resolution: {integrity: sha512-aq+t5NAc+cS6rZQQVWC2x98CPqGtKKTMDd4Gaodv0wShnItdKg/51djkGJ1hqH+Oy0ivDftCbSLCQob8zso01w==} - engines: {node: '>= 10'} - iptv-playlist-parser@https://codeload.github.com/4gray/iptv-playlist-parser/tar.gz/d547b6be8a44431c85fbcbcdefb41f28c0dadf6d: resolution: {tarball: https://codeload.github.com/4gray/iptv-playlist-parser/tar.gz/d547b6be8a44431c85fbcbcdefb41f28c0dadf6d} version: 0.15.2-iptvnator.2 @@ -7528,11 +6697,6 @@ packages: is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} - is-docker@2.2.1: - resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} - engines: {node: '>=8'} - hasBin: true - is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7598,10 +6762,6 @@ packages: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} - is-network-error@1.3.2: - resolution: {integrity: sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==} - engines: {node: '>=16'} - is-number-object@1.1.1: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} @@ -7610,10 +6770,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - is-plain-obj@3.0.0: - resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} - engines: {node: '>=10'} - is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -7622,10 +6778,6 @@ packages: resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} engines: {node: '>=0.10.0'} - is-plain-object@5.0.0: - resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} - engines: {node: '>=0.10.0'} - is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} @@ -7683,9 +6835,9 @@ packages: is-what@3.14.1: resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} - is-wsl@2.2.0: - resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} - engines: {node: '>=8'} + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} is-wsl@3.1.1: resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} @@ -7720,11 +6872,6 @@ packages: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} - isomorphic-ws@5.0.0: - resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} - peerDependencies: - ws: '*' - istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -7757,10 +6904,6 @@ packages: resolution: {integrity: sha512-0+bvMM/ENhDI29Z8q1r4HxiDIi4G5tnBmSw4esfPQoj9q8Nik7KIyuxHkTVnnniJQf05SxpGaKDQ+4h28ynGPg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-circus@30.4.2: - resolution: {integrity: sha512-rvHH7VlY6LgbJXJTQ87GW62g1FntOtbhh0zT+v04kC+pgL6aBKyYINXxWukCpj3dcIBMw5/XUbtDS9dU9JTXeQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-circus@30.5.1: resolution: {integrity: sha512-NgliezXQ6yznqR4W5Gqw++0cZSbBZlF0NknNIAE5VmSJKWOtmWJmWnBq3TSkUOVBTPrT7FGGhVdA8DLWnxo2Sw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -7775,21 +6918,6 @@ packages: node-notifier: optional: true - jest-config@30.4.2: - resolution: {integrity: sha512-rNHAShJQqQwFNoL0hbf3BphSBOWnpOUAKvidLS/AjNVLPfoj5mSf4jQMfW3cYOs6hXeZC7nF7mDHaBnbxELOzg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - peerDependencies: - '@types/node': '*' - esbuild-register: '>=3.4.0' - ts-node: '>=9.0.0' - peerDependenciesMeta: - '@types/node': - optional: true - esbuild-register: - optional: true - ts-node: - optional: true - jest-config@30.5.1: resolution: {integrity: sha512-L8PKM2X/ngG8PxfLMqglKGZjylPgw84bVPUlMY9W/o76TnLqPWrmQgsaT0HrheGdRtpGE5FbmREA0Kvb+Qx5gQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -7809,26 +6937,14 @@ packages: resolution: {integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-diff@30.4.1: - resolution: {integrity: sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-diff@30.5.1: resolution: {integrity: sha512-e3cNNMpv8Kh20MjjphTXs+3Vz7DQyLM1nft7KJhnh46atFhjVJRa+0Hq0beywuwsACtMQUBihQkFl8zxb7gt1Q==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-docblock@30.4.0: - resolution: {integrity: sha512-ZPMabUZCx5MpbZ2eBYSvZ0J8fvo3dR9oM+eeUpb3aKNQFuS2tu3Duw1TNlMoP8k3WQgKGJuhcMFvwcVuq6T7oA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-docblock@30.5.0: resolution: {integrity: sha512-NwDqcxtoZi33RhuW+zJS/RVA3rmheQ8BnwpYZuc/Eruaz6seQb7+aoCeDZu/3X7W2XmD8DbSo9Pn72DbKsBFYw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-each@30.4.1: - resolution: {integrity: sha512-/8MJbH6fuj48TstjrMf+u/pd06Qezz5xOXvZA6442heNOWr8bdeoGZX2d9fCn028CoMgYmroH9//zky5GfyYmA==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-each@30.5.1: resolution: {integrity: sha512-S1af0TU4v1EZ/AUlkFs/sxf/5KGsbAT9kRgdyoMX/x72y7C8ZEgETE5o1TPnbKRnrbprc5FR/moYLfdRmqEjsQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -7842,26 +6958,14 @@ packages: canvas: optional: true - jest-environment-node@30.4.1: - resolution: {integrity: sha512-4FZYVOk85hz2AyT6BbarKy9u37g6DbrDyCdFhsnDdXqyrueYQvB+0zO4f/kqLCRD0BsPRXPMNJeQwihKZV8naw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-environment-node@30.5.1: resolution: {integrity: sha512-LrPj3sPMjsQoOB3jrb8p/sa+XkSFNKo60TTsyc+EB2kxQJHgbwElpXnx1yX25fdFn5958FIObRtcLSHyV8VIAw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-haste-map@30.4.1: - resolution: {integrity: sha512-rFrcONd8jeFsyw+Z9CrScJgglRf2+NFmNam8dKu7n+SoHqNYT47mn0DdEcVUZJpvh7Iz6/si7f7yUH7GJHVgnw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-haste-map@30.5.1: resolution: {integrity: sha512-VIFgt67jW480YDxKfEv9IYQKrFpYt7bOCMn3VsnjK7AK9qo7p6acpnA1DHwsVOULE3dTaYew/6JaTD/d0VDHoQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-leak-detector@30.4.1: - resolution: {integrity: sha512-IpmyiioeHxiWDhesHnUFmOxcTzwCwKpgACgWajtAP+nYQXiY7DakTxB6Bx9JFiRMljr0AX1PvnQdaU1KFoz6NQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-leak-detector@30.5.1: resolution: {integrity: sha512-gt4GT2aWgEoCNTcBe4rqS74xTIUJxi+UD9SNSu9aOk5LmTEd6fS5KSTmAKAfitCCktQHNN+upUuL6EkBfFbMDQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -7870,10 +6974,6 @@ packages: resolution: {integrity: sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-matcher-utils@30.4.1: - resolution: {integrity: sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-matcher-utils@30.5.1: resolution: {integrity: sha512-aroZVqwOz/wC2y6pC+obgFWKV9viaQWQTTSB6W5H55+egtUczIfXR/rxExTv92xD/ADYwpqaYfVWx1aqwKg7FA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -7882,10 +6982,6 @@ packages: resolution: {integrity: sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-message-util@30.4.1: - resolution: {integrity: sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-message-util@30.5.1: resolution: {integrity: sha512-UdQlLdd9wL/Ys7xRErckqwD6wPlSZYueosSWuHc1r2ztGLwlgPvtSJq2+BPEgaEY13WLvfFbmhTj8pba0Sd1jg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -7894,43 +6990,25 @@ packages: resolution: {integrity: sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-mock@30.4.1: - resolution: {integrity: sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-mock@30.5.1: resolution: {integrity: sha512-9fVjc3leUpGID2/by/LU4Dvdcp7PFh9LlxS3QRWK3ABm+KtvEVsG/AEGeLY3gKOZsjkBxyfwGltoAVlW7dygHg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-pnp-resolver@1.2.3: - resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} - engines: {node: '>=6'} + jest-preset-angular@17.0.0: + resolution: {integrity: sha512-2yAHkA1c5rSICGJVtLYYqPC5RDsvo4+i4CwWFHVXwv41cHNX7gCWeh074IuZ5mFw7Vwsr+i25EowCnGtKkxNWw==} + engines: {node: ^20.11.1 || >=22.0.0} peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true - - jest-preset-angular@15.0.3: - resolution: {integrity: sha512-W1OpeQ/tq72ZSGXRvUDktL02QBAWIM5SZPK4KI0zoXNvRGiGhNMbxuaFF7anLi27cojmWHm+TewJQI1Dsm9s6A==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} - peerDependencies: - '@angular/compiler-cli': '>=18.0.0 <21.0.0' - '@angular/core': '>=18.0.0 <21.0.0' - '@angular/platform-browser': '>=18.0.0 <21.0.0' - '@angular/platform-browser-dynamic': '>=18.0.0 <21.0.0' + '@angular/compiler-cli': '>=20.0.0 <23.0.0' + '@angular/core': '>=20.0.0 <23.0.0' + '@angular/platform-browser': '>=20.0.0 <23.0.0' jest: ^30.0.0 jsdom: '>=26.0.0' - typescript: '>=5.5' + typescript: '>=5.8' jest-regex-util@30.0.1: resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-regex-util@30.4.0: - resolution: {integrity: sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-regex-util@30.5.0: resolution: {integrity: sha512-Mg0WK7A6xRHLSA1udJ8y9f3lM0uUhFTBnLKzwPmqB9AylvpleJ6BLemR8K9dK27DY+cesDryoA7yLZCAHsPG1A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -7939,34 +7017,18 @@ packages: resolution: {integrity: sha512-JKpXGONDcTaunrNVn8KCl6qAnwl06jIkvv70lhq0Ze47lsPx9sH5HoeEUiXX6iFGnliHN/qpIbQ0l38wrVmXaw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-resolve@30.4.1: - resolution: {integrity: sha512-Zry8Yq/yJcNAZ7dJ5F2heic8AheXvbFZ7XI5V+h28nrYZ7Qoyy4dItq8OodjnYD270mvX+ZudmrNV9cysqhW5Q==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-resolve@30.5.1: resolution: {integrity: sha512-wprhLejRtwN6h8ZgaqC0eYjGJ1uMdGPT/+b3eFncbG4NWuuP9QL+vWwRinu9waw7hkOLcpMJGVJAMgBwsPssMQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-runner@30.4.2: - resolution: {integrity: sha512-2dw0PslVYXxffXGpLo+Ejad+KcI1Qkjn7f4X4619gf21oCUmL+SPfjqIa/losUem3yEOvfNZe/F1HWUcNpODcg==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-runner@30.5.1: resolution: {integrity: sha512-FPlQE4+mwnFxXmpPrSi836KV2ZzvK1g6/nPCT8o5BcoDUUNJQeHo1/Qdkoe/QeoL4M7OeJpbnGUhYKhC1VMdaQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-runtime@30.4.2: - resolution: {integrity: sha512-3/5e8iPz2k/VLqlr8DgTftYyLUv8Su3FkCAO2/Od81UsUTpSxOrS6O5x5KkoQwyUjmpYyDJKeyAvg2T2nvpNkQ==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-runtime@30.5.1: resolution: {integrity: sha512-UB88+NRkK2Tw/OqV7dofYcyiUGrVZtD41k/N0xQOs9fG//57XHK7JLWG52HD1UYpUAhjK4xm6DBvFX3yWudsMA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-snapshot@30.4.1: - resolution: {integrity: sha512-tEOkkfOMppUyeiHwjZswOQ3lcnoTnws/q5FnGIaeIh/jmoU0ZlgMYRR8sTlTj+nNGCoJ0RDq6SfxGxCsyMTPmw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-snapshot@30.5.1: resolution: {integrity: sha512-cNWFdSb5xuDGl8hKkAZJ3YtI/PzHpAPFV+HUXWIOG8rMhpDTLVbvAc2d2wRicoYw2wGsJGLaurJT6BLC97bLXQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -7975,26 +7037,14 @@ packages: resolution: {integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-util@30.4.1: - resolution: {integrity: sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-util@30.5.1: resolution: {integrity: sha512-yKuxmNy2rSbTXw+3SIPanJo+nV4/BS1p26v44IYBFMsswSQySfMMcPHErnOncda7i9HEz0q605rIhSTBVgrZTg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-validate@30.4.1: - resolution: {integrity: sha512-PDWi4SOwLnwqNDfHZjOcsEFyZ4fc/2W2gVL3DEoyqnB6jCQMLRtfBong8s6omIw3lI0HWOus12xfnFmQtjW3fw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-validate@30.5.1: resolution: {integrity: sha512-i/buJ56wTpxihE93hQYNMfdOThS87+HGvLZzZJmU4xggPcdTYQq051iwALLCHp3q+SkCGH+EFejQZz5EbBSkkg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-watcher@30.4.1: - resolution: {integrity: sha512-/l9UonmvCwjHH7d2h3iAwIloLc1H0S8mJZ/LNK3i86hqwPAz8otUJjP9MfYtz9Tt77Su5FD2xGjZn8d31IZHlw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-watcher@30.5.1: resolution: {integrity: sha512-+FHJ7C+S7b3ySfhA1aFmoa6TztsnwZVv84ycPRn0tVN93np2EU4b8C/KadqA3l6igdjgLBTnUotab9ER0HLG8A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -8003,10 +7053,6 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} - jest-worker@30.4.1: - resolution: {integrity: sha512-SHynN/q/QD++iNyvMdy+WMmbCGk8jIsNcRxycXbWubSOhvo6T+j2afcfUSl+3hYsiBebOTo0cT7c2H7CXugu1g==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-worker@30.5.1: resolution: {integrity: sha512-Cbxh5v7AoLuFRmFJSM4/aHdQ68rjXvUWr716EE0Dh3I7T+T/3FgFKhOERGXHcU2Meftq9+9zxPM3TSNyI9D+HA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -8025,10 +7071,6 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - jiti@2.4.2: - resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} - hasBin: true - jiti@2.7.0: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true @@ -8036,6 +7078,9 @@ packages: jose@6.2.9: resolution: {integrity: sha512-XrchZOFZUl/T3vTwRe8XK+cJrGtMF4th1ARnDfwbBXFKThGhlsxEE4Zu03AD/bjJSt/9jT/mxrOCkJWOg77aPA==} + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -8071,10 +7116,6 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-parse-even-better-errors@5.0.0: - resolution: {integrity: sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==} - engines: {node: ^20.17.0 || >=22.9.0} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -8103,9 +7144,6 @@ packages: resolution: {integrity: sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - jsonc-parser@3.2.0: - resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} - jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} @@ -8115,10 +7153,6 @@ packages: jsonfile@6.2.1: resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} - jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} - keycode@2.2.1: resolution: {integrity: sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==} @@ -8129,25 +7163,9 @@ packages: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} - launch-editor@2.14.1: - resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==} - lazy-val@1.0.5: resolution: {integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==} - less-loader@12.3.3: - resolution: {integrity: sha512-F0+ErFFDj3Pt+nVrCN6VlEGEzocv9s7x/aR9v2riI+WM83UAfTYBDBjGPJnT55lBLR6JSI4fmWblt+aA2JN6/w==} - engines: {node: '>= 18.12.0'} - peerDependencies: - '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 - less: ^3.5.0 || ^4.0.0 - webpack: ^5.0.0 - peerDependenciesMeta: - '@rspack/core': - optional: true - webpack: - optional: true - less@4.5.1: resolution: {integrity: sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==} engines: {node: '>=14'} @@ -8254,22 +7272,18 @@ packages: resolution: {integrity: sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - listr2@9.0.5: - resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} - engines: {node: '>=20.0.0'} + listr2@11.0.0: + resolution: {integrity: sha512-8K88S0aSrcSXdJfiZtEy5BQMnR+TyjrCGLcgAvQs6ta0NEnIm0RJ72/Pv67Jvg07cfBhDbuN74V81lSSVYEFEw==} + engines: {node: '>=22.13.0'} - lmdb@3.5.1: - resolution: {integrity: sha512-NYHA0MRPjvNX+vSw8Xxg6FLKxzAG+e7Pt8RqAQA/EehzHVXq9SxDqJIN3JL1hK0dweb884y8kIh6rkWvPyg9Wg==} + lmdb@3.5.6: + resolution: {integrity: sha512-j3uE8ReKNyUWDjhfEFSJqE/1DLtfTR5Z8yFzVHvBjAk37wNg7HdScjcv8ttPHRvrdgPQMPWxFFI0SsdBzI5lBw==} hasBin: true loader-runner@4.3.1: resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} engines: {node: '>=6.11.5'} - loader-utils@2.0.4: - resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} - engines: {node: '>=8.9.0'} - locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -8278,10 +7292,6 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - locate-path@7.2.0: - resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} @@ -8301,9 +7311,6 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - lodash.uniq@4.5.0: - resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} @@ -8315,9 +7322,9 @@ packages: resolution: {integrity: sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==} engines: {node: '>=18'} - log-update@6.1.0: - resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} - engines: {node: '>=18'} + log-update@8.0.0: + resolution: {integrity: sha512-lddSgOt3bPASrylL54ZSpy8nBHns+vBVSoILlVOx+dei300pnLRN958rj/EdlVLKuWlSESU3qdnDZdAI7FXYGg==} + engines: {node: '>=22'} loglevel@1.9.2: resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} @@ -8353,6 +7360,9 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magic-string@1.0.0: + resolution: {integrity: sha512-CGvjzMN08iv6w1mm4/x3Gh1hLb4VnyRUA15FFpl6CsCIGGoe36k7kY5KNz9QDbSBN5I/fWHM6ZlIkUTa5xdUEA==} + magic-string@1.2.2: resolution: {integrity: sha512-veT/+7iXrXzT39XnEN4lOxtNl72dMgJ8Lp+5Bd6YcMSWpb0n0MjBM8Uuooi6jgJr8dhUW2swQgBmoZVMni5SVg==} @@ -8370,13 +7380,6 @@ packages: make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - make-fetch-happen@15.0.6: - resolution: {integrity: sha512-Je0fLJ0F5atA7F+eIlLzk+Wkcl57JDf4kf+EW8xiP5E31xOQxkIxTbgf1Oi1Lw9tRI9UEMRdI5Vz2xTzoNU1Jw==} - engines: {node: ^20.17.0 || >=22.9.0} - - makeerror@1.0.12: - resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - markdown-extensions@2.0.0: resolution: {integrity: sha512-o5vL7aDWatOTX8LzaS1WMoaoxIiLRQJuIKKe2wAw6IeULDHaqbiqiggmx+pKvZDb1Sj+pE46Sn1T7lCqfFtg1Q==} engines: {node: '>=16'} @@ -8457,10 +7460,6 @@ packages: mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} - media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - media-typer@1.1.0: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} @@ -8469,12 +7468,6 @@ packages: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} - memfs@4.68.0: - resolution: {integrity: sha512-qlU8XrIfXUuZcmXk/GODL47KXXnhSbbaWtW5z+L7cLFfIQbgAOWzL5lqUYn39kfJh+BHLaD/siaM94/UTweGow==} - - merge-descriptors@1.0.3: - resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} - merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -8486,10 +7479,6 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -8644,15 +7633,6 @@ packages: min-document@2.19.2: resolution: {integrity: sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==} - mini-css-extract-plugin@2.4.7: - resolution: {integrity: sha512-euWmddf0sk9Nv1O0gfeeUAvAkoSlWncNLF77C0TP2+WoPvy8mAHKOzMajcCz2dzvyt3CNgxb1obIEVFIRxaipg==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^5.0.0 - - minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - minimatch@10.2.5: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} @@ -8675,73 +7655,6 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minimizer-webpack-plugin@5.6.1: - resolution: {integrity: sha512-DoeAZz8Q1C1znwsUzej1fdoi4jCf7/+Em27ouLqfK/+3m8G+D7yDhUwrc3CNhjSzGUN1kn7Iv4sWmjflQHenpw==} - engines: {node: '>= 10.13.0'} - peerDependencies: - '@minify-html/node': '*' - '@swc/core': '*' - '@swc/css': '*' - '@swc/html': '*' - clean-css: '*' - cssnano: '*' - csso: '*' - esbuild: '*' - html-minifier-terser: '*' - lightningcss: '*' - postcss: '*' - uglify-js: '*' - webpack: ^5.1.0 - peerDependenciesMeta: - '@minify-html/node': - optional: true - '@swc/core': - optional: true - '@swc/css': - optional: true - '@swc/html': - optional: true - clean-css: - optional: true - cssnano: - optional: true - csso: - optional: true - esbuild: - optional: true - html-minifier-terser: - optional: true - lightningcss: - optional: true - postcss: - optional: true - uglify-js: - optional: true - - minipass-collect@2.0.1: - resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} - engines: {node: '>=16 || 14 >=14.17'} - - minipass-fetch@5.0.2: - resolution: {integrity: sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==} - engines: {node: ^20.17.0 || >=22.9.0} - - minipass-flush@1.0.7: - resolution: {integrity: sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==} - engines: {node: '>= 8'} - - minipass-pipeline@1.2.4: - resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} - engines: {node: '>=8'} - - minipass-sized@2.0.0: - resolution: {integrity: sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==} - engines: {node: '>=8'} - - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - minipass@7.1.3: resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} @@ -8770,9 +7683,6 @@ packages: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} - ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -8783,13 +7693,9 @@ packages: msgpackr@1.12.1: resolution: {integrity: sha512-4EUH9tQHnMmEgzW/MdAP0KIfa1T9AF+htl0ffe2n5vb2EKn9y2co8ccpgWko6S52Jy1PQZKwRnx5/KkYjtd9MQ==} - multicast-dns@7.2.5: - resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} - hasBin: true - - 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} mux.js@6.0.1: resolution: {integrity: sha512-22CHb59rH8pWGcPGW5Og7JngJ9s+z4XuSlYvnxhLuc58cA1WqGDQPzuG8I+sPm1/p0CdgpzVTaKW408k5DNn8w==} @@ -8822,14 +7728,6 @@ packages: engines: {node: '>= 4.4.x'} hasBin: true - negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - - negotiator@0.6.4: - resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} - engines: {node: '>= 0.6'} - negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} @@ -8841,8 +7739,8 @@ packages: resolution: {integrity: sha512-WmmLty1YWwJl9yZi77v2dVIV6X2kuYV8YYBI/G3LWGKdGHmHUvL1z7FW0iDvEvGAwNEoc5x1tOOOyDnf5jJw/w==} engines: {node: '>= 10'} - ng-mocks@14.17.3: - resolution: {integrity: sha512-IQ6j31VlX9m2vxq08eyJ2A+EXdFvE5QkjgURHXwq0kqW/jpk0QBuaNpiKz4zSXA+rAcl4DtuYQGTGYf5CTC6ug==} + ng-mocks@14.17.6: + resolution: {integrity: sha512-8SK+6fwLN5HxNLfNkVc3UCP998NtfzeQWGPFyh5PL5pJA7Ohf7OKEPzLM5S4qRZKHeFXK0Omc9S3+daKBb+VxQ==} peerDependencies: '@angular/common': 5.0.0-alpha - 5 || 6.0.0-alpha - 6 || 7.0.0-alpha - 7 || 8.0.0-alpha - 8 || 9.0.0-alpha - 9 || 10.0.0-alpha - 10 || 11.0.0-alpha - 11 || 12.0.0-alpha - 12 || 13.0.0-alpha - 13 || 14.0.0-alpha - 14 || 15.0.0-alpha - 15 || 16.0.0-alpha - 16 || 17.0.0-alpha - 17 || 18.0.0-alpha - 18 || 19.0.0-alpha - 19 || 20.0.0-alpha - 20 || 21.0.0-alpha - 21 || 22.0.0-alpha - 22 '@angular/core': 5.0.0-alpha - 5 || 6.0.0-alpha - 6 || 7.0.0-alpha - 7 || 8.0.0-alpha - 8 || 9.0.0-alpha - 9 || 10.0.0-alpha - 10 || 11.0.0-alpha - 11 || 12.0.0-alpha - 12 || 13.0.0-alpha - 13 || 14.0.0-alpha - 14 || 15.0.0-alpha - 15 || 16.0.0-alpha - 16 || 17.0.0-alpha - 17 || 18.0.0-alpha - 18 || 19.0.0-alpha - 19 || 20.0.0-alpha - 20 || 21.0.0-alpha - 21 || 22.0.0-alpha - 22 @@ -8887,19 +7785,6 @@ packages: node-fetch-native@1.6.7: resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - - node-forge@1.4.0: - resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} - engines: {node: '>= 6.13.0'} - node-gyp-build-optional-packages@5.2.2: resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} hasBin: true @@ -8932,33 +7817,9 @@ packages: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} - npm-bundled@5.0.0: - resolution: {integrity: sha512-JLSpbzh6UUXIEoqPsYBvVNVmyrjVZ1fzEFbqxKkTJQkWBO3xFzFT+KDnSKQWwOQNbuWRwt5LSD6HOTLGIWzfrw==} - engines: {node: ^20.17.0 || >=22.9.0} - - npm-install-checks@8.0.0: - resolution: {integrity: sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==} - engines: {node: ^20.17.0 || >=22.9.0} - - npm-normalize-package-bin@5.0.0: - resolution: {integrity: sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==} - engines: {node: ^20.17.0 || >=22.9.0} - - npm-package-arg@13.0.2: - resolution: {integrity: sha512-IciCE3SY3uE84Ld8WZU23gAPPV9rIYod4F+rc+vJ7h7cwAJt9Vk6TVsK60ry7Uj3SRS3bqRRIGuTp9YVlk6WNA==} - engines: {node: ^20.17.0 || >=22.9.0} - - npm-packlist@10.0.4: - resolution: {integrity: sha512-uMW73iajD8hiH4ZBxEV3HC+eTnppIqwakjOYuvgddnalIw2lJguKviK1pcUJDlIWm1wSJkchpDZDSVVsZEYRng==} - engines: {node: ^20.17.0 || >=22.9.0} - - npm-pick-manifest@11.0.3: - resolution: {integrity: sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==} - engines: {node: ^20.17.0 || >=22.9.0} - - npm-registry-fetch@19.1.1: - resolution: {integrity: sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==} - engines: {node: ^20.17.0 || >=22.9.0} + npm-package-arg@14.0.0: + resolution: {integrity: sha512-69XQh3k+dtGa1p+7RaR57IuG3rCko96xr/nUfN4yDYBXbTYICiWcOpsFKLN2GtGE9cyIljE+f1exnaYt9MvM+Q==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} @@ -8977,8 +7838,8 @@ packages: '@nx/workspace': ^22.0.0 electron: '*' - nx@22.7.8: - resolution: {integrity: sha512-ceEhmaGCvY7oi7L7G/Nm/UZSnbfwaJxU1XbDLro7CTmVcnrmxAnxExCp0J/Tpf1xQaMZtwxgV7QATdKA/7vLQw==} + nx@23.2.1: + resolution: {integrity: sha512-QOgM7EcJ6gvLKZ3Azno7wdoXXhRhZHbRDN79gCmGXsBxH9WBXCeSy3DyGdee/d5Fa6GnxVOgwKTCx8l0eA+vHw==} hasBin: true peerDependencies: '@swc-node/register': ^1.11.1 @@ -9021,9 +7882,6 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} - obuf@1.1.2: - resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - obug@2.1.4: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} engines: {node: '>=12.20.0'} @@ -9038,10 +7896,6 @@ packages: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} - on-headers@1.1.0: - resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} - engines: {node: '>= 0.8'} - once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -9059,14 +7913,10 @@ packages: oniguruma-to-es@4.3.6: resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} - open@10.2.0: - resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + open@10.1.0: + resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} engines: {node: '>=18'} - open@8.4.2: - resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} - engines: {node: '>=12'} - opener@1.5.2: resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} hasBin: true @@ -9078,12 +7928,12 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - ora@5.3.0: - resolution: {integrity: sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g==} + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} - ora@9.3.0: - resolution: {integrity: sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==} + ora@9.4.1: + resolution: {integrity: sha512-6VlU9MLXbjVQD04AZCMX28hVtA5bUoadvUqO76MUCVA0ilwJbMiHsITRPfyVm6p/BC0Av/BXMujx39WCe1LEqw==} engines: {node: '>=20'} ordered-binary@1.6.1: @@ -9093,6 +7943,10 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + oxc-parser@0.142.0: + resolution: {integrity: sha512-kKR+jPiRJYJDexVoziIg/FVGvr1fT1FZSSJOk6tVoMKKSlsf1Cso+cgGCJkOEDWOP174vRntCPFKg+AS7InWvw==} + engines: {node: ^20.19.0 || >=22.12.0} + oxc-resolver@11.17.1: resolution: {integrity: sha512-pyRXK9kH81zKlirHufkFhOFBZRks8iAMLwPH8gU7lvKFiuzUH9L8MxDEllazwOb8fjXMcWjY1PMDfMJ2/yh5cw==} @@ -9108,10 +7962,6 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-limit@7.3.1: resolution: {integrity: sha512-0trZaiG7Y7kN/Egy9a8j47t9osC0Tch4PaIWd9yGF6bvmlk7muExRvGNYb8sXBwEKMoNKsbNN9P8EefuQekE4Q==} engines: {node: '>=20'} @@ -9124,22 +7974,10 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-locate@6.0.0: - resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - p-map@7.0.6: - resolution: {integrity: sha512-I4Prw6ivkd6p8PiYR1tXASOAOBzIJwu0TB7fqaX0c/8c3QAehNYmX57EijyGGGBt3c/BIowGwV03RVBtXvHEVg==} - engines: {node: '>=18'} - p-queue@9.3.3: resolution: {integrity: sha512-NXAOdnEe5FsZJfT4oK84lE1Y5cFFdWlRuOo5tww8DyNMxyRXwn39fIkUtNLKppcPC+UYU/bXujNCUGDv01y7CA==} engines: {node: '>=20'} - p-retry@6.2.1: - resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} - engines: {node: '>=16.17'} - p-timeout@7.0.1: resolution: {integrity: sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==} engines: {node: '>=20'} @@ -9154,11 +7992,6 @@ packages: package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} - pacote@21.5.1: - resolution: {integrity: sha512-KvcJ9iy3crysCsgqc4+PknH/w6jkrp8JN36mpZBPwNaDRwTfMZD37YzRazNstiZUOhuF5pno9f78n9mEJBavwg==} - engines: {node: ^20.17.0 || >=22.9.0} - hasBin: true - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -9177,21 +8010,15 @@ packages: resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} engines: {node: '>= 0.10'} - 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==} - parse5@4.0.0: - resolution: {integrity: sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==} - parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - parse5@8.0.0: - resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} - parse5@8.0.1: resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} @@ -9203,10 +8030,6 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-exists@5.0.0: - resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -9226,9 +8049,6 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} - path-to-regexp@0.1.13: - resolution: {integrity: sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==} - path-to-regexp@8.3.0: resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} @@ -9294,10 +8114,6 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - pkg-dir@7.0.0: - resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} - engines: {node: '>=14.16'} - pkijs@3.4.0: resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==} engines: {node: '>=16.0.0'} @@ -9328,54 +8144,6 @@ packages: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} - postcss-calc@10.1.1: - resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==} - engines: {node: ^18.12 || ^20.9 || >=22.0} - peerDependencies: - postcss: 8.5.26 - - postcss-colormin@7.0.10: - resolution: {integrity: sha512-yFr6JezOolHLta/buLE71VKPh2mXursp4saVe98/ol8ZnEWhL+racShqPKlvd/DKWLre/39B6HhcMXf7RZ3hxg==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-convert-values@7.0.12: - resolution: {integrity: sha512-xurKu5qqk4viR3Cp3p4xBR4KfnZm4w4ys6+UBwBmeuBSNkH7+DtLnYOYnOffgtE4yx8sH9S1VZ6RAAvROXzP2Q==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-discard-comments@7.0.8: - resolution: {integrity: sha512-CvvS5S9WrXblFXCEJ9nVo+4z+eA7zSC7Z88V1HEJuwlQhlFnYTIjg1xJY+BCUiG2bvICap2tXii4mP22BD108Q==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-discard-duplicates@7.0.4: - resolution: {integrity: sha512-VBNn1+EuMZkeGVVtz0gRfbNGtx9IFgAsAV+E2pHtXPrp4qfGBkhTIiAuE/wrb+Y6Pakg9NewAlfTpYIFAWODtw==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-discard-empty@7.0.3: - resolution: {integrity: sha512-M2pyjQCU+/7cMHVtL6bKTHjv0lZnPLMpicgr67Dlth7AbuV9gjVTtUqaRwn6Pp6BwSDspUzhz8SaUrRykJU5Dw==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-discard-overridden@7.0.3: - resolution: {integrity: sha512-aNovXo9UsZuRNLzHJtp13lHIvinDPfiXBPePpXkSjCbgp++iU2FqE+YxvjIsg6EdyPZsASFbfu+JcBFVsErXIQ==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-import@14.1.0: - resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==} - engines: {node: '>=10.0.0'} - peerDependencies: - postcss: 8.5.26 - postcss-import@15.1.0: resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} @@ -9406,160 +8174,15 @@ packages: yaml: optional: true - postcss-loader@8.2.1: - resolution: {integrity: sha512-k98jtRzthjj3f76MYTs9JTpRqV1RaaMhEU0Lpw9OTmQZQdppg4B30VZ74BojuBHt3F4KyubHJoXCMUeM8Bqeow==} - engines: {node: '>= 18.12.0'} - peerDependencies: - '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 - postcss: 8.5.26 - webpack: ^5.0.0 - peerDependenciesMeta: - '@rspack/core': - optional: true - webpack: - optional: true - postcss-media-query-parser@0.2.3: resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} - postcss-merge-longhand@7.0.7: - resolution: {integrity: sha512-b3mfYUxR388u5Pt0HPcVIUtUDn/k15UfTY9M+ORW+meCR6JLNxoZffiYvXyOYQoRYQNZyX/UFkMCM/mNHxe1qA==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-merge-rules@7.0.11: - resolution: {integrity: sha512-SJUPM18g2BmPhf8BVlbwqWz4aK3pLu6u6xjfwEzra7xL6IBR10sUaiB++EzqcVfadPHrKBSMlNdP+XieykhI+Q==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-minify-font-values@7.0.3: - resolution: {integrity: sha512-yilG/VOaNI74IylQvAQQxm3/wZVBkXyYUqNUAdxqwtbWUXPsbK1q8Ms0mL83v+f8YicgcyfYCRZtWACUdYajpA==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-minify-gradients@7.0.5: - resolution: {integrity: sha512-YraROyQRg3BI1+Hg8E05B/JPdnTm8EDSVu4P2BxdM+CRiOyfmou809+chGIqo6fQqwjPGQ947nbGncSjmTU1WQ==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-minify-params@7.0.9: - resolution: {integrity: sha512-R8itbB8BhlpoYyBm1ou0dD+vJnQ3F6adQipR4UnkCHUwlo+S9WXJaDRg1RHjC8YVAtIdrQzSWvJl40HnGDTKjA==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-minify-selectors@7.1.2: - resolution: {integrity: sha512-aQtrEWKwqafNlExcKHQvPGsXR2+vlUqqJtf5XsCQcgsSb5PL4wlujWBYDJuWsP4UnQX1YHDHU8qRlD+1PzTQ+Q==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-modules-extract-imports@3.1.0: - resolution: {integrity: sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: 8.5.26 - - postcss-modules-local-by-default@4.2.0: - resolution: {integrity: sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: 8.5.26 - - postcss-modules-scope@3.2.1: - resolution: {integrity: sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: 8.5.26 - - postcss-modules-values@4.0.0: - resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} - engines: {node: ^10 || ^12 || >= 14} - peerDependencies: - postcss: 8.5.26 - postcss-nested@6.2.0: resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} engines: {node: '>=12.0'} peerDependencies: postcss: 8.5.26 - postcss-normalize-charset@7.0.3: - resolution: {integrity: sha512-NoBfZu8PR4c2NlmjvrqQTzCzLY79hwcSRgNQ3ZiNK0ABzf9kYKloE/jNj+/8GQY1wsm8pRRgANk6ydLH8cwo0Q==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-normalize-display-values@7.0.3: - resolution: {integrity: sha512-ldsCX0QIt05pKIOobZtVQ48wXJecr+czw4+e1/YjVhLMqslShgpVxgPtI2CefURR8oyVoYaU/l829MMwExDMLw==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-normalize-positions@7.0.4: - resolution: {integrity: sha512-VEvlpeGd3Ju1Hqa/oN4jaP3+ms4laYwkEL9N9u+B6k54PZjXbW1n6wI+aVprf1BQXlCYpS5+1pl/7/vHiKgARg==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-normalize-repeat-style@7.0.4: - resolution: {integrity: sha512-6mPKlY/8cSaDHxX502wERADarJsccwlky6yIrOapHH2ZgfoKAV94SbiTKfKEs4EEpdazuc3J72WsqeYk7hp9+Q==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-normalize-string@7.0.3: - resolution: {integrity: sha512-HnEQPUchi1eznmDKEYrKUTqrprEq97SrpUYClgUkv7V2zRODD9DFoUsYU+m9ZOetmD5ku7fEMZB/lwy8IT6xVQ==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-normalize-timing-functions@7.0.3: - resolution: {integrity: sha512-zmEzHdvpZBZu0OKlbJSfgASQvaayyAoVuWtvyr34IJ/LyS+DaOKvvR3EvFJ9RWWtNIx+CMvO125OVophaxNYew==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-normalize-unicode@7.0.9: - resolution: {integrity: sha512-DRAdWfeh/TjmhLJsw91vdiWCnUod9iwvM7xyS02/nF/sLsCR3A8l3pztrSUrWG8DSBqfX7yEk9FM0USaVJ2mSg==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-normalize-url@7.0.3: - resolution: {integrity: sha512-CL93wmloq5qsffmFv+bw24MIRbmhHrp53qoh1LDAb/5TtjWEXI/np4xcP/Gw9oWCb2XyWnqHYLDUwiKRoJBA1Q==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-normalize-whitespace@7.0.3: - resolution: {integrity: sha512-FdHjjn+Ht5Z2ZRjNOmeCbNq6lq09sUYKpmlF/Aq0XjVNSLTL6fmHlA/3swN2wP2caY9GV/tjSDcIIyS7aN7W0A==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-ordered-values@7.0.4: - resolution: {integrity: sha512-nubSi49hDHQk4E8KIj+IbLY8Bg+8OcSUEhgyolgM+atnOvXjV7EjaR6bac4YGZoFyPa9mWoAF3EaYbWdFkKqVg==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-reduce-initial@7.0.9: - resolution: {integrity: sha512-ztTNPdIxXTxtBcG03E9u8v44M4ElXbMIRT7pf2onlquGula0Y83nKKxqM22FA/hMgkfCjN7ohevkVlaNwI8iOQ==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - - postcss-reduce-transforms@7.0.3: - resolution: {integrity: sha512-FXsnN9ZwcZTT8Yf8cAHA8qIGUXcX6WfLd9JoYhrdDfmvsVhhfqkkv7m4AC3rwFOfz+GzkUa87OCKF9dUcicd+g==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - postcss-safe-parser@7.0.1: resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} engines: {node: '>=18.0'} @@ -9574,22 +8197,6 @@ packages: resolution: {integrity: sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==} engines: {node: '>=4'} - postcss-selector-parser@7.1.5: - resolution: {integrity: sha512-KvvtD7SrlBP7dlgkBghEE3r84CABm5SmV2aNcG4oCA+qDnJ/tvKonFVvwWAyyWUEwxuNawdfEAZKP9zM3oZ2Uw==} - engines: {node: '>=4'} - - postcss-svgo@7.1.3: - resolution: {integrity: sha512-2QfoFOYMcj8lwcVEf9WeTlkVIAm7u2QvOEhMzkQU3KUhhGX/l8hVV9EtjMv4iq3E9iI3OeeMN0YoMLbGusuigw==} - engines: {node: ^18.12.0 || ^20.9.0 || >= 18} - peerDependencies: - postcss: ^8.5.13 - - postcss-unique-selectors@7.0.7: - resolution: {integrity: sha512-d+sCkaRnSefghOUdH8CMJZV9yUQhj2ojpe8Nw/lA+LV1UOfeleGkLTl6XdCFFSai9UJ+DJPb69FFuqthXYsY8w==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -9615,10 +8222,6 @@ packages: resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - pretty-format@30.4.1: - resolution: {integrity: sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==} - engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - pretty-format@30.5.1: resolution: {integrity: sha512-byhRAPguVKMQIj4kjJwJ5lAskVhfuiSdiYl/aLTWpgkGEmic2jYhJh1yE9ih8Ox44Xg9ccCT11/S6QrzSJuNrg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -9631,6 +8234,10 @@ packages: resolution: {integrity: sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==} engines: {node: ^20.17.0 || >=22.9.0} + proc-log@7.0.0: + resolution: {integrity: sha512-FYgfaA69XZ93zaXLoMNQ+ViDXGGBgR8aLh03txzcFhV+9xOXx7+8DLCULrKKpR9+GsH9ZfHm82aSUPpozX0Ztg==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} + process-ancestry@0.1.0: resolution: {integrity: sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==} engines: {node: '>=18.0.0'} @@ -9660,6 +8267,15 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} + proxy-agent-negotiate@1.1.0: + resolution: {integrity: sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==} + engines: {node: '>= 20'} + peerDependencies: + kerberos: ^2.0.0 + peerDependenciesMeta: + kerberos: + optional: true + proxy-from-env@2.1.0: resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} engines: {node: '>=10'} @@ -9707,14 +8323,6 @@ packages: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} - range-parser@1.3.0: - resolution: {integrity: sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==} - engines: {node: '>= 0.6'} - - raw-body@2.5.3: - resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} - engines: {node: '>= 0.8'} - raw-body@3.0.2: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} @@ -9722,12 +8330,8 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-is@19.2.8: - resolution: {integrity: sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==} - - react-refresh@0.18.0: - resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} - engines: {node: '>=0.10.0'} + react-is@19.3.0: + resolution: {integrity: sha512-UpMYezM4v5/18F28aC66AEsjXIgE02kyEMH6yLdgLXu/UTfa1Ntwck/nNLrbqJsEXW7gPb0coNO9FQse9WTovA==} read-binary-file-arch@1.0.6: resolution: {integrity: sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==} @@ -9912,17 +8516,10 @@ packages: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} - retry@0.13.1: - resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} - engines: {node: '>= 4'} - 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==} - rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} deprecated: Rimraf versions prior to v4 are no longer supported @@ -9932,8 +8529,13 @@ packages: resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} engines: {node: '>=8.0'} - rolldown@1.0.0-rc.4: - resolution: {integrity: sha512-V2tPDUrY3WSevrvU2E41ijZlpF+5PbZu4giH+VpNraaadsJGHa4fR6IFwsocVwEXDoAdIv5qgPPxgrvKAOIPtA==} + rolldown@1.1.5: + resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rolldown@1.2.0: + resolution: {integrity: sha512-u7tgm5l4Yw1iTqUL4EcYOAt7fFvCgQMLeidrnD4GALlC6aOznCjezYajgxeyKw27u0Q5N7fwgCzjVyPIWzwuBA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -9954,6 +8556,10 @@ packages: rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + run-applescript@7.0.0: + resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} + engines: {node: '>=18'} + run-applescript@7.1.0: resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} engines: {node: '>=18'} @@ -10116,42 +8722,16 @@ packages: engines: {node: '>=16.0.0'} hasBin: true - sass-loader@16.0.8: - resolution: {integrity: sha512-hcov4ZwZJIGbEuyNr9EmiTmZueyrxSToE6GOzoZnq5JM7ecRO7ttyvilPn+VmRsqiP16+VYZzVnGZj/hzZgKBA==} - engines: {node: '>= 18.12.0'} - peerDependencies: - '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 - node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 - sass: ^1.3.0 - sass-embedded: '*' - webpack: ^5.0.0 - peerDependenciesMeta: - '@rspack/core': - optional: true - node-sass: - optional: true - sass: - optional: true - sass-embedded: - optional: true - webpack: - optional: true - sass@1.100.0: resolution: {integrity: sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==} engines: {node: '>=20.19.0'} hasBin: true - sass@1.102.0: - resolution: {integrity: sha512-NSOyTnaQF7rTAEOtI2fwb386vL+akyiQLBZu8Na7hXCb+umJy0GAqlcMIaqACZ6Z1VgTBS4K9PG6B3IdjHGJsw==} + sass@1.101.0: + resolution: {integrity: sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==} engines: {node: '>=20.19.0'} hasBin: true - sass@1.97.3: - resolution: {integrity: sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==} - engines: {node: '>=14.0.0'} - hasBin: true - satteri@0.10.5: resolution: {integrity: sha512-Ao1LKpAEa9Wdg0otgbVKViZHEq9ebdXe4DMrp3s9vQAU0HNIuHnFEuMuOcm0ZIXyV0Yzxj91NvhLpvXZJO/5ZQ==} @@ -10171,10 +8751,6 @@ packages: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} - schema-utils@4.3.0: - resolution: {integrity: sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==} - engines: {node: '>= 10.13.0'} - schema-utils@4.3.3: resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} engines: {node: '>= 10.13.0'} @@ -10182,17 +8758,6 @@ packages: secure-compare@3.0.1: resolution: {integrity: sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==} - select-hose@2.0.0: - resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} - - selfsigned@2.4.1: - resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} - engines: {node: '>=10'} - - selfsigned@5.5.0: - resolution: {integrity: sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==} - engines: {node: '>=18'} - semver-compare@1.0.0: resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} @@ -10214,15 +8779,16 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.4: + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + engines: {node: '>=10'} + hasBin: true + semver@7.8.5: resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true - send@0.19.2: - resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} - engines: {node: '>= 0.8.0'} - send@1.2.1: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} @@ -10235,14 +8801,6 @@ packages: resolution: {integrity: sha512-RNEqWOyhhUQYN9V1GfHwu9AR/g+NTciH6Z5u3/no6X3/w+04J2lVDL+svFQVXgXrEGBMG2puMVN3gq2SNGuTGw==} engines: {node: '>=20.0.0'} - serve-index@1.9.2: - resolution: {integrity: sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==} - engines: {node: '>= 0.8.0'} - - serve-static@1.16.3: - resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} - engines: {node: '>= 0.8.0'} - serve-static@2.2.1: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} @@ -10297,10 +8855,6 @@ packages: resolution: {integrity: sha512-s/9q9PEtcRmDTz69+cJ3yYBAe9yGrL7e46gm2bU4pQ9N48ecPK9QrGFnLwYgb4smOHskx4PL7wCNMktW2AoD+g==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - shell-quote@1.10.0: - resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} - engines: {node: '>= 0.4'} - shiki@4.4.3: resolution: {integrity: sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==} engines: {node: '>=20'} @@ -10336,10 +8890,6 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sigstore@4.1.1: - resolution: {integrity: sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w==} - engines: {node: ^20.17.0 || >=22.9.0} - simple-update-notifier@2.0.0: resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} engines: {node: '>=10'} @@ -10360,46 +8910,18 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} - slice-ansi@7.1.2: - resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} - engines: {node: '>=18'} - - slice-ansi@8.0.0: - resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} - engines: {node: '>=20'} - - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + slice-ansi@9.0.0: + resolution: {integrity: sha512-SO/3iYL5S3W57LLEniscOGPZgOqZUPCx6d3dB+52B80yJ0XstzsC/eV8gnA4tM3MHDrKz+OCFSLNjswdSC+/bA==} + engines: {node: '>=22'} smol-toml@1.6.1: resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} engines: {node: '>= 18'} - sockjs@0.3.24: - resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} - - socks-proxy-agent@8.0.5: - resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} - engines: {node: '>= 14'} - - 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: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map-loader@5.0.0: - resolution: {integrity: sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==} - engines: {node: '>= 18.12.0'} - peerDependencies: - webpack: ^5.72.1 - - source-map-support@0.5.13: - resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} - source-map-support@0.5.19: resolution: {integrity: sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==} @@ -10417,47 +8939,20 @@ packages: space-separated-tokens@2.0.2: resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - - spdx-expression-parse@4.0.0: - resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} - - spdx-license-ids@3.0.23: - resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} - - spdy-transport@3.0.0: - resolution: {integrity: sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==} - - spdy@4.0.2: - resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} - engines: {node: '>=6.0.0'} - sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - ssri@13.0.1: - resolution: {integrity: sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==} - engines: {node: ^20.17.0 || >=22.9.0} - stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} - stackframe@1.3.4: - resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} - stat-mode@1.0.0: resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==} engines: {node: '>= 6'} - statuses@1.5.0: - resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} - engines: {node: '>= 0.6'} - statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -10539,24 +9034,12 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - style-loader@3.3.4: - resolution: {integrity: sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==} - engines: {node: '>= 12.13.0'} - peerDependencies: - webpack: ^5.0.0 - style-to-js@1.1.21: resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} style-to-object@1.0.14: resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} - stylehacks@7.0.11: - resolution: {integrity: sha512-iODNfhXVLqc5LADs+Y6Oh5wJuK5ZcHbVng8aiK3y9pjMQdc5hLrBW0eFU6FtnpNrE6PoEg/MmFTU4waotj5WNg==} - engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} - peerDependencies: - postcss: ^8.5.13 - sucrase@3.35.1: resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} @@ -10607,10 +9090,6 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} - tapable@2.3.3: - resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} - engines: {node: '>=6'} - tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} @@ -10642,49 +9121,6 @@ packages: uglify-js: optional: true - terser-webpack-plugin@5.6.1: - resolution: {integrity: sha512-201R5j+sJpK8nFWwKVyNfZot8FaJbLZDq5evriVzbV1wDtSXDjRUDRfJzHpAaxFDMEhsZL1QkeqM61wgsS3KaQ==} - engines: {node: '>= 10.13.0'} - peerDependencies: - '@minify-html/node': '*' - '@swc/core': '*' - '@swc/css': '*' - '@swc/html': '*' - clean-css: '*' - cssnano: '*' - csso: '*' - esbuild: '*' - html-minifier-terser: '*' - lightningcss: '*' - postcss: '*' - uglify-js: '*' - webpack: ^5.1.0 - peerDependenciesMeta: - '@minify-html/node': - optional: true - '@swc/core': - optional: true - '@swc/css': - optional: true - '@swc/html': - optional: true - clean-css: - optional: true - cssnano: - optional: true - csso: - optional: true - esbuild: - optional: true - html-minifier-terser: - optional: true - lightningcss: - optional: true - postcss: - optional: true - uglify-js: - optional: true - terser@5.44.1: resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==} engines: {node: '>=10'} @@ -10695,10 +9131,6 @@ packages: engines: {node: '>=10'} hasBin: true - test-exclude@6.0.0: - resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} - engines: {node: '>=8'} - test-exclude@7.0.2: resolution: {integrity: sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw==} engines: {node: '>=18'} @@ -10710,15 +9142,6 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - thingies@2.6.1: - resolution: {integrity: sha512-cV/CMGTK3M4MlnJ/0At6ismOw/A0EEniDNScajjz/Br3c1sqE72YD01rGpPTKwd27wAxI5Pr+6+0w8yofzFRYw==} - engines: {node: '>=10.18'} - peerDependencies: - tslib: ^2 - - thunky@1.1.0: - resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} - tiny-async-pool@1.3.0: resolution: {integrity: sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA==} @@ -10736,10 +9159,6 @@ packages: resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} engines: {node: '>=18'} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} @@ -10758,9 +9177,6 @@ packages: resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} engines: {node: '>=14.14'} - tmpl@1.0.5: - resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -10773,19 +9189,10 @@ packages: resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} engines: {node: '>=16'} - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tr46@5.1.1: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} - tree-dump@1.1.0: - resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==} - engines: {node: '>=10.0'} - peerDependencies: - tslib: '2' - tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -10799,30 +9206,12 @@ packages: truncate-utf8-bytes@1.0.2: resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} - ts-api-utils@2.4.0: - resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} - engines: {node: '>=18.12'} - peerDependencies: - typescript: '>=4.8.4' - ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' - ts-checker-rspack-plugin@1.6.0: - resolution: {integrity: sha512-T9xp7YhbvxDP0uUAn8n5iBnzFXdPJTwiR+ePNVVRYrIsYqNkXxxfvdMFC7CWT4kPkOFXuV0N1wTIGkjxMeURYg==} - peerDependencies: - '@rspack/core': ^1.0.0 || ^2.0.0 - '@typescript/native-preview': ^7.0.0-0 - typescript: '>=3.8.0' - peerDependenciesMeta: - '@rspack/core': - optional: true - '@typescript/native-preview': - optional: true - ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -10853,13 +9242,6 @@ packages: jest-util: optional: true - ts-loader@9.5.4: - resolution: {integrity: sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==} - engines: {node: '>=12.0.0'} - peerDependencies: - typescript: '*' - webpack: ^5.0.0 - ts-loader@9.6.2: resolution: {integrity: sha512-R4iuczmtgxvtuI556s+hTZ6/7Ee03VCAk/l/M8LY1OAsUgB7YydsCxkgq9D9pKRaD7GJqUi2u8fp9zZP/ufjKA==} engines: {node: '>=12.0.0'} @@ -10896,9 +9278,6 @@ packages: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} - tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -10907,14 +9286,6 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - tsyringe@4.10.0: - resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} - engines: {node: '>= 6.0.0'} - - tuf-js@4.1.0: - resolution: {integrity: sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==} - engines: {node: ^20.17.0 || >=22.9.0} - type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -10939,10 +9310,6 @@ packages: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} - type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} - type-is@2.0.1: resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} engines: {node: '>= 0.6'} @@ -10963,9 +9330,6 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typed-assert@1.0.9: - resolution: {integrity: sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==} - typescript-eslint@8.69.0: resolution: {integrity: sha512-B3MltX0VqjUBNEe3b3sSuiRbfa6XrfHFtBiPamjT5AsW/dfq+y+bc0wyuS9DxAS1LyzCxRp2+rxzpLUvqM2BvA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -10973,8 +9337,8 @@ packages: 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 @@ -11175,14 +9539,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} - - uuid@11.1.1: - resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} - hasBin: true - v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} @@ -11190,9 +9546,9 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} - validate-npm-package-name@7.0.2: - resolution: {integrity: sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==} - engines: {node: ^20.17.0 || >=22.9.0} + validate-npm-package-name@8.0.0: + resolution: {integrity: sha512-SCv6OOV6Xj2/3cXy3dGmADluJTNcL3o7hZAglNPTe+WYuEuvxgJzxPrSDLZhF+CwyQOubqgecjMmTJGMVLWjYQ==} + engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} varint@6.0.0: resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} @@ -11201,6 +9557,10 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + verkit@0.3.2: + resolution: {integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==} + engines: {node: '>=18.12.0'} + vfile-location@5.0.3: resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==} @@ -11235,15 +9595,16 @@ packages: videojs-vtt.js@0.15.5: resolution: {integrity: sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==} - vite@7.3.6: - resolution: {integrity: sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==} + vite@8.1.5: + resolution: {integrity: sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.3.0 + esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 - lightningcss: ^1.21.0 sass: ^1.70.0 sass-embedded: ^1.70.0 stylus: '>=0.54.8' @@ -11254,12 +9615,14 @@ packages: peerDependenciesMeta: '@types/node': optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true jiti: optional: true less: optional: true - lightningcss: - optional: true sass: optional: true sass-embedded: @@ -11330,24 +9693,10 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - walker@1.0.8: - resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - - watchpack@2.5.0: - resolution: {integrity: sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==} - engines: {node: '>=10.13.0'} - - watchpack@2.5.1: - resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} - engines: {node: '>=10.13.0'} - watchpack@2.5.2: resolution: {integrity: sha512-6i/00NBjP4yGPs+caKSyRfpTF/8Torsu0MOW3mMzIbhgISFder8i7xbqgHlLMwJrdiN8ndBV3UA1/AfzPSr+jg==} engines: {node: '>=10.13.0'} - wbuf@1.7.3: - resolution: {integrity: sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==} - wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} @@ -11360,35 +9709,10 @@ packages: webcrypto-core@1.9.2: resolution: {integrity: sha512-gsXecm82UQNlTBURJGuqOWy1Ww08S3kZUcr3aOJS02Pk0xLtkfeUAVC0u0xhgdonFme80edSJUIJyuvL/7250Q==} - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} - webpack-dev-middleware@7.4.5: - resolution: {integrity: sha512-uxQ6YqGdE4hgDKNf7hUiPXOdtkXvBJXrfEGYSx7P7LC8hnUYGK70X6xQXUvXeNyBDDcsiQXpG2m3G9vxowaEuA==} - engines: {node: '>= 18.12.0'} - peerDependencies: - webpack: ^5.0.0 - peerDependenciesMeta: - webpack: - optional: true - - webpack-dev-server@5.2.6: - resolution: {integrity: sha512-HNLRmamRvVavZQ+avceZifmv8hmdUjg43t6MI4SqJDwFdW7RPQwH5vzGhDRZSX59SgfbeHhLnq3g+uooWo7pVw==} - engines: {node: '>= 18.12.0'} - hasBin: true - peerDependencies: - webpack: ^5.0.0 - webpack-cli: '*' - peerDependenciesMeta: - webpack: - optional: true - webpack-cli: - optional: true - webpack-merge@5.10.0: resolution: {integrity: sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==} engines: {node: '>=10.0.0'} @@ -11401,20 +9725,6 @@ packages: resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} engines: {node: '>=10.13.0'} - webpack-sources@3.5.1: - resolution: {integrity: sha512-jyuiGJdtvY434z5bUZrjz67v76/ePNvFZTp9Mdz29IlH4+GPsgyGjiv0fKI+M7BdkU6ADjulUcKAd3tUK3WlEw==} - engines: {node: '>=10.13.0'} - - webpack-subresource-integrity@5.1.0: - resolution: {integrity: sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==} - engines: {node: '>= 12'} - peerDependencies: - html-webpack-plugin: '>= 5.0.0-beta.1 < 6' - webpack: ^5.12.0 - peerDependenciesMeta: - html-webpack-plugin: - optional: true - webpack@5.104.1: resolution: {integrity: sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==} engines: {node: '>=10.13.0'} @@ -11425,24 +9735,6 @@ packages: webpack-cli: optional: true - webpack@5.109.2: - resolution: {integrity: sha512-U9/cvLzxObKNEZ9+TtdqrHM5/9z3lgl2c+c4BzbqGxFQvQvBAq87yql5A8pQ+rrMbS496MZJeF5enVBndIy2hw==} - engines: {node: '>=10.13.0'} - hasBin: true - peerDependencies: - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - - websocket-driver@0.7.5: - resolution: {integrity: sha512-ZL2+3c7kMBdIRCMz6l8jQMHyGVxj+UL+xVk74Ombiciboca8rHa15L86B19E5oh1pL9Ii/uj54gtsIrZGMo6zA==} - engines: {node: '>=0.8.0'} - - websocket-extensions@0.1.4: - resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} - engines: {node: '>=0.8.0'} - whatwg-encoding@2.0.0: resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} engines: {node: '>=12'} @@ -11461,9 +9753,6 @@ packages: resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} engines: {node: '>=18'} - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -11488,6 +9777,11 @@ packages: engines: {node: '>= 8'} hasBin: true + which@3.0.1: + resolution: {integrity: sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + which@5.0.0: resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} engines: {node: ^18.17.0 || >=20.5.0} @@ -11508,6 +9802,10 @@ packages: wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + wrap-ansi@10.0.1: + resolution: {integrity: sha512-M0N4xzyzosiIok3svYlEo1sdLZts/8FPgYH/GPC3wvlmPoRvnoManGMrE54waYj3tISA8w6lsdesfVv67qSr8Q==} + engines: {node: '>=20'} + wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -11531,18 +9829,6 @@ packages: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ws@8.21.0: - resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} - 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.3: resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} engines: {node: '>=10.0.0'} @@ -11555,10 +9841,6 @@ packages: utf-8-validate: optional: true - wsl-utils@0.1.0: - resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} - engines: {node: '>=18'} - xml-js@1.6.11: resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} hasBin: true @@ -11627,10 +9909,6 @@ packages: resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} engines: {node: '>=12'} - yargs@18.0.0: - resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=23} - yargs@18.1.0: resolution: {integrity: sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} @@ -11647,10 +9925,6 @@ packages: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} - yoctocolors-cjs@2.1.3: - resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} - engines: {node: '>=18'} - yoctocolors@2.2.0: resolution: {integrity: sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==} engines: {node: '>=18'} @@ -11660,104 +9934,20 @@ packages: peerDependencies: zod: ^3.25.28 || ^4 - zod@4.3.6: - resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} zod@4.5.4: resolution: {integrity: sha512-sC95tT5iHHH9gtpj6A81kh+NEaRAUFN+qlUPDUbRfOMvNf5QCBqsb3WgvnpVtK5Y+4UfA6KqufotuTvMGiTlsA==} - zone.js@0.16.2: - resolution: {integrity: sha512-Eky7p2Z1Ig3NnbfodSPoARCjKBSTFMnE/ACsP1L/XJEfY4SdOFce19BsUCWVwL6K5ABZFy5J3bjcMWffX+YM3Q==} + zone.js@0.16.3: + resolution: {integrity: sha512-ihXL9+vhYyEhXz4TDNpHeAOZN9FVrbog0Il64OIEI28UP/n5AaI6gsccRuOHBfx+206agzyoK527bYIO0Foy6A==} zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} snapshots: - '@algolia/abtesting@1.14.1': - 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-abtesting@5.48.1': - 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-analytics@5.48.1': - 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.48.1': {} - - '@algolia/client-insights@5.48.1': - 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-personalization@5.48.1': - 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-query-suggestions@5.48.1': - 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-search@5.48.1': - 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/ingestion@1.48.1': - 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/monitoring@1.48.1': - 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/recommend@5.48.1': - 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/requester-browser-xhr@5.48.1': - dependencies: - '@algolia/client-common': 5.48.1 - - '@algolia/requester-fetch@5.48.1': - dependencies: - '@algolia/client-common': 5.48.1 - - '@algolia/requester-node-http@5.48.1': - dependencies: - '@algolia/client-common': 5.48.1 - '@alloc/quick-lru@5.2.0': {} '@ampproject/remapping@2.3.0': @@ -11765,31 +9955,17 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.2101.4(chokidar@5.0.0)': + '@angular-devkit/architect@0.2201.8(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.1.4(chokidar@5.0.0) + '@angular-devkit/core': 22.1.8(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/architect@0.2102.19(chokidar@5.0.0)': + '@angular-devkit/core@22.1.8(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.19(chokidar@5.0.0) - rxjs: 7.8.2 - transitivePeerDependencies: - - chokidar - - '@angular-devkit/architect@0.2102.21(chokidar@5.0.0)': - dependencies: - '@angular-devkit/core': 21.2.21(chokidar@5.0.0) - rxjs: 7.8.2 - transitivePeerDependencies: - - chokidar - - '@angular-devkit/core@21.1.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.5 rxjs: 7.8.2 @@ -11797,123 +9973,56 @@ snapshots: optionalDependencies: chokidar: 5.0.0 - '@angular-devkit/core@21.2.19(chokidar@5.0.0)': + '@angular-devkit/schematics@22.1.8(chokidar@5.0.0)': dependencies: - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) + '@angular-devkit/core': 22.1.8(chokidar@5.0.0) jsonc-parser: 3.3.1 - picomatch: 4.0.4 - rxjs: 7.8.2 - source-map: 0.7.6 - optionalDependencies: - chokidar: 5.0.0 - - '@angular-devkit/core@21.2.21(chokidar@5.0.0)': - dependencies: - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) - jsonc-parser: 3.3.1 - picomatch: 4.0.4 - rxjs: 7.8.2 - source-map: 0.7.6 - optionalDependencies: - chokidar: 5.0.0 - - '@angular-devkit/schematics@21.2.19(chokidar@5.0.0)': - dependencies: - '@angular-devkit/core': 21.2.19(chokidar@5.0.0) - jsonc-parser: 3.3.1 - magic-string: 0.30.21 - ora: 9.3.0 + magic-string: 1.0.0 + ora: 9.4.1 rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/schematics@21.2.21(chokidar@5.0.0)': + '@angular-eslint/builder@22.5.0(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3)': dependencies: - '@angular-devkit/core': 21.2.21(chokidar@5.0.0) - jsonc-parser: 3.3.1 - magic-string: 0.30.21 - ora: 9.3.0 - rxjs: 7.8.2 - transitivePeerDependencies: - - chokidar - - '@angular-eslint/builder@21.3.1(@angular/cli@21.2.21(@types/node@20.19.9)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@angular-devkit/architect': 0.2101.4(chokidar@5.0.0) - '@angular-devkit/core': 21.2.19(chokidar@5.0.0) - '@angular/cli': 21.2.21(@types/node@20.19.9)(chokidar@5.0.0) + '@angular-devkit/architect': 0.2201.8(chokidar@5.0.0) + '@angular-devkit/core': 22.1.8(chokidar@5.0.0) eslint: 9.39.2(jiti@1.21.7) - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - chokidar - '@angular-eslint/builder@21.4.0(@angular/cli@21.2.21(@types/node@20.19.9)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@angular-eslint/bundled-angular-compiler@22.5.0': {} + + '@angular-eslint/eslint-plugin-template@22.5.0(@angular-eslint/template-parser@22.5.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(@typescript-eslint/types@8.69.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3)': dependencies: - '@angular-devkit/architect': 0.2102.21(chokidar@5.0.0) - '@angular-devkit/core': 21.2.19(chokidar@5.0.0) - '@angular/cli': 21.2.21(@types/node@20.19.9)(chokidar@5.0.0) - eslint: 9.39.2(jiti@1.21.7) - typescript: 5.9.3 - transitivePeerDependencies: - - chokidar - - '@angular-eslint/bundled-angular-compiler@21.3.1': {} - - '@angular-eslint/bundled-angular-compiler@21.4.0': {} - - '@angular-eslint/eslint-plugin-template@21.3.1(@angular-eslint/template-parser@21.3.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@typescript-eslint/types@8.69.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@angular-eslint/bundled-angular-compiler': 21.3.1 - '@angular-eslint/template-parser': 21.3.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@angular-eslint/utils': 21.3.1(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@angular-eslint/bundled-angular-compiler': 22.5.0 + '@angular-eslint/template-parser': 22.5.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + '@angular-eslint/utils': 22.5.0(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) aria-query: 5.3.2 axobject-query: 4.1.0 eslint: 9.39.2(jiti@1.21.7) - typescript: 5.9.3 + typescript: 6.0.3 - '@angular-eslint/eslint-plugin-template@21.4.0(@angular-eslint/template-parser@21.4.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@typescript-eslint/types@8.66.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@angular-eslint/eslint-plugin@22.5.0(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 21.4.0 - '@angular-eslint/template-parser': 21.4.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@angular-eslint/utils': 21.4.0(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - aria-query: 5.3.2 - axobject-query: 4.1.0 + '@angular-eslint/bundled-angular-compiler': 22.5.0 + '@angular-eslint/utils': 22.5.0(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) eslint: 9.39.2(jiti@1.21.7) - typescript: 5.9.3 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 - '@angular-eslint/eslint-plugin@21.3.1(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@angular-eslint/schematics@22.5.0(@angular-eslint/template-parser@22.5.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(@typescript-eslint/types@8.69.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 21.3.1 - '@angular-eslint/utils': 21.3.1(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.2(jiti@1.21.7) - ts-api-utils: 2.4.0(typescript@5.9.3) - typescript: 5.9.3 - - '@angular-eslint/eslint-plugin@21.4.0(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@angular-eslint/bundled-angular-compiler': 21.4.0 - '@angular-eslint/utils': 21.4.0(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.2(jiti@1.21.7) - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 - - '@angular-eslint/schematics@21.3.1(@angular-eslint/template-parser@21.3.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@angular/cli@21.2.21(@types/node@20.19.9)(chokidar@5.0.0))(@typescript-eslint/types@8.69.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@angular-devkit/core': 21.2.19(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.21(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 21.3.1(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.3.1(@angular-eslint/template-parser@21.3.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@typescript-eslint/types@8.69.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@angular/cli': 21.2.21(@types/node@20.19.9)(chokidar@5.0.0) - ignore: 7.0.5 - semver: 7.7.4 + '@angular-devkit/core': 22.1.8(chokidar@5.0.0) + '@angular-devkit/schematics': 22.1.8(chokidar@5.0.0) + '@angular-eslint/eslint-plugin': 22.5.0(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + '@angular-eslint/eslint-plugin-template': 22.5.0(@angular-eslint/template-parser@22.5.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(@typescript-eslint/types@8.69.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + ignore: 7.0.8 + semver: 7.8.5 strip-json-comments: 3.1.1 transitivePeerDependencies: - '@angular-eslint/template-parser' @@ -11923,105 +10032,73 @@ snapshots: - eslint - typescript - '@angular-eslint/schematics@21.4.0(@angular-eslint/template-parser@21.4.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@angular/cli@21.2.21(@types/node@20.19.9)(chokidar@5.0.0))(@typescript-eslint/types@8.66.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@angular-eslint/template-parser@22.5.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3)': dependencies: - '@angular-devkit/core': 21.2.19(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.21(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 21.4.0(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.4.0(@angular-eslint/template-parser@21.4.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@typescript-eslint/types@8.66.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@angular/cli': 21.2.21(@types/node@20.19.9)(chokidar@5.0.0) - ignore: 7.0.5 - semver: 7.7.4 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - '@angular-eslint/template-parser' - - '@typescript-eslint/types' - - '@typescript-eslint/utils' - - chokidar - - eslint - - typescript - - '@angular-eslint/template-parser@21.3.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@angular-eslint/bundled-angular-compiler': 21.3.1 + '@angular-eslint/bundled-angular-compiler': 22.5.0 eslint: 9.39.2(jiti@1.21.7) eslint-scope: 9.1.2 - typescript: 5.9.3 + typescript: 6.0.3 - '@angular-eslint/template-parser@21.4.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@angular-eslint/utils@22.5.0(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 21.4.0 + '@angular-eslint/bundled-angular-compiler': 22.5.0 + '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) eslint: 9.39.2(jiti@1.21.7) - eslint-scope: 9.1.2 - typescript: 5.9.3 + typescript: 6.0.3 - '@angular-eslint/utils@21.3.1(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))': dependencies: - '@angular-eslint/bundled-angular-compiler': 21.3.1 - '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.2(jiti@1.21.7) - typescript: 5.9.3 - - '@angular-eslint/utils@21.4.0(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@angular-eslint/bundled-angular-compiler': 21.4.0 - '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.2(jiti@1.21.7) - typescript: 5.9.3 - - '@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))': - dependencies: - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) tslib: 2.8.1 - '@angular/build@21.2.19(@angular/compiler-cli@21.2.19(@angular/compiler@21.2.19)(typescript@5.9.3))(@angular/compiler@21.2.19)(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/service-worker@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@20.19.9)(chokidar@5.0.0)(jiti@1.21.7)(less@4.5.1)(lightningcss@1.33.0)(postcss@8.5.26)(sass-embedded@1.100.0)(tailwindcss@3.4.19(tsx@4.23.13)(yaml@2.9.0))(terser@5.49.2)(tslib@2.8.1)(tsx@4.23.13)(typescript@5.9.3)(yaml@2.9.0)': + '@angular/build@22.1.8(@angular/compiler-cli@22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3))(@angular/compiler@22.1.6)(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/service-worker@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@types/node@20.19.9)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(jiti@1.21.7)(less@4.5.1)(postcss@8.5.26)(rollup@4.59.0)(sass-embedded@1.100.0)(tailwindcss@3.4.19(tsx@4.23.13)(yaml@2.9.0))(terser@5.49.2)(tslib@2.8.1)(tsx@4.23.13)(typescript@6.0.3)(yaml@2.9.0)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2102.19(chokidar@5.0.0) - '@angular/compiler': 21.2.19 - '@angular/compiler-cli': 21.2.19(@angular/compiler@21.2.19)(typescript@5.9.3) - '@babel/core': 7.29.7 - '@babel/helper-annotate-as-pure': 7.27.3 + '@angular-devkit/architect': 0.2201.8(chokidar@5.0.0) + '@angular/compiler': 22.1.6 + '@angular/compiler-cli': 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) + '@babel/core': 8.0.1 + '@babel/helper-annotate-as-pure': 8.0.0 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@20.19.9) - '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.6(patch_hash=215b6585a9c92fa68c096d500f4676983200307b1e64c1321e20ccbd50bbc8f3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(lightningcss@1.33.0)(sass-embedded@1.100.0)(sass@1.97.3)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0)) - beasties: 0.4.1 + '@inquirer/confirm': 6.1.1(@types/node@20.19.9) + '@vitejs/plugin-basic-ssl': 2.3.0(vite@8.1.5(patch_hash=469ec4025880fc7b86d3d8b3b4e2028572d6acf65bdfb93973db18fdb3c00ad9)(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0)) + beasties: 0.4.3 browserslist: 4.28.8 - esbuild: 0.28.1 - https-proxy-agent: 7.0.6 - istanbul-lib-instrument: 6.0.3 + esbuild: 0.28.2 + https-proxy-agent: 9.1.0 jsonc-parser: 3.3.1 - listr2: 9.0.5 - magic-string: 0.30.21 + listr2: 11.0.0 + magic-string: 1.0.0 mrmime: 2.0.1 - parse5-html-rewriting-stream: 8.0.0 - picomatch: 4.0.4 + oxc-parser: 0.142.0 + parse5-html-rewriting-stream: 8.0.1 + picomatch: 4.0.5 piscina: 5.2.0 - rolldown: 1.0.0-rc.4(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) - sass: 1.97.3 - semver: 7.7.4 + rolldown: 1.2.0 + sass: 1.101.0 + semver: 7.8.5 source-map-support: 0.5.21 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 tslib: 2.8.1 - typescript: 5.9.3 - undici: 7.29.0 - vite: 7.3.6(patch_hash=215b6585a9c92fa68c096d500f4676983200307b1e64c1321e20ccbd50bbc8f3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(lightningcss@1.33.0)(sass-embedded@1.100.0)(sass@1.97.3)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0) - watchpack: 2.5.1 + typescript: 6.0.3 + vite: 8.1.5(patch_hash=469ec4025880fc7b86d3d8b3b4e2028572d6acf65bdfb93973db18fdb3c00ad9)(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0) + watchpack: 2.5.2 optionalDependencies: - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/platform-browser': 21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) - '@angular/service-worker': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/platform-browser': 22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) + '@angular/service-worker': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + istanbul-lib-instrument: 6.0.3 less: 4.5.1 - lmdb: 3.5.1 + lmdb: 3.5.6 postcss: 8.5.26 + rollup: 4.59.0 tailwindcss: 3.4.19(tsx@4.23.13)(yaml@2.9.0) transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - '@types/node' + - '@vitejs/devtools' - chokidar - jiti - - lightningcss + - kerberos - sass-embedded - stylus - sugarss @@ -12030,51 +10107,48 @@ snapshots: - tsx - yaml - '@angular/cdk@21.2.14(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2)': + '@angular/cdk@22.1.6(d84494623d562fa967d8dbca5e8215e7)': dependencies: - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/platform-browser': 21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) - parse5: 8.0.0 + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/forms': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) + '@angular/platform-browser': 22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) + parse5: 8.0.1 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/cli@21.2.21(@types/node@20.19.9)(chokidar@5.0.0)': + '@angular/cli@22.1.8(@types/node@20.19.9)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2102.21(chokidar@5.0.0) - '@angular-devkit/core': 21.2.21(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.21(chokidar@5.0.0) - '@inquirer/prompts': 7.10.1(@types/node@20.19.9) - '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@20.19.9))(@types/node@20.19.9)(listr2@9.0.5) - '@modelcontextprotocol/sdk': 1.30.0(zod@4.3.6) - '@schematics/angular': 21.2.21(chokidar@5.0.0) - '@yarnpkg/lockfile': 1.1.0 - algoliasearch: 5.48.1 - ini: 6.0.0 + '@angular-devkit/architect': 0.2201.8(chokidar@5.0.0) + '@angular-devkit/core': 22.1.8(chokidar@5.0.0) + '@angular-devkit/schematics': 22.1.8(chokidar@5.0.0) + '@inquirer/prompts': 8.5.2(@types/node@20.19.9) + '@listr2/prompt-adapter-inquirer': 4.2.5(@inquirer/prompts@8.5.2(@types/node@20.19.9))(@types/node@20.19.9)(listr2@11.0.0) + '@modelcontextprotocol/sdk': 1.30.0(zod@4.4.3) + '@schematics/angular': 22.1.8(chokidar@5.0.0) jsonc-parser: 3.3.1 - listr2: 9.0.5 - npm-package-arg: 13.0.2 - pacote: 21.5.1 - parse5-html-rewriting-stream: 8.0.0 - semver: 7.7.4 - yargs: 18.0.0 - zod: 4.3.6 + listr2: 11.0.0 + npm-package-arg: 14.0.0 + parse5-html-rewriting-stream: 8.0.1 + semver: 7.8.5 + yargs: 18.1.0 + zod: 4.4.3 transitivePeerDependencies: - '@cfworker/json-schema' - '@types/node' - chokidar - supports-color - '@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2)': + '@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@21.2.19(@angular/compiler@21.2.19)(typescript@5.9.3)': + '@angular/compiler-cli@22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3)': dependencies: - '@angular/compiler': 21.2.19 - '@babel/core': 7.29.7 + '@angular/compiler': 22.1.6 + '@babel/core': 8.0.1 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 5.0.0 convert-source-map: 1.9.0 @@ -12083,70 +10157,69 @@ snapshots: tslib: 2.8.1 yargs: 18.1.0 optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color + typescript: 6.0.3 - '@angular/compiler@21.2.19': + '@angular/compiler@22.1.6': dependencies: tslib: 2.8.1 - '@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)': + '@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 21.2.19 - zone.js: 0.16.2 + '@angular/compiler': 22.1.6 + zone.js: 0.16.3 - '@angular/forms@21.2.19(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2)': + '@angular/forms@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/platform-browser': 21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/platform-browser': 22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) '@standard-schema/spec': 1.1.0 rxjs: 7.8.2 tslib: 2.8.1 + zod: 4.5.4 - '@angular/language-service@21.2.19': {} + '@angular/language-service@22.1.6': {} - '@angular/material@21.2.14(e62c7210bef08c270f76f8032e5d0530)': + '@angular/material@22.1.6(42f78374544ce4ecfab2a610740b5d20)': dependencies: - '@angular/cdk': 21.2.14(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/forms': 21.2.19(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) - '@angular/platform-browser': 21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) + '@angular/cdk': 22.1.6(d84494623d562fa967d8dbca5e8215e7) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/forms': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) + '@angular/platform-browser': 22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/platform-browser-dynamic@21.2.19(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/compiler@21.2.19)(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))': + '@angular/platform-browser-dynamic@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/compiler@22.1.6)(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))': dependencies: - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/compiler': 21.2.19 - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/platform-browser': 21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/compiler': 22.1.6 + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/platform-browser': 22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) tslib: 2.8.1 - '@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))': + '@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))': dependencies: - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) tslib: 2.8.1 optionalDependencies: - '@angular/animations': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) + '@angular/animations': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) - '@angular/router@21.2.19(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2)': + '@angular/router@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/platform-browser': 21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/platform-browser': 22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/service-worker@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2)': + '@angular/service-worker@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) rxjs: 7.8.2 tslib: 2.8.1 @@ -12178,7 +10251,7 @@ snapshots: '@astrojs/compiler-binding-wasm32-wasi@0.3.2(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': dependencies: - '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -12252,13 +10325,13 @@ snapshots: github-slugger: 2.0.0 satteri: 0.10.5 - '@astrojs/mdx@7.0.7(@astrojs/markdown-satteri@0.3.7)(astro@7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.102.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0))': + '@astrojs/mdx@7.0.7(@astrojs/markdown-satteri@0.3.7)(astro@7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0))': dependencies: '@astrojs/internal-helpers': 0.10.4 '@astrojs/markdown-remark': 7.2.4 '@mdx-js/mdx': 3.1.1 acorn: 8.18.0 - astro: 7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.102.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0) + astro: 7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0) es-module-lexer: 2.3.1 estree-util-visit: 2.0.0 hast-util-to-html: 9.0.5 @@ -12302,8 +10375,15 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@8.0.0': + dependencies: + '@babel/helper-validator-identifier': 8.0.4 + js-tokens: 10.0.0 + '@babel/compat-data@7.29.7': {} + '@babel/compat-data@8.0.5': {} + '@babel/core@7.29.7': dependencies: '@babel/code-frame': 7.29.7 @@ -12324,6 +10404,25 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@8.0.1': + dependencies: + '@babel/code-frame': 8.0.0 + '@babel/generator': 8.0.5 + '@babel/helper-compilation-targets': 8.0.5 + '@babel/helpers': 8.0.5 + '@babel/parser': 8.0.5 + '@babel/template': 8.0.0 + '@babel/traverse': 8.0.5 + '@babel/types': 8.0.5 + '@types/gensync': 1.0.5 + convert-source-map: 2.0.0 + empathic: 2.0.1 + gensync: 1.0.0-beta.2 + import-meta-resolve: 4.2.0 + json5: 2.2.3 + obug: 2.1.4 + semver: 7.8.5 + '@babel/generator@7.29.8': dependencies: '@babel/parser': 7.29.8 @@ -12332,14 +10431,23 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.27.3': + '@babel/generator@8.0.5': dependencies: - '@babel/types': 7.29.8 + '@babel/parser': 8.0.5 + '@babel/types': 8.0.5 + '@jridgewell/gen-mapping': 0.4.0-beta.0 + '@jridgewell/trace-mapping': 0.3.31 + '@types/jsesc': 2.5.1 + jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.29.7': dependencies: '@babel/types': 7.29.8 + '@babel/helper-annotate-as-pure@8.0.0': + dependencies: + '@babel/types': 8.0.5 + '@babel/helper-compilation-targets@7.29.7': dependencies: '@babel/compat-data': 7.29.7 @@ -12348,6 +10456,14 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-compilation-targets@8.0.5': + dependencies: + '@babel/compat-data': 8.0.5 + '@babel/helper-validator-option': 8.0.0 + browserslist: 4.28.8 + lru-cache: 11.5.2 + verkit: 0.3.2 + '@babel/helper-create-class-features-plugin@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -12381,6 +10497,8 @@ snapshots: '@babel/helper-globals@7.29.7': {} + '@babel/helper-globals@8.0.0': {} + '@babel/helper-member-expression-to-functions@7.29.7': dependencies: '@babel/traverse': 7.29.8 @@ -12441,12 +10559,18 @@ snapshots: '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-string-parser@8.0.0': {} + '@babel/helper-validator-identifier@7.28.5': {} '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-identifier@8.0.4': {} + '@babel/helper-validator-option@7.29.7': {} + '@babel/helper-validator-option@8.0.0': {} + '@babel/helper-wrap-function@7.29.7': dependencies: '@babel/template': 7.29.7 @@ -12460,10 +10584,19 @@ snapshots: '@babel/template': 7.29.7 '@babel/types': 7.29.8 + '@babel/helpers@8.0.5': + dependencies: + '@babel/template': 8.0.0 + '@babel/types': 8.0.5 + '@babel/parser@7.29.8': dependencies: '@babel/types': 7.29.8 + '@babel/parser@8.0.5': + dependencies: + '@babel/types': 8.0.5 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -13080,6 +11213,12 @@ snapshots: '@babel/parser': 7.29.8 '@babel/types': 7.29.8 + '@babel/template@8.0.0': + dependencies: + '@babel/code-frame': 8.0.0 + '@babel/parser': 8.0.5 + '@babel/types': 8.0.5 + '@babel/traverse@7.29.8': dependencies: '@babel/code-frame': 7.29.7 @@ -13092,11 +11231,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@8.0.5': + dependencies: + '@babel/code-frame': 8.0.0 + '@babel/generator': 8.0.5 + '@babel/helper-globals': 8.0.0 + '@babel/parser': 8.0.5 + '@babel/template': 8.0.0 + '@babel/types': 8.0.5 + obug: 2.1.4 + '@babel/types@7.29.8': dependencies: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@babel/types@8.0.5': + dependencies: + '@babel/helper-string-parser': 8.0.0 + '@babel/helper-validator-identifier': 8.0.4 + '@bcoe/v8-coverage@0.2.3': {} '@bruits/satteri-darwin-arm64@0.10.5': @@ -13130,7 +11284,8 @@ snapshots: '@bruits/satteri-win32-x64-msvc@0.10.5': optional: true - '@bufbuild/protobuf@2.13.0': {} + '@bufbuild/protobuf@2.15.0': + optional: true '@capsizecss/unpack@4.0.0': dependencies: @@ -13148,8 +11303,6 @@ snapshots: fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 - '@colordx/core@5.5.0': {} - '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 @@ -13282,11 +11435,16 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@1.11.2': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + '@emnapi/core@1.11.3': dependencies: '@emnapi/wasi-threads': 1.2.3 tslib: 2.8.1 - optional: true '@emnapi/core@1.4.5': dependencies: @@ -13303,11 +11461,15 @@ snapshots: tslib: 2.8.1 optional: true - '@emnapi/runtime@1.11.3': + '@emnapi/runtime@1.11.2': dependencies: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + '@emnapi/runtime@1.4.5': dependencies: tslib: 2.8.1 @@ -13329,7 +11491,6 @@ snapshots: '@emnapi/wasi-threads@1.2.3': dependencies: tslib: 2.8.1 - optional: true '@esbuild-kit/core-utils@3.3.2': dependencies: @@ -13344,234 +11505,156 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/aix-ppc64@0.28.1': - optional: true - '@esbuild/aix-ppc64@0.28.2': optional: true '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-arm64@0.28.1': - optional: true - '@esbuild/android-arm64@0.28.2': optional: true '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/android-arm@0.28.1': - optional: true - '@esbuild/android-arm@0.28.2': optional: true '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/android-x64@0.28.1': - optional: true - '@esbuild/android-x64@0.28.2': optional: true '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.28.1': - optional: true - '@esbuild/darwin-arm64@0.28.2': optional: true '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/darwin-x64@0.28.1': - optional: true - '@esbuild/darwin-x64@0.28.2': optional: true '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.28.1': - optional: true - '@esbuild/freebsd-arm64@0.28.2': optional: true '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.28.1': - optional: true - '@esbuild/freebsd-x64@0.28.2': optional: true '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/linux-arm64@0.28.1': - optional: true - '@esbuild/linux-arm64@0.28.2': optional: true '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/linux-arm@0.28.1': - optional: true - '@esbuild/linux-arm@0.28.2': optional: true '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/linux-ia32@0.28.1': - optional: true - '@esbuild/linux-ia32@0.28.2': optional: true '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/linux-loong64@0.28.1': - optional: true - '@esbuild/linux-loong64@0.28.2': optional: true '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/linux-mips64el@0.28.1': - optional: true - '@esbuild/linux-mips64el@0.28.2': optional: true '@esbuild/linux-ppc64@0.25.12': optional: true - '@esbuild/linux-ppc64@0.28.1': - optional: true - '@esbuild/linux-ppc64@0.28.2': optional: true '@esbuild/linux-riscv64@0.25.12': optional: true - '@esbuild/linux-riscv64@0.28.1': - optional: true - '@esbuild/linux-riscv64@0.28.2': optional: true '@esbuild/linux-s390x@0.25.12': optional: true - '@esbuild/linux-s390x@0.28.1': - optional: true - '@esbuild/linux-s390x@0.28.2': optional: true '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/linux-x64@0.28.1': - optional: true - '@esbuild/linux-x64@0.28.2': optional: true '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.28.1': - optional: true - '@esbuild/netbsd-arm64@0.28.2': optional: true '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/netbsd-x64@0.28.1': - optional: true - '@esbuild/netbsd-x64@0.28.2': optional: true '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.28.1': - optional: true - '@esbuild/openbsd-arm64@0.28.2': optional: true '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/openbsd-x64@0.28.1': - optional: true - '@esbuild/openbsd-x64@0.28.2': optional: true '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.28.1': - optional: true - '@esbuild/openharmony-arm64@0.28.2': optional: true '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/sunos-x64@0.28.1': - optional: true - '@esbuild/sunos-x64@0.28.2': optional: true '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/win32-arm64@0.28.1': - optional: true - '@esbuild/win32-arm64@0.28.2': optional: true '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/win32-ia32@0.28.1': - optional: true - '@esbuild/win32-ia32@0.28.2': optional: true '@esbuild/win32-x64@0.25.12': optional: true - '@esbuild/win32-x64@0.28.1': - optional: true - '@esbuild/win32-x64@0.28.2': optional: true @@ -13640,8 +11723,6 @@ snapshots: '@fontsource/roboto@5.3.0': {} - '@gar/promise-retry@1.0.3': {} - '@harperfast/extended-iterable@1.0.3': optional: true @@ -13771,128 +11852,141 @@ snapshots: '@img/sharp-win32-x64@0.35.4': optional: true - '@inquirer/ansi@1.0.2': {} + '@inquirer/ansi@2.0.8': {} - '@inquirer/checkbox@4.3.2(@types/node@20.19.9)': + '@inquirer/checkbox@5.2.5(@types/node@20.19.9)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@20.19.9) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@20.19.9) - yoctocolors-cjs: 2.1.3 + '@inquirer/ansi': 2.0.8 + '@inquirer/core': 12.0.3(@types/node@20.19.9) + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.1.1(@types/node@20.19.9) optionalDependencies: '@types/node': 20.19.9 - '@inquirer/confirm@5.1.21(@types/node@20.19.9)': + '@inquirer/confirm@6.1.1(@types/node@20.19.9)': dependencies: - '@inquirer/core': 10.3.2(@types/node@20.19.9) - '@inquirer/type': 3.0.10(@types/node@20.19.9) + '@inquirer/core': 11.2.1(@types/node@20.19.9) + '@inquirer/type': 4.1.1(@types/node@20.19.9) optionalDependencies: '@types/node': 20.19.9 - '@inquirer/core@10.3.2(@types/node@20.19.9)': + '@inquirer/confirm@6.3.2(@types/node@20.19.9)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@20.19.9) + '@inquirer/core': 12.0.3(@types/node@20.19.9) + '@inquirer/type': 4.1.1(@types/node@20.19.9) + optionalDependencies: + '@types/node': 20.19.9 + + '@inquirer/core@11.2.1(@types/node@20.19.9)': + dependencies: + '@inquirer/ansi': 2.0.8 + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.1.1(@types/node@20.19.9) 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': 20.19.9 - '@inquirer/editor@4.2.23(@types/node@20.19.9)': + '@inquirer/core@12.0.3(@types/node@20.19.9)': dependencies: - '@inquirer/core': 10.3.2(@types/node@20.19.9) - '@inquirer/external-editor': 1.0.3(@types/node@20.19.9) - '@inquirer/type': 3.0.10(@types/node@20.19.9) + '@inquirer/ansi': 2.0.8 + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.1.1(@types/node@20.19.9) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 + signal-exit: 4.1.0 optionalDependencies: '@types/node': 20.19.9 - '@inquirer/expand@4.0.23(@types/node@20.19.9)': + '@inquirer/editor@5.3.3(@types/node@20.19.9)': dependencies: - '@inquirer/core': 10.3.2(@types/node@20.19.9) - '@inquirer/type': 3.0.10(@types/node@20.19.9) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 12.0.3(@types/node@20.19.9) + '@inquirer/external-editor': 3.0.5(@types/node@20.19.9) + '@inquirer/type': 4.1.1(@types/node@20.19.9) optionalDependencies: '@types/node': 20.19.9 - '@inquirer/external-editor@1.0.3(@types/node@20.19.9)': + '@inquirer/expand@5.1.5(@types/node@20.19.9)': + dependencies: + '@inquirer/core': 12.0.3(@types/node@20.19.9) + '@inquirer/type': 4.1.1(@types/node@20.19.9) + optionalDependencies: + '@types/node': 20.19.9 + + '@inquirer/external-editor@3.0.5(@types/node@20.19.9)': dependencies: chardet: 2.2.0 iconv-lite: 0.7.3 optionalDependencies: '@types/node': 20.19.9 - '@inquirer/figures@1.0.15': {} + '@inquirer/figures@2.0.9': {} - '@inquirer/input@4.3.1(@types/node@20.19.9)': + '@inquirer/input@5.1.6(@types/node@20.19.9)': dependencies: - '@inquirer/core': 10.3.2(@types/node@20.19.9) - '@inquirer/type': 3.0.10(@types/node@20.19.9) + '@inquirer/core': 12.0.3(@types/node@20.19.9) + '@inquirer/type': 4.1.1(@types/node@20.19.9) optionalDependencies: '@types/node': 20.19.9 - '@inquirer/number@3.0.23(@types/node@20.19.9)': + '@inquirer/number@4.2.3(@types/node@20.19.9)': dependencies: - '@inquirer/core': 10.3.2(@types/node@20.19.9) - '@inquirer/type': 3.0.10(@types/node@20.19.9) + '@inquirer/core': 12.0.3(@types/node@20.19.9) + '@inquirer/type': 4.1.1(@types/node@20.19.9) optionalDependencies: '@types/node': 20.19.9 - '@inquirer/password@4.0.23(@types/node@20.19.9)': + '@inquirer/password@5.2.2(@types/node@20.19.9)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@20.19.9) - '@inquirer/type': 3.0.10(@types/node@20.19.9) + '@inquirer/ansi': 2.0.8 + '@inquirer/core': 12.0.3(@types/node@20.19.9) + '@inquirer/type': 4.1.1(@types/node@20.19.9) optionalDependencies: '@types/node': 20.19.9 - '@inquirer/prompts@7.10.1(@types/node@20.19.9)': + '@inquirer/prompts@8.5.2(@types/node@20.19.9)': dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@20.19.9) - '@inquirer/confirm': 5.1.21(@types/node@20.19.9) - '@inquirer/editor': 4.2.23(@types/node@20.19.9) - '@inquirer/expand': 4.0.23(@types/node@20.19.9) - '@inquirer/input': 4.3.1(@types/node@20.19.9) - '@inquirer/number': 3.0.23(@types/node@20.19.9) - '@inquirer/password': 4.0.23(@types/node@20.19.9) - '@inquirer/rawlist': 4.1.11(@types/node@20.19.9) - '@inquirer/search': 3.2.2(@types/node@20.19.9) - '@inquirer/select': 4.4.2(@types/node@20.19.9) + '@inquirer/checkbox': 5.2.5(@types/node@20.19.9) + '@inquirer/confirm': 6.3.2(@types/node@20.19.9) + '@inquirer/editor': 5.3.3(@types/node@20.19.9) + '@inquirer/expand': 5.1.5(@types/node@20.19.9) + '@inquirer/input': 5.1.6(@types/node@20.19.9) + '@inquirer/number': 4.2.3(@types/node@20.19.9) + '@inquirer/password': 5.2.2(@types/node@20.19.9) + '@inquirer/rawlist': 5.3.5(@types/node@20.19.9) + '@inquirer/search': 4.3.3(@types/node@20.19.9) + '@inquirer/select': 5.2.5(@types/node@20.19.9) optionalDependencies: '@types/node': 20.19.9 - '@inquirer/rawlist@4.1.11(@types/node@20.19.9)': + '@inquirer/rawlist@5.3.5(@types/node@20.19.9)': dependencies: - '@inquirer/core': 10.3.2(@types/node@20.19.9) - '@inquirer/type': 3.0.10(@types/node@20.19.9) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 12.0.3(@types/node@20.19.9) + '@inquirer/type': 4.1.1(@types/node@20.19.9) optionalDependencies: '@types/node': 20.19.9 - '@inquirer/search@3.2.2(@types/node@20.19.9)': + '@inquirer/search@4.3.3(@types/node@20.19.9)': dependencies: - '@inquirer/core': 10.3.2(@types/node@20.19.9) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@20.19.9) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 12.0.3(@types/node@20.19.9) + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.1.1(@types/node@20.19.9) optionalDependencies: '@types/node': 20.19.9 - '@inquirer/select@4.4.2(@types/node@20.19.9)': + '@inquirer/select@5.2.5(@types/node@20.19.9)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@20.19.9) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@20.19.9) - yoctocolors-cjs: 2.1.3 + '@inquirer/ansi': 2.0.8 + '@inquirer/core': 12.0.3(@types/node@20.19.9) + '@inquirer/figures': 2.0.9 + '@inquirer/type': 4.1.1(@types/node@20.19.9) optionalDependencies: '@types/node': 20.19.9 - '@inquirer/type@3.0.10(@types/node@20.19.9)': + '@inquirer/type@4.1.1(@types/node@20.19.9)': optionalDependencies: '@types/node': 20.19.9 @@ -13919,15 +12013,6 @@ snapshots: '@istanbuljs/schema@0.1.6': {} - '@jest/console@30.4.1': - dependencies: - '@jest/types': 30.4.1 - '@types/node': 20.19.9 - chalk: 4.1.2 - jest-message-util: 30.4.1 - jest-util: 30.4.1 - slash: 3.0.0 - '@jest/console@30.5.1': dependencies: '@jest/types': 30.5.1 @@ -13937,7 +12022,7 @@ snapshots: jest-util: 30.5.1 slash: 3.0.0 - '@jest/core@30.5.1(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3))': + '@jest/core@30.5.1(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3))': dependencies: '@jest/console': 30.5.1 '@jest/pattern': 30.5.0 @@ -13953,7 +12038,7 @@ snapshots: fast-json-stable-stringify: 2.1.0 graceful-fs: 4.2.11 jest-changed-files: 30.5.1 - jest-config: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)) + jest-config: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)) jest-haste-map: 30.5.1 jest-message-util: 30.5.1 jest-regex-util: 30.5.0 @@ -13975,21 +12060,8 @@ snapshots: '@jest/diff-sequences@30.0.1': {} - '@jest/diff-sequences@30.4.0': {} - '@jest/diff-sequences@30.5.0': {} - '@jest/environment-jsdom-abstract@30.2.0(jsdom@26.1.0)': - dependencies: - '@jest/environment': 30.2.0 - '@jest/fake-timers': 30.2.0 - '@jest/types': 30.2.0 - '@types/jsdom': 21.1.7 - '@types/node': 20.19.9 - jest-mock: 30.2.0 - jest-util: 30.2.0 - jsdom: 26.1.0 - '@jest/environment-jsdom-abstract@30.5.1(@types/jsdom@21.1.7)(jsdom@26.1.0)': dependencies: '@jest/environment': 30.5.1 @@ -14001,20 +12073,6 @@ snapshots: jest-util: 30.5.1 jsdom: 26.1.0 - '@jest/environment@30.2.0': - dependencies: - '@jest/fake-timers': 30.2.0 - '@jest/types': 30.2.0 - '@types/node': 20.19.9 - jest-mock: 30.2.0 - - '@jest/environment@30.4.1': - dependencies: - '@jest/fake-timers': 30.4.1 - '@jest/types': 30.4.1 - '@types/node': 20.19.9 - jest-mock: 30.4.1 - '@jest/environment@30.5.1': dependencies: '@jest/fake-timers': 30.5.1 @@ -14026,21 +12084,10 @@ snapshots: dependencies: '@jest/get-type': 30.1.0 - '@jest/expect-utils@30.4.1': - dependencies: - '@jest/get-type': 30.1.0 - '@jest/expect-utils@30.5.1': dependencies: '@jest/get-type': 30.5.0 - '@jest/expect@30.4.1': - dependencies: - expect: 30.4.1 - jest-snapshot: 30.4.1 - transitivePeerDependencies: - - supports-color - '@jest/expect@30.5.1': dependencies: expect: 30.5.1 @@ -14048,24 +12095,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@jest/fake-timers@30.2.0': - dependencies: - '@jest/types': 30.2.0 - '@sinonjs/fake-timers': 13.0.5 - '@types/node': 20.19.9 - jest-message-util: 30.2.0 - jest-mock: 30.2.0 - jest-util: 30.2.0 - - '@jest/fake-timers@30.4.1': - dependencies: - '@jest/types': 30.4.1 - '@sinonjs/fake-timers': 15.4.0 - '@types/node': 20.19.9 - jest-message-util: 30.4.1 - jest-mock: 30.4.1 - jest-util: 30.4.1 - '@jest/fake-timers@30.5.1': dependencies: '@jest/types': 30.5.1 @@ -14079,15 +12108,6 @@ snapshots: '@jest/get-type@30.5.0': {} - '@jest/globals@30.4.1': - dependencies: - '@jest/environment': 30.4.1 - '@jest/expect': 30.4.1 - '@jest/types': 30.4.1 - jest-mock: 30.4.1 - transitivePeerDependencies: - - supports-color - '@jest/globals@30.5.1': dependencies: '@jest/environment': 30.5.1 @@ -14102,44 +12122,11 @@ snapshots: '@types/node': 20.19.9 jest-regex-util: 30.0.1 - '@jest/pattern@30.4.0': - dependencies: - '@types/node': 20.19.9 - jest-regex-util: 30.4.0 - '@jest/pattern@30.5.0': dependencies: '@types/node': 20.19.9 jest-regex-util: 30.5.0 - '@jest/reporters@30.4.1': - dependencies: - '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 30.4.1 - '@jest/test-result': 30.4.1 - '@jest/transform': 30.4.1 - '@jest/types': 30.4.1 - '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 20.19.9 - chalk: 4.1.2 - collect-v8-coverage: 1.0.3 - exit-x: 0.2.2 - glob: 10.5.0 - graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 - istanbul-reports: 3.2.0 - jest-message-util: 30.4.1 - jest-util: 30.4.1 - jest-worker: 30.4.1 - slash: 3.0.0 - string-length: 4.0.2 - v8-to-istanbul: 9.3.0 - transitivePeerDependencies: - - supports-color - '@jest/reporters@30.5.1': dependencies: '@bcoe/v8-coverage': 0.2.3 @@ -14172,21 +12159,10 @@ snapshots: dependencies: '@sinclair/typebox': 0.34.46 - '@jest/schemas@30.4.1': - dependencies: - '@sinclair/typebox': 0.34.52 - '@jest/schemas@30.5.0': dependencies: '@sinclair/typebox': 0.34.52 - '@jest/snapshot-utils@30.4.1': - dependencies: - '@jest/types': 30.4.1 - chalk: 4.1.2 - graceful-fs: 4.2.11 - natural-compare: 1.4.0 - '@jest/snapshot-utils@30.5.1': dependencies: '@jest/types': 30.5.1 @@ -14194,12 +12170,6 @@ snapshots: graceful-fs: 4.2.11 natural-compare: 1.4.0 - '@jest/source-map@30.0.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - callsites: 3.1.0 - graceful-fs: 4.2.11 - '@jest/source-map@30.5.0': dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -14207,13 +12177,6 @@ snapshots: convert-source-map: 2.0.0 graceful-fs: 4.2.11 - '@jest/test-result@30.4.1': - dependencies: - '@jest/console': 30.4.1 - '@jest/types': 30.4.1 - '@types/istanbul-lib-coverage': 2.0.6 - collect-v8-coverage: 1.0.3 - '@jest/test-result@30.5.1': dependencies: '@jest/console': 30.5.1 @@ -14221,13 +12184,6 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 collect-v8-coverage: 1.0.3 - '@jest/test-sequencer@30.4.1': - dependencies: - '@jest/test-result': 30.4.1 - graceful-fs: 4.2.11 - jest-haste-map: 30.4.1 - slash: 3.0.0 - '@jest/test-sequencer@30.5.1': dependencies: '@jest/test-result': 30.5.1 @@ -14235,25 +12191,6 @@ snapshots: jest-haste-map: 30.5.1 slash: 3.0.0 - '@jest/transform@30.4.1': - dependencies: - '@babel/core': 7.29.7 - '@jest/types': 30.4.1 - '@jridgewell/trace-mapping': 0.3.31 - babel-plugin-istanbul: 7.0.1 - chalk: 4.1.2 - convert-source-map: 2.0.0 - fast-json-stable-stringify: 2.1.0 - graceful-fs: 4.2.11 - jest-haste-map: 30.4.1 - jest-regex-util: 30.4.0 - jest-util: 30.4.1 - pirates: 4.0.7 - slash: 3.0.0 - write-file-atomic: 5.0.1 - transitivePeerDependencies: - - supports-color - '@jest/transform@30.5.1': dependencies: '@babel/core': 7.29.7 @@ -14283,16 +12220,6 @@ snapshots: '@types/yargs': 17.0.35 chalk: 4.1.2 - '@jest/types@30.4.1': - dependencies: - '@jest/pattern': 30.4.0 - '@jest/schemas': 30.4.1 - '@types/istanbul-lib-coverage': 2.0.6 - '@types/istanbul-reports': 3.0.4 - '@types/node': 20.19.9 - '@types/yargs': 17.0.35 - chalk: 4.1.2 - '@jest/types@30.5.1': dependencies: '@jest/pattern': 30.5.0 @@ -14308,6 +12235,11 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/gen-mapping@0.4.0-beta.0': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/remapping@2.3.5': dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -14322,6 +12254,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/sourcemap-codec@1.6.0': {} + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -14332,163 +12266,33 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': + '@listr2/prompt-adapter-inquirer@4.2.5(@inquirer/prompts@8.5.2(@types/node@20.19.9))(@types/node@20.19.9)(listr2@11.0.0)': dependencies: - tslib: 2.8.1 - - '@jsonjoy.com/base64@17.67.0(tslib@2.8.1)': - dependencies: - tslib: 2.8.1 - - '@jsonjoy.com/buffers@1.2.1(tslib@2.8.1)': - dependencies: - tslib: 2.8.1 - - '@jsonjoy.com/buffers@17.67.0(tslib@2.8.1)': - dependencies: - tslib: 2.8.1 - - '@jsonjoy.com/codegen@1.0.0(tslib@2.8.1)': - dependencies: - tslib: 2.8.1 - - '@jsonjoy.com/codegen@17.67.0(tslib@2.8.1)': - dependencies: - tslib: 2.8.1 - - '@jsonjoy.com/fs-core@4.68.0(tslib@2.8.1)': - dependencies: - '@jsonjoy.com/fs-node-builtins': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-node-utils': 4.68.0(tslib@2.8.1) - thingies: 2.6.1(tslib@2.8.1) - tslib: 2.8.1 - - '@jsonjoy.com/fs-fsa@4.68.0(tslib@2.8.1)': - dependencies: - '@jsonjoy.com/fs-core': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-node-builtins': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-node-utils': 4.68.0(tslib@2.8.1) - thingies: 2.6.1(tslib@2.8.1) - tslib: 2.8.1 - - '@jsonjoy.com/fs-node-builtins@4.68.0(tslib@2.8.1)': - dependencies: - tslib: 2.8.1 - - '@jsonjoy.com/fs-node-to-fsa@4.68.0(tslib@2.8.1)': - dependencies: - '@jsonjoy.com/fs-fsa': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-node-builtins': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-node-utils': 4.68.0(tslib@2.8.1) - tslib: 2.8.1 - - '@jsonjoy.com/fs-node-utils@4.68.0(tslib@2.8.1)': - dependencies: - '@jsonjoy.com/fs-node-builtins': 4.68.0(tslib@2.8.1) - glob-to-regex.js: 1.2.0(tslib@2.8.1) - tslib: 2.8.1 - - '@jsonjoy.com/fs-node@4.68.0(tslib@2.8.1)': - dependencies: - '@jsonjoy.com/fs-core': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-node-builtins': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-node-utils': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-print': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-snapshot': 4.68.0(tslib@2.8.1) - glob-to-regex.js: 1.2.0(tslib@2.8.1) - thingies: 2.6.1(tslib@2.8.1) - tslib: 2.8.1 - - '@jsonjoy.com/fs-print@4.68.0(tslib@2.8.1)': - dependencies: - '@jsonjoy.com/fs-node-utils': 4.68.0(tslib@2.8.1) - tree-dump: 1.1.0(tslib@2.8.1) - tslib: 2.8.1 - - '@jsonjoy.com/fs-snapshot@4.68.0(tslib@2.8.1)': - dependencies: - '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) - '@jsonjoy.com/fs-node-utils': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/json-pack': 17.67.0(tslib@2.8.1) - '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) - tslib: 2.8.1 - - '@jsonjoy.com/json-pack@1.21.0(tslib@2.8.1)': - dependencies: - '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) - '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) - '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) - '@jsonjoy.com/json-pointer': 1.0.2(tslib@2.8.1) - '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) - hyperdyperid: 1.2.0 - thingies: 2.6.1(tslib@2.8.1) - tree-dump: 1.1.0(tslib@2.8.1) - tslib: 2.8.1 - - '@jsonjoy.com/json-pack@17.67.0(tslib@2.8.1)': - dependencies: - '@jsonjoy.com/base64': 17.67.0(tslib@2.8.1) - '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) - '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) - '@jsonjoy.com/json-pointer': 17.67.0(tslib@2.8.1) - '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) - hyperdyperid: 1.2.0 - thingies: 2.6.1(tslib@2.8.1) - tree-dump: 1.1.0(tslib@2.8.1) - tslib: 2.8.1 - - '@jsonjoy.com/json-pointer@1.0.2(tslib@2.8.1)': - dependencies: - '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) - '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) - tslib: 2.8.1 - - '@jsonjoy.com/json-pointer@17.67.0(tslib@2.8.1)': - dependencies: - '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) - tslib: 2.8.1 - - '@jsonjoy.com/util@1.9.0(tslib@2.8.1)': - dependencies: - '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) - '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) - tslib: 2.8.1 - - '@jsonjoy.com/util@17.67.0(tslib@2.8.1)': - dependencies: - '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) - '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) - tslib: 2.8.1 - - '@leichtgewicht/ip-codec@2.0.5': {} - - '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@20.19.9))(@types/node@20.19.9)(listr2@9.0.5)': - dependencies: - '@inquirer/prompts': 7.10.1(@types/node@20.19.9) - '@inquirer/type': 3.0.10(@types/node@20.19.9) - listr2: 9.0.5 + '@inquirer/prompts': 8.5.2(@types/node@20.19.9) + '@inquirer/type': 4.1.1(@types/node@20.19.9) + listr2: 11.0.0 transitivePeerDependencies: - '@types/node' - '@lmdb/lmdb-darwin-arm64@3.5.1': + '@lmdb/lmdb-darwin-arm64@3.5.6': optional: true - '@lmdb/lmdb-darwin-x64@3.5.1': + '@lmdb/lmdb-darwin-x64@3.5.6': optional: true - '@lmdb/lmdb-linux-arm64@3.5.1': + '@lmdb/lmdb-linux-arm64@3.5.6': optional: true - '@lmdb/lmdb-linux-arm@3.5.1': + '@lmdb/lmdb-linux-arm@3.5.6': optional: true - '@lmdb/lmdb-linux-x64@3.5.1': + '@lmdb/lmdb-linux-x64@3.5.6': optional: true - '@lmdb/lmdb-win32-arm64@3.5.1': + '@lmdb/lmdb-win32-arm64@3.5.6': optional: true - '@lmdb/lmdb-win32-x64@3.5.1': + '@lmdb/lmdb-win32-x64@3.5.6': optional: true '@malept/cross-spawn-promise@2.0.0': @@ -14534,11 +12338,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@modelcontextprotocol/sdk@1.30.0(zod@4.3.6)': + '@modelcontextprotocol/sdk@1.30.0(zod@4.4.3)': dependencies: '@hono/node-server': 1.19.17(hono@4.12.34) - 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) content-type: 1.0.5 cors: 2.8.6 cross-spawn: 7.0.6 @@ -14551,170 +12355,11 @@ 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.3 + zod-to-json-schema: 3.25.2(zod@4.4.3) transitivePeerDependencies: - supports-color - '@module-federation/bridge-react-webpack-plugin@2.8.2': - dependencies: - '@module-federation/sdk': 2.8.2 - - '@module-federation/cli@2.8.2(typescript@5.9.3)': - dependencies: - '@module-federation/dts-plugin': 2.8.2(typescript@5.9.3) - '@module-federation/sdk': 2.8.2 - commander: 11.1.0 - jiti: 2.4.2 - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - vue-tsc - - '@module-federation/dts-plugin@2.8.2(typescript@5.9.3)': - dependencies: - '@module-federation/error-codes': 2.8.2 - '@module-federation/managers': 2.8.2 - '@module-federation/sdk': 2.8.2 - '@module-federation/third-party-dts-extractor': 2.8.2 - adm-zip: 0.6.0 - isomorphic-ws: 5.0.0(ws@8.21.0) - typescript: 5.9.3 - undici: 7.29.0 - ws: 8.21.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@module-federation/enhanced@2.8.2(@rspack/core@1.6.8(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26))': - dependencies: - '@module-federation/bridge-react-webpack-plugin': 2.8.2 - '@module-federation/cli': 2.8.2(typescript@5.9.3) - '@module-federation/dts-plugin': 2.8.2(typescript@5.9.3) - '@module-federation/error-codes': 2.8.2 - '@module-federation/inject-external-runtime-core-plugin': 2.8.2(@module-federation/runtime-tools@2.8.2) - '@module-federation/managers': 2.8.2 - '@module-federation/manifest': 2.8.2(typescript@5.9.3) - '@module-federation/rspack': 2.8.2(@rspack/core@1.6.8(@swc/helpers@0.5.23))(typescript@5.9.3) - '@module-federation/runtime-tools': 2.8.2 - '@module-federation/sdk': 2.8.2 - '@module-federation/webpack-bundler-runtime': 2.8.2 - schema-utils: 4.3.0 - tapable: 2.3.0 - optionalDependencies: - typescript: 5.9.3 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - transitivePeerDependencies: - - '@rspack/core' - - bufferutil - - utf-8-validate - - '@module-federation/error-codes@0.21.6': {} - - '@module-federation/error-codes@2.8.2': {} - - '@module-federation/inject-external-runtime-core-plugin@2.8.2(@module-federation/runtime-tools@2.8.2)': - dependencies: - '@module-federation/runtime-tools': 2.8.2 - - '@module-federation/managers@2.8.2': - dependencies: - '@module-federation/sdk': 2.8.2 - - '@module-federation/manifest@2.8.2(typescript@5.9.3)': - dependencies: - '@module-federation/dts-plugin': 2.8.2(typescript@5.9.3) - '@module-federation/managers': 2.8.2 - '@module-federation/sdk': 2.8.2 - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - vue-tsc - - '@module-federation/node@2.7.49(@rspack/core@1.6.8(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26))': - dependencies: - '@module-federation/enhanced': 2.8.2(@rspack/core@1.6.8(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - '@module-federation/runtime': 2.8.2 - '@module-federation/sdk': 2.8.2 - encoding: 0.1.13 - node-fetch: 2.7.0(encoding@0.1.13) - tapable: 2.3.0 - optionalDependencies: - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - transitivePeerDependencies: - - '@rspack/core' - - bufferutil - - typescript - - utf-8-validate - - vue-tsc - - '@module-federation/rspack@2.8.2(@rspack/core@1.6.8(@swc/helpers@0.5.23))(typescript@5.9.3)': - dependencies: - '@module-federation/bridge-react-webpack-plugin': 2.8.2 - '@module-federation/dts-plugin': 2.8.2(typescript@5.9.3) - '@module-federation/inject-external-runtime-core-plugin': 2.8.2(@module-federation/runtime-tools@2.8.2) - '@module-federation/managers': 2.8.2 - '@module-federation/manifest': 2.8.2(typescript@5.9.3) - '@module-federation/runtime-tools': 2.8.2 - '@module-federation/sdk': 2.8.2 - '@rspack/core': 1.6.8(@swc/helpers@0.5.23) - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@module-federation/runtime-core@0.21.6': - dependencies: - '@module-federation/error-codes': 0.21.6 - '@module-federation/sdk': 0.21.6 - - '@module-federation/runtime-core@2.8.2': - dependencies: - '@module-federation/error-codes': 2.8.2 - '@module-federation/sdk': 2.8.2 - - '@module-federation/runtime-tools@0.21.6': - dependencies: - '@module-federation/runtime': 0.21.6 - '@module-federation/webpack-bundler-runtime': 0.21.6 - - '@module-federation/runtime-tools@2.8.2': - dependencies: - '@module-federation/runtime': 2.8.2 - '@module-federation/webpack-bundler-runtime': 2.8.2 - - '@module-federation/runtime@0.21.6': - dependencies: - '@module-federation/error-codes': 0.21.6 - '@module-federation/runtime-core': 0.21.6 - '@module-federation/sdk': 0.21.6 - - '@module-federation/runtime@2.8.2': - dependencies: - '@module-federation/error-codes': 2.8.2 - '@module-federation/runtime-core': 2.8.2 - '@module-federation/sdk': 2.8.2 - - '@module-federation/sdk@0.21.6': {} - - '@module-federation/sdk@2.8.2': {} - - '@module-federation/third-party-dts-extractor@2.8.2': {} - - '@module-federation/webpack-bundler-runtime@0.21.6': - dependencies: - '@module-federation/runtime': 0.21.6 - '@module-federation/sdk': 0.21.6 - - '@module-federation/webpack-bundler-runtime@2.8.2': - dependencies: - '@module-federation/error-codes': 2.8.2 - '@module-federation/runtime': 2.8.2 - '@module-federation/sdk': 2.8.2 - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': optional: true @@ -14813,32 +12458,18 @@ snapshots: optional: true '@napi-rs/wasm-runtime@0.2.4': - dependencies: - '@emnapi/core': 1.4.5 - '@emnapi/runtime': 1.4.5 - '@tybys/wasm-util': 0.9.0 - - '@napi-rs/wasm-runtime@1.0.7': dependencies: '@emnapi/core': 1.11.3 '@emnapi/runtime': 1.11.3 - '@tybys/wasm-util': 0.10.3 - optional: true + '@tybys/wasm-util': 0.9.0 - '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@napi-rs/wasm-runtime@1.2.3(@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.3 optional: true - '@napi-rs/wasm-runtime@1.2.2(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': - dependencies: - '@emnapi/core': 1.11.3 - '@emnapi/runtime': 1.11.3 - '@tybys/wasm-util': 0.10.3 - optional: true - '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: '@emnapi/core': 1.11.1 @@ -14846,74 +12477,87 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true - '@ngrx/effects@21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@ngrx/store@21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(rxjs@7.8.2)': + '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2)': dependencies: - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) - '@ngrx/store': 21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': + dependencies: + '@emnapi/core': 1.11.3 + '@emnapi/runtime': 1.11.3 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@ngrx/effects@22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@ngrx/store@22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(rxjs@7.8.2)': + dependencies: + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) + '@ngrx/store': 22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) rxjs: 7.8.2 tslib: 2.8.1 - '@ngrx/entity@21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@ngrx/store@21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(rxjs@7.8.2)': + '@ngrx/entity@22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@ngrx/store@22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) - '@ngrx/store': 21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) + '@ngrx/store': 22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) rxjs: 7.8.2 tslib: 2.8.1 - '@ngrx/eslint-plugin@21.1.1(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript-eslint@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(typescript@5.9.3)': + '@ngrx/eslint-plugin@22.0.1(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript-eslint@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(typescript@6.0.3)': dependencies: - '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) eslint: 9.39.2(jiti@1.21.7) - strip-json-comments: 3.1.1 - typescript: 5.9.3 - typescript-eslint: 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + typescript: 6.0.3 + typescript-eslint: 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) - '@ngrx/router-store@21.1.1(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/router@21.2.19(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(@ngrx/store@21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(rxjs@7.8.2)': + '@ngrx/router-store@22.0.1(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/router@22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2))(@ngrx/store@22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/router': 21.2.19(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) - '@ngrx/store': 21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/router': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) + '@ngrx/store': 22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) rxjs: 7.8.2 tslib: 2.8.1 - '@ngrx/signals@21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2)': + '@ngrx/signals@22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) tslib: 2.8.1 optionalDependencies: rxjs: 7.8.2 - '@ngrx/store-devtools@21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@ngrx/store@21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(rxjs@7.8.2)': + '@ngrx/store-devtools@22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@ngrx/store@22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) - '@ngrx/store': 21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) + '@ngrx/store': 22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) rxjs: 7.8.2 tslib: 2.8.1 - '@ngrx/store@21.1.1(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2)': + '@ngrx/store@22.0.1(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) rxjs: 7.8.2 tslib: 2.8.1 - '@ngx-pwa/local-storage@21.0.0(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2)': + '@ngx-pwa/local-storage@22.0.0(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) rxjs: 7.8.2 tslib: 2.8.1 - '@ngx-translate/core@16.0.4(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))': + '@ngx-translate/core@16.0.4(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))': dependencies: - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) tslib: 2.8.1 - '@ngx-translate/http-loader@16.0.1(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))': + '@ngx-translate/http-loader@16.0.1(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))': dependencies: - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) tslib: 2.8.1 '@noble/hashes@1.4.0': {} @@ -14993,144 +12637,56 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@npmcli/agent@4.0.2': + '@nx/angular@23.2.1(e86c4a4fb085aa7a209327ce2b2f688a)': dependencies: - agent-base: 7.1.4 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 - lru-cache: 11.5.2 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - - '@npmcli/fs@5.0.0': - dependencies: - 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.2 - npm-pick-manifest: 11.0.3 - proc-log: 6.1.0 - semver: 7.7.4 - which: 6.0.1 - - '@npmcli/installed-package-contents@4.0.0': - dependencies: - npm-bundled: 5.0.0 - npm-normalize-package-bin: 5.0.0 - - '@npmcli/node-gyp@5.0.0': {} - - '@npmcli/package-json@7.0.5': - dependencies: - '@npmcli/git': 7.0.2 - glob: 13.0.6 - hosted-git-info: 9.0.3 - json-parse-even-better-errors: 5.0.0 - proc-log: 6.1.0 - semver: 7.7.4 - spdx-expression-parse: 4.0.0 - - '@npmcli/promise-spawn@9.0.1': - dependencies: - which: 6.0.1 - - '@npmcli/redact@4.0.0': {} - - '@npmcli/run-script@10.0.4': - dependencies: - '@npmcli/node-gyp': 5.0.0 - '@npmcli/package-json': 7.0.5 - '@npmcli/promise-spawn': 9.0.1 - node-gyp: 12.4.0 - proc-log: 6.1.0 - - '@nx/angular@22.7.8(9da22baaa942ea628a4531574667d706)': - dependencies: - '@angular-devkit/core': 21.2.19(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.21(chokidar@5.0.0) - '@nx/devkit': 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/eslint': 22.7.8(c6268304c3e993c057e90202eca80e3c) - '@nx/js': 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/module-federation': 22.7.8(c3c8e52b0839abcbeddb0667e71eb5e2) - '@nx/rspack': 22.7.8(3579752277ba60d7d0ceb886096052bc) - '@nx/web': 22.7.8(e36557eae963c5bd5b3bad2c1c9ad54a) - '@nx/webpack': 22.7.8(@babel/traverse@7.29.8)(@rspack/core@1.6.8(@swc/helpers@0.5.23))(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(typescript@5.9.3) - '@nx/workspace': 22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)) - '@phenomnomnominal/tsquery': 6.2.0(typescript@5.9.3) - '@schematics/angular': 21.2.19(chokidar@5.0.0) - '@typescript-eslint/type-utils': 8.66.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - enquirer: 2.3.6 + '@angular-devkit/core': 22.1.8(chokidar@5.0.0) + '@angular-devkit/schematics': 22.1.8(chokidar@5.0.0) + '@nx/devkit': 23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/eslint': 23.2.1(@babel/traverse@7.29.8)(@nx/jest@23.2.1(72e9877c63da59ac93e1f6ba5cf055e8))(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.2(jiti@1.21.7))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/js': 23.2.1(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/web': 23.2.1(f806492ec758094179990070b023c41c) + '@phenomnomnominal/tsquery': 6.2.0(typescript@6.0.3) + '@schematics/angular': 22.1.8(chokidar@5.0.0) + '@typescript-eslint/type-utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + jsonc-parser: 3.3.1 magic-string: 0.30.21 picocolors: 1.1.1 picomatch: 4.0.4 rxjs: 7.8.2 semver: 7.8.5 tslib: 2.8.1 - webpack-merge: 5.10.0 optionalDependencies: - '@angular/build': 21.2.19(@angular/compiler-cli@21.2.19(@angular/compiler@21.2.19)(typescript@5.9.3))(@angular/compiler@21.2.19)(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/service-worker@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@20.19.9)(chokidar@5.0.0)(jiti@1.21.7)(less@4.5.1)(lightningcss@1.33.0)(postcss@8.5.26)(sass-embedded@1.100.0)(tailwindcss@3.4.19(tsx@4.23.13)(yaml@2.9.0))(terser@5.49.2)(tslib@2.8.1)(tsx@4.23.13)(typescript@5.9.3)(yaml@2.9.0) + '@angular/build': 22.1.8(@angular/compiler-cli@22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3))(@angular/compiler@22.1.6)(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/service-worker@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@types/node@20.19.9)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(jiti@1.21.7)(less@4.5.1)(postcss@8.5.26)(rollup@4.59.0)(sass-embedded@1.100.0)(tailwindcss@3.4.19(tsx@4.23.13)(yaml@2.9.0))(terser@5.49.2)(tslib@2.8.1)(tsx@4.23.13)(typescript@6.0.3)(yaml@2.9.0) + '@nx/playwright': 23.2.1(@babel/traverse@7.29.8)(@nx/jest@23.2.1(72e9877c63da59ac93e1f6ba5cf055e8))(@playwright/test@1.62.1)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.2(jiti@1.21.7))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + webpack-merge: 5.10.0 transitivePeerDependencies: - '@babel/traverse' - - '@minify-html/node' - - '@module-federation/enhanced' - - '@module-federation/node' - - '@nx/cypress' - '@nx/jest' - - '@nx/playwright' - '@nx/vite' - - '@parcel/css' - - '@rspack/core' - '@swc-node/register' + - '@swc/cli' - '@swc/core' - - '@swc/css' - - '@swc/helpers' - - '@swc/html' - - '@typescript/native-preview' - '@zkochan/js-yaml' - - bufferutil - - clean-css - - cssnano - - csso - debug - - esbuild - eslint - - html-minifier-terser - - html-webpack-plugin - - less - - lightningcss - - node-sass - nx - - postcss - - react-refresh - supports-color - typescript - - uglify-js - - utf-8-validate - verdaccio - - vue-tsc - - webpack-cli - - webpack-hot-middleware - '@nx/devkit@22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))': + '@nx/devkit@23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))': dependencies: - '@zkochan/js-yaml': 0.0.7 ejs: 5.0.1 - enquirer: 2.3.6 minimatch: 10.2.5 - nx: 22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)) + nx: 23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)) semver: 7.8.5 tslib: 2.8.1 yargs-parser: 21.1.1 - '@nx/esbuild@22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))': + '@nx/esbuild@23.2.1(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))': dependencies: - '@nx/devkit': 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/js': 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/devkit': 23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/js': 23.2.1(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) picocolors: 1.1.1 tinyglobby: 0.2.17 tsconfig-paths: 4.2.0 @@ -15140,30 +12696,32 @@ snapshots: transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' + - '@swc/cli' - '@swc/core' - nx - supports-color - verdaccio - '@nx/eslint-plugin@22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(typescript@5.9.3)': + '@nx/eslint-plugin@23.2.1(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@1.21.7)))(eslint@9.39.2(jiti@1.21.7))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(typescript@6.0.3)': dependencies: - '@nx/devkit': 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/js': 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@phenomnomnominal/tsquery': 6.2.0(typescript@5.9.3) - '@typescript-eslint/parser': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/type-utils': 8.66.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - chalk: 4.1.2 + '@nx/devkit': 23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/js': 23.2.1(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@phenomnomnominal/tsquery': 6.2.0(typescript@6.0.3) + '@typescript-eslint/type-utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) confusing-browser-globals: 1.0.11 globals: 17.9.0 jsonc-eslint-parser: 2.4.2 + picocolors: 1.1.1 semver: 7.8.5 tslib: 2.8.1 optionalDependencies: + '@typescript-eslint/parser': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) eslint-config-prettier: 10.1.8(eslint@9.39.2(jiti@1.21.7)) transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' + - '@swc/cli' - '@swc/core' - eslint - nx @@ -15171,45 +12729,51 @@ snapshots: - typescript - verdaccio - '@nx/eslint@22.7.8(c6268304c3e993c057e90202eca80e3c)': + '@nx/eslint@23.2.1(@babel/traverse@7.29.8)(@nx/jest@23.2.1(72e9877c63da59ac93e1f6ba5cf055e8))(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.2(jiti@1.21.7))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))': dependencies: - '@nx/devkit': 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/js': 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/devkit': 23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/js': 23.2.1(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) eslint: 9.39.2(jiti@1.21.7) semver: 7.8.5 tslib: 2.8.1 - typescript: 5.9.3 + typescript: 6.0.3 optionalDependencies: - '@nx/jest': 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3))(typescript@5.9.3) + '@nx/jest': 23.2.1(72e9877c63da59ac93e1f6ba5cf055e8) '@zkochan/js-yaml': 0.0.7 transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' + - '@swc/cli' - '@swc/core' - nx - supports-color - verdaccio - '@nx/jest@22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3))(typescript@5.9.3)': + '@nx/jest@23.2.1(72e9877c63da59ac93e1f6ba5cf055e8)': dependencies: - '@jest/reporters': 30.4.1 - '@jest/test-result': 30.4.1 - '@nx/devkit': 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/js': 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@phenomnomnominal/tsquery': 6.2.0(typescript@5.9.3) + '@jest/reporters': 30.5.1 + '@jest/test-result': 30.5.1 + '@nx/devkit': 23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/js': 23.2.1(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@phenomnomnominal/tsquery': 6.2.0(typescript@6.0.3) identity-obj-proxy: 3.0.0 - jest-config: 30.4.2(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)) - jest-resolve: 30.4.1 + jest-config: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)) + jest-resolve: 30.5.1 jest-util: 30.5.1 + jsonc-parser: 3.3.1 minimatch: 10.2.5 picocolors: 1.1.1 resolve.exports: 2.0.3 semver: 7.8.5 tslib: 2.8.1 yargs-parser: 21.1.1 + optionalDependencies: + jest: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)) + ts-jest: 29.4.12(@babel/core@7.29.7)(@jest/transform@30.5.1)(@jest/types@30.5.1)(babel-jest@30.5.1(@babel/core@7.29.7))(esbuild@0.28.2)(jest-util@30.5.1)(jest@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)))(typescript@6.0.3) transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' + - '@swc/cli' - '@swc/core' - '@types/node' - babel-plugin-macros @@ -15221,17 +12785,20 @@ snapshots: - typescript - verdaccio - '@nx/js@22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))': + '@nx/js@23.2.1(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-proposal-decorators': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-class-properties': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-class-static-block': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-private-methods': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-private-property-in-object': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-runtime': 7.29.7(@babel/core@7.29.7) '@babel/preset-env': 7.29.7(@babel/core@7.29.7) '@babel/preset-typescript': 7.29.7(@babel/core@7.29.7) '@babel/runtime': 7.29.7 - '@nx/devkit': 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/workspace': 22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)) + '@nx/devkit': 23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/workspace': 23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)) '@zkochan/js-yaml': 0.0.7 babel-plugin-const-enum: 1.2.0(@babel/core@7.29.7) babel-plugin-macros: 3.1.0 @@ -15239,9 +12806,9 @@ snapshots: chalk: 4.1.2 columnify: 1.6.0 detect-port: 2.1.0 - ignore: 7.0.6 + ignore: 7.0.8 js-tokens: 4.0.0 - jsonc-parser: 3.2.0 + jsonc-parser: 3.3.1 npm-run-path: 4.0.1 picocolors: 1.1.1 picomatch: 4.0.4 @@ -15256,88 +12823,44 @@ snapshots: - nx - supports-color - '@nx/module-federation@22.7.8(c3c8e52b0839abcbeddb0667e71eb5e2)': + '@nx/nx-darwin-arm64@23.2.1': + optional: true + + '@nx/nx-darwin-x64@23.2.1': + optional: true + + '@nx/nx-freebsd-x64@23.2.1': + optional: true + + '@nx/nx-linux-arm-gnueabihf@23.2.1': + optional: true + + '@nx/nx-linux-arm64-gnu@23.2.1': + optional: true + + '@nx/nx-linux-arm64-musl@23.2.1': + optional: true + + '@nx/nx-linux-x64-gnu@23.2.1': + optional: true + + '@nx/nx-linux-x64-musl@23.2.1': + optional: true + + '@nx/nx-win32-arm64-msvc@23.2.1': + optional: true + + '@nx/nx-win32-x64-msvc@23.2.1': + optional: true + + '@nx/playwright@23.2.1(@babel/traverse@7.29.8)(@nx/jest@23.2.1(72e9877c63da59ac93e1f6ba5cf055e8))(@playwright/test@1.62.1)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.2(jiti@1.21.7))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))': dependencies: - '@module-federation/enhanced': 2.8.2(@rspack/core@1.6.8(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - '@module-federation/node': 2.7.49(@rspack/core@1.6.8(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - '@module-federation/sdk': 2.8.2 - '@nx/devkit': 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/js': 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/web': 22.7.8(e36557eae963c5bd5b3bad2c1c9ad54a) - '@rspack/core': 1.6.8(@swc/helpers@0.5.23) - express: 4.22.2 - http-proxy-middleware: 3.0.7 - picocolors: 1.1.1 - tslib: 2.8.1 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - transitivePeerDependencies: - - '@babel/traverse' - - '@minify-html/node' - - '@nx/cypress' - - '@nx/eslint' - - '@nx/jest' - - '@nx/playwright' - - '@nx/vite' - - '@nx/webpack' - - '@swc-node/register' - - '@swc/core' - - '@swc/css' - - '@swc/helpers' - - '@swc/html' - - bufferutil - - clean-css - - cssnano - - csso - - debug - - esbuild - - html-minifier-terser - - lightningcss - - nx - - postcss - - supports-color - - typescript - - uglify-js - - utf-8-validate - - verdaccio - - vue-tsc - - webpack-cli - - '@nx/nx-darwin-arm64@22.7.8': - optional: true - - '@nx/nx-darwin-x64@22.7.8': - optional: true - - '@nx/nx-freebsd-x64@22.7.8': - optional: true - - '@nx/nx-linux-arm-gnueabihf@22.7.8': - optional: true - - '@nx/nx-linux-arm64-gnu@22.7.8': - optional: true - - '@nx/nx-linux-arm64-musl@22.7.8': - optional: true - - '@nx/nx-linux-x64-gnu@22.7.8': - optional: true - - '@nx/nx-linux-x64-musl@22.7.8': - optional: true - - '@nx/nx-win32-arm64-msvc@22.7.8': - optional: true - - '@nx/nx-win32-x64-msvc@22.7.8': - optional: true - - '@nx/playwright@22.7.8(b0b327483e86e3b4312dfd51a3eacfc2)': - dependencies: - '@nx/devkit': 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/eslint': 22.7.8(c6268304c3e993c057e90202eca80e3c) - '@nx/js': 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/devkit': 23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/eslint': 23.2.1(@babel/traverse@7.29.8)(@nx/jest@23.2.1(72e9877c63da59ac93e1f6ba5cf055e8))(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.2(jiti@1.21.7))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/js': 23.2.1(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + https-proxy-agent: 7.0.6 minimatch: 10.2.5 + semver: 7.8.5 tslib: 2.8.1 optionalDependencies: '@playwright/test': 1.62.1 @@ -15345,6 +12868,7 @@ snapshots: - '@babel/traverse' - '@nx/jest' - '@swc-node/register' + - '@swc/cli' - '@swc/core' - '@zkochan/js-yaml' - eslint @@ -15352,171 +12876,34 @@ snapshots: - supports-color - verdaccio - '@nx/rspack@22.7.8(3579752277ba60d7d0ceb886096052bc)': + '@nx/web@23.2.1(f806492ec758094179990070b023c41c)': dependencies: - '@module-federation/enhanced': 2.8.2(@rspack/core@1.6.8(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - '@module-federation/node': 2.7.49(@rspack/core@1.6.8(@swc/helpers@0.5.23))(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - '@nx/devkit': 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/js': 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/module-federation': 22.7.8(c3c8e52b0839abcbeddb0667e71eb5e2) - '@nx/web': 22.7.8(e36557eae963c5bd5b3bad2c1c9ad54a) - '@phenomnomnominal/tsquery': 6.2.0(typescript@5.9.3) - '@rspack/core': 1.6.8(@swc/helpers@0.5.23) - '@rspack/dev-server': 1.2.1(@rspack/core@1.6.8(@swc/helpers@0.5.23))(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - '@rspack/plugin-react-refresh': 1.6.2(react-refresh@0.18.0) - autoprefixer: 10.5.4(postcss@8.5.26) - browserslist: 4.28.8 - css-loader: 6.11.0(@rspack/core@1.6.8(@swc/helpers@0.5.23))(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - enquirer: 2.3.6 - express: 4.22.2 - http-proxy-middleware: 3.0.7 - less-loader: 12.3.3(@rspack/core@1.6.8(@swc/helpers@0.5.23))(less@4.5.1)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - license-webpack-plugin: 4.0.2(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - loader-utils: 2.0.4 - parse5: 4.0.0 - picocolors: 1.1.1 - postcss: 8.5.26 - postcss-import: 14.1.0(postcss@8.5.26) - postcss-loader: 8.2.1(@rspack/core@1.6.8(@swc/helpers@0.5.23))(postcss@8.5.26)(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - sass: 1.102.0 - sass-embedded: 1.100.0 - sass-loader: 16.0.8(@rspack/core@1.6.8(@swc/helpers@0.5.23))(sass-embedded@1.100.0)(sass@1.102.0)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - source-map-loader: 5.0.0(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - style-loader: 3.3.4(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - ts-checker-rspack-plugin: 1.6.0(@rspack/core@1.6.8(@swc/helpers@0.5.23))(typescript@5.9.3) - tslib: 2.8.1 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - webpack-node-externals: 3.0.0 - transitivePeerDependencies: - - '@babel/traverse' - - '@minify-html/node' - - '@nx/cypress' - - '@nx/eslint' - - '@nx/jest' - - '@nx/playwright' - - '@nx/vite' - - '@nx/webpack' - - '@swc-node/register' - - '@swc/core' - - '@swc/css' - - '@swc/helpers' - - '@swc/html' - - '@typescript/native-preview' - - bufferutil - - clean-css - - cssnano - - csso - - debug - - esbuild - - html-minifier-terser - - less - - lightningcss - - node-sass - - nx - - react-refresh - - supports-color - - typescript - - uglify-js - - utf-8-validate - - verdaccio - - vue-tsc - - webpack-cli - - webpack-hot-middleware - - '@nx/web@22.7.8(e36557eae963c5bd5b3bad2c1c9ad54a)': - dependencies: - '@nx/devkit': 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/js': 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/devkit': 23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/js': 23.2.1(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) detect-port: 2.1.0 http-server: 14.1.1 picocolors: 1.1.1 tslib: 2.8.1 optionalDependencies: - '@nx/eslint': 22.7.8(c6268304c3e993c057e90202eca80e3c) - '@nx/jest': 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3))(typescript@5.9.3) - '@nx/playwright': 22.7.8(b0b327483e86e3b4312dfd51a3eacfc2) - '@nx/webpack': 22.7.8(@babel/traverse@7.29.8)(@rspack/core@1.6.8(@swc/helpers@0.5.23))(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(typescript@5.9.3) + '@nx/eslint': 23.2.1(@babel/traverse@7.29.8)(@nx/jest@23.2.1(72e9877c63da59ac93e1f6ba5cf055e8))(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.2(jiti@1.21.7))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/jest': 23.2.1(72e9877c63da59ac93e1f6ba5cf055e8) + '@nx/playwright': 23.2.1(@babel/traverse@7.29.8)(@nx/jest@23.2.1(72e9877c63da59ac93e1f6ba5cf055e8))(@playwright/test@1.62.1)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(@zkochan/js-yaml@0.0.7)(eslint@9.39.2(jiti@1.21.7))(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) transitivePeerDependencies: - '@babel/traverse' - '@swc-node/register' + - '@swc/cli' - '@swc/core' - debug - nx - supports-color - verdaccio - '@nx/webpack@22.7.8(@babel/traverse@7.29.8)(@rspack/core@1.6.8(@swc/helpers@0.5.23))(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(typescript@5.9.3)': + '@nx/workspace@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))': dependencies: - '@babel/core': 7.29.7 - '@nx/devkit': 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/js': 22.7.8(@babel/traverse@7.29.8)(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@phenomnomnominal/tsquery': 6.2.0(typescript@5.9.3) - ajv: 8.18.0 - autoprefixer: 10.5.4(postcss@8.5.26) - babel-loader: 9.2.1(@babel/core@7.29.7)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - browserslist: 4.28.8 - copy-webpack-plugin: 14.0.0(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - css-loader: 6.11.0(@rspack/core@1.6.8(@swc/helpers@0.5.23))(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - css-minimizer-webpack-plugin: 8.0.0(esbuild@0.28.2)(lightningcss@1.33.0)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - less: 4.5.1 - less-loader: 12.3.3(@rspack/core@1.6.8(@swc/helpers@0.5.23))(less@4.5.1)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - license-webpack-plugin: 4.0.2(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - loader-utils: 2.0.4 - mini-css-extract-plugin: 2.4.7(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - parse5: 4.0.0 - picocolors: 1.1.1 - postcss: 8.5.26 - postcss-import: 14.1.0(postcss@8.5.26) - postcss-loader: 8.2.1(@rspack/core@1.6.8(@swc/helpers@0.5.23))(postcss@8.5.26)(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - rxjs: 7.8.2 - sass: 1.102.0 - sass-embedded: 1.100.0 - sass-loader: 16.0.8(@rspack/core@1.6.8(@swc/helpers@0.5.23))(sass-embedded@1.100.0)(sass@1.102.0)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - source-map-loader: 5.0.0(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - style-loader: 3.3.4(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - terser-webpack-plugin: 5.6.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - ts-loader: 9.6.2(loader-utils@2.0.4)(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - tsconfig-paths-webpack-plugin: 4.2.0 - tslib: 2.8.1 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - webpack-dev-server: 5.2.6(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - webpack-node-externals: 3.0.0 - webpack-subresource-integrity: 5.1.0(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - transitivePeerDependencies: - - '@babel/traverse' - - '@minify-html/node' - - '@parcel/css' - - '@rspack/core' - - '@swc-node/register' - - '@swc/core' - - '@swc/css' - - '@swc/html' - - bufferutil - - clean-css - - cssnano - - csso - - debug - - esbuild - - html-minifier-terser - - html-webpack-plugin - - lightningcss - - node-sass - - nx - - supports-color - - typescript - - uglify-js - - utf-8-validate - - verdaccio - - webpack-cli - - '@nx/workspace@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))': - dependencies: - '@nx/devkit': 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/devkit': 23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) '@zkochan/js-yaml': 0.0.7 chalk: 4.1.2 - enquirer: 2.3.6 - nx: 22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)) + nx: 23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)) picomatch: 4.0.4 semver: 7.8.5 tslib: 2.8.1 @@ -15527,7 +12914,75 @@ snapshots: '@oslojs/encoding@1.1.0': {} - '@oxc-project/types@0.113.0': {} + '@oxc-parser/binding-android-arm-eabi@0.142.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.142.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.142.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.142.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.142.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.142.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.142.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.142.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.142.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.142.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.142.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.142.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.142.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.142.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.142.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.142.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.142.0': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.142.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.142.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.142.0': + optional: true + + '@oxc-project/types@0.139.0': {} + + '@oxc-project/types@0.140.0': {} + + '@oxc-project/types@0.142.0': {} '@oxc-project/types@0.146.0': {} @@ -15581,7 +13036,7 @@ snapshots: '@oxc-resolver/binding-wasm32-wasi@11.17.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': dependencies: - '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -15637,7 +13092,7 @@ snapshots: detect-libc: 2.1.2 is-glob: 4.0.3 node-addon-api: 7.1.1 - picomatch: 4.0.4 + picomatch: 4.0.5 optionalDependencies: '@parcel/watcher-android-arm64': 2.6.0 '@parcel/watcher-darwin-arm64': 2.6.0 @@ -15652,82 +13107,12 @@ snapshots: '@parcel/watcher-win32-arm64': 2.6.0 '@parcel/watcher-win32-x64': 2.6.0 - '@peculiar/asn1-cms@2.8.0': - dependencies: - '@peculiar/asn1-schema': 2.8.0 - '@peculiar/asn1-x509': 2.8.0 - '@peculiar/asn1-x509-attr': 2.8.0 - asn1js: 3.0.10 - tslib: 2.8.1 - - '@peculiar/asn1-csr@2.8.0': - dependencies: - '@peculiar/asn1-schema': 2.8.0 - '@peculiar/asn1-x509': 2.8.0 - asn1js: 3.0.10 - tslib: 2.8.1 - - '@peculiar/asn1-ecc@2.8.0': - dependencies: - '@peculiar/asn1-schema': 2.8.0 - '@peculiar/asn1-x509': 2.8.0 - asn1js: 3.0.10 - tslib: 2.8.1 - - '@peculiar/asn1-pfx@2.8.0': - dependencies: - '@peculiar/asn1-cms': 2.8.0 - '@peculiar/asn1-pkcs8': 2.8.0 - '@peculiar/asn1-rsa': 2.8.0 - '@peculiar/asn1-schema': 2.8.0 - asn1js: 3.0.10 - tslib: 2.8.1 - - '@peculiar/asn1-pkcs8@2.8.0': - dependencies: - '@peculiar/asn1-schema': 2.8.0 - '@peculiar/asn1-x509': 2.8.0 - asn1js: 3.0.10 - tslib: 2.8.1 - - '@peculiar/asn1-pkcs9@2.8.0': - dependencies: - '@peculiar/asn1-cms': 2.8.0 - '@peculiar/asn1-pfx': 2.8.0 - '@peculiar/asn1-pkcs8': 2.8.0 - '@peculiar/asn1-schema': 2.8.0 - '@peculiar/asn1-x509': 2.8.0 - '@peculiar/asn1-x509-attr': 2.8.0 - asn1js: 3.0.10 - tslib: 2.8.1 - - '@peculiar/asn1-rsa@2.8.0': - dependencies: - '@peculiar/asn1-schema': 2.8.0 - '@peculiar/asn1-x509': 2.8.0 - asn1js: 3.0.10 - tslib: 2.8.1 - '@peculiar/asn1-schema@2.8.0': dependencies: '@peculiar/utils': 2.0.3 asn1js: 3.0.10 tslib: 2.8.1 - '@peculiar/asn1-x509-attr@2.8.0': - dependencies: - '@peculiar/asn1-schema': 2.8.0 - '@peculiar/asn1-x509': 2.8.0 - asn1js: 3.0.10 - tslib: 2.8.1 - - '@peculiar/asn1-x509@2.8.0': - dependencies: - '@peculiar/asn1-schema': 2.8.0 - '@peculiar/utils': 2.0.3 - asn1js: 3.0.10 - tslib: 2.8.1 - '@peculiar/json-schema@1.1.12': dependencies: tslib: 2.8.1 @@ -15744,25 +13129,11 @@ snapshots: tslib: 2.8.1 webcrypto-core: 1.9.2 - '@peculiar/x509@1.14.3': - dependencies: - '@peculiar/asn1-cms': 2.8.0 - '@peculiar/asn1-csr': 2.8.0 - '@peculiar/asn1-ecc': 2.8.0 - '@peculiar/asn1-pkcs9': 2.8.0 - '@peculiar/asn1-rsa': 2.8.0 - '@peculiar/asn1-schema': 2.8.0 - '@peculiar/asn1-x509': 2.8.0 - pvtsutils: 1.3.6 - reflect-metadata: 0.2.2 - tslib: 2.8.1 - tsyringe: 4.10.0 - - '@phenomnomnominal/tsquery@6.2.0(typescript@5.9.3)': + '@phenomnomnominal/tsquery@6.2.0(typescript@6.0.3)': dependencies: '@types/esquery': 1.5.4 esquery: 1.7.0 - typescript: 5.9.3 + typescript: 6.0.3 '@pkgjs/parseargs@0.11.0': optional: true @@ -15776,94 +13147,146 @@ snapshots: '@rolldown/binding-android-arm-eabi@1.2.5': optional: true - '@rolldown/binding-android-arm64@1.0.0-rc.4': + '@rolldown/binding-android-arm64@1.1.5': + optional: true + + '@rolldown/binding-android-arm64@1.2.0': optional: true '@rolldown/binding-android-arm64@1.2.5': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-rc.4': + '@rolldown/binding-darwin-arm64@1.1.5': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.0': optional: true '@rolldown/binding-darwin-arm64@1.2.5': optional: true - '@rolldown/binding-darwin-x64@1.0.0-rc.4': + '@rolldown/binding-darwin-x64@1.1.5': + optional: true + + '@rolldown/binding-darwin-x64@1.2.0': optional: true '@rolldown/binding-darwin-x64@1.2.5': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-rc.4': + '@rolldown/binding-freebsd-x64@1.1.5': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.0': optional: true '@rolldown/binding-freebsd-x64@1.2.5': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.4': + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.0': optional: true '@rolldown/binding-linux-arm-gnueabihf@1.2.5': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.4': + '@rolldown/binding-linux-arm64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.0': optional: true '@rolldown/binding-linux-arm64-gnu@1.2.5': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.4': + '@rolldown/binding-linux-arm64-musl@1.1.5': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.0': optional: true '@rolldown/binding-linux-arm64-musl@1.2.5': optional: true + '@rolldown/binding-linux-ppc64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-ppc64-gnu@1.2.5': optional: true + '@rolldown/binding-linux-s390x-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.0': + optional: true + '@rolldown/binding-linux-s390x-gnu@1.2.5': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.4': + '@rolldown/binding-linux-x64-gnu@1.1.5': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.0': optional: true '@rolldown/binding-linux-x64-gnu@1.2.5': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-rc.4': + '@rolldown/binding-linux-x64-musl@1.1.5': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.0': optional: true '@rolldown/binding-linux-x64-musl@1.2.5': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-rc.4': + '@rolldown/binding-openharmony-arm64@1.1.5': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.0': optional: true '@rolldown/binding-openharmony-arm64@1.2.5': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-rc.4(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)': + '@rolldown/binding-wasm32-wasi@1.1.5': dependencies: - '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.4': + '@rolldown/binding-wasm32-wasi@1.2.0': + dependencies: + '@emnapi/core': 1.11.2 + '@emnapi/runtime': 1.11.2 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.11.2)(@emnapi/runtime@1.11.2) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.1.5': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.0': optional: true '@rolldown/binding-win32-arm64-msvc@1.2.5': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.4': + '@rolldown/binding-win32-x64-msvc@1.1.5': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.0': optional: true '@rolldown/binding-win32-x64-msvc@1.2.5': optional: true - '@rolldown/pluginutils@1.0.0-rc.4': {} - '@rolldown/pluginutils@1.0.1': {} '@rollup/rollup-android-arm-eabi@4.59.0': @@ -15941,121 +13364,14 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true - '@rspack/binding-darwin-arm64@1.6.8': - optional: true - - '@rspack/binding-darwin-x64@1.6.8': - optional: true - - '@rspack/binding-linux-arm64-gnu@1.6.8': - optional: true - - '@rspack/binding-linux-arm64-musl@1.6.8': - optional: true - - '@rspack/binding-linux-x64-gnu@1.6.8': - optional: true - - '@rspack/binding-linux-x64-musl@1.6.8': - optional: true - - '@rspack/binding-wasm32-wasi@1.6.8': - dependencies: - '@napi-rs/wasm-runtime': 1.0.7 - optional: true - - '@rspack/binding-win32-arm64-msvc@1.6.8': - optional: true - - '@rspack/binding-win32-ia32-msvc@1.6.8': - optional: true - - '@rspack/binding-win32-x64-msvc@1.6.8': - optional: true - - '@rspack/binding@1.6.8': - optionalDependencies: - '@rspack/binding-darwin-arm64': 1.6.8 - '@rspack/binding-darwin-x64': 1.6.8 - '@rspack/binding-linux-arm64-gnu': 1.6.8 - '@rspack/binding-linux-arm64-musl': 1.6.8 - '@rspack/binding-linux-x64-gnu': 1.6.8 - '@rspack/binding-linux-x64-musl': 1.6.8 - '@rspack/binding-wasm32-wasi': 1.6.8 - '@rspack/binding-win32-arm64-msvc': 1.6.8 - '@rspack/binding-win32-ia32-msvc': 1.6.8 - '@rspack/binding-win32-x64-msvc': 1.6.8 - - '@rspack/core@1.6.8(@swc/helpers@0.5.23)': - dependencies: - '@module-federation/runtime-tools': 0.21.6 - '@rspack/binding': 1.6.8 - '@rspack/lite-tapable': 1.1.0 - optionalDependencies: - '@swc/helpers': 0.5.23 - - '@rspack/dev-server@1.2.1(@rspack/core@1.6.8(@swc/helpers@0.5.23))(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26))': - dependencies: - '@rspack/core': 1.6.8(@swc/helpers@0.5.23) - '@types/bonjour': 3.5.13 - '@types/connect-history-api-fallback': 1.5.4 - '@types/express': 4.17.25 - '@types/express-serve-static-core': 4.19.9 - '@types/serve-index': 1.9.4 - '@types/serve-static': 1.15.10 - '@types/sockjs': 0.3.36 - '@types/ws': 8.18.1 - ansi-html-community: 0.0.8 - bonjour-service: 1.4.4 - chokidar: 3.6.0 - colorette: 2.0.20 - compression: 1.8.1 - connect-history-api-fallback: 2.0.0 - express: 4.22.2 - graceful-fs: 4.2.11 - http-proxy-middleware: 2.0.10(@types/express@4.17.25) - ipaddr.js: 2.5.0 - launch-editor: 2.14.1 - open: 10.2.0 - p-retry: 6.2.1 - schema-utils: 4.3.3 - selfsigned: 2.4.1 - serve-index: 1.9.2 - sockjs: 0.3.24 - spdy: 4.0.2 - webpack-dev-middleware: 7.4.5(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - ws: 8.21.3 - transitivePeerDependencies: - - bufferutil - - debug - - supports-color - - utf-8-validate - - webpack - - '@rspack/lite-tapable@1.1.0': {} - - '@rspack/lite-tapable@1.1.5': {} - - '@rspack/plugin-react-refresh@1.6.2(react-refresh@0.18.0)': - dependencies: - error-stack-parser: 2.1.4 - react-refresh: 0.18.0 - '@rtsao/scc@1.1.0': {} - '@schematics/angular@21.2.19(chokidar@5.0.0)': + '@schematics/angular@22.1.8(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.19(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.19(chokidar@5.0.0) - jsonc-parser: 3.3.1 - transitivePeerDependencies: - - chokidar - - '@schematics/angular@21.2.21(chokidar@5.0.0)': - dependencies: - '@angular-devkit/core': 21.2.21(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.21(chokidar@5.0.0) + '@angular-devkit/core': 22.1.8(chokidar@5.0.0) + '@angular-devkit/schematics': 22.1.8(chokidar@5.0.0) jsonc-parser: 3.3.1 + typescript: 6.0.3 transitivePeerDependencies: - chokidar @@ -16099,38 +13415,6 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} - '@sigstore/bundle@4.0.0': - dependencies: - '@sigstore/protobuf-specs': 0.5.1 - - '@sigstore/core@3.2.1': {} - - '@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.1 - '@sigstore/protobuf-specs': 0.5.1 - make-fetch-happen: 15.0.6 - proc-log: 6.1.0 - transitivePeerDependencies: - - supports-color - - '@sigstore/tuf@4.0.2': - dependencies: - '@sigstore/protobuf-specs': 0.5.1 - tuf-js: 4.1.0 - transitivePeerDependencies: - - supports-color - - '@sigstore/verify@3.1.1': - dependencies: - '@sigstore/bundle': 4.0.0 - '@sigstore/core': 3.2.1 - '@sigstore/protobuf-specs': 0.5.1 - '@sinclair/typebox@0.34.46': {} '@sinclair/typebox@0.34.52': {} @@ -16143,10 +13427,6 @@ snapshots: dependencies: type-detect: 4.0.8 - '@sinonjs/fake-timers@13.0.5': - dependencies: - '@sinonjs/commons': 3.0.1 - '@sinonjs/fake-timers@15.4.0': dependencies: '@sinonjs/commons': 3.0.1 @@ -16158,7 +13438,7 @@ snapshots: '@swc/core': 1.16.1(@swc/helpers@0.5.23) '@swc/types': 0.1.28 - '@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3)': + '@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3)': dependencies: '@node-rs/xxhash': 1.7.6 '@swc-node/core': 1.15.0(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28) @@ -16170,7 +13450,7 @@ snapshots: oxc-resolver: 11.17.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) pirates: 4.0.7 tslib: 2.8.1 - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -16264,13 +13544,6 @@ snapshots: '@tsconfig/node16@1.0.4': {} - '@tufjs/canonical-json@2.0.0': {} - - '@tufjs/models@4.1.0': - dependencies: - '@tufjs/canonical-json': 2.0.0 - minimatch: 10.2.6 - '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 @@ -16310,10 +13583,6 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 20.19.9 - '@types/bonjour@3.5.13': - dependencies: - '@types/node': 20.19.9 - '@types/cacheable-request@6.0.3': dependencies: '@types/http-cache-semantics': 4.0.4 @@ -16321,11 +13590,6 @@ snapshots: '@types/node': 20.19.9 '@types/responselike': 1.0.3 - '@types/connect-history-api-fallback@1.5.4': - dependencies: - '@types/express-serve-static-core': 4.19.9 - '@types/node': 20.19.9 - '@types/connect@3.4.38': dependencies: '@types/node': 20.19.9 @@ -16341,11 +13605,11 @@ snapshots: '@types/eslint-scope@3.7.7': dependencies: '@types/eslint': 9.6.1 - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 '@types/eslint@9.6.1': dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 '@types/json-schema': 7.0.15 '@types/esquery@1.5.4': @@ -16362,13 +13626,6 @@ snapshots: '@types/estree@1.0.9': {} - '@types/express-serve-static-core@4.19.9': - dependencies: - '@types/node': 20.19.9 - '@types/qs': 6.15.1 - '@types/range-parser': 1.2.7 - '@types/send': 1.2.1 - '@types/express-serve-static-core@5.1.1': dependencies: '@types/node': 20.19.9 @@ -16376,13 +13633,6 @@ snapshots: '@types/range-parser': 1.2.7 '@types/send': 1.2.1 - '@types/express@4.17.25': - dependencies: - '@types/body-parser': 1.19.6 - '@types/express-serve-static-core': 4.19.9 - '@types/qs': 6.15.1 - '@types/serve-static': 1.15.10 - '@types/express@5.0.6': dependencies: '@types/body-parser': 1.19.6 @@ -16393,6 +13643,8 @@ snapshots: dependencies: '@types/node': 20.19.9 + '@types/gensync@1.0.5': {} + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 @@ -16405,10 +13657,6 @@ snapshots: '@types/http-errors@2.0.5': {} - '@types/http-proxy@1.17.17': - dependencies: - '@types/node': 20.19.9 - '@types/istanbul-lib-coverage@2.0.6': {} '@types/istanbul-lib-report@3.0.3': @@ -16430,6 +13678,8 @@ snapshots: '@types/tough-cookie': 4.0.5 parse5: 7.3.0 + '@types/jsesc@2.5.1': {} + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} @@ -16444,8 +13694,6 @@ snapshots: '@types/mdx@2.0.13': {} - '@types/mime@1.3.5': {} - '@types/mocha@9.0.0': {} '@types/ms@2.1.0': {} @@ -16454,10 +13702,6 @@ snapshots: dependencies: '@types/unist': 3.0.3 - '@types/node-forge@1.3.14': - dependencies: - '@types/node': 20.19.9 - '@types/node@20.19.9': dependencies: undici-types: 6.21.0 @@ -16470,48 +13714,25 @@ snapshots: '@types/qs@6.14.0': {} - '@types/qs@6.15.1': {} - '@types/range-parser@1.2.7': {} '@types/responselike@1.0.3': dependencies: '@types/node': 20.19.9 - '@types/retry@0.12.2': {} - '@types/sax@1.2.7': dependencies: '@types/node': 24.13.3 - '@types/send@0.17.6': - dependencies: - '@types/mime': 1.3.5 - '@types/node': 20.19.9 - '@types/send@1.2.1': dependencies: '@types/node': 20.19.9 - '@types/serve-index@1.9.4': - dependencies: - '@types/express': 5.0.6 - - '@types/serve-static@1.15.10': - dependencies: - '@types/http-errors': 2.0.5 - '@types/node': 20.19.9 - '@types/send': 0.17.6 - '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 '@types/node': 20.19.9 - '@types/sockjs@0.3.36': - dependencies: - '@types/node': 20.19.9 - '@types/stack-utils@2.0.3': {} '@types/tough-cookie@4.0.5': {} @@ -16522,165 +13743,98 @@ snapshots: '@types/video.js@7.3.58': {} - '@types/ws@8.18.1': - dependencies: - '@types/node': 20.19.9 - '@types/yargs-parser@21.0.3': {} '@types/yargs@17.0.35': dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.69.0(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.69.0(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/parser': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.69.0 - '@typescript-eslint/type-utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/type-utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.69.0 eslint: 9.39.2(jiti@1.21.7) ignore: 7.0.8 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.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3)': dependencies: '@typescript-eslint/scope-manager': 8.69.0 '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/typescript-estree': 8.69.0(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.69.0(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.69.0 debug: 4.4.3(supports-color@7.2.0) eslint: 9.39.2(jiti@1.21.7) - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.66.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.69.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.66.0(typescript@5.9.3) - '@typescript-eslint/types': 8.66.0 - debug: 4.4.3(supports-color@7.2.0) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/project-service@8.69.0(typescript@5.9.3)': - dependencies: - '@typescript-eslint/tsconfig-utils': 8.69.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.69.0(typescript@6.0.3) '@typescript-eslint/types': 8.69.0 debug: 4.4.3(supports-color@7.2.0) - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.66.0': - dependencies: - '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/visitor-keys': 8.66.0 - '@typescript-eslint/scope-manager@8.69.0': dependencies: '@typescript-eslint/types': 8.69.0 '@typescript-eslint/visitor-keys': 8.69.0 - '@typescript-eslint/tsconfig-utils@8.66.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.69.0(typescript@6.0.3)': dependencies: - typescript: 5.9.3 + typescript: 6.0.3 - '@typescript-eslint/tsconfig-utils@8.69.0(typescript@5.9.3)': - dependencies: - typescript: 5.9.3 - - '@typescript-eslint/type-utils@8.66.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.66.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - debug: 4.4.3(supports-color@7.2.0) - eslint: 9.39.2(jiti@1.21.7) - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/type-utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/typescript-estree': 8.69.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.69.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) debug: 4.4.3(supports-color@7.2.0) eslint: 9.39.2(jiti@1.21.7) - 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.66.0': {} - '@typescript-eslint/types@8.69.0': {} - '@typescript-eslint/typescript-estree@8.66.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.69.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.66.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.66.0(typescript@5.9.3) - '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/visitor-keys': 8.66.0 - debug: 4.4.3(supports-color@7.2.0) - minimatch: 10.2.6 - semver: 7.8.5 - tinyglobby: 0.2.17 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/typescript-estree@8.69.0(typescript@5.9.3)': - dependencies: - '@typescript-eslint/project-service': 8.69.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.69.0(typescript@5.9.3) + '@typescript-eslint/project-service': 8.69.0(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.69.0(typescript@6.0.3) '@typescript-eslint/types': 8.69.0 '@typescript-eslint/visitor-keys': 8.69.0 debug: 4.4.3(supports-color@7.2.0) minimatch: 10.2.6 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.66.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': - dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.2(jiti@1.21.7)) - '@typescript-eslint/scope-manager': 8.66.0 - '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.9.3) - eslint: 9.39.2(jiti@1.21.7) - typescript: 5.9.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3)': dependencies: '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.2(jiti@1.21.7)) '@typescript-eslint/scope-manager': 8.69.0 '@typescript-eslint/types': 8.69.0 - '@typescript-eslint/typescript-estree': 8.69.0(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.69.0(typescript@6.0.3) eslint: 9.39.2(jiti@1.21.7) - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.66.0': - dependencies: - '@typescript-eslint/types': 8.66.0 - eslint-visitor-keys: 5.0.1 - '@typescript-eslint/visitor-keys@8.69.0': dependencies: '@typescript-eslint/types': 8.69.0 @@ -16748,7 +13902,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.2.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': @@ -16805,9 +13959,9 @@ snapshots: global: 4.4.0 is-function: 1.0.2 - '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.6(patch_hash=215b6585a9c92fa68c096d500f4676983200307b1e64c1321e20ccbd50bbc8f3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(lightningcss@1.33.0)(sass-embedded@1.100.0)(sass@1.97.3)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0))': + '@vitejs/plugin-basic-ssl@2.3.0(vite@8.1.5(patch_hash=469ec4025880fc7b86d3d8b3b4e2028572d6acf65bdfb93973db18fdb3c00ad9)(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0))': dependencies: - vite: 7.3.6(patch_hash=215b6585a9c92fa68c096d500f4676983200307b1e64c1321e20ccbd50bbc8f3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(lightningcss@1.33.0)(sass-embedded@1.100.0)(sass@1.97.3)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0) + vite: 8.1.5(patch_hash=469ec4025880fc7b86d3d8b3b4e2028572d6acf65bdfb93973db18fdb3c00ad9)(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0) '@webassemblyjs/ast@1.14.1': dependencies: @@ -16904,11 +14058,6 @@ snapshots: abbrev@4.0.0: {} - accepts@1.3.8: - dependencies: - mime-types: 2.1.35 - negotiator: 0.6.3 - accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -16936,8 +14085,6 @@ snapshots: address@2.0.3: {} - adm-zip@0.6.0: {} - aes-decrypter@3.1.3: dependencies: '@babel/runtime': 7.29.7 @@ -16960,21 +14107,23 @@ snapshots: agent-base@7.1.4: {} - ajv-formats@2.1.1(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 + agent-base@9.0.0: {} - ajv-formats@3.0.1(ajv@8.18.0): + ajv-formats@2.1.1(ajv@8.20.0): optionalDependencies: - ajv: 8.18.0 + ajv: 8.20.0 + + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 ajv-keywords@3.5.2(ajv@6.14.0): dependencies: ajv: 6.14.0 - ajv-keywords@5.1.0(ajv@8.18.0): + ajv-keywords@5.1.0(ajv@8.20.0): dependencies: - ajv: 8.18.0 + ajv: 8.20.0 fast-deep-equal: 3.1.3 ajv@6.14.0: @@ -16991,13 +14140,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.6 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 @@ -17005,55 +14147,35 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - algoliasearch@5.48.1: - 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 - am-i-vibing@0.4.0: dependencies: process-ancestry: 0.1.0 - angular-eslint@21.4.0(@angular/cli@21.2.21(@types/node@20.19.9)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript-eslint@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(typescript@5.9.3): + angular-eslint@22.5.0(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript-eslint@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(typescript@6.0.3): dependencies: - '@angular-devkit/core': 21.2.19(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.21(chokidar@5.0.0) - '@angular-eslint/builder': 21.4.0(@angular/cli@21.2.21(@types/node@20.19.9)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@angular-eslint/eslint-plugin': 21.4.0(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.4.0(@angular-eslint/template-parser@21.4.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@typescript-eslint/types@8.66.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@angular-eslint/schematics': 21.4.0(@angular-eslint/template-parser@21.4.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(@angular/cli@21.2.21(@types/node@20.19.9)(chokidar@5.0.0))(@typescript-eslint/types@8.66.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@angular-eslint/template-parser': 21.4.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@angular/cli': 21.2.21(@types/node@20.19.9)(chokidar@5.0.0) - '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@angular-devkit/core': 22.1.8(chokidar@5.0.0) + '@angular-devkit/schematics': 22.1.8(chokidar@5.0.0) + '@angular-eslint/builder': 22.5.0(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + '@angular-eslint/eslint-plugin': 22.5.0(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + '@angular-eslint/eslint-plugin-template': 22.5.0(@angular-eslint/template-parser@22.5.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(@typescript-eslint/types@8.69.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + '@angular-eslint/schematics': 22.5.0(@angular-eslint/template-parser@22.5.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(@typescript-eslint/types@8.69.0)(@typescript-eslint/utils@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(chokidar@5.0.0)(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + '@angular-eslint/template-parser': 22.5.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + '@typescript-eslint/types': 8.69.0 + '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) eslint: 9.39.2(jiti@1.21.7) - typescript: 5.9.3 - typescript-eslint: 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + typescript: 6.0.3 + typescript-eslint: 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) transitivePeerDependencies: - chokidar - supports-color - angularx-qrcode@21.0.5(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)): + angularx-qrcode@22.0.1(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)): dependencies: - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) qrcode: 1.5.4 tslib: 2.8.1 - ansi-colors@4.1.3: {} - ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 @@ -17062,8 +14184,6 @@ snapshots: dependencies: environment: 1.1.0 - ansi-html-community@0.0.8: {} - ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} @@ -17150,8 +14270,6 @@ snapshots: call-bound: 1.0.4 is-array-buffer: 3.0.5 - array-flatten@1.1.1: {} - array-includes@3.1.9: dependencies: call-bind: 1.0.8 @@ -17211,7 +14329,7 @@ snapshots: astring@1.9.0: {} - astro@7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.102.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0): + astro@7.2.4(@astrojs/markdown-remark@7.2.4)(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0): dependencies: '@astrojs/compiler-rs': 0.3.2(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3) '@astrojs/internal-helpers': 0.10.4 @@ -17261,8 +14379,8 @@ snapshots: ultrahtml: 1.6.0 unifont: 0.7.5 unstorage: 1.17.5 - vite: 8.2.2(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.102.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0) - vitefu: 1.1.3(vite@8.2.2(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.102.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0)) + vite: 8.2.2(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.2.2(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0)) xxhash-wasm: 1.1.0 yargs-parser: 22.0.0 zod: 4.5.4 @@ -17340,7 +14458,7 @@ snapshots: axios@1.20.0: dependencies: - follow-redirects: 1.16.0(debug@4.4.3) + follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0)) form-data: 4.0.6 https-proxy-agent: 5.0.1(supports-color@7.2.0) proxy-from-env: 2.1.0 @@ -17350,19 +14468,6 @@ snapshots: axobject-query@4.1.0: {} - babel-jest@30.4.1(@babel/core@7.29.7): - dependencies: - '@babel/core': 7.29.7 - '@jest/transform': 30.4.1 - '@types/babel__core': 7.20.5 - babel-plugin-istanbul: 7.0.1 - babel-preset-jest: 30.4.0(@babel/core@7.29.7) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - babel-jest@30.5.1(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 @@ -17376,13 +14481,6 @@ snapshots: transitivePeerDependencies: - supports-color - babel-loader@9.2.1(@babel/core@7.29.7)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - '@babel/core': 7.29.7 - find-cache-dir: 4.0.0 - schema-utils: 4.3.3 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - babel-plugin-const-enum@1.2.0(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 @@ -17392,16 +14490,6 @@ snapshots: transitivePeerDependencies: - supports-color - babel-plugin-istanbul@7.0.1: - dependencies: - '@babel/helper-plugin-utils': 7.29.7 - '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.6 - istanbul-lib-instrument: 6.0.3 - test-exclude: 6.0.0 - transitivePeerDependencies: - - supports-color - babel-plugin-istanbul@8.0.0: dependencies: '@babel/helper-plugin-utils': 7.29.7 @@ -17412,10 +14500,6 @@ snapshots: transitivePeerDependencies: - supports-color - babel-plugin-jest-hoist@30.4.0: - dependencies: - '@types/babel__core': 7.20.5 - babel-plugin-jest-hoist@30.5.0: dependencies: '@types/babel__core': 7.20.5 @@ -17484,12 +14568,6 @@ snapshots: '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.7) '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.7) - babel-preset-jest@30.4.0(@babel/core@7.29.7): - dependencies: - '@babel/core': 7.29.7 - babel-plugin-jest-hoist: 30.4.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7) - babel-preset-jest@30.5.0(@babel/core@7.29.7): dependencies: '@babel/core': 7.29.7 @@ -17512,9 +14590,7 @@ snapshots: dependencies: safe-buffer: 5.1.2 - batch@0.6.1: {} - - beasties@0.4.1: + beasties@0.4.3: dependencies: css-select: 6.0.0 css-what: 7.0.0 @@ -17530,8 +14606,6 @@ snapshots: dependencies: node-addon-api: 8.9.1 - big.js@5.2.2: {} - binary-extensions@2.3.0: {} bl@4.1.0: @@ -17542,23 +14616,6 @@ snapshots: bluebird@3.7.2: {} - body-parser@1.20.6: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.1 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.15.3 - raw-body: 2.5.3 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - body-parser@2.2.1: dependencies: bytes: 3.1.2 @@ -17573,11 +14630,6 @@ snapshots: transitivePeerDependencies: - supports-color - bonjour-service@1.4.4: - dependencies: - fast-deep-equal: 3.1.3 - multicast-dns: 7.2.5 - boolbase@1.0.0: {} boolean@3.2.0: @@ -17592,10 +14644,6 @@ snapshots: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.8: - dependencies: - balanced-match: 4.0.3 - brace-expansion@5.0.9: dependencies: balanced-match: 4.0.4 @@ -17661,19 +14709,6 @@ snapshots: bytestreamjs@2.0.1: {} - cacache@20.0.4: - dependencies: - '@npmcli/fs': 5.0.0 - fs-minipass: 3.0.3 - glob: 13.0.6 - 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.6 - ssri: 13.0.1 - cacheable-lookup@5.0.4: {} cacheable-request@7.0.4: @@ -17711,13 +14746,6 @@ snapshots: camelcase@6.3.0: {} - caniuse-api@3.0.0: - dependencies: - browserslist: 4.28.8 - caniuse-lite: 1.0.30001809 - lodash.memoize: 4.1.2 - lodash.uniq: 4.5.0 - caniuse-lite@1.0.30001809: {} ccount@2.0.1: {} @@ -17785,9 +14813,9 @@ snapshots: cli-spinners@3.4.0: {} - cli-truncate@5.2.0: + cli-truncate@6.1.1: dependencies: - slice-ansi: 8.0.0 + slice-ansi: 9.0.0 string-width: 8.2.2 cli-width@4.1.0: {} @@ -17838,7 +14866,8 @@ snapshots: colorette@2.0.20: {} - colorjs.io@0.5.2: {} + colorjs.io@0.5.2: + optional: true columnify@1.6.0: dependencies: @@ -17866,36 +14895,12 @@ snapshots: common-ancestor-path@2.0.0: {} - common-path-prefix@3.0.0: {} - compare-version@0.1.2: {} - compressible@2.0.18: - dependencies: - mime-db: 1.54.0 - - compression@1.8.1: - dependencies: - bytes: 3.1.2 - compressible: 2.0.18 - debug: 2.6.9 - negotiator: 0.6.4 - on-headers: 1.1.0 - safe-buffer: 5.2.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - concat-map@0.0.1: {} confusing-browser-globals@1.0.11: {} - connect-history-api-fallback@2.0.0: {} - - content-disposition@0.5.4: - dependencies: - safe-buffer: 5.2.1 - content-disposition@1.0.1: {} content-type@1.0.5: {} @@ -17906,8 +14911,6 @@ snapshots: cookie-es@1.2.2: {} - cookie-signature@1.0.7: {} - cookie-signature@1.2.2: {} cookie@0.7.2: {} @@ -17917,6 +14920,7 @@ snapshots: copy-anything@2.0.6: dependencies: is-what: 3.14.1 + optional: true copy-webpack-plugin@12.0.2(webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)): dependencies: @@ -17928,15 +14932,6 @@ snapshots: serialize-javascript: 7.1.0 webpack: 5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2) - copy-webpack-plugin@14.0.0(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - glob-parent: 6.0.2 - normalize-path: 3.0.0 - schema-utils: 4.3.3 - serialize-javascript: 7.1.0 - tinyglobby: 0.2.17 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - core-js-compat@3.50.0: dependencies: browserslist: 4.28.8 @@ -17958,23 +14953,14 @@ snapshots: path-type: 4.0.0 yaml: 1.10.3 - cosmiconfig@8.3.6(typescript@5.9.3): + cosmiconfig@8.3.6(typescript@6.0.3): dependencies: import-fresh: 3.3.1 js-yaml: 4.3.1 parse-json: 5.2.0 path-type: 4.0.0 optionalDependencies: - typescript: 5.9.3 - - cosmiconfig@9.0.2(typescript@5.9.3): - dependencies: - env-paths: 2.2.1 - import-fresh: 3.3.1 - js-yaml: 4.3.1 - parse-json: 5.2.0 - optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 create-require@1.1.1: {} @@ -17991,37 +14977,6 @@ snapshots: dependencies: uncrypto: 0.1.3 - css-declaration-sorter@7.4.0(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - - css-loader@6.11.0(@rspack/core@1.6.8(@swc/helpers@0.5.23))(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - icss-utils: 5.1.0(postcss@8.5.26) - postcss: 8.5.26 - postcss-modules-extract-imports: 3.1.0(postcss@8.5.26) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.26) - postcss-modules-scope: 3.2.1(postcss@8.5.26) - postcss-modules-values: 4.0.0(postcss@8.5.26) - postcss-value-parser: 4.2.0 - semver: 7.8.5 - optionalDependencies: - '@rspack/core': 1.6.8(@swc/helpers@0.5.23) - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - - css-minimizer-webpack-plugin@8.0.0(esbuild@0.28.2)(lightningcss@1.33.0)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - cssnano: 7.1.9(postcss@8.5.26) - jest-worker: 30.4.1 - postcss: 8.5.26 - schema-utils: 4.3.3 - serialize-javascript: 7.1.0 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - optionalDependencies: - esbuild: 0.28.2 - lightningcss: 1.33.0 - css-select@5.2.2: dependencies: boolbase: 1.0.0 @@ -18054,50 +15009,6 @@ snapshots: cssesc@3.0.0: {} - cssnano-preset-default@7.0.17(postcss@8.5.26): - dependencies: - browserslist: 4.28.8 - css-declaration-sorter: 7.4.0(postcss@8.5.26) - cssnano-utils: 5.0.3(postcss@8.5.26) - postcss: 8.5.26 - postcss-calc: 10.1.1(postcss@8.5.26) - postcss-colormin: 7.0.10(postcss@8.5.26) - postcss-convert-values: 7.0.12(postcss@8.5.26) - postcss-discard-comments: 7.0.8(postcss@8.5.26) - postcss-discard-duplicates: 7.0.4(postcss@8.5.26) - postcss-discard-empty: 7.0.3(postcss@8.5.26) - postcss-discard-overridden: 7.0.3(postcss@8.5.26) - postcss-merge-longhand: 7.0.7(postcss@8.5.26) - postcss-merge-rules: 7.0.11(postcss@8.5.26) - postcss-minify-font-values: 7.0.3(postcss@8.5.26) - postcss-minify-gradients: 7.0.5(postcss@8.5.26) - postcss-minify-params: 7.0.9(postcss@8.5.26) - postcss-minify-selectors: 7.1.2(postcss@8.5.26) - postcss-normalize-charset: 7.0.3(postcss@8.5.26) - postcss-normalize-display-values: 7.0.3(postcss@8.5.26) - postcss-normalize-positions: 7.0.4(postcss@8.5.26) - postcss-normalize-repeat-style: 7.0.4(postcss@8.5.26) - postcss-normalize-string: 7.0.3(postcss@8.5.26) - postcss-normalize-timing-functions: 7.0.3(postcss@8.5.26) - postcss-normalize-unicode: 7.0.9(postcss@8.5.26) - postcss-normalize-url: 7.0.3(postcss@8.5.26) - postcss-normalize-whitespace: 7.0.3(postcss@8.5.26) - postcss-ordered-values: 7.0.4(postcss@8.5.26) - postcss-reduce-initial: 7.0.9(postcss@8.5.26) - postcss-reduce-transforms: 7.0.3(postcss@8.5.26) - postcss-svgo: 7.1.3(postcss@8.5.26) - postcss-unique-selectors: 7.0.7(postcss@8.5.26) - - cssnano-utils@5.0.3(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - - cssnano@7.1.9(postcss@8.5.26): - dependencies: - cssnano-preset-default: 7.0.17(postcss@8.5.26) - lilconfig: 3.1.3 - postcss: 8.5.26 - csso@5.0.5: dependencies: css-tree: 2.2.1 @@ -18134,10 +15045,6 @@ snapshots: dayjs@1.11.21: {} - debug@2.6.9: - dependencies: - ms: 2.0.0 - debug@3.2.7: dependencies: ms: 2.1.3 @@ -18168,8 +15075,15 @@ snapshots: deepmerge@4.3.1: {} + default-browser-id@5.0.0: {} + default-browser-id@5.0.1: {} + default-browser@5.2.1: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + default-browser@5.5.0: dependencies: bundle-name: 4.1.0 @@ -18189,8 +15103,6 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - define-lazy-prop@2.0.0: {} - define-lazy-prop@3.0.0: {} define-properties@1.2.1: @@ -18203,21 +15115,18 @@ snapshots: delayed-stream@1.0.0: {} - depd@1.1.2: {} - depd@2.0.0: {} dequal@2.0.3: {} destr@2.0.5: {} - destroy@1.2.0: {} - detect-libc@2.1.2: {} detect-newline@3.1.0: {} - detect-node@2.1.0: {} + detect-node@2.1.0: + optional: true detect-port@2.1.0: dependencies: @@ -18254,10 +15163,6 @@ snapshots: - electron-builder-squirrel-windows - supports-color - dns-packet@5.6.1: - dependencies: - '@leichtgewicht/ip-codec': 2.0.5 - doctrine@2.1.0: dependencies: esutils: 2.0.3 @@ -18288,7 +15193,7 @@ snapshots: dotenv-expand@12.0.3: dependencies: - dotenv: 16.4.7 + dotenv: 16.6.1 dotenv@16.4.7: {} @@ -18355,7 +15260,7 @@ snapshots: electron-conf@1.3.0(electron@43.3.0): dependencies: - ajv: 8.18.0 + ajv: 8.20.0 electron: 43.3.0 electron-playwright-helpers@1.8.2: @@ -18419,14 +15324,10 @@ snapshots: emoji-regex@9.2.2: {} - emojis-list@3.0.0: {} + empathic@2.0.1: {} encodeurl@2.0.0: {} - encoding@0.1.13: - dependencies: - iconv-lite: 0.6.3 - end-of-stream@1.4.5: dependencies: once: 1.4.0 @@ -18436,15 +15337,6 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 - enhanced-resolve@5.24.5: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.3.3 - - enquirer@2.3.6: - dependencies: - ansi-colors: 4.1.3 - entities@4.5.0: {} entities@6.0.1: {} @@ -18476,10 +15368,6 @@ snapshots: dependencies: is-arrayish: 0.2.1 - error-stack-parser@2.1.4: - dependencies: - stackframe: 1.3.4 - es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 @@ -18599,7 +15487,7 @@ snapshots: - supports-color optional: true - esbuild-wasm@0.27.2: {} + esbuild-wasm@0.28.2: {} esbuild@0.25.12: optionalDependencies: @@ -18630,35 +15518,6 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 - 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 - esbuild@0.28.2: optionalDependencies: '@esbuild/aix-ppc64': 0.28.2 @@ -18712,17 +15571,17 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@1.21.7)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@1.21.7)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/parser': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) eslint: 9.39.2(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -18733,7 +15592,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.2(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@1.21.7)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2(jiti@1.21.7)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -18745,7 +15604,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/parser': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -18769,7 +15628,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 @@ -18918,15 +15777,6 @@ snapshots: jest-mock: 30.2.0 jest-util: 30.2.0 - expect@30.4.1: - dependencies: - '@jest/expect-utils': 30.4.1 - '@jest/get-type': 30.1.0 - jest-matcher-utils: 30.4.1 - jest-message-util: 30.4.1 - jest-mock: 30.4.1 - jest-util: 30.4.1 - expect@30.5.1: dependencies: '@jest/expect-utils': 30.5.1 @@ -18943,42 +15793,6 @@ snapshots: express: 5.2.1 ip-address: 10.5.0 - express@4.22.2: - dependencies: - accepts: 1.3.8 - array-flatten: 1.1.1 - body-parser: 1.20.6 - content-disposition: 0.5.4 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.0.7 - debug: 2.6.9 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 1.3.2 - fresh: 0.5.2 - http-errors: 2.0.1 - merge-descriptors: 1.0.3 - methods: 1.1.2 - on-finished: 2.4.1 - parseurl: 1.3.3 - path-to-regexp: 0.1.13 - proxy-addr: 2.0.7 - qs: 6.15.3 - range-parser: 1.2.1 - safe-buffer: 5.2.1 - send: 0.19.2 - serve-static: 1.16.3 - setprototypeof: 1.2.0 - statuses: 2.0.2 - type-is: 1.6.18 - utils-merge: 1.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - express@5.2.1: dependencies: accepts: 2.0.0 @@ -19036,6 +15850,10 @@ snapshots: fast-uri@3.1.6: {} + fast-wrap-ansi@0.2.0: + dependencies: + fast-string-width: 3.0.2 + fast-wrap-ansi@0.2.2: dependencies: fast-string-width: 3.0.2 @@ -19044,18 +15862,10 @@ snapshots: dependencies: reusify: 1.1.0 - faye-websocket@0.11.4: - dependencies: - websocket-driver: 0.7.5 - fb-watchman@2.0.2: dependencies: bser: 2.1.1 - fdir@6.5.0(picomatch@4.0.5): - optionalDependencies: - picomatch: 4.0.5 - fdir@6.5.0(picomatch@4.0.7): optionalDependencies: picomatch: 4.0.7 @@ -19076,18 +15886,6 @@ snapshots: dependencies: to-regex-range: 5.0.1 - finalhandler@1.3.2: - dependencies: - debug: 2.6.9 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.2 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - finalhandler@2.1.1: dependencies: debug: 4.4.3(supports-color@7.2.0) @@ -19099,11 +15897,6 @@ snapshots: transitivePeerDependencies: - supports-color - find-cache-dir@4.0.0: - dependencies: - common-path-prefix: 3.0.0 - pkg-dir: 7.0.0 - find-process@2.1.1: dependencies: chalk: 4.1.2 @@ -19120,11 +15913,6 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - find-up@6.3.0: - dependencies: - locate-path: 7.2.0 - path-exists: 5.0.0 - fix-path@5.0.0: dependencies: shell-path: 3.1.0 @@ -19145,10 +15933,6 @@ snapshots: optionalDependencies: debug: 4.4.3(supports-color@7.2.0) - follow-redirects@1.16.0(debug@4.4.3): - optionalDependencies: - debug: 4.4.3(supports-color@7.2.0) - fontace@0.4.1: dependencies: fontkitten: 1.0.2 @@ -19166,12 +15950,12 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 - fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)): + fork-ts-checker-webpack-plugin@9.1.0(typescript@6.0.3)(webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)): dependencies: '@babel/code-frame': 7.27.1 chalk: 4.1.2 chokidar: 4.0.3 - cosmiconfig: 8.3.6(typescript@5.9.3) + cosmiconfig: 8.3.6(typescript@6.0.3) deepmerge: 4.3.1 fs-extra: 10.1.0 memfs: 3.5.3 @@ -19180,26 +15964,9 @@ snapshots: schema-utils: 3.3.0 semver: 7.8.5 tapable: 2.3.0 - typescript: 5.9.3 + typescript: 6.0.3 webpack: 5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2) - fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - '@babel/code-frame': 7.27.1 - chalk: 4.1.2 - chokidar: 4.0.3 - cosmiconfig: 8.3.6(typescript@5.9.3) - deepmerge: 4.3.1 - fs-extra: 10.1.0 - memfs: 3.5.3 - minimatch: 3.1.5 - node-abort-controller: 3.1.1 - schema-utils: 3.3.0 - semver: 7.8.5 - tapable: 2.3.0 - typescript: 5.9.3 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - form-data@4.0.6: dependencies: asynckit: 0.4.0 @@ -19212,8 +15979,6 @@ snapshots: fraction.js@5.3.4: {} - fresh@0.5.2: {} - fresh@2.0.0: {} fs-constants@1.0.0: {} @@ -19255,10 +16020,6 @@ snapshots: jsonfile: 6.2.1 universalify: 2.0.1 - fs-minipass@3.0.3: - dependencies: - minipass: 7.1.3 - fs-monkey@1.1.0: {} fs.realpath@1.0.0: {} @@ -19340,10 +16101,6 @@ snapshots: dependencies: is-glob: 4.0.3 - glob-to-regex.js@1.2.0(tslib@2.8.1): - dependencies: - tslib: 2.8.1 - glob-to-regexp@0.4.1: {} glob@10.5.0: @@ -19376,7 +16133,7 @@ snapshots: es6-error: 4.1.1 matcher: 3.0.0 roarr: 2.15.4 - semver: 7.7.4 + semver: 7.8.5 serialize-error: 7.0.1 optional: true @@ -19439,8 +16196,6 @@ snapshots: ufo: 1.6.3 uncrypto: 0.1.3 - handle-thing@2.0.1: {} - handlebars@4.7.9: dependencies: minimist: 1.2.8 @@ -19612,20 +16367,13 @@ snapshots: hono@4.12.34: {} - hosted-git-info@4.1.0: - dependencies: - lru-cache: 6.0.0 - - hosted-git-info@9.0.3: + hosted-git-info@10.1.1: dependencies: lru-cache: 11.5.2 - hpack.js@2.1.6: + hosted-git-info@4.1.0: dependencies: - inherits: 2.0.4 - obuf: 1.1.2 - readable-stream: 2.3.8 - wbuf: 1.7.3 + lru-cache: 6.0.0 html-encoding-sniffer@3.0.0: dependencies: @@ -19650,16 +16398,6 @@ snapshots: http-cache-semantics@4.2.0: {} - http-deceiver@1.2.7: {} - - http-errors@1.8.1: - dependencies: - depd: 1.1.2 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 1.5.0 - toidentifier: 1.0.1 - http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -19668,8 +16406,6 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 - http-parser-js@0.5.10: {} - http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 @@ -19677,33 +16413,10 @@ snapshots: transitivePeerDependencies: - supports-color - http-proxy-middleware@2.0.10(@types/express@4.17.25): - dependencies: - '@types/http-proxy': 1.17.17 - http-proxy: 1.18.1(debug@4.4.3) - is-glob: 4.0.3 - is-plain-obj: 3.0.0 - micromatch: 4.0.8 - optionalDependencies: - '@types/express': 4.17.25 - transitivePeerDependencies: - - debug - - http-proxy-middleware@3.0.7: - dependencies: - '@types/http-proxy': 1.17.17 - debug: 4.4.3(supports-color@7.2.0) - http-proxy: 1.18.1(debug@4.4.3) - is-glob: 4.0.3 - is-plain-object: 5.0.0 - micromatch: 4.0.8 - transitivePeerDependencies: - - supports-color - - http-proxy@1.18.1(debug@4.4.3): + http-proxy@1.18.1: dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.16.0(debug@4.4.3) + follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0)) requires-port: 1.0.0 transitivePeerDependencies: - debug @@ -19715,7 +16428,7 @@ snapshots: corser: 2.0.1 he: 1.2.0 html-encoding-sniffer: 3.0.0 - http-proxy: 1.18.1(debug@4.4.3) + http-proxy: 1.18.1 mime: 1.6.0 minimist: 1.2.8 opener: 1.5.2 @@ -19746,13 +16459,16 @@ snapshots: transitivePeerDependencies: - supports-color - human-signals@2.1.0: {} - - hyperdyperid@1.2.0: {} - - iconv-lite@0.4.24: + https-proxy-agent@9.1.0: dependencies: - safer-buffer: 2.1.2 + agent-base: 9.0.0 + debug: 4.4.3(supports-color@7.2.0) + proxy-agent-negotiate: 1.1.0 + transitivePeerDependencies: + - kerberos + - supports-color + + human-signals@2.1.0: {} iconv-lite@0.6.3: dependencies: @@ -19766,26 +16482,16 @@ snapshots: dependencies: safer-buffer: 2.1.2 - icss-utils@5.1.0(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - identity-obj-proxy@3.0.0: dependencies: harmony-reflect: 1.6.2 ieee754@1.2.1: {} - ignore-walk@8.0.0: - dependencies: - minimatch: 10.2.6 - ignore@5.3.2: {} ignore@7.0.5: {} - ignore@7.0.6: {} - ignore@7.0.8: {} image-size@0.5.5: @@ -19803,6 +16509,8 @@ snapshots: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 + import-meta-resolve@4.2.0: {} + imurmurhash@0.1.4: {} individual@2.0.0: {} @@ -19814,8 +16522,6 @@ snapshots: inherits@2.0.4: {} - ini@6.0.0: {} - inline-style-parser@0.2.7: {} internal-slot@1.1.0: @@ -19828,8 +16534,6 @@ snapshots: ipaddr.js@1.9.1: {} - ipaddr.js@2.5.0: {} - iptv-playlist-parser@https://codeload.github.com/4gray/iptv-playlist-parser/tar.gz/d547b6be8a44431c85fbcbcdefb41f28c0dadf6d: {} iron-webcrypto@1.2.1: {} @@ -19893,8 +16597,6 @@ snapshots: is-decimal@2.0.1: {} - is-docker@2.2.1: {} - is-docker@3.0.0: {} is-docker@4.0.0: {} @@ -19941,8 +16643,6 @@ snapshots: is-negative-zero@2.0.3: {} - is-network-error@1.3.2: {} - is-number-object@1.1.1: dependencies: call-bound: 1.0.4 @@ -19950,16 +16650,12 @@ snapshots: is-number@7.0.0: {} - is-plain-obj@3.0.0: {} - is-plain-obj@4.1.0: {} is-plain-object@2.0.4: dependencies: isobject: 3.0.1 - is-plain-object@5.0.0: {} - is-potential-custom-element-name@1.0.1: {} is-promise@4.0.0: {} @@ -20009,11 +16705,12 @@ snapshots: call-bound: 1.0.4 get-intrinsic: 1.3.0 - is-what@3.14.1: {} + is-what@3.14.1: + optional: true - is-wsl@2.2.0: + is-wsl@3.1.0: dependencies: - is-docker: 2.2.1 + is-inside-container: 1.0.0 is-wsl@3.1.1: dependencies: @@ -20035,10 +16732,6 @@ snapshots: isobject@3.0.1: {} - isomorphic-ws@5.0.0(ws@8.21.0): - dependencies: - ws: 8.21.0 - istanbul-lib-coverage@3.2.2: {} istanbul-lib-instrument@6.0.3: @@ -20047,7 +16740,7 @@ snapshots: '@babel/parser': 7.29.8 '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 - semver: 7.7.4 + semver: 7.8.5 transitivePeerDependencies: - supports-color @@ -20088,32 +16781,6 @@ snapshots: jest-util: 30.5.1 p-limit: 3.1.0 - jest-circus@30.4.2(babel-plugin-macros@3.1.0): - dependencies: - '@jest/environment': 30.4.1 - '@jest/expect': 30.4.1 - '@jest/test-result': 30.4.1 - '@jest/types': 30.4.1 - '@types/node': 20.19.9 - chalk: 4.1.2 - co: 4.6.0 - dedent: 1.7.2(babel-plugin-macros@3.1.0) - is-generator-fn: 2.1.0 - jest-each: 30.4.1 - jest-matcher-utils: 30.4.1 - jest-message-util: 30.4.1 - jest-runtime: 30.4.2 - jest-snapshot: 30.4.1 - jest-util: 30.4.1 - p-limit: 3.1.0 - pretty-format: 30.4.1 - pure-rand: 7.0.1 - slash: 3.0.0 - stack-utils: 2.0.6 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - jest-circus@30.5.1(babel-plugin-macros@3.1.0): dependencies: '@jest/environment': 30.5.1 @@ -20140,15 +16807,15 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)): + jest-cli@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)): dependencies: - '@jest/core': 30.5.1(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)) + '@jest/core': 30.5.1(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)) '@jest/test-result': 30.5.1 '@jest/types': 30.5.1 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)) + jest-config: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)) jest-util: 30.5.1 jest-validate: 30.5.1 yargs: 17.7.3 @@ -20159,40 +16826,7 @@ snapshots: - supports-color - ts-node - jest-config@30.4.2(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)): - dependencies: - '@babel/core': 7.29.7 - '@jest/get-type': 30.1.0 - '@jest/pattern': 30.4.0 - '@jest/test-sequencer': 30.4.1 - '@jest/types': 30.4.1 - babel-jest: 30.4.1(@babel/core@7.29.7) - chalk: 4.1.2 - ci-info: 4.4.0 - deepmerge: 4.3.1 - glob: 10.5.0 - graceful-fs: 4.2.11 - jest-circus: 30.4.2(babel-plugin-macros@3.1.0) - jest-docblock: 30.4.0 - jest-environment-node: 30.4.1 - jest-regex-util: 30.4.0 - jest-resolve: 30.4.1 - jest-runner: 30.4.2 - jest-util: 30.4.1 - jest-validate: 30.4.1 - parse-json: 5.2.0 - pretty-format: 30.4.1 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 20.19.9 - esbuild-register: 3.6.0(esbuild@0.28.2) - ts-node: 10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-config@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)): + jest-config@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)): dependencies: '@babel/core': 7.29.7 '@jest/get-type': 30.5.0 @@ -20220,7 +16854,7 @@ snapshots: optionalDependencies: '@types/node': 20.19.9 esbuild-register: 3.6.0(esbuild@0.28.2) - ts-node: 10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3) + ts-node: 10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -20232,13 +16866,6 @@ snapshots: chalk: 4.1.2 pretty-format: 30.2.0 - jest-diff@30.4.1: - dependencies: - '@jest/diff-sequences': 30.4.0 - '@jest/get-type': 30.1.0 - chalk: 4.1.2 - pretty-format: 30.4.1 - jest-diff@30.5.1: dependencies: '@jest/diff-sequences': 30.5.0 @@ -20246,22 +16873,10 @@ snapshots: chalk: 4.1.2 pretty-format: 30.5.1 - jest-docblock@30.4.0: - dependencies: - detect-newline: 3.1.0 - jest-docblock@30.5.0: dependencies: detect-newline: 3.1.0 - jest-each@30.4.1: - dependencies: - '@jest/get-type': 30.1.0 - '@jest/types': 30.4.1 - chalk: 4.1.2 - jest-util: 30.4.1 - pretty-format: 30.4.1 - jest-each@30.5.1: dependencies: '@jest/get-type': 30.5.0 @@ -20281,16 +16896,6 @@ snapshots: - supports-color - utf-8-validate - jest-environment-node@30.4.1: - dependencies: - '@jest/environment': 30.4.1 - '@jest/fake-timers': 30.4.1 - '@jest/types': 30.4.1 - '@types/node': 20.19.9 - jest-mock: 30.4.1 - jest-util: 30.4.1 - jest-validate: 30.4.1 - jest-environment-node@30.5.1: dependencies: '@jest/environment': 30.5.1 @@ -20301,21 +16906,6 @@ snapshots: jest-util: 30.5.1 jest-validate: 30.5.1 - jest-haste-map@30.4.1: - dependencies: - '@jest/types': 30.4.1 - '@types/node': 20.19.9 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 30.4.0 - jest-util: 30.4.1 - jest-worker: 30.4.1 - picomatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - jest-haste-map@30.5.1: dependencies: '@jest/types': 30.5.1 @@ -20323,17 +16913,12 @@ snapshots: '@types/node': 20.19.9 anymatch: 3.1.3 fb-watchman: 2.0.2 - fdir: 6.5.0(picomatch@4.0.5) + fdir: 6.5.0(picomatch@4.0.7) graceful-fs: 4.2.11 jest-regex-util: 30.5.0 jest-util: 30.5.1 jest-worker: 30.5.1 - picomatch: 4.0.5 - - jest-leak-detector@30.4.1: - dependencies: - '@jest/get-type': 30.1.0 - pretty-format: 30.4.1 + picomatch: 4.0.7 jest-leak-detector@30.5.1: dependencies: @@ -20347,13 +16932,6 @@ snapshots: jest-diff: 30.2.0 pretty-format: 30.2.0 - jest-matcher-utils@30.4.1: - dependencies: - '@jest/get-type': 30.1.0 - chalk: 4.1.2 - jest-diff: 30.4.1 - pretty-format: 30.4.1 - jest-matcher-utils@30.5.1: dependencies: '@jest/get-type': 30.5.0 @@ -20373,19 +16951,6 @@ snapshots: slash: 3.0.0 stack-utils: 2.0.6 - jest-message-util@30.4.1: - dependencies: - '@babel/code-frame': 7.29.7 - '@jest/types': 30.4.1 - '@types/stack-utils': 2.0.3 - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-util: 30.4.1 - picomatch: 4.0.5 - pretty-format: 30.4.1 - slash: 3.0.0 - stack-utils: 2.0.6 - jest-message-util@30.5.1: dependencies: '@babel/code-frame': 7.29.7 @@ -20394,7 +16959,7 @@ snapshots: chalk: 4.1.2 graceful-fs: 4.2.11 jest-util: 30.5.1 - picomatch: 4.0.5 + picomatch: 4.0.7 pretty-format: 30.5.1 slash: 3.0.0 stack-utils: 2.0.6 @@ -20405,12 +16970,6 @@ snapshots: '@types/node': 20.19.9 jest-util: 30.2.0 - jest-mock@30.4.1: - dependencies: - '@jest/types': 30.4.1 - '@types/node': 20.19.9 - jest-util: 30.4.1 - jest-mock@30.5.1: dependencies: '@jest/expect-utils': 30.5.1 @@ -20418,38 +16977,32 @@ snapshots: '@types/node': 20.19.9 jest-util: 30.5.1 - jest-pnp-resolver@1.2.3(jest-resolve@30.4.1): - optionalDependencies: - jest-resolve: 30.4.1 - - jest-preset-angular@15.0.3(49d964010ff732709c8b8b9285288d9d): + jest-preset-angular@17.0.0(@angular/compiler-cli@22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@babel/core@7.29.7)(@jest/transform@30.5.1)(@jest/types@30.5.1)(@types/jsdom@21.1.7)(babel-jest@30.5.1(@babel/core@7.29.7))(jest@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)))(jsdom@26.1.0)(typescript@6.0.3): dependencies: - '@angular/compiler-cli': 21.2.19(@angular/compiler@21.2.19)(typescript@5.9.3) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/platform-browser': 21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) - '@angular/platform-browser-dynamic': 21.2.19(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/compiler@21.2.19)(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))) - '@jest/environment-jsdom-abstract': 30.2.0(jsdom@26.1.0) + '@angular/compiler-cli': 22.1.6(@angular/compiler@22.1.6)(typescript@6.0.3) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/platform-browser': 22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) + '@jest/environment-jsdom-abstract': 30.5.1(@types/jsdom@21.1.7)(jsdom@26.1.0) bs-logger: 0.2.6 - esbuild-wasm: 0.27.2 - jest: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)) + esbuild-wasm: 0.28.2 + jest: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)) jest-util: 30.5.1 jsdom: 26.1.0 - pretty-format: 30.2.0 - ts-jest: 29.4.12(@babel/core@7.29.7)(@jest/transform@30.5.1)(@jest/types@30.5.1)(babel-jest@30.5.1(@babel/core@7.29.7))(esbuild@0.28.2)(jest-util@30.5.1)(jest@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)))(typescript@5.9.3) - typescript: 5.9.3 + pretty-format: 30.5.1 + ts-jest: 29.4.12(@babel/core@7.29.7)(@jest/transform@30.5.1)(@jest/types@30.5.1)(babel-jest@30.5.1(@babel/core@7.29.7))(esbuild@0.28.2)(jest-util@30.5.1)(jest@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)))(typescript@6.0.3) + typescript: 6.0.3 optionalDependencies: esbuild: 0.28.2 transitivePeerDependencies: - '@babel/core' - '@jest/transform' - '@jest/types' + - '@types/jsdom' - babel-jest - canvas jest-regex-util@30.0.1: {} - jest-regex-util@30.4.0: {} - jest-regex-util@30.5.0: {} jest-resolve-dependencies@30.5.1: @@ -20459,17 +17012,6 @@ snapshots: transitivePeerDependencies: - supports-color - jest-resolve@30.4.1: - dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 30.4.1 - jest-pnp-resolver: 1.2.3(jest-resolve@30.4.1) - jest-util: 30.4.1 - jest-validate: 30.4.1 - slash: 3.0.0 - unrs-resolver: 1.12.2 - jest-resolve@30.5.1: dependencies: chalk: 4.1.2 @@ -20480,33 +17022,6 @@ snapshots: slash: 3.0.0 unrs-resolver: 1.12.2 - jest-runner@30.4.2: - dependencies: - '@jest/console': 30.4.1 - '@jest/environment': 30.4.1 - '@jest/test-result': 30.4.1 - '@jest/transform': 30.4.1 - '@jest/types': 30.4.1 - '@types/node': 20.19.9 - chalk: 4.1.2 - emittery: 0.13.1 - exit-x: 0.2.2 - graceful-fs: 4.2.11 - jest-docblock: 30.4.0 - jest-environment-node: 30.4.1 - jest-haste-map: 30.4.1 - jest-leak-detector: 30.4.1 - jest-message-util: 30.4.1 - jest-resolve: 30.4.1 - jest-runtime: 30.4.2 - jest-util: 30.4.1 - jest-watcher: 30.4.1 - jest-worker: 30.4.1 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color - jest-runner@30.5.1: dependencies: '@jest/console': 30.5.1 @@ -20534,33 +17049,6 @@ snapshots: transitivePeerDependencies: - supports-color - jest-runtime@30.4.2: - dependencies: - '@jest/environment': 30.4.1 - '@jest/fake-timers': 30.4.1 - '@jest/globals': 30.4.1 - '@jest/source-map': 30.0.1 - '@jest/test-result': 30.4.1 - '@jest/transform': 30.4.1 - '@jest/types': 30.4.1 - '@types/node': 20.19.9 - chalk: 4.1.2 - cjs-module-lexer: 2.2.1 - collect-v8-coverage: 1.0.3 - glob: 10.5.0 - graceful-fs: 4.2.11 - jest-haste-map: 30.4.1 - jest-message-util: 30.4.1 - jest-mock: 30.4.1 - jest-regex-util: 30.4.0 - jest-resolve: 30.4.1 - jest-snapshot: 30.4.1 - jest-util: 30.4.1 - slash: 3.0.0 - strip-bom: 4.0.0 - transitivePeerDependencies: - - supports-color - jest-runtime@30.5.1: dependencies: '@jest/environment': 30.5.1 @@ -20589,32 +17077,6 @@ snapshots: transitivePeerDependencies: - supports-color - jest-snapshot@30.4.1: - dependencies: - '@babel/core': 7.29.7 - '@babel/generator': 7.29.8 - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) - '@babel/types': 7.29.8 - '@jest/expect-utils': 30.4.1 - '@jest/get-type': 30.1.0 - '@jest/snapshot-utils': 30.4.1 - '@jest/transform': 30.4.1 - '@jest/types': 30.4.1 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7) - chalk: 4.1.2 - expect: 30.4.1 - graceful-fs: 4.2.11 - jest-diff: 30.4.1 - jest-matcher-utils: 30.4.1 - jest-message-util: 30.4.1 - jest-util: 30.4.1 - pretty-format: 30.4.1 - semver: 7.8.5 - synckit: 0.11.13 - transitivePeerDependencies: - - supports-color - jest-snapshot@30.5.1: dependencies: '@babel/core': 7.29.7 @@ -20648,16 +17110,7 @@ snapshots: chalk: 4.1.2 ci-info: 4.4.0 graceful-fs: 4.2.11 - picomatch: 4.0.5 - - jest-util@30.4.1: - dependencies: - '@jest/types': 30.4.1 - '@types/node': 20.19.9 - chalk: 4.1.2 - ci-info: 4.4.0 - graceful-fs: 4.2.11 - picomatch: 4.0.5 + picomatch: 4.0.7 jest-util@30.5.1: dependencies: @@ -20666,16 +17119,7 @@ snapshots: chalk: 4.1.2 ci-info: 4.4.0 graceful-fs: 4.2.11 - picomatch: 4.0.5 - - jest-validate@30.4.1: - dependencies: - '@jest/get-type': 30.1.0 - '@jest/types': 30.4.1 - camelcase: 6.3.0 - chalk: 4.1.2 - leven: 3.1.0 - pretty-format: 30.4.1 + picomatch: 4.0.7 jest-validate@30.5.1: dependencies: @@ -20686,17 +17130,6 @@ snapshots: leven: 3.1.0 pretty-format: 30.5.1 - jest-watcher@30.4.1: - dependencies: - '@jest/test-result': 30.4.1 - '@jest/types': 30.4.1 - '@types/node': 20.19.9 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - emittery: 0.13.1 - jest-util: 30.4.1 - string-length: 4.0.2 - jest-watcher@30.5.1: dependencies: '@jest/test-result': 30.5.1 @@ -20714,14 +17147,6 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest-worker@30.4.1: - dependencies: - '@types/node': 20.19.9 - '@ungap/structured-clone': 1.3.3 - jest-util: 30.4.1 - merge-stream: 2.0.0 - supports-color: 8.1.1 - jest-worker@30.5.1: dependencies: '@types/node': 20.19.9 @@ -20730,12 +17155,12 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)): + jest@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)): dependencies: - '@jest/core': 30.5.1(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)) + '@jest/core': 30.5.1(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)) '@jest/types': 30.5.1 import-local: 3.2.0 - jest-cli: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)) + jest-cli: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -20745,12 +17170,12 @@ snapshots: jiti@1.21.7: {} - jiti@2.4.2: {} - jiti@2.7.0: {} jose@6.2.9: {} + js-tokens@10.0.0: {} + js-tokens@4.0.0: {} js-yaml@3.15.2: @@ -20799,8 +17224,6 @@ snapshots: json-parse-even-better-errors@2.3.1: {} - json-parse-even-better-errors@5.0.0: {} - json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} @@ -20825,8 +17248,6 @@ snapshots: espree: 9.6.1 semver: 7.7.3 - jsonc-parser@3.2.0: {} - jsonc-parser@3.3.1: {} jsonfile@4.0.0: @@ -20839,8 +17260,6 @@ snapshots: optionalDependencies: graceful-fs: 4.2.11 - jsonparse@1.3.1: {} - keycode@2.2.1: {} keyv@4.5.4: @@ -20849,20 +17268,8 @@ snapshots: kind-of@6.0.3: {} - launch-editor@2.14.1: - dependencies: - picocolors: 1.1.1 - shell-quote: 1.10.0 - lazy-val@1.0.5: {} - less-loader@12.3.3(@rspack/core@1.6.8(@swc/helpers@0.5.23))(less@4.5.1)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - less: 4.5.1 - optionalDependencies: - '@rspack/core': 1.6.8(@swc/helpers@0.5.23) - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - less@4.5.1: dependencies: copy-anything: 2.0.6 @@ -20876,6 +17283,7 @@ snapshots: mime: 1.6.0 needle: 3.5.0 source-map: 0.6.1 + optional: true leven@3.1.0: {} @@ -20890,12 +17298,6 @@ snapshots: optionalDependencies: webpack: 5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2) - license-webpack-plugin@4.0.2(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - webpack-sources: 3.3.3 - optionalDependencies: - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - lightningcss-android-arm64@1.33.0: optional: true @@ -20951,16 +17353,13 @@ snapshots: lines-and-columns@2.0.3: {} - listr2@9.0.5: + listr2@11.0.0: 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 + cli-truncate: 6.1.1 + log-update: 8.0.0 + wrap-ansi: 10.0.1 - lmdb@3.5.1: + lmdb@3.5.6: dependencies: '@harperfast/extended-iterable': 1.0.3 msgpackr: 1.12.1 @@ -20969,23 +17368,17 @@ 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.6 + '@lmdb/lmdb-darwin-x64': 3.5.6 + '@lmdb/lmdb-linux-arm': 3.5.6 + '@lmdb/lmdb-linux-arm64': 3.5.6 + '@lmdb/lmdb-linux-x64': 3.5.6 + '@lmdb/lmdb-win32-arm64': 3.5.6 + '@lmdb/lmdb-win32-x64': 3.5.6 optional: true loader-runner@4.3.1: {} - loader-utils@2.0.4: - dependencies: - big.js: 5.2.2 - emojis-list: 3.0.0 - json5: 2.2.3 - locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -20994,10 +17387,6 @@ snapshots: dependencies: p-locate: 5.0.0 - locate-path@7.2.0: - dependencies: - p-locate: 6.0.0 - lodash.debounce@4.0.8: {} lodash.escaperegexp@4.1.2: {} @@ -21010,8 +17399,6 @@ snapshots: lodash.merge@4.6.2: {} - lodash.uniq@4.5.0: {} - lodash@4.18.1: {} log-symbols@4.1.0: @@ -21024,13 +17411,14 @@ snapshots: is-unicode-supported: 2.1.0 yoctocolors: 2.2.0 - log-update@6.1.0: + log-update@8.0.0: dependencies: ansi-escapes: 7.3.0 cli-cursor: 5.0.0 - slice-ansi: 7.1.2 + slice-ansi: 9.0.0 + string-width: 8.2.2 strip-ansi: 7.2.0 - wrap-ansi: 9.0.2 + wrap-ansi: 10.0.1 loglevel@1.9.2: {} @@ -21066,6 +17454,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magic-string@1.0.0: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + magic-string@1.2.2: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -21088,27 +17480,6 @@ snapshots: make-error@1.3.6: {} - make-fetch-happen@15.0.6: - dependencies: - '@gar/promise-retry': 1.0.3 - '@npmcli/agent': 4.0.2 - '@npmcli/redact': 4.0.0 - cacache: 20.0.4 - http-cache-semantics: 4.2.0 - minipass: 7.1.3 - minipass-fetch: 5.0.2 - minipass-flush: 1.0.7 - minipass-pipeline: 1.2.4 - negotiator: 1.0.0 - proc-log: 6.1.0 - ssri: 13.0.1 - transitivePeerDependencies: - - supports-color - - makeerror@1.0.12: - dependencies: - tmpl: 1.0.5 - markdown-extensions@2.0.0: {} markdown-table@3.0.4: {} @@ -21297,41 +17668,18 @@ snapshots: mdn-data@2.27.1: {} - media-typer@0.3.0: {} - media-typer@1.1.0: {} memfs@3.5.3: dependencies: fs-monkey: 1.1.0 - memfs@4.68.0: - dependencies: - '@jsonjoy.com/fs-core': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-fsa': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-node': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-node-builtins': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-node-to-fsa': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-node-utils': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-print': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/fs-snapshot': 4.68.0(tslib@2.8.1) - '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) - '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) - glob-to-regex.js: 1.2.0(tslib@2.8.1) - thingies: 2.6.1(tslib@2.8.1) - tree-dump: 1.1.0(tslib@2.8.1) - tslib: 2.8.1 - - merge-descriptors@1.0.3: {} - merge-descriptors@2.0.0: {} merge-stream@2.0.0: {} merge2@1.4.1: {} - methods@1.1.2: {} - micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.3.0 @@ -21629,16 +17977,9 @@ snapshots: dependencies: dom-walk: 0.1.2 - mini-css-extract-plugin@2.4.7(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - schema-utils: 4.3.3 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - - minimalistic-assert@1.0.1: {} - minimatch@10.2.5: dependencies: - brace-expansion: 5.0.8 + brace-expansion: 5.0.9 minimatch@10.2.6: dependencies: @@ -21658,47 +17999,6 @@ snapshots: minimist@1.2.8: {} - minimizer-webpack-plugin@5.6.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - jest-worker: 27.5.1 - schema-utils: 4.3.3 - terser: 5.49.2 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - optionalDependencies: - '@swc/core': 1.16.1(@swc/helpers@0.5.23) - esbuild: 0.28.2 - lightningcss: 1.33.0 - postcss: 8.5.26 - - minipass-collect@2.0.1: - dependencies: - minipass: 7.1.3 - - minipass-fetch@5.0.2: - dependencies: - minipass: 7.1.3 - minipass-sized: 2.0.0 - minizlib: 3.1.0 - optionalDependencies: - iconv-lite: 0.7.3 - - minipass-flush@1.0.7: - dependencies: - minipass: 3.3.6 - - minipass-pipeline@1.2.4: - dependencies: - minipass: 3.3.6 - - minipass-sized@2.0.0: - dependencies: - minipass: 7.1.3 - - minipass@3.3.6: - dependencies: - yallist: 4.0.0 - minipass@7.1.3: {} minizlib@3.1.0: @@ -21730,8 +18030,6 @@ snapshots: mrmime@2.0.1: {} - ms@2.0.0: {} - ms@2.1.3: {} msgpackr-extract@3.0.4: @@ -21751,12 +18049,7 @@ snapshots: msgpackr-extract: 3.0.4 optional: true - multicast-dns@7.2.5: - dependencies: - dns-packet: 5.6.1 - thunky: 1.1.0 - - mute-stream@2.0.0: {} + mute-stream@3.0.0: {} mux.js@6.0.1: dependencies: @@ -21786,33 +18079,29 @@ snapshots: sax: 1.6.1 optional: true - negotiator@0.6.3: {} - - negotiator@0.6.4: {} - negotiator@1.0.0: {} neo-async@2.6.2: {} neotraverse@1.0.1: {} - ng-mocks@14.17.3(b285834fe0000b6ade3305b52068a4d2): + ng-mocks@14.17.6(c5ca909b0ec8f50b848b5bca41287c00): dependencies: - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/forms': 21.2.19(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) - '@angular/platform-browser': 21.2.19(@angular/animations@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) + '@angular/forms': 22.1.6(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(@angular/platform-browser@22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(rxjs@7.8.2) + '@angular/platform-browser': 22.1.6(@angular/animations@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)))(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)) - ngx-indexed-db@22.0.0(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)): + ngx-indexed-db@22.0.0(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)): dependencies: - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) tslib: 2.8.1 - ngx-skeleton-loader@11.3.0(@angular/common@21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2)): + ngx-skeleton-loader@11.3.0(@angular/common@22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2))(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3)): dependencies: - '@angular/common': 21.2.19(@angular/core@21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.19(@angular/compiler@21.2.19)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/common': 22.1.6(@angular/core@22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3))(rxjs@7.8.2) + '@angular/core': 22.1.6(@angular/compiler@22.1.6)(rxjs@7.8.2)(zone.js@0.16.3) tslib: 2.8.1 nlcst-to-string@4.0.0: @@ -21821,7 +18110,7 @@ snapshots: node-abi@4.33.0: dependencies: - semver: 7.7.4 + semver: 7.8.5 node-abort-controller@3.1.1: {} @@ -21834,18 +18123,10 @@ snapshots: node-api-version@0.2.1: dependencies: - semver: 7.7.4 + semver: 7.8.5 node-fetch-native@1.6.7: {} - node-fetch@2.7.0(encoding@0.1.13): - dependencies: - whatwg-url: 5.0.0 - optionalDependencies: - encoding: 0.1.13 - - node-forge@1.4.0: {} - node-gyp-build-optional-packages@5.2.2: dependencies: detect-libc: 2.1.2 @@ -21858,7 +18139,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.22 tinyglobby: 0.2.17 undici: 6.28.0 @@ -21878,47 +18159,12 @@ snapshots: normalize-url@6.1.0: {} - npm-bundled@5.0.0: + npm-package-arg@14.0.0: dependencies: - npm-normalize-package-bin: 5.0.0 - - npm-install-checks@8.0.0: - dependencies: - semver: 7.7.4 - - npm-normalize-package-bin@5.0.0: {} - - npm-package-arg@13.0.2: - dependencies: - hosted-git-info: 9.0.3 - proc-log: 6.1.0 - semver: 7.7.4 - validate-npm-package-name: 7.0.2 - - npm-packlist@10.0.4: - dependencies: - ignore-walk: 8.0.0 - proc-log: 6.1.0 - - npm-pick-manifest@11.0.3: - dependencies: - npm-install-checks: 8.0.0 - npm-normalize-package-bin: 5.0.0 - npm-package-arg: 13.0.2 - semver: 7.7.4 - - npm-registry-fetch@19.1.1: - dependencies: - '@npmcli/redact': 4.0.0 - jsonparse: 1.3.1 - make-fetch-happen: 15.0.6 - minipass: 7.1.3 - minipass-fetch: 5.0.2 - minizlib: 3.1.0 - npm-package-arg: 13.0.2 - proc-log: 6.1.0 - transitivePeerDependencies: - - supports-color + hosted-git-info: 10.1.1 + proc-log: 7.0.0 + semver: 7.8.5 + validate-npm-package-name: 8.0.0 npm-run-path@4.0.1: dependencies: @@ -21930,35 +18176,39 @@ snapshots: nwsapi@2.2.27: {} - nx-electron@22.0.0(patch_hash=f4bbde1778360c4c462b255bec47a337c0465d59cdcab14ecb601161f3467002)(@nx/devkit@22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))))(@nx/workspace@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(@swc/core@1.16.1(@swc/helpers@0.5.23))(electron-builder-squirrel-windows@26.15.7)(electron@43.3.0)(esbuild@0.28.2)(rxjs@7.8.2)(typescript@5.9.3): + nx-electron@22.0.0(patch_hash=4d5ac9c5b10268dcc40998d7a2b166d004105ae7875fa182130d6810871a1e84)(@nx/devkit@23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))))(@nx/workspace@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)))(@swc/core@1.16.1(@swc/helpers@0.5.23))(electron-builder-squirrel-windows@26.15.7)(electron@43.3.0)(esbuild@0.28.2)(rxjs@7.8.2)(typescript@6.0.3): dependencies: - '@nx/devkit': 22.7.8(nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) - '@nx/workspace': 22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)) + '@nx/devkit': 23.2.1(nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23))) + '@nx/workspace': 23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)) copy-webpack-plugin: 12.0.2(webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)) electron: 43.3.0 electron-builder: 26.15.7(electron-builder-squirrel-windows@26.15.7) - fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)) + fork-ts-checker-webpack-plugin: 9.1.0(typescript@6.0.3)(webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)) license-webpack-plugin: 4.0.2(webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)) rxjs-for-await: 1.0.0(rxjs@7.8.2) strip-json-comments: 3.1.1 terser-webpack-plugin: 5.3.16(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)) tree-kill: 1.2.2 - ts-loader: 9.5.4(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)) + ts-loader: 9.6.2(typescript@6.0.3)(webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)) tsconfig-paths-webpack-plugin: 4.2.0 webpack: 5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2) webpack-merge: 5.10.0 + webpack-node-externals: 3.0.0 transitivePeerDependencies: - '@swc/core' - electron-builder-squirrel-windows - esbuild + - loader-utils - rxjs - supports-color - typescript - uglify-js - webpack-cli - nx@22.7.8(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)): + nx@23.2.1(@swc-node/register@1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3))(@swc/core@1.16.1(@swc/helpers@0.5.23)): dependencies: + '@clack/core': 1.4.3 + '@clack/prompts': 1.7.0 '@emnapi/core': 1.4.5 '@emnapi/runtime': 1.4.5 '@emnapi/wasi-threads': 1.0.4 @@ -21968,7 +18218,6 @@ snapshots: '@yarnpkg/lockfile': 1.1.0 '@zkochan/js-yaml': 0.0.7 agent-base: 6.0.2(supports-color@7.2.0) - ansi-colors: 4.1.3 ansi-regex: 5.0.1 ansi-styles: 4.3.0 argparse: 2.0.1 @@ -21977,8 +18226,9 @@ snapshots: balanced-match: 4.0.3 base64-js: 1.5.1 bl: 4.1.0 - brace-expansion: 5.0.8 + brace-expansion: 5.0.9 buffer: 5.7.1 + bundle-name: 4.1.0 call-bind-apply-helpers: 1.0.2 chalk: 4.1.2 cli-cursor: 3.1.0 @@ -21989,8 +18239,10 @@ snapshots: color-name: 1.1.4 combined-stream: 1.0.8 debug: 4.4.3(supports-color@7.2.0) + default-browser: 5.2.1 + default-browser-id: 5.0.0 defaults: 1.0.4 - define-lazy-prop: 2.0.0 + define-lazy-prop: 3.0.0 delayed-stream: 1.0.0 dotenv: 16.4.7 dotenv-expand: 12.0.3 @@ -21998,13 +18250,15 @@ snapshots: ejs: 5.0.1 emoji-regex: 8.0.0 end-of-stream: 1.4.5 - enquirer: 2.3.6 es-define-property: 1.0.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-set-tostringtag: 2.1.0 escalade: 3.2.0 escape-string-regexp: 1.0.5 + fast-string-truncated-width: 3.0.3 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.0 figures: 3.2.0 flat: 5.0.2 follow-redirects: 1.16.0(debug@4.4.3(supports-color@7.2.0)) @@ -22023,13 +18277,15 @@ snapshots: ieee754: 1.2.1 ignore: 7.0.5 inherits: 2.0.4 - is-docker: 2.2.1 + is-docker: 3.0.0 is-fullwidth-code-point: 3.0.0 + is-inside-container: 1.0.0 is-interactive: 1.0.0 is-unicode-supported: 0.1.0 - is-wsl: 2.2.0 + is-wsl: 3.1.0 + isexe: 2.0.0 json5: 2.2.3 - jsonc-parser: 3.2.0 + jsonc-parser: 3.3.1 lines-and-columns: 2.0.3 log-symbols: 4.1.0 math-intrinsics: 1.1.0 @@ -22042,8 +18298,8 @@ snapshots: npm-run-path: 4.0.1 once: 1.4.0 onetime: 5.1.2 - open: 8.4.2 - ora: 5.3.0 + open: 10.1.0 + ora: 5.4.1 path-key: 3.1.1 picocolors: 1.1.1 proxy-from-env: 2.1.0 @@ -22051,9 +18307,11 @@ snapshots: require-directory: 2.1.1 resolve.exports: 2.0.3 restore-cursor: 3.1.0 + run-applescript: 7.0.0 safe-buffer: 5.2.1 - semver: 7.7.4 + semver: 7.8.4 signal-exit: 3.0.7 + sisteransi: 1.0.5 smol-toml: 1.6.1 string-width: 4.2.3 string_decoder: 1.3.0 @@ -22062,11 +18320,11 @@ snapshots: supports-color: 7.2.0 tar-stream: 2.2.0 tmp: 0.2.7 - tree-kill: 1.2.2 tsconfig-paths: 4.2.0 tslib: 2.8.1 util-deprecate: 1.0.2 wcwidth: 1.0.1 + which: 3.0.1 wrap-ansi: 7.0.0 wrappy: 1.0.2 y18n: 5.0.8 @@ -22074,17 +18332,17 @@ snapshots: yargs: 17.7.2 yargs-parser: 21.1.1 optionalDependencies: - '@nx/nx-darwin-arm64': 22.7.8 - '@nx/nx-darwin-x64': 22.7.8 - '@nx/nx-freebsd-x64': 22.7.8 - '@nx/nx-linux-arm-gnueabihf': 22.7.8 - '@nx/nx-linux-arm64-gnu': 22.7.8 - '@nx/nx-linux-arm64-musl': 22.7.8 - '@nx/nx-linux-x64-gnu': 22.7.8 - '@nx/nx-linux-x64-musl': 22.7.8 - '@nx/nx-win32-arm64-msvc': 22.7.8 - '@nx/nx-win32-x64-msvc': 22.7.8 - '@swc-node/register': 1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@5.9.3) + '@nx/nx-darwin-arm64': 23.2.1 + '@nx/nx-darwin-x64': 23.2.1 + '@nx/nx-freebsd-x64': 23.2.1 + '@nx/nx-linux-arm-gnueabihf': 23.2.1 + '@nx/nx-linux-arm64-gnu': 23.2.1 + '@nx/nx-linux-arm64-musl': 23.2.1 + '@nx/nx-linux-x64-gnu': 23.2.1 + '@nx/nx-linux-x64-musl': 23.2.1 + '@nx/nx-win32-arm64-msvc': 23.2.1 + '@nx/nx-win32-x64-msvc': 23.2.1 + '@swc-node/register': 1.12.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3)(@swc/core@1.16.1(@swc/helpers@0.5.23))(@swc/types@0.1.28)(typescript@6.0.3) '@swc/core': 1.16.1(@swc/helpers@0.5.23) object-assign@4.1.1: {} @@ -22124,8 +18382,6 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.2 - obuf@1.1.2: {} - obug@2.1.4: {} ofetch@1.5.1: @@ -22140,8 +18396,6 @@ snapshots: dependencies: ee-first: 1.1.1 - on-headers@1.1.0: {} - once@1.4.0: dependencies: wrappy: 1.0.2 @@ -22162,18 +18416,12 @@ snapshots: regex: 6.1.0 regex-recursion: 6.0.2 - open@10.2.0: + open@10.1.0: dependencies: default-browser: 5.5.0 define-lazy-prop: 3.0.0 is-inside-container: 1.0.0 - wsl-utils: 0.1.0 - - open@8.4.2: - dependencies: - define-lazy-prop: 2.0.0 - is-docker: 2.2.1 - is-wsl: 2.2.0 + is-wsl: 3.1.1 opener@1.5.2: {} @@ -22190,18 +18438,19 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - ora@5.3.0: + ora@5.4.1: dependencies: bl: 4.1.0 chalk: 4.1.2 cli-cursor: 3.1.0 cli-spinners: 2.6.1 is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 log-symbols: 4.1.0 strip-ansi: 6.0.1 wcwidth: 1.0.1 - ora@9.3.0: + ora@9.4.1: dependencies: chalk: 5.6.2 cli-cursor: 5.0.0 @@ -22221,6 +18470,31 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + oxc-parser@0.142.0: + dependencies: + '@oxc-project/types': 0.142.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.142.0 + '@oxc-parser/binding-android-arm64': 0.142.0 + '@oxc-parser/binding-darwin-arm64': 0.142.0 + '@oxc-parser/binding-darwin-x64': 0.142.0 + '@oxc-parser/binding-freebsd-x64': 0.142.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.142.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.142.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.142.0 + '@oxc-parser/binding-linux-arm64-musl': 0.142.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.142.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.142.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.142.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.142.0 + '@oxc-parser/binding-linux-x64-gnu': 0.142.0 + '@oxc-parser/binding-linux-x64-musl': 0.142.0 + '@oxc-parser/binding-openharmony-arm64': 0.142.0 + '@oxc-parser/binding-wasm32-wasi': 0.142.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.142.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.142.0 + '@oxc-parser/binding-win32-x64-msvc': 0.142.0 + oxc-resolver@11.17.1(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3): optionalDependencies: '@oxc-resolver/binding-android-arm-eabi': 11.17.1 @@ -22257,10 +18531,6 @@ snapshots: dependencies: yocto-queue: 0.1.0 - p-limit@4.0.0: - dependencies: - yocto-queue: 1.2.2 - p-limit@7.3.1: dependencies: yocto-queue: 1.2.2 @@ -22273,23 +18543,11 @@ snapshots: dependencies: p-limit: 3.1.0 - p-locate@6.0.0: - dependencies: - p-limit: 4.0.0 - - p-map@7.0.6: {} - p-queue@9.3.3: dependencies: eventemitter3: 5.0.4 p-timeout: 7.0.1 - p-retry@6.2.1: - dependencies: - '@types/retry': 0.12.2 - is-network-error: 1.3.2 - retry: 0.13.1 - p-timeout@7.0.1: {} p-try@2.2.0: {} @@ -22298,28 +18556,6 @@ snapshots: package-manager-detector@1.6.0: {} - 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 - '@npmcli/promise-spawn': 9.0.1 - '@npmcli/run-script': 10.0.4 - cacache: 20.0.4 - fs-minipass: 3.0.3 - minipass: 7.1.3 - npm-package-arg: 13.0.2 - npm-packlist: 10.0.4 - npm-pick-manifest: 11.0.3 - npm-registry-fetch: 19.1.1 - proc-log: 6.1.0 - sigstore: 4.1.1 - ssri: 13.0.1 - tar: 7.5.22 - transitivePeerDependencies: - - supports-color - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -22350,11 +18586,12 @@ snapshots: unist-util-visit-children: 3.0.0 vfile: 6.0.3 - parse-node-version@1.0.1: {} + parse-node-version@1.0.1: + optional: true - 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 @@ -22362,16 +18599,10 @@ snapshots: dependencies: parse5: 8.0.1 - parse5@4.0.0: {} - parse5@7.3.0: dependencies: entities: 6.0.1 - parse5@8.0.0: - dependencies: - entities: 6.0.1 - parse5@8.0.1: dependencies: entities: 8.0.0 @@ -22380,8 +18611,6 @@ snapshots: path-exists@4.0.0: {} - path-exists@5.0.0: {} - path-is-absolute@1.0.1: {} path-key@3.1.1: {} @@ -22398,8 +18627,6 @@ snapshots: lru-cache: 11.5.2 minipass: 7.1.3 - path-to-regexp@0.1.13: {} - path-to-regexp@8.3.0: {} path-type@4.0.0: {} @@ -22441,10 +18668,6 @@ snapshots: dependencies: find-up: 4.1.0 - pkg-dir@7.0.0: - dependencies: - find-up: 6.3.0 - pkijs@3.4.0: dependencies: '@noble/hashes': 1.4.0 @@ -22479,50 +18702,6 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-calc@10.1.1(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-selector-parser: 7.1.5 - postcss-value-parser: 4.2.0 - - postcss-colormin@7.0.10(postcss@8.5.26): - dependencies: - '@colordx/core': 5.5.0 - browserslist: 4.28.8 - caniuse-api: 3.0.0 - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-convert-values@7.0.12(postcss@8.5.26): - dependencies: - browserslist: 4.28.8 - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-discard-comments@7.0.8(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-selector-parser: 7.1.5 - - postcss-discard-duplicates@7.0.4(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - - postcss-discard-empty@7.0.3(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - - postcss-discard-overridden@7.0.3(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - - postcss-import@14.1.0(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - read-cache: 1.0.0 - resolve: 1.22.12 - postcss-import@15.1.0(postcss@8.5.26): dependencies: postcss: 8.5.26 @@ -22544,149 +18723,13 @@ snapshots: tsx: 4.23.13 yaml: 2.9.0 - postcss-loader@8.2.1(@rspack/core@1.6.8(@swc/helpers@0.5.23))(postcss@8.5.26)(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - cosmiconfig: 9.0.2(typescript@5.9.3) - jiti: 2.7.0 - postcss: 8.5.26 - semver: 7.8.5 - optionalDependencies: - '@rspack/core': 1.6.8(@swc/helpers@0.5.23) - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - transitivePeerDependencies: - - typescript - postcss-media-query-parser@0.2.3: {} - postcss-merge-longhand@7.0.7(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - stylehacks: 7.0.11(postcss@8.5.26) - - postcss-merge-rules@7.0.11(postcss@8.5.26): - dependencies: - browserslist: 4.28.8 - caniuse-api: 3.0.0 - cssnano-utils: 5.0.3(postcss@8.5.26) - postcss: 8.5.26 - postcss-selector-parser: 7.1.5 - - postcss-minify-font-values@7.0.3(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-minify-gradients@7.0.5(postcss@8.5.26): - dependencies: - '@colordx/core': 5.5.0 - cssnano-utils: 5.0.3(postcss@8.5.26) - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-minify-params@7.0.9(postcss@8.5.26): - dependencies: - browserslist: 4.28.8 - cssnano-utils: 5.0.3(postcss@8.5.26) - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-minify-selectors@7.1.2(postcss@8.5.26): - dependencies: - browserslist: 4.28.8 - caniuse-api: 3.0.0 - cssesc: 3.0.0 - postcss: 8.5.26 - postcss-selector-parser: 7.1.5 - - postcss-modules-extract-imports@3.1.0(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - - postcss-modules-local-by-default@4.2.0(postcss@8.5.26): - dependencies: - icss-utils: 5.1.0(postcss@8.5.26) - postcss: 8.5.26 - postcss-selector-parser: 7.1.5 - postcss-value-parser: 4.2.0 - - postcss-modules-scope@3.2.1(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-selector-parser: 7.1.5 - - postcss-modules-values@4.0.0(postcss@8.5.26): - dependencies: - icss-utils: 5.1.0(postcss@8.5.26) - postcss: 8.5.26 - postcss-nested@6.2.0(postcss@8.5.26): dependencies: postcss: 8.5.26 postcss-selector-parser: 6.1.4 - postcss-normalize-charset@7.0.3(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - - postcss-normalize-display-values@7.0.3(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-normalize-positions@7.0.4(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-normalize-repeat-style@7.0.4(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-normalize-string@7.0.3(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-normalize-timing-functions@7.0.3(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-normalize-unicode@7.0.9(postcss@8.5.26): - dependencies: - browserslist: 4.28.8 - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-normalize-url@7.0.3(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-normalize-whitespace@7.0.3(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-ordered-values@7.0.4(postcss@8.5.26): - dependencies: - cssnano-utils: 5.0.3(postcss@8.5.26) - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - - postcss-reduce-initial@7.0.9(postcss@8.5.26): - dependencies: - browserslist: 4.28.8 - caniuse-api: 3.0.0 - postcss: 8.5.26 - - postcss-reduce-transforms@7.0.3(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - postcss-safe-parser@7.0.1(postcss@8.5.26): dependencies: postcss: 8.5.26 @@ -22701,22 +18744,6 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-selector-parser@7.1.5: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - - postcss-svgo@7.1.3(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-value-parser: 4.2.0 - svgo: 4.0.2 - - postcss-unique-selectors@7.0.7(postcss@8.5.26): - dependencies: - postcss: 8.5.26 - postcss-selector-parser: 7.1.5 - postcss-value-parser@4.2.0: {} postcss@8.5.26: @@ -22740,17 +18767,10 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 - pretty-format@30.4.1: - dependencies: - '@jest/schemas': 30.4.1 - ansi-styles: 5.2.0 - react-is-18: react-is@18.3.1 - react-is-19: react-is@19.2.8 - pretty-format@30.5.1: dependencies: '@jest/react-is-18': react-is@18.3.1 - '@jest/react-is-19': react-is@19.2.8 + '@jest/react-is-19': react-is@19.3.0 '@jest/schemas': 30.5.0 ansi-styles: 5.2.0 @@ -22758,6 +18778,8 @@ snapshots: proc-log@6.1.0: {} + proc-log@7.0.0: {} + process-ancestry@0.1.0: {} process-nextick-args@2.0.1: {} @@ -22784,6 +18806,8 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 + proxy-agent-negotiate@1.1.0: {} + proxy-from-env@2.1.0: {} prr@1.0.1: @@ -22823,15 +18847,6 @@ snapshots: range-parser@1.2.1: {} - range-parser@1.3.0: {} - - raw-body@2.5.3: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.1 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - raw-body@3.0.2: dependencies: bytes: 3.1.2 @@ -22841,9 +18856,7 @@ snapshots: react-is@18.3.1: {} - react-is@19.2.8: {} - - react-refresh@0.18.0: {} + react-is@19.3.0: {} read-binary-file-arch@1.0.6: dependencies: @@ -23109,12 +19122,8 @@ snapshots: retry@0.12.0: {} - retry@0.13.1: {} - reusify@1.1.0: {} - rfdc@1.4.1: {} - rimraf@2.6.3: dependencies: glob: 7.2.3 @@ -23129,27 +19138,47 @@ snapshots: sprintf-js: 1.1.3 optional: true - rolldown@1.0.0-rc.4(@emnapi/core@1.11.3)(@emnapi/runtime@1.11.3): + rolldown@1.1.5: dependencies: - '@oxc-project/types': 0.113.0 - '@rolldown/pluginutils': 1.0.0-rc.4 + '@oxc-project/types': 0.139.0 + '@rolldown/pluginutils': 1.0.1 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.11.3)(@emnapi/runtime@1.11.3) - '@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' + '@rolldown/binding-android-arm64': 1.1.5 + '@rolldown/binding-darwin-arm64': 1.1.5 + '@rolldown/binding-darwin-x64': 1.1.5 + '@rolldown/binding-freebsd-x64': 1.1.5 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.5 + '@rolldown/binding-linux-arm64-gnu': 1.1.5 + '@rolldown/binding-linux-arm64-musl': 1.1.5 + '@rolldown/binding-linux-ppc64-gnu': 1.1.5 + '@rolldown/binding-linux-s390x-gnu': 1.1.5 + '@rolldown/binding-linux-x64-gnu': 1.1.5 + '@rolldown/binding-linux-x64-musl': 1.1.5 + '@rolldown/binding-openharmony-arm64': 1.1.5 + '@rolldown/binding-wasm32-wasi': 1.1.5 + '@rolldown/binding-win32-arm64-msvc': 1.1.5 + '@rolldown/binding-win32-x64-msvc': 1.1.5 + + rolldown@1.2.0: + dependencies: + '@oxc-project/types': 0.140.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.2.0 + '@rolldown/binding-darwin-arm64': 1.2.0 + '@rolldown/binding-darwin-x64': 1.2.0 + '@rolldown/binding-freebsd-x64': 1.2.0 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.0 + '@rolldown/binding-linux-arm64-gnu': 1.2.0 + '@rolldown/binding-linux-arm64-musl': 1.2.0 + '@rolldown/binding-linux-ppc64-gnu': 1.2.0 + '@rolldown/binding-linux-s390x-gnu': 1.2.0 + '@rolldown/binding-linux-x64-gnu': 1.2.0 + '@rolldown/binding-linux-x64-musl': 1.2.0 + '@rolldown/binding-openharmony-arm64': 1.2.0 + '@rolldown/binding-wasm32-wasi': 1.2.0 + '@rolldown/binding-win32-arm64-msvc': 1.2.0 + '@rolldown/binding-win32-x64-msvc': 1.2.0 rolldown@1.2.5: dependencies: @@ -23202,6 +19231,7 @@ snapshots: '@rollup/rollup-win32-x64-gnu': 4.59.0 '@rollup/rollup-win32-x64-msvc': 4.59.0 fsevents: 2.3.3 + optional: true router@2.2.0: dependencies: @@ -23215,6 +19245,8 @@ snapshots: rrweb-cssom@0.8.0: {} + run-applescript@7.0.0: {} + run-applescript@7.1.0: {} run-parallel@1.2.0: @@ -23326,7 +19358,7 @@ snapshots: sass-embedded@1.100.0: dependencies: - '@bufbuild/protobuf': 2.13.0 + '@bufbuild/protobuf': 2.15.0 colorjs.io: 0.5.2 immutable: 5.1.9 rxjs: 7.8.2 @@ -23352,15 +19384,7 @@ snapshots: sass-embedded-unknown-all: 1.100.0 sass-embedded-win32-arm64: 1.100.0 sass-embedded-win32-x64: 1.100.0 - - sass-loader@16.0.8(@rspack/core@1.6.8(@swc/helpers@0.5.23))(sass-embedded@1.100.0)(sass@1.102.0)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - neo-async: 2.6.2 - optionalDependencies: - '@rspack/core': 1.6.8(@swc/helpers@0.5.23) - sass: 1.102.0 - sass-embedded: 1.100.0 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) + optional: true sass@1.100.0: dependencies: @@ -23371,7 +19395,7 @@ snapshots: '@parcel/watcher': 2.6.0 optional: true - sass@1.102.0: + sass@1.101.0: dependencies: chokidar: 5.0.0 immutable: 5.1.9 @@ -23379,14 +19403,6 @@ snapshots: optionalDependencies: '@parcel/watcher': 2.6.0 - sass@1.97.3: - dependencies: - chokidar: 4.0.3 - immutable: 5.1.9 - source-map-js: 1.2.1 - optionalDependencies: - '@parcel/watcher': 2.6.0 - satteri@0.10.5: dependencies: '@types/estree-jsx': 1.0.5 @@ -23418,34 +19434,15 @@ snapshots: ajv: 6.14.0 ajv-keywords: 3.5.2(ajv@6.14.0) - schema-utils@4.3.0: - dependencies: - '@types/json-schema': 7.0.15 - ajv: 8.18.0 - ajv-formats: 2.1.1(ajv@8.18.0) - ajv-keywords: 5.1.0(ajv@8.18.0) - schema-utils@4.3.3: dependencies: '@types/json-schema': 7.0.15 - ajv: 8.18.0 - ajv-formats: 2.1.1(ajv@8.18.0) - ajv-keywords: 5.1.0(ajv@8.18.0) + ajv: 8.20.0 + ajv-formats: 2.1.1(ajv@8.20.0) + ajv-keywords: 5.1.0(ajv@8.20.0) secure-compare@3.0.1: {} - select-hose@2.0.0: {} - - selfsigned@2.4.1: - dependencies: - '@types/node-forge': 1.3.14 - node-forge: 1.4.0 - - selfsigned@5.5.0: - dependencies: - '@peculiar/x509': 1.14.3 - pkijs: 3.4.0 - semver-compare@1.0.0: optional: true @@ -23457,25 +19454,9 @@ snapshots: semver@7.7.4: {} - semver@7.8.5: {} + semver@7.8.4: {} - send@0.19.2: - dependencies: - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 0.5.2 - http-errors: 2.0.1 - mime: 1.6.0 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color + semver@7.8.5: {} send@1.2.1: dependencies: @@ -23500,27 +19481,6 @@ snapshots: serialize-javascript@7.1.0: {} - serve-index@1.9.2: - dependencies: - accepts: 1.3.8 - batch: 0.6.1 - debug: 2.6.9 - escape-html: 1.0.3 - http-errors: 1.8.1 - mime-types: 2.1.35 - parseurl: 1.3.3 - transitivePeerDependencies: - - supports-color - - serve-static@1.16.3: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 0.19.2 - transitivePeerDependencies: - - supports-color - serve-static@2.2.1: dependencies: encodeurl: 2.0.0 @@ -23611,8 +19571,6 @@ snapshots: dependencies: shell-env: 4.0.1 - shell-quote@1.10.0: {} - shiki@4.4.3: dependencies: '@shikijs/core': 4.4.3 @@ -23669,17 +19627,6 @@ snapshots: signal-exit@4.1.0: {} - sigstore@4.1.1: - dependencies: - '@sigstore/bundle': 4.0.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.1 - transitivePeerDependencies: - - supports-color - simple-update-notifier@2.0.0: dependencies: semver: 7.8.5 @@ -23697,52 +19644,15 @@ snapshots: slash@5.1.0: {} - slice-ansi@7.1.2: + slice-ansi@9.0.0: dependencies: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 - slice-ansi@8.0.0: - dependencies: - ansi-styles: 6.2.3 - is-fullwidth-code-point: 5.1.0 - - smart-buffer@4.2.0: {} - smol-toml@1.6.1: {} - sockjs@0.3.24: - dependencies: - faye-websocket: 0.11.4 - uuid: 11.1.1 - websocket-driver: 0.7.5 - - socks-proxy-agent@8.0.5: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3(supports-color@7.2.0) - socks: 2.8.9 - transitivePeerDependencies: - - supports-color - - socks@2.8.9: - dependencies: - ip-address: 10.5.0 - smart-buffer: 4.2.0 - source-map-js@1.2.1: {} - source-map-loader@5.0.0(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - iconv-lite: 0.6.3 - source-map-js: 1.2.1 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - - source-map-support@0.5.13: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - source-map-support@0.5.19: dependencies: buffer-from: 1.1.2 @@ -23759,55 +19669,17 @@ snapshots: space-separated-tokens@2.0.2: {} - spdx-exceptions@2.5.0: {} - - spdx-expression-parse@4.0.0: - dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.23 - - spdx-license-ids@3.0.23: {} - - spdy-transport@3.0.0: - dependencies: - debug: 4.4.3(supports-color@7.2.0) - detect-node: 2.1.0 - hpack.js: 2.1.6 - obuf: 1.1.2 - readable-stream: 3.6.2 - wbuf: 1.7.3 - transitivePeerDependencies: - - supports-color - - spdy@4.0.2: - dependencies: - debug: 4.4.3(supports-color@7.2.0) - handle-thing: 2.0.1 - http-deceiver: 1.2.7 - select-hose: 2.0.0 - spdy-transport: 3.0.0 - transitivePeerDependencies: - - supports-color - sprintf-js@1.0.3: {} sprintf-js@1.1.3: optional: true - ssri@13.0.1: - dependencies: - minipass: 7.1.3 - stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 - stackframe@1.3.4: {} - stat-mode@1.0.0: {} - statuses@1.5.0: {} - statuses@2.0.2: {} stdin-discarder@0.3.2: {} @@ -23901,10 +19773,6 @@ snapshots: strip-json-comments@3.1.1: {} - style-loader@3.3.4(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - style-to-js@1.1.21: dependencies: style-to-object: 1.0.14 @@ -23913,12 +19781,6 @@ snapshots: dependencies: inline-style-parser: 0.2.7 - stylehacks@7.0.11(postcss@8.5.26): - dependencies: - browserslist: 4.28.8 - postcss: 8.5.26 - postcss-selector-parser: 7.1.5 - sucrase@3.35.1: dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -23960,8 +19822,10 @@ snapshots: sync-child-process@1.0.2: dependencies: sync-message-port: 1.2.0 + optional: true - sync-message-port@1.2.0: {} + sync-message-port@1.2.0: + optional: true synckit@0.11.13: dependencies: @@ -23997,8 +19861,6 @@ snapshots: tapable@2.3.0: {} - tapable@2.3.3: {} - tar-stream@2.2.0: dependencies: bl: 4.1.0 @@ -24037,19 +19899,6 @@ snapshots: '@swc/core': 1.16.1(@swc/helpers@0.5.23) esbuild: 0.28.2 - terser-webpack-plugin@5.6.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - jest-worker: 27.5.1 - schema-utils: 4.3.3 - terser: 5.49.2 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - optionalDependencies: - '@swc/core': 1.16.1(@swc/helpers@0.5.23) - esbuild: 0.28.2 - lightningcss: 1.33.0 - postcss: 8.5.26 - terser@5.44.1: dependencies: '@jridgewell/source-map': 0.3.11 @@ -24063,12 +19912,7 @@ snapshots: acorn: 8.18.0 commander: 2.20.3 source-map-support: 0.5.21 - - test-exclude@6.0.0: - dependencies: - '@istanbuljs/schema': 0.1.6 - glob: 7.2.3 - minimatch: 3.1.5 + optional: true test-exclude@7.0.2: dependencies: @@ -24084,12 +19928,6 @@ snapshots: dependencies: any-promise: 1.3.0 - thingies@2.6.1(tslib@2.8.1): - dependencies: - tslib: 2.8.1 - - thunky@1.1.0: {} - tiny-async-pool@1.3.0: dependencies: semver: 5.7.2 @@ -24102,11 +19940,6 @@ snapshots: tinyexec@1.3.0: {} - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.5) - picomatch: 4.0.5 - tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.7) @@ -24124,8 +19957,6 @@ snapshots: tmp@0.2.7: {} - tmpl@1.0.5: {} - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -24136,16 +19967,10 @@ snapshots: dependencies: tldts: 6.1.86 - tr46@0.0.3: {} - tr46@5.1.1: dependencies: punycode: 2.3.1 - tree-dump@1.1.0(tslib@2.8.1): - dependencies: - tslib: 2.8.1 - tree-kill@1.2.2: {} trim-lines@3.0.1: {} @@ -24156,38 +19981,24 @@ snapshots: dependencies: utf8-byte-length: 1.0.5 - ts-api-utils@2.4.0(typescript@5.9.3): + ts-api-utils@2.5.0(typescript@6.0.3): dependencies: - typescript: 5.9.3 - - ts-api-utils@2.5.0(typescript@5.9.3): - dependencies: - typescript: 5.9.3 - - ts-checker-rspack-plugin@1.6.0(@rspack/core@1.6.8(@swc/helpers@0.5.23))(typescript@5.9.3): - dependencies: - '@rspack/lite-tapable': 1.1.5 - chokidar: 3.6.0 - memfs: 4.68.0 - picocolors: 1.1.1 - typescript: 5.9.3 - optionalDependencies: - '@rspack/core': 1.6.8(@swc/helpers@0.5.23) + typescript: 6.0.3 ts-interface-checker@0.1.13: {} - ts-jest@29.4.12(@babel/core@7.29.7)(@jest/transform@30.5.1)(@jest/types@30.5.1)(babel-jest@30.5.1(@babel/core@7.29.7))(esbuild@0.28.2)(jest-util@30.5.1)(jest@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)))(typescript@5.9.3): + ts-jest@29.4.12(@babel/core@7.29.7)(@jest/transform@30.5.1)(@jest/types@30.5.1)(babel-jest@30.5.1(@babel/core@7.29.7))(esbuild@0.28.2)(jest-util@30.5.1)(jest@30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)))(typescript@6.0.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.9 - jest: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3)) + jest: 30.5.1(@types/node@20.19.9)(babel-plugin-macros@3.1.0)(esbuild-register@3.6.0(esbuild@0.28.2))(ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 semver: 7.8.5 type-fest: 4.41.0 - typescript: 5.9.3 + typescript: 6.0.3 yargs-parser: 21.1.1 optionalDependencies: '@babel/core': 7.29.7 @@ -24197,27 +20008,15 @@ snapshots: esbuild: 0.28.2 jest-util: 30.5.1 - ts-loader@9.5.4(typescript@5.9.3)(webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)): + ts-loader@9.6.2(typescript@6.0.3)(webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)): dependencies: chalk: 4.1.2 - enhanced-resolve: 5.18.4 - micromatch: 4.0.8 - semver: 7.8.5 + picomatch: 4.0.7 source-map: 0.7.6 - typescript: 5.9.3 + typescript: 6.0.3 webpack: 5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2) - ts-loader@9.6.2(loader-utils@2.0.4)(typescript@5.9.3)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - chalk: 4.1.2 - picomatch: 4.0.5 - source-map: 0.7.6 - typescript: 5.9.3 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - optionalDependencies: - loader-utils: 2.0.4 - - ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@5.9.3): + ts-node@10.9.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(@types/node@20.19.9)(typescript@6.0.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.12 @@ -24231,7 +20030,7 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.9.3 + typescript: 6.0.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: @@ -24257,8 +20056,6 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 - tslib@1.14.1: {} - tslib@2.8.1: {} tsx@4.23.13: @@ -24267,18 +20064,6 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - tsyringe@4.10.0: - dependencies: - tslib: 1.14.1 - - tuf-js@4.1.0: - dependencies: - '@tufjs/models': 4.1.0 - debug: 4.4.3(supports-color@7.2.0) - make-fetch-happen: 15.0.6 - transitivePeerDependencies: - - supports-color - type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -24294,11 +20079,6 @@ snapshots: type-fest@4.41.0: {} - type-is@1.6.18: - dependencies: - media-typer: 0.3.0 - mime-types: 2.1.35 - type-is@2.0.1: dependencies: content-type: 1.0.5 @@ -24338,20 +20118,18 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typed-assert@1.0.9: {} - - typescript-eslint@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): + typescript-eslint@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.69.0(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/parser': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.69.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.69.0(@typescript-eslint/parser@8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + '@typescript-eslint/parser': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.69.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.69.0(eslint@9.39.2(jiti@1.21.7))(typescript@6.0.3) eslint: 9.39.2(jiti@1.21.7) - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - typescript@5.9.3: {} + typescript@6.0.3: {} ufo@1.6.3: {} @@ -24375,7 +20153,8 @@ snapshots: undici@6.28.0: {} - undici@7.29.0: {} + undici@7.29.0: + optional: true undici@8.10.0: {} @@ -24528,10 +20307,6 @@ snapshots: util-deprecate@1.0.2: {} - utils-merge@1.0.1: {} - - uuid@11.1.1: {} - v8-compile-cache-lib@3.0.1: {} v8-to-istanbul@9.3.0: @@ -24540,12 +20315,15 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 - validate-npm-package-name@7.0.2: {} + validate-npm-package-name@8.0.0: {} - varint@6.0.0: {} + varint@6.0.0: + optional: true vary@1.1.2: {} + verkit@0.3.2: {} + vfile-location@5.0.3: dependencies: '@types/unist': 3.0.3 @@ -24611,30 +20389,29 @@ snapshots: dependencies: global: 4.4.0 - vite@7.3.6(patch_hash=215b6585a9c92fa68c096d500f4676983200307b1e64c1321e20ccbd50bbc8f3)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(lightningcss@1.33.0)(sass-embedded@1.100.0)(sass@1.97.3)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0): + vite@8.1.5(patch_hash=469ec4025880fc7b86d3d8b3b4e2028572d6acf65bdfb93973db18fdb3c00ad9)(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0): dependencies: - esbuild: 0.28.2 - fdir: 6.5.0(picomatch@4.0.5) - picomatch: 4.0.5 + lightningcss: 1.33.0 + picomatch: 4.0.7 postcss: 8.5.26 - rollup: 4.59.0 - tinyglobby: 0.2.15 + rolldown: 1.1.5 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 20.19.9 + esbuild: 0.28.2 fsevents: 2.3.3 jiti: 1.21.7 less: 4.5.1 - lightningcss: 1.33.0 - sass: 1.97.3 + sass: 1.101.0 sass-embedded: 1.100.0 terser: 5.49.2 tsx: 4.23.13 yaml: 2.9.0 - vite@8.2.2(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.102.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0): + vite@8.2.2(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0): dependencies: lightningcss: 1.33.0 - picomatch: 4.0.5 + picomatch: 4.0.7 postcss: 8.5.26 rolldown: 1.2.5 tinyglobby: 0.2.17 @@ -24644,42 +20421,24 @@ snapshots: fsevents: 2.3.3 jiti: 1.21.7 less: 4.5.1 - sass: 1.102.0 + sass: 1.101.0 sass-embedded: 1.100.0 terser: 5.49.2 tsx: 4.23.13 yaml: 2.9.0 - vitefu@1.1.3(vite@8.2.2(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.102.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0)): + vitefu@1.1.3(vite@8.2.2(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0)): optionalDependencies: - vite: 8.2.2(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.102.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0) + vite: 8.2.2(@types/node@20.19.9)(esbuild@0.28.2)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.100.0)(sass@1.101.0)(terser@5.49.2)(tsx@4.23.13)(yaml@2.9.0) w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 - walker@1.0.8: - dependencies: - makeerror: 1.0.12 - - watchpack@2.5.0: - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - - watchpack@2.5.1: - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - watchpack@2.5.2: dependencies: graceful-fs: 4.2.11 - wbuf@1.7.3: - dependencies: - minimalistic-assert: 1.0.1 - wcwidth@1.0.1: dependencies: defaults: 1.0.4 @@ -24697,59 +20456,8 @@ snapshots: asn1js: 3.0.10 tslib: 2.8.1 - webidl-conversions@3.0.1: {} - webidl-conversions@7.0.0: {} - webpack-dev-middleware@7.4.5(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - colorette: 2.0.20 - memfs: 4.68.0 - mime-types: 3.0.2 - on-finished: 2.4.1 - range-parser: 1.3.0 - schema-utils: 4.3.3 - optionalDependencies: - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - - webpack-dev-server@5.2.6(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - '@types/bonjour': 3.5.13 - '@types/connect-history-api-fallback': 1.5.4 - '@types/express': 4.17.25 - '@types/express-serve-static-core': 4.19.9 - '@types/serve-index': 1.9.4 - '@types/serve-static': 1.15.10 - '@types/sockjs': 0.3.36 - '@types/ws': 8.18.1 - ansi-html-community: 0.0.8 - bonjour-service: 1.4.4 - chokidar: 3.6.0 - colorette: 2.0.20 - compression: 1.8.1 - connect-history-api-fallback: 2.0.0 - express: 4.22.2 - graceful-fs: 4.2.11 - http-proxy-middleware: 2.0.10(@types/express@4.17.25) - ipaddr.js: 2.5.0 - launch-editor: 2.14.1 - open: 10.2.0 - p-retry: 6.2.1 - schema-utils: 4.3.3 - selfsigned: 5.5.0 - serve-index: 1.9.2 - sockjs: 0.3.24 - spdy: 4.0.2 - webpack-dev-middleware: 7.4.5(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - ws: 8.21.3 - optionalDependencies: - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - transitivePeerDependencies: - - bufferutil - - debug - - supports-color - - utf-8-validate - webpack-merge@5.10.0: dependencies: clone-deep: 4.0.1 @@ -24760,13 +20468,6 @@ snapshots: webpack-sources@3.3.3: {} - webpack-sources@3.5.1: {} - - webpack-subresource-integrity@5.1.0(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)): - dependencies: - typed-assert: 1.0.9 - webpack: 5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26) - webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2): dependencies: '@types/eslint-scope': 3.7.7 @@ -24792,57 +20493,13 @@ snapshots: schema-utils: 4.3.3 tapable: 2.3.0 terser-webpack-plugin: 5.3.16(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(webpack@5.104.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)) - watchpack: 2.5.0 + watchpack: 2.5.2 webpack-sources: 3.3.3 transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26): - dependencies: - '@types/estree': 1.0.9 - '@types/json-schema': 7.0.15 - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/wasm-edit': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.18.0 - browserslist: 4.28.8 - chrome-trace-event: 1.0.4 - enhanced-resolve: 5.24.5 - es-module-lexer: 2.3.1 - eslint-scope: 5.1.1 - events: 3.3.0 - graceful-fs: 4.2.11 - mime-db: 1.54.0 - minimizer-webpack-plugin: 5.6.1(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)(webpack@5.109.2(@swc/core@1.16.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(lightningcss@1.33.0)(postcss@8.5.26)) - neo-async: 2.6.2 - schema-utils: 4.3.3 - tapable: 2.3.3 - watchpack: 2.5.2 - webpack-sources: 3.5.1 - transitivePeerDependencies: - - '@minify-html/node' - - '@swc/core' - - '@swc/css' - - '@swc/html' - - clean-css - - cssnano - - csso - - esbuild - - html-minifier-terser - - lightningcss - - postcss - - uglify-js - - websocket-driver@0.7.5: - dependencies: - http-parser-js: 0.5.10 - safe-buffer: 5.2.1 - websocket-extensions: 0.1.4 - - websocket-extensions@0.1.4: {} - whatwg-encoding@2.0.0: dependencies: iconv-lite: 0.6.3 @@ -24858,11 +20515,6 @@ snapshots: tr46: 5.1.1 webidl-conversions: 7.0.0 - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -24910,6 +20562,10 @@ snapshots: dependencies: isexe: 2.0.0 + which@3.0.1: + dependencies: + isexe: 2.0.0 + which@5.0.0: dependencies: isexe: 3.1.5 @@ -24924,6 +20580,11 @@ snapshots: wordwrap@1.0.0: {} + wrap-ansi@10.0.1: + dependencies: + ansi-styles: 6.2.3 + string-width: 8.2.2 + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 @@ -24955,14 +20616,8 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.1.0 - ws@8.21.0: {} - ws@8.21.3: {} - wsl-utils@0.1.0: - dependencies: - is-wsl: 3.1.1 - xml-js@1.6.11: dependencies: sax: 1.6.1 @@ -25032,15 +20687,6 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yargs@18.0.0: - dependencies: - cliui: 9.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - string-width: 7.2.0 - y18n: 5.0.8 - yargs-parser: 22.0.0 - yargs@18.1.0: dependencies: cliui: 9.0.1 @@ -25056,18 +20702,16 @@ snapshots: yocto-queue@1.2.2: {} - yoctocolors-cjs@2.1.3: {} - yoctocolors@2.2.0: {} - zod-to-json-schema@3.25.2(zod@4.3.6): + zod-to-json-schema@3.25.2(zod@4.4.3): dependencies: - zod: 4.3.6 + zod: 4.4.3 - zod@4.3.6: {} + zod@4.4.3: {} zod@4.5.4: {} - zone.js@0.16.2: {} + zone.js@0.16.3: {} zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8476a11c3..5bcfb8919 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,10 +1,26 @@ ignoredBuiltDependencies: - - '@parcel/watcher' - - '@swc/core' - - esbuild - - lmdb - - msgpackr-extract - - unrs-resolver + - '@parcel/watcher' + - '@swc/core' + - esbuild + - lmdb + - msgpackr-extract + - unrs-resolver onlyBuiltDependencies: - - nx + - nx + +# nx-electron imports this at runtime but omits it from its manifest. +packageExtensions: + 'nx-electron@22.0.0': + dependencies: + webpack-node-externals: '3.0.0' + +# Scoped compatibility bridges until these packages declare current peers. +# nx-electron also needs patches/nx-electron@22.0.0.patch for Nx 23 exports. +# Covered by executor/package tests and the historical IndexedDB v1 E2E. +peerDependencyRules: + allowedVersions: + 'nx-electron@22.0.0>@nx/devkit': '23.2.1' + 'nx-electron@22.0.0>@nx/workspace': '23.2.1' + 'ngx-indexed-db@22.0.0>@angular/common': '22.1.6' + 'ngx-indexed-db@22.0.0>@angular/core': '22.1.6' diff --git a/tools/dependencies/vite-transform-filter.test.mjs b/tools/dependencies/vite-transform-filter.test.mjs index fc627e6af..004ea52b2 100644 --- a/tools/dependencies/vite-transform-filter.test.mjs +++ b/tools/dependencies/vite-transform-filter.test.mjs @@ -14,7 +14,7 @@ const vitePackagePath = angularBuildRequire.resolve('vite/package.json'); const vitePackage = JSON.parse(await readFile(vitePackagePath, 'utf8')); const viteConfigPath = join( dirname(vitePackagePath), - 'dist/node/chunks/config.js' + 'dist/node/chunks/node.js' ); const viteConfig = await readFile(viteConfigPath, 'utf8'); @@ -26,8 +26,8 @@ function extractRegExp(name) { return runInNewContext(declaration[1]); } -test('pins the Vite version carrying the local transform-filter backport', () => { - assert.equal(vitePackage.version, '7.3.6'); +test('pins the Vite version carrying the local transform-filter patch', () => { + assert.equal(vitePackage.version, '8.1.5'); }); test('uses bounded Vite prefilters with precise handler matchers', () => { @@ -41,6 +41,15 @@ test('uses bounded Vite prefilters with precise handler matchers', () => { /filter: \{ code: workerImportMetaUrlFilterRE \}/.test(viteConfig), 'worker import-meta transform must use the bounded prefilter' ); + assert.ok( + /filter: \{ code: assetImportMetaUrlFilterRE \}/.test(viteConfig), + 'bundled asset URL rewriting must also use the bounded prefilter' + ); + assert.doesNotMatch( + viteConfig, + /code:\s*(?:asset|worker)ImportMetaUrlRE\b/, + 'precise handler matchers must not filter raw code containing comments' + ); assert.ok( /const re = new RegExp\(assetImportMetaUrlRE\)/.test(viteConfig), 'asset handler must clone the precise matcher' diff --git a/tools/packaging/electron-package-identity.test.mjs b/tools/packaging/electron-package-identity.test.mjs index 6630c4a7f..8f72c1b50 100644 --- a/tools/packaging/electron-package-identity.test.mjs +++ b/tools/packaging/electron-package-identity.test.mjs @@ -525,6 +525,17 @@ test('Flatpak launcher validation locks descriptor-based ELF inspection', () => ); }); +test('nx-electron executors load against the installed Nx exports', () => { + const { executors } = require('nx-electron/executors.json'); + + for (const [name, { implementation }] of Object.entries(executors)) { + const executor = require( + `nx-electron/${implementation.replace(/^\.\//, '')}` + ); + assert.equal(typeof executor.default, 'function', `${name} must load`); + } +}); + test('nx-electron packaging does not copy duplicate root package metadata', () => { const nxElectronExecutorPath = require.resolve('nx-electron/src/executors/package/executor.js'); diff --git a/tsconfig.base.json b/tsconfig.base.json index bc38b7d67..79394d16e 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -116,7 +116,11 @@ "@iptvnator/shared/marketing-fixtures": [ "./libs/shared/marketing-fixtures/src/index.ts" ] - } + }, + "strict": false, + "noUncheckedSideEffectImports": false, + "types": ["*"], + "ignoreDeprecations": "6.0" }, "exclude": ["node_modules", "tmp"], "files": ["global.d.ts"] From 7f724494b9b3bd7acbc7becb42e661792bd50ac8 Mon Sep 17 00:00:00 2001 From: 4gray <4gray@users.noreply.github.com> Date: Mon, 14 Sep 2026 21:05:19 +0200 Subject: [PATCH 06/43] feat(portal): mark movies as watched from the detail page (#1605) --- .changes/portal-mark-movie-watched.md | 10 + CLAUDE.md | 1 + apps/web/src/assets/i18n/ar.json | 3 + apps/web/src/assets/i18n/ary.json | 3 + apps/web/src/assets/i18n/by.json | 3 + apps/web/src/assets/i18n/de.json | 3 + apps/web/src/assets/i18n/el.json | 3 + apps/web/src/assets/i18n/en.json | 3 + apps/web/src/assets/i18n/es.json | 3 + apps/web/src/assets/i18n/fr.json | 3 + apps/web/src/assets/i18n/hu.json | 3 + apps/web/src/assets/i18n/it.json | 3 + apps/web/src/assets/i18n/ja.json | 3 + apps/web/src/assets/i18n/ko.json | 3 + apps/web/src/assets/i18n/nl.json | 3 + apps/web/src/assets/i18n/pl.json | 3 + apps/web/src/assets/i18n/pt.json | 3 + apps/web/src/assets/i18n/ru.json | 3 + apps/web/src/assets/i18n/tr.json | 3 + apps/web/src/assets/i18n/zh.json | 3 + apps/web/src/assets/i18n/zhtw.json | 3 + docs/architecture/embedded-inline-playback.md | 44 +++ .../grid-list/grid-list.component.ts | 19 +- libs/portal/shared/util/src/index.ts | 3 + .../src/lib/pending-playback-start.spec.ts | 30 ++ .../util/src/lib/pending-playback-start.ts | 44 +++ .../util/src/lib/portal-catalog-facade.ts | 14 +- .../shared/util/src/lib/portal-watch-state.ts | 78 +++++ .../util/src/lib/vod-watched-toggle.spec.ts | 293 ++++++++++++++++++ .../shared/util/src/lib/vod-watched-toggle.ts | 154 +++++++++ .../stalker-catalog-detail.component.html | 5 + .../stalker-catalog-detail.component.spec.ts | 137 +++++++- .../stalker-catalog-detail.component.ts | 66 +++- .../stalker-catalog-facade.service.spec.ts | 41 ++- .../src/lib/stalker-catalog-facade.service.ts | 35 +-- ...talker-collection-detail.component.spec.ts | 286 ++++++++++++++++- .../stalker-collection-detail.component.ts | 20 +- .../stalker-collection-playback.controller.ts | 59 ++++ .../stalker-inline-detail.component.html | 5 + .../stalker-inline-detail.component.spec.ts | 32 ++ .../stalker-inline-detail.component.ts | 12 + .../lib/stalker-vod-playback-controller.ts | 39 ++- .../lib/stalker-vod-watched-toggle.spec.ts | 96 ++++++ .../src/lib/stalker-vod-watched-toggle.ts | 95 ++++++ .../vod-details-playback.service.spec.ts | 83 +++++ .../vod-details-playback.service.ts | 116 ++++--- .../vod-details-route.actions.spec.ts | 39 +++ .../vod-details-route.component.html | 18 ++ .../vod-details-route.component.ts | 12 +- .../vod-details-watched.service.spec.ts | 241 ++++++++++++++ .../vod-details-watched.service.ts | 99 ++++++ .../lib/xtream-catalog-facade.service.spec.ts | 38 ++- .../src/lib/xtream-catalog-facade.service.ts | 15 +- .../vod-details/vod-details.component.html | 20 ++ .../vod-details/vod-details.component.spec.ts | 100 ++++++ .../lib/vod-details/vod-details.component.ts | 53 +++- libs/ui/styles/_detail-view-actions.scss | 33 ++ 57 files changed, 2420 insertions(+), 122 deletions(-) create mode 100644 .changes/portal-mark-movie-watched.md create mode 100644 libs/portal/shared/util/src/lib/pending-playback-start.spec.ts create mode 100644 libs/portal/shared/util/src/lib/pending-playback-start.ts create mode 100644 libs/portal/shared/util/src/lib/portal-watch-state.ts create mode 100644 libs/portal/shared/util/src/lib/vod-watched-toggle.spec.ts create mode 100644 libs/portal/shared/util/src/lib/vod-watched-toggle.ts create mode 100644 libs/portal/stalker/feature/src/lib/stalker-vod-watched-toggle.spec.ts create mode 100644 libs/portal/stalker/feature/src/lib/stalker-vod-watched-toggle.ts create mode 100644 libs/portal/xtream/feature/src/lib/vod-details/vod-details-watched.service.spec.ts create mode 100644 libs/portal/xtream/feature/src/lib/vod-details/vod-details-watched.service.ts diff --git a/.changes/portal-mark-movie-watched.md b/.changes/portal-mark-movie-watched.md new file mode 100644 index 000000000..37f0c5ce3 --- /dev/null +++ b/.changes/portal-mark-movie-watched.md @@ -0,0 +1,10 @@ +--- +type: feature +area: portal +--- + +Movies can now be marked as watched (or unwatched) by hand from their +detail page on Xtream and Stalker sources, including the Favorites and +Recently Viewed views — the same way seasons and episodes already could. The +catalog shows the green check right away, and a watched movie offers Play +instead of a Resume near the end. diff --git a/CLAUDE.md b/CLAUDE.md index 1acd3a9b9..60da49c6a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1563,6 +1563,7 @@ stream_id`); it drops `series_id`/`movie_id`, so the builder pins the - Hosts pass hero chips/meta/actions as `*appDetailTags`/`*appDetailMeta`/`*appDetailActions` templates; the shell stamps them into both the hero and the About block - Seasons are tabs (`SeasonTabsComponent`, dropdown beyond 6 seasons) with auto-selection (playing episode's season → resume season → earliest season with unwatched episodes → latest non-empty season; Stalker lazy-VOD series with unhydrated seasons fall back to the first season, and a session's own watched-toggle echo never re-resolves the selection) that fires the same `seasonSelected` lazy-load/enrichment hooks as manual clicks; grid/list episode view toggle persists to localStorage; season descriptions come from `get_series_info` (Xtream, provider-first with URL-only junk filtered by `sanitizeProviderOverview` and a TMDB season-overview fallback stored as `tmdb_season_overviews` by the lazy season enrichment) or TMDB (Stalker) - The season header hosts a bulk watched toggle next to "Download season" (both portals): marking writes full-progress position rows for the unwatched episodes only — skipping the episode currently playing/launching, whose position ticks would overwrite the row — and a fully watched season flips the action to unwatch-all (`buildSeasonWatchToggleRequest` in `libs/ui/components/.../season-watch-toggle.util.ts`). Xtream persists via the batch IPC `DB_SAVE/CLEAR_PLAYBACK_POSITIONS_BATCH` (one SQLite transaction; the PWA data source rewrites its localStorage blob once) and refreshes `XtreamStore.loadAllPositions` after any toggle so catalog progress badges follow; Stalker loops the serialized position-mutation queue (legacy-row reconciliation, one coalesced reload) and reports direction-specific partial failures. A batch resolving after navigation neither mutates the new page's state nor shows its snackbar. A series-level counterpart sits in a `⋮` menu at the end of the header row (`SeasonWatchPresenter` owns both scopes' state math; `buildSeriesWatchToggleRequest` flattens every loaded season; the direction is always the one the label advertised). It reuses the same host machinery per portal (Xtream: scope-parameterized `SerialDetailsSeasonWatchService`; Stalker: shared `runWatchToggleBatch` core). Stalker lazy-VOD hydrates unloaded seasons sequentially first (abort with zero writes on a failed fetch; a well-formed EMPTY portal answer marks the season loaded-and-empty via `VodSeriesSeasonVm.episodesLoaded` rather than eternally pending, while `fetchVodSeriesEpisodes` rejects malformed envelopes and answers without recognizable episodes; `loadEpisodesForSeason` is single-flight per season so concurrent callers join one request), re-runs the position reconcile synchronously so hydrated episodes' legacy rows are cleaned, then rebuilds the request keeping the clicked direction — the `hasUnloadedSeasons` container input blocks the unwatch verdict and the count label until everything is loaded. Contract: `docs/architecture/embedded-inline-playback.md` +- Movies get the same manual toggle in the detail action row (Xtream: icon square after Favorite, `VodDetailsWatchedService`; Stalker: labelled button in the shared `app-vod-details`, both the routed catalog detail and the collection inline detail wire it). Both portals go through one helper, `createVodWatchedToggle` in `@iptvnator/portal/shared/util`: marking writes a full-progress `vod` position row (stored duration → provider `duration_secs` → 1 s fallback, since Stalker states no runtime), unmarking deletes the row and so forgets the resume point, both through the rejecting `*OrThrow` persistence boundary so the row on screen changes only after a confirmed write. The toggle is disabled while the movie plays inline or in an external session (the ~15 s position tick would overwrite the row), acts only on the route copy's row (a pinned multi-source alternative keeps its own), and a completion landing after navigation refreshes the catalog badges but neither patches the new page nor shows its snackbar. A watched copy shows Play, never "Resume" from its final seconds. Catalog cards on both portals derive their corner badge from one shared `PortalWatchState` (`resolvePortalWatchState` / `watchStateFromProgressPercent`, 90% threshold; `resolvePortalSeriesWatchState` reports a series as at most `in-progress`, because the list payload never carries the episode total). - The dashboard hero CTA and the Continue Watching cards' explicit "Resume episode" ⋮ action for an Xtream series carry a one-shot resume target through the global-recent inline-detail handoff; after series metadata and playback positions load, the exact saved episode starts at its stored position. A failed positions load leaves the target unconsumed and the handoff detail-only, so a transient storage error never starts the episode from the beginning. Continue Watching cards' DEFAULT click is detail-only (movie-like, issue #1441), and their ⋮ menu (`buildDashboardContinueWatchingActions`) also offers "Mark as Watched" (maxes out the existing position row via `DashboardDataService.markRecentItemWatched`) and "Remove from history". Ordinary global-recent grid clicks remain detail-only. - See `docs/architecture/embedded-inline-playback.md` ("Two-State Detail Layout") diff --git a/apps/web/src/assets/i18n/ar.json b/apps/web/src/assets/i18n/ar.json index 7dcd6a825..5425e8ebb 100644 --- a/apps/web/src/assets/i18n/ar.json +++ b/apps/web/src/assets/i18n/ar.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "تم وضع علامة على المسلسل كغير مشاهد", "SERIES_WATCH_UPDATE_FAILED": "فشل تحديث المسلسل", "SERIES_WATCH_MENU": "إجراءات مشاهدة المسلسل", + "MOVIE_MARKED_WATCHED": "تم وضع علامة كمشاهد", + "MOVIE_MARKED_UNWATCHED": "تم وضع علامة كغير مشاهد", + "MOVIE_WATCH_UPDATE_FAILED": "فشل تحديث حالة المشاهدة", "EPISODES_WATCHED": "تمت مشاهدة {{count}} من {{total}} حلقة", "DETAIL_FALLBACK": { "STATUS": "البيانات الوصفية للبوابة غير متاحة", diff --git a/apps/web/src/assets/i18n/ary.json b/apps/web/src/assets/i18n/ary.json index 05cbda747..b54da8eeb 100644 --- a/apps/web/src/assets/i18n/ary.json +++ b/apps/web/src/assets/i18n/ary.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "السلسلة تعلمات كما متشافاش", "SERIES_WATCH_UPDATE_FAILED": "فشل تحديث السلسلة", "SERIES_WATCH_MENU": "إجراءات مشاهدة السلسلة", + "MOVIE_MARKED_WATCHED": "تعلم كمتشاف", + "MOVIE_MARKED_UNWATCHED": "تعلم كما متشافش", + "MOVIE_WATCH_UPDATE_FAILED": "فشل تحديث حالة المشاهدة", "EPISODES_WATCHED": "{{count}} من {{total}} ديال الحلقات متشافين", "DETAIL_FALLBACK": { "STATUS": "البيانات الوصفية ديال البوابة ما متوفراش", diff --git a/apps/web/src/assets/i18n/by.json b/apps/web/src/assets/i18n/by.json index 56c58b83f..90af07b39 100644 --- a/apps/web/src/assets/i18n/by.json +++ b/apps/web/src/assets/i18n/by.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "Серыял адзначаны як непрагледжаны", "SERIES_WATCH_UPDATE_FAILED": "Не ўдалося абнавіць серыял", "SERIES_WATCH_MENU": "Дзеянні прагляду серыяла", + "MOVIE_MARKED_WATCHED": "Адзначана як прагледжанае", + "MOVIE_MARKED_UNWATCHED": "Адзначана як непрагледжанае", + "MOVIE_WATCH_UPDATE_FAILED": "Не ўдалося абнавіць стан прагляду", "EPISODES_WATCHED": "{{count}} з {{total}} эпізодаў прагледжана", "DETAIL_FALLBACK": { "STATUS": "Метаданыя партала недаступныя", diff --git a/apps/web/src/assets/i18n/de.json b/apps/web/src/assets/i18n/de.json index 0503d6cac..90d946f34 100644 --- a/apps/web/src/assets/i18n/de.json +++ b/apps/web/src/assets/i18n/de.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "Serie als nicht angesehen markiert", "SERIES_WATCH_UPDATE_FAILED": "Aktualisierung der Serie fehlgeschlagen", "SERIES_WATCH_MENU": "Aktionen zum Markieren der Serie", + "MOVIE_MARKED_WATCHED": "Als angesehen markiert", + "MOVIE_MARKED_UNWATCHED": "Als nicht angesehen markiert", + "MOVIE_WATCH_UPDATE_FAILED": "Aktualisierung des Status „angesehen“ fehlgeschlagen", "EPISODES_WATCHED": "{{count}} von {{total}} Folgen angesehen", "DETAIL_FALLBACK": { "STATUS": "Portal-Metadaten fehlen", diff --git a/apps/web/src/assets/i18n/el.json b/apps/web/src/assets/i18n/el.json index c20094823..f701bcd84 100644 --- a/apps/web/src/assets/i18n/el.json +++ b/apps/web/src/assets/i18n/el.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "Η σειρά σημειώθηκε ως μη προβληθείσα", "SERIES_WATCH_UPDATE_FAILED": "Η ενημέρωση της σειράς απέτυχε", "SERIES_WATCH_MENU": "Ενέργειες παρακολούθησης σειράς", + "MOVIE_MARKED_WATCHED": "Σημειώθηκε ως προβληθέν", + "MOVIE_MARKED_UNWATCHED": "Σημειώθηκε ως μη προβληθέν", + "MOVIE_WATCH_UPDATE_FAILED": "Η ενημέρωση της κατάστασης προβολής απέτυχε", "EPISODES_WATCHED": "{{count}} από {{total}} επεισόδια προβλήθηκαν", "DETAIL_FALLBACK": { "STATUS": "Τα μεταδεδομένα της πύλης δεν είναι διαθέσιμα", diff --git a/apps/web/src/assets/i18n/en.json b/apps/web/src/assets/i18n/en.json index a9fe5a77b..8eae1e98a 100644 --- a/apps/web/src/assets/i18n/en.json +++ b/apps/web/src/assets/i18n/en.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "Series marked as unwatched", "SERIES_WATCH_UPDATE_FAILED": "Updating the series failed", "SERIES_WATCH_MENU": "Series watch actions", + "MOVIE_MARKED_WATCHED": "Marked as watched", + "MOVIE_MARKED_UNWATCHED": "Marked as unwatched", + "MOVIE_WATCH_UPDATE_FAILED": "Updating the watched state failed", "EPISODES_WATCHED": "{{count}} of {{total}} episodes watched", "DETAIL_FALLBACK": { "STATUS": "Portal metadata unavailable", diff --git a/apps/web/src/assets/i18n/es.json b/apps/web/src/assets/i18n/es.json index 1cced59af..31b746032 100644 --- a/apps/web/src/assets/i18n/es.json +++ b/apps/web/src/assets/i18n/es.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "Serie marcada como no vista", "SERIES_WATCH_UPDATE_FAILED": "La actualización de la serie falló", "SERIES_WATCH_MENU": "Acciones de visualización de la serie", + "MOVIE_MARKED_WATCHED": "Marcado como visto", + "MOVIE_MARKED_UNWATCHED": "Marcado como no visto", + "MOVIE_WATCH_UPDATE_FAILED": "La actualización del estado de visto falló", "EPISODES_WATCHED": "{{count}} de {{total}} episodios vistos", "DETAIL_FALLBACK": { "STATUS": "Metadatos del portal no disponibles", diff --git a/apps/web/src/assets/i18n/fr.json b/apps/web/src/assets/i18n/fr.json index 843ec3e85..495eaaa8d 100644 --- a/apps/web/src/assets/i18n/fr.json +++ b/apps/web/src/assets/i18n/fr.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "Série marquée comme non vue", "SERIES_WATCH_UPDATE_FAILED": "Échec de la mise à jour de la série", "SERIES_WATCH_MENU": "Actions de visionnage de la série", + "MOVIE_MARKED_WATCHED": "Marqué comme vu", + "MOVIE_MARKED_UNWATCHED": "Marqué comme non vu", + "MOVIE_WATCH_UPDATE_FAILED": "Échec de la mise à jour de l’état « vu »", "EPISODES_WATCHED": "{{count}} sur {{total}} épisodes vus", "DETAIL_FALLBACK": { "STATUS": "Métadonnées du portail indisponibles", diff --git a/apps/web/src/assets/i18n/hu.json b/apps/web/src/assets/i18n/hu.json index 2fb55d30f..b5088ad2f 100644 --- a/apps/web/src/assets/i18n/hu.json +++ b/apps/web/src/assets/i18n/hu.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "Sorozat megjelölve nem megtekintettként", "SERIES_WATCH_UPDATE_FAILED": "Nem sikerült frissíteni a sorozatot", "SERIES_WATCH_MENU": "A sorozat megtekintési műveletei", + "MOVIE_MARKED_WATCHED": "Megjelölve megtekintettként", + "MOVIE_MARKED_UNWATCHED": "Megjelölve nem megtekintettként", + "MOVIE_WATCH_UPDATE_FAILED": "Nem sikerült frissíteni a megtekintési állapotot", "EPISODES_WATCHED": "Megtekintett epizódok: {{count}} / {{total}}", "DETAIL_FALLBACK": { "STATUS": "A portál metaadatai nem állnak rendelkezésre", diff --git a/apps/web/src/assets/i18n/it.json b/apps/web/src/assets/i18n/it.json index 3bb7a3d33..f01f63221 100644 --- a/apps/web/src/assets/i18n/it.json +++ b/apps/web/src/assets/i18n/it.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "Serie segnata come non vista", "SERIES_WATCH_UPDATE_FAILED": "Aggiornamento della serie non riuscito", "SERIES_WATCH_MENU": "Azioni di visione della serie", + "MOVIE_MARKED_WATCHED": "Segnato come visto", + "MOVIE_MARKED_UNWATCHED": "Segnato come non visto", + "MOVIE_WATCH_UPDATE_FAILED": "Aggiornamento dello stato di visione non riuscito", "EPISODES_WATCHED": "{{count}} di {{total}} episodi visti", "DETAIL_FALLBACK": { "STATUS": "Metadati del portale non disponibili", diff --git a/apps/web/src/assets/i18n/ja.json b/apps/web/src/assets/i18n/ja.json index f4c8ec8df..bd3960095 100644 --- a/apps/web/src/assets/i18n/ja.json +++ b/apps/web/src/assets/i18n/ja.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "シリーズを未視聴にしました", "SERIES_WATCH_UPDATE_FAILED": "シリーズの更新に失敗しました", "SERIES_WATCH_MENU": "シリーズの視聴操作", + "MOVIE_MARKED_WATCHED": "視聴済みにしました", + "MOVIE_MARKED_UNWATCHED": "未視聴にしました", + "MOVIE_WATCH_UPDATE_FAILED": "視聴状態の更新に失敗しました", "EPISODES_WATCHED": "{{count}} / {{total}}エピソード視聴済み", "DETAIL_FALLBACK": { "STATUS": "ポータルのメタデータは利用できません", diff --git a/apps/web/src/assets/i18n/ko.json b/apps/web/src/assets/i18n/ko.json index 3429e5ae6..b73b4e881 100644 --- a/apps/web/src/assets/i18n/ko.json +++ b/apps/web/src/assets/i18n/ko.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "시리즈가 시청하지 않은 것으로 표시되었습니다", "SERIES_WATCH_UPDATE_FAILED": "시리즈 업데이트에 실패했습니다", "SERIES_WATCH_MENU": "시리즈 시청 작업", + "MOVIE_MARKED_WATCHED": "시청한 것으로 표시되었습니다", + "MOVIE_MARKED_UNWATCHED": "시청하지 않은 것으로 표시되었습니다", + "MOVIE_WATCH_UPDATE_FAILED": "시청 상태 업데이트에 실패했습니다", "EPISODES_WATCHED": "{{total}}개 중 {{count}}개 에피소드 시청", "DETAIL_FALLBACK": { "STATUS": "포털 메타데이터를 사용할 수 없습니다", diff --git a/apps/web/src/assets/i18n/nl.json b/apps/web/src/assets/i18n/nl.json index d65ea1ab4..251a079ae 100644 --- a/apps/web/src/assets/i18n/nl.json +++ b/apps/web/src/assets/i18n/nl.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "Serie gemarkeerd als niet bekeken", "SERIES_WATCH_UPDATE_FAILED": "Bijwerken van de serie is mislukt", "SERIES_WATCH_MENU": "Kijkacties voor serie", + "MOVIE_MARKED_WATCHED": "Gemarkeerd als bekeken", + "MOVIE_MARKED_UNWATCHED": "Gemarkeerd als niet bekeken", + "MOVIE_WATCH_UPDATE_FAILED": "Bijwerken van de kijkstatus is mislukt", "EPISODES_WATCHED": "{{count}} van {{total}} afleveringen bekeken", "DETAIL_FALLBACK": { "STATUS": "Portaalmetadata niet beschikbaar", diff --git a/apps/web/src/assets/i18n/pl.json b/apps/web/src/assets/i18n/pl.json index 52275f216..3000ee8f2 100644 --- a/apps/web/src/assets/i18n/pl.json +++ b/apps/web/src/assets/i18n/pl.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "Oznaczono serial jako nieobejrzany", "SERIES_WATCH_UPDATE_FAILED": "Nie udało się zaktualizować serialu", "SERIES_WATCH_MENU": "Akcje oglądania serialu", + "MOVIE_MARKED_WATCHED": "Oznaczono jako obejrzane", + "MOVIE_MARKED_UNWATCHED": "Oznaczono jako nieobejrzane", + "MOVIE_WATCH_UPDATE_FAILED": "Nie udało się zaktualizować stanu obejrzenia", "EPISODES_WATCHED": "Obejrzano {{count}} z {{total}} odcinków", "DETAIL_FALLBACK": { "STATUS": "Metadane portalu niedostępne", diff --git a/apps/web/src/assets/i18n/pt.json b/apps/web/src/assets/i18n/pt.json index 364582bcb..49ae5217e 100644 --- a/apps/web/src/assets/i18n/pt.json +++ b/apps/web/src/assets/i18n/pt.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "Série marcada como não assistida", "SERIES_WATCH_UPDATE_FAILED": "A atualização da série falhou", "SERIES_WATCH_MENU": "Ações de visualização da série", + "MOVIE_MARKED_WATCHED": "Marcado como assistido", + "MOVIE_MARKED_UNWATCHED": "Marcado como não assistido", + "MOVIE_WATCH_UPDATE_FAILED": "A atualização do estado de assistido falhou", "EPISODES_WATCHED": "{{count}} de {{total}} episódios assistidos", "DETAIL_FALLBACK": { "STATUS": "Metadados do portal indisponíveis", diff --git a/apps/web/src/assets/i18n/ru.json b/apps/web/src/assets/i18n/ru.json index bdfe7dc3d..fcf050c39 100644 --- a/apps/web/src/assets/i18n/ru.json +++ b/apps/web/src/assets/i18n/ru.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "Сериал отмечен как непросмотренный", "SERIES_WATCH_UPDATE_FAILED": "Не удалось обновить сериал", "SERIES_WATCH_MENU": "Действия просмотра сериала", + "MOVIE_MARKED_WATCHED": "Отмечено как просмотренное", + "MOVIE_MARKED_UNWATCHED": "Отмечено как непросмотренное", + "MOVIE_WATCH_UPDATE_FAILED": "Не удалось обновить статус просмотра", "EPISODES_WATCHED": "Просмотрено {{count}} из {{total}} эпизодов", "DETAIL_FALLBACK": { "STATUS": "Метаданные портала недоступны", diff --git a/apps/web/src/assets/i18n/tr.json b/apps/web/src/assets/i18n/tr.json index 5e883964d..3846b7b8f 100644 --- a/apps/web/src/assets/i18n/tr.json +++ b/apps/web/src/assets/i18n/tr.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "Dizi izlenmedi olarak işaretlendi", "SERIES_WATCH_UPDATE_FAILED": "Dizi güncellemesi başarısız oldu", "SERIES_WATCH_MENU": "Dizi izleme işlemleri", + "MOVIE_MARKED_WATCHED": "İzlendi olarak işaretlendi", + "MOVIE_MARKED_UNWATCHED": "İzlenmedi olarak işaretlendi", + "MOVIE_WATCH_UPDATE_FAILED": "İzlenme durumu güncellemesi başarısız oldu", "EPISODES_WATCHED": "{{total}} bölümden {{count}} tanesi izlendi", "DETAIL_FALLBACK": { "STATUS": "Portal üst verisi mevcut değil", diff --git a/apps/web/src/assets/i18n/zh.json b/apps/web/src/assets/i18n/zh.json index 5d3595771..935e58f0e 100644 --- a/apps/web/src/assets/i18n/zh.json +++ b/apps/web/src/assets/i18n/zh.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "剧集已标记为未观看", "SERIES_WATCH_UPDATE_FAILED": "无法更新剧集", "SERIES_WATCH_MENU": "剧集观看操作", + "MOVIE_MARKED_WATCHED": "已标记为已观看", + "MOVIE_MARKED_UNWATCHED": "已标记为未观看", + "MOVIE_WATCH_UPDATE_FAILED": "无法更新观看状态", "EPISODES_WATCHED": "已观看 {{count}} / {{total}} 集", "DETAIL_FALLBACK": { "STATUS": "门户元数据不可用", diff --git a/apps/web/src/assets/i18n/zhtw.json b/apps/web/src/assets/i18n/zhtw.json index 0afdb9eb0..3764551b9 100644 --- a/apps/web/src/assets/i18n/zhtw.json +++ b/apps/web/src/assets/i18n/zhtw.json @@ -1079,6 +1079,9 @@ "SERIES_MARKED_UNWATCHED": "本影集已標記為未觀看", "SERIES_WATCH_UPDATE_FAILED": "本影集更新失敗", "SERIES_WATCH_MENU": "影集觀看操作", + "MOVIE_MARKED_WATCHED": "已標記為已觀看", + "MOVIE_MARKED_UNWATCHED": "已標記為未觀看", + "MOVIE_WATCH_UPDATE_FAILED": "觀看狀態更新失敗", "EPISODES_WATCHED": "已觀看 {{total}} 集中的 {{count}} 集", "DETAIL_FALLBACK": { "STATUS": "無法取得入口網站中繼資料", diff --git a/docs/architecture/embedded-inline-playback.md b/docs/architecture/embedded-inline-playback.md index 38f8aa7e7..6e1fb0b27 100644 --- a/docs/architecture/embedded-inline-playback.md +++ b/docs/architecture/embedded-inline-playback.md @@ -266,6 +266,50 @@ episode" chip appears when the playing episode is outside the opened season. Season descriptions come from `get_series_info` seasons (Xtream) or `TmdbEnrichmentService.getSeason` (Stalker). +### Manual watched toggle for movies + +Movies carry the same manual "watched" affordance as episodes, in the +detail action row of both portals: Xtream renders an icon square after +Favorite (`vod-details-watched.service.ts`), Stalker a labelled button +inside the shared `app-vod-details` component, wired by the routed +catalog detail and by the collection inline detail (Favorites / Recent). +Both hosts delegate to `createVodWatchedToggle()` in +`@iptvnator/portal/shared/util`: + +- **Marking** writes a full-progress `vod` position row — the same shape + playback leaves behind, so catalog badges, the dashboard and the + Play/Resume rule need no new state. The duration is the stored row's + (real), else the provider's `duration_secs` (Xtream), else 1 s: Stalker + VOD details state no runtime, and "position === duration" is what + "watched" means, whatever the number. +- **Unmarking** deletes the row, which also forgets the resume point — + the trade the episode toggle already makes. +- Both writes use the rejecting `savePlaybackPositionOrThrow` / + `clearPlaybackPositionOrThrow` boundary: the row on screen changes only + after a confirmed write, and a refused write reports instead of showing + the movie as (un)watched. +- The toggle is **disabled while the movie plays** (inline player mounted, + external session live or launching for this content): the player + persists its position every ~15 s and would overwrite a just-written + row, silently flipping the movie back. +- It acts on the **route copy's row only**. Positions are keyed by + (playlist, stream), so a pinned multi-source alternative keeps its own + state, exactly as playback would leave it. +- A completion that lands **after navigation** (`stillCurrent`) still + refreshes the playlist's catalog positions (the write did land), but + neither patches the new page's row nor shows its snackbar. +- A watched copy shows **Play**, never "Resume 1:32:00" from its final + seconds — `app-vod-details` folds `isWatched` into `hasPlaybackPosition`, + which Xtream's route already did through its 90% rule. + +Catalog cards derive their corner badge from one shared `PortalWatchState` +(`unwatched` / `in-progress` / `watched`, `portal-watch-state.ts`): both +facades map a movie's position through `watchStateFromProgressPercent` / +`resolvePortalWatchState` (90% threshold, shared with the Resume rule), +and a series through `resolvePortalSeriesWatchState`, which reports at +most `in-progress` — the list payload never carries the episode total, so +"every episode watched" is not decidable there. + The season header carries a season-level watched toggle next to "Download season" (`season-watch-toggle.util.ts` builds the request: marking touches only unwatched episodes so real durations survive; diff --git a/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.ts b/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.ts index dcd42b66b..28ac27fd6 100644 --- a/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.ts +++ b/libs/portal/shared/ui/src/lib/components/grid-list/grid-list.component.ts @@ -15,6 +15,7 @@ import { MatTooltip } from '@angular/material/tooltip'; import { TranslatePipe } from '@ngx-translate/core'; import { applyChannelNameStrip } from '@iptvnator/shared/m3u-utils'; import { + PortalWatchState, getXtreamCatchupDays, isXtreamCatchupAvailable, } from '@iptvnator/portal/shared/util'; @@ -42,8 +43,7 @@ interface GridListItem { rating?: string | number; rating_imdb?: string | number; progress?: number; - isWatched?: boolean; - hasSeriesProgress?: boolean; + watchState?: PortalWatchState; tv_archive?: number | string | null; tv_archive_duration?: number | string | null; [key: string]: unknown; @@ -173,7 +173,8 @@ function normalizeArtworkUrl(value: string | undefined): string | undefined { data-test-id="grid-catchup-badge" [matTooltip]=" catchupLabelKey(i) - | translate: { days: catchupDays(i) } + | translate + : { days: catchupDays(i) } " > history @@ -181,16 +182,22 @@ function normalizeArtworkUrl(value: string | undefined): string | undefined { status as text for AT users --> {{ catchupLabelKey(i) - | translate: { days: catchupDays(i) } + | translate + : { days: catchupDays(i) } }}
} - @if (i.isWatched) { + @if (i.watchState === 'watched') { - } @else if (i.hasSeriesProgress) { + } @else if ( + i.watchState === 'in-progress' && !i.progress + ) { + { + it('is pending only for the owner of the latest start', () => { + const tracker = createPendingPlaybackStart(); + const pendingForA = computed(() => tracker.isPendingFor('a')); + const pendingForB = computed(() => tracker.isPendingFor('b')); + + expect(pendingForA()).toBe(false); + const first = tracker.begin('a'); + expect(pendingForA()).toBe(true); + expect(pendingForB()).toBe(false); + + tracker.settle(first); + expect(pendingForA()).toBe(false); + }); + + it('lets only the latest start clear the flag', () => { + const tracker = createPendingPlaybackStart(); + const older = tracker.begin('a'); + const newer = tracker.begin('b'); + + tracker.settle(older); + expect(tracker.isPendingFor('b')).toBe(true); + + tracker.settle(newer); + expect(tracker.isPendingFor('b')).toBe(false); + }); +}); diff --git a/libs/portal/shared/util/src/lib/pending-playback-start.ts b/libs/portal/shared/util/src/lib/pending-playback-start.ts new file mode 100644 index 000000000..280dc016c --- /dev/null +++ b/libs/portal/shared/util/src/lib/pending-playback-start.ts @@ -0,0 +1,44 @@ +import { signal } from '@angular/core'; + +/** + * Tracks the one playback start still between the click and the player + * (a portal resolution, or closing the previous player), keyed by the + * content it was started for. + * + * Only the latest start may clear the flag, so an older resolution that + * settles late cannot erase a newer one — and `isPendingFor` answers for + * ONE owner, so a stale start for the previous movie never holds the next + * movie's watched toggle hostage. + */ +export interface PendingPlaybackStart { + /** Records a new start and returns its id for `settle`. */ + begin(owner: TOwner): number; + /** Clears the flag if this start is still the latest one. */ + settle(startId: number): void; + /** Reactive: whether the latest start belongs to this owner. */ + isPendingFor(owner: TOwner): boolean; +} + +export function createPendingPlaybackStart< + TOwner, +>(): PendingPlaybackStart { + const pending = signal<{ startId: number; owner: TOwner } | null>(null); + let sequence = 0; + + return { + begin(owner) { + const startId = ++sequence; + pending.set({ startId, owner }); + return startId; + }, + settle(startId) { + if (pending()?.startId === startId) { + pending.set(null); + } + }, + isPendingFor(owner) { + const current = pending(); + return current !== null && current.owner === owner; + }, + }; +} diff --git a/libs/portal/shared/util/src/lib/portal-catalog-facade.ts b/libs/portal/shared/util/src/lib/portal-catalog-facade.ts index 79202c9ae..d72da4e61 100644 --- a/libs/portal/shared/util/src/lib/portal-catalog-facade.ts +++ b/libs/portal/shared/util/src/lib/portal-catalog-facade.ts @@ -1,5 +1,6 @@ import { InjectionToken, Signal } from '@angular/core'; import { ResolvedPortalPlayback } from '@iptvnator/shared/interfaces'; +import type { PortalWatchState } from './portal-watch-state'; export type PortalCatalogProvider = 'xtream' | 'stalker'; @@ -21,10 +22,14 @@ export interface PortalCatalogPlaylistMeta { origin?: string; } +/** + * What a catalog card shows about playback: a percent for the movie + * progress capsule, and one shared watch state for the corner badge + * (`resolvePortalWatchState` / `resolvePortalSeriesWatchState`). + */ export interface PortalCatalogItemProgress { progress?: number; - isWatched?: boolean; - hasSeriesProgress?: boolean; + watchState?: PortalWatchState; } export interface PortalCatalogFacade< @@ -115,7 +120,10 @@ export interface StalkerPortalCatalogFacade< macAddress: string, cmd: string, series?: number, - linkFlags?: { use_http_tmp_link?: unknown; use_load_balancing?: unknown } + linkFlags?: { + use_http_tmp_link?: unknown; + use_load_balancing?: unknown; + } ): Promise; resolveVodPlayback( cmd?: string, diff --git a/libs/portal/shared/util/src/lib/portal-watch-state.ts b/libs/portal/shared/util/src/lib/portal-watch-state.ts new file mode 100644 index 000000000..d3912c57f --- /dev/null +++ b/libs/portal/shared/util/src/lib/portal-watch-state.ts @@ -0,0 +1,78 @@ +import { PlaybackPositionData } from '@iptvnator/shared/interfaces'; +import { getPortalPlaybackProgressPercent } from './portal-playback-positions'; + +/** + * One watch state for every catalog card, movie or series, on every portal. + * + * - `watched`: the position row reached the completion threshold, whether + * playback got there or the user marked it by hand. + * - `in-progress`: something was started but not finished. For a series it + * means at least one episode has a row — the catalog list cannot know how + * many episodes a series has, so a series never reaches `watched` here. + * - `unwatched`: no row at all. + */ +export type PortalWatchState = 'unwatched' | 'in-progress' | 'watched'; + +/** Progress at or above which a movie or episode counts as watched. */ +export const PORTAL_WATCHED_PROGRESS_PERCENT = 90; + +export function watchStateFromProgressPercent( + percent: number +): PortalWatchState { + if (percent >= PORTAL_WATCHED_PROGRESS_PERCENT) { + return 'watched'; + } + return percent > 0 ? 'in-progress' : 'unwatched'; +} + +export function resolvePortalWatchState( + position: PlaybackPositionData | null | undefined +): PortalWatchState { + return watchStateFromProgressPercent( + getPortalPlaybackProgressPercent(position) + ); +} + +/** + * Series catalog cards only know whether ANY episode carries a row; the + * total episode count is not part of the list payload on either portal, so + * the strongest truthful answer is "started". + */ +export function resolvePortalSeriesWatchState( + hasAnyEpisodeProgress: boolean +): PortalWatchState { + return hasAnyEpisodeProgress ? 'in-progress' : 'unwatched'; +} + +/** Full-progress position row that marks one movie as watched. */ +export function buildWatchedVodPosition(options: { + playlistId: string; + contentXtreamId: number; + /** Runtime the detail view knows (Xtream `duration_secs`); may be absent. */ + durationSeconds?: number | null; + /** The row already stored, whose real duration beats any metadata guess. */ + currentPosition?: PlaybackPositionData | null; +}): PlaybackPositionData { + // A watched row is "position === duration", so any positive duration + // works; 1 s is the same fallback the episode toggle uses when the + // provider states no runtime at all. + const duration = + positiveOrNull(options.currentPosition?.durationSeconds) ?? + positiveOrNull(options.durationSeconds) ?? + 1; + + return { + contentXtreamId: options.contentXtreamId, + contentType: 'vod', + positionSeconds: duration, + durationSeconds: duration, + playlistId: options.playlistId, + updatedAt: new Date().toISOString(), + }; +} + +function positiveOrNull(value: number | null | undefined): number | null { + return typeof value === 'number' && Number.isFinite(value) && value > 0 + ? value + : null; +} diff --git a/libs/portal/shared/util/src/lib/vod-watched-toggle.spec.ts b/libs/portal/shared/util/src/lib/vod-watched-toggle.spec.ts new file mode 100644 index 000000000..47af81167 --- /dev/null +++ b/libs/portal/shared/util/src/lib/vod-watched-toggle.spec.ts @@ -0,0 +1,293 @@ +import { signal } from '@angular/core'; +import { PlaybackPositionData } from '@iptvnator/shared/interfaces'; +import { + buildWatchedVodPosition, + resolvePortalSeriesWatchState, + resolvePortalWatchState, + watchStateFromProgressPercent, +} from './portal-watch-state'; +import { + VodWatchedToggleFeedback, + createVodWatchedToggle, +} from './vod-watched-toggle'; + +const PLAYLIST = 'playlist-1'; + +function row( + positionSeconds: number, + durationSeconds = 5400 +): PlaybackPositionData { + return { + playlistId: PLAYLIST, + contentXtreamId: 42, + contentType: 'vod', + positionSeconds, + durationSeconds, + updatedAt: '2026-01-01T00:00:00.000Z', + }; +} + +describe('portal watch state', () => { + it('maps progress onto the three catalog states at the 90% threshold', () => { + expect(watchStateFromProgressPercent(0)).toBe('unwatched'); + expect(watchStateFromProgressPercent(1)).toBe('in-progress'); + expect(watchStateFromProgressPercent(89)).toBe('in-progress'); + expect(watchStateFromProgressPercent(90)).toBe('watched'); + expect(watchStateFromProgressPercent(100)).toBe('watched'); + }); + + it('resolves a position row the same way the Resume rule does', () => { + expect(resolvePortalWatchState(null)).toBe('unwatched'); + expect(resolvePortalWatchState(row(0))).toBe('unwatched'); + expect(resolvePortalWatchState(row(600))).toBe('in-progress'); + expect(resolvePortalWatchState(row(5000))).toBe('watched'); + // No duration means no percent — never "watched" by accident. + expect(resolvePortalWatchState(row(5000, 0))).toBe('unwatched'); + }); + + it('never reports a series as fully watched from the catalog list', () => { + expect(resolvePortalSeriesWatchState(true)).toBe('in-progress'); + expect(resolvePortalSeriesWatchState(false)).toBe('unwatched'); + }); + + it('builds a full-progress row preferring the stored duration', () => { + const stored = buildWatchedVodPosition({ + playlistId: PLAYLIST, + contentXtreamId: 42, + durationSeconds: 7200, + currentPosition: row(600, 5400), + }); + expect(stored).toMatchObject({ + playlistId: PLAYLIST, + contentXtreamId: 42, + contentType: 'vod', + positionSeconds: 5400, + durationSeconds: 5400, + }); + + const fromMetadata = buildWatchedVodPosition({ + playlistId: PLAYLIST, + contentXtreamId: 42, + durationSeconds: 7200, + currentPosition: null, + }); + expect(fromMetadata.positionSeconds).toBe(7200); + expect(fromMetadata.durationSeconds).toBe(7200); + + const unknown = buildWatchedVodPosition({ + playlistId: PLAYLIST, + contentXtreamId: 42, + durationSeconds: 0, + currentPosition: row(600, 0), + }); + expect(unknown.positionSeconds).toBe(1); + expect(unknown.durationSeconds).toBe(1); + expect(resolvePortalWatchState(unknown)).toBe('watched'); + }); +}); + +describe('createVodWatchedToggle', () => { + function setup(initial: PlaybackPositionData | null) { + const position = signal(initial); + const playingNow = signal(false); + const feedback: VodWatchedToggleFeedback[] = []; + const persisted: string[] = []; + const playbackPositions = { + savePlaybackPositionOrThrow: jest.fn().mockResolvedValue(undefined), + clearPlaybackPositionOrThrow: jest + .fn() + .mockResolvedValue(undefined), + }; + const logger = { error: jest.fn() }; + const toggle = createVodWatchedToggle({ + playbackPositions, + position, + applyPosition: (next) => position.set(next), + playingNow, + notify: (kind) => feedback.push(kind), + onPersisted: (playlistId) => persisted.push(playlistId), + logger, + }); + const target = (stillCurrent = () => true) => ({ + playlistId: PLAYLIST, + contentXtreamId: 42, + durationSeconds: 5400, + stillCurrent, + }); + return { + toggle, + position, + playingNow, + feedback, + persisted, + playbackPositions, + logger, + target, + }; + } + + it('marks an unwatched movie by writing a full-progress row', async () => { + const t = setup(null); + expect(t.toggle.isWatched()).toBe(false); + + await expect(t.toggle.toggle(t.target())).resolves.toBe(true); + + expect( + t.playbackPositions.savePlaybackPositionOrThrow + ).toHaveBeenCalledWith( + PLAYLIST, + expect.objectContaining({ + contentXtreamId: 42, + contentType: 'vod', + positionSeconds: 5400, + durationSeconds: 5400, + }) + ); + expect(t.toggle.isWatched()).toBe(true); + expect(t.feedback).toEqual(['marked']); + expect(t.persisted).toEqual([PLAYLIST]); + }); + + it('unmarks a watched movie by clearing its row', async () => { + const t = setup(row(5400)); + expect(t.toggle.isWatched()).toBe(true); + + await expect(t.toggle.toggle(t.target())).resolves.toBe(true); + + expect( + t.playbackPositions.clearPlaybackPositionOrThrow + ).toHaveBeenCalledWith(PLAYLIST, 42, 'vod'); + expect(t.position()).toBeNull(); + expect(t.toggle.isWatched()).toBe(false); + expect(t.feedback).toEqual(['unmarked']); + }); + + it('keeps the rendered row and reports when the write is refused', async () => { + const t = setup(row(600)); + t.playbackPositions.savePlaybackPositionOrThrow.mockRejectedValue( + new Error('disk full') + ); + + await expect(t.toggle.toggle(t.target())).resolves.toBe(false); + + expect(t.position()).toEqual(row(600)); + expect(t.feedback).toEqual(['failed']); + expect(t.persisted).toEqual([]); + expect(t.logger.error).toHaveBeenCalled(); + expect(t.toggle.busy()).toBe(false); + }); + + it('logs a failed post-write refresh without failing the toggle', async () => { + const position = signal(null); + const logger = { error: jest.fn() }; + const toggle = createVodWatchedToggle({ + playbackPositions: { + savePlaybackPositionOrThrow: jest + .fn() + .mockResolvedValue(undefined), + clearPlaybackPositionOrThrow: jest + .fn() + .mockResolvedValue(undefined), + }, + position, + applyPosition: (next) => position.set(next), + notify: jest.fn(), + onPersisted: () => Promise.reject(new Error('ipc down')), + logger, + }); + + await expect( + toggle.toggle({ + playlistId: PLAYLIST, + contentXtreamId: 42, + stillCurrent: () => true, + }) + ).resolves.toBe(true); + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(toggle.isWatched()).toBe(true); + expect(logger.error).toHaveBeenCalledWith( + 'Refresh after watched toggle failed', + expect.any(Error) + ); + }); + + it('stays disabled while playback could overwrite the row', async () => { + const t = setup(null); + t.playingNow.set(true); + + expect(t.toggle.enabled()).toBe(false); + await expect(t.toggle.toggle(t.target())).resolves.toBe(false); + expect( + t.playbackPositions.savePlaybackPositionOrThrow + ).not.toHaveBeenCalled(); + }); + + it('stays disabled until the stored row is in hand', async () => { + const position = signal(null); + const positionReady = signal(false); + const save = jest.fn().mockResolvedValue(undefined); + const toggle = createVodWatchedToggle({ + playbackPositions: { + savePlaybackPositionOrThrow: save, + clearPlaybackPositionOrThrow: jest + .fn() + .mockResolvedValue(undefined), + }, + position, + applyPosition: (next) => position.set(next), + positionReady, + notify: jest.fn(), + }); + const target = { + playlistId: PLAYLIST, + contentXtreamId: 42, + stillCurrent: () => true, + }; + + expect(toggle.enabled()).toBe(false); + await expect(toggle.toggle(target)).resolves.toBe(false); + expect(save).not.toHaveBeenCalled(); + + positionReady.set(true); + expect(toggle.enabled()).toBe(true); + await expect(toggle.toggle(target)).resolves.toBe(true); + }); + + it('serializes overlapping toggles', async () => { + const t = setup(null); + let release!: () => void; + t.playbackPositions.savePlaybackPositionOrThrow.mockReturnValue( + new Promise((resolve) => { + release = resolve; + }) + ); + + const first = t.toggle.toggle(t.target()); + expect(t.toggle.busy()).toBe(true); + expect(t.toggle.enabled()).toBe(false); + await expect(t.toggle.toggle(t.target())).resolves.toBe(false); + + release(); + await expect(first).resolves.toBe(true); + expect( + t.playbackPositions.savePlaybackPositionOrThrow + ).toHaveBeenCalledTimes(1); + }); + + it('does not patch the page or announce after the movie changed', async () => { + const t = setup(null); + + await expect(t.toggle.toggle(t.target(() => false))).resolves.toBe( + true + ); + + expect( + t.playbackPositions.savePlaybackPositionOrThrow + ).toHaveBeenCalledTimes(1); + expect(t.position()).toBeNull(); + expect(t.feedback).toEqual([]); + // The row did land for that playlist, so its badges still refresh. + expect(t.persisted).toEqual([PLAYLIST]); + }); +}); diff --git a/libs/portal/shared/util/src/lib/vod-watched-toggle.ts b/libs/portal/shared/util/src/lib/vod-watched-toggle.ts new file mode 100644 index 000000000..582f1aea4 --- /dev/null +++ b/libs/portal/shared/util/src/lib/vod-watched-toggle.ts @@ -0,0 +1,154 @@ +import { Signal, computed, signal } from '@angular/core'; +import { PlaybackPositionData } from '@iptvnator/shared/interfaces'; +import type { Logger } from './logger'; +import { + PortalPlaybackPositions, + isPortalPlaybackWatched, +} from './portal-playback-positions'; +import { buildWatchedVodPosition } from './portal-watch-state'; + +export type VodWatchedToggleFeedback = 'marked' | 'unmarked' | 'failed'; + +export interface VodWatchedToggleTarget { + playlistId: string; + contentXtreamId: number; + /** Runtime the detail view knows; the stored row's duration wins over it. */ + durationSeconds?: number | null; + /** + * Whether the page still shows this movie once the write returns. Detail + * hosts are reused across navigations and ids collide across playlists, + * so a late completion must neither patch the next movie's row nor show + * its feedback there. The write itself is safe: it carries its own ids. + */ + stillCurrent: () => boolean; +} + +export interface VodWatchedToggleConfig { + playbackPositions: Pick< + PortalPlaybackPositions, + 'savePlaybackPositionOrThrow' | 'clearPlaybackPositionOrThrow' + >; + /** The movie's row as the page shows it. */ + position: Signal; + /** Replaces that row after a confirmed write. */ + applyPosition: (position: PlaybackPositionData | null) => void; + /** + * Playback whose next position tick would overwrite a just-written row + * (inline player mounted, external session live or launching). While it + * is true the toggle stays disabled rather than silently flipping back. + */ + playingNow?: Signal; + /** + * Whether `position` is the stored row rather than a placeholder: until + * the host's read lands the button would offer the wrong direction + * (re-marking a watched movie, or clearing a row it never saw). + */ + positionReady?: Signal; + notify: (feedback: VodWatchedToggleFeedback) => void; + /** + * Runs after a confirmed write, e.g. to refresh catalog badges. Its + * failure is logged and swallowed: the mutation it follows is already + * confirmed, so it must neither reject the toggle nor escape unhandled. + */ + onPersisted?: (playlistId: string) => void | Promise; + logger?: Pick; +} + +export interface VodWatchedToggle { + isWatched: Signal; + busy: Signal; + enabled: Signal; + /** Resolves true when the write landed, false when nothing changed. */ + toggle(target: VodWatchedToggleTarget): Promise; +} + +/** + * Manual "watched" toggle for a movie, shared by the Xtream and Stalker + * detail views. Marking writes a full-progress position row (the same shape + * playback itself leaves behind, so every catalog badge and Resume rule keeps + * working unchanged); unmarking deletes the row, which also forgets the + * resume point — the same trade the episode toggle makes. + * + * Both writes go through the strict, rejecting persistence boundary: the + * row on screen changes only after the write is confirmed, never before. + */ +export function createVodWatchedToggle( + config: VodWatchedToggleConfig +): VodWatchedToggle { + const busy = signal(false); + const isWatched = computed(() => + isPortalPlaybackWatched(config.position()) + ); + const enabled = computed( + () => + !busy() && + !(config.playingNow?.() ?? false) && + (config.positionReady?.() ?? true) + ); + + async function toggle(target: VodWatchedToggleTarget): Promise { + if (!enabled() || !target.playlistId) { + return false; + } + + const markWatched = !isWatched(); + busy.set(true); + try { + let next: PlaybackPositionData | null = null; + if (markWatched) { + next = buildWatchedVodPosition({ + playlistId: target.playlistId, + contentXtreamId: target.contentXtreamId, + durationSeconds: target.durationSeconds, + currentPosition: config.position(), + }); + await config.playbackPositions.savePlaybackPositionOrThrow( + target.playlistId, + next + ); + } else { + await config.playbackPositions.clearPlaybackPositionOrThrow( + target.playlistId, + target.contentXtreamId, + 'vod' + ); + } + + void Promise.resolve() + .then(() => config.onPersisted?.(target.playlistId)) + .catch((error) => + config.logger?.error( + 'Refresh after watched toggle failed', + error + ) + ); + if (!target.stillCurrent()) { + return true; + } + config.applyPosition(next); + config.notify(markWatched ? 'marked' : 'unmarked'); + return true; + } catch (error) { + // Nothing was confirmed persisted — keep the rendered state. + config.logger?.error('Watched toggle failed', error); + if (target.stillCurrent()) { + config.notify('failed'); + } + return false; + } finally { + busy.set(false); + } + } + + return { isWatched, busy, enabled, toggle }; +} + +/** Translation keys behind each feedback kind, shared by every host. */ +export const VOD_WATCHED_FEEDBACK_KEYS: Record< + VodWatchedToggleFeedback, + string +> = { + marked: 'XTREAM.MOVIE_MARKED_WATCHED', + unmarked: 'XTREAM.MOVIE_MARKED_UNWATCHED', + failed: 'XTREAM.MOVIE_WATCH_UPDATE_FAILED', +}; diff --git a/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.html b/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.html index 39c85e94a..728a260e4 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.html +++ b/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.html @@ -15,9 +15,14 @@ [inlinePlayback]="inlinePlayback()" [externalPlayback]="externalPlayback.activeSession()" [providerOnly]="providerOnly()" + [isWatched]="watchedToggle.isWatched()" + [watchedToggleBusy]="watchedToggle.busy()" + [watchedToggleReady]="positionLoaded()" + [playbackStartPending]="playbackStartPending()" (playClicked)="onVodPlay($event)" (resumeClicked)="onVodResume($event)" (favoriteToggled)="onVodFavoriteToggled($event)" + (watchedToggled)="onVodWatchedToggled($event)" (downloadRequested)="onVodDownload($event)" (backClicked)="onVodBack()" (inlineTimeUpdated)="handleInlineTimeUpdate($event)" diff --git a/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.spec.ts b/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.spec.ts index 38af4f584..95b26effb 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.spec.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.spec.ts @@ -52,9 +52,14 @@ class StubVodDetailsComponent { readonly playbackPosition = input(null); readonly inlinePlayback = input(null); readonly externalPlayback = input(null); + readonly isWatched = input(false); + readonly watchedToggleBusy = input(false); + readonly watchedToggleReady = input(true); + readonly playbackStartPending = input(false); readonly playClicked = output(); readonly resumeClicked = output(); readonly favoriteToggled = output(); + readonly watchedToggled = output<{ item: unknown; watched: boolean }>(); readonly downloadRequested = output(); readonly backClicked = output(); readonly inlineTimeUpdated = output(); @@ -84,6 +89,9 @@ describe('StalkerCatalogDetailComponent provider presentation', () => { const contentType = signal<'vod' | 'series'>('vod'); const catalogPlaylist = signal({ id: 'stalker-1' }); const snackBar = { open: jest.fn() }; + const refreshPositions = jest.fn().mockResolvedValue(undefined); + const getPlaybackPosition = jest.fn().mockResolvedValue(null); + const savePlaybackPositionOrThrow = jest.fn().mockResolvedValue(undefined); const routerMock = { navigateByUrl: jest.fn() }; const locationMock = { back: jest.fn() }; const originalHistoryState = window.history.state; @@ -104,6 +112,9 @@ describe('StalkerCatalogDetailComponent provider presentation', () => { portalPlayer.isEmbeddedPlayer.mockReturnValue(true); catalogPlaylist.set({ id: 'stalker-1' }); snackBar.open.mockReset(); + refreshPositions.mockClear(); + getPlaybackPosition.mockReset().mockResolvedValue(null); + savePlaybackPositionOrThrow.mockClear().mockResolvedValue(undefined); routerMock.navigateByUrl.mockReset(); locationMock.back.mockReset(); @@ -118,13 +129,18 @@ describe('StalkerCatalogDetailComponent provider presentation', () => { playlist: catalogPlaylist, clearSelectedItem: jest.fn(), resolveVodPlayback, + refreshPositions, }, }, { provide: PORTAL_PLAYBACK_POSITIONS, useValue: { - getPlaybackPosition: jest.fn().mockResolvedValue(null), + getPlaybackPosition, savePlaybackPosition: jest.fn(), + savePlaybackPositionOrThrow, + clearPlaybackPositionOrThrow: jest + .fn() + .mockResolvedValue(undefined), }, }, { @@ -196,6 +212,61 @@ describe('StalkerCatalogDetailComponent provider presentation', () => { ); }); + it('marks the open movie watched from the child toggle and refreshes catalog badges', async () => { + await fixture.whenStable(); + const child = fixture.debugElement.query( + By.directive(StubVodDetailsComponent) + ).componentInstance as StubVodDetailsComponent; + expect(child.isWatched()).toBe(false); + + child.watchedToggled.emit({ item: child.item(), watched: true }); + await fixture.whenStable(); + fixture.detectChanges(); + + expect(savePlaybackPositionOrThrow).toHaveBeenCalledWith( + 'stalker-1', + expect.objectContaining({ contentXtreamId: 42, contentType: 'vod' }) + ); + expect(refreshPositions).toHaveBeenCalledWith('stalker-1'); + expect(child.isWatched()).toBe(true); + expect(snackBar.open).toHaveBeenCalledWith( + 'XTREAM.MOVIE_MARKED_WATCHED', + undefined, + expect.anything() + ); + }); + + it('blocks the toggle until the initial position read lands, then honours it', async () => { + let resolveRead!: (value: null) => void; + getPlaybackPosition.mockReturnValueOnce( + new Promise((resolve) => { + resolveRead = resolve; + }) + ); + fixture.detectChanges(); + await fixture.whenStable(); + const child = fixture.debugElement.query( + By.directive(StubVodDetailsComponent) + ).componentInstance as StubVodDetailsComponent; + expect(child.watchedToggleReady()).toBe(false); + + // Before the row is known the direction would be a guess: refused. + child.watchedToggled.emit({ item: child.item(), watched: true }); + await fixture.whenStable(); + expect(savePlaybackPositionOrThrow).not.toHaveBeenCalled(); + + resolveRead(null); + await fixture.whenStable(); + fixture.detectChanges(); + expect(child.watchedToggleReady()).toBe(true); + + child.watchedToggled.emit({ item: child.item(), watched: true }); + await fixture.whenStable(); + fixture.detectChanges(); + expect(savePlaybackPositionOrThrow).toHaveBeenCalledTimes(1); + expect(child.isWatched()).toBe(true); + }); + it('keeps provider-only presentation disabled for a regular VOD open', async () => { selectedItem.set({ id: '99', @@ -241,6 +312,70 @@ describe('StalkerCatalogDetailComponent provider presentation', () => { } ); + it('blocks the watched toggle while a Play is still resolving', async () => { + let resolve!: (value: { streamUrl: string }) => void; + resolveVodPlayback.mockReturnValueOnce( + new Promise((resolvePromise) => { + resolve = resolvePromise; + }) + ); + fixture.detectChanges(); + await fixture.whenStable(); + const child = fixture.debugElement.query( + By.directive(StubVodDetailsComponent) + ).componentInstance as StubVodDetailsComponent; + expect(child.playbackStartPending()).toBe(false); + + fixture.componentInstance.onVodPlay({ + type: 'stalker', + cmd: '/media/42', + data: selectedItem(), + } as never); + fixture.detectChanges(); + expect(child.playbackStartPending()).toBe(true); + expect(fixture.componentInstance.watchedToggle.enabled()).toBe(false); + + // A toggle in this window is refused, so nothing is written. + child.watchedToggled.emit({ item: child.item(), watched: true }); + await fixture.whenStable(); + expect(savePlaybackPositionOrThrow).not.toHaveBeenCalled(); + + resolve({ streamUrl: 'https://portal.example/movie.mpg' }); + await fixture.whenStable(); + fixture.detectChanges(); + expect(child.playbackStartPending()).toBe(false); + }); + + it('does not carry a pending start over to the next selected movie', async () => { + let resolve!: (value: { streamUrl: string }) => void; + resolveVodPlayback.mockReturnValueOnce( + new Promise((resolvePromise) => { + resolve = resolvePromise; + }) + ); + fixture.detectChanges(); + await fixture.whenStable(); + fixture.componentInstance.onVodPlay({ + type: 'stalker', + cmd: '/media/42', + data: selectedItem(), + } as never); + fixture.detectChanges(); + expect(fixture.componentInstance.playbackStartPending()).toBe(true); + + selectedItem.set({ + id: '99', + cmd: '/media/99', + info: { name: 'Replacement movie' }, + }); + fixture.detectChanges(); + expect(fixture.componentInstance.playbackStartPending()).toBe(false); + + resolve({ streamUrl: 'https://stale.example/movie.mpg' }); + await fixture.whenStable(); + expect(fixture.componentInstance.playbackStartPending()).toBe(false); + }); + it('does not mount a VOD resolution after the catalog owner changes', async () => { let resolve!: (value: { streamUrl: string }) => void; resolveVodPlayback.mockReturnValueOnce( diff --git a/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.ts b/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.ts index 96fffc7dd..34c038f54 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-catalog-detail/stalker-catalog-detail.component.ts @@ -13,13 +13,14 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { - consumeStalkerReturnMarker, - resolveStalkerBackNavigation, PORTAL_EXTERNAL_PLAYBACK, PORTAL_PLAYBACK_POSITIONS, PORTAL_PLAYER, - createLogger, + consumeStalkerReturnMarker, createInlinePlaybackPositionWriter, + createLogger, + createPendingPlaybackStart, + resolveStalkerBackNavigation, } from '@iptvnator/portal/shared/util'; import { createPortalFavoritesResource, @@ -50,6 +51,7 @@ import { StalkerCatalogFacadeService } from '../stalker-catalog-facade.service'; import { StalkerSeriesViewComponent } from '../stalker-series-view/stalker-series-view.component'; import { startStalkerVodDownload } from './stalker-vod-download'; +import { createStalkerVodWatchedToggle } from '../stalker-vod-watched-toggle'; import { createPlaybackSessionKey } from '@iptvnator/playback/util'; @Component({ @@ -108,6 +110,18 @@ export class StalkerCatalogDetailComponent implements OnDestroy { null ); private unsubscribePositionUpdates: (() => void) | null = null; + private positionLoadGeneration = 0; + /** The stored row is in hand (not the placeholder shown while reading). */ + readonly positionLoaded = signal(false); + /** + * The start still waiting on the portal between the click and playback, + * keyed by its owner: a stale resolution for the previous movie must not + * hold the next movie's toggle hostage. + */ + private readonly pendingStart = createPendingPlaybackStart(); + readonly playbackStartPending = computed(() => + this.pendingStart.isPendingFor(this.playbackOwnerKey()) + ); readonly isSeriesDetail = computed(() => { const item = this.selectedItem(); @@ -134,6 +148,33 @@ export class StalkerCatalogDetailComponent implements OnDestroy { () => this.selectedVodPosition()?.positionSeconds ?? null ); + /** Manual watched toggle; the child gates it on live playback itself. */ + readonly watchedToggle = createStalkerVodWatchedToggle({ + owner: () => { + const playlistId = this.catalog.playlist()?.id; + const vodId = Number(this.selectedItem()?.id); + return playlistId && Number.isFinite(vodId) + ? { playlistId, vodId } + : null; + }, + playbackPositions: this.playbackPositions, + position: this.selectedVodPosition, + playingNow: computed( + () => this.inlinePlayback() !== null || this.playbackStartPending() + ), + positionReady: this.positionLoaded, + applyPosition: (position) => { + // A read still in flight started from the pre-write row; letting + // it land would revert the toggle it never saw. + this.positionLoadGeneration++; + this.selectedVodPosition.set(position); + }, + snackBar: this.snackBar, + translateService: this.translateService, + logger: this.logger, + onPersisted: (playlistId) => this.catalog.refreshPositions(playlistId), + }); + readonly portalFavorites = createPortalFavoritesResource( this.playlistService, () => this.catalog.playlist()?.id, @@ -228,6 +269,10 @@ export class StalkerCatalogDetailComponent implements OnDestroy { }); } + onVodWatchedToggled(event: { item: VodDetailsItem }): void { + void this.watchedToggle.toggleItem(event.item); + } + onVodBack(): void { const back = resolveStalkerBackNavigation( window.history.state, @@ -320,6 +365,8 @@ export class StalkerCatalogDetailComponent implements OnDestroy { playlistId: string, vodId: number ): Promise { + const generation = ++this.positionLoadGeneration; + this.positionLoaded.set(false); if (Number.isNaN(vodId)) { this.selectedVodPosition.set(null); return; @@ -330,7 +377,17 @@ export class StalkerCatalogDetailComponent implements OnDestroy { vodId, 'vod' ); + // Only the newest read for the item still on screen may land: an + // older one would revert a watched toggle or a later selection. + if ( + generation !== this.positionLoadGeneration || + this.catalog.playlist()?.id !== playlistId || + Number(this.selectedItem()?.id) !== vodId + ) { + return; + } this.selectedVodPosition.set(position ?? null); + this.positionLoaded.set(true); } private async startStalkerVodPlayback( @@ -345,6 +402,7 @@ export class StalkerCatalogDetailComponent implements OnDestroy { const usesEmbeddedPlayer = this.portalPlayer.isEmbeddedPlayer(); if (usesEmbeddedPlayer && !sessionKey) return; + const startId = this.pendingStart.begin(ownerKey); try { const playback = await this.catalog.resolveVodPlayback( cmd, @@ -384,6 +442,8 @@ export class StalkerCatalogDetailComponent implements OnDestroy { this.snackBar.open(errorMessage, undefined, { duration: 3000, }); + } finally { + this.pendingStart.settle(startId); } } } diff --git a/libs/portal/stalker/feature/src/lib/stalker-catalog-facade.service.spec.ts b/libs/portal/stalker/feature/src/lib/stalker-catalog-facade.service.spec.ts index 33601663f..9913d02a9 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-catalog-facade.service.spec.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-catalog-facade.service.spec.ts @@ -148,7 +148,7 @@ describe('StalkerCatalogFacadeService', () => { expect( service.getItemProgress({ id: '42', is_series: isSeries }) - ).toEqual({ hasSeriesProgress: false }); + ).toEqual({ watchState: 'unwatched' }); } ); @@ -168,7 +168,7 @@ describe('StalkerCatalogFacadeService', () => { ); expect(service.getItemProgress(item)).toEqual({ progress: 0, - isWatched: false, + watchState: 'unwatched', }); } ); @@ -200,6 +200,25 @@ describe('StalkerCatalogFacadeService', () => { ); }); + it('reports a fully played movie as watched on its catalog card', async () => { + const service = TestBed.inject(StalkerCatalogFacadeService); + await Promise.resolve(); + + playbackUpdateHandler?.({ + playlistId: playlist._id, + contentXtreamId: 17359, + contentType: 'vod', + positionSeconds: 5400, + durationSeconds: 5400, + }); + await Promise.resolve(); + + expect(service.getItemProgress({ id: '17359' })).toEqual({ + progress: 100, + watchState: 'watched', + }); + }); + it('ignores external playback updates for other playlists', async () => { TestBed.inject(StalkerCatalogFacadeService); await Promise.resolve(); @@ -218,9 +237,9 @@ describe('StalkerCatalogFacadeService', () => { it('splits loading into the initial skeleton and the append tail by portal page', () => { const service = TestBed.inject(StalkerCatalogFacadeService); - const loading = stalkerStoreMock['isPaginatedContentLoading'] as ReturnType< - typeof signal - >; + const loading = stalkerStoreMock[ + 'isPaginatedContentLoading' + ] as ReturnType>; const page = stalkerStoreMock['page'] as ReturnType< typeof signal >; @@ -237,9 +256,9 @@ describe('StalkerCatalogFacadeService', () => { it('guards loadMore behind loading, append errors, and hasMore', () => { const service = TestBed.inject(StalkerCatalogFacadeService); - const loading = stalkerStoreMock['isPaginatedContentLoading'] as ReturnType< - typeof signal - >; + const loading = stalkerStoreMock[ + 'isPaginatedContentLoading' + ] as ReturnType>; const hasMore = stalkerStoreMock['hasMoreContent'] as ReturnType< typeof signal >; @@ -294,9 +313,9 @@ describe('StalkerCatalogFacadeService', () => { // The route provider (and this facade) survives a same-config portal // switch — the identity must include the playlist. const service = TestBed.inject(StalkerCatalogFacadeService); - const currentPlaylist = stalkerStoreMock['currentPlaylist'] as ReturnType< - typeof signal<{ _id: string } | undefined> - >; + const currentPlaylist = stalkerStoreMock[ + 'currentPlaylist' + ] as ReturnType>; service.saveScrollPosition(420); diff --git a/libs/portal/stalker/feature/src/lib/stalker-catalog-facade.service.ts b/libs/portal/stalker/feature/src/lib/stalker-catalog-facade.service.ts index 31f6df46f..ebfcc5323 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-catalog-facade.service.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-catalog-facade.service.ts @@ -22,23 +22,12 @@ import { PORTAL_CATALOG_FACADE, PORTAL_PLAYBACK_POSITIONS, StalkerPortalCatalogFacade, + getPortalPlaybackProgressPercent, + resolvePortalSeriesWatchState, + resolvePortalWatchState, } from '@iptvnator/portal/shared/util'; import { PlaybackPositionData } from '@iptvnator/shared/interfaces'; -function calculateProgress(position: PlaybackPositionData | undefined): number { - if (!position || !position.durationSeconds) { - return 0; - } - - const percent = (position.positionSeconds / position.durationSeconds) * 100; - - if (position.positionSeconds > 10 && percent < 1) { - return 1; - } - - return Math.min(100, Math.round(percent)); -} - @Injectable() export class StalkerCatalogFacadeService implements StalkerPortalCatalogFacade< Record, @@ -283,20 +272,16 @@ export class StalkerCatalogFacadeService implements StalkerPortalCatalogFacade< this.contentType() === 'series' || isStalkerSeriesFlag(item.is_series); - if (hasSeriesProgress) { - return { hasSeriesProgress: true }; + if (hasSeriesProgress || isSeries) { + return { + watchState: resolvePortalSeriesWatchState(hasSeriesProgress), + }; } - if (isSeries) { - return { hasSeriesProgress: false }; - } - - const progress = calculateProgress( - this.stalkerPositions().get(`vod_${numericId}`) - ); + const position = this.stalkerPositions().get(`vod_${numericId}`); return { - progress, - isWatched: progress >= 90, + progress: getPortalPlaybackProgressPercent(position), + watchState: resolvePortalWatchState(position), }; } diff --git a/libs/portal/stalker/feature/src/lib/stalker-collection-detail.component.spec.ts b/libs/portal/stalker/feature/src/lib/stalker-collection-detail.component.spec.ts index 86d328a7f..083c99f03 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-collection-detail.component.spec.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-collection-detail.component.spec.ts @@ -21,8 +21,12 @@ import { UnifiedCollectionItem, } from '@iptvnator/portal/shared/util'; import { StalkerStore } from '@iptvnator/portal/stalker/data-access'; -import { PlaylistsService } from '@iptvnator/services'; import { + PlaybackPositionRuntimeBridgeService, + PlaylistsService, +} from '@iptvnator/services'; +import { + PlaybackPositionData, Playlist, ResolvedPortalPlayback, VodDetailsItem, @@ -59,10 +63,18 @@ class StubStalkerInlineDetailComponent { readonly playbackPosition = input(null); readonly inlinePlayback = input(null); readonly externalPlayback = input(null); + readonly isWatched = input(false); + readonly watchedToggleBusy = input(false); + readonly watchedToggleReady = input(true); + readonly playbackStartPending = input(false); readonly backClicked = output(); readonly playClicked = output(); readonly resumeClicked = output(); readonly favoriteToggled = output(); + readonly watchedToggled = output<{ + item: VodDetailsItem; + watched: boolean; + }>(); readonly inlineTimeUpdated = output(); readonly inlinePlaybackClosed = output(); readonly streamUrlCopied = output(); @@ -99,12 +111,16 @@ describe('StalkerCollectionDetailComponent', () => { }; let playbackPositions: { savePlaybackPosition: jest.Mock; + savePlaybackPositionOrThrow: jest.Mock; + clearPlaybackPositionOrThrow: jest.Mock; getPlaybackPosition: jest.Mock; getSeriesPlaybackPositions: jest.Mock; getAllPlaybackPositions: jest.Mock; clearPlaybackPosition: jest.Mock; }; let snackBar: { open: jest.Mock }; + let playbackUpdateHandler: + ((data: PlaybackPositionData) => void) | undefined; let playlistsService: { getPlaylistById: jest.Mock }; let routerNavigate: jest.Mock; @@ -153,12 +169,15 @@ describe('StalkerCollectionDetailComponent', () => { }; playbackPositions = { savePlaybackPosition: jest.fn(), + savePlaybackPositionOrThrow: jest.fn(async () => undefined), + clearPlaybackPositionOrThrow: jest.fn(async () => undefined), getPlaybackPosition: jest.fn(async () => null), getSeriesPlaybackPositions: jest.fn(), getAllPlaybackPositions: jest.fn(), clearPlaybackPosition: jest.fn(), }; snackBar = { open: jest.fn() }; + playbackUpdateHandler = undefined; playlistsService = { getPlaylistById: jest.fn((playlistId: string) => of({ ...playlist, _id: playlistId }) @@ -213,6 +232,17 @@ describe('StalkerCollectionDetailComponent', () => { onDefaultLangChange: new Subject(), }, }, + { + provide: PlaybackPositionRuntimeBridgeService, + useValue: { + onPlaybackPositionUpdate: jest.fn( + (handler: (data: PlaybackPositionData) => void) => { + playbackUpdateHandler = handler; + return () => undefined; + } + ), + }, + }, { provide: PlaylistsService, useValue: { @@ -549,6 +579,260 @@ describe('StalkerCollectionDetailComponent', () => { expect(snackBar.open).not.toHaveBeenCalled(); }); + it('marks a collection movie watched under its owning playlist', async () => { + const sourceItem = { + id: '1701', + title: 'Collection Movie', + category_id: 'vod', + cmd: '/media/file_1701.mpg', + info: { name: 'Collection Movie', movie_image: 'movie.jpg' }, + }; + fixture.componentRef.setInput( + 'item', + buildCollectionItem({ + contentType: 'movie', + categoryId: 'vod', + stalkerItem: sourceItem, + }) + ); + await settleDetail(fixture); + await settleDetail(fixture); + + const detail = fixture.debugElement.query( + By.directive(StubStalkerInlineDetailComponent) + ).componentInstance as StubStalkerInlineDetailComponent; + expect(detail.isWatched()).toBe(false); + + detail.watchedToggled.emit({ + item: createStalkerVodItem(sourceItem, playlist._id), + watched: true, + }); + await settleDetail(fixture); + + expect( + playbackPositions.savePlaybackPositionOrThrow + ).toHaveBeenCalledWith( + 'stalker-1', + expect.objectContaining({ + contentXtreamId: 1701, + contentType: 'vod', + }) + ); + expect(detail.isWatched()).toBe(true); + expect(snackBar.open).toHaveBeenCalledWith( + 'XTREAM.MOVIE_MARKED_WATCHED', + undefined, + expect.anything() + ); + }); + + it('blocks the watched toggle while a collection Play is still resolving', async () => { + const sourceItem = { + id: '1701', + title: 'Collection Movie', + category_id: 'vod', + cmd: '/media/file_1701.mpg', + info: { name: 'Collection Movie', movie_image: 'movie.jpg' }, + }; + let resolve!: (value: ResolvedPortalPlayback) => void; + stalkerStore.resolveVodPlayback.mockReturnValueOnce( + new Promise((resolvePromise) => { + resolve = resolvePromise; + }) + ); + fixture.componentRef.setInput( + 'item', + buildCollectionItem({ + contentType: 'movie', + categoryId: 'vod', + stalkerItem: sourceItem, + }) + ); + await settleDetail(fixture); + await settleDetail(fixture); + const detail = fixture.debugElement.query( + By.directive(StubStalkerInlineDetailComponent) + ).componentInstance as StubStalkerInlineDetailComponent; + + fixture.componentInstance.onVodPlay( + createStalkerVodItem(sourceItem, playlist._id) + ); + fixture.detectChanges(); + expect(detail.playbackStartPending()).toBe(true); + + detail.watchedToggled.emit({ + item: createStalkerVodItem(sourceItem, playlist._id), + watched: true, + }); + await settleDetail(fixture); + expect( + playbackPositions.savePlaybackPositionOrThrow + ).not.toHaveBeenCalled(); + + resolve({ streamUrl: 'https://streams.test/a.mp4', title: 'x' }); + await settleDetail(fixture); + expect(detail.playbackStartPending()).toBe(false); + }); + + it('does not carry a pending collection start over to the next item', async () => { + const sourceItem = { + id: '1701', + title: 'Collection Movie', + category_id: 'vod', + cmd: '/media/file_1701.mpg', + info: { name: 'Collection Movie', movie_image: 'movie.jpg' }, + }; + let resolve!: (value: ResolvedPortalPlayback) => void; + stalkerStore.resolveVodPlayback.mockReturnValueOnce( + new Promise((resolvePromise) => { + resolve = resolvePromise; + }) + ); + fixture.componentRef.setInput( + 'item', + buildCollectionItem({ + contentType: 'movie', + categoryId: 'vod', + stalkerItem: sourceItem, + }) + ); + await settleDetail(fixture); + await settleDetail(fixture); + fixture.componentInstance.onVodPlay( + createStalkerVodItem(sourceItem, playlist._id) + ); + fixture.detectChanges(); + expect(fixture.componentInstance.playback.playbackStartPending()).toBe( + true + ); + + fixture.componentRef.setInput( + 'item', + buildCollectionItem({ + uid: 'stalker::stalker-1::item-2', + stalkerId: 'item-2', + contentType: 'movie', + categoryId: 'vod', + stalkerItem: { ...sourceItem, id: '1702' }, + }) + ); + await settleDetail(fixture); + expect(fixture.componentInstance.playback.playbackStartPending()).toBe( + false + ); + + resolve({ streamUrl: 'https://streams.test/a.mp4', title: 'x' }); + await settleDetail(fixture); + expect(fixture.componentInstance.playback.playbackStartPending()).toBe( + false + ); + }); + + it('mirrors an external player position into the collection row', async () => { + const sourceItem = { + id: '1701', + title: 'Collection Movie', + category_id: 'vod', + cmd: '/media/file_1701.mpg', + info: { name: 'Collection Movie', movie_image: 'movie.jpg' }, + }; + playbackPositions.getPlaybackPosition.mockResolvedValueOnce({ + playlistId: 'stalker-1', + contentXtreamId: 1701, + contentType: 'vod', + positionSeconds: 5400, + durationSeconds: 5400, + }); + fixture.componentRef.setInput( + 'item', + buildCollectionItem({ + contentType: 'movie', + categoryId: 'vod', + stalkerItem: sourceItem, + }) + ); + await settleDetail(fixture); + await settleDetail(fixture); + const detail = fixture.debugElement.query( + By.directive(StubStalkerInlineDetailComponent) + ).componentInstance as StubStalkerInlineDetailComponent; + expect(detail.isWatched()).toBe(true); + + // MPV replays the movie from the start; a foreign item's tick is ignored. + playbackUpdateHandler?.({ + playlistId: 'stalker-1', + contentXtreamId: 9999, + contentType: 'vod', + positionSeconds: 10, + durationSeconds: 5400, + }); + playbackUpdateHandler?.({ + playlistId: 'stalker-1', + contentXtreamId: 1701, + contentType: 'vod', + positionSeconds: 1080, + durationSeconds: 5400, + }); + await settleDetail(fixture); + + expect(detail.isWatched()).toBe(false); + expect(detail.playbackPosition()).toBe(1080); + expect(detail.watchedToggleReady()).toBe(true); + }); + + it('blocks the collection toggle until the initial position read lands', async () => { + let resolveRead!: (value: null) => void; + playbackPositions.getPlaybackPosition.mockReturnValueOnce( + new Promise((resolve) => { + resolveRead = resolve; + }) + ); + const sourceItem = { + id: '1701', + title: 'Collection Movie', + category_id: 'vod', + cmd: '/media/file_1701.mpg', + info: { name: 'Collection Movie', movie_image: 'movie.jpg' }, + }; + fixture.componentRef.setInput( + 'item', + buildCollectionItem({ + contentType: 'movie', + categoryId: 'vod', + stalkerItem: sourceItem, + }) + ); + await settleDetail(fixture); + await settleDetail(fixture); + + const detail = fixture.debugElement.query( + By.directive(StubStalkerInlineDetailComponent) + ).componentInstance as StubStalkerInlineDetailComponent; + expect(detail.watchedToggleReady()).toBe(false); + detail.watchedToggled.emit({ + item: createStalkerVodItem(sourceItem, playlist._id), + watched: true, + }); + await settleDetail(fixture); + expect( + playbackPositions.savePlaybackPositionOrThrow + ).not.toHaveBeenCalled(); + + resolveRead(null); + await settleDetail(fixture); + expect(detail.watchedToggleReady()).toBe(true); + + detail.watchedToggled.emit({ + item: createStalkerVodItem(sourceItem, playlist._id), + watched: true, + }); + await settleDetail(fixture); + expect( + playbackPositions.savePlaybackPositionOrThrow + ).toHaveBeenCalledTimes(1); + expect(detail.isWatched()).toBe(true); + }); + it('does not load VOD playback position when the playlist id is missing', async () => { const playlistsService = TestBed.inject(PlaylistsService) as { getPlaylistById: jest.Mock; diff --git a/libs/portal/stalker/feature/src/lib/stalker-collection-detail.component.ts b/libs/portal/stalker/feature/src/lib/stalker-collection-detail.component.ts index ad358096f..599159a6f 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-collection-detail.component.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-collection-detail.component.ts @@ -1,6 +1,7 @@ import { ChangeDetectionStrategy, Component, + DestroyRef, computed, effect, forwardRef, @@ -36,7 +37,10 @@ import { StalkerStore, } from '@iptvnator/portal/stalker/data-access'; import type { PlaybackFallbackRequest } from '@iptvnator/ui/playback'; -import { PlaylistsService } from '@iptvnator/services'; +import { + PlaybackPositionRuntimeBridgeService, + PlaylistsService, +} from '@iptvnator/services'; import { Playlist, VodDetailsItem } from '@iptvnator/shared/interfaces'; import { firstValueFrom } from 'rxjs'; import { StalkerInlineDetailComponent } from './stalker-inline-detail/stalker-inline-detail.component'; @@ -68,10 +72,15 @@ import { [playbackPosition]="selectedVodPlaybackPosition()" [inlinePlayback]="inlinePlayback()" [externalPlayback]="externalPlayback.activeSession()" + [isWatched]="playback.watchedToggle.isWatched()" + [watchedToggleBusy]="playback.watchedToggle.busy()" + [watchedToggleReady]="playback.positionLoaded()" + [playbackStartPending]="playback.playbackStartPending()" (backClicked)="closeRequested.emit()" (playClicked)="onVodPlay($event)" (resumeClicked)="onVodResume($event)" (favoriteToggled)="onVodFavoriteToggled($event)" + (watchedToggled)="playback.toggleSelectedVodWatched($event)" (inlineTimeUpdated)="handleInlineTimeUpdate($event)" (inlinePlaybackClosed)="closeInlinePlayer()" (streamUrlCopied)="showCopyNotification()" @@ -127,7 +136,7 @@ export class StalkerCollectionDetailComponent implements ViewInPortalHandoff { vodDetailsItem: () => this.vodDetailsItem(), }); - private readonly playback = new StalkerCollectionPlaybackController({ + readonly playback = new StalkerCollectionPlaybackController({ item: () => this.item(), stalkerStore: this.stalkerStore, playbackPositions: this.playbackPositions, @@ -169,6 +178,13 @@ export class StalkerCollectionDetailComponent implements ViewInPortalHandoff { private currentPlaybackOwnerKey = ''; constructor() { + const unsubscribePositionUpdates = inject( + PlaybackPositionRuntimeBridgeService + ).onPlaybackPositionUpdate((data) => + this.playback.applyRuntimePosition(data) + ); + inject(DestroyRef).onDestroy(() => unsubscribePositionUpdates?.()); + effect(() => { this.portalFavorites.value(); this.favorites.sync(); diff --git a/libs/portal/stalker/feature/src/lib/stalker-collection-playback.controller.ts b/libs/portal/stalker/feature/src/lib/stalker-collection-playback.controller.ts index 8520c6cdc..e53c547ab 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-collection-playback.controller.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-collection-playback.controller.ts @@ -19,6 +19,7 @@ import { VodDetailsItem, } from '@iptvnator/shared/interfaces'; import { StalkerVodPlaybackController } from './stalker-vod-playback-controller'; +import { createStalkerVodWatchedToggle } from './stalker-vod-watched-toggle'; interface StalkerCollectionPlaybackOwner { readonly sourceId: string; @@ -60,6 +61,36 @@ export class StalkerCollectionPlaybackController { private readonly vodPlayback: StalkerVodPlaybackController; + readonly playbackStartPending = computed(() => + this.vodPlayback.playbackStartPending() + ); + readonly positionLoaded = computed(() => this.vodPlayback.positionLoaded()); + + /** Manual watched toggle; the child gates it on live playback itself. */ + readonly watchedToggle = createStalkerVodWatchedToggle({ + owner: () => { + const owner = this.playbackOwner(); + return owner + ? { playlistId: owner.sourceId, vodId: Number(owner.contentId) } + : null; + }, + playbackPositions: this.config.playbackPositions, + position: this.selectedVodPosition, + playingNow: computed( + () => this.inlinePlayback() !== null || this.playbackStartPending() + ), + positionReady: this.positionLoaded, + applyPosition: (position) => { + // A read still in flight started from the pre-write row; letting + // it land would revert the toggle it never saw. + this.vodPlayback.discardPendingPositionLoad(); + this.selectedVodPosition.set(position); + }, + snackBar: this.config.snackBar, + translateService: this.config.translateService, + logger: this.config.logger, + }); + constructor( private readonly config: StalkerCollectionPlaybackControllerConfig ) { @@ -126,10 +157,38 @@ export class StalkerCollectionPlaybackController { await this.vodPlayback.loadSelectedVodPosition(playlistId, vodId); } + /** + * Mirrors an external player's position into the row while it owns + * this item. Persistence already has it; without this the row shown + * here (and the watched toggle's direction) would stay at what the + * one-time read returned before MPV/VLC played. + */ + applyRuntimePosition(data: PlaybackPositionData): void { + const owner = this.playbackOwner(); + if ( + !owner || + data.contentType !== 'vod' || + data.playlistId !== owner.sourceId || + String(data.contentXtreamId) !== owner.contentId + ) { + return; + } + // A live tick is newer than any stored read still in flight. + this.vodPlayback.discardPendingPositionLoad(); + this.selectedVodPosition.set(data); + this.vodPlayback.positionLoaded.set(true); + } + clearSelectedVodPosition(): void { + this.vodPlayback.discardPendingPositionLoad(); + this.vodPlayback.positionLoaded.set(false); this.selectedVodPosition.set(null); } + toggleSelectedVodWatched(event: { item: VodDetailsItem }): void { + void this.watchedToggle.toggleItem(event.item); + } + private async startVodPlayback( cmd?: string, title?: string, diff --git a/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.html b/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.html index 00997ac87..f45dde9a2 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.html +++ b/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.html @@ -22,9 +22,14 @@ [inlinePlayback]="inlinePlayback()" [externalPlayback]="externalPlayback()" [providerOnly]="providerOnly()" + [isWatched]="isWatched()" + [watchedToggleBusy]="watchedToggleBusy()" + [watchedToggleReady]="watchedToggleReady()" + [playbackStartPending]="playbackStartPending()" (playClicked)="onPlayClicked($event)" (resumeClicked)="onResumeClicked($event)" (favoriteToggled)="onFavoriteToggled($event)" + (watchedToggled)="onWatchedToggled($event)" (backClicked)="onBackClicked()" (inlineTimeUpdated)="onInlineTimeUpdated($event)" (inlinePlaybackClosed)="onInlinePlaybackClosed()" diff --git a/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.spec.ts b/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.spec.ts index f892b3598..eab5d0668 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.spec.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.spec.ts @@ -24,9 +24,14 @@ class StubVodDetailsComponent { readonly playbackPosition = input(null); readonly inlinePlayback = input(null); readonly externalPlayback = input(null); + readonly isWatched = input(false); + readonly watchedToggleBusy = input(false); + readonly watchedToggleReady = input(true); + readonly playbackStartPending = input(false); readonly playClicked = output(); readonly resumeClicked = output(); readonly favoriteToggled = output(); + readonly watchedToggled = output<{ item: unknown; watched: boolean }>(); readonly backClicked = output(); readonly inlineTimeUpdated = output(); readonly inlinePlaybackClosed = output(); @@ -91,6 +96,33 @@ describe('StalkerInlineDetailComponent provider presentation', () => { expect(child.playbackSessionKey()).toBe('collection-owned-vod-key'); }); + it('relays the watched state and toggle of regular VOD details', async () => { + fixture.componentRef.setInput('categoryId', 'vod'); + fixture.componentRef.setInput('vodDetailsItem', VOD_ITEM); + fixture.componentRef.setInput('isWatched', true); + fixture.componentRef.setInput('watchedToggleBusy', true); + fixture.componentRef.setInput('watchedToggleReady', false); + fixture.componentRef.setInput('playbackStartPending', true); + const watchedToggled = jest.fn(); + fixture.componentInstance.watchedToggled.subscribe(watchedToggled); + await fixture.whenStable(); + + const child = fixture.debugElement.query( + By.directive(StubVodDetailsComponent) + ).componentInstance as StubVodDetailsComponent; + expect(child.isWatched()).toBe(true); + expect(child.watchedToggleBusy()).toBe(true); + expect(child.watchedToggleReady()).toBe(false); + expect(child.playbackStartPending()).toBe(true); + + child.watchedToggled.emit({ item: VOD_ITEM, watched: false }); + + expect(watchedToggled).toHaveBeenCalledWith({ + item: VOD_ITEM, + watched: false, + }); + }); + it.each([ ['embedded VOD series', 'vod', { id: '42', series: [1] }, false], ['Ministra VOD series', 'vod', { id: '42' }, true], diff --git a/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.ts b/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.ts index 10fe38211..7980314d2 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-inline-detail/stalker-inline-detail.component.ts @@ -42,6 +42,10 @@ export class StalkerInlineDetailComponent { readonly inlinePlayback = input(null); readonly externalPlayback = input(null); readonly providerOnly = input(false); + readonly isWatched = input(false); + readonly watchedToggleBusy = input(false); + readonly watchedToggleReady = input(true); + readonly playbackStartPending = input(false); readonly backClicked = output(); readonly playClicked = output(); @@ -53,6 +57,10 @@ export class StalkerInlineDetailComponent { item: VodDetailsItem; isFavorite: boolean; }>(); + readonly watchedToggled = output<{ + item: VodDetailsItem; + watched: boolean; + }>(); readonly inlineTimeUpdated = output<{ currentTime: number; duration: number; @@ -78,6 +86,10 @@ export class StalkerInlineDetailComponent { this.favoriteToggled.emit(event); } + onWatchedToggled(event: { item: VodDetailsItem; watched: boolean }) { + this.watchedToggled.emit(event); + } + onInlineTimeUpdated(event: { currentTime: number; duration: number }) { this.inlineTimeUpdated.emit(event); } diff --git a/libs/portal/stalker/feature/src/lib/stalker-vod-playback-controller.ts b/libs/portal/stalker/feature/src/lib/stalker-vod-playback-controller.ts index 94c28bd8e..36ba0ae1f 100644 --- a/libs/portal/stalker/feature/src/lib/stalker-vod-playback-controller.ts +++ b/libs/portal/stalker/feature/src/lib/stalker-vod-playback-controller.ts @@ -1,10 +1,11 @@ -import type { WritableSignal } from '@angular/core'; +import { type WritableSignal, computed, signal } from '@angular/core'; import type { MatSnackBar } from '@angular/material/snack-bar'; import type { TranslateService } from '@ngx-translate/core'; -import type { - Logger, - PortalPlaybackPositions, - PortalPlayer, +import { + type Logger, + type PortalPlaybackPositions, + type PortalPlayer, + createPendingPlaybackStart, } from '@iptvnator/portal/shared/util'; import type { PlaybackFallbackRequest } from '@iptvnator/ui/playback'; import { @@ -25,6 +26,17 @@ interface StalkerVodPlaybackControllerConfig { } export class StalkerVodPlaybackController { + /** + * The start still waiting on the portal between the click and playback, + * keyed by its owner: a stale resolution for the previous item must not + * hold the next item's watched toggle hostage. + */ + private readonly pendingStart = createPendingPlaybackStart< + string | undefined + >(); + readonly playbackStartPending = computed(() => + this.pendingStart.isPendingFor(this.config.playbackOwnerKey?.()) + ); private lastInlineSaveTime = 0; private loadSelectedVodPositionRequestId = 0; private playbackRequestId = 0; @@ -37,6 +49,7 @@ export class StalkerVodPlaybackController { const requestId = ++this.playbackRequestId; const usesEmbeddedPlayer = this.config.portalPlayer.isEmbeddedPlayer(); const playbackOwnerKey = this.config.playbackOwnerKey?.(); + const startId = this.pendingStart.begin(playbackOwnerKey); try { const playback = await resolvePlayback(); if (!this.isPlaybackRequestCurrent(requestId, playbackOwnerKey)) { @@ -70,14 +83,29 @@ export class StalkerVodPlaybackController { this.config.snackBar.open(errorMessage, undefined, { duration: 3000, }); + } finally { + this.pendingStart.settle(startId); } } + /** Retires a stored-position read still in flight (a row was written since). */ + discardPendingPositionLoad(): void { + this.loadSelectedVodPositionRequestId++; + } + + /** + * Whether `selectedVodPosition` is the stored row rather than the + * placeholder shown while the read is in flight. Fails closed: a read + * that never lands keeps the watched toggle off rather than guessing. + */ + readonly positionLoaded = signal(false); + async loadSelectedVodPosition( playlistId: string, vodId: number ): Promise { const requestId = ++this.loadSelectedVodPositionRequestId; + this.positionLoaded.set(false); if (!playlistId || !Number.isFinite(vodId)) { this.config.selectedVodPosition.set(null); @@ -95,6 +123,7 @@ export class StalkerVodPlaybackController { } this.config.selectedVodPosition.set(position ?? null); + this.positionLoaded.set(true); } handleInlineTimeUpdate(event: { diff --git a/libs/portal/stalker/feature/src/lib/stalker-vod-watched-toggle.spec.ts b/libs/portal/stalker/feature/src/lib/stalker-vod-watched-toggle.spec.ts new file mode 100644 index 000000000..e6d47a453 --- /dev/null +++ b/libs/portal/stalker/feature/src/lib/stalker-vod-watched-toggle.spec.ts @@ -0,0 +1,96 @@ +import { signal } from '@angular/core'; +import type { MatSnackBar } from '@angular/material/snack-bar'; +import type { TranslateService } from '@ngx-translate/core'; +import type { PortalPlaybackPositions } from '@iptvnator/portal/shared/util'; +import { + PlaybackPositionData, + createStalkerVodItem, +} from '@iptvnator/shared/interfaces'; +import { + StalkerVodWatchedOwner, + createStalkerVodWatchedToggle, +} from './stalker-vod-watched-toggle'; + +describe('createStalkerVodWatchedToggle', () => { + function setup(owner: StalkerVodWatchedOwner | null) { + const ownerSignal = signal(owner); + const position = signal(null); + const save = jest.fn().mockResolvedValue(undefined); + const snackBar = { open: jest.fn() }; + const toggle = createStalkerVodWatchedToggle({ + owner: () => ownerSignal(), + playbackPositions: { + savePlaybackPositionOrThrow: save, + clearPlaybackPositionOrThrow: jest + .fn() + .mockResolvedValue(undefined), + } as unknown as PortalPlaybackPositions, + position, + applyPosition: (next) => position.set(next), + playingNow: signal(false), + positionReady: signal(true), + snackBar: snackBar as unknown as MatSnackBar, + translateService: { + instant: (key: string) => key, + } as unknown as TranslateService, + logger: { error: jest.fn() } as never, + }); + return { toggle, ownerSignal, position, save, snackBar }; + } + + const item = (id: string, playlistId = 'stalker-1') => + createStalkerVodItem( + { id, cmd: `/media/${id}`, info: { name: 'Movie' } } as never, + playlistId + ); + + it('toggles the item the host currently owns and announces it', async () => { + const t = setup({ playlistId: 'stalker-1', vodId: 42 }); + + await expect(t.toggle.toggleItem(item('42'))).resolves.toBe(true); + + expect(t.save).toHaveBeenCalledWith( + 'stalker-1', + expect.objectContaining({ contentXtreamId: 42, contentType: 'vod' }) + ); + expect(t.toggle.isWatched()).toBe(true); + expect(t.snackBar.open).toHaveBeenCalledWith( + 'XTREAM.MOVIE_MARKED_WATCHED', + undefined, + expect.anything() + ); + }); + + it('refuses an item that is not the owner on screen', async () => { + const t = setup({ playlistId: 'stalker-1', vodId: 42 }); + + // The input moved on to 42 while 41 is still rendered. + await expect(t.toggle.toggleItem(item('41'))).resolves.toBe(false); + // Same id from another playlist is a different row entirely. + await expect( + t.toggle.toggleItem(item('42', 'stalker-2')) + ).resolves.toBe(false); + t.ownerSignal.set(null); + await expect(t.toggle.toggleItem(item('42'))).resolves.toBe(false); + + expect(t.save).not.toHaveBeenCalled(); + }); + + it('does not patch the row after the owner changed mid-write', async () => { + const t = setup({ playlistId: 'stalker-1', vodId: 42 }); + let release!: () => void; + t.save.mockReturnValue( + new Promise((resolve) => { + release = resolve; + }) + ); + + const pending = t.toggle.toggleItem(item('42')); + t.ownerSignal.set({ playlistId: 'stalker-1', vodId: 43 }); + release(); + await expect(pending).resolves.toBe(true); + + expect(t.position()).toBeNull(); + expect(t.snackBar.open).not.toHaveBeenCalled(); + }); +}); diff --git a/libs/portal/stalker/feature/src/lib/stalker-vod-watched-toggle.ts b/libs/portal/stalker/feature/src/lib/stalker-vod-watched-toggle.ts new file mode 100644 index 000000000..e04af86fa --- /dev/null +++ b/libs/portal/stalker/feature/src/lib/stalker-vod-watched-toggle.ts @@ -0,0 +1,95 @@ +import type { Signal } from '@angular/core'; +import type { MatSnackBar } from '@angular/material/snack-bar'; +import type { TranslateService } from '@ngx-translate/core'; +import { + type Logger, + type PortalPlaybackPositions, + VOD_WATCHED_FEEDBACK_KEYS, + type VodWatchedToggle, + createVodWatchedToggle, +} from '@iptvnator/portal/shared/util'; +import type { + PlaybackPositionData, + VodDetailsItem, +} from '@iptvnator/shared/interfaces'; + +/** The movie the host currently shows; null while nothing is open. */ +export interface StalkerVodWatchedOwner { + playlistId: string; + vodId: number; +} + +export interface StalkerVodWatchedToggle extends VodWatchedToggle { + /** Toggles the item the child emitted, scoped to the owner on screen. */ + toggleItem(item: VodDetailsItem): Promise; +} + +export interface StalkerVodWatchedToggleConfig { + owner: () => StalkerVodWatchedOwner | null; + playbackPositions: PortalPlaybackPositions; + position: Signal; + applyPosition: (position: PlaybackPositionData | null) => void; + playingNow: Signal; + positionReady: Signal; + snackBar: MatSnackBar; + translateService: TranslateService; + logger: Logger; + onPersisted?: (playlistId: string) => void | Promise; +} + +/** + * The shared movie watched toggle with Stalker's snackbar feedback wired + * in — one place for the routed catalog detail and the collection detail. + */ +export function createStalkerVodWatchedToggle( + config: StalkerVodWatchedToggleConfig +): StalkerVodWatchedToggle { + const toggle = createVodWatchedToggle({ + playbackPositions: config.playbackPositions, + position: config.position, + applyPosition: config.applyPosition, + playingNow: config.playingNow, + positionReady: config.positionReady, + notify: (feedback) => + config.snackBar.open( + config.translateService.instant( + VOD_WATCHED_FEEDBACK_KEYS[feedback] + ), + undefined, + { duration: 5000 } + ), + onPersisted: config.onPersisted, + logger: config.logger, + }); + + return { + ...toggle, + toggleItem(item) { + const owner = config.owner(); + // The owner follows the host's input at once while the previous + // item stays rendered until its detail is prepared; a click in + // that window must not pair the new playlist with the old id. + if ( + item.type !== 'stalker' || + !owner || + item.playlistId !== owner.playlistId || + Number(item.data.id) !== owner.vodId + ) { + return Promise.resolve(false); + } + return toggle.toggle({ + playlistId: owner.playlistId, + contentXtreamId: owner.vodId, + // Hosts are reused across movies: a late completion must + // neither patch the next movie's row nor announce there. + stillCurrent: () => { + const current = config.owner(); + return ( + current?.playlistId === owner.playlistId && + current.vodId === owner.vodId + ); + }, + }); + }, + }; +} diff --git a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.spec.ts b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.spec.ts index 7974c3cc4..63c39333f 100644 --- a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.spec.ts +++ b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.spec.ts @@ -441,6 +441,46 @@ describe('VodDetailsPlaybackService — external session ownership', () => { }); }); + it('reports a start as pending until the previous player is closed', async () => { + const launched = sessionFor(ROUTE_PLAYLIST, ROUTE_VOD_ID); + activeSession.set(launched); + const closing = deferred(); + closeSession.mockReturnValueOnce(closing.promise); + + const pending = service.startResolvedPlayback({ + streamUrl: 'https://example.com/second.mkv', + title: 'Second movie', + contentInfo: launched.contentInfo, + }); + expect(service.playbackStartPending()).toBe(true); + + closing.resolve(); + await pending; + + expect(service.playbackStartPending()).toBe(false); + }); + + it('does not carry a pending start over to the next routed movie', async () => { + const launched = sessionFor(ROUTE_PLAYLIST, ROUTE_VOD_ID); + activeSession.set(launched); + const closing = deferred(); + closeSession.mockReturnValueOnce(closing.promise); + + const pending = service.startResolvedPlayback({ + streamUrl: 'https://example.com/second.mkv', + title: 'Second movie', + contentInfo: launched.contentInfo, + }); + expect(service.playbackStartPending()).toBe(true); + + routeVodId.set(ROUTE_VOD_ID + 1); + expect(service.playbackStartPending()).toBe(false); + + closing.resolve(); + await pending; + expect(service.playbackStartPending()).toBe(false); + }); + describe('stored position loads', () => { it('drops a result after the route changes without starting another load', async () => { const oldLoad = deferred(); @@ -492,6 +532,49 @@ describe('VodDetailsPlaybackService — external session ownership', () => { expect(service.routePlaybackPosition()?.positionSeconds).toBe(300); }); + it('reports the row as loaded only once the read for the current route lands', async () => { + const load = deferred(); + getPlaybackPosition.mockReturnValueOnce(load.promise); + expect(service.positionLoaded()).toBe(false); + + const pending = service.loadPosition(ROUTE_PLAYLIST, ROUTE_VOD_ID); + expect(service.positionLoaded()).toBe(false); + load.resolve(null); + await pending; + expect(service.positionLoaded()).toBe(true); + + // A stale answer for a route that moved on never flips it back on. + const stale = deferred(); + getPlaybackPosition.mockReturnValueOnce(stale.promise); + const stalePending = service.loadPosition( + ROUTE_PLAYLIST, + ROUTE_VOD_ID + ); + expect(service.positionLoaded()).toBe(false); + routeVodId.set(ROUTE_VOD_ID + 1); + stale.resolve(null); + await stalePending; + expect(service.positionLoaded()).toBe(false); + }); + + it('drops a pending result once a row was written since the read started', async () => { + const oldLoad = deferred(); + getPlaybackPosition.mockReturnValueOnce(oldLoad.promise); + const pending = service.loadPosition(ROUTE_PLAYLIST, ROUTE_VOD_ID); + + // The manual watched toggle writes and applies a row meanwhile. + service.discardPendingPositionLoads(); + const watched = positionFor(ROUTE_PLAYLIST, ROUTE_VOD_ID, 5400); + service.routePlaybackPosition.set(watched); + service.vodPlaybackPosition.set(watched); + + oldLoad.resolve(null); + await pending; + + expect(service.routePlaybackPosition()).toEqual(watched); + expect(service.vodPlaybackPosition()).toEqual(watched); + }); + it('does not publish a pending result after destruction', async () => { const oldLoad = deferred(); getPlaybackPosition.mockReturnValueOnce(oldLoad.promise); diff --git a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.ts b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.ts index 0f3241f47..62529c89f 100644 --- a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.ts +++ b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-playback.service.ts @@ -13,7 +13,7 @@ import { createExternalPlaybackButtonState, createInlinePlaybackPositionWriter, createLogger, - getPortalPlaybackProgressPercent, + createPendingPlaybackStart, } from '@iptvnator/portal/shared/util'; import { resolveXtreamVodPlaybackSource, @@ -40,7 +40,7 @@ import { } from './vod-details-external-launch-owner'; import { settleOwnedExternalLaunch } from './vod-details-external-launch'; import { resolveXtreamVodPlaybackPresentation } from './vod-details-playback-presentation'; -import { formatPlaybackPosition } from './vod-primary-action-position'; +import { isResumablePosition } from './vod-primary-action-position'; export interface VodDetailsPlaybackBindings { /** Current vod id resolved from the route */ @@ -125,10 +125,6 @@ export class VodDetailsPlaybackService { ); readonly isExternalStopAction = this.externalButton.isStopAction; readonly externalPrimaryButtonState = this.externalButton.buttonState; - readonly vodPlaybackProgress = computed(() => - getPortalPlaybackProgressPercent(this.vodPlaybackPosition()) - ); - /** Mirrors an incoming position into the route's row when it owns it. */ private trackPosition(position: PlaybackPositionData | null): void { this.vodPlaybackPosition.set(position); @@ -146,17 +142,9 @@ export class VodDetailsPlaybackService { } /** Whether the ROUTE copy has somewhere to resume from. */ - readonly hasPlaybackPosition = computed(() => { - const progress = getPortalPlaybackProgressPercent( - this.routePlaybackPosition() - ); - const inProgress = progress > 0 && progress < 90; - this.logger.debug('hasPlaybackPosition check', { - vodId: this.bindings()?.vodId(), - inProgress, - }); - return inProgress; - }); + readonly hasPlaybackPosition = computed(() => + isResumablePosition(this.routePlaybackPosition()) + ); constructor() { const unsubscribePositionUpdates = @@ -306,11 +294,9 @@ export class VodDetailsPlaybackService { } stopExternalPlayback(): Promise { - return this.externalPlayback.closeSession(this.matchedExternalPlayback()); - } - - formatPosition(): string { - return formatPlaybackPosition(this.routePlaybackPosition()); + return this.externalPlayback.closeSession( + this.matchedExternalPlayback() + ); } closeInlinePlayer(): void { @@ -357,8 +343,21 @@ export class VodDetailsPlaybackService { ); } + /** + * Retires every stored-position read still in flight. A row written + * since (the manual watched toggle) must not be overwritten by the older + * answer that read started from. + */ + discardPendingPositionLoads(): void { + this.positionLoadGeneration++; + } + + /** The route copy's row is in hand; fails closed while a read is in flight. */ + readonly positionLoaded = signal(false); + async loadPosition(playlistId: string, vodId: number): Promise { const generation = ++this.positionLoadGeneration; + this.positionLoaded.set(false); const position = await this.playbackPositions.getPlaybackPosition( playlistId, vodId, @@ -375,6 +374,7 @@ export class VodDetailsPlaybackService { this.vodPlaybackPosition.set(position); this.routePlaybackPosition.set(position); + this.positionLoaded.set(true); } private addToRecentlyViewed(): void { @@ -410,6 +410,19 @@ export class VodDetailsPlaybackService { private launchedExternallyGeneration = 0; private readonly externalLaunchGeneration = signal(null); + /** + * Starts still between the click and `inlinePlayback` / the external + * launch (closing the previous player is a round-trip). The watched + * toggle waits them out: a row written inside that window would be + * overwritten by the new player's first position tick. + */ + private readonly pendingStart = createPendingPlaybackStart< + number | undefined + >(); + readonly playbackStartPending = computed(() => + this.pendingStart.isPendingFor(this.bindings()?.vodId()) + ); + async startResolvedPlayback( playback: ResolvedPortalPlayback, isCurrent: () => boolean = () => true @@ -430,31 +443,37 @@ export class VodDetailsPlaybackService { } const generation = ++this.startGeneration; + const startId = this.pendingStart.begin(this.bindings()?.vodId()); + try { + // A switch REPLACES what is playing. With MPV or VLC and instance + // reuse off, the backend spawns a second detached player + // otherwise — both sources keep running and Stop owns only the + // newer one. + const previousPlayerClosed = await closeRunningExternalSession( + runningSession, + (session) => this.externalPlayback.closeSession(session), + (message, error) => this.logger.warn(message, error) + ); - // A switch REPLACES what is playing. With MPV or VLC and instance - // reuse off, the backend spawns a second detached player otherwise — - // both sources keep running and Stop owns only the newer one. - const previousPlayerClosed = await closeRunningExternalSession( - runningSession, - (session) => this.externalPlayback.closeSession(session), - (message, error) => this.logger.warn(message, error) - ); + // Closing is a round-trip, and a second pick across it would + // otherwise reach this line too: both would have seen the same + // session, closed it once, and then launched independently — two + // detached players again, the older one holding a source the user + // has moved on from. + if ( + !previousPlayerClosed || + generation !== this.startGeneration || + !isCurrent() + ) { + return false; + } - // Closing is a round-trip, and a second pick across it would otherwise - // reach this line too: both would have seen the same session, closed - // it once, and then launched independently — two detached players - // again, the older one holding a source the user has moved on from. - if ( - !previousPlayerClosed || - generation !== this.startGeneration || - !isCurrent() - ) { - return false; + // Same movie, different source: still a view. + this.addToRecentlyViewed(); + return await this.applyPlayback(playback, isCurrent); + } finally { + this.pendingStart.settle(startId); } - - // Same movie, different source: still a view. - this.addToRecentlyViewed(); - return await this.applyPlayback(playback, isCurrent); } private startPlayback(playback: ResolvedPortalPlayback): Promise { @@ -483,11 +502,7 @@ export class VodDetailsPlaybackService { this.closeInlinePlayer(); this.claimExternalLaunch(playback, generation); const launch = this.portalPlayer.openResolvedPlayback(playback, true); - return await this.settleExternalLaunch( - generation, - isCurrent, - launch - ); + return await this.settleExternalLaunch(generation, isCurrent, launch); } private claimExternalLaunch( @@ -514,8 +529,7 @@ export class VodDetailsPlaybackService { error ), clearPending: () => this.clearExternalLaunchPending(generation), - clearOwnership: () => - this.clearExternalLaunchOwnership(generation), + clearOwnership: () => this.clearExternalLaunchOwnership(generation), }); } diff --git a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.actions.spec.ts b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.actions.spec.ts index 618a9b972..019500ff6 100644 --- a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.actions.spec.ts +++ b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.actions.spec.ts @@ -101,6 +101,8 @@ describe('VodDetailsRouteComponent fallback actions', () => { const playDownload = jest.fn().mockResolvedValue(undefined); const revealFile = jest.fn().mockResolvedValue({ success: true }); const toggleFavorite = jest.fn(); + const savePlaybackPositionOrThrow = jest.fn().mockResolvedValue(undefined); + const loadAllPositions = jest.fn().mockResolvedValue(undefined); const sparseItem = (): SparseVodItem => ({ info: [], stream_id: 650020, @@ -197,6 +199,8 @@ describe('VodDetailsRouteComponent fallback actions', () => { playDownload.mockClear(); revealFile.mockClear(); toggleFavorite.mockClear(); + savePlaybackPositionOrThrow.mockClear().mockResolvedValue(undefined); + loadAllPositions.mockClear(); await TestBed.configureTestingModule({ imports: [VodDetailsRouteComponent], providers: [ @@ -247,6 +251,7 @@ describe('VodDetailsRouteComponent fallback actions', () => { constructVodStreamUrl, addRecentItem: jest.fn(), backfillContentMetadata: jest.fn(), + loadAllPositions, }, }, { @@ -289,6 +294,10 @@ describe('VodDetailsRouteComponent fallback actions', () => { useValue: { getPlaybackPosition: jest.fn().mockResolvedValue(null), savePlaybackPosition: jest.fn(), + savePlaybackPositionOrThrow, + clearPlaybackPositionOrThrow: jest + .fn() + .mockResolvedValue(undefined), }, }, { @@ -353,6 +362,36 @@ describe('VodDetailsRouteComponent fallback actions', () => { expect(fixture.componentInstance.selectedCatalogItem()).toBeNull(); expect(fixture.componentInstance.selectedCategory()).toBeNull(); }); + it('marks the movie watched from the action row and refreshes catalog badges', async () => { + selectedItem.set(richItem()); + fixture.detectChanges(); + const host = fixture.nativeElement as HTMLElement; + const toggle = host.querySelector( + '[data-testid="vod-watched-toggle"]' + ); + expect(toggle).not.toBeNull(); + expect(toggle?.getAttribute('aria-pressed')).toBe('false'); + + toggle?.click(); + await fixture.whenStable(); + fixture.detectChanges(); + + expect(savePlaybackPositionOrThrow).toHaveBeenCalledWith( + 'playlist-1', + expect.objectContaining({ + contentXtreamId: 650020, + contentType: 'vod', + }) + ); + expect(loadAllPositions).toHaveBeenCalledWith('playlist-1'); + expect( + host + .querySelector('[data-testid="vod-watched-toggle"]') + ?.getAttribute('aria-pressed') + ).toBe('true'); + // A watched copy reads Play, never "Resume" from its final seconds. + expect(host.querySelector('.play-btn--resume')).toBeNull(); + }); it('starts playable sparse VOD inline with catalog presentation', () => { const item = sparseItem(); selectedItem.set(item); diff --git a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.html b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.html index 407855726..ec826e9e7 100644 --- a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.html +++ b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.html @@ -96,6 +96,24 @@ isFavorite() ? 'favorite' : 'favorite_border' }} + @let watchedLabel = + (isWatched() ? 'XTREAM.MARK_UNWATCHED' : 'XTREAM.MARK_WATCHED') + | translate; + @if (isElectron() && !providerOnly()) { @if (isDownloaded()) { } + + @let watchedLabel = + (isWatched() ? 'XTREAM.MARK_UNWATCHED' : 'XTREAM.MARK_WATCHED') + | translate; + + @if (isElectron() && !providerOnly()) { @if (isDownloaded()) { diff --git a/libs/ui/playback/src/lib/vod-details/vod-details.component.spec.ts b/libs/ui/playback/src/lib/vod-details/vod-details.component.spec.ts index 157a94e73..0a1dc6b00 100644 --- a/libs/ui/playback/src/lib/vod-details/vod-details.component.spec.ts +++ b/libs/ui/playback/src/lib/vod-details/vod-details.component.spec.ts @@ -148,10 +148,12 @@ describe('VodDetailsComponent offline playback', () => { playbackPosition = null, externalPlayback = null, providerOnly = false, + isWatched = false, }: { playbackPosition?: number | null; externalPlayback?: ExternalPlayerSession | null; providerOnly?: boolean; + isWatched?: boolean; } = {}) => { fixture = TestBed.createComponent(VodDetailsComponent); fixture.componentRef.setInput('item', STALKER_VOD); @@ -162,6 +164,7 @@ describe('VodDetailsComponent offline playback', () => { fixture.componentRef.setInput('playbackPosition', playbackPosition); fixture.componentRef.setInput('externalPlayback', externalPlayback); fixture.componentRef.setInput('providerOnly', providerOnly); + fixture.componentRef.setInput('isWatched', isWatched); playClicked = jest.fn(); resumeClicked = jest.fn(); fixture.componentInstance.playClicked.subscribe(playClicked); @@ -312,6 +315,8 @@ describe('VodDetailsComponent offline playback', () => { PLAY: 'Play', RESUME: 'Resume', RESTART: 'Restart', + MARK_WATCHED: 'Mark as Watched', + MARK_UNWATCHED: 'Mark as Unwatched', }, PORTALS: { ADD_TO_FAVORITES: 'Add to favorites', @@ -494,4 +499,99 @@ describe('VodDetailsComponent offline playback', () => { expect(playClicked).toHaveBeenCalledWith(STALKER_VOD); expect(playDownload).not.toHaveBeenCalled(); }); + + describe('manual watched toggle', () => { + const watchedButton = (): HTMLButtonElement => + fixture.nativeElement.querySelector( + '[data-test-id="vod-watched-toggle"]' + ) as HTMLButtonElement; + + it('offers Mark as Watched and emits the desired state', async () => { + await render({ playbackPosition: 600 }); + const watchedToggled = jest.fn(); + fixture.componentInstance.watchedToggled.subscribe(watchedToggled); + + expect(buttonText(watchedButton())).toContain('Mark as Watched'); + expect(watchedButton().getAttribute('aria-pressed')).toBe('false'); + expect(buttonText(primaryButton())).toContain('Resume'); + + watchedButton().click(); + + expect(watchedToggled).toHaveBeenCalledWith({ + item: STALKER_VOD, + watched: true, + }); + }); + + it('shows Play instead of Resume once the movie is watched', async () => { + await render({ playbackPosition: 5400, isWatched: true }); + const watchedToggled = jest.fn(); + fixture.componentInstance.watchedToggled.subscribe(watchedToggled); + + expect(buttonText(primaryButton())).toContain('Play'); + expect(buttonText(primaryButton())).not.toContain('Resume'); + expect(findButtonWithText('Restart')).toBeUndefined(); + expect(buttonText(watchedButton())).toContain('Mark as Unwatched'); + expect(watchedButton().getAttribute('aria-pressed')).toBe('true'); + expect(watchedButton().classList).toContain('favorite-btn--active'); + + watchedButton().click(); + + expect(watchedToggled).toHaveBeenCalledWith({ + item: STALKER_VOD, + watched: false, + }); + }); + + it.each([ + [ + 'the inline player is mounted', + () => + fixture.componentRef.setInput('inlinePlayback', { + streamUrl: 'https://portal.example/movie.mp4', + title: 'Catalog Movie', + }), + ], + [ + 'an external session owns the movie', + () => + fixture.componentRef.setInput( + 'externalPlayback', + MATCHING_MPV_SESSION + ), + ], + [ + 'an external launch is pending', + () => + fixture.componentRef.setInput( + 'externalPlayback', + MATCHING_LAUNCHING_MPV_SESSION + ), + ], + [ + 'a watched write is in flight', + () => fixture.componentRef.setInput('watchedToggleBusy', true), + ], + [ + 'the stored position has not loaded yet', + () => + fixture.componentRef.setInput('watchedToggleReady', false), + ], + [ + 'a playback start is still resolving', + () => + fixture.componentRef.setInput('playbackStartPending', true), + ], + ])('disables the toggle while %s', async (_label, arrange) => { + await render(); + const watchedToggled = jest.fn(); + fixture.componentInstance.watchedToggled.subscribe(watchedToggled); + arrange(); + fixture.detectChanges(); + + expect(watchedButton().disabled).toBe(true); + fixture.componentInstance.toggleWatched(); + expect(watchedToggled).not.toHaveBeenCalled(); + }); + }); }); diff --git a/libs/ui/playback/src/lib/vod-details/vod-details.component.ts b/libs/ui/playback/src/lib/vod-details/vod-details.component.ts index c45d592a9..5b804f61e 100644 --- a/libs/ui/playback/src/lib/vod-details/vod-details.component.ts +++ b/libs/ui/playback/src/lib/vod-details/vod-details.component.ts @@ -102,6 +102,21 @@ export class VodDetailsComponent { /** Provider detail handoff hides local/download presentation only. */ readonly providerOnly = input(false); + /** The host's verdict on the position row (≥90% or marked by hand). */ + readonly isWatched = input(false); + + /** A watched write is in flight; the toggle waits for it. */ + readonly watchedToggleBusy = input(false); + + /** The host has the stored row in hand (`playbackPosition` is not a placeholder). */ + readonly watchedToggleReady = input(true); + + /** + * A Play/Resume is still resolving with the portal: nothing plays yet, + * but the player about to start would overwrite a row written now. + */ + readonly playbackStartPending = input(false); + // ============ Outputs ============ /** Emitted when play button is clicked */ @@ -119,6 +134,12 @@ export class VodDetailsComponent { isFavorite: boolean; }>(); + /** Emitted when the manual watched toggle is clicked (desired state) */ + readonly watchedToggled = output<{ + item: VodDetailsItem; + watched: boolean; + }>(); + /** Emitted when back button is clicked */ readonly backClicked = output(); @@ -197,10 +218,13 @@ export class VodDetailsComponent { void this.router.navigate(this.crossPortalSimilar.buildLink(item)); } - /** Whether there's a playback position to resume from */ + /** + * Whether there's a playback position to resume from. A watched movie + * shows Play, not "Resume 1:32:00" from its final seconds. + */ readonly hasPlaybackPosition = computed(() => { const pos = this.playbackPosition(); - return pos !== null && pos > 0; + return pos !== null && pos > 0 && !this.isWatched(); }); /** Formatted playback position (e.g., "12:34" or "1:23:45") */ @@ -293,6 +317,31 @@ export class VodDetailsComponent { } } + /** + * The manual watched toggle stays off while playback owns the row: the + * player persists its position every ~15 s and would overwrite a + * just-written full-progress row, silently flipping the movie back. + */ + readonly canToggleWatched = computed( + () => + !this.watchedToggleBusy() && + this.watchedToggleReady() && + !this.playbackStartPending() && + this.inlinePlayback() === null && + this.matchedExternalPlayback() === null && + !this.isExternalLaunchPending() + ); + + toggleWatched(): void { + if (!this.canToggleWatched()) { + return; + } + this.watchedToggled.emit({ + item: this.item(), + watched: !this.isWatched(), + }); + } + /** Handle favorite toggle - emits the desired new state */ toggleFavorite(): void { this.favoriteToggled.emit({ diff --git a/libs/ui/styles/_detail-view-actions.scss b/libs/ui/styles/_detail-view-actions.scss index 71cfae339..3335f83f6 100644 --- a/libs/ui/styles/_detail-view-actions.scss +++ b/libs/ui/styles/_detail-view-actions.scss @@ -74,6 +74,24 @@ background: rgba(255, 91, 110, 0.24); } } + + // The watched toggle shares the ghost surface but reads green when + // active, like the catalog's watched badge. + &.watched-btn.favorite-btn--active { + background: rgba(74, 222, 128, 0.16); + border-color: rgba(74, 222, 128, 0.4); + color: #4ade80; + + &:hover { + background: rgba(74, 222, 128, 0.24); + } + } + + &.watched-btn:disabled { + opacity: 0.55; + cursor: default; + pointer-events: none; + } } @mixin download-button-state { @@ -174,6 +192,21 @@ animation: icon-action-pulse 180ms ease; } + // Watched: the same green as the catalog badge, filled glyph. + &--watched.is-active { + color: #4ade80; + + &:hover { + color: #6ee7a0; + } + } + + &--watched:disabled { + opacity: 0.55; + cursor: default; + pointer-events: none; + } + // Downloaded: green check; the click now opens the file's location. &--done { color: #4ade80; From 6f7973a9fb92a84bfbffe65bbbbe71e58139fe0f Mon Sep 17 00:00:00 2001 From: 4gray <4gray@users.noreply.github.com> Date: Tue, 15 Sep 2026 17:49:22 +0200 Subject: [PATCH 07/43] feat(updater): nightly builds and a stable/nightly update channel (#1608) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(updater): nightly builds and a stable/nightly update channel Every master push publishes its artifacts as a prerelease of 4gray/iptvnator-nightly instead of the rolling test-master draft, with a version of -nightly.. applied in every build job. Settings → About gains an Update channel switch; AppUpdateService re-points electron-updater per check (feed repository, allowPrerelease, channel name, allowDowngrade reset) and reads release notes from the repository the requested version belongs to. Channel switches are forward-only: a nightly build stays until a newer stable release exists. Co-Authored-By: Claude Fable 5.1 * fix(updater): compute the nightly version once and keep re-runs safe Review follow-ups: the nightly version is resolved by a leading job and handed to every build job, and the patch is bumped only when the base tag already exists so the release-cut window stays below the imminent release. A re-run never deletes a published nightly; only a draft left by a failed run is replaced. Typed update-status literals in the remaining specs carry the new channel fields. Co-Authored-By: Claude Fable 5.1 * test(packaging): expect the nightly-version prerequisite in the build workflow graph Co-Authored-By: Claude Fable 5.1 --------- Co-authored-by: Claude Fable 5.1 --- .changes/updater-nightly-channel.md | 7 + .github/workflows/build-and-make.yaml | 290 +++++++++++++++- CLAUDE.md | 5 +- README.md | 12 + .../src/app/events/app-update.events.spec.ts | 10 +- .../src/app/events/settings.events.spec.ts | 33 ++ .../src/app/events/settings.events.ts | 7 + .../src/app/services/app-update-channel.ts | 48 +++ .../src/app/services/app-update-feed.ts | 48 +++ .../services/app-update-release-catalog.ts | 178 ++++++++++ .../app/services/app-update.service.spec.ts | 252 ++++++++++++++ .../src/app/services/app-update.service.ts | 323 +++++++----------- .../src/app/services/store.service.ts | 14 +- apps/electron-backend/src/main.ts | 8 + ...pdate-notification-panel.component.spec.ts | 2 + .../app-update-install.service.spec.ts | 3 + .../settings-about-section.component.html | 59 ++++ .../settings-about-section.component.spec.ts | 85 +++++ .../settings-about-section.component.ts | 40 ++- .../app/settings/settings-form.utils.spec.ts | 32 ++ .../src/app/settings/settings-form.utils.ts | 5 + apps/web/src/app/settings/settings-options.ts | 12 + .../src/app/settings/settings.component.html | 2 + .../src/app/settings/settings.component.ts | 2 + apps/web/src/app/settings/settings.models.ts | 6 + .../test-stubs/settings-test-harness.stub.ts | 3 + apps/web/src/assets/i18n/ar.json | 6 + apps/web/src/assets/i18n/ary.json | 6 + apps/web/src/assets/i18n/by.json | 6 + apps/web/src/assets/i18n/de.json | 6 + apps/web/src/assets/i18n/el.json | 6 + apps/web/src/assets/i18n/en.json | 6 + apps/web/src/assets/i18n/es.json | 6 + apps/web/src/assets/i18n/fr.json | 6 + apps/web/src/assets/i18n/hu.json | 6 + apps/web/src/assets/i18n/it.json | 6 + apps/web/src/assets/i18n/ja.json | 6 + apps/web/src/assets/i18n/ko.json | 6 + apps/web/src/assets/i18n/nl.json | 6 + apps/web/src/assets/i18n/pl.json | 6 + apps/web/src/assets/i18n/pt.json | 6 + apps/web/src/assets/i18n/ru.json | 6 + apps/web/src/assets/i18n/tr.json | 6 + apps/web/src/assets/i18n/zh.json | 6 + apps/web/src/assets/i18n/zhtw.json | 6 + docs/architecture/release-pipeline.md | 87 +++++ .../src/lib/settings-store.service.ts | 5 + libs/shared/interfaces/src/index.ts | 1 + .../src/lib/app-update-channel.util.spec.ts | 97 ++++++ .../src/lib/app-update-channel.util.ts | 177 ++++++++++ .../src/lib/electron-api.interface.ts | 6 + .../interfaces/src/lib/settings.interface.ts | 10 + nx.json | 5 +- .../configure-linux-frame-copy-build.test.mjs | 10 +- tools/release/nightly-version.mjs | 255 ++++++++++++++ tools/release/nightly-version.test.mjs | 219 ++++++++++++ tools/release/project.json | 6 +- 57 files changed, 2249 insertions(+), 229 deletions(-) create mode 100644 .changes/updater-nightly-channel.md create mode 100644 apps/electron-backend/src/app/services/app-update-channel.ts create mode 100644 apps/electron-backend/src/app/services/app-update-feed.ts create mode 100644 apps/electron-backend/src/app/services/app-update-release-catalog.ts create mode 100644 libs/shared/interfaces/src/lib/app-update-channel.util.spec.ts create mode 100644 libs/shared/interfaces/src/lib/app-update-channel.util.ts create mode 100644 tools/release/nightly-version.mjs create mode 100644 tools/release/nightly-version.test.mjs diff --git a/.changes/updater-nightly-channel.md b/.changes/updater-nightly-channel.md new file mode 100644 index 000000000..3041a169a --- /dev/null +++ b/.changes/updater-nightly-channel.md @@ -0,0 +1,7 @@ +--- +type: feature +area: updater +highlight: Nightly update channel +--- + +Settings → About now has an Update channel switch. Nightly follows every merge into master: the desktop updater offers each new build automatically, and About shows the build's commit for bug reports. Stable stays the default; a nightly build remains installed until the next stable release is newer. diff --git a/.github/workflows/build-and-make.yaml b/.github/workflows/build-and-make.yaml index 1cd46c20d..7a91c9b8d 100644 --- a/.github/workflows/build-and-make.yaml +++ b/.github/workflows/build-and-make.yaml @@ -13,6 +13,14 @@ name: Build and Make Electron App # cleanup-pr-draft.yml deletes the draft when the PR closes, so the stale # window is visible and bounded; refreshing drafts on skipped runs is not # worth a separate workflow. +# +# Master pushes are the nightly channel: the nightly-version job computes +# one -nightly.. version for the whole run +# (tools/release/nightly-version.mjs), every build job writes it into +# package.json so electron-updater treats the build as newer than the +# released version, and the release job publishes the artifacts as a +# prerelease of 4gray/iptvnator-nightly instead of the rolling test-master +# draft. Contract: docs/architecture/release-pipeline.md ("Nightly channel"). on: push: branches: @@ -42,6 +50,39 @@ permissions: contents: read jobs: + # One version for the whole run. Computed here rather than in each build + # job because the rule depends on whether the base tag exists on origin: + # a tag pushed while the matrix runs would otherwise give one run two + # different versions. Empty output means "not a nightly build". + nightly-version: + name: Resolve nightly version + runs-on: ubuntu-latest + timeout-minutes: 5 + outputs: + version: ${{ steps.resolve.outputs.version }} + steps: + - name: Checkout code + if: github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == '4gray/iptvnator' + uses: actions/checkout@v7 + + - name: Resolve nightly version + id: resolve + shell: bash + env: + NIGHTLY: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == '4gray/iptvnator' }} + run: | + set -euo pipefail + + if [ "${NIGHTLY}" != "true" ]; then + echo "version=" >> "${GITHUB_OUTPUT}" + echo "Not a master push; no nightly version." + exit 0 + fi + + VERSION="$(node tools/release/nightly-version.mjs)" + echo "version=${VERSION}" >> "${GITHUB_OUTPUT}" + echo "Nightly version: ${VERSION}" + linux-embedded-mpv-runtime: name: Build pinned Linux Embedded MPV runtime runs-on: ubuntu-22.04 @@ -340,6 +381,7 @@ jobs: build-cross-platform: name: Build on ${{ matrix.os }} ${{ matrix.arch }} + needs: nightly-version runs-on: ${{ matrix.runner }} timeout-minutes: 120 concurrency: @@ -445,6 +487,18 @@ jobs: BUILD_COMMIT: ${{ github.event.pull_request.head.sha || github.sha }} run: node tools/build/inject-build-commit.mjs + - name: Apply nightly version + # Master merges feed the nightly update channel. The version + # must be greater than the released one for electron-updater to + # offer it, and it must be in package.json before the frontend + # and backend builds and electron-builder read it. The value + # comes from the nightly-version job so every job of this run + # builds the same version. + if: needs.nightly-version.outputs.version != '' + env: + NIGHTLY_VERSION: ${{ needs.nightly-version.outputs.version }} + run: node tools/release/nightly-version.mjs --apply --version "${NIGHTLY_VERSION}" + - name: Build frontend run: pnpm nx build web --skip-nx-cache @@ -1188,6 +1242,7 @@ jobs: dist/executables/**/*.dmg dist/executables/**/*.zip dist/executables/**/latest-mac.yml + dist/executables/**/nightly-mac.yml dist/executables/**/*.blockmap retention-days: 7 @@ -1212,6 +1267,7 @@ jobs: dist/executables/*.AppImage dist/executables/*.snap dist/executables/**/latest-linux*.yml + dist/executables/**/nightly-linux*.yml dist/executables/**/*.blockmap retention-days: 7 @@ -1234,12 +1290,15 @@ jobs: dist/executables/**/*.msi dist/executables/**/*.zip dist/executables/**/latest.yml + dist/executables/**/nightly.yml dist/executables/**/*.blockmap retention-days: 7 build-linux: name: Build on ${{ matrix.os }} ${{ matrix.arch }} (${{ matrix.linux_profile }}) - needs: linux-embedded-mpv-runtime + needs: + - linux-embedded-mpv-runtime + - nightly-version runs-on: ${{ matrix.runner }} timeout-minutes: 120 concurrency: @@ -1276,6 +1335,7 @@ jobs: create-release: name: Create Draft Release needs: + - nightly-version - build-cross-platform - build-linux if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }} @@ -1285,6 +1345,12 @@ jobs: cancel-in-progress: false permissions: contents: write + env: + # Master pushes publish to the nightly repository (steps at the + # end of this job) instead of the rolling draft. + NIGHTLY: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' && github.repository == '4gray/iptvnator' }} + NIGHTLY_REPOSITORY: 4gray/iptvnator-nightly + NIGHTLY_KEEP_RELEASES: '20' steps: - name: Checkout code @@ -1306,10 +1372,20 @@ jobs: node <<'NODE' const fs = require('fs'); - const candidates = [ - 'artifacts/macos-x64-artifacts/latest-mac.yml', - 'artifacts/macos-arm64-artifacts/latest-mac.yml', - ].filter((filePath) => fs.existsSync(filePath)); + // electron-builder names the updater metadata after the + // channel: latest-mac.yml for releases, nightly-mac.yml for + // the prerelease versions master builds carry. + const channelFile = ['latest-mac.yml', 'nightly-mac.yml'].find( + (name) => + fs.existsSync(`artifacts/macos-x64-artifacts/${name}`) || + fs.existsSync(`artifacts/macos-arm64-artifacts/${name}`) + ); + const candidates = channelFile + ? [ + `artifacts/macos-x64-artifacts/${channelFile}`, + `artifacts/macos-arm64-artifacts/${channelFile}`, + ].filter((filePath) => fs.existsSync(filePath)) + : []; if (candidates.length === 0) { console.log('No macOS update metadata found; skipping merge.'); @@ -1407,8 +1483,8 @@ jobs: mergedEntries ); - fs.writeFileSync('artifacts/latest-mac.yml', merged); - console.log(`Merged ${candidates.length} macOS update metadata files.`); + fs.writeFileSync(`artifacts/${channelFile}`, merged); + console.log(`Merged ${candidates.length} macOS update metadata files into ${channelFile}.`); NODE - name: Get version from package.json @@ -1422,6 +1498,7 @@ jobs: # for every push. PR builds must not use github.sha here: that is the # ephemeral merge-commit SHA, which resolves to nothing in the repo. - name: Compose release metadata + if: env.NIGHTLY != 'true' id: release-meta shell: bash env: @@ -1499,7 +1576,7 @@ jobs: # full current set right after. Only drafts are pruned; published # releases are never touched. - name: Prune stale draft assets - if: github.event_name != 'pull_request' || steps.pr-state.outputs.state == 'open' + if: env.NIGHTLY != 'true' && (github.event_name != 'pull_request' || steps.pr-state.outputs.state == 'open') env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_TAG: ${{ steps.release-meta.outputs.tag }} @@ -1522,7 +1599,7 @@ jobs: - name: Create Draft Release id: draft-release - if: github.event_name != 'pull_request' || steps.pr-state.outputs.state == 'open' + if: env.NIGHTLY != 'true' && (github.event_name != 'pull_request' || steps.pr-state.outputs.state == 'open') uses: softprops/action-gh-release@v3 with: draft: true @@ -1569,7 +1646,7 @@ jobs: # body is left as the action set it and only title/commitish are # re-asserted. - name: Ensure draft metadata is current - if: steps.draft-release.outputs.id != '' + if: env.NIGHTLY != 'true' && steps.draft-release.outputs.id != '' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_ID: ${{ steps.draft-release.outputs.id }} @@ -1612,3 +1689,196 @@ jobs: --arg body "${FULL_BODY}" \ '{tag_name: $tag, name: $name, target_commitish: $commitish, body: ($body | .[0:120000])}' | gh api -X PATCH "repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}" --input - > /dev/null + + # ── Nightly channel ────────────────────────────────────────── + # Drafts are invisible to anyone without write access and to + # electron-updater, so master builds are published as prereleases + # of the nightly repository, which the desktop app's Nightly update + # channel follows. The repository needs one commit on its default + # branch (gh creates the release tag there) and a fine-grained PAT + # with Contents: read/write on it, stored as NIGHTLY_RELEASE_TOKEN. + # Without the token the build still succeeds and only warns. + - name: Resolve nightly release metadata + if: env.NIGHTLY == 'true' + id: nightly-meta + shell: bash + env: + NIGHTLY_RELEASE_TOKEN: ${{ secrets.NIGHTLY_RELEASE_TOKEN }} + VERSION: ${{ needs.nightly-version.outputs.version }} + run: | + set -euo pipefail + + if [ -z "${VERSION}" ]; then + echo "::error::The nightly-version job produced no version for this master push." + exit 1 + fi + { + echo "version=${VERSION}" + echo "tag=v${VERSION}" + } >> "${GITHUB_OUTPUT}" + + if [ -z "${NIGHTLY_RELEASE_TOKEN}" ]; then + echo "::warning::NIGHTLY_RELEASE_TOKEN is not configured; the nightly release for ${VERSION} is skipped." + echo "publish=false" >> "${GITHUB_OUTPUT}" + else + echo "publish=true" >> "${GITHUB_OUTPUT}" + fi + + # The notes list the master commits since the previous nightly. + # That nightly's source commit is read back from the marker its + # own notes carry, because the nightly repository has no copy of + # the app history to compare against. The main-repository compare + # uses GITHUB_TOKEN; only the nightly repository is read with the + # PAT. + - name: Compose nightly release notes + if: steps.nightly-meta.outputs.publish == 'true' + id: nightly-notes + shell: bash + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NIGHTLY_RELEASE_TOKEN: ${{ secrets.NIGHTLY_RELEASE_TOKEN }} + VERSION: ${{ steps.nightly-meta.outputs.version }} + HEAD_SHA: ${{ github.sha }} + REPO_URL: ${{ github.server_url }}/${{ github.repository }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + set -euo pipefail + + SHORT_SHA="${HEAD_SHA:0:7}" + NOTES_FILE="${RUNNER_TEMP}/nightly-notes.md" + + PREVIOUS_TAG="$(GH_TOKEN="${NIGHTLY_RELEASE_TOKEN}" gh release list \ + --repo "${NIGHTLY_REPOSITORY}" --exclude-drafts --limit 1 \ + --json tagName --jq '.[0].tagName // ""')" + PREVIOUS_SHA="" + if [ -n "${PREVIOUS_TAG}" ]; then + PREVIOUS_SHA="$(GH_TOKEN="${NIGHTLY_RELEASE_TOKEN}" gh release view "${PREVIOUS_TAG}" \ + --repo "${NIGHTLY_REPOSITORY}" --json body --jq '.body // ""' | + sed -n 's/.*.*/\1/p' | head -n 1)" + fi + + COMMITS="" + if [ -n "${PREVIOUS_SHA}" ] && [ "${PREVIOUS_SHA}" != "${HEAD_SHA}" ]; then + # A rewritten history makes the compare fail; the notes + # then just omit the list rather than failing the release. + COMMITS="$(gh api "repos/${GITHUB_REPOSITORY}/compare/${PREVIOUS_SHA}...${HEAD_SHA}" \ + --jq '.commits[] | "- [`\(.sha[0:7])`](\(.html_url)) \(.commit.message | split("\n")[0])"' || true)" + fi + + { + printf '🌙 Nightly build from `master` — commit [`%s`](%s/commit/%s) · [workflow run](%s)\n\n' \ + "${SHORT_SHA}" "${REPO_URL}" "${HEAD_SHA}" "${RUN_URL}" + printf 'Untested snapshot of master for the desktop app'"'"'s **Nightly** update channel (Settings → About → Update channel). Nightly builds may break, and their database changes are permanent: switching back to Stable keeps this build installed until the next stable release is newer. Back up your playlists first.\n\n' + if [ -n "${COMMITS}" ]; then + printf '## Changes since %s\n\n%s\n\n' "${PREVIOUS_TAG}" "${COMMITS}" + else + printf 'See the [commit history](%s/commits/master) for what changed.\n\n' "${REPO_URL}" + fi + printf '\n' "${HEAD_SHA}" + } > "${NOTES_FILE}" + + echo "notes-file=${NOTES_FILE}" >> "${GITHUB_OUTPUT}" + + # Created as a draft, assets uploaded, then published in one edit, + # so electron-updater never sees a release whose channel file is + # still missing. A published release is never deleted here: a + # rerun after a successful publish is a no-op, and only a draft + # left behind by a failed run is replaced. The release job is + # serialized per ref but two master runs can still finish out of + # order, so a nightly that is older than the newest published one + # is dropped instead of becoming the feed's newest entry. + - name: Publish nightly release + if: steps.nightly-meta.outputs.publish == 'true' + shell: bash + env: + GH_TOKEN: ${{ secrets.NIGHTLY_RELEASE_TOKEN }} + TAG: ${{ steps.nightly-meta.outputs.tag }} + VERSION: ${{ steps.nightly-meta.outputs.version }} + HEAD_SHA: ${{ github.sha }} + NOTES_FILE: ${{ steps.nightly-notes.outputs.notes-file }} + run: | + set -euo pipefail + shopt -s nullglob + + NEWEST_PUBLISHED="$(gh release list --repo "${NIGHTLY_REPOSITORY}" --exclude-drafts --limit 200 \ + --json tagName --jq '.[].tagName | select(test("^v[0-9]+\\.[0-9]+\\.[0-9]+-nightly\\."))' | + sort -V | tail -n 1)" + if [ -n "${NEWEST_PUBLISHED}" ] && [ "${NEWEST_PUBLISHED}" != "${TAG}" ] && + [ "$(printf '%s\n%s\n' "${NEWEST_PUBLISHED}" "${TAG}" | sort -V | tail -n 1)" != "${TAG}" ]; then + echo "::notice::${NEWEST_PUBLISHED} is already published and newer than ${TAG}; not publishing this superseded build." + exit 0 + fi + + for required in \ + artifacts/nightly-mac.yml \ + artifacts/windows-artifacts/nightly.yml \ + artifacts/linux-portable-artifacts/nightly-linux.yml; do + if [ ! -f "${required}" ]; then + echo "::error::Missing updater metadata ${required}; the nightly channel would be unable to install this build." + exit 1 + fi + done + + assets=( + artifacts/macos-x64-artifacts/*-x64.dmg + artifacts/macos-x64-artifacts/*-x64.zip + artifacts/macos-x64-artifacts/*.blockmap + artifacts/macos-arm64-artifacts/*-arm64.dmg + artifacts/macos-arm64-artifacts/*-arm64.zip + artifacts/macos-arm64-artifacts/*.blockmap + artifacts/nightly-mac.yml + artifacts/linux-system-artifacts/*.deb + artifacts/linux-system-artifacts/*.rpm + artifacts/linux-system-artifacts/*.pacman + artifacts/linux-system-artifacts/*.pkg.tar.* + artifacts/linux-portable-artifacts/*.AppImage + artifacts/linux-portable-artifacts/*.snap + artifacts/linux-portable-artifacts/nightly-linux*.yml + artifacts/linux-portable-artifacts/*.blockmap + artifacts/linux-flatpak-artifacts/*.flatpak + artifacts/linux-frame-copy-runtime-sources/linux-frame-copy-runtime-sources.tar.xz + artifacts/windows-artifacts/*-setup.exe + artifacts/windows-artifacts/*.msi + artifacts/windows-artifacts/*.zip + artifacts/windows-artifacts/nightly.yml + artifacts/windows-artifacts/*.blockmap + ) + + EXISTING="$(gh api "repos/${NIGHTLY_REPOSITORY}/releases?per_page=100" --paginate | + jq -c --arg tag "${TAG}" 'map(select(.tag_name == $tag)) | first // empty')" + if [ -n "${EXISTING}" ]; then + if [ "$(jq -r '.draft' <<< "${EXISTING}")" != "true" ]; then + echo "::notice::${TAG} is already published in ${NIGHTLY_REPOSITORY}; nothing to do for this re-run." + exit 0 + fi + DRAFT_ID="$(jq -r '.id' <<< "${EXISTING}")" + echo "Removing the draft ${TAG} (id ${DRAFT_ID}) a failed run left behind." + gh api -X DELETE "repos/${NIGHTLY_REPOSITORY}/releases/${DRAFT_ID}" + fi + + gh release create "${TAG}" "${assets[@]}" \ + --repo "${NIGHTLY_REPOSITORY}" \ + --draft \ + --prerelease \ + --title "Nightly ${VERSION} (${HEAD_SHA:0:7})" \ + --notes-file "${NOTES_FILE}" + gh release edit "${TAG}" --repo "${NIGHTLY_REPOSITORY}" --draft=false --prerelease + + echo "Published ${TAG} to ${NIGHTLY_REPOSITORY} with ${#assets[@]} assets." + + - name: Prune old nightly releases + if: steps.nightly-meta.outputs.publish == 'true' + shell: bash + env: + GH_TOKEN: ${{ secrets.NIGHTLY_RELEASE_TOKEN }} + run: | + set -euo pipefail + + gh release list --repo "${NIGHTLY_REPOSITORY}" --exclude-drafts --limit 200 \ + --json tagName --jq '.[].tagName | select(test("^v[0-9]+\\.[0-9]+\\.[0-9]+-nightly\\."))' | + sort -V -r | tail -n "+$((NIGHTLY_KEEP_RELEASES + 1))" | + while read -r tag; do + [ -n "${tag}" ] || continue + echo "Deleting nightly ${tag} (keeping the newest ${NIGHTLY_KEEP_RELEASES})." + gh release delete "${tag}" --repo "${NIGHTLY_REPOSITORY}" --cleanup-tag --yes + done diff --git a/CLAUDE.md b/CLAUDE.md index 60da49c6a..7d1012edf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1712,7 +1712,10 @@ Build configurations in `apps/web/project.json`: The factory pattern ensures a single codebase works in both environments without conditional checks scattered throughout the application. All environment-specific logic is encapsulated in the service implementations. **Build Commit In About**: -CI injects the git commit into `apps/web/src/environments/build-commit.ts` via `tools/build/inject-build-commit.mjs` (same placeholder pattern as the TMDB key inject); `Settings > About` then shows `" ()"`. The semver version itself deliberately stays untouched — a `-sha` suffix would flip electron-updater into prerelease mode and leak into installer/artifact version fields. Local/dev builds keep the placeholder empty and show the plain version. +CI injects the git commit into `apps/web/src/environments/build-commit.ts` via `tools/build/inject-build-commit.mjs` (same placeholder pattern as the TMDB key inject); `Settings > About` then shows `" ()"`. The semver version itself stays untouched on PR and tag builds — a `-sha` suffix would flip electron-updater into prerelease mode and leak into installer/artifact version fields. Local/dev builds keep the placeholder empty and show the plain version. + +**Nightly Builds And Update Channel**: +Master pushes are the nightly channel. The leading `nightly-version` job computes one `-nightly..` version per run (`tools/release/nightly-version.mjs`; the patch is bumped only when `v` already exists on origin, so the release-cut window stays below the imminent release), every build job writes it into `package.json` (`--apply --version`), and the `create-release` job publishes the artifacts as a prerelease of `4gray/iptvnator-nightly` (secret `NIGHTLY_RELEASE_TOKEN`; missing token only warns) instead of the rolling `test-master` draft, keeping the newest 20. electron-builder names the updater metadata after the prerelease tag (`nightly-mac.yml` / `nightly.yml` / `nightly-linux.yml`), so upload globs and the macOS merge accept both names. `Settings.updateChannel` (Settings → About, Electron only, default `stable`) is mirrored into the main-process config (`APP_UPDATE_CHANNEL`, `app-update-channel.ts`) for the startup check; `AppUpdateService` applies the channel to electron-updater before every check (`app-update-feed.ts`: feed repository, `allowPrerelease`, channel name, and `allowDowngrade` forced back to `false` — assigning a channel silently enables downgrades) and reads release notes from the repository the requested version belongs to (`app-update-release-catalog.ts`). Switching back to stable is forward-only: the nightly stays until a newer stable release exists, because a downgrade could hit a database schema a nightly migration already applied. Contract: `docs/architecture/release-pipeline.md` ("Nightly channel"). ### Testing Strategy diff --git a/README.md b/README.md index 89869f174..eaea6e8ae 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,18 @@ Download the latest version of the application for macOS, Windows, and Linux fro Alternatively, you can install the application using one of the following package managers: +### Nightly builds + +Every merge into `master` is published as a prerelease in +[4gray/iptvnator-nightly](https://github.com/4gray/iptvnator-nightly). The +desktop app can follow them: **Settings → About → Update channel → Nightly** +offers each new build through the built-in updater (macOS, Windows, and the +Linux AppImage). Nightly builds are untested snapshots — they may break, and +their database changes are permanent, so switching back to **Stable** keeps +the installed nightly until the next stable release is newer. Back up your +playlists first, and mention the version and commit shown in *About* when +reporting a bug from a nightly. + ### Homebrew ```shell diff --git a/apps/electron-backend/src/app/events/app-update.events.spec.ts b/apps/electron-backend/src/app/events/app-update.events.spec.ts index d0e80b40f..799893e1c 100644 --- a/apps/electron-backend/src/app/events/app-update.events.spec.ts +++ b/apps/electron-backend/src/app/events/app-update.events.spec.ts @@ -14,8 +14,9 @@ jest.mock('electron', () => ({ ipcMain: { handle: jest.fn( (channel: string, handler: (...args: unknown[]) => unknown) => { - mockHandlers.set(channel, handler); - }), + mockHandlers.set(channel, handler); + } + ), }, })); @@ -32,6 +33,8 @@ describe('AppUpdateEvents', () => { 'https://github.com/4gray/iptvnator/releases/latest', status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Idle, supportedSelfUpdate: true, + channel: 'stable', + installedChannel: 'stable', }; const service = { checkForUpdates: jest.fn().mockResolvedValue({ @@ -59,7 +62,8 @@ describe('AppUpdateEvents', () => { status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Downloaded, })), }; - const { default: AppUpdateEvents } = await import('./app-update.events'); + const { default: AppUpdateEvents } = + await import('./app-update.events'); AppUpdateEvents.bootstrapAppUpdateEvents(service); diff --git a/apps/electron-backend/src/app/events/settings.events.spec.ts b/apps/electron-backend/src/app/events/settings.events.spec.ts index 65c7f459d..a5cd5c03a 100644 --- a/apps/electron-backend/src/app/events/settings.events.spec.ts +++ b/apps/electron-backend/src/app/events/settings.events.spec.ts @@ -22,6 +22,7 @@ type SettingsUpdateHandler = ( const SETTINGS_UPDATE = 'SETTINGS_UPDATE'; const STORE_KEYS = { + APP_UPDATE_CHANNEL: 'APP_UPDATE_CHANNEL', EMBEDDED_MPV_AUTO_RECONNECT: 'EMBEDDED_MPV_AUTO_RECONNECT', EMBEDDED_MPV_EXTRA_OPTIONS: 'EMBEDDED_MPV_EXTRA_OPTIONS', EMBEDDED_MPV_FRAME_COPY: 'EMBEDDED_MPV_FRAME_COPY', @@ -50,6 +51,7 @@ jest.mock('electron', () => ({ })); jest.mock('../services/store.service', () => ({ + APP_UPDATE_CHANNEL: STORE_KEYS.APP_UPDATE_CHANNEL, EMBEDDED_MPV_AUTO_RECONNECT: STORE_KEYS.EMBEDDED_MPV_AUTO_RECONNECT, EMBEDDED_MPV_EXTRA_OPTIONS: STORE_KEYS.EMBEDDED_MPV_EXTRA_OPTIONS, EMBEDDED_MPV_FRAME_COPY: STORE_KEYS.EMBEDDED_MPV_FRAME_COPY, @@ -219,6 +221,37 @@ describe('SETTINGS_UPDATE', () => { ]); }); + it('mirrors the update channel into the main-process store and notifies the updater', async () => { + const channelModule = await import('../services/app-update-channel'); + const listener = jest.fn(); + channelModule.onAppUpdateChannelChange(listener); + + settingsUpdateHandler({}, { updateChannel: 'nightly' }); + + expect(mockStoreSet.mock.calls).toEqual([ + [STORE_KEYS.APP_UPDATE_CHANNEL, 'nightly'], + ]); + expect(listener).toHaveBeenCalledWith('nightly'); + }); + + it('normalizes an unknown update channel to stable and stays quiet when nothing changed', async () => { + const channelModule = await import('../services/app-update-channel'); + const listener = jest.fn(); + channelModule.onAppUpdateChannelChange(listener); + + settingsUpdateHandler( + {}, + { + updateChannel: 'canary' as unknown as 'stable', + } + ); + + expect(mockStoreSet.mock.calls).toEqual([ + [STORE_KEYS.APP_UPDATE_CHANNEL, 'stable'], + ]); + expect(listener).not.toHaveBeenCalled(); + }); + it('reconciles an enabled remote-control update with the stored port', () => { mockStoreGet.mockImplementation( (key: string, fallbackValue: unknown): unknown => diff --git a/apps/electron-backend/src/app/events/settings.events.ts b/apps/electron-backend/src/app/events/settings.events.ts index bb32607f5..c289739c2 100644 --- a/apps/electron-backend/src/app/events/settings.events.ts +++ b/apps/electron-backend/src/app/events/settings.events.ts @@ -19,6 +19,7 @@ import { } from '../services/store.service'; import { httpServer } from '../server/http-server'; import { setHostConnectivityGuardEnabled } from '../util/host-connectivity-guard'; +import { persistAppUpdateChannel } from '../services/app-update-channel'; export default class SettingsEvents { static bootstrapSettingsEvents(): Electron.IpcMain { @@ -91,6 +92,12 @@ ipcMain.handle('SETTINGS_UPDATE', (_event, arg) => { store.set(VLC_REUSE_INSTANCE, arg.vlcReuseInstance); } + // Mirrored for the startup update check, which runs before the renderer + // exists; a change re-points the running updater immediately. + if (arg.updateChannel !== undefined) { + persistAppUpdateChannel(arg.updateChannel); + } + // Handle remote control settings if ( arg.remoteControl !== undefined || diff --git a/apps/electron-backend/src/app/services/app-update-channel.ts b/apps/electron-backend/src/app/services/app-update-channel.ts new file mode 100644 index 000000000..755eb3d53 --- /dev/null +++ b/apps/electron-backend/src/app/services/app-update-channel.ts @@ -0,0 +1,48 @@ +import { + AppUpdateChannel, + normalizeAppUpdateChannel, +} from '@iptvnator/shared/interfaces'; +import { APP_UPDATE_CHANNEL, store } from './store.service'; + +type AppUpdateChannelListener = (channel: AppUpdateChannel) => void; + +const listeners = new Set(); + +/** + * The persisted update channel, read synchronously from the main-process + * config so the startup update check can use it before any renderer exists. + * Junk in the config file collapses to `stable`. + */ +export function readStoredAppUpdateChannel(): AppUpdateChannel { + return normalizeAppUpdateChannel(store.get(APP_UPDATE_CHANNEL)); +} + +/** + * Mirrors a saved `Settings.updateChannel` into the config file and tells + * the updater. Listeners only run on an actual change, so a Save that keeps + * the channel cannot restart an update check. + */ +export function persistAppUpdateChannel(value: unknown): AppUpdateChannel { + const channel = normalizeAppUpdateChannel(value); + const previous = readStoredAppUpdateChannel(); + + store.set(APP_UPDATE_CHANNEL, channel); + + if (channel !== previous) { + for (const listener of listeners) { + listener(channel); + } + } + + return channel; +} + +export function onAppUpdateChannelChange( + listener: AppUpdateChannelListener +): () => void { + listeners.add(listener); + + return () => { + listeners.delete(listener); + }; +} diff --git a/apps/electron-backend/src/app/services/app-update-feed.ts b/apps/electron-backend/src/app/services/app-update-feed.ts new file mode 100644 index 000000000..0966e7cf2 --- /dev/null +++ b/apps/electron-backend/src/app/services/app-update-feed.ts @@ -0,0 +1,48 @@ +import { + AppUpdateChannel, + appUpdateRepository, + NIGHTLY_PRERELEASE_TAG, +} from '@iptvnator/shared/interfaces'; + +export interface AppUpdateGitHubFeedOptions { + provider: 'github'; + owner: string; + repo: string; +} + +/** + * The slice of electron-updater's `AppUpdater` the channel switch touches. + * Kept structural so the service spec can drive it with a fake. + */ +export interface AppUpdateFeedTarget { + allowPrerelease: boolean; + allowDowngrade: boolean; + channel: string | null; + setFeedURL(options: AppUpdateGitHubFeedOptions): void; +} + +/** + * Points electron-updater at the repository behind `channel` right before a + * check. Applied on every check rather than once, because the channel can + * change while the app runs. + * + * Two electron-updater quirks decide the shape of this function: + * + * - The `channel` setter throws once a string channel is replaced by `null`, + * so stable is written as the explicit `latest` (the default channel + * file name) instead of being cleared. + * - Assigning `channel` silently flips `allowDowngrade` to `true`, and the + * constructor turns `allowPrerelease` on for any prerelease build. Both + * are reset afterwards: a nightly build on the stable channel must wait + * for the next stable release rather than downgrade onto one that may + * not understand its database schema. + */ +export function applyAppUpdateChannel( + updater: AppUpdateFeedTarget, + channel: AppUpdateChannel +): void { + updater.setFeedURL({ provider: 'github', ...appUpdateRepository(channel) }); + updater.allowPrerelease = channel === 'nightly'; + updater.channel = channel === 'nightly' ? NIGHTLY_PRERELEASE_TAG : 'latest'; + updater.allowDowngrade = false; +} diff --git a/apps/electron-backend/src/app/services/app-update-release-catalog.ts b/apps/electron-backend/src/app/services/app-update-release-catalog.ts new file mode 100644 index 000000000..e629a83be --- /dev/null +++ b/apps/electron-backend/src/app/services/app-update-release-catalog.ts @@ -0,0 +1,178 @@ +import { + AppUpdateChannel, + appUpdateReleasesApiUrl, + appUpdateReleasesPageUrl, + appVersionChannel, +} from '@iptvnator/shared/interfaces'; + +const GITHUB_RELEASES_PER_PAGE = 10; + +interface GitHubReleaseResponse { + body?: string | null; + draft?: boolean; + html_url?: string; + name?: string | null; + prerelease?: boolean; + published_at?: string | null; + tag_name?: string; +} + +export interface CachedGitHubRelease { + bodyMarkdown: string; + draft: boolean; + htmlUrl: string; + prerelease: boolean; + publishedAt?: string | null; + releaseName?: string | null; + tagName: string; + version: string; +} + +export interface ReleaseFetchResponse { + json(): Promise; + ok: boolean; + status: number; + statusText: string; +} + +export type ReleaseFetcher = ( + url: string, + init?: { headers?: Record } +) => Promise; + +export function normalizeVersion(value: string | null | undefined): string { + return (value ?? '').trim().replace(/^v/i, ''); +} + +function isGitHubRelease(value: unknown): value is GitHubReleaseResponse { + return Boolean( + value && + typeof value === 'object' && + 'tag_name' in value && + typeof (value as GitHubReleaseResponse).tag_name === 'string' + ); +} + +/** + * Lazily paged, newest-first list of one channel's published GitHub + * releases. Drafts are never listed. The stable catalog also drops + * prereleases (a beta tag must not become "the latest version"), while the + * nightly catalog keeps only releases whose version carries the nightly + * identifier, so a stray tag in that repository cannot be offered either. + */ +export class AppUpdateReleaseCatalog { + readonly releases: CachedGitHubRelease[] = []; + loadedReleasePages = 0; + loadedAllReleases = false; + + constructor( + readonly channel: AppUpdateChannel, + private readonly fetcher: ReleaseFetcher, + private readonly userAgent: string + ) {} + + get latest(): CachedGitHubRelease | undefined { + return this.releases[0]; + } + + async ensureFirstReleaseLoaded(): Promise { + while (!this.loadedAllReleases && this.releases.length === 0) { + await this.ensurePageLoaded(this.loadedReleasePages + 1); + } + } + + async ensurePageLoaded(page: number): Promise { + while (!this.loadedAllReleases && this.loadedReleasePages < page) { + await this.loadPage(this.loadedReleasePages + 1); + } + } + + /** Index of `version` (or its tag), paging further until it is found. */ + async findIndex(version: string | undefined): Promise { + if (!version) { + return -1; + } + + const normalizedVersion = normalizeVersion(version); + + while (true) { + const index = this.releases.findIndex( + (release) => + normalizeVersion(release.version) === normalizedVersion || + normalizeVersion(release.tagName) === normalizedVersion + ); + + if (index !== -1 || this.loadedAllReleases) { + return index; + } + + await this.ensurePageLoaded(this.loadedReleasePages + 1); + } + } + + private belongsToChannel(release: GitHubReleaseResponse): boolean { + if (release.draft) { + return false; + } + + if (this.channel === 'nightly') { + return appVersionChannel(release.tag_name) === 'nightly'; + } + + return !release.prerelease; + } + + private async loadPage(page: number): Promise { + const url = `${appUpdateReleasesApiUrl(this.channel)}?per_page=${GITHUB_RELEASES_PER_PAGE}&page=${page}`; + const response = await this.fetcher(url, { + headers: { + Accept: 'application/vnd.github+json', + 'User-Agent': this.userAgent, + }, + }); + + if (!response.ok) { + throw new Error( + `GitHub releases request failed: ${response.status} ${response.statusText}` + ); + } + + const payload = await response.json(); + + if (!Array.isArray(payload)) { + throw new Error('GitHub releases response was not an array'); + } + + const releasesPageUrl = appUpdateReleasesPageUrl(this.channel).replace( + /\/latest$/, + '' + ); + + for (const release of payload) { + if (!isGitHubRelease(release) || !this.belongsToChannel(release)) { + continue; + } + + const tagName = release.tag_name; + + if (this.releases.some((cached) => cached.tagName === tagName)) { + continue; + } + + this.releases.push({ + bodyMarkdown: release.body ?? '', + draft: Boolean(release.draft), + htmlUrl: + release.html_url ?? `${releasesPageUrl}/tag/${tagName}`, + prerelease: Boolean(release.prerelease), + publishedAt: release.published_at, + releaseName: release.name ?? tagName, + tagName, + version: normalizeVersion(tagName), + }); + } + + this.loadedReleasePages = page; + this.loadedAllReleases = payload.length < GITHUB_RELEASES_PER_PAGE; + } +} diff --git a/apps/electron-backend/src/app/services/app-update.service.spec.ts b/apps/electron-backend/src/app/services/app-update.service.spec.ts index ae266b8b6..c506ee136 100644 --- a/apps/electron-backend/src/app/services/app-update.service.spec.ts +++ b/apps/electron-backend/src/app/services/app-update.service.spec.ts @@ -1,5 +1,6 @@ import { EventEmitter } from 'events'; import { + AppUpdateChannel, ELECTRON_BRIDGE_APP_UPDATE_STATUSES, ElectronBridgeAppUpdateStatus, } from '@iptvnator/shared/interfaces'; @@ -8,11 +9,66 @@ import { AppUpdateService } from './app-update.service'; class FakeUpdater extends EventEmitter { autoDownload = true; autoInstallOnAppQuit = true; + // electron-updater turns these on by itself for prerelease builds and + // channel assignments; the service must reset them explicitly. + allowPrerelease = true; + allowDowngrade = true; + channel: string | null = null; + setFeedURL = jest.fn(); checkForUpdates = jest.fn().mockResolvedValue(null); downloadUpdate = jest.fn().mockResolvedValue([]); quitAndInstall = jest.fn(); } +const nightlyReleases = [ + { + body: '## Nightly\n\nNewest master build\n\n', + draft: false, + html_url: + 'https://github.com/4gray/iptvnator-nightly/releases/tag/v0.23.1-nightly.20260915.7', + name: 'Nightly 0.23.1-nightly.20260915.7', + prerelease: true, + published_at: '2026-09-15T00:00:00.000Z', + tag_name: 'v0.23.1-nightly.20260915.7', + }, + { + body: 'not a nightly', + draft: false, + html_url: + 'https://github.com/4gray/iptvnator-nightly/releases/tag/v9.9.9', + name: 'stray tag', + prerelease: false, + published_at: '2026-09-14T12:00:00.000Z', + tag_name: 'v9.9.9', + }, + { + body: '## Nightly\n\nOlder master build', + draft: false, + html_url: + 'https://github.com/4gray/iptvnator-nightly/releases/tag/v0.23.1-nightly.20260914.5', + name: 'Nightly 0.23.1-nightly.20260914.5', + prerelease: true, + published_at: '2026-09-14T00:00:00.000Z', + tag_name: 'v0.23.1-nightly.20260914.5', + }, +]; + +/** Answers the stable and nightly repositories with their own lists. */ +function createChannelReleaseFetcher() { + return jest.fn(async (url: string) => { + const releases = url.includes('/iptvnator-nightly/') + ? nightlyReleases + : githubReleases; + + return { + json: jest.fn().mockResolvedValue(releases), + ok: true, + status: 200, + statusText: 'OK', + }; + }); +} + const githubReleases = [ { body: '## New\n\nFresh build', @@ -84,6 +140,8 @@ function createService( env?: NodeJS.ProcessEnv; prepareQuit?: () => void; cancelPreparedQuit?: () => void; + channel?: AppUpdateChannel; + appVersion?: string; } = {} ) { const updater = new FakeUpdater(); @@ -93,6 +151,8 @@ function createService( getVersion: () => '0.22.0', isPackaged: overrides.isPackaged ?? true, }, + appVersion: overrides.appVersion, + channel: overrides.channel, getMainWindow: () => win, cancelPreparedQuit: overrides.cancelPreparedQuit, platform: overrides.platform ?? 'darwin', @@ -115,6 +175,8 @@ describe('AppUpdateService', () => { 'https://github.com/4gray/iptvnator/releases/latest', status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Unsupported, supportedSelfUpdate: false, + channel: 'stable', + installedChannel: 'stable', }); expect(updater.autoDownload).toBe(true); }); @@ -136,6 +198,8 @@ describe('AppUpdateService', () => { 'https://github.com/4gray/iptvnator/releases/latest', status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Unsupported, supportedSelfUpdate: false, + channel: 'stable', + installedChannel: 'stable', }); await service.checkForUpdatesOnStartup(); @@ -160,6 +224,8 @@ describe('AppUpdateService', () => { 'https://github.com/4gray/iptvnator/releases/latest', status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Unsupported, supportedSelfUpdate: false, + channel: 'stable', + installedChannel: 'stable', }); }); @@ -281,6 +347,8 @@ describe('AppUpdateService', () => { }, status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Available, supportedSelfUpdate: false, + channel: 'stable', + installedChannel: 'stable', }); }); @@ -312,6 +380,8 @@ describe('AppUpdateService', () => { latestVersion: '0.24.0', status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Available, supportedSelfUpdate: false, + channel: 'stable', + installedChannel: 'stable', }); }); @@ -534,4 +604,186 @@ describe('AppUpdateService', () => { expect(cancelPreparedQuit).not.toHaveBeenCalled(); }); + it('points electron-updater at the stable repository before every check', async () => { + const { service, updater } = createService(); + + await service.checkForUpdates(); + + expect(updater.setFeedURL).toHaveBeenCalledWith({ + provider: 'github', + owner: '4gray', + repo: 'iptvnator', + }); + expect(updater.allowPrerelease).toBe(false); + expect(updater.channel).toBe('latest'); + expect(updater.allowDowngrade).toBe(false); + expect(updater.setFeedURL.mock.invocationCallOrder[0]).toBeLessThan( + updater.checkForUpdates.mock.invocationCallOrder[0] + ); + }); + + it('follows the nightly repository and its prerelease channel when configured', async () => { + const { service, updater } = createService({ channel: 'nightly' }); + + expect(service.getStatus()).toMatchObject({ + channel: 'nightly', + installedChannel: 'stable', + manualDownloadUrl: + 'https://github.com/4gray/iptvnator-nightly/releases', + }); + + await service.checkForUpdates(); + + expect(updater.setFeedURL).toHaveBeenCalledWith({ + provider: 'github', + owner: '4gray', + repo: 'iptvnator-nightly', + }); + expect(updater.allowPrerelease).toBe(true); + expect(updater.channel).toBe('nightly'); + expect(updater.allowDowngrade).toBe(false); + }); + + it('reports the installed channel from the running version', () => { + const { service } = createService({ + appVersion: '0.23.1-nightly.20260915.7', + }); + + expect(service.getStatus()).toMatchObject({ + currentVersion: '0.23.1-nightly.20260915.7', + channel: 'stable', + installedChannel: 'nightly', + }); + }); + + it('re-checks on an idle updater when the channel changes and forgets the old verdict', async () => { + const { service, updater, win } = createService(); + await service.checkForUpdates(); + service.handleUpdateNotAvailable({ version: '0.22.0' }); + updater.checkForUpdates.mockClear(); + + service.setChannel('stable'); + expect(updater.checkForUpdates).not.toHaveBeenCalled(); + + service.setChannel('nightly'); + await Promise.resolve(); + + expect(updater.checkForUpdates).toHaveBeenCalledTimes(1); + expect(updater.setFeedURL).toHaveBeenLastCalledWith( + expect.objectContaining({ repo: 'iptvnator-nightly' }) + ); + const pushed = ( + win.webContents.send.mock.calls as [ + string, + ElectronBridgeAppUpdateStatus, + ][] + ).map(([, status]) => status); + expect( + pushed.some( + (status) => + status.channel === 'nightly' && + status.latestVersion === undefined && + status.release === undefined + ) + ).toBe(true); + }); + + it('leaves a running download on the old channel but reports the new one', async () => { + const { service, updater } = createService(); + service.handleUpdateAvailable({ version: '0.23.0' }); + updater.downloadUpdate.mockImplementationOnce( + () => new Promise(() => undefined) + ); + void service.downloadUpdate(); + updater.checkForUpdates.mockClear(); + + service.setChannel('nightly'); + await Promise.resolve(); + + expect(updater.checkForUpdates).not.toHaveBeenCalled(); + expect(service.getStatus()).toMatchObject({ + channel: 'nightly', + latestVersion: '0.23.0', + manualDownloadUrl: + 'https://github.com/4gray/iptvnator-nightly/releases', + status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Downloading, + }); + }); + + it('offers newer nightlies through the manual fallback using prerelease-aware ordering', async () => { + const fetcher = createChannelReleaseFetcher(); + const { service, updater } = createService({ + appVersion: '0.23.1-nightly.20260914.5', + channel: 'nightly', + env: {}, + fetcher, + platform: 'linux', + }); + + await service.checkForUpdates(); + + expect(updater.checkForUpdates).not.toHaveBeenCalled(); + expect(fetcher).toHaveBeenCalledWith( + 'https://api.github.com/repos/4gray/iptvnator-nightly/releases?per_page=10&page=1', + expect.any(Object) + ); + // v9.9.9 is not a nightly and must not win the list. + expect(service.getStatus()).toMatchObject({ + latestVersion: '0.23.1-nightly.20260915.7', + status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Available, + installedChannel: 'nightly', + }); + }); + + it('never downgrades a nightly build that follows the stable channel', async () => { + const fetcher = createChannelReleaseFetcher(); + const { service } = createService({ + appVersion: '0.24.1-nightly.20260915.7', + env: {}, + fetcher, + platform: 'linux', + }); + + await service.checkForUpdates(); + + expect(service.getStatus()).toMatchObject({ + latestVersion: '0.24.0', + status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.NotAvailable, + installedChannel: 'nightly', + }); + }); + + it('reads release notes from the repository the requested version belongs to', async () => { + const fetcher = createChannelReleaseFetcher(); + const { service } = createService({ fetcher }); + + const nightlyNotes = await service.getReleaseNotes({ + version: '0.23.1-nightly.20260915.7', + }); + expect(nightlyNotes).toMatchObject({ + tagName: 'v0.23.1-nightly.20260915.7', + hasNext: false, + hasPrevious: true, + }); + + const previous = await service.getReleaseNotes({ + version: nightlyNotes.tagName, + direction: 'previous', + }); + expect(previous.tagName).toBe('v0.23.1-nightly.20260914.5'); + + const stableNotes = await service.getReleaseNotes({ + version: '0.23.0', + }); + expect(stableNotes.tagName).toBe('v0.23.0'); + + const latestOnStableChannel = await service.getReleaseNotes(); + expect(latestOnStableChannel.tagName).toBe('v0.24.0'); + expect( + fetcher.mock.calls.map(([url]) => new URL(url).pathname) + ).toEqual([ + '/repos/4gray/iptvnator-nightly/releases', + '/repos/4gray/iptvnator/releases', + ]); + }); }); diff --git a/apps/electron-backend/src/app/services/app-update.service.ts b/apps/electron-backend/src/app/services/app-update.service.ts index 6fe2d9cd7..c8b61cdf5 100644 --- a/apps/electron-backend/src/app/services/app-update.service.ts +++ b/apps/electron-backend/src/app/services/app-update.service.ts @@ -1,17 +1,28 @@ import { APP_UPDATE_STATUS_CHANGED, + AppUpdateChannel, + appUpdateReleasesPageUrl, + appVersionChannel, + compareAppVersions, + DEFAULT_APP_UPDATE_CHANNEL, ELECTRON_BRIDGE_APP_UPDATE_STATUSES, ElectronBridgeAppUpdateRelease, ElectronBridgeAppUpdateReleaseNotes, ElectronBridgeAppUpdateReleaseNotesRequest, ElectronBridgeAppUpdateStatus, } from '@iptvnator/shared/interfaces'; +import { AppUpdateFeedTarget, applyAppUpdateChannel } from './app-update-feed'; +import { + AppUpdateReleaseCatalog, + CachedGitHubRelease, + normalizeVersion, + ReleaseFetcher, + ReleaseFetchResponse, +} from './app-update-release-catalog'; -export const APP_UPDATE_MANUAL_DOWNLOAD_URL = - 'https://github.com/4gray/iptvnator/releases/latest'; -const GITHUB_RELEASES_API_URL = - 'https://api.github.com/repos/4gray/iptvnator/releases'; -const GITHUB_RELEASES_PER_PAGE = 10; +export const APP_UPDATE_MANUAL_DOWNLOAD_URL = appUpdateReleasesPageUrl( + DEFAULT_APP_UPDATE_CHANNEL +); interface AppUpdateAppAdapter { getVersion(): string; @@ -41,7 +52,7 @@ interface AppUpdateProgressInfo { transferred?: number; } -interface AppUpdaterAdapter { +interface AppUpdaterAdapter extends AppUpdateFeedTarget { autoDownload: boolean; autoInstallOnAppQuit?: boolean; checkForUpdates(): Promise; @@ -69,39 +80,6 @@ interface AppUpdaterAdapter { type AppUpdaterAdapterProvider = AppUpdaterAdapter | (() => AppUpdaterAdapter); -interface GitHubReleaseResponse { - body?: string | null; - draft?: boolean; - html_url?: string; - name?: string | null; - prerelease?: boolean; - published_at?: string | null; - tag_name?: string; -} - -interface CachedGitHubRelease { - bodyMarkdown: string; - draft: boolean; - htmlUrl: string; - prerelease: boolean; - publishedAt?: string | null; - releaseName?: string | null; - tagName: string; - version: string; -} - -interface ReleaseFetchResponse { - json(): Promise; - ok: boolean; - status: number; - statusText: string; -} - -type ReleaseFetcher = ( - url: string, - init?: { headers?: Record } -) => Promise; - export interface AppUpdateServiceOptions { app: AppUpdateAppAdapter; appVersion?: string; @@ -110,6 +88,8 @@ export interface AppUpdateServiceOptions { platform?: NodeJS.Platform; processEnv?: NodeJS.ProcessEnv; releaseFetcher?: ReleaseFetcher; + /** Persisted update channel at startup; `setChannel` follows later saves. */ + channel?: AppUpdateChannel; /** * Runs right before `quitAndInstall()`. Lets the unsaved-settings close * guard stand down for the updater-driven window close, which on macOS @@ -165,37 +145,6 @@ function normalizeError(error: unknown): string { return String(error); } -function normalizeVersion(value: string | null | undefined): string { - return (value ?? '').trim().replace(/^v/i, ''); -} - -function parseVersionParts(value: string): [number, number, number] | null { - const match = normalizeVersion(value).match(/^(\d+)\.(\d+)\.(\d+)/); - - if (!match) { - return null; - } - - return [Number(match[1]), Number(match[2]), Number(match[3])]; -} - -function isVersionGreaterThan(candidate: string, current: string): boolean { - const candidateParts = parseVersionParts(candidate); - const currentParts = parseVersionParts(current); - - if (!candidateParts || !currentParts) { - return false; - } - - for (let index = 0; index < candidateParts.length; index += 1) { - if (candidateParts[index] !== currentParts[index]) { - return candidateParts[index] > currentParts[index]; - } - } - - return false; -} - function resolveCurrentVersion( app: AppUpdateAppAdapter, appVersion: string | undefined @@ -217,8 +166,7 @@ function toReleaseInfo( function toReleaseNotes( release: CachedGitHubRelease, index: number, - releaseCount: number, - loadedAllReleases: boolean + catalog: AppUpdateReleaseCatalog ): ElectronBridgeAppUpdateReleaseNotes { return { version: release.version, @@ -228,28 +176,22 @@ function toReleaseNotes( bodyMarkdown: release.bodyMarkdown, htmlUrl: release.htmlUrl, hasNext: index > 0, - hasPrevious: index < releaseCount - 1 || !loadedAllReleases, + hasPrevious: + index < catalog.releases.length - 1 || !catalog.loadedAllReleases, }; } -function isGitHubRelease(value: unknown): value is GitHubReleaseResponse { - return Boolean( - value && - typeof value === 'object' && - 'tag_name' in value && - typeof (value as GitHubReleaseResponse).tag_name === 'string' - ); -} - export class AppUpdateService { private readonly currentVersion: string; private readonly isPackaged: boolean; private readonly supportedSelfUpdate: boolean; private readonly releaseFetcher: ReleaseFetcher; - private readonly releases: CachedGitHubRelease[] = []; + private readonly catalogs = new Map< + AppUpdateChannel, + AppUpdateReleaseCatalog + >(); private readonly updater: AppUpdaterAdapter | null = null; - private loadedReleasePages = 0; - private loadedAllReleases = false; + private channel: AppUpdateChannel; private checkForUpdatesPromise: Promise | null = null; private status: ElectronBridgeAppUpdateStatus; @@ -262,6 +204,7 @@ export class AppUpdateService { options.appVersion ); this.isPackaged = options.app.isPackaged; + this.channel = options.channel ?? DEFAULT_APP_UPDATE_CHANNEL; this.supportedSelfUpdate = isSelfUpdateSupported( options.app.isPackaged, options.platform ?? process.platform, @@ -269,12 +212,15 @@ export class AppUpdateService { ); this.releaseFetcher = options.releaseFetcher ?? - ((url, init) => fetch(url, init) as Promise); + ((url, init) => + fetch(url, init) as Promise); this.status = { currentVersion: this.currentVersion, - manualDownloadUrl: APP_UPDATE_MANUAL_DOWNLOAD_URL, + manualDownloadUrl: appUpdateReleasesPageUrl(this.channel), status: getInitialStatus(this.supportedSelfUpdate), supportedSelfUpdate: this.supportedSelfUpdate, + channel: this.channel, + installedChannel: appVersionChannel(this.currentVersion), }; if (this.supportedSelfUpdate) { @@ -289,6 +235,33 @@ export class AppUpdateService { return { ...this.status }; } + /** + * Follows a saved channel change. A download already running or + * finished belongs to the previous channel and is left alone — the + * user can still install it — so only an idle updater re-checks. + */ + setChannel(channel: AppUpdateChannel): void { + if (channel === this.channel) { + return; + } + + this.channel = channel; + + const busy = + this.status.status === + ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Downloading || + this.status.status === ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Downloaded; + + if (busy || !this.isPackaged) { + this.setStatus({}); + return; + } + + // The last verdict described the other channel's releases. + this.setStatus({ latestVersion: undefined, release: undefined }); + void this.checkForUpdates(); + } + async checkForUpdates(): Promise { if (this.checkForUpdatesPromise) { return this.checkForUpdatesPromise; @@ -311,11 +284,14 @@ export class AppUpdateService { this.setStatus({ error: undefined, + latestVersion: undefined, + release: undefined, status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Checking, }); try { if (this.updater) { + applyAppUpdateChannel(this.updater, this.channel); await this.updater.checkForUpdates(); } else { await this.checkGitHubReleaseForManualUpdate(); @@ -335,19 +311,28 @@ export class AppUpdateService { return this.checkForUpdates(); } + /** + * Release notes come from the catalog of the channel the requested + * version belongs to, not the configured one: a nightly build on the + * stable channel still reads its own notes, and paging from a nightly + * tag stays inside the nightly list. + */ async getReleaseNotes( request: ElectronBridgeAppUpdateReleaseNotesRequest = {} ): Promise { + const catalog = this.catalogFor( + request.version ? appVersionChannel(request.version) : this.channel + ); const canFallbackToLatest = !request.direction && (!request.version || request.fallbackToLatest); if (canFallbackToLatest) { - await this.ensureFirstStableReleaseLoaded(); + await catalog.ensureFirstReleaseLoaded(); } else { - await this.ensureReleasePageLoaded(1); + await catalog.ensurePageLoaded(1); } - let index = await this.findReleaseIndex(request.version); + let index = await catalog.findIndex(request.version); if (index === -1 && canFallbackToLatest) { index = 0; @@ -361,25 +346,20 @@ export class AppUpdateService { if (request.direction === 'previous') { index += 1; - while (index >= this.releases.length && !this.loadedAllReleases) { - await this.ensureReleasePageLoaded(this.loadedReleasePages + 1); + while (index >= catalog.releases.length && !catalog.loadedAllReleases) { + await catalog.ensurePageLoaded(catalog.loadedReleasePages + 1); } } else if (request.direction === 'next') { index -= 1; } - const release = this.releases[index]; + const release = catalog.releases[index]; if (!release) { throw new Error('No release notes are available in that direction'); } - return toReleaseNotes( - release, - index, - this.releases.length, - this.loadedAllReleases - ); + return toReleaseNotes(release, index, catalog); } async downloadUpdate(): Promise { @@ -489,6 +469,21 @@ export class AppUpdateService { }); } + private catalogFor(channel: AppUpdateChannel): AppUpdateReleaseCatalog { + let catalog = this.catalogs.get(channel); + + if (!catalog) { + catalog = new AppUpdateReleaseCatalog( + channel, + this.releaseFetcher, + `iptvnator/${this.currentVersion}` + ); + this.catalogs.set(channel, catalog); + } + + return catalog; + } + private resolveUpdater( updater: AppUpdaterAdapterProvider ): AppUpdaterAdapter { @@ -517,9 +512,15 @@ export class AppUpdateService { updater.on('error', (error) => this.handleError(error)); } + /** + * Packaged builds without self-update (Linux deb/rpm/snap/…) only learn + * whether a newer version exists on the configured channel and point + * the user at its release page. + */ private async checkGitHubReleaseForManualUpdate(): Promise { - await this.ensureFirstStableReleaseLoaded(); - const latestRelease = this.releases[0]; + const catalog = this.catalogFor(this.channel); + await catalog.ensureFirstReleaseLoaded(); + const latestRelease = catalog.latest; if (!latestRelease) { this.setStatus({ @@ -528,124 +529,28 @@ export class AppUpdateService { return; } - if (isVersionGreaterThan(latestRelease.version, this.currentVersion)) { - this.setStatus({ - error: undefined, - latestVersion: latestRelease.version, - release: toReleaseInfo(latestRelease), - status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Available, - }); - return; - } + const isNewer = + compareAppVersions(latestRelease.version, this.currentVersion) > 0; this.setStatus({ error: undefined, latestVersion: latestRelease.version, release: toReleaseInfo(latestRelease), - status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.NotAvailable, + status: isNewer + ? ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Available + : ELECTRON_BRIDGE_APP_UPDATE_STATUSES.NotAvailable, }); } - private async ensureFirstStableReleaseLoaded(): Promise { - while (!this.loadedAllReleases && this.releases.length === 0) { - await this.ensureReleasePageLoaded(this.loadedReleasePages + 1); - } - } - - private async findReleaseIndex( - version: string | undefined - ): Promise { - if (!version) { - return -1; - } - - const normalizedVersion = normalizeVersion(version); - - while (true) { - const index = this.releases.findIndex( - (release) => - normalizeVersion(release.version) === normalizedVersion || - normalizeVersion(release.tagName) === normalizedVersion - ); - - if (index !== -1 || this.loadedAllReleases) { - return index; - } - - await this.ensureReleasePageLoaded(this.loadedReleasePages + 1); - } - } - - private async ensureReleasePageLoaded(page: number): Promise { - while (!this.loadedAllReleases && this.loadedReleasePages < page) { - await this.loadReleasePage(this.loadedReleasePages + 1); - } - } - - private async loadReleasePage(page: number): Promise { - const url = `${GITHUB_RELEASES_API_URL}?per_page=${GITHUB_RELEASES_PER_PAGE}&page=${page}`; - const response = await this.releaseFetcher(url, { - headers: { - Accept: 'application/vnd.github+json', - 'User-Agent': `iptvnator/${this.currentVersion}`, - }, - }); - - if (!response.ok) { - throw new Error( - `GitHub releases request failed: ${response.status} ${response.statusText}` - ); - } - - const payload = await response.json(); - - if (!Array.isArray(payload)) { - throw new Error('GitHub releases response was not an array'); - } - - for (const release of payload) { - if (!isGitHubRelease(release)) { - continue; - } - - if (release.draft || release.prerelease) { - continue; - } - - const tagName = release.tag_name; - const version = normalizeVersion(tagName); - - if ( - this.releases.some( - (cachedRelease) => cachedRelease.tagName === tagName - ) - ) { - continue; - } - - this.releases.push({ - bodyMarkdown: release.body ?? '', - draft: Boolean(release.draft), - htmlUrl: - release.html_url ?? - `${APP_UPDATE_MANUAL_DOWNLOAD_URL.replace('/latest', '')}/tag/${tagName}`, - prerelease: Boolean(release.prerelease), - publishedAt: release.published_at, - releaseName: release.name ?? tagName, - tagName, - version, - }); - } - - this.loadedReleasePages = page; - this.loadedAllReleases = payload.length < GITHUB_RELEASES_PER_PAGE; - } - private setStatus( update: Partial< Omit< ElectronBridgeAppUpdateStatus, - 'currentVersion' | 'manualDownloadUrl' | 'supportedSelfUpdate' + | 'currentVersion' + | 'manualDownloadUrl' + | 'supportedSelfUpdate' + | 'channel' + | 'installedChannel' > > ): void { @@ -653,8 +558,10 @@ export class AppUpdateService { ...this.status, ...update, currentVersion: this.currentVersion, - manualDownloadUrl: APP_UPDATE_MANUAL_DOWNLOAD_URL, + manualDownloadUrl: appUpdateReleasesPageUrl(this.channel), supportedSelfUpdate: this.supportedSelfUpdate, + channel: this.channel, + installedChannel: this.status.installedChannel, }; this.emitStatus(); } diff --git a/apps/electron-backend/src/app/services/store.service.ts b/apps/electron-backend/src/app/services/store.service.ts index 9bdb10d30..0362b3637 100644 --- a/apps/electron-backend/src/app/services/store.service.ts +++ b/apps/electron-backend/src/app/services/store.service.ts @@ -1,6 +1,9 @@ import { Conf } from 'electron-conf/main'; import { getElectronConfigDirectory } from '@iptvnator/shared/database'; -import type { StartupWindowMode } from '@iptvnator/shared/interfaces'; +import type { + AppUpdateChannel, + StartupWindowMode, +} from '@iptvnator/shared/interfaces'; export const WINDOW_BOUNDS = 'WINDOW_BOUNDS'; export const MPV_PLAYER_PATH = 'MPV_PLAYER_PATH'; @@ -28,6 +31,14 @@ export const STARTUP_WINDOW_MODE = 'STARTUP_WINDOW_MODE'; /** Desktop portal request cooldown; absent means enabled. */ export const PORTAL_CONNECTIVITY_GUARD = 'PORTAL_CONNECTIVITY_GUARD'; +/** + * Update channel (`stable` / `nightly`). Mirrored here from the renderer's + * settings by the SETTINGS_UPDATE handler because the startup update check + * runs before the renderer's IndexedDB settings are reachable; absent means + * `stable`. Read and written only through `app-update-channel.ts`. + */ +export const APP_UPDATE_CHANNEL = 'APP_UPDATE_CHANNEL'; + /** * Local XMLTV files the EPG importer may read: every path the native picker * returned plus every hand-typed path the user allowed in the main-process @@ -64,6 +75,7 @@ export type StoreType = { [EMBEDDED_MPV_AUTO_RECONNECT]: boolean; [STARTUP_WINDOW_MODE]: StartupWindowMode; [PORTAL_CONNECTIVITY_GUARD]: boolean; + [APP_UPDATE_CHANNEL]: AppUpdateChannel; [TRUSTED_LOCAL_EPG_SOURCES]: string[]; }; diff --git a/apps/electron-backend/src/main.ts b/apps/electron-backend/src/main.ts index 69827f094..f3a98459d 100644 --- a/apps/electron-backend/src/main.ts +++ b/apps/electron-backend/src/main.ts @@ -33,6 +33,10 @@ import StalkerEvents from './app/events/stalker.events'; import { isStartupTraceEnabled, trace } from './app/services/debug-trace'; import { registerStaticHeaderShims } from './app/services/request-header-overrides.service'; import { AppUpdateService } from './app/services/app-update.service'; +import { + onAppUpdateChannelChange, + readStoredAppUpdateChannel, +} from './app/services/app-update-channel'; import { databaseWorkerClient } from './app/services/database-worker-client'; import WindowEvents from './app/events/window.events'; import { bootstrapWindowCloseGuard } from './app/services/window-close-guard.service'; @@ -141,6 +145,7 @@ export default class Main { const appUpdateService = new AppUpdateService({ app, appVersion: environment.version, + channel: readStoredAppUpdateChannel(), getMainWindow: () => App.mainWindow, updater: () => autoUpdater, // quitAndInstall() closes the windows before 'before-quit' fires @@ -150,6 +155,9 @@ export default class Main { cancelPreparedQuit: () => windowCloseGuard.revokeAllowedClose(), }); AppUpdateEvents.bootstrapAppUpdateEvents(appUpdateService); + onAppUpdateChannelChange((channel) => + appUpdateService.setChannel(channel) + ); registerStaticHeaderShims(); ElectronEvents.bootstrapElectronEvents(); diff --git a/apps/web/src/app/app-update-notification-panel.component.spec.ts b/apps/web/src/app/app-update-notification-panel.component.spec.ts index 110f07e51..f567a2573 100644 --- a/apps/web/src/app/app-update-notification-panel.component.spec.ts +++ b/apps/web/src/app/app-update-notification-panel.component.spec.ts @@ -14,6 +14,8 @@ const availableStatus: ElectronBridgeAppUpdateStatus = { manualDownloadUrl: 'https://github.com/4gray/iptvnator/releases/latest', status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Available, supportedSelfUpdate: true, + channel: 'stable', + installedChannel: 'stable', }; describe('AppUpdateNotificationPanelComponent', () => { diff --git a/apps/web/src/app/services/app-update-install.service.spec.ts b/apps/web/src/app/services/app-update-install.service.spec.ts index dd2639428..658efc39c 100644 --- a/apps/web/src/app/services/app-update-install.service.spec.ts +++ b/apps/web/src/app/services/app-update-install.service.spec.ts @@ -12,6 +12,9 @@ const BASE_STATUS: ElectronBridgeAppUpdateStatus = { currentVersion: '0.23.0', status: ELECTRON_BRIDGE_APP_UPDATE_STATUSES.Idle, supportedSelfUpdate: true, + manualDownloadUrl: 'https://github.com/4gray/iptvnator/releases/latest', + channel: 'stable', + installedChannel: 'stable', }; /** diff --git a/apps/web/src/app/settings/settings-about-section.component.html b/apps/web/src/app/settings/settings-about-section.component.html index 34881ab6d..79210c5f1 100644 --- a/apps/web/src/app/settings/settings-about-section.component.html +++ b/apps/web/src/app/settings/settings-about-section.component.html @@ -63,6 +63,65 @@ > }
+ @if (canSelectUpdateChannel()) { +
+ + {{ + 'SETTINGS.APP_UPDATE_CHANNEL' | translate + }} + + @for ( + option of updateChannelOptions(); + track option.value + ) { + + {{ option.labelKey | translate }} + + } + + + + {{ + 'SETTINGS.APP_UPDATE_CHANNEL_DESCRIPTION' + | translate + }} + + @if (form()!.get('updateChannel')?.value === 'nightly') { + + {{ + 'SETTINGS.APP_UPDATE_CHANNEL_NIGHTLY_WARNING' + | translate + }} + + } + @if (isNightlyBuildOnStableChannel()) { + + {{ + 'SETTINGS.APP_UPDATE_NIGHTLY_ON_STABLE_HINT' + | translate + }} + + } +
+ }
+ (pointercancel)="onHotZoneLeave()" + > + + @if (!state.open()) { +
+ } +