diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index cd8b3127..d61deb0c 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -49,6 +49,10 @@ jobs: run: pnpm install --frozen-lockfile --ignore-scripts working-directory: insights-webapp/src/main/frontend + - name: Typecheck Frontend + run: pnpm typecheck + working-directory: insights-webapp/src/main/frontend + - name: Lint Frontend run: pnpm lint working-directory: insights-webapp/src/main/frontend diff --git a/insights-webapp/src/main/frontend/angular.json b/insights-webapp/src/main/frontend/angular.json index c2e1bdbf..9c386553 100644 --- a/insights-webapp/src/main/frontend/angular.json +++ b/insights-webapp/src/main/frontend/angular.json @@ -23,7 +23,7 @@ }, "index": "src/index.html", "browser": "src/main.ts", - "polyfills": ["zone.js"], + "polyfills": [], "tsConfig": "tsconfig.app.json", "inlineStyleLanguage": "scss", "assets": [ @@ -78,7 +78,7 @@ "test": { "builder": "@angular/build:karma", "options": { - "polyfills": ["zone.js", "zone.js/testing"], + "polyfills": [], "tsConfig": "tsconfig.spec.json", "inlineStyleLanguage": "scss", "assets": ["src/assets"], diff --git a/insights-webapp/src/main/frontend/cypress/e2e/release-graph.cy.ts b/insights-webapp/src/main/frontend/cypress/e2e/release-graph.cy.ts index 19069c54..03dcb561 100644 --- a/insights-webapp/src/main/frontend/cypress/e2e/release-graph.cy.ts +++ b/insights-webapp/src/main/frontend/cypress/e2e/release-graph.cy.ts @@ -222,7 +222,7 @@ describe('Graph Rendering and Interaction', () => { cy.get('@graphSvg').then(($svg) => { const nightlyNodes = $svg.find('a[data-cy*="-nightly"]'); if (nightlyNodes.length > 0) { - nightlyNodes.each((idx, node) => { + nightlyNodes.each((idx: number, node: HTMLElement) => { const transform = Cypress.$(node).attr('transform'); const yPos = transform?.match(/translate\([^,]+,([^)]+)\)/)?.[1]; const isOnMasterBranch = yPos === '0'; diff --git a/insights-webapp/src/main/frontend/package.json b/insights-webapp/src/main/frontend/package.json index 2dd7cc19..ff6d0121 100644 --- a/insights-webapp/src/main/frontend/package.json +++ b/insights-webapp/src/main/frontend/package.json @@ -9,6 +9,7 @@ "test": "ng test", "lint": "ng lint", "lint:fix": "ng lint --fix", + "typecheck": "ngc -p tsconfig.app.json --noEmit && ngc -p tsconfig.spec.json --noEmit && tsc -p cypress/tsconfig.json --noEmit", "e2e": "ng e2e", "cypress:open": "cypress open --project cypress", "cypress:run": "cypress run --project cypress" @@ -30,8 +31,7 @@ "ng2-charts": "^10.0.0", "ngx-markdown": "^22.0.0", "rxjs": "~7.8.2", - "tslib": "^2.8.1", - "zone.js": "~0.16.2" + "tslib": "^2.8.1" }, "devDependencies": { "@angular-eslint/builder": "^22.1.0", @@ -57,12 +57,12 @@ "eslint-plugin-sonarjs": "^4.2.0", "eslint-plugin-unicorn": "^64.0.0", "istanbul-lib-instrument": "^6.0.3", - "jasmine-core": "~6.3.0", + "jasmine-core": "~7.0.1", "karma": "~6.4.4", "karma-chrome-launcher": "~3.2.0", "karma-coverage": "~2.2.1", "karma-jasmine": "~5.1.0", - "karma-jasmine-html-reporter": "~2.2.0", + "karma-jasmine-html-reporter": "~2.3.0", "prettier": "^3.9.6", "typescript": "~6.0.3", "typescript-eslint": "^8.66.0" diff --git a/insights-webapp/src/main/frontend/src/app/app.config.ts b/insights-webapp/src/main/frontend/src/app/app.config.ts index 6b31e4b1..e2b60948 100644 --- a/insights-webapp/src/main/frontend/src/app/app.config.ts +++ b/insights-webapp/src/main/frontend/src/app/app.config.ts @@ -1,4 +1,4 @@ -import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; +import { ApplicationConfig, provideZonelessChangeDetection } from '@angular/core'; import { provideRouter, UrlSerializer, withComponentInputBinding } from '@angular/router'; import { routes } from './app.routes'; import { ReadableUrlSerializer } from './services/readable-url.serializer'; @@ -13,7 +13,7 @@ import { HttpInterceptorService } from './services/http-interceptor.service'; export const appConfig: ApplicationConfig = { providers: [ - provideZoneChangeDetection({ eventCoalescing: true }), + provideZonelessChangeDetection(), provideRouter(routes, withComponentInputBinding()), { provide: UrlSerializer, useClass: ReadableUrlSerializer }, provideHttpClient( diff --git a/insights-webapp/src/main/frontend/src/app/components/tooltip/tooltip.component.spec.ts b/insights-webapp/src/main/frontend/src/app/components/tooltip/tooltip.component.spec.ts index f62c1fc2..df5ce4cb 100644 --- a/insights-webapp/src/main/frontend/src/app/components/tooltip/tooltip.component.spec.ts +++ b/insights-webapp/src/main/frontend/src/app/components/tooltip/tooltip.component.spec.ts @@ -1,5 +1,5 @@ import { WritableSignal, signal } from '@angular/core'; -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { TooltipComponent } from './tooltip.component'; @@ -83,7 +83,7 @@ describe('TooltipComponent', () => { expect(tooltipElement).toBeNull(); }); - it('should display the tooltip with correct data when service emits state', fakeAsync(() => { + it('should display the tooltip with correct data when service emits state', () => { const tooltipData: TooltipData = { title: 'Tooltip Test Issue', details: [ @@ -95,7 +95,6 @@ describe('TooltipComponent', () => { }; tooltipState.set(tooltipData); fixture.detectChanges(); - tick(); const tooltipElement = fixture.debugElement.query(By.css('.tooltip')); @@ -108,9 +107,9 @@ describe('TooltipComponent', () => { expect(detailsElement.length).toBe(2); expect(detailsElement[0].nativeElement.textContent).toContain('Priority: High'); expect(detailsElement[1].nativeElement.textContent).toContain('Points: 8'); - })); + }); - it('should render a detail without a label as plain text', fakeAsync(() => { + it('should render a detail without a label as plain text', () => { const tooltipData: TooltipData = { title: 'Attack Vector', details: [{ value: 'How the vulnerability is exploited' }], @@ -119,18 +118,16 @@ describe('TooltipComponent', () => { }; tooltipState.set(tooltipData); fixture.detectChanges(); - tick(); const detailElement = fixture.debugElement.query(By.css('.tooltip-detail')); expect(detailElement.nativeElement.textContent.trim()).toBe('How the vulnerability is exploited'); - })); + }); - it('should hide the tooltip when service emits null after showing', fakeAsync(() => { + it('should hide the tooltip when service emits null after showing', () => { const tooltipData: TooltipData = { title: 'Tooltip Test Issue', details: [], top: '100px', left: '200px' }; tooltipState.set(tooltipData); fixture.detectChanges(); - tick(); let tooltipElement = fixture.debugElement.query(By.css('.tooltip')); @@ -138,10 +135,9 @@ describe('TooltipComponent', () => { tooltipState.set(null); fixture.detectChanges(); - tick(); tooltipElement = fixture.debugElement.query(By.css('.tooltip')); expect(tooltipElement).toBeNull(); - })); + }); }); diff --git a/insights-webapp/src/main/frontend/src/app/pages/cve-overview/cve-overview.component.spec.ts b/insights-webapp/src/main/frontend/src/app/pages/cve-overview/cve-overview.component.spec.ts index 8f74971c..2aa33efc 100644 --- a/insights-webapp/src/main/frontend/src/app/pages/cve-overview/cve-overview.component.spec.ts +++ b/insights-webapp/src/main/frontend/src/app/pages/cve-overview/cve-overview.component.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { provideHttpClient, withXhr } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; import { provideRouter } from '@angular/router'; @@ -552,26 +552,38 @@ describe('CveOverviewComponent', () => { }); describe('onSearch()', () => { - it('calls the search subject (debounced)', fakeAsync(() => { + const DEBOUNCE_TICK_MS = 400; + + beforeEach(() => { + TestBed.tick(); + jasmine.clock().install(); + jasmine.clock().mockDate(); + }); + + afterEach(() => { + jasmine.clock().uninstall(); + }); + + it('calls the search subject (debounced)', () => { component.searchQuery.set('CVE-2024'); - fixture.detectChanges(); - tick(400); + TestBed.tick(); + jasmine.clock().tick(DEBOUNCE_TICK_MS); expect(vulnerabilityService.getAllVulnerabilitiesPaginated).toHaveBeenCalledWith(0, 20, 'CVE-2024'); - })); + }); - it('deduplicated rapid searches only trigger once', fakeAsync(() => { + it('deduplicated rapid searches only trigger once', () => { component.searchQuery.set('CVE'); - fixture.detectChanges(); + TestBed.tick(); component.searchQuery.set('CVE'); - fixture.detectChanges(); - tick(400); + TestBed.tick(); + jasmine.clock().tick(DEBOUNCE_TICK_MS); const uniqueCveSearches = vulnerabilityService.getAllVulnerabilitiesPaginated.calls .all() .filter((c) => c.args[2] === 'CVE'); expect(uniqueCveSearches.length).toBe(1); - })); + }); }); describe('LTS branch detection', () => { diff --git a/insights-webapp/src/main/frontend/src/app/pages/release-details/release-details.component.spec.ts b/insights-webapp/src/main/frontend/src/app/pages/release-details/release-details.component.spec.ts index 05dc101d..e1697052 100644 --- a/insights-webapp/src/main/frontend/src/app/pages/release-details/release-details.component.spec.ts +++ b/insights-webapp/src/main/frontend/src/app/pages/release-details/release-details.component.spec.ts @@ -1,9 +1,8 @@ import { WritableSignal, signal } from '@angular/core'; -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute, DefaultUrlSerializer, Router, UrlTree } from '@angular/router'; import { Location } from '@angular/common'; import { of, throwError, Subject } from 'rxjs'; -import { delay } from 'rxjs/operators'; import { provideHttpClient, withXhr } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; import { ReleaseDetailsComponent } from './release-details.component'; @@ -120,7 +119,7 @@ describe('ReleaseDetailsComponent', () => { mockRouter.serializeUrl.and.callFake((tree: UrlTree) => urlSerializer.serialize(tree)); graphQueryParametersState = signal>({}); mockGraphStateService = { graphQueryParams: graphQueryParametersState.asReadonly() }; - mockReleaseService.getAllReleases.and.returnValue(of([mockRelease]).pipe(delay(0))); + mockReleaseService.getAllReleases.and.returnValue(of([mockRelease])); parameterMapSubject = new Subject(); @@ -149,32 +148,31 @@ describe('ReleaseDetailsComponent', () => { component = fixture.componentInstance; }); - function setupAndLoad(businessValues: BusinessValue[]): Promise { - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); - mockReleaseService.getAllReleases.and.returnValue(of([mockRelease]).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels).pipe(delay(0))); - mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues).pipe(delay(0))); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(businessValues).pipe(delay(0))); - return Promise.resolve(); + function setupAndLoad(businessValues: BusinessValue[]): void { + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease)); + mockReleaseService.getAllReleases.and.returnValue(of([mockRelease])); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels)); + mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues)); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities)); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(businessValues)); } function setupNightlyLoad(allReleases: Release[], currentRelease: Release = mockRelease): void { - mockReleaseService.getReleaseById.and.returnValue(of(currentRelease).pipe(delay(0))); - mockReleaseService.getAllReleases.and.returnValue(of(allReleases).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of([]).pipe(delay(0))); - mockIssueService.getIssuesByReleaseId.and.returnValue(of([]).pipe(delay(0))); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of([]).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of([]).pipe(delay(0))); + mockReleaseService.getReleaseById.and.returnValue(of(currentRelease)); + mockReleaseService.getAllReleases.and.returnValue(of(allReleases)); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of([])); + mockIssueService.getIssuesByReleaseId.and.returnValue(of([])); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of([])); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of([])); } function setupNavLoad(allReleases: Release[]): void { - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); - mockReleaseService.getAllReleases.and.returnValue(of(allReleases).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of([]).pipe(delay(0))); - mockIssueService.getIssuesByReleaseId.and.returnValue(of([]).pipe(delay(0))); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of([]).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of([]).pipe(delay(0))); + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease)); + mockReleaseService.getAllReleases.and.returnValue(of(allReleases)); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of([])); + mockIssueService.getIssuesByReleaseId.and.returnValue(of([])); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of([])); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of([])); } it('should create', () => { @@ -182,33 +180,35 @@ describe('ReleaseDetailsComponent', () => { }); describe('ngOnInit - Data Fetching', () => { - it('should set isLoading to true initially, and then to false after data is fetched', fakeAsync(() => { - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels).pipe(delay(0))); - mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues).pipe(delay(0))); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues).pipe(delay(0))); + it('should set isLoading to true initially, and then to false after data is fetched', () => { + // Held open so the loading state is observable before the release resolves. + const releaseSubject = new Subject(); + mockReleaseService.getReleaseById.and.returnValue(releaseSubject); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels)); + mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues)); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities)); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues)); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); expect(component.isLoading()).toBe(true); - tick(); + releaseSubject.next(mockRelease); + releaseSubject.complete(); expect(component.isLoading()).toBe(false); - })); + }); - it('should fetch release data when a valid release ID is provided', fakeAsync(() => { - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels).pipe(delay(0))); - mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues).pipe(delay(0))); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues).pipe(delay(0))); + it('should fetch release data when a valid release ID is provided', () => { + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease)); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels)); + mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues)); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities)); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues)); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(mockReleaseService.getReleaseById).toHaveBeenCalledWith('release-1'); expect(mockLabelService.getHighLightsByReleaseId).toHaveBeenCalledWith('release-1'); @@ -220,119 +220,112 @@ describe('ReleaseDetailsComponent', () => { expect(component.releaseIssues()).toEqual(mockIssues); expect(component.vulnerabilities()).toEqual(mockVulnerabilities); expect(component.businessValues()).toEqual(mockBusinessValues); - })); + }); - it('should handle release fetch error gracefully', fakeAsync(() => { + it('should handle release fetch error gracefully', () => { mockReleaseService.getReleaseById.and.returnValue( - throwError(() => new Error('Release API Error')).pipe(delay(0)), + throwError(() => new Error('Release API Error')), ); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.isLoading()).toBe(false); expect(component.release()).toBeNull(); - })); + }); - it('should handle label fetch error gracefully', fakeAsync(() => { - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); + it('should handle label fetch error gracefully', () => { + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease)); mockLabelService.getHighLightsByReleaseId.and.returnValue( - throwError(() => new Error('Label API Error')).pipe(delay(0)), + throwError(() => new Error('Label API Error')), ); - mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues).pipe(delay(0))); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues).pipe(delay(0))); + mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues)); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities)); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues)); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.isLoading()).toBe(false); expect(component.highlightedLabels()).toBeNull(); expect(component.releaseIssues()).toEqual(mockIssues); expect(component.vulnerabilities()).toEqual(mockVulnerabilities); - })); + }); - it('should handle issue fetch error gracefully', fakeAsync(() => { - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels).pipe(delay(0))); + it('should handle issue fetch error gracefully', () => { + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease)); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels)); mockIssueService.getIssuesByReleaseId.and.returnValue( - throwError(() => new Error('Issue API Error')).pipe(delay(0)), + throwError(() => new Error('Issue API Error')), ); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues).pipe(delay(0))); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities)); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues)); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.isLoading()).toBe(false); expect(component.releaseIssues()).toBeNull(); expect(component.highlightedLabels()).toEqual(mockLabels); expect(component.vulnerabilities()).toEqual(mockVulnerabilities); - })); + }); - it('should set data to null if API returns an empty array for labels', fakeAsync(() => { - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of([]).pipe(delay(0))); - mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues).pipe(delay(0))); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues).pipe(delay(0))); + it('should set data to null if API returns an empty array for labels', () => { + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease)); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of([])); + mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues)); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities)); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues)); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.highlightedLabels()).toBeNull(); - })); + }); - it('should set data to undefined if API returns an empty array for issues', fakeAsync(() => { - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels).pipe(delay(0))); - mockIssueService.getIssuesByReleaseId.and.returnValue(of([]).pipe(delay(0))); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues).pipe(delay(0))); + it('should set data to undefined if API returns an empty array for issues', () => { + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease)); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels)); + mockIssueService.getIssuesByReleaseId.and.returnValue(of([])); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities)); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues)); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.releaseIssues()).toBeNull(); - })); + }); - it('should handle vulnerability fetch error gracefully', fakeAsync(() => { - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels).pipe(delay(0))); - mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues).pipe(delay(0))); + it('should handle vulnerability fetch error gracefully', () => { + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease)); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels)); + mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues)); mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue( - throwError(() => new Error('Vulnerability API Error')).pipe(delay(0)), + throwError(() => new Error('Vulnerability API Error')), ); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues).pipe(delay(0))); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues)); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.isLoading()).toBe(false); expect(component.vulnerabilities()).toEqual([]); - })); + }); - it('should set vulnerabilities to empty array if API returns empty array', fakeAsync(() => { - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels).pipe(delay(0))); - mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues).pipe(delay(0))); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of([]).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues).pipe(delay(0))); + it('should set vulnerabilities to empty array if API returns empty array', () => { + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease)); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels)); + mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues)); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of([])); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues)); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.vulnerabilities()).toEqual([]); - })); + }); - it('should refetch data when route parameter changes', fakeAsync(() => { + it('should refetch data when route parameter changes', () => { const mockRelease2: Release = { id: 'release-2', name: 'v2.0.0', @@ -342,58 +335,54 @@ describe('ReleaseDetailsComponent', () => { branch: { id: 'b2', name: 'master' }, }; - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels).pipe(delay(0))); - mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues).pipe(delay(0))); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues).pipe(delay(0))); + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease)); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels)); + mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues)); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities)); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues)); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.release()).toEqual(mockRelease); // Change route parameter - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease2).pipe(delay(0))); + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease2)); parameterMapSubject.next({ get: () => 'release-2' }); - tick(); expect(mockReleaseService.getReleaseById).toHaveBeenCalledWith('release-2'); expect(component.release()).toEqual(mockRelease2); - })); + }); }); describe('activeView behavior', () => { - it('should set activeView to business-value when businessValues are loaded', fakeAsync(() => { - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels).pipe(delay(0))); - mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues).pipe(delay(0))); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues).pipe(delay(0))); + it('should set activeView to business-value when businessValues are loaded', () => { + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease)); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels)); + mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues)); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities)); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of(mockBusinessValues)); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.businessValues()).toEqual(mockBusinessValues); expect(component.activeView()).toBe('business-value'); - })); + }); - it('should set activeView to issues when businessValues is empty', fakeAsync(() => { - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels).pipe(delay(0))); - mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues).pipe(delay(0))); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of([]).pipe(delay(0))); + it('should set activeView to issues when businessValues is empty', () => { + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease)); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of(mockLabels)); + mockIssueService.getIssuesByReleaseId.and.returnValue(of(mockIssues)); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of(mockVulnerabilities)); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of([])); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.businessValues()).toBeNull(); expect(component.activeView()).toBe('issues'); - })); + }); it('should switch view when setActiveView is called', () => { component.activeView.set('business-value'); @@ -461,35 +450,32 @@ describe('ReleaseDetailsComponent', () => { }); describe('view toggle template', () => { - it('should show view toggle when businessValues are present', fakeAsync(() => { + it('should show view toggle when businessValues are present', () => { setupAndLoad(mockBusinessValues); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); fixture.detectChanges(); const toggle = fixture.nativeElement.querySelector('.section-view-toggle'); expect(toggle).toBeTruthy(); - })); + }); - it('should not show view toggle when there are no businessValues', fakeAsync(() => { + it('should not show view toggle when there are no businessValues', () => { setupAndLoad([]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); fixture.detectChanges(); const toggle = fixture.nativeElement.querySelector('.section-view-toggle'); expect(toggle).toBeNull(); - })); + }); - it('should render two tab buttons with correct labels when businessValues are present', fakeAsync(() => { + it('should render two tab buttons with correct labels when businessValues are present', () => { setupAndLoad(mockBusinessValues); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); fixture.detectChanges(); const tabs = fixture.nativeElement.querySelectorAll('.view-tab'); @@ -497,39 +483,36 @@ describe('ReleaseDetailsComponent', () => { expect(tabs.length).toBe(2); expect(tabs[0].textContent.trim()).toBe('Business Values'); expect(tabs[1].textContent.trim()).toBe('Important Issues'); - })); + }); - it('should have correct tooltip titles on toggle buttons', fakeAsync(() => { + it('should have correct tooltip titles on toggle buttons', () => { setupAndLoad(mockBusinessValues); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); fixture.detectChanges(); const tabs = fixture.nativeElement.querySelectorAll('.view-tab'); expect(tabs[0].title).toBe('Grouped issues that contribute to one functional item.'); expect(tabs[1].title).toBe('Subsets of issues that might be relevant to you.'); - })); + }); - it('should mark Business Values tab as active by default when businessValues are present', fakeAsync(() => { + it('should mark Business Values tab as active by default when businessValues are present', () => { setupAndLoad(mockBusinessValues); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); fixture.detectChanges(); const tabs = fixture.nativeElement.querySelectorAll('.view-tab'); expect(tabs[0].classList).toContain('active'); expect(tabs[1].classList).not.toContain('active'); - })); + }); - it('should switch active tab when Important Issues is clicked', fakeAsync(() => { + it('should switch active tab when Important Issues is clicked', () => { setupAndLoad(mockBusinessValues); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); fixture.detectChanges(); const tabs = fixture.nativeElement.querySelectorAll('.view-tab'); @@ -539,104 +522,95 @@ describe('ReleaseDetailsComponent', () => { expect(component.activeView()).toBe('issues'); expect(tabs[1].classList).toContain('active'); expect(tabs[0].classList).not.toContain('active'); - })); + }); - it('should show issues section when there are no businessValues', fakeAsync(() => { + it('should show issues section when there are no businessValues', () => { setupAndLoad([]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); fixture.detectChanges(); expect(component.activeView()).toBe('issues'); const issuesSection = fixture.nativeElement.querySelector('app-release-important-issues'); expect(issuesSection).toBeTruthy(); - })); + }); }); describe('branch navigation', () => { - it('should set previousRelease and nextRelease when current is in the middle of branch', fakeAsync(() => { + it('should set previousRelease and nextRelease when current is in the middle of branch', () => { setupNavLoad([mockReleasePrevious, mockRelease, mockReleaseNext]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.previousRelease()).toEqual(mockReleasePrevious); expect(component.nextRelease()).toEqual(mockReleaseNext); - })); + }); - it('should set previousRelease to null when current is the first in branch', fakeAsync(() => { + it('should set previousRelease to null when current is the first in branch', () => { setupNavLoad([mockRelease, mockReleaseNext]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.previousRelease()).toBeNull(); expect(component.nextRelease()).toEqual(mockReleaseNext); - })); + }); - it('should set nextRelease to null when current is the last in branch', fakeAsync(() => { + it('should set nextRelease to null when current is the last in branch', () => { setupNavLoad([mockReleasePrevious, mockRelease]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.previousRelease()).toEqual(mockReleasePrevious); expect(component.nextRelease()).toBeNull(); - })); + }); - it('should set branchReleases sorted by publishedAt ascending', fakeAsync(() => { + it('should set branchReleases sorted by publishedAt ascending', () => { setupNavLoad([mockReleaseNext, mockRelease, mockReleasePrevious]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); const ids = component.branchReleases().map((r) => r.id); expect(ids).toEqual([mockReleasePrevious.id, mockRelease.id, mockReleaseNext.id]); - })); + }); - it('should not include releases from a different branch', fakeAsync(() => { + it('should not include releases from a different branch', () => { setupNavLoad([mockReleasePrevious, mockRelease, mockReleaseNext, mockReleaseOtherBranch]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); const ids = component.branchReleases().map((r) => r.id); expect(ids).not.toContain(mockReleaseOtherBranch.id); expect(ids.length).toBe(3); - })); + }); - it('should set branchReleases to single item and both nav signals null when only one release in branch', fakeAsync(() => { + it('should set branchReleases to single item and both nav signals null when only one release in branch', () => { setupNavLoad([mockRelease]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.branchReleases().length).toBe(1); expect(component.previousRelease()).toBeNull(); expect(component.nextRelease()).toBeNull(); - })); + }); - it('should not render version-nav buttons when only one release in branch', fakeAsync(() => { + it('should not render version-nav buttons when only one release in branch', () => { setupNavLoad([mockRelease]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); fixture.detectChanges(); const buttons = fixture.nativeElement.querySelectorAll('.version-nav-btn'); expect(buttons.length).toBe(0); - })); + }); - it('should render two version-nav buttons when current is in the middle', fakeAsync(() => { + it('should render two version-nav buttons when current is in the middle', () => { setupNavLoad([mockReleasePrevious, mockRelease, mockReleaseNext]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); fixture.detectChanges(); const buttons = fixture.nativeElement.querySelectorAll('.version-nav-btn'); @@ -644,87 +618,81 @@ describe('ReleaseDetailsComponent', () => { expect(buttons.length).toBe(2); expect(buttons[0].textContent.trim()).toBe(mockReleasePrevious.name); expect(buttons[1].textContent.trim()).toBe(mockReleaseNext.name); - })); + }); - it('should render only next button when current is the first in branch', fakeAsync(() => { + it('should render only next button when current is the first in branch', () => { setupNavLoad([mockRelease, mockReleaseNext]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); fixture.detectChanges(); const buttons = fixture.nativeElement.querySelectorAll('.version-nav-btn'); expect(buttons.length).toBe(1); expect(buttons[0].textContent.trim()).toBe(mockReleaseNext.name); - })); + }); - it('should render only prev button when current is the last in branch', fakeAsync(() => { + it('should render only prev button when current is the last in branch', () => { setupNavLoad([mockReleasePrevious, mockRelease]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); fixture.detectChanges(); const buttons = fixture.nativeElement.querySelectorAll('.version-nav-btn'); expect(buttons.length).toBe(1); expect(buttons[0].textContent.trim()).toBe(mockReleasePrevious.name); - })); + }); - it('should gracefully handle getAllReleases error and leave navigation empty', fakeAsync(() => { - mockReleaseService.getReleaseById.and.returnValue(of(mockRelease).pipe(delay(0))); - mockReleaseService.getAllReleases.and.returnValue(throwError(() => new Error('Network error')).pipe(delay(0))); - mockLabelService.getHighLightsByReleaseId.and.returnValue(of([]).pipe(delay(0))); - mockIssueService.getIssuesByReleaseId.and.returnValue(of([]).pipe(delay(0))); - mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of([]).pipe(delay(0))); - mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of([]).pipe(delay(0))); + it('should gracefully handle getAllReleases error and leave navigation empty', () => { + mockReleaseService.getReleaseById.and.returnValue(of(mockRelease)); + mockReleaseService.getAllReleases.and.returnValue(throwError(() => new Error('Network error'))); + mockLabelService.getHighLightsByReleaseId.and.returnValue(of([])); + mockIssueService.getIssuesByReleaseId.and.returnValue(of([])); + mockVulnerabilityService.getVulnerabilitiesByReleaseId.and.returnValue(of([])); + mockBusinessValueService.getBusinessValuesByReleaseId.and.returnValue(of([])); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.release()).toEqual(mockRelease); expect(component.branchReleases()).toEqual([]); expect(component.previousRelease()).toBeNull(); expect(component.nextRelease()).toBeNull(); - })); + }); }); describe('nightly filtering in branch navigation', () => { - it('should exclude a nightly that has a stable release after it', fakeAsync(() => { + it('should exclude a nightly that has a stable release after it', () => { setupNightlyLoad([mockReleasePrevious, mockReleaseNightlyMid, mockRelease, mockReleaseNext]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); const ids = component.branchReleases().map((r) => r.id); expect(ids).not.toContain(mockReleaseNightlyMid.id); expect(ids).toEqual([mockReleasePrevious.id, mockRelease.id, mockReleaseNext.id]); - })); + }); - it('should keep a nightly at the trailing end of the branch', fakeAsync(() => { + it('should keep a nightly at the trailing end of the branch', () => { setupNightlyLoad([mockReleasePrevious, mockRelease, mockReleaseNightlyEnd]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); const ids = component.branchReleases().map((r) => r.id); expect(ids).toEqual([mockReleasePrevious.id, mockRelease.id, mockReleaseNightlyEnd.id]); - })); + }); - it('should set nextRelease to trailing nightly', fakeAsync(() => { + it('should set nextRelease to trailing nightly', () => { setupNightlyLoad([mockReleasePrevious, mockRelease, mockReleaseNightlyEnd]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); expect(component.nextRelease()).toEqual(mockReleaseNightlyEnd); - })); + }); - it('should remove multiple mid-list nightlies but keep trailing ones', fakeAsync(() => { + it('should remove multiple mid-list nightlies but keep trailing ones', () => { const anotherMidNightly: Release = { id: 'release-nightly-mid2', name: 'v0.9.5-nightly', @@ -743,7 +711,6 @@ describe('ReleaseDetailsComponent', () => { ]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); const ids = component.branchReleases().map((r) => r.id); @@ -751,9 +718,9 @@ describe('ReleaseDetailsComponent', () => { expect(ids).not.toContain(anotherMidNightly.id); expect(ids).toContain(mockReleaseNightlyEnd.id); expect(ids.length).toBe(4); - })); + }); - it('should correctly detect nightly from tagName with release/ prefix', fakeAsync(() => { + it('should correctly detect nightly from tagName with release/ prefix', () => { const prefixedNightly: Release = { id: 'release-prefixed', name: '9.0-nightly', @@ -765,14 +732,13 @@ describe('ReleaseDetailsComponent', () => { setupNightlyLoad([prefixedNightly, mockReleasePrevious, mockRelease]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); const ids = component.branchReleases().map((r) => r.id); expect(ids).not.toContain(prefixedNightly.id); - })); + }); - it('should keep current release when it is a trailing nightly', fakeAsync(() => { + it('should keep current release when it is a trailing nightly', () => { const nightlyCurrent: Release = { id: 'release-nightly-current', name: 'v1.2.0-nightly', @@ -784,20 +750,18 @@ describe('ReleaseDetailsComponent', () => { setupNightlyLoad([mockRelease, mockReleaseNext, nightlyCurrent], nightlyCurrent); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-nightly-current' }); - tick(); const ids = component.branchReleases().map((r) => r.id); expect(ids).toContain(nightlyCurrent.id); expect(component.previousRelease()).toEqual(mockReleaseNext); expect(component.nextRelease()).toBeNull(); - })); + }); - it('should have no nav when all releases in branch are mid-nightlies except current', fakeAsync(() => { + it('should have no nav when all releases in branch are mid-nightlies except current', () => { setupNightlyLoad([mockReleaseNightlyMid, mockRelease]); fixture.detectChanges(); parameterMapSubject.next({ get: () => 'release-1' }); - tick(); const ids = component.branchReleases().map((r) => r.id); @@ -805,6 +769,6 @@ describe('ReleaseDetailsComponent', () => { expect(ids).toEqual([mockRelease.id]); expect(component.previousRelease()).toBeNull(); expect(component.nextRelease()).toBeNull(); - })); + }); }); }); diff --git a/insights-webapp/src/main/frontend/src/app/pages/release-details/vulnerability-details-off-canvas/vulnerability-details-off-canvas.spec.ts b/insights-webapp/src/main/frontend/src/app/pages/release-details/vulnerability-details-off-canvas/vulnerability-details-off-canvas.spec.ts index dc0e82af..a5aab80b 100644 --- a/insights-webapp/src/main/frontend/src/app/pages/release-details/vulnerability-details-off-canvas/vulnerability-details-off-canvas.spec.ts +++ b/insights-webapp/src/main/frontend/src/app/pages/release-details/vulnerability-details-off-canvas/vulnerability-details-off-canvas.spec.ts @@ -1,5 +1,5 @@ import { ApplicationRef } from '@angular/core'; -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { provideHttpClient, withXhr } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; import { VulnerabilityDetailsOffCanvas } from './vulnerability-details-off-canvas'; @@ -188,7 +188,7 @@ describe('VulnerabilityDetailsOffCanvas', () => { }); describe('Description Overflow Detection', () => { - it('should set showSeeMoreButton to false when element does not overflow', fakeAsync(() => { + it('should set showSeeMoreButton to false when element does not overflow', async () => { fixture.componentRef.setInput('vulnerability', mockVulnerability); fixture.detectChanges(); @@ -201,13 +201,13 @@ describe('VulnerabilityDetailsOffCanvas', () => { fixture.detectChanges(); - tick(); + await fixture.whenStable(); fixture.detectChanges(); expect(component.showSeeMoreButton()).toBe(false); - })); + }); - it('should set showSeeMoreButton to true when element overflows', fakeAsync(() => { + it('should set showSeeMoreButton to true when element overflows', async () => { fixture.componentRef.setInput('vulnerability', mockVulnerability); fixture.detectChanges(); @@ -221,10 +221,10 @@ describe('VulnerabilityDetailsOffCanvas', () => { fixture.detectChanges(); TestBed.inject(ApplicationRef).tick(); - tick(); + await fixture.whenStable(); expect(component.showSeeMoreButton()).toBe(true); - })); + }); it('should set showSeeMoreButton to false when element is not found', () => { component['isDescriptionOverflowing'](); diff --git a/insights-webapp/src/main/frontend/src/app/pages/release-manage/vulnerability-impact-manage/vulnerability-impact-manage.component.spec.ts b/insights-webapp/src/main/frontend/src/app/pages/release-manage/vulnerability-impact-manage/vulnerability-impact-manage.component.spec.ts index 7e7e315e..6df6da96 100644 --- a/insights-webapp/src/main/frontend/src/app/pages/release-manage/vulnerability-impact-manage/vulnerability-impact-manage.component.spec.ts +++ b/insights-webapp/src/main/frontend/src/app/pages/release-manage/vulnerability-impact-manage/vulnerability-impact-manage.component.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { provideHttpClient, withXhr } from '@angular/common/http'; import { provideHttpClientTesting } from '@angular/common/http/testing'; import { Location } from '@angular/common'; @@ -118,14 +118,29 @@ describe('VulnerabilityImpactManageComponent', () => { expect(component.totalElements()).toBe(2); }); - it('should handle search query change with debounce', fakeAsync(() => { - fixture.detectChanges(); + describe('search debounce', () => { + const DEBOUNCE_TICK_MS = 400; + + beforeEach(() => { + fixture.detectChanges(); + TestBed.tick(); + jasmine.clock().install(); + jasmine.clock().mockDate(); + }); + + afterEach(() => { + jasmine.clock().uninstall(); + }); - component.onSearchQueryChanged('CVE-2024-0001'); - tick(350); + it('should handle search query change with debounce', () => { + component.onSearchQueryChanged('CVE-2024-0001'); + TestBed.tick(); + jasmine.clock().tick(DEBOUNCE_TICK_MS); - expect(component.vulnerabilitySearchQuery()).toBe('CVE-2024-0001'); - })); + expect(component.vulnerabilitySearchQuery()).toBe('CVE-2024-0001'); + expect(vulnerabilityService.getAllVulnerabilitiesPaginated).toHaveBeenCalledWith(0, 20, 'CVE-2024-0001'); + }); + }); it('should select vulnerability', () => { fixture.detectChanges(); @@ -165,19 +180,18 @@ describe('VulnerabilityImpactManageComponent', () => { expect(component.selectedVulnerability()?.impactDescription).toBe('New impact'); }); - it('should delete vulnerability impact and update selected vulnerability', fakeAsync(() => { + it('should delete vulnerability impact and update selected vulnerability', () => { vulnerabilityService.deleteVulnerabilityImpact.and.returnValue(of(void 0)); fixture.detectChanges(); component.onVulnerabilitySelected(mockVulnerabilities[0]); component.onImpactDeleted(); - tick(); expect(vulnerabilityService.deleteVulnerabilityImpact).toHaveBeenCalledWith('CVE-2024-0001'); expect(component.selectedVulnerability()?.impactScore).toBeNull(); expect(component.selectedVulnerability()?.impactDescription).toBeNull(); - })); + }); it('should handle error when loading vulnerabilities fails', () => { vulnerabilityService.getAllVulnerabilitiesPaginated.and.returnValue(throwError(() => new Error('Error'))); diff --git a/insights-webapp/src/main/frontend/src/app/pages/release-roadmap/release-roadmap.component.spec.ts b/insights-webapp/src/main/frontend/src/app/pages/release-roadmap/release-roadmap.component.spec.ts index 1cb31b6c..717de6e1 100644 --- a/insights-webapp/src/main/frontend/src/app/pages/release-roadmap/release-roadmap.component.spec.ts +++ b/insights-webapp/src/main/frontend/src/app/pages/release-roadmap/release-roadmap.component.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { of } from 'rxjs'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { ReleaseRoadmapComponent } from './release-roadmap.component'; @@ -55,6 +55,12 @@ describe('ReleaseRoadmapComponent', () => { milestoneService.getMilestones.and.returnValue(of([])); issueService.getIssuesByMilestoneId.and.returnValue(of([])); issueService.getFutureEpicIssues.and.callFake(() => of(getEpicsCopy())); + + jasmine.clock().install(); + }); + + afterEach(() => { + jasmine.clock().uninstall(); }); it('should create', () => { @@ -66,7 +72,7 @@ describe('ReleaseRoadmapComponent', () => { }); describe('Unplanned Epics Milestone', () => { - beforeEach(fakeAsync(() => { + beforeEach(() => { jasmine.clock().mockDate(new Date(2025, 6, 10, 12, 0, 0)); const MOCK_MILESTONE = { @@ -82,9 +88,8 @@ describe('ReleaseRoadmapComponent', () => { issueService.getIssuesByMilestoneId.withArgs('m1').and.returnValue(of([])); fixture.detectChanges(); - tick(); fixture.detectChanges(); - })); + }); it('should create a special milestone for unplanned epics', () => { const unplannedMilestone = component.milestones().find((m) => m.id === 'unplanned-epics'); diff --git a/insights-webapp/src/main/frontend/src/app/services/http-interceptor.service.spec.ts b/insights-webapp/src/main/frontend/src/app/services/http-interceptor.service.spec.ts index 9f6eec24..348fa575 100644 --- a/insights-webapp/src/main/frontend/src/app/services/http-interceptor.service.spec.ts +++ b/insights-webapp/src/main/frontend/src/app/services/http-interceptor.service.spec.ts @@ -1,4 +1,4 @@ -import { TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { TestBed } from '@angular/core/testing'; import { HttpClient, HTTP_INTERCEPTORS, withXhr } from '@angular/common/http'; import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; @@ -45,7 +45,7 @@ describe('HttpInterceptorService', () => { httpTestingController.verify(); }); - it('should logout and redirect on 401 Unauthorized error', fakeAsync(() => { + it('should logout and redirect on 401 Unauthorized error', () => { httpClient.get('/api/data').subscribe({ error: () => {}, }); @@ -53,13 +53,11 @@ describe('HttpInterceptorService', () => { const request = httpTestingController.expectOne('/api/data'); request.flush({ message: 'Unauthorized' }, { status: 401, statusText: 'Unauthorized' }); - tick(); - expect(authService.logout).toHaveBeenCalledWith(); expect(router.navigate).toHaveBeenCalledWith(['/graph']); - })); + }); - it('should logout and redirect on 403 Forbidden error', fakeAsync(() => { + it('should logout and redirect on 403 Forbidden error', () => { httpClient.get('/api/data').subscribe({ error: () => {}, }); @@ -67,13 +65,11 @@ describe('HttpInterceptorService', () => { const request = httpTestingController.expectOne('/api/data'); request.flush({ message: 'Forbidden' }, { status: 403, statusText: 'Forbidden' }); - tick(); - expect(authService.logout).toHaveBeenCalledWith(); expect(router.navigate).toHaveBeenCalledWith(['/graph']); - })); + }); - it('should NOT logout on 500 Internal Server Error', fakeAsync(() => { + it('should NOT logout on 500 Internal Server Error', () => { httpClient.get('/api/data').subscribe({ error: () => {}, }); @@ -81,13 +77,11 @@ describe('HttpInterceptorService', () => { const request = httpTestingController.expectOne('/api/data'); request.flush({ message: 'Something went wrong' }, { status: 500, statusText: 'Internal Server Error' }); - tick(); - expect(authService.logout).not.toHaveBeenCalled(); expect(router.navigate).not.toHaveBeenCalled(); - })); + }); - it('should NOT logout on 400 error (even with "invalid token" message)', fakeAsync(() => { + it('should NOT logout on 400 error (even with "invalid token" message)', () => { httpClient.get('/api/data').subscribe({ error: () => {}, }); @@ -95,13 +89,11 @@ describe('HttpInterceptorService', () => { const request = httpTestingController.expectOne('/api/data'); request.flush({ message: 'Invalid token provided' }, { status: 400, statusText: 'Bad Request' }); - tick(); - expect(authService.logout).not.toHaveBeenCalled(); expect(router.navigate).not.toHaveBeenCalled(); - })); + }); - it('should NOT logout on other errors like 404', fakeAsync(() => { + it('should NOT logout on other errors like 404', () => { httpClient.get('/api/data').subscribe({ error: () => {}, }); @@ -109,13 +101,11 @@ describe('HttpInterceptorService', () => { const request = httpTestingController.expectOne('/api/data'); request.flush({ message: 'Not found' }, { status: 404, statusText: 'Not Found' }); - tick(); - expect(authService.logout).not.toHaveBeenCalled(); expect(router.navigate).not.toHaveBeenCalled(); - })); + }); - it('should NOT logout on 429 rate limiting error', fakeAsync(() => { + it('should NOT logout on 429 rate limiting error', () => { httpClient.get('/api/data').subscribe({ error: () => {}, }); @@ -123,13 +113,11 @@ describe('HttpInterceptorService', () => { const request = httpTestingController.expectOne('/api/data'); request.flush({ message: 'Too many requests' }, { status: 429, statusText: 'Too Many Requests' }); - tick(); - expect(authService.logout).not.toHaveBeenCalled(); expect(router.navigate).not.toHaveBeenCalled(); - })); + }); - it('should NOT logout if request is to /auth/logout endpoint', fakeAsync(() => { + it('should NOT logout if request is to /auth/logout endpoint', () => { httpClient.post('/api/auth/logout', {}).subscribe({ error: () => {}, }); @@ -137,13 +125,11 @@ describe('HttpInterceptorService', () => { const request = httpTestingController.expectOne('/api/auth/logout'); request.flush({ message: 'Unauthorized' }, { status: 401, statusText: 'Unauthorized' }); - tick(); - expect(authService.logout).not.toHaveBeenCalled(); expect(router.navigate).not.toHaveBeenCalled(); - })); + }); - it('should NOT logout if request is to /auth/user endpoint', fakeAsync(() => { + it('should NOT logout if request is to /auth/user endpoint', () => { httpClient.get('/api/auth/user').subscribe({ error: () => {}, }); @@ -151,13 +137,11 @@ describe('HttpInterceptorService', () => { const request = httpTestingController.expectOne('/api/auth/user'); request.flush({ message: 'Unauthorized' }, { status: 401, statusText: 'Unauthorized' }); - tick(); - expect(authService.logout).not.toHaveBeenCalled(); expect(router.navigate).not.toHaveBeenCalled(); - })); + }); - it('should navigate to login even if logout fails', fakeAsync(() => { + it('should navigate to login even if logout fails', () => { authService.logout.and.returnValue(throwError(() => new Error('Logout failed'))); httpClient.get('/api/data').subscribe({ @@ -167,9 +151,7 @@ describe('HttpInterceptorService', () => { const request = httpTestingController.expectOne('/api/data'); request.flush({ message: 'Unauthorized' }, { status: 401, statusText: 'Unauthorized' }); - tick(); - expect(authService.logout).toHaveBeenCalledWith(); expect(router.navigate).toHaveBeenCalledWith(['/graph']); - })); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9545da0a..3caec813 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -64,43 +64,40 @@ importers: tslib: specifier: ^2.8.1 version: 2.8.1 - zone.js: - specifier: ~0.16.2 - version: 0.16.2 devDependencies: '@angular-eslint/builder': specifier: ^22.1.0 - version: 22.1.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) + version: 22.1.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) '@angular-eslint/eslint-plugin': specifier: ^22.1.0 - version: 22.1.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) + version: 22.1.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) '@angular-eslint/eslint-plugin-template': specifier: ^22.1.0 - version: 22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) + version: 22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(@typescript-eslint/types@8.66.0)(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) '@angular-eslint/schematics': specifier: ^22.1.0 - version: 22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3))(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) + version: 22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0))(@typescript-eslint/types@8.66.0)(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) '@angular-eslint/template-parser': specifier: ^22.1.0 - version: 22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) + version: 22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) '@angular/build': specifier: ^22.1.3 - version: 22.1.4(@angular/compiler-cli@22.1.2(@angular/compiler@22.1.2)(typescript@6.0.3))(@angular/compiler@22.1.2)(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.1.2(@angular/animations@22.1.0(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@22.1.2(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@25.5.2)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3(supports-color@8.1.1))(jiti@2.6.1)(karma@6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1))(less@4.6.7)(postcss@8.5.25)(rollup@4.62.2)(supports-color@8.1.1)(terser@5.49.0)(tslib@2.8.1)(typescript@6.0.3)(yaml@2.9.0) + version: 22.1.4(@angular/compiler-cli@22.1.2(@angular/compiler@22.1.2)(typescript@6.0.3))(@angular/compiler@22.1.2)(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.1.2(@angular/animations@22.1.0(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@22.1.2(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@25.5.2)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.6.7)(postcss@8.5.25)(rollup@4.62.2)(terser@5.49.0)(tslib@2.8.1)(typescript@6.0.3)(yaml@2.9.0) '@angular/cli': specifier: ^22.1.3 - version: 22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1) + version: 22.1.4(@types/node@25.5.2)(chokidar@5.0.0) '@angular/compiler-cli': specifier: ^22.1.1 version: 22.1.2(@angular/compiler@22.1.2)(typescript@6.0.3) '@cypress/schematic': specifier: 5.0.0 - version: 5.0.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1))(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))(cypress@15.20.1) + version: 5.0.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0))(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))(cypress@15.20.1) '@eslint/eslintrc': specifier: ^3.3.6 - version: 3.3.6(supports-color@8.1.1) + version: 3.3.6 '@eslint/js': specifier: ^10.0.1 - version: 10.0.1(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)) + version: 10.0.1(eslint@10.8.0(jiti@2.6.1)) '@types/dompurify': specifier: ^3.2.0 version: 3.2.0 @@ -109,52 +106,52 @@ importers: version: 6.0.0 '@typescript-eslint/eslint-plugin': specifier: ^8.66.0 - version: 8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + version: 8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/parser': specifier: ^8.66.0 - version: 8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + version: 8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) angular-eslint: specifier: ^22.1.0 - version: 22.1.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript-eslint@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(typescript@6.0.3) + version: 22.1.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1))(typescript-eslint@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(typescript@6.0.3) eslint: specifier: ^10.8.0 - version: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + version: 10.8.0(jiti@2.6.1) eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)) + version: 10.1.8(eslint@10.8.0(jiti@2.6.1)) eslint-plugin-jasmine: specifier: ^4.2.2 version: 4.2.2 eslint-plugin-prettier: specifier: ^5.5.6 - version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(prettier@3.9.6) + version: 5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.8.0(jiti@2.6.1)))(eslint@10.8.0(jiti@2.6.1))(prettier@3.9.6) eslint-plugin-sonarjs: specifier: ^4.2.0 - version: 4.2.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)) + version: 4.2.0(eslint@10.8.0(jiti@2.6.1)) eslint-plugin-unicorn: specifier: ^64.0.0 - version: 64.0.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)) + version: 64.0.0(eslint@10.8.0(jiti@2.6.1)) istanbul-lib-instrument: specifier: ^6.0.3 - version: 6.0.3(supports-color@8.1.1) + version: 6.0.3 jasmine-core: - specifier: ~6.3.0 - version: 6.3.0 + specifier: ~7.0.1 + version: 7.0.1 karma: specifier: ~6.4.4 - version: 6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1) + version: 6.4.4 karma-chrome-launcher: specifier: ~3.2.0 version: 3.2.0 karma-coverage: specifier: ~2.2.1 - version: 2.2.1(supports-color@8.1.1) + version: 2.2.1 karma-jasmine: specifier: ~5.1.0 - version: 5.1.0(karma@6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1)) + version: 5.1.0(karma@6.4.4) karma-jasmine-html-reporter: - specifier: ~2.2.0 - version: 2.2.0(jasmine-core@6.3.0)(karma-jasmine@5.1.0(karma@6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1)))(karma@6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1)) + specifier: ~2.3.0 + version: 2.3.0(jasmine-core@7.0.1)(karma-jasmine@5.1.0(karma@6.4.4))(karma@6.4.4) prettier: specifier: ^3.9.6 version: 3.9.6 @@ -163,7 +160,7 @@ importers: version: 6.0.3 typescript-eslint: specifier: ^8.66.0 - version: 8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + version: 8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) insights-webapp/src/main/frontend/cypress: devDependencies: @@ -172,7 +169,7 @@ importers: version: 15.20.1 cypress-multi-reporters: specifier: ^2.0.5 - version: 2.0.5(mocha@11.8.0)(supports-color@8.1.1) + version: 2.0.5(mocha@11.8.0) mocha: specifier: ^11.8.0 version: 11.8.0 @@ -1092,49 +1089,42 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@napi-rs/nice-linux-arm64-musl@1.1.1': resolution: {integrity: sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@napi-rs/nice-linux-ppc64-gnu@1.1.1': resolution: {integrity: sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==} engines: {node: '>= 10'} cpu: [ppc64] os: [linux] - libc: [glibc] '@napi-rs/nice-linux-riscv64-gnu@1.1.1': resolution: {integrity: sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] - libc: [glibc] '@napi-rs/nice-linux-s390x-gnu@1.1.1': resolution: {integrity: sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==} engines: {node: '>= 10'} cpu: [s390x] os: [linux] - libc: [glibc] '@napi-rs/nice-linux-x64-gnu@1.1.1': resolution: {integrity: sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@napi-rs/nice-linux-x64-musl@1.1.1': resolution: {integrity: sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@napi-rs/nice-openharmony-arm64@1.1.1': resolution: {integrity: sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==} @@ -1218,56 +1208,48 @@ packages: 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==} @@ -1336,42 +1318,36 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.6': resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.6': resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.6': resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.6': resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.6': resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [musl] '@parcel/watcher-win32-arm64@2.5.6': resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} @@ -1468,84 +1444,72 @@ packages: 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] - libc: [glibc] '@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] - 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-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-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] - libc: [glibc] '@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] - libc: [musl] '@rolldown/binding-openharmony-arm64@1.1.5': resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} @@ -1630,79 +1594,66 @@ packages: resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.62.2': resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.62.2': resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.62.2': resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.62.2': resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.62.2': resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} cpu: [loong64] os: [linux] - libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.62.2': resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.62.2': resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} cpu: [ppc64] os: [linux] - libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.62.2': resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.62.2': resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.62.2': resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.62.2': resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.62.2': resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-openbsd-x64@4.62.2': resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} @@ -3052,8 +3003,8 @@ packages: jasmine-core@4.6.1: resolution: {integrity: sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==} - jasmine-core@6.3.0: - resolution: {integrity: sha512-eMm5qBovNjNoGOcgE/W207+wrcK5zrQv0Rg/rWGboUJUmZp0dFCpHTyjpuDAfCwRCqg7f9U2q2jtv/aUuzdCQg==} + jasmine-core@7.0.1: + resolution: {integrity: sha512-AVUnCekV7RBFS6k4+bc9OrLTbEcDYM9gVHtUuPMWrpFUZBovr5p47WbVTKbr6gBYeAtBBe+nX32ivrvc0Ne2XA==} jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} @@ -3130,10 +3081,10 @@ packages: resolution: {integrity: sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==} engines: {node: '>=10.0.0'} - karma-jasmine-html-reporter@2.2.0: - resolution: {integrity: sha512-J0laEC43Oy2RdR5V5R3bqmdo7yRIYySq6XHKbA+e5iSAgLjhR1oICLGeSREPlJXpeyNcdJf3J17YcdhD0mRssQ==} + karma-jasmine-html-reporter@2.3.0: + resolution: {integrity: sha512-iFDjVpcWHnupRoJfnjuAeNF68sAGk+7RfceCzm33HHYNV6OgBa6EC1nPgJUFM+tVDHLlQ5NhgcTKz7AbdrV3AQ==} peerDependencies: - jasmine-core: ^4.0.0 || ^5.0.0 || ^6.0.0 + jasmine-core: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 karma: ^6.0.0 karma-jasmine: ^5.0.0 @@ -3195,28 +3146,24 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} @@ -4412,67 +4359,67 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-eslint/builder@22.1.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3)': + '@angular-eslint/builder@22.1.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@angular-devkit/architect': 0.2201.2(chokidar@5.0.0) '@angular-devkit/core': 22.1.2(chokidar@5.0.0) - '@angular/cli': 22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1) - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + '@angular/cli': 22.1.4(@types/node@25.5.2)(chokidar@5.0.0) + eslint: 10.8.0(jiti@2.6.1) typescript: 6.0.3 transitivePeerDependencies: - chokidar '@angular-eslint/bundled-angular-compiler@22.1.0': {} - '@angular-eslint/eslint-plugin-template@22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3)': + '@angular-eslint/eslint-plugin-template@22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.1.0 - '@angular-eslint/template-parser': 22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@angular-eslint/utils': 22.1.0(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) + '@angular-eslint/template-parser': 22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@angular-eslint/utils': 22.1.0(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/types': 8.65.0 - '@typescript-eslint/utils': 8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/utils': 8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) aria-query: 5.3.2 axobject-query: 4.1.0 - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + eslint: 10.8.0(jiti@2.6.1) typescript: 6.0.3 - '@angular-eslint/eslint-plugin-template@22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3)': + '@angular-eslint/eslint-plugin-template@22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(@typescript-eslint/types@8.66.0)(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.1.0 - '@angular-eslint/template-parser': 22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@angular-eslint/utils': 22.1.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@typescript-eslint/types': 8.65.0 - '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@angular-eslint/template-parser': 22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@angular-eslint/utils': 22.1.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/types': 8.66.0 + '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) aria-query: 5.3.2 axobject-query: 4.1.0 - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + eslint: 10.8.0(jiti@2.6.1) typescript: 6.0.3 - '@angular-eslint/eslint-plugin@22.1.0(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3)': + '@angular-eslint/eslint-plugin@22.1.0(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.1.0 - '@angular-eslint/utils': 22.1.0(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@typescript-eslint/utils': 8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + '@angular-eslint/utils': 22.1.0(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/utils': 8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + eslint: 10.8.0(jiti@2.6.1) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 - '@angular-eslint/eslint-plugin@22.1.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3)': + '@angular-eslint/eslint-plugin@22.1.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.1.0 - '@angular-eslint/utils': 22.1.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + '@angular-eslint/utils': 22.1.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + eslint: 10.8.0(jiti@2.6.1) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 - '@angular-eslint/schematics@22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3))(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3)': + '@angular-eslint/schematics@22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@angular-devkit/core': 22.1.2(chokidar@5.0.0) '@angular-devkit/schematics': 22.1.2(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 22.1.0(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@angular-eslint/eslint-plugin-template': 22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@angular/cli': 22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1) + '@angular-eslint/eslint-plugin': 22.1.0(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@angular-eslint/eslint-plugin-template': 22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@angular/cli': 22.1.4(@types/node@25.5.2)(chokidar@5.0.0) ignore: 7.0.5 semver: 7.8.5 strip-json-comments: 3.1.1 @@ -4484,13 +4431,13 @@ snapshots: - eslint - typescript - '@angular-eslint/schematics@22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3))(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3)': + '@angular-eslint/schematics@22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0))(@typescript-eslint/types@8.66.0)(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@angular-devkit/core': 22.1.2(chokidar@5.0.0) '@angular-devkit/schematics': 22.1.2(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 22.1.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@angular-eslint/eslint-plugin-template': 22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@angular/cli': 22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1) + '@angular-eslint/eslint-plugin': 22.1.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@angular-eslint/eslint-plugin-template': 22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(@typescript-eslint/types@8.66.0)(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@angular/cli': 22.1.4(@types/node@25.5.2)(chokidar@5.0.0) ignore: 7.0.5 semver: 7.8.5 strip-json-comments: 3.1.1 @@ -4502,25 +4449,25 @@ snapshots: - eslint - typescript - '@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3)': + '@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.1.0 - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + eslint: 10.8.0(jiti@2.6.1) eslint-scope: 9.1.2 typescript: 6.0.3 - '@angular-eslint/utils@22.1.0(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3)': + '@angular-eslint/utils@22.1.0(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.1.0 - '@typescript-eslint/utils': 8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + '@typescript-eslint/utils': 8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + eslint: 10.8.0(jiti@2.6.1) typescript: 6.0.3 - '@angular-eslint/utils@22.1.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3)': + '@angular-eslint/utils@22.1.0(@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@angular-eslint/bundled-angular-compiler': 22.1.0 - '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + eslint: 10.8.0(jiti@2.6.1) typescript: 6.0.3 '@angular/animations@22.1.0(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))': @@ -4528,7 +4475,7 @@ snapshots: '@angular/core': 22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2) tslib: 2.8.1 - '@angular/build@22.1.4(@angular/compiler-cli@22.1.2(@angular/compiler@22.1.2)(typescript@6.0.3))(@angular/compiler@22.1.2)(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.1.2(@angular/animations@22.1.0(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@22.1.2(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@25.5.2)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3(supports-color@8.1.1))(jiti@2.6.1)(karma@6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1))(less@4.6.7)(postcss@8.5.25)(rollup@4.62.2)(supports-color@8.1.1)(terser@5.49.0)(tslib@2.8.1)(typescript@6.0.3)(yaml@2.9.0)': + '@angular/build@22.1.4(@angular/compiler-cli@22.1.2(@angular/compiler@22.1.2)(typescript@6.0.3))(@angular/compiler@22.1.2)(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.1.2(@angular/animations@22.1.0(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@22.1.2(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@25.5.2)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(jiti@2.6.1)(karma@6.4.4)(less@4.6.7)(postcss@8.5.25)(rollup@4.62.2)(terser@5.49.0)(tslib@2.8.1)(typescript@6.0.3)(yaml@2.9.0)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2201.4(chokidar@5.0.0) @@ -4542,7 +4489,7 @@ snapshots: beasties: 0.4.3 browserslist: 4.28.2 esbuild: 0.28.2 - https-proxy-agent: 9.1.0(supports-color@8.1.1) + https-proxy-agent: 9.1.0 jsonc-parser: 3.3.1 listr2: 10.2.2 magic-string: 1.0.0 @@ -4563,8 +4510,8 @@ snapshots: optionalDependencies: '@angular/core': 22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2) '@angular/platform-browser': 22.1.2(@angular/animations@22.1.0(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2)))(@angular/common@22.1.2(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2)) - istanbul-lib-instrument: 6.0.3(supports-color@8.1.1) - karma: 6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1) + istanbul-lib-instrument: 6.0.3 + karma: 6.4.4 less: 4.6.7 lmdb: 3.5.6 postcss: 8.5.25 @@ -4592,14 +4539,14 @@ snapshots: rxjs: 7.8.2 tslib: 2.8.1 - '@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1)': + '@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0)': dependencies: '@angular-devkit/architect': 0.2201.4(chokidar@5.0.0) '@angular-devkit/core': 22.1.4(chokidar@5.0.0) '@angular-devkit/schematics': 22.1.4(chokidar@5.0.0) '@inquirer/prompts': 8.5.2(@types/node@25.5.2) '@listr2/prompt-adapter-inquirer': 4.2.4(@inquirer/prompts@8.5.2(@types/node@25.5.2))(@types/node@25.5.2)(listr2@10.2.2) - '@modelcontextprotocol/sdk': 1.30.0(supports-color@8.1.1)(zod@4.4.3) + '@modelcontextprotocol/sdk': 1.30.0(zod@4.4.3) '@schematics/angular': 22.1.4(chokidar@5.0.0) jsonc-parser: 3.3.1 listr2: 10.2.2 @@ -4695,16 +4642,16 @@ snapshots: '@babel/compat-data@8.0.0': {} - '@babel/core@7.29.7(supports-color@8.1.1)': + '@babel/core@7.29.7': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) '@babel/helpers': 7.29.7 '@babel/parser': 7.29.7 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7(supports-color@8.1.1) + '@babel/traverse': 7.29.7 '@babel/types': 7.29.7 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 @@ -4775,19 +4722,19 @@ snapshots: '@babel/helper-globals@8.0.0': {} - '@babel/helper-module-imports@7.29.7(supports-color@8.1.1)': + '@babel/helper-module-imports@7.29.7': dependencies: - '@babel/traverse': 7.29.7(supports-color@8.1.1) + '@babel/traverse': 7.29.7 '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) - '@babel/helper-module-imports': 7.29.7(supports-color@8.1.1) + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.7(supports-color@8.1.1) + '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color @@ -4839,7 +4786,7 @@ snapshots: '@babel/parser': 8.0.4 '@babel/types': 8.0.4 - '@babel/traverse@7.29.7(supports-color@8.1.1)': + '@babel/traverse@7.29.7': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 @@ -4893,9 +4840,9 @@ snapshots: tough-cookie: 5.1.2 tunnel-agent: 0.6.0 - '@cypress/schematic@5.0.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1))(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))(cypress@15.20.1)': + '@cypress/schematic@5.0.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0))(@angular/core@22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2))(cypress@15.20.1)': dependencies: - '@angular/cli': 22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1) + '@angular/cli': 22.1.4(@types/node@25.5.2)(chokidar@5.0.0) '@angular/core': 22.1.2(@angular/compiler@22.1.2)(rxjs@7.8.2)(zone.js@0.16.2) cypress: 15.20.1 jsonc-parser: 3.3.1 @@ -5014,14 +4961,14 @@ snapshots: '@esbuild/win32-x64@0.28.2': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))': + '@eslint-community/eslint-utils@4.9.1(eslint@10.8.0(jiti@2.6.1))': dependencies: - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + eslint: 10.8.0(jiti@2.6.1) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/config-array@0.23.5(supports-color@8.1.1)': + '@eslint/config-array@0.23.5': dependencies: '@eslint/object-schema': 3.0.5 debug: 4.4.3(supports-color@8.1.1) @@ -5037,7 +4984,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.6(supports-color@8.1.1)': + '@eslint/eslintrc@3.3.6': dependencies: ajv: 6.14.0 debug: 4.4.3(supports-color@8.1.1) @@ -5051,9 +4998,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@10.0.1(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))': + '@eslint/js@10.0.1(eslint@10.8.0(jiti@2.6.1))': optionalDependencies: - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + eslint: 10.8.0(jiti@2.6.1) '@eslint/object-schema@3.0.5': {} @@ -5266,7 +5213,7 @@ snapshots: '@lmdb/lmdb-win32-x64@3.5.6': optional: true - '@modelcontextprotocol/sdk@1.30.0(supports-color@8.1.1)(zod@4.4.3)': + '@modelcontextprotocol/sdk@1.30.0(zod@4.4.3)': dependencies: '@hono/node-server': 2.1.0(hono@4.13.0) ajv: 8.20.0 @@ -5276,8 +5223,8 @@ snapshots: cross-spawn: 7.0.6 eventsource: 3.0.7 eventsource-parser: 3.0.6 - express: 5.2.1(supports-color@8.1.1) - express-rate-limit: 8.3.2(express@5.2.1(supports-color@8.1.1)) + express: 5.2.1 + express-rate-limit: 8.3.2(express@5.2.1) hono: 4.13.0 jose: 6.2.2 json-schema-typed: 8.0.2 @@ -5764,15 +5711,15 @@ snapshots: dependencies: '@types/node': 25.5.2 - '@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/parser': 8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/scope-manager': 8.66.0 - '@typescript-eslint/type-utils': 8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) - '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/type-utils': 8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.66.0 - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + eslint: 10.8.0(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -5780,19 +5727,19 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': + '@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@typescript-eslint/scope-manager': 8.66.0 '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/typescript-estree': 8.66.0(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.66.0(typescript@6.0.3) '@typescript-eslint/visitor-keys': 8.66.0 debug: 4.4.3(supports-color@8.1.1) - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + eslint: 10.8.0(jiti@2.6.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.65.0(supports-color@8.1.1)(typescript@6.0.3)': + '@typescript-eslint/project-service@8.65.0(typescript@6.0.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.65.0(typescript@6.0.3) '@typescript-eslint/types': 8.65.0 @@ -5801,7 +5748,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.66.0(supports-color@8.1.1)(typescript@6.0.3)': + '@typescript-eslint/project-service@8.66.0(typescript@6.0.3)': dependencies: '@typescript-eslint/tsconfig-utils': 8.66.0(typescript@6.0.3) '@typescript-eslint/types': 8.66.0 @@ -5828,13 +5775,13 @@ snapshots: dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/typescript-estree': 8.66.0(supports-color@8.1.1)(typescript@6.0.3) - '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.66.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) debug: 4.4.3(supports-color@8.1.1) - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + eslint: 10.8.0(jiti@2.6.1) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: @@ -5844,9 +5791,9 @@ snapshots: '@typescript-eslint/types@8.66.0': {} - '@typescript-eslint/typescript-estree@8.65.0(supports-color@8.1.1)(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.65.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.65.0(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/project-service': 8.65.0(typescript@6.0.3) '@typescript-eslint/tsconfig-utils': 8.65.0(typescript@6.0.3) '@typescript-eslint/types': 8.65.0 '@typescript-eslint/visitor-keys': 8.65.0 @@ -5859,9 +5806,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.66.0(supports-color@8.1.1)(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.66.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.66.0(supports-color@8.1.1)(typescript@6.0.3) + '@typescript-eslint/project-service': 8.66.0(typescript@6.0.3) '@typescript-eslint/tsconfig-utils': 8.66.0(typescript@6.0.3) '@typescript-eslint/types': 8.66.0 '@typescript-eslint/visitor-keys': 8.66.0 @@ -5874,24 +5821,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': + '@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.0(jiti@2.6.1)) '@typescript-eslint/scope-manager': 8.65.0 '@typescript-eslint/types': 8.65.0 - '@typescript-eslint/typescript-estree': 8.65.0(supports-color@8.1.1)(typescript@6.0.3) - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + '@typescript-eslint/typescript-estree': 8.65.0(typescript@6.0.3) + eslint: 10.8.0(jiti@2.6.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3)': + '@typescript-eslint/utils@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.0(jiti@2.6.1)) '@typescript-eslint/scope-manager': 8.66.0 '@typescript-eslint/types': 8.66.0 - '@typescript-eslint/typescript-estree': 8.66.0(supports-color@8.1.1)(typescript@6.0.3) - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + '@typescript-eslint/typescript-estree': 8.66.0(typescript@6.0.3) + eslint: 10.8.0(jiti@2.6.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -5946,21 +5893,21 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - angular-eslint@22.1.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript-eslint@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(typescript@6.0.3): + angular-eslint@22.1.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1))(typescript-eslint@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(typescript@6.0.3): dependencies: '@angular-devkit/core': 22.1.2(chokidar@5.0.0) '@angular-devkit/schematics': 22.1.2(chokidar@5.0.0) - '@angular-eslint/builder': 22.1.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@angular-eslint/eslint-plugin': 22.1.0(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@angular-eslint/eslint-plugin-template': 22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@angular-eslint/schematics': 22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3))(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@angular-eslint/template-parser': 22.1.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(typescript@6.0.3) - '@angular/cli': 22.1.4(@types/node@25.5.2)(chokidar@5.0.0)(supports-color@8.1.1) + '@angular-eslint/builder': 22.1.0(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@angular-eslint/eslint-plugin': 22.1.0(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@angular-eslint/eslint-plugin-template': 22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@angular-eslint/schematics': 22.1.0(@angular-eslint/template-parser@22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(@angular/cli@22.1.4(@types/node@25.5.2)(chokidar@5.0.0))(@typescript-eslint/types@8.65.0)(@typescript-eslint/utils@8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(chokidar@5.0.0)(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@angular-eslint/template-parser': 22.1.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@angular/cli': 22.1.4(@types/node@25.5.2)(chokidar@5.0.0) '@typescript-eslint/types': 8.65.0 - '@typescript-eslint/utils': 8.65.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + '@typescript-eslint/utils': 8.65.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + eslint: 10.8.0(jiti@2.6.1) typescript: 6.0.3 - typescript-eslint: 8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) + typescript-eslint: 8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) transitivePeerDependencies: - chokidar - supports-color @@ -6038,11 +5985,11 @@ snapshots: bluebird@3.7.2: {} - body-parser@1.20.6(supports-color@8.1.1): + body-parser@1.20.6: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 2.6.9(supports-color@8.1.1) + debug: 2.6.9 depd: 2.0.0 destroy: 1.2.0 http-errors: 2.0.1 @@ -6055,7 +6002,7 @@ snapshots: transitivePeerDependencies: - supports-color - body-parser@2.3.0(supports-color@8.1.1): + body-parser@2.3.0: dependencies: bytes: 3.1.2 content-type: 2.0.0 @@ -6233,10 +6180,10 @@ snapshots: concat-map@0.0.1: {} - connect@3.7.0(supports-color@8.1.1): + connect@3.7.0: dependencies: - debug: 2.6.9(supports-color@8.1.1) - finalhandler: 1.1.2(supports-color@8.1.1) + debug: 2.6.9 + finalhandler: 1.1.2 parseurl: 1.3.3 utils-merge: 1.0.1 transitivePeerDependencies: @@ -6290,9 +6237,9 @@ snapshots: custom-event@1.0.1: {} - cypress-multi-reporters@2.0.5(mocha@11.8.0)(supports-color@8.1.1): + cypress-multi-reporters@2.0.5(mocha@11.8.0): dependencies: - debug: 4.4.1(supports-color@8.1.1) + debug: 4.4.1 lodash: 4.18.1 mocha: 11.8.0 semver: 7.7.2 @@ -6350,11 +6297,9 @@ snapshots: dayjs@1.11.20: {} - debug@2.6.9(supports-color@8.1.1): + debug@2.6.9: dependencies: ms: 2.0.0 - optionalDependencies: - supports-color: 8.1.1 debug@3.2.7(supports-color@8.1.1): dependencies: @@ -6362,11 +6307,9 @@ snapshots: optionalDependencies: supports-color: 8.1.1 - debug@4.4.1(supports-color@8.1.1): + debug@4.4.1: dependencies: ms: 2.1.3 - optionalDependencies: - supports-color: 8.1.1 debug@4.4.3(supports-color@8.1.1): dependencies: @@ -6456,7 +6399,7 @@ snapshots: engine.io-parser@5.2.3: {} - engine.io@6.6.9(supports-color@8.1.1): + engine.io@6.6.9: dependencies: '@types/cors': 2.8.19 '@types/node': 25.5.2 @@ -6549,28 +6492,28 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.8(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)): + eslint-config-prettier@10.1.8(eslint@10.8.0(jiti@2.6.1)): dependencies: - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + eslint: 10.8.0(jiti@2.6.1) eslint-plugin-jasmine@4.2.2: {} - eslint-plugin-prettier@5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(prettier@3.9.6): + eslint-plugin-prettier@5.5.6(@types/eslint@9.6.1)(eslint-config-prettier@10.1.8(eslint@10.8.0(jiti@2.6.1)))(eslint@10.8.0(jiti@2.6.1))(prettier@3.9.6): dependencies: - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + eslint: 10.8.0(jiti@2.6.1) prettier: 3.9.6 prettier-linter-helpers: 1.0.1 synckit: 0.11.13 optionalDependencies: '@types/eslint': 9.6.1 - eslint-config-prettier: 10.1.8(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)) + eslint-config-prettier: 10.1.8(eslint@10.8.0(jiti@2.6.1)) - eslint-plugin-sonarjs@4.2.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)): + eslint-plugin-sonarjs@4.2.0(eslint@10.8.0(jiti@2.6.1)): dependencies: '@eslint-community/regexpp': 4.12.2 builtin-modules: 3.3.0 bytes: 3.1.2 - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + eslint: 10.8.0(jiti@2.6.1) functional-red-black-tree: 1.0.1 globals: 17.7.0 jsx-ast-utils-x: 0.1.0 @@ -6582,15 +6525,15 @@ snapshots: typescript: 6.0.3 yaml: 2.9.0 - eslint-plugin-unicorn@64.0.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)): + eslint-plugin-unicorn@64.0.0(eslint@10.8.0(jiti@2.6.1)): dependencies: '@babel/helper-validator-identifier': 7.28.5 - '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.0(jiti@2.6.1)) change-case: 5.4.4 ci-info: 4.4.0 clean-regexp: 1.0.0 core-js-compat: 3.49.0 - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + eslint: 10.8.0(jiti@2.6.1) find-up-simple: 1.0.1 globals: 17.5.0 indent-string: 5.0.0 @@ -6615,11 +6558,11 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1): + eslint@10.8.0(jiti@2.6.1): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.8.0(jiti@2.6.1)) '@eslint-community/regexpp': 4.12.2 - '@eslint/config-array': 0.23.5(supports-color@8.1.1) + '@eslint/config-array': 0.23.5 '@eslint/config-helpers': 0.7.0 '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.2 @@ -6706,15 +6649,15 @@ snapshots: dependencies: pify: 2.3.0 - express-rate-limit@8.3.2(express@5.2.1(supports-color@8.1.1)): + express-rate-limit@8.3.2(express@5.2.1): dependencies: - express: 5.2.1(supports-color@8.1.1) + express: 5.2.1 ip-address: 10.5.0 - express@5.2.1(supports-color@8.1.1): + express@5.2.1: dependencies: accepts: 2.0.0 - body-parser: 2.3.0(supports-color@8.1.1) + body-parser: 2.3.0 content-disposition: 1.1.0 content-type: 1.0.5 cookie: 0.7.2 @@ -6724,7 +6667,7 @@ snapshots: encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 2.1.1(supports-color@8.1.1) + finalhandler: 2.1.1 fresh: 2.0.0 http-errors: 2.0.1 merge-descriptors: 2.0.0 @@ -6735,9 +6678,9 @@ snapshots: proxy-addr: 2.0.7 qs: 6.15.3 range-parser: 1.2.1 - router: 2.2.0(supports-color@8.1.1) - send: 1.2.1(supports-color@8.1.1) - serve-static: 2.2.1(supports-color@8.1.1) + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 statuses: 2.0.2 type-is: 2.1.0 vary: 1.1.2 @@ -6780,9 +6723,9 @@ snapshots: dependencies: to-regex-range: 5.0.1 - finalhandler@1.1.2(supports-color@8.1.1): + finalhandler@1.1.2: dependencies: - debug: 2.6.9(supports-color@8.1.1) + debug: 2.6.9 encodeurl: 1.0.2 escape-html: 1.0.3 on-finished: 2.3.0 @@ -6792,7 +6735,7 @@ snapshots: transitivePeerDependencies: - supports-color - finalhandler@2.1.1(supports-color@8.1.1): + finalhandler@2.1.1: dependencies: debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 @@ -6819,9 +6762,7 @@ snapshots: flatted@3.4.2: {} - follow-redirects@1.16.0(debug@4.4.3(supports-color@8.1.1)): - optionalDependencies: - debug: 4.4.3(supports-color@8.1.1) + follow-redirects@1.16.0: {} foreground-child@3.3.1: dependencies: @@ -6986,10 +6927,10 @@ snapshots: statuses: 2.0.2 toidentifier: 1.0.1 - http-proxy@1.18.1(debug@4.4.3(supports-color@8.1.1)): + http-proxy@1.18.1: dependencies: eventemitter3: 4.0.7 - follow-redirects: 1.16.0(debug@4.4.3(supports-color@8.1.1)) + follow-redirects: 1.16.0 requires-port: 1.0.0 transitivePeerDependencies: - debug @@ -7000,7 +6941,7 @@ snapshots: jsprim: 2.0.2 sshpk: 1.18.0 - https-proxy-agent@9.1.0(supports-color@8.1.1): + https-proxy-agent@9.1.0: dependencies: agent-base: 9.0.0 debug: 4.4.3(supports-color@8.1.1) @@ -7120,9 +7061,9 @@ snapshots: istanbul-lib-coverage@3.2.2: {} - istanbul-lib-instrument@5.2.1(supports-color@8.1.1): + istanbul-lib-instrument@5.2.1: dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) + '@babel/core': 7.29.7 '@babel/parser': 7.29.7 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 @@ -7130,9 +7071,9 @@ snapshots: transitivePeerDependencies: - supports-color - istanbul-lib-instrument@6.0.3(supports-color@8.1.1): + istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.29.7(supports-color@8.1.1) + '@babel/core': 7.29.7 '@babel/parser': 7.29.7 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 @@ -7146,7 +7087,7 @@ snapshots: make-dir: 4.0.0 supports-color: 7.2.0 - istanbul-lib-source-maps@4.0.1(supports-color@8.1.1): + istanbul-lib-source-maps@4.0.1: dependencies: debug: 4.4.3(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 @@ -7167,7 +7108,7 @@ snapshots: jasmine-core@4.6.1: {} - jasmine-core@6.3.0: {} + jasmine-core@7.0.1: {} jiti@2.6.1: optional: true @@ -7227,50 +7168,50 @@ snapshots: dependencies: which: 1.3.1 - karma-coverage@2.2.1(supports-color@8.1.1): + karma-coverage@2.2.1: dependencies: istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 5.2.1(supports-color@8.1.1) + istanbul-lib-instrument: 5.2.1 istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1(supports-color@8.1.1) + istanbul-lib-source-maps: 4.0.1 istanbul-reports: 3.2.0 minimatch: 3.1.5 transitivePeerDependencies: - supports-color - karma-jasmine-html-reporter@2.2.0(jasmine-core@6.3.0)(karma-jasmine@5.1.0(karma@6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1)))(karma@6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1)): + karma-jasmine-html-reporter@2.3.0(jasmine-core@7.0.1)(karma-jasmine@5.1.0(karma@6.4.4))(karma@6.4.4): dependencies: - jasmine-core: 6.3.0 - karma: 6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1) - karma-jasmine: 5.1.0(karma@6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1)) + jasmine-core: 7.0.1 + karma: 6.4.4 + karma-jasmine: 5.1.0(karma@6.4.4) - karma-jasmine@5.1.0(karma@6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1)): + karma-jasmine@5.1.0(karma@6.4.4): dependencies: jasmine-core: 4.6.1 - karma: 6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1) + karma: 6.4.4 - karma@6.4.4(debug@4.4.3(supports-color@8.1.1))(supports-color@8.1.1): + karma@6.4.4: dependencies: '@colors/colors': 1.5.0 - body-parser: 1.20.6(supports-color@8.1.1) + body-parser: 1.20.6 braces: 3.0.3 chokidar: 3.6.0 - connect: 3.7.0(supports-color@8.1.1) + connect: 3.7.0 di: 0.0.1 dom-serialize: 2.2.1 glob: 7.2.3 graceful-fs: 4.2.11 - http-proxy: 1.18.1(debug@4.4.3(supports-color@8.1.1)) + http-proxy: 1.18.1 isbinaryfile: 4.0.10 lodash: 4.18.1 - log4js: 6.9.1(supports-color@8.1.1) + log4js: 6.9.1 mime: 2.6.0 minimatch: 3.1.5 mkdirp: 0.5.6 qjobs: 1.2.0 range-parser: 1.2.1 rimraf: 3.0.2 - socket.io: 4.8.3(supports-color@8.1.1) + socket.io: 4.8.3 source-map: 0.6.1 tmp: 0.2.7 ua-parser-js: 0.7.41 @@ -7424,13 +7365,13 @@ snapshots: strip-ansi: 7.2.0 wrap-ansi: 9.0.2 - log4js@6.9.1(supports-color@8.1.1): + log4js@6.9.1: dependencies: date-format: 4.0.14 debug: 4.4.3(supports-color@8.1.1) flatted: 3.4.2 rfdc: 1.4.1 - streamroller: 3.1.5(supports-color@8.1.1) + streamroller: 3.1.5 transitivePeerDependencies: - supports-color @@ -7996,7 +7937,7 @@ snapshots: fsevents: 2.3.3 optional: true - router@2.2.0(supports-color@8.1.1): + router@2.2.0: dependencies: debug: 4.4.3(supports-color@8.1.1) depd: 2.0.0 @@ -8045,7 +7986,7 @@ snapshots: semver@7.8.5: {} - send@1.2.1(supports-color@8.1.1): + send@1.2.1: dependencies: debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 @@ -8063,12 +8004,12 @@ snapshots: serialize-javascript@7.1.0: {} - serve-static@2.2.1(supports-color@8.1.1): + serve-static@2.2.1: dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 1.2.1(supports-color@8.1.1) + send: 1.2.1 transitivePeerDependencies: - supports-color @@ -8122,7 +8063,7 @@ snapshots: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 - socket.io-adapter@2.5.6(supports-color@8.1.1): + socket.io-adapter@2.5.6: dependencies: debug: 4.4.3(supports-color@8.1.1) ws: 8.21.3 @@ -8131,22 +8072,22 @@ snapshots: - supports-color - utf-8-validate - socket.io-parser@4.2.7(supports-color@8.1.1): + socket.io-parser@4.2.7: dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color - socket.io@4.8.3(supports-color@8.1.1): + socket.io@4.8.3: dependencies: accepts: 1.3.8 base64id: 2.0.0 cors: 2.8.6 debug: 4.4.3(supports-color@8.1.1) - engine.io: 6.6.9(supports-color@8.1.1) - socket.io-adapter: 2.5.6(supports-color@8.1.1) - socket.io-parser: 4.2.7(supports-color@8.1.1) + engine.io: 6.6.9 + socket.io-adapter: 2.5.6 + socket.io-parser: 4.2.7 transitivePeerDependencies: - bufferutil - supports-color @@ -8181,7 +8122,7 @@ snapshots: stdin-discarder@0.3.2: {} - streamroller@3.1.5(supports-color@8.1.1): + streamroller@3.1.5: dependencies: date-format: 4.0.14 debug: 4.4.3(supports-color@8.1.1) @@ -8315,13 +8256,13 @@ snapshots: media-typer: 1.1.0 mime-types: 3.0.2 - typescript-eslint@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3): + typescript-eslint@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) - '@typescript-eslint/parser': 8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.66.0(supports-color@8.1.1)(typescript@6.0.3) - '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.6.1)(supports-color@8.1.1))(supports-color@8.1.1)(typescript@6.0.3) - eslint: 10.8.0(jiti@2.6.1)(supports-color@8.1.1) + '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3))(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/parser': 8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.66.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.66.0(eslint@10.8.0(jiti@2.6.1))(typescript@6.0.3) + eslint: 10.8.0(jiti@2.6.1) typescript: 6.0.3 transitivePeerDependencies: - supports-color