diff --git a/libs/catalogue/data-access/src/index.ts b/libs/catalogue/data-access/src/index.ts index d5c611885..f96119b2a 100644 --- a/libs/catalogue/data-access/src/index.ts +++ b/libs/catalogue/data-access/src/index.ts @@ -1,2 +1,2 @@ -export * from './lib/catalouge-search.service'; +export * from './lib/services'; export * from './lib/models'; diff --git a/libs/catalogue/data-access/src/lib/catalouge-search.service.ts b/libs/catalogue/data-access/src/lib/catalouge-search.service.ts deleted file mode 100644 index b8292f9c3..000000000 --- a/libs/catalogue/data-access/src/lib/catalouge-search.service.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { inject, Injectable } from '@angular/core'; -import { SearchService } from '@generated/swagger/cat-search-api'; -import { map, Observable } from 'rxjs'; -import { Item } from './models'; - -@Injectable({ providedIn: 'root' }) -export class CatalougeSearchService { - #searchService = inject(SearchService); - - searchByEans(...ean: string[]): Observable { - return this.#searchService.SearchByEAN(ean).pipe( - map((res) => { - if (res.error) { - throw new Error(res.message); - } - - return res.result as Item[]; - }), - ); - } -} diff --git a/libs/catalogue/data-access/src/lib/schemas/catalouge-search.schemas.ts b/libs/catalogue/data-access/src/lib/schemas/catalouge-search.schemas.ts new file mode 100644 index 000000000..b0bda1e9b --- /dev/null +++ b/libs/catalogue/data-access/src/lib/schemas/catalouge-search.schemas.ts @@ -0,0 +1,9 @@ +import { z } from 'zod'; + +export const SearchByTermParamsSchema = z.object({ + term: z.string().min(1, 'Search term must not be empty'), + skip: z.number().int().min(0).default(0), + take: z.number().int().min(1).max(100).default(20), +}); + +export type SearchByTermParams = z.infer; diff --git a/libs/catalogue/data-access/src/lib/schemas/index.ts b/libs/catalogue/data-access/src/lib/schemas/index.ts new file mode 100644 index 000000000..115851cf2 --- /dev/null +++ b/libs/catalogue/data-access/src/lib/schemas/index.ts @@ -0,0 +1 @@ +export * from './catalouge-search.schemas'; diff --git a/libs/catalogue/data-access/src/lib/services/catalouge-search.service.ts b/libs/catalogue/data-access/src/lib/services/catalouge-search.service.ts new file mode 100644 index 000000000..d6a841e5f --- /dev/null +++ b/libs/catalogue/data-access/src/lib/services/catalouge-search.service.ts @@ -0,0 +1,51 @@ +import { inject, Injectable } from '@angular/core'; +import { SearchService } from '@generated/swagger/cat-search-api'; +import { firstValueFrom, map, Observable } from 'rxjs'; +import { takeUntilAborted } from '@isa/common/data-access'; +import { Item } from '../models'; +import { + SearchByTermParams, + SearchByTermParamsSchema, +} from '../schemas/catalouge-search.schemas'; + +@Injectable({ providedIn: 'root' }) +export class CatalougeSearchService { + #searchService = inject(SearchService); + + searchByEans(...ean: string[]): Observable { + return this.#searchService.SearchByEAN(ean).pipe( + map((res) => { + if (res.error) { + throw new Error(res.message); + } + + return res.result as Item[]; + }), + ); + } + + async searchByTerm( + params: SearchByTermParams, + abortSignal: AbortSignal, + ): Promise { + const { term, skip, take } = SearchByTermParamsSchema.parse(params); + + const req$ = this.#searchService + .SearchSearch({ + filter: { + qs: term, + }, + skip, + take, + }) + .pipe(takeUntilAborted(abortSignal)); + + const res = await firstValueFrom(req$); + + if (res.error) { + throw new Error(res.message); + } + + return res.result as Item[]; + } +} diff --git a/libs/catalogue/data-access/src/lib/index.ts b/libs/catalogue/data-access/src/lib/services/index.ts similarity index 62% rename from libs/catalogue/data-access/src/lib/index.ts rename to libs/catalogue/data-access/src/lib/services/index.ts index 019f684af..d7506715a 100644 --- a/libs/catalogue/data-access/src/lib/index.ts +++ b/libs/catalogue/data-access/src/lib/services/index.ts @@ -1,2 +1 @@ export * from './catalouge-search.service'; -export * from './models'; diff --git a/libs/common/print/src/lib/components/print-button/print-button.component.ts b/libs/common/print/src/lib/components/print-button/print-button.component.ts index ebbbe591c..b38c95c77 100644 --- a/libs/common/print/src/lib/components/print-button/print-button.component.ts +++ b/libs/common/print/src/lib/components/print-button/print-button.component.ts @@ -84,7 +84,7 @@ export class PrintButtonComponent { directPrint: this.directPrint(), }); } catch (error) { - this.#logger.error('Print operation failed', { error }); + this.#logger.error('Print operation failed', error as Error); } this.printing.set(false); } diff --git a/libs/core/logging/README.md b/libs/core/logging/README.md index 2c3e73313..9cbb65f61 100644 --- a/libs/core/logging/README.md +++ b/libs/core/logging/README.md @@ -1,21 +1,103 @@ -# Core Logging +# @isa/core/logging -A structured, high-performance logging library for Angular applications. +A structured, high-performance logging library for Angular applications with hierarchical context support and flexible sink architecture. ## Overview -The Core Logging library provides a centralized logging service for the ISA Frontend application. It offers a structured way to log messages, errors, and other information across the application, with support for different log levels and output destinations. +The Core Logging library provides a centralized logging service for the ISA Frontend application. It offers a structured way to log messages, errors, and other information across the application, with support for different log levels, multiple output destinations, and rich contextual metadata. + +## Table of Contents + +- [Features](#features) +- [Quick Start](#quick-start) +- [Core Concepts](#core-concepts) +- [API Reference](#api-reference) +- [Configuration](#configuration) +- [Usage Examples](#usage-examples) +- [Creating Custom Sinks](#creating-custom-sinks) +- [Performance Considerations](#performance-considerations) +- [Testing](#testing) +- [Migration Guide](#migration-guide) ## Features -- Multiple log levels (trace, debug, info, warn, error) -- Configurable logging targets (console, remote server, etc.) -- Context-aware logging with metadata support -- Production/development mode detection -- Filtering capabilities based on log level or context -- Highly optimized for performance -- Type-safe APIs -- Error resilience - logging failures don't affect application behavior +- **Multiple log levels** (trace, debug, info, warn, error, off) +- **Flexible sink architecture** - Multiple output destinations (console, remote server, etc.) +- **Hierarchical context system** - Component, instance, and message-level contexts +- **Performance optimized** - Early filtering and lazy evaluation +- **Type-safe APIs** - Full TypeScript support with comprehensive interfaces +- **Error resilience** - Logging failures don't affect application behavior +- **Angular integration** - Native dependency injection support +- **Factory pattern** - Easy logger creation with `logger()` function +- **Configurable providers** - Simple configuration through Angular providers + +## Quick Start + +### 1. Install and Configure + +Add the logging provider to your application configuration: + +```typescript +// app.config.ts +import { ApplicationConfig, isDevMode } from '@angular/core'; +import { + provideLogging, + withLogLevel, + withSink, + LogLevel, + ConsoleLogSink +} from '@isa/core/logging'; + +export const appConfig: ApplicationConfig = { + providers: [ + // Other providers... + provideLogging( + withLogLevel(isDevMode() ? LogLevel.Debug : LogLevel.Warn), + withSink(ConsoleLogSink) + ) + ] +}; +``` + +### 2. Use in Components + +```typescript +import { Component } from '@angular/core'; +import { logger } from '@isa/core/logging'; + +@Component({ + selector: 'app-example', + template: '...' +}) +export class ExampleComponent { + #logger = logger(); + + ngOnInit(): void { + this.#logger.info('Component initialized'); + } + + handleUserAction(): void { + this.#logger.debug('User action triggered', () => ({ + action: 'button-click', + timestamp: Date.now() + })); + } +} +``` + +### 3. Error Handling + +```typescript +try { + await this.processData(); +} catch (error) { + this.#logger.error( + 'Data processing failed', + error as Error, + () => ({ operationId: '12345' }) + ); +} +``` ## Core Concepts @@ -40,29 +122,191 @@ Log sinks are destinations where log messages are sent. The library supports mul - Send critical errors to a monitoring service - Store logs for later analysis -## API +### Hierarchical Context System -### LoggingService +The logging system supports a powerful hierarchical context system that merges context from multiple levels: -The main service for logging functionality. +1. **Global Context** - Set during application configuration +2. **Component Context** - Provided at component/service level +3. **Instance Context** - Provided when creating logger instances +4. **Message Context** - Provided with individual log calls -#### Methods - -| Method | Description | -| ---------------------------------------------------------- | --------------------------- | -| `trace(message: string, context?: unknown)` | Logs trace information | -| `debug(message: string, context?: unknown)` | Logs debug information | -| `info(message: string, context?: unknown)` | Logs informational messages | -| `warn(message: string, context?: unknown)` | Logs warning messages | -| `error(message: string, error?: Error, context?: unknown)` | Logs error messages | - -### Logger Factory - -The recommended way to use the logging system is through the `logger()` factory function: +Contexts are merged in order, with later contexts taking precedence: ```typescript -export class MyComponent { - #logger = logger(); +// 1. Global context (app.config.ts) +provideLogging( + withContext({ app: 'ISA', version: '1.0.0' }) +) + +// 2. Component context +@Component({ + providers: [ + provideLoggerContext({ component: 'UserProfile', module: 'CRM' }) + ] +}) +export class UserProfileComponent { + // 3. Instance context + #logger = logger(() => ({ + userId: this.currentUser?.id, + sessionId: this.sessionService.id + })); + + saveProfile(): void { + // 4. Message context + this.#logger.info('Saving profile', () => ({ + profileId: this.profile.id, + changedFields: this.getChangedFields() + })); + + // Final merged context will include all levels: + // { + // app: 'ISA', + // version: '1.0.0', + // component: 'UserProfile', + // module: 'CRM', + // userId: '12345', + // sessionId: 'sess-abc', + // profileId: 'prof-456', + // changedFields: ['name', 'email'] + // } + } +} +``` + +## API Reference + +### Core Interfaces + +#### `LoggerApi` + +The main interface for logging operations returned by the `logger()` factory: + +```typescript +interface LoggerApi { + trace(message: string, context?: () => LoggerContext): void; + debug(message: string, context?: () => LoggerContext): void; + info(message: string, context?: () => LoggerContext): void; + warn(message: string, context?: () => LoggerContext): void; + error(message: string, error?: Error, context?: () => LoggerContext): void; +} +``` + +#### `LoggerContext` + +Type definition for context data passed to logging methods: + +```typescript +interface LoggerContext { + [key: string]: unknown; +} +``` + +#### `Sink` + +Interface for creating custom logging destinations: + +```typescript +interface Sink { + log( + level: LogLevel, + message: string, + context?: LoggerContext, + error?: Error + ): void; +} +``` + +### Factory Functions + +#### `logger(ctxFn?: () => LoggerContext): LoggerApi` + +Creates a logger instance with optional dynamic context: + +```typescript +// Basic usage +const basicLogger = logger(); + +// With dynamic context +const contextLogger = logger(() => ({ + userId: getCurrentUserId(), + sessionId: getSessionId() +})); +``` + +**Parameters:** +- `ctxFn` (optional): Function that returns context data to be included with each log message + +**Returns:** A `LoggerApi` instance configured with the specified context + +### Configuration Functions + +#### `provideLogging(...configs: LoggingConfigData[]): EnvironmentProviders` + +Main configuration function for setting up the logging system: + +```typescript +provideLogging( + withLogLevel(LogLevel.Debug), + withSink(ConsoleLogSink), + withContext({ app: 'MyApp' }) +) +``` + +#### `withLogLevel(level: LogLevel): LoggingConfigData` + +Sets the minimum log level for processing: + +```typescript +withLogLevel(LogLevel.Debug) // Process debug and above +withLogLevel(LogLevel.Error) // Process only errors +``` + +#### `withSink(sink: Sink | Type): LoggingConfigData` + +Adds a logging destination: + +```typescript +withSink(ConsoleLogSink) // Class reference +withSink(new CustomSink()) // Instance +``` + +#### `withSinkFn(sinkFn: SinkFn): LoggingConfigData` + +Adds a sink factory function: + +```typescript +withSinkFn(() => { + const http = inject(HttpClient); + return (level, message, context) => { + // Custom sink implementation + }; +}) +``` + +#### `withContext(context: LoggerContext): LoggingConfigData` + +Adds global context to all log messages: + +```typescript +withContext({ + app: 'ISA', + version: '1.0.0', + environment: 'production' +}) +``` + +#### `provideLoggerContext(context: LoggerContext): Provider[]` + +Provides component-level context: + +```typescript +@Component({ + providers: [ + provideLoggerContext({ component: 'UserProfile' }) + ] +}) +export class UserProfileComponent { ngOnInit(): void { this.#logger.info('Component initialized'); @@ -355,36 +599,63 @@ describe('MyService', () => { }); ``` -## Recent Changes +## Migration Guide -### Logger Factory Enhancements +### Migrating from Previous Versions -- **Context Functionality**: The `logger` factory function now accepts an optional `ctxFn` parameter. This allows additional context to be dynamically merged into log messages at runtime. -- **Enhanced Context Merging**: The `mergeContexts` function has been updated to include injector-level context, component-level context, and message-specific context. This ensures a more comprehensive and flexible logging context. - -#### Example Usage +#### Context Parameter Changes +**Old API:** ```typescript -import { logger } from '@isa/core/logging'; - -@Component({ - selector: 'app-example', - template: '...' -}) -export class ExampleComponent { - #logger = logger(() => ({ userId: '12345' })); - - ngOnInit(): void { - this.#logger.info('Component initialized'); - } - - performAction(): void { - this.#logger.debug('Action performed', { action: 'example' }); - } -} +// Previous version - context as direct parameter +logger.info('Message', { userId: '123' }); +logger.error('Error occurred', error, { operationId: '456' }); ``` -### Benefits +**New API:** +```typescript +// Current version - context as function +logger.info('Message', () => ({ userId: '123' })); +logger.error('Error occurred', error, () => ({ operationId: '456' })); +``` -- **Dynamic Context**: Add runtime-specific context to log messages without modifying the logger instance. -- **Improved Debugging**: Enhanced context merging provides richer information for troubleshooting. +#### Factory Function Updates + +**Old API:** +```typescript +// Previous version - no context support in factory +const logger = logger(); +``` + +**New API:** +```typescript +// Current version - dynamic context support +const logger = logger(() => ({ sessionId: getSessionId() })); +``` + +#### Configuration Changes + +**Old API:** +```typescript +// Previous version - direct service injection +constructor(private loggingService: LoggingService) {} +``` + +**New API:** +```typescript +// Current version - factory function +#logger = logger(); +``` + +### Breaking Changes + +1. **Context parameters** now require function wrappers for lazy evaluation +2. **Error parameter** in LoggerApi is now optional instead of unknown type +3. **Factory function** signature changed to support dynamic context + +### Migration Steps + +1. **Update context calls** - Wrap context objects in functions +2. **Replace service injection** - Use `logger()` factory instead of injecting `LoggingService` +3. **Update error handling** - Error parameter is now optional in error() method +4. **Add dynamic context** - Leverage new context functionality for better debugging diff --git a/libs/core/logging/src/lib/console-log.sink.ts b/libs/core/logging/src/lib/console-log.sink.ts index 69df00221..33676676f 100644 --- a/libs/core/logging/src/lib/console-log.sink.ts +++ b/libs/core/logging/src/lib/console-log.sink.ts @@ -1,23 +1,53 @@ import { Injectable } from '@angular/core'; import { LogLevel } from './log-level.enum'; -import { Sink } from './logging.types'; +import { Sink, LoggerContext } from './logging.types'; /** * A sink implementation that outputs logs to the browser console. + * This is the primary logging destination for development environments. + * + * The sink maps each log level to the appropriate console method: + * - Trace → console.trace (with stack trace) + * - Debug → console.debug + * - Info → console.info + * - Warn → console.warn + * - Error → console.error + * + * Context data and error objects are passed as additional arguments to provide + * rich debugging information in the browser developer tools. + * + * @example + * ```typescript + * // Configure in app initialization + * provideLogging( + * withLogLevel(LogLevel.Debug), + * withSink(ConsoleLogSink) + * ); + * ``` */ @Injectable() export class ConsoleLogSink implements Sink { /** * Logs a message to the browser console with the appropriate log level. - * @param level The log level. - * @param message The log message. - * @param context Optional context data. - * @param error Optional error object. + * The console method used depends on the log level, and context/error data + * is passed as additional arguments for improved debugging experience. + * + * @param level - The severity level of the log message + * @param message - The main log message content + * @param context - Optional structured data or metadata about the log event + * @param error - Optional error object when logging errors + * + * @example + * ```typescript + * const sink = new ConsoleLogSink(); + * sink.log(LogLevel.Info, 'User logged in', { userId: '123' }); + * sink.log(LogLevel.Error, 'API failed', { endpoint: '/users' }, new Error('Network error')); + * ``` */ log( level: LogLevel, message: string, - context?: unknown, + context?: LoggerContext, error?: Error, ): void { switch (level) { diff --git a/libs/core/logging/src/lib/logging.types.ts b/libs/core/logging/src/lib/logging.types.ts index 04c8c130c..a626f4fea 100644 --- a/libs/core/logging/src/lib/logging.types.ts +++ b/libs/core/logging/src/lib/logging.types.ts @@ -133,10 +133,10 @@ export interface LoggerApi { * Use for error conditions that affect functionality. * * @param message - The error message to log - * @param error - Any error object or value that caused this error condition + * @param error - Optional error object that caused this error condition * @param context - Optional context data associated with the error */ - error(message: string, error: unknown, context?: () => LoggerContext): void; + error(message: string, error?: Error, context?: () => LoggerContext): void; } /** diff --git a/libs/oms/data-access/src/lib/stores/return-task-list.store.ts b/libs/oms/data-access/src/lib/stores/return-task-list.store.ts index 1e4fb821a..e9dd25670 100644 --- a/libs/oms/data-access/src/lib/stores/return-task-list.store.ts +++ b/libs/oms/data-access/src/lib/stores/return-task-list.store.ts @@ -90,7 +90,7 @@ export const ReturnTaskListStore = signalStore( * @param error - The error that occurred during the fetch operation */ fetchError(id: number, error: unknown) { - logService.error('Error fetching task list items', error); + logService.error('Error fetching task list items', error as Error); patchState( store, updateEntity({ diff --git a/package-lock.json b/package-lock.json index 3e0881793..ed6eca63a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ "zone.js": "~0.15.0" }, "devDependencies": { - "@angular-devkit/build-angular": "20.0.2", + "@angular-devkit/build-angular": "20.0.3", "@angular-devkit/core": "20.0.2", "@angular-devkit/schematics": "20.0.2", "@angular/cli": "20.0.2", @@ -104,8 +104,8 @@ "typescript-eslint": "^8.19.0" }, "engines": { - "node": ">=22.13.0", - "npm": ">=11.0.0" + "node": ">=22.0.0", + "npm": ">=10.0.0" }, "optionalDependencies": { "@esbuild/linux-x64": "^0.25.5" @@ -161,17 +161,16 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "20.0.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-20.0.2.tgz", - "integrity": "sha512-IvIyWSat0W9ZlTn6Hd6xCoMal49JJmrml3oV2+dYFadt0wFcxZ5ygIF9pRymLWBHEjCUhuZzBCAD3gKgZXPMHA==", + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-20.0.3.tgz", + "integrity": "sha512-KJjSoJv3tjx8PaZ6NW12fBjYM4QMjlpZ+ep6Jc6Y6k0brUpVX8bC5+ZBvKB7VzNi2REB61JM573foY2Uvi0fQw==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2000.2", - "@angular-devkit/build-webpack": "0.2000.2", - "@angular-devkit/core": "20.0.2", - "@angular/build": "20.0.2", + "@angular-devkit/architect": "0.2000.3", + "@angular-devkit/build-webpack": "0.2000.3", + "@angular-devkit/core": "20.0.3", + "@angular/build": "20.0.3", "@babel/core": "7.27.1", "@babel/generator": "7.27.1", "@babel/helper-annotate-as-pure": "7.27.1", @@ -182,7 +181,7 @@ "@babel/preset-env": "7.27.2", "@babel/runtime": "7.27.1", "@discoveryjs/json-ext": "0.6.3", - "@ngtools/webpack": "20.0.2", + "@ngtools/webpack": "20.0.3", "@vitejs/plugin-basic-ssl": "2.0.0", "ansi-colors": "4.1.3", "autoprefixer": "10.4.21", @@ -238,7 +237,7 @@ "@angular/platform-browser": "^20.0.0", "@angular/platform-server": "^20.0.0", "@angular/service-worker": "^20.0.0", - "@angular/ssr": "^20.0.2", + "@angular/ssr": "^20.0.3", "@web/test-runner": "^0.20.0", "browser-sync": "^3.0.2", "jest": "^29.5.0", @@ -294,6 +293,48 @@ } } }, + "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { + "version": "0.2000.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2000.3.tgz", + "integrity": "sha512-37S4dzlwB3C8gnBlwxjjvNUqwSeKnDe2j1XWg7sj94kbg/jLJV0Db/Dvb7zJjKher6Ed1Bnj3pMOM206ALJW2A==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "20.0.3", + "rxjs": "7.8.2" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.0.3.tgz", + "integrity": "sha512-XgEIbIky0pMtJSomHRaf16BT1jzJNQCm2geNZ642n3cj8fYLm4jHJX/r738kIfbHWoWXT/hlTmVgIH9TdQPicA==", + "dev": true, + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.2", + "source-map": "0.7.4" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, "node_modules/@angular-devkit/build-angular/node_modules/ansi-regex": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", @@ -655,13 +696,12 @@ } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.2000.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2000.2.tgz", - "integrity": "sha512-kFqvZNOcU1zwwMRG3Kh7V661HWgtt9G3+xId9A2VNXvW0ZfuVZmgfaR8TLEmtCSrQ4KHGGQmK0zn2lPHttY5HA==", + "version": "0.2000.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2000.3.tgz", + "integrity": "sha512-6ZpShG/avZDDuY/LzRxHrSqDE25QsniLYs4Gf+32NFsoPM6hCSgpUpQRX2NtL592X3gDMykUj30TZoGf62zX2w==", "dev": true, - "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.2000.2", + "@angular-devkit/architect": "0.2000.3", "rxjs": "7.8.2" }, "engines": { @@ -674,6 +714,48 @@ "webpack-dev-server": "^5.0.2" } }, + "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { + "version": "0.2000.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2000.3.tgz", + "integrity": "sha512-37S4dzlwB3C8gnBlwxjjvNUqwSeKnDe2j1XWg7sj94kbg/jLJV0Db/Dvb7zJjKher6Ed1Bnj3pMOM206ALJW2A==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "20.0.3", + "rxjs": "7.8.2" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.0.3.tgz", + "integrity": "sha512-XgEIbIky0pMtJSomHRaf16BT1jzJNQCm2geNZ642n3cj8fYLm4jHJX/r738kIfbHWoWXT/hlTmVgIH9TdQPicA==", + "dev": true, + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.2", + "source-map": "0.7.4" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, "node_modules/@angular-devkit/core": { "version": "20.0.2", "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.0.2.tgz", @@ -1108,14 +1190,13 @@ } }, "node_modules/@angular/build": { - "version": "20.0.2", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.0.2.tgz", - "integrity": "sha512-nxha/dncAwEbY0nkgDWeiWSi+MSCJBuQbFf5bjTZ+pu0fS+5SOQllZKzZE9H+dms/JsLHm2YmPiScIYVvUenDw==", + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.0.3.tgz", + "integrity": "sha512-xA5eTGop85SI/+hfiOSJR/xI1w1NK3qylpEZ277YRaw8Ikh7r1DKPJOMGBfXNd8QsZYBSWGHA8SXvCmOh/hvLQ==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2000.2", + "@angular-devkit/architect": "0.2000.3", "@babel/core": "7.27.1", "@babel/helper-annotate-as-pure": "7.27.1", "@babel/helper-split-export-declaration": "7.24.7", @@ -1157,7 +1238,7 @@ "@angular/platform-browser": "^20.0.0", "@angular/platform-server": "^20.0.0", "@angular/service-worker": "^20.0.0", - "@angular/ssr": "^20.0.2", + "@angular/ssr": "^20.0.3", "karma": "^6.4.0", "less": "^4.2.0", "ng-packagr": "^20.0.0", @@ -1206,12 +1287,53 @@ } } }, + "node_modules/@angular/build/node_modules/@angular-devkit/architect": { + "version": "0.2000.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2000.3.tgz", + "integrity": "sha512-37S4dzlwB3C8gnBlwxjjvNUqwSeKnDe2j1XWg7sj94kbg/jLJV0Db/Dvb7zJjKher6Ed1Bnj3pMOM206ALJW2A==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "20.0.3", + "rxjs": "7.8.2" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/build/node_modules/@angular-devkit/core": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.0.3.tgz", + "integrity": "sha512-XgEIbIky0pMtJSomHRaf16BT1jzJNQCm2geNZ642n3cj8fYLm4jHJX/r738kIfbHWoWXT/hlTmVgIH9TdQPicA==", + "dev": true, + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.2", + "source-map": "0.7.4" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^4.0.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, "node_modules/@angular/build/node_modules/@inquirer/confirm": { "version": "5.1.10", "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.10.tgz", "integrity": "sha512-FxbQ9giWxUWKUk2O5XZ6PduVnH2CZ/fmMKMBkH71MHJvWr7WL5AHKevhzF1L5uYWB2P548o1RzVxrNd3dpmk6g==", "dev": true, - "license": "MIT", "dependencies": { "@inquirer/core": "^10.1.11", "@inquirer/type": "^3.0.6" @@ -1233,7 +1355,6 @@ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", "dev": true, - "license": "MIT", "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" @@ -1250,7 +1371,6 @@ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "dev": true, - "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -1872,27 +1992,6 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@asamuzakjp/css-color": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", - "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@csstools/css-calc": "^2.1.3", - "@csstools/css-color-parser": "^3.0.9", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "lru-cache": "^10.4.3" - } - }, - "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -1986,7 +2085,6 @@ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.1.tgz", "integrity": "sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow==", "dev": true, - "license": "MIT", "dependencies": { "@babel/types": "^7.27.1" }, @@ -2257,7 +2355,6 @@ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", "dev": true, - "license": "MIT", "dependencies": { "@babel/types": "^7.24.7" }, @@ -3896,121 +3993,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@csstools/color-helpers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", - "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", - "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.0.10", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", - "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", - "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", - "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/@discoveryjs/json-ext": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", @@ -5541,7 +5523,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -5851,7 +5832,6 @@ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -6059,11 +6039,10 @@ } }, "node_modules/@ngtools/webpack": { - "version": "20.0.2", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-20.0.2.tgz", - "integrity": "sha512-Thpk7LR4cpM90sjGXdvrM+HnUFvHzNJn2h0U6/Ow5ez+OipQDYKoFm1bisk2K/B6zGI7sGavXUlZagoTOXucUw==", + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-20.0.3.tgz", + "integrity": "sha512-LVI3N45iBqrXxZgRpYOyGzOxssXnCckKEOSeqmvus1IJ1rN6nz+q4Pq428lDrb0KQOkRIt7iR/jQLD1RpcTHRw==", "dev": true, - "license": "MIT", "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", @@ -9270,6 +9249,15 @@ "@testing-library/dom": ">=7.21.4" } }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/@trysound/sax": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", @@ -10095,7 +10083,6 @@ "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.0.0.tgz", "integrity": "sha512-gc9Tjg8bUxBVSTzeWT3Njc0Cl3PakHFKdNfABnZWiUgbxqmHDEn7uECv3fHVylxoYgNzAcmU7ZrILz+BwSo3sA==", "dev": true, - "license": "MIT", "engines": { "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, @@ -10369,6 +10356,13 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true + }, "node_modules/abbrev": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz", @@ -10418,6 +10412,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -11382,7 +11386,6 @@ "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.3.4.tgz", "integrity": "sha512-NmzN1zN1cvGccXFyZ73335+ASXwBlVWcUPssiUDIlFdfyatHPRRufjCd5w8oPaQPvVnf9ELklaCGb1gi9FBwIw==", "dev": true, - "license": "Apache-2.0", "dependencies": { "css-select": "^5.1.0", "css-what": "^6.1.0", @@ -11402,7 +11405,6 @@ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -11419,7 +11421,6 @@ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, - "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -11434,7 +11435,6 @@ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" }, @@ -11450,7 +11450,6 @@ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -11465,7 +11464,6 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -11485,7 +11483,6 @@ "url": "https://github.com/sponsors/fb55" } ], - "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", @@ -11498,7 +11495,6 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "dev": true, - "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -13181,19 +13177,11 @@ "dev": true, "license": "CC0-1.0" }, - "node_modules/cssstyle": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.4.0.tgz", - "integrity": "sha512-W0Y2HOXlPkb2yaKrCVRjinYKciu/qSLEmK0K9mcfDei3zwlnHFEHAs/Du3cIRwPqY+J4JsiBzUjoHyc8RsJ03A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@asamuzakjp/css-color": "^3.2.0", - "rrweb-cssom": "^0.8.0" - }, - "engines": { - "node": ">=18" - } + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true }, "node_modules/csstype": { "version": "3.1.3", @@ -14110,6 +14098,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/eslint": { "version": "9.28.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz", @@ -17771,6 +17790,77 @@ } } }, + "node_modules/jest-environment-jsdom/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/jest-environment-jsdom/node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-jsdom/node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/jest-environment-jsdom/node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-environment-jsdom/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jest-environment-jsdom/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/jest-environment-jsdom/node_modules/jest-mock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", @@ -17786,6 +17876,127 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/jest-environment-jsdom/node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-jsdom/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-environment-jsdom/node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jest-environment-jsdom/node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-environment-jsdom/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-environment-jsdom/node_modules/ws": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/jest-environment-jsdom/node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/jest-environment-node": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", @@ -18698,108 +18909,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsdom": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz", - "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssstyle": "^4.2.1", - "data-urls": "^5.0.0", - "decimal.js": "^10.4.3", - "form-data": "^4.0.1", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.16", - "parse5": "^7.2.1", - "rrweb-cssom": "^0.8.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.0.0", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.0", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsdom/node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-encoding": "^3.1.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/jsdom/node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/jsdom/node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/jsdom/node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -19397,7 +19506,6 @@ "integrity": "sha512-MgJocUI6QEiSXQBFWLeyo1R7eQj8Rke5dlPxX0KFwli8/bsCxpM/KbXO5y0qmV/5llQ3wpneDWcTYxa+4vn8iQ==", "dev": true, "hasInstallScript": true, - "license": "MIT", "optional": true, "dependencies": { "msgpackr": "^1.11.2", @@ -19424,7 +19532,6 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/loader-runner": { @@ -20263,7 +20370,6 @@ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=10" } @@ -20279,7 +20385,6 @@ "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.4.tgz", "integrity": "sha512-uaff7RG9VIC4jacFW9xzL3jc0iM32DNHe4jYVycBcjUePT/Klnfj7pqtWJt9khvDFizmjN2TlYniYmSS2LIaZg==", "dev": true, - "license": "MIT", "optional": true, "optionalDependencies": { "msgpackr-extract": "^3.0.2" @@ -20291,7 +20396,6 @@ "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", "dev": true, "hasInstallScript": true, - "license": "MIT", "optional": true, "dependencies": { "node-gyp-build-optional-packages": "5.2.2" @@ -20446,7 +20550,6 @@ "resolved": "https://registry.npmjs.org/ng-packagr/-/ng-packagr-20.0.0.tgz", "integrity": "sha512-p4pKkeulFi8wIOE2oLCPuzGLWE3uCNdrovSHGK5/w3+eA3l6DZmdEcvXM8YrEhbi253aB3yp4nKipc1OlK7hvQ==", "dev": true, - "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.3.0", "@rollup/plugin-json": "^6.1.0", @@ -20535,7 +20638,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.0.tgz", "integrity": "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==", "dev": true, - "license": "MIT", "engines": { "node": ">=20" } @@ -20571,7 +20673,6 @@ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", "dev": true, - "license": "MIT", "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" @@ -20588,7 +20689,6 @@ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "dev": true, - "license": "MIT", "engines": { "node": ">=12" }, @@ -20818,7 +20918,6 @@ "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", "dev": true, - "license": "MIT", "optional": true, "dependencies": { "detect-libc": "^2.0.1" @@ -21651,7 +21750,6 @@ "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.3.tgz", "integrity": "sha512-oGFr3T+pYdTGJ+YFEILMpS3es+GiIbs9h/XQrclBXUtd44ey7XwfsMzM31f64I1SQOawDoDr/D823kNCADI8TA==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/os-tmpdir": { @@ -22496,8 +22594,7 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/postcss-merge-longhand": { "version": "6.0.5", @@ -24124,13 +24221,6 @@ "dev": true, "license": "MIT" }, - "node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "license": "MIT" - }, "node_modules/run-applescript": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", @@ -26444,19 +26534,6 @@ "node": ">= 4.0.0" } }, - "node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/tree-dump": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.3.tgz", @@ -27169,7 +27246,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dev": true, - "license": "MIT", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -27314,7 +27390,6 @@ "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", "dev": true, - "license": "MIT", "optional": true }, "node_modules/webidl-conversions": { @@ -27749,30 +27824,6 @@ "node": ">=12" } }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -28021,16 +28072,6 @@ "dev": true, "license": "MIT" }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18" - } - }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", diff --git a/package.json b/package.json index 208a2e608..5b82c4260 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "zone.js": "~0.15.0" }, "devDependencies": { - "@angular-devkit/build-angular": "20.0.2", + "@angular-devkit/build-angular": "20.0.3", "@angular-devkit/core": "20.0.2", "@angular-devkit/schematics": "20.0.2", "@angular/cli": "20.0.2", @@ -101,7 +101,7 @@ "jest-preset-angular": "14.6.0", "jsonc-eslint-parser": "^2.1.0", "ng-mocks": "14.13.5", - "ng-packagr": "^20.0.0", + "ng-packagr": "20.0.1", "ng-swagger-gen": "^2.3.1", "nx": "21.2.0", "postcss": "^8.5.3", @@ -119,12 +119,7 @@ "@esbuild/linux-x64": "^0.25.5" }, "engines": { - "node": ">=22.13.0", - "npm": ">=11.0.0" - }, - "overrides": { - "jest-environment-jsdom": { - "jsdom": "26.0.0" - } + "node": ">=22.0.0", + "npm": ">=10.0.0" } }