diff --git a/packages/angular-persist-query-client-experimental/package.json b/packages/angular-persist-query-client-experimental/package.json index efef2362c42..644da881eb2 100644 --- a/packages/angular-persist-query-client-experimental/package.json +++ b/packages/angular-persist-query-client-experimental/package.json @@ -59,6 +59,8 @@ "@angular/platform-browser-dynamic": "^17.3.12", "@microsoft/api-extractor": "^7.47.4", "@tanstack/angular-query-experimental": "workspace:*", + "@testing-library/angular": "^17.3.2", + "@testing-library/dom": "^10.4.0", "eslint-plugin-jsdoc": "^50.2.2", "tsup": "8.0.2", "typescript": "5.3.3" diff --git a/packages/angular-persist-query-client-experimental/src/__tests__/utils.ts b/packages/angular-persist-query-client-experimental/src/__tests__/utils.ts new file mode 100644 index 00000000000..c6936976730 --- /dev/null +++ b/packages/angular-persist-query-client-experimental/src/__tests__/utils.ts @@ -0,0 +1,11 @@ +let queryKeyCount = 0 +export function queryKey(): Array { + queryKeyCount++ + return [`query_${queryKeyCount}`] +} + +export function sleep(timeout: number): Promise { + return new Promise((resolve, _reject) => { + setTimeout(resolve, timeout) + }) +} diff --git a/packages/angular-persist-query-client-experimental/src/__tests__/with-persist-query-client.test.ts b/packages/angular-persist-query-client-experimental/src/__tests__/with-persist-query-client.test.ts new file mode 100644 index 00000000000..c8c88b11292 --- /dev/null +++ b/packages/angular-persist-query-client-experimental/src/__tests__/with-persist-query-client.test.ts @@ -0,0 +1,501 @@ +import { describe, expect, test, vi } from 'vitest' +import { + injectQuery, + provideTanStackQuery, + QueryClient, +} from '@tanstack/angular-query-experimental' +import type { + PersistedClient, + Persister, +} from '@tanstack/query-persist-client-core' +import { persistQueryClientSave } from '@tanstack/query-persist-client-core' +import { Component, effect } from '@angular/core' +import { render, screen, waitFor } from '@testing-library/angular' +import { withPersistQueryClient } from '../with-persist-query-client' +import { queryKey, sleep } from './utils' + +const createMockPersister = (): Persister => { + let storedState: PersistedClient | undefined + + return { + async persistClient(persistClient: PersistedClient) { + storedState = persistClient + }, + async restoreClient() { + await sleep(10) + return storedState + }, + removeClient() { + storedState = undefined + }, + } +} + +const createMockErrorPersister = ( + removeClient: Persister['removeClient'], +): [Error, Persister] => { + const error = new Error('restore failed') + return [ + error, + { + async persistClient() { + // noop + }, + async restoreClient() { + await sleep(10) + throw error + }, + removeClient, + }, + ] +} + +describe('withPersistQueryClient', () => { + test('restores cache from persister', async () => { + const key = queryKey() + const states: Array<{ + status: string + fetchStatus: string + data: string | undefined + }> = [] + + const queryClient = new QueryClient() + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.resolve('hydrated'), + }) + + const persister = createMockPersister() + + await persistQueryClientSave({ queryClient, persister }) + + queryClient.clear() + + @Component({ + template: ` +
+

{{ state.data() }}

+

fetchStatus: {{ state.fetchStatus() }}

+
+ `, + }) + class Page { + state = injectQuery(() => ({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'fetched' + }, + })) + _ = effect(() => { + states.push({ + status: this.state.status(), + fetchStatus: this.state.fetchStatus(), + data: this.state.data(), + }) + }) + } + + render(Page, { + providers: [ + provideTanStackQuery( + queryClient, + withPersistQueryClient([{ persistOptions: { persister } }]), + ), + ], + }) + + await waitFor(() => screen.getByText('fetchStatus: idle')) + await waitFor(() => screen.getByText('hydrated')) + await waitFor(() => screen.getByText('fetched')) + + expect(states).toHaveLength(3) + + expect(states[0]).toMatchObject({ + status: 'pending', + fetchStatus: 'idle', + data: undefined, + }) + + expect(states[1]).toMatchObject({ + status: 'success', + fetchStatus: 'fetching', + data: 'hydrated', + }) + + expect(states[2]).toMatchObject({ + status: 'success', + fetchStatus: 'idle', + data: 'fetched', + }) + }) + + test.todo( + '(Write this test after injectQueries is working) should also put injectQueries into idle state', + ) + + test('should show initialData while restoring', async () => { + const key = queryKey() + const states: Array<{ + status: string + fetchStatus: string + data: string | undefined + }> = [] + + const queryClient = new QueryClient() + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.resolve('hydrated'), + }) + + const persister = createMockPersister() + + await persistQueryClientSave({ queryClient, persister }) + + queryClient.clear() + + @Component({ + template: ` +
+

{{ state.data() }}

+

fetchStatus: {{ state.fetchStatus() }}

+
+ `, + }) + class Page { + state = injectQuery(() => ({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'fetched' + }, + initialData: 'initial', + // make sure that initial data is older than the hydration data + // otherwise initialData would be newer and takes precedence + initialDataUpdatedAt: 1, + })) + _ = effect(() => { + states.push({ + status: this.state.status(), + fetchStatus: this.state.fetchStatus(), + data: this.state.data(), + }) + }) + } + + render(Page, { + providers: [ + provideTanStackQuery( + queryClient, + withPersistQueryClient([{ persistOptions: { persister } }]), + ), + ], + }) + + await waitFor(() => screen.getByText('fetched')) + + expect(states).toHaveLength(3) + + expect(states[0]).toMatchObject({ + status: 'success', + fetchStatus: 'idle', + data: 'initial', + }) + + expect(states[1]).toMatchObject({ + status: 'success', + fetchStatus: 'fetching', + data: 'hydrated', + }) + + expect(states[2]).toMatchObject({ + status: 'success', + fetchStatus: 'idle', + data: 'fetched', + }) + }) + + test('should not refetch after restoring when data is fresh', async () => { + const key = queryKey() + const states: Array<{ + status: string + fetchStatus: string + data: string | undefined + }> = [] + + const queryClient = new QueryClient() + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.resolve('hydrated'), + }) + + const persister = createMockPersister() + + await persistQueryClientSave({ queryClient, persister }) + + queryClient.clear() + + let fetched = false + + @Component({ + template: ` +
+

data: {{ state.data() ?? 'null' }}

+

fetchStatus: {{ state.fetchStatus() }}

+
+ `, + }) + class Page { + state = injectQuery(() => ({ + queryKey: key, + queryFn: async () => { + fetched = true + await sleep(10) + return 'fetched' + }, + staleTime: Infinity, + })) + _ = effect(() => { + states.push({ + status: this.state.status(), + fetchStatus: this.state.fetchStatus(), + data: this.state.data(), + }) + }) + } + + render(Page, { + providers: [ + provideTanStackQuery( + queryClient, + withPersistQueryClient([{ persistOptions: { persister } }]), + ), + ], + }) + + await waitFor(() => screen.getByText('data: null')) + await waitFor(() => screen.getByText('data: hydrated')) + + expect(states).toHaveLength(2) + + expect(fetched).toBe(false) + + expect(states[0]).toMatchObject({ + status: 'pending', + fetchStatus: 'idle', + data: undefined, + }) + + expect(states[1]).toMatchObject({ + status: 'success', + fetchStatus: 'idle', + data: 'hydrated', + }) + }) + + test('should call onSuccess after successful restoring', async () => { + const key = queryKey() + const queryClient = new QueryClient() + await queryClient.prefetchQuery({ + queryKey: key, + queryFn: () => Promise.resolve('hydrated'), + }) + + const persister = createMockPersister() + await persistQueryClientSave({ queryClient, persister }) + + queryClient.clear() + + @Component({ + template: ` +
+

{{ state.data() }}

+

fetchStatus: {{ state.fetchStatus() }}

+
+ `, + }) + class Page { + state = injectQuery(() => ({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'fetched' + }, + })) + } + + const onSuccess = vi.fn() + + render(Page, { + providers: [ + provideTanStackQuery( + queryClient, + withPersistQueryClient([ + { + persistOptions: { persister }, + onSuccess, + }, + ]), + ), + ], + }) + + expect(onSuccess).toHaveBeenCalledTimes(0) + await waitFor(() => screen.getByText('fetched')) + expect(onSuccess).toHaveBeenCalledTimes(1) + }) + + test('should remove cache after non-successful restoring', async () => { + const key = queryKey() + const onErrorMock = vi + .spyOn(console, 'error') + .mockImplementation(() => undefined) + const queryClient = new QueryClient() + const removeClient = vi.fn() + const [error, persister] = createMockErrorPersister(removeClient) + + @Component({ + template: ` +
+

{{ state.data() }}

+

fetchStatus: {{ state.fetchStatus() }}

+
+ `, + }) + class Page { + state = injectQuery(() => ({ + queryKey: key, + queryFn: async () => { + await sleep(10) + return 'fetched' + }, + })) + } + + render(Page, { + providers: [ + provideTanStackQuery( + queryClient, + withPersistQueryClient([ + { + persistOptions: { persister }, + }, + ]), + ), + ], + }) + + await waitFor(() => screen.getByText('fetched')) + expect(removeClient).toHaveBeenCalledTimes(1) + expect(onErrorMock).toHaveBeenCalledTimes(1) + expect(onErrorMock).toHaveBeenNthCalledWith(1, error) + onErrorMock.mockRestore() + }) + + test('should be able to support multiple persisters', async () => { + const key1 = queryKey() + const key2 = queryKey() + const states1: Array<{ + status: string + fetchStatus: string + data: string | undefined + }> = [] + const states2: Array<{ + status: string + fetchStatus: string + data: string | undefined + }> = [] + + const queryClient = new QueryClient() + await queryClient.prefetchQuery({ + queryKey: key1, + queryFn: () => Promise.resolve('hydrated 1'), + }) + + const persister1 = createMockPersister() + await persistQueryClientSave({ queryClient, persister: persister1 }) + queryClient.clear() + + const persister2 = createMockPersister() + await queryClient.prefetchQuery({ + queryKey: key2, + queryFn: () => Promise.resolve('hydrated 2'), + }) + await persistQueryClientSave({ queryClient, persister: persister2 }) + queryClient.clear() + + @Component({ + template: ` +
+

{{ query1.data() }}

+

fetchStatus: {{ query1.fetchStatus() }}

+
+
+

{{ query2.data() }}

+

fetchStatus: {{ query2.fetchStatus() }}

+
+ `, + }) + class Page { + query1 = injectQuery(() => ({ + queryKey: key1, + queryFn: async () => { + await sleep(10) + return 'fetched 1' + }, + })) + query2 = injectQuery(() => ({ + queryKey: key2, + queryFn: async () => { + await sleep(10) + return 'fetched 2' + }, + })) + + _ = effect(() => { + states1.push({ + status: this.query1.status(), + fetchStatus: this.query1.fetchStatus(), + data: this.query1.data(), + }) + states2.push({ + status: this.query2.status(), + fetchStatus: this.query2.fetchStatus(), + data: this.query2.data(), + }) + }) + } + + const onSuccess1 = vi.fn() + const onSuccess2 = vi.fn() + + render(Page, { + providers: [ + provideTanStackQuery( + queryClient, + withPersistQueryClient([ + { + persistOptions: { + persister: persister1, + }, + onSuccess: onSuccess1, + }, + { + persistOptions: { + persister: persister2, + }, + onSuccess: onSuccess2, + }, + ]), + ), + ], + }) + + expect(onSuccess1).toHaveBeenCalledTimes(0) + expect(onSuccess2).toHaveBeenCalledTimes(0) + await waitFor(() => screen.getByText('fetched 1')) + await waitFor(() => screen.getByText('fetched 2')) + expect(onSuccess1).toHaveBeenCalledTimes(1) + expect(onSuccess2).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/angular-persist-query-client-experimental/src/test-setup.ts b/packages/angular-persist-query-client-experimental/src/test-setup.ts new file mode 100644 index 00000000000..cb5fd340f3d --- /dev/null +++ b/packages/angular-persist-query-client-experimental/src/test-setup.ts @@ -0,0 +1,12 @@ +import '@analogjs/vite-plugin-angular/setup-vitest' + +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting, +} from '@angular/platform-browser-dynamic/testing' +import { getTestBed } from '@angular/core/testing' + +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting(), +) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8d12ee585e2..9018cd6abd3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -162,7 +162,7 @@ importers: devDependencies: '@angular-devkit/build-angular': specifier: ^17.3.8 - version: 17.3.8(@angular/compiler-cli@17.3.12(@angular/compiler@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(typescript@5.3.3))(@types/express@4.17.21)(@types/node@22.7.4)(chokidar@3.6.0)(html-webpack-plugin@5.6.0(webpack@5.90.3(esbuild@0.20.1)))(ng-packagr@17.3.0(@angular/compiler-cli@17.3.12(@angular/compiler@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(typescript@5.3.3))(tailwindcss@3.4.7)(tslib@2.7.0)(typescript@5.3.3))(tailwindcss@3.4.7)(typescript@5.3.3) + version: 17.3.8(@angular/compiler-cli@17.3.12(@angular/compiler@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(typescript@5.3.3))(@types/express@4.17.21)(@types/node@22.7.4)(chokidar@3.6.0)(html-webpack-plugin@5.6.0(webpack@5.90.3(esbuild@0.21.5)))(ng-packagr@17.3.0(@angular/compiler-cli@17.3.12(@angular/compiler@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(typescript@5.3.3))(tailwindcss@3.4.7)(tslib@2.7.0)(typescript@5.3.3))(tailwindcss@3.4.7)(typescript@5.3.3) '@angular/cli': specifier: ^17.3.8 version: 17.3.8(chokidar@3.6.0) @@ -2041,6 +2041,12 @@ importers: '@tanstack/angular-query-experimental': specifier: workspace:* version: link:../angular-query-experimental + '@testing-library/angular': + specifier: ^17.3.2 + version: 17.3.2(@angular/common@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(rxjs@7.8.1))(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(@angular/platform-browser@17.3.12(@angular/animations@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(@angular/common@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(rxjs@7.8.1))(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(@angular/router@17.3.12(@angular/common@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(rxjs@7.8.1))(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(@angular/platform-browser@17.3.12(@angular/animations@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(@angular/common@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(rxjs@7.8.1))(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(rxjs@7.8.1))(@testing-library/dom@10.4.0) + '@testing-library/dom': + specifier: ^10.4.0 + version: 10.4.0 eslint-plugin-jsdoc: specifier: ^50.2.2 version: 50.2.2(eslint@9.9.1(jiti@1.21.6)) @@ -6210,6 +6216,15 @@ packages: react: '>=16' react-dom: '>=16' + '@testing-library/angular@17.3.2': + resolution: {integrity: sha512-hieGKhopemhcvY469T/MgOX1pzufLmFz1Y21VNSOZL+ijLANJWuYZDdpwXTb4dLFrggni8pyFLqb84t0orIRRg==} + peerDependencies: + '@angular/common': '>= 17.0.0' + '@angular/core': '>= 17.0.0' + '@angular/platform-browser': '>= 17.0.0' + '@angular/router': '>= 17.0.0' + '@testing-library/dom': ^10.0.0 + '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} @@ -17285,7 +17300,7 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-devkit/build-angular@17.3.8(@angular/compiler-cli@17.3.12(@angular/compiler@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(typescript@5.3.3))(@types/express@4.17.21)(@types/node@22.7.4)(chokidar@3.6.0)(html-webpack-plugin@5.6.0(webpack@5.90.3(esbuild@0.20.1)))(ng-packagr@17.3.0(@angular/compiler-cli@17.3.12(@angular/compiler@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(typescript@5.3.3))(tailwindcss@3.4.7)(tslib@2.7.0)(typescript@5.3.3))(tailwindcss@3.4.7)(typescript@5.3.3)': + '@angular-devkit/build-angular@17.3.8(@angular/compiler-cli@17.3.12(@angular/compiler@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(typescript@5.3.3))(@types/express@4.17.21)(@types/node@22.7.4)(chokidar@3.6.0)(html-webpack-plugin@5.6.0(webpack@5.90.3(esbuild@0.21.5)))(ng-packagr@17.3.0(@angular/compiler-cli@17.3.12(@angular/compiler@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(typescript@5.3.3))(tailwindcss@3.4.7)(tslib@2.7.0)(typescript@5.3.3))(tailwindcss@3.4.7)(typescript@5.3.3)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.1703.8(chokidar@3.6.0) @@ -17349,9 +17364,9 @@ snapshots: watchpack: 2.4.0 webpack: 5.90.3(esbuild@0.21.5) webpack-dev-middleware: 6.1.2(webpack@5.90.3(esbuild@0.20.1)) - webpack-dev-server: 4.15.1(webpack@5.90.3(esbuild@0.20.1)) + webpack-dev-server: 4.15.1(webpack@5.90.3(esbuild@0.21.5)) webpack-merge: 5.10.0 - webpack-subresource-integrity: 5.1.0(html-webpack-plugin@5.6.0(webpack@5.90.3(esbuild@0.20.1)))(webpack@5.90.3(esbuild@0.20.1)) + webpack-subresource-integrity: 5.1.0(html-webpack-plugin@5.6.0(webpack@5.90.3(esbuild@0.21.5)))(webpack@5.90.3(esbuild@0.20.1)) optionalDependencies: esbuild: 0.20.1 ng-packagr: 17.3.0(@angular/compiler-cli@17.3.12(@angular/compiler@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(typescript@5.3.3))(tailwindcss@3.4.7)(tslib@2.7.0)(typescript@5.3.3) @@ -17437,7 +17452,7 @@ snapshots: undici: 6.11.1 vite: 5.1.7(@types/node@22.7.4)(less@4.2.0)(sass@1.71.1)(terser@5.29.1) watchpack: 2.4.0 - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) webpack-dev-middleware: 6.1.2(webpack@5.90.3(esbuild@0.20.1)) webpack-dev-server: 4.15.1(webpack@5.90.3(esbuild@0.20.1)) webpack-merge: 5.10.0 @@ -17527,7 +17542,7 @@ snapshots: undici: 6.11.1 vite: 5.1.7(@types/node@22.7.4)(less@4.2.0)(sass@1.71.1)(terser@5.29.1) watchpack: 2.4.0 - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) webpack-dev-middleware: 6.1.2(webpack@5.90.3(esbuild@0.20.1)) webpack-dev-server: 4.15.1(webpack@5.90.3(esbuild@0.20.1)) webpack-merge: 5.10.0 @@ -17559,7 +17574,7 @@ snapshots: dependencies: '@angular-devkit/architect': 0.1703.8(chokidar@3.6.0) rxjs: 7.8.1 - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) webpack-dev-server: 4.15.1(webpack@5.90.3(esbuild@0.20.1)) transitivePeerDependencies: - chokidar @@ -21292,7 +21307,7 @@ snapshots: dependencies: '@angular/compiler-cli': 17.3.12(@angular/compiler@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(typescript@5.3.3) typescript: 5.3.3 - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) '@ngtools/webpack@17.3.8(@angular/compiler-cli@17.3.12(@angular/compiler@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(typescript@5.3.3))(typescript@5.3.3)(webpack@5.93.0(esbuild@0.19.12))': dependencies: @@ -22622,6 +22637,15 @@ snapshots: react: 19.0.0-rc-4c2e457c7c-20240522 react-dom: 19.0.0-rc-4c2e457c7c-20240522(react@19.0.0-rc-4c2e457c7c-20240522) + '@testing-library/angular@17.3.2(@angular/common@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(rxjs@7.8.1))(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(@angular/platform-browser@17.3.12(@angular/animations@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(@angular/common@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(rxjs@7.8.1))(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(@angular/router@17.3.12(@angular/common@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(rxjs@7.8.1))(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(@angular/platform-browser@17.3.12(@angular/animations@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(@angular/common@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(rxjs@7.8.1))(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(rxjs@7.8.1))(@testing-library/dom@10.4.0)': + dependencies: + '@angular/common': 17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(rxjs@7.8.1) + '@angular/core': 17.3.12(rxjs@7.8.1)(zone.js@0.14.8) + '@angular/platform-browser': 17.3.12(@angular/animations@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(@angular/common@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(rxjs@7.8.1))(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)) + '@angular/router': 17.3.12(@angular/common@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(rxjs@7.8.1))(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(@angular/platform-browser@17.3.12(@angular/animations@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(@angular/common@17.3.12(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8))(rxjs@7.8.1))(@angular/core@17.3.12(rxjs@7.8.1)(zone.js@0.14.8)))(rxjs@7.8.1) + '@testing-library/dom': 10.4.0 + tslib: 2.7.0 + '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.25.7 @@ -24159,7 +24183,7 @@ snapshots: '@babel/core': 7.24.0 find-cache-dir: 4.0.0 schema-utils: 4.2.0 - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) babel-plugin-add-module-exports@0.2.1: {} @@ -25167,7 +25191,7 @@ snapshots: normalize-path: 3.0.0 schema-utils: 4.2.0 serialize-javascript: 6.0.2 - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) core-js-compat@3.37.1: dependencies: @@ -25450,7 +25474,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.6.3 optionalDependencies: - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) css-loader@6.11.0(webpack@5.93.0(esbuild@0.21.5)): dependencies: @@ -27939,7 +27963,7 @@ snapshots: util.promisify: 1.0.0 webpack: 4.44.2 - html-webpack-plugin@5.6.0(webpack@5.90.3(esbuild@0.20.1)): + html-webpack-plugin@5.6.0(webpack@5.90.3(esbuild@0.21.5)): dependencies: '@types/html-minifier-terser': 6.1.0 html-minifier-terser: 6.1.0 @@ -29000,7 +29024,7 @@ snapshots: dependencies: klona: 2.0.6 less: 4.2.0 - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) less@4.2.0: dependencies: @@ -29032,7 +29056,7 @@ snapshots: dependencies: webpack-sources: 3.2.3 optionalDependencies: - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) lie@3.1.1: dependencies: @@ -29985,7 +30009,7 @@ snapshots: dependencies: schema-utils: 4.2.0 tapable: 2.2.1 - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) mini-css-extract-plugin@2.9.0(webpack@5.93.0(esbuild@0.21.5)): dependencies: @@ -31637,7 +31661,7 @@ snapshots: postcss: 8.4.35 semver: 7.6.3 optionalDependencies: - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) transitivePeerDependencies: - typescript @@ -33453,7 +33477,7 @@ snapshots: neo-async: 2.6.2 optionalDependencies: sass: 1.71.1 - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) sass@1.71.1: dependencies: @@ -33929,7 +33953,7 @@ snapshots: dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) source-map-resolve@0.5.3: dependencies: @@ -34590,6 +34614,17 @@ snapshots: optionalDependencies: esbuild: 0.19.12 + terser-webpack-plugin@5.3.10(esbuild@0.20.1)(webpack@5.90.3(esbuild@0.20.1)): + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.31.3 + webpack: 5.90.3(esbuild@0.20.1) + optionalDependencies: + esbuild: 0.20.1 + terser-webpack-plugin@5.3.10(esbuild@0.21.5)(webpack@5.90.3(esbuild@0.20.1)): dependencies: '@jridgewell/trace-mapping': 0.3.25 @@ -35887,6 +35922,15 @@ snapshots: webpack-log: 2.0.0 webpack-dev-middleware@5.3.4(webpack@5.90.3(esbuild@0.20.1)): + dependencies: + colorette: 2.0.20 + memfs: 3.5.3 + mime-types: 2.1.35 + range-parser: 1.2.1 + schema-utils: 4.2.0 + webpack: 5.90.3(esbuild@0.20.1) + + webpack-dev-middleware@5.3.4(webpack@5.90.3(esbuild@0.21.5)): dependencies: colorette: 2.0.20 memfs: 3.5.3 @@ -35912,7 +35956,7 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.2.0 optionalDependencies: - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) webpack-dev-server@3.11.1(webpack@4.44.2): dependencies: @@ -35986,6 +36030,46 @@ snapshots: spdy: 4.0.2(supports-color@6.1.0) webpack-dev-middleware: 5.3.4(webpack@5.90.3(esbuild@0.20.1)) ws: 8.18.0 + optionalDependencies: + webpack: 5.90.3(esbuild@0.20.1) + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + + webpack-dev-server@4.15.1(webpack@5.90.3(esbuild@0.21.5)): + dependencies: + '@types/bonjour': 3.5.13 + '@types/connect-history-api-fallback': 1.5.4 + '@types/express': 4.17.21 + '@types/serve-index': 1.9.4 + '@types/serve-static': 1.15.7 + '@types/sockjs': 0.3.36 + '@types/ws': 8.5.12 + ansi-html-community: 0.0.8 + bonjour-service: 1.2.1 + chokidar: 3.6.0 + colorette: 2.0.20 + compression: 1.7.4(supports-color@6.1.0) + connect-history-api-fallback: 2.0.0 + default-gateway: 6.0.3 + express: 4.19.2(supports-color@6.1.0) + graceful-fs: 4.2.11 + html-entities: 2.5.2 + http-proxy-middleware: 2.0.6(@types/express@4.17.21) + ipaddr.js: 2.2.0 + launch-editor: 2.8.0 + open: 8.4.2 + p-retry: 4.6.2 + rimraf: 3.0.2 + schema-utils: 4.2.0 + selfsigned: 2.4.1 + serve-index: 1.9.1(supports-color@6.1.0) + sockjs: 0.3.24 + spdy: 4.0.2(supports-color@6.1.0) + webpack-dev-middleware: 5.3.4(webpack@5.90.3(esbuild@0.21.5)) + ws: 8.18.0 optionalDependencies: webpack: 5.90.3(esbuild@0.21.5) transitivePeerDependencies: @@ -36071,24 +36155,24 @@ snapshots: webpack-sources@3.2.3: {} - webpack-subresource-integrity@5.1.0(html-webpack-plugin@5.6.0(webpack@5.90.3(esbuild@0.20.1)))(webpack@5.90.3(esbuild@0.20.1)): + webpack-subresource-integrity@5.1.0(html-webpack-plugin@5.6.0(webpack@5.90.3(esbuild@0.21.5)))(webpack@5.90.3(esbuild@0.20.1)): dependencies: typed-assert: 1.0.9 webpack: 5.90.3(esbuild@0.21.5) optionalDependencies: - html-webpack-plugin: 5.6.0(webpack@5.90.3(esbuild@0.20.1)) + html-webpack-plugin: 5.6.0(webpack@5.90.3(esbuild@0.21.5)) webpack-subresource-integrity@5.1.0(html-webpack-plugin@5.6.0(webpack@5.93.0(esbuild@0.19.12)))(webpack@5.90.3(esbuild@0.20.1)): dependencies: typed-assert: 1.0.9 - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) optionalDependencies: html-webpack-plugin: 5.6.0(webpack@5.93.0(esbuild@0.19.12)) webpack-subresource-integrity@5.1.0(html-webpack-plugin@5.6.0(webpack@5.93.0(esbuild@0.21.5)))(webpack@5.90.3(esbuild@0.20.1)): dependencies: typed-assert: 1.0.9 - webpack: 5.90.3(esbuild@0.21.5) + webpack: 5.90.3(esbuild@0.20.1) optionalDependencies: html-webpack-plugin: 5.6.0(webpack@5.93.0(esbuild@0.21.5)) @@ -36122,6 +36206,37 @@ snapshots: transitivePeerDependencies: - supports-color + webpack@5.90.3(esbuild@0.20.1): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.6 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/wasm-edit': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + acorn: 8.12.1 + acorn-import-assertions: 1.9.0(acorn@8.12.1) + browserslist: 4.23.2 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.1 + es-module-lexer: 1.5.4 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.10(esbuild@0.20.1)(webpack@5.90.3(esbuild@0.20.1)) + watchpack: 2.4.1 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + webpack@5.90.3(esbuild@0.21.5): dependencies: '@types/eslint-scope': 3.7.7