Merge branch 'dspace-8_x' into Issue/4201-DS-8

This commit is contained in:
Oscar Chacón
2025-05-09 13:35:40 -06:00
committed by GitHub
13 changed files with 150 additions and 107 deletions

View File

@@ -66,7 +66,7 @@
"@angular/platform-browser-dynamic": "^17.3.11", "@angular/platform-browser-dynamic": "^17.3.11",
"@angular/platform-server": "^17.3.11", "@angular/platform-server": "^17.3.11",
"@angular/router": "^17.3.11", "@angular/router": "^17.3.11",
"@angular/ssr": "^17.3.16", "@angular/ssr": "^17.3.17",
"@babel/runtime": "7.27.1", "@babel/runtime": "7.27.1",
"@kolkov/ngx-gallery": "^2.0.1", "@kolkov/ngx-gallery": "^2.0.1",
"@ng-bootstrap/ng-bootstrap": "^11.0.0", "@ng-bootstrap/ng-bootstrap": "^11.0.0",
@@ -125,14 +125,14 @@
}, },
"devDependencies": { "devDependencies": {
"@angular-builders/custom-webpack": "~17.0.2", "@angular-builders/custom-webpack": "~17.0.2",
"@angular-devkit/build-angular": "^17.3.16", "@angular-devkit/build-angular": "^17.3.17",
"@angular-eslint/builder": "17.5.3", "@angular-eslint/builder": "17.5.3",
"@angular-eslint/bundled-angular-compiler": "17.5.3", "@angular-eslint/bundled-angular-compiler": "17.5.3",
"@angular-eslint/eslint-plugin": "17.5.3", "@angular-eslint/eslint-plugin": "17.5.3",
"@angular-eslint/eslint-plugin-template": "17.5.3", "@angular-eslint/eslint-plugin-template": "17.5.3",
"@angular-eslint/schematics": "17.5.3", "@angular-eslint/schematics": "17.5.3",
"@angular-eslint/template-parser": "17.5.3", "@angular-eslint/template-parser": "17.5.3",
"@angular/cli": "^17.3.16", "@angular/cli": "^17.3.17",
"@angular/compiler-cli": "^17.3.11", "@angular/compiler-cli": "^17.3.11",
"@angular/language-service": "^17.3.11", "@angular/language-service": "^17.3.11",
"@cypress/schematic": "^1.5.0", "@cypress/schematic": "^1.5.0",

View File

@@ -16,12 +16,7 @@
</ng-container> </ng-container>
</div> </div>
<ng-template #showThumbnail> <ng-template #showThumbnail>
<ds-media-viewer-image *ngIf="mediaOptions.image && mediaOptions.video" <ds-thumbnail [thumbnail]="(thumbnailsRD$ | async)?.payload?.page[0]">
[image]="(thumbnailsRD$ | async)?.payload?.page[0]?._links.content.href || thumbnailPlaceholder"
[preview]="false"
></ds-media-viewer-image>
<ds-thumbnail *ngIf="!(mediaOptions.image && mediaOptions.video)"
[thumbnail]="(thumbnailsRD$ | async)?.payload?.page[0]">
</ds-thumbnail> </ds-thumbnail>
</ng-template> </ng-template>
</ng-container> </ng-container>

View File

@@ -1,4 +1,7 @@
import { NO_ERRORS_SCHEMA } from '@angular/core'; import {
NO_ERRORS_SCHEMA,
PLATFORM_ID,
} from '@angular/core';
import { import {
ComponentFixture, ComponentFixture,
TestBed, TestBed,
@@ -14,7 +17,9 @@ import { of as observableOf } from 'rxjs';
import { AuthService } from '../../core/auth/auth.service'; import { AuthService } from '../../core/auth/auth.service';
import { BitstreamDataService } from '../../core/data/bitstream-data.service'; import { BitstreamDataService } from '../../core/data/bitstream-data.service';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { Bitstream } from '../../core/shared/bitstream.model'; import { Bitstream } from '../../core/shared/bitstream.model';
import { FileService } from '../../core/shared/file.service';
import { MediaViewerItem } from '../../core/shared/media-viewer-item.model'; import { MediaViewerItem } from '../../core/shared/media-viewer-item.model';
import { MetadataFieldWrapperComponent } from '../../shared/metadata-field-wrapper/metadata-field-wrapper.component'; import { MetadataFieldWrapperComponent } from '../../shared/metadata-field-wrapper/metadata-field-wrapper.component';
import { AuthServiceMock } from '../../shared/mocks/auth.service.mock'; import { AuthServiceMock } from '../../shared/mocks/auth.service.mock';
@@ -31,6 +36,9 @@ import { MediaViewerComponent } from './media-viewer.component';
describe('MediaViewerComponent', () => { describe('MediaViewerComponent', () => {
let comp: MediaViewerComponent; let comp: MediaViewerComponent;
let fixture: ComponentFixture<MediaViewerComponent>; let fixture: ComponentFixture<MediaViewerComponent>;
let authService;
let authorizationService;
let fileService;
const mockBitstream: Bitstream = Object.assign(new Bitstream(), { const mockBitstream: Bitstream = Object.assign(new Bitstream(), {
sizeBytes: 10201, sizeBytes: 10201,
@@ -55,7 +63,7 @@ describe('MediaViewerComponent', () => {
'dc.title': [ 'dc.title': [
{ {
language: null, language: null,
value: 'test_word.docx', value: 'test_image.jpg',
}, },
], ],
}, },
@@ -73,6 +81,15 @@ describe('MediaViewerComponent', () => {
); );
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
authService = jasmine.createSpyObj('AuthService', {
isAuthenticated: observableOf(true),
});
authorizationService = jasmine.createSpyObj('AuthorizationService', {
isAuthorized: observableOf(true),
});
fileService = jasmine.createSpyObj('FileService', {
retrieveFileDownloadLink: null,
});
return TestBed.configureTestingModule({ return TestBed.configureTestingModule({
imports: [ imports: [
TranslateModule.forRoot({ TranslateModule.forRoot({
@@ -88,6 +105,10 @@ describe('MediaViewerComponent', () => {
MetadataFieldWrapperComponent, MetadataFieldWrapperComponent,
], ],
providers: [ providers: [
{ provide: AuthService, useValue: authService },
{ provide: AuthorizationDataService, useValue: authorizationService },
{ provide: FileService, useValue: fileService },
{ provide: PLATFORM_ID, useValue: 'browser' },
{ provide: BitstreamDataService, useValue: bitstreamDataService }, { provide: BitstreamDataService, useValue: bitstreamDataService },
{ provide: ThemeService, useValue: getMockThemeService() }, { provide: ThemeService, useValue: getMockThemeService() },
{ provide: AuthService, useValue: new AuthServiceMock() }, { provide: AuthService, useValue: new AuthServiceMock() },
@@ -150,9 +171,9 @@ describe('MediaViewerComponent', () => {
expect(mediaItem.thumbnail).toBe(null); expect(mediaItem.thumbnail).toBe(null);
}); });
it('should display a default, thumbnail', () => { it('should display a default thumbnail', () => {
const defaultThumbnail = fixture.debugElement.query( const defaultThumbnail = fixture.debugElement.query(
By.css('ds-media-viewer-image'), By.css('ds-thumbnail'),
); );
expect(defaultThumbnail.nativeElement).toBeDefined(); expect(defaultThumbnail.nativeElement).toBeDefined();
}); });

View File

@@ -22,6 +22,11 @@ export class UploaderOptions {
*/ */
maxFileNumber: number; maxFileNumber: number;
/**
* Impersonating user uuid
*/
impersonatingID: string;
/** /**
* The request method to use for the file upload request * The request method to use for the file upload request
*/ */

View File

@@ -47,6 +47,11 @@ import { UploaderProperties } from './uploader-properties.model';
}) })
export class UploaderComponent implements OnInit, AfterViewInit { export class UploaderComponent implements OnInit, AfterViewInit {
/**
* Header key to impersonate a user
*/
private readonly ON_BEHALF_HEADER = 'X-On-Behalf-Of';
/** /**
* The message to show when drag files on the drop zone * The message to show when drag files on the drop zone
*/ */
@@ -162,7 +167,13 @@ export class UploaderComponent implements OnInit, AfterViewInit {
item.url = this.uploader.options.url; item.url = this.uploader.options.url;
} }
// Ensure the current XSRF token is included in every upload request (token may change between items uploaded) // Ensure the current XSRF token is included in every upload request (token may change between items uploaded)
this.uploader.options.headers = [{ name: XSRF_REQUEST_HEADER, value: this.tokenExtractor.getToken() }]; // Ensure the behalf header is set if impersonating
this.uploader.options.headers = [
{ name: XSRF_REQUEST_HEADER, value: this.tokenExtractor.getToken() },
];
if (hasValue(this.uploadFilesOptions.impersonatingID)) {
this.uploader.options.headers.push({ name: this.ON_BEHALF_HEADER, value: this.uploadFilesOptions.impersonatingID });
}
this.onBeforeUpload(); this.onBeforeUpload();
this.isOverDocumentDropZone = observableOf(false); this.isOverDocumentDropZone = observableOf(false);
}; };

View File

@@ -209,6 +209,7 @@ export class SubmissionFormComponent implements OnChanges, OnDestroy {
distinctUntilChanged()) distinctUntilChanged())
.subscribe((endpointURL) => { .subscribe((endpointURL) => {
this.uploadFilesOptions.authToken = this.authService.buildAuthHeader(); this.uploadFilesOptions.authToken = this.authService.buildAuthHeader();
this.uploadFilesOptions.impersonatingID = this.authService.getImpersonateID();
this.uploadFilesOptions.url = endpointURL.concat(`/${this.submissionId}`); this.uploadFilesOptions.url = endpointURL.concat(`/${this.submissionId}`);
this.definitionId = this.submissionDefinition.name; this.definitionId = this.submissionDefinition.name;
this.submissionService.dispatchInit( this.submissionService.dispatchInit(

View File

@@ -1,15 +1,15 @@
<div class="thumbnail" [class.limit-width]="limitWidth"> <div class="thumbnail" [class.limit-width]="limitWidth">
<div *ngIf="isLoading" class="thumbnail-content outer"> <div *ngIf="(isLoading$ | async)" class="thumbnail-content outer">
<div class="inner"> <div class="inner">
<div class="centered"> <div class="centered">
<ds-loading [spinner]="true"></ds-loading> <ds-loading [spinner]="true"></ds-loading>
</div> </div>
</div> </div>
</div> </div>
<!-- don't use *ngIf="!isLoading" so the thumbnail can load in while the animation is playing --> <!-- don't use *ngIf="!(isLoading$ | async)" so the thumbnail can load in while the animation is playing -->
<img *ngIf="src !== null" class="thumbnail-content img-fluid" [ngClass]="{'d-none': isLoading}" <img *ngIf="(src$ | async) !== null" class="thumbnail-content img-fluid" [ngClass]="{'d-none': (isLoading$ | async)}"
[src]="src | dsSafeUrl" [alt]="alt | translate" (error)="errorHandler()" (load)="successHandler()"> [src]="(src$ | async) | dsSafeUrl" [alt]="alt | translate" (error)="errorHandler()" (load)="successHandler()">
<div *ngIf="src === null && !isLoading" class="thumbnail-content outer"> <div *ngIf="(src$ | async) === null && (isLoading$ | async) === false" class="thumbnail-content outer">
<div class="inner"> <div class="inner">
<div class="thumbnail-placeholder centered lead"> <div class="thumbnail-placeholder centered lead">
{{ placeholder | translate }} {{ placeholder | translate }}

View File

@@ -100,31 +100,31 @@ describe('ThumbnailComponent', () => {
describe('loading', () => { describe('loading', () => {
it('should start out with isLoading$ true', () => { it('should start out with isLoading$ true', () => {
expect(comp.isLoading).toBeTrue(); expect(comp.isLoading$.getValue()).toBeTrue();
}); });
it('should set isLoading$ to false once an image is successfully loaded', () => { it('should set isLoading$ to false once an image is successfully loaded', () => {
comp.setSrc('http://bit.stream'); comp.setSrc('http://bit.stream');
fixture.debugElement.query(By.css('img.thumbnail-content')).triggerEventHandler('load', new Event('load')); fixture.debugElement.query(By.css('img.thumbnail-content')).triggerEventHandler('load', new Event('load'));
expect(comp.isLoading).toBeFalse(); expect(comp.isLoading$.getValue()).toBeFalse();
}); });
it('should set isLoading$ to false once the src is set to null', () => { it('should set isLoading$ to false once the src is set to null', () => {
comp.setSrc(null); comp.setSrc(null);
expect(comp.isLoading).toBeFalse(); expect(comp.isLoading$.getValue()).toBeFalse();
}); });
it('should show a loading animation while isLoading$ is true', () => { it('should show a loading animation while isLoading$ is true', () => {
expect(de.query(By.css('ds-loading'))).toBeTruthy(); expect(de.query(By.css('ds-loading'))).toBeTruthy();
comp.isLoading = false; comp.isLoading$.next(false);
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.query(By.css('ds-loading'))).toBeFalsy(); expect(fixture.debugElement.query(By.css('ds-loading'))).toBeFalsy();
}); });
describe('with a thumbnail image', () => { describe('with a thumbnail image', () => {
beforeEach(() => { beforeEach(() => {
comp.src = 'https://bit.stream'; comp.src$.next('https://bit.stream');
fixture.detectChanges(); fixture.detectChanges();
}); });
@@ -133,7 +133,7 @@ describe('ThumbnailComponent', () => {
expect(img).toBeTruthy(); expect(img).toBeTruthy();
expect(img.classes['d-none']).toBeTrue(); expect(img.classes['d-none']).toBeTrue();
comp.isLoading = false; comp.isLoading$.next(false);
fixture.detectChanges(); fixture.detectChanges();
img = fixture.debugElement.query(By.css('img.thumbnail-content')); img = fixture.debugElement.query(By.css('img.thumbnail-content'));
expect(img).toBeTruthy(); expect(img).toBeTruthy();
@@ -144,14 +144,14 @@ describe('ThumbnailComponent', () => {
describe('without a thumbnail image', () => { describe('without a thumbnail image', () => {
beforeEach(() => { beforeEach(() => {
comp.src = null; comp.src$.next(null);
fixture.detectChanges(); fixture.detectChanges();
}); });
it('should only show the HTML placeholder once done loading', () => { it('should only show the HTML placeholder once done loading', () => {
expect(fixture.debugElement.query(By.css('div.thumbnail-placeholder'))).toBeFalsy(); expect(fixture.debugElement.query(By.css('div.thumbnail-placeholder'))).toBeFalsy();
comp.isLoading = false; comp.isLoading$.next(false);
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.query(By.css('div.thumbnail-placeholder'))).toBeTruthy(); expect(fixture.debugElement.query(By.css('div.thumbnail-placeholder'))).toBeTruthy();
}); });
@@ -247,14 +247,14 @@ describe('ThumbnailComponent', () => {
describe('fallback', () => { describe('fallback', () => {
describe('if there is a default image', () => { describe('if there is a default image', () => {
it('should display the default image', () => { it('should display the default image', () => {
comp.src = 'http://bit.stream'; comp.src$.next('http://bit.stream');
comp.defaultImage = 'http://default.img'; comp.defaultImage = 'http://default.img';
comp.errorHandler(); comp.errorHandler();
expect(comp.src).toBe(comp.defaultImage); expect(comp.src$.getValue()).toBe(comp.defaultImage);
}); });
it('should include the alt text', () => { it('should include the alt text', () => {
comp.src = 'http://bit.stream'; comp.src$.next('http://bit.stream');
comp.defaultImage = 'http://default.img'; comp.defaultImage = 'http://default.img';
comp.errorHandler(); comp.errorHandler();
@@ -266,10 +266,10 @@ describe('ThumbnailComponent', () => {
describe('if there is no default image', () => { describe('if there is no default image', () => {
it('should display the HTML placeholder', () => { it('should display the HTML placeholder', () => {
comp.src = 'http://default.img'; comp.src$.next('http://default.img');
comp.defaultImage = null; comp.defaultImage = null;
comp.errorHandler(); comp.errorHandler();
expect(comp.src).toBe(null); expect(comp.src$.getValue()).toBe(null);
fixture.detectChanges(); fixture.detectChanges();
const placeholder = fixture.debugElement.query(By.css('div.thumbnail-placeholder')).nativeElement; const placeholder = fixture.debugElement.query(By.css('div.thumbnail-placeholder')).nativeElement;
@@ -361,7 +361,7 @@ describe('ThumbnailComponent', () => {
it('should show the default image', () => { it('should show the default image', () => {
comp.defaultImage = 'default/image.jpg'; comp.defaultImage = 'default/image.jpg';
comp.ngOnChanges({}); comp.ngOnChanges({});
expect(comp.src).toBe('default/image.jpg'); expect(comp.src$.getValue()).toBe('default/image.jpg');
}); });
}); });
}); });
@@ -417,7 +417,7 @@ describe('ThumbnailComponent', () => {
}); });
it('should start out with isLoading$ true', () => { it('should start out with isLoading$ true', () => {
expect(comp.isLoading).toBeTrue(); expect(comp.isLoading$.getValue()).toBeTrue();
expect(de.query(By.css('ds-loading'))).toBeTruthy(); expect(de.query(By.css('ds-loading'))).toBeTruthy();
}); });

View File

@@ -11,7 +11,10 @@ import {
SimpleChanges, SimpleChanges,
} from '@angular/core'; } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { of as observableOf } from 'rxjs'; import {
BehaviorSubject,
of as observableOf,
} from 'rxjs';
import { switchMap } from 'rxjs/operators'; import { switchMap } from 'rxjs/operators';
import { AuthService } from '../core/auth/auth.service'; import { AuthService } from '../core/auth/auth.service';
@@ -55,7 +58,7 @@ export class ThumbnailComponent implements OnChanges {
/** /**
* The src attribute used in the template to render the image. * The src attribute used in the template to render the image.
*/ */
src: string = undefined; src$: BehaviorSubject<string> = new BehaviorSubject<string>(undefined);
retriedWithToken = false; retriedWithToken = false;
@@ -78,7 +81,7 @@ export class ThumbnailComponent implements OnChanges {
* Whether the thumbnail is currently loading * Whether the thumbnail is currently loading
* Start out as true to avoid flashing the alt text while a thumbnail is being loaded. * Start out as true to avoid flashing the alt text while a thumbnail is being loaded.
*/ */
isLoading = true; isLoading$: BehaviorSubject<boolean> = new BehaviorSubject(true);
constructor( constructor(
@Inject(PLATFORM_ID) private platformID: any, @Inject(PLATFORM_ID) private platformID: any,
@@ -134,7 +137,7 @@ export class ThumbnailComponent implements OnChanges {
* Otherwise, fall back to the default image or a HTML placeholder * Otherwise, fall back to the default image or a HTML placeholder
*/ */
errorHandler() { errorHandler() {
const src = this.src; const src = this.src$.getValue();
const thumbnail = this.bitstream; const thumbnail = this.bitstream;
const thumbnailSrc = thumbnail?._links?.content?.href; const thumbnailSrc = thumbnail?._links?.content?.href;
@@ -186,9 +189,22 @@ export class ThumbnailComponent implements OnChanges {
* @param src * @param src
*/ */
setSrc(src: string): void { setSrc(src: string): void {
this.src = src; // only update the src if it has changed (the parent component may fire the same one multiple times
if (src === null) { if (this.src$.getValue() !== src) {
this.isLoading = false; // every time the src changes we need to start the loading animation again, as it's possible
// that it is first set to null when the parent component initializes and then set to
// the actual value
//
// isLoading$ will be set to false by the error or success handler afterwards, except in the
// case where src is null, then we have to set it manually here (because those handlers won't
// trigger)
if (src !== null && this.isLoading$.getValue() === false) {
this.isLoading$.next(true);
}
this.src$.next(src);
if (src === null && this.isLoading$.getValue() === true) {
this.isLoading$.next(false);
}
} }
} }
@@ -196,6 +212,6 @@ export class ThumbnailComponent implements OnChanges {
* Stop the loading animation once the thumbnail is successfully loaded * Stop the loading animation once the thumbnail is successfully loaded
*/ */
successHandler() { successHandler() {
this.isLoading = false; this.isLoading$.next(false);
} }
} }

View File

@@ -28,7 +28,6 @@
::ng-deep { ::ng-deep {
.ds-menu-item, .ds-menu-toggler-wrapper { .ds-menu-item, .ds-menu-toggler-wrapper {
white-space: nowrap;
text-decoration: none; text-decoration: none;
} }

View File

@@ -1,5 +1,5 @@
<header id="main-site-header"> <header id="main-site-header">
<div class="container h-100 d-flex flex-row flex-wrap align-items-center justify-content-between gapx-3 gapy-2" id="main-site-header-container"> <div class="container h-100 d-flex flex-row align-items-center justify-content-between gapx-3 gapy-2" id="main-site-header-container">
<!-- Logo and navbar wrapper --> <!-- Logo and navbar wrapper -->
<div id="header-left" <div id="header-left"
[attr.role]="(isMobile$ | async) ? 'navigation' : 'presentation'" [attr.role]="(isMobile$ | async) ? 'navigation' : 'presentation'"
@@ -8,12 +8,12 @@
<a class="d-block my-2 my-md-0" routerLink="/home" [attr.aria-label]="'home.title' | translate" role="button" tabindex="0"> <a class="d-block my-2 my-md-0" routerLink="/home" [attr.aria-label]="'home.title' | translate" role="button" tabindex="0">
<img id="header-logo" src="assets/images/dspace-logo.svg" [attr.alt]="'menu.header.image.logo' | translate"/> <img id="header-logo" src="assets/images/dspace-logo.svg" [attr.alt]="'menu.header.image.logo' | translate"/>
</a> </a>
<nav *ngIf="(isMobile$ | async) !== true" class="navbar navbar-expand p-0 align-items-stretch align-self-stretch" id="desktop-navbar" [attr.aria-label]="'nav.main.description' | translate"> <nav *ngIf="(isMobile$ | async) !== true" class="navbar navbar-expand p-0 align-items-stretch align-self-stretch flex-grow-1 flex-shrink-1" id="desktop-navbar" [attr.aria-label]="'nav.main.description' | translate">
<ds-navbar></ds-navbar> <ds-navbar></ds-navbar>
</nav> </nav>
</div> </div>
<!-- Search bar and other menus --> <!-- Search bar and other menus -->
<div id="header-right" class="h-100 d-flex flex-row flex-nowrap justify-content-end align-items-center gapx-1 ml-auto"> <div id="header-right" class="h-100 d-flex flex-row flex-nowrap flex-shrink-0 justify-content-end align-items-center gapx-1 ml-auto">
<ds-search-navbar></ds-search-navbar> <ds-search-navbar></ds-search-navbar>
<div role="menubar" class="h-100 d-flex flex-row flex-nowrap align-items-center gapx-1"> <div role="menubar" class="h-100 d-flex flex-row flex-nowrap align-items-center gapx-1">
<ds-lang-switch></ds-lang-switch> <ds-lang-switch></ds-lang-switch>

View File

@@ -1,7 +1,7 @@
<ng-container *ngIf="(isMobile$ | async) && (isAuthenticated$ | async)"> <ng-container *ngIf="(isMobile$ | async) && (isAuthenticated$ | async)">
<ds-user-menu [inExpandableNavbar]="true"></ds-user-menu> <ds-user-menu [inExpandableNavbar]="true"></ds-user-menu>
</ng-container> </ng-container>
<div class="navbar-nav h-100 align-items-md-stretch gapx-3" role="menubar" id="main-site-navigation" [ngClass]="(isMobile$ | async) ? 'navbar-nav-mobile' : 'navbar-nav-desktop'"> <div class="navbar-nav flex-shrink-1 h-100 align-items-md-stretch gapx-3" role="menubar" id="main-site-navigation" [ngClass]="(isMobile$ | async) ? 'navbar-nav-mobile' : 'navbar-nav-desktop'">
<ng-container *ngFor="let section of (sections | async)"> <ng-container *ngFor="let section of (sections | async)">
<ng-container <ng-container
*ngComponentOutlet="(sectionMap$ | async).get(section.id)?.component; injector: (sectionMap$ | async).get(section.id)?.injector;"></ng-container> *ngComponentOutlet="(sectionMap$ | async).get(section.id)?.component; injector: (sectionMap$ | async).get(section.id)?.injector;"></ng-container>

115
yarn.lock
View File

@@ -31,12 +31,12 @@
lodash "^4.17.15" lodash "^4.17.15"
webpack-merge "^5.7.3" webpack-merge "^5.7.3"
"@angular-devkit/architect@0.1703.16", "@angular-devkit/architect@>=0.1700.0 < 0.1800.0": "@angular-devkit/architect@0.1703.17", "@angular-devkit/architect@>=0.1700.0 < 0.1800.0":
version "0.1703.16" version "0.1703.17"
resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1703.16.tgz#1088cf9909eb07d816e32572660f9358d45de900" resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1703.17.tgz#4cc8abc39d69214fd905e0300e724252dfc8b130"
integrity sha512-wuqKu20ekgzzikUTZD28dS72F6vjniZuiQ7RgAYhykmsU0z0br2tksHQvjD/auzVArtgQir1+V9wp6BN4dSdNQ== integrity sha512-LD6po8lGP2FI7WbnsSxtvpiIi+FYL0aNfteunkT+7po9jUNflBEYHA64UWNO56u7ryKNdbuiN8/TEh7FEUnmCw==
dependencies: dependencies:
"@angular-devkit/core" "17.3.16" "@angular-devkit/core" "17.3.17"
rxjs "7.8.1" rxjs "7.8.1"
"@angular-devkit/architect@^0.1202.10": "@angular-devkit/architect@^0.1202.10":
@@ -47,15 +47,15 @@
"@angular-devkit/core" "12.2.18" "@angular-devkit/core" "12.2.18"
rxjs "6.6.7" rxjs "6.6.7"
"@angular-devkit/build-angular@^17.0.0", "@angular-devkit/build-angular@^17.3.16": "@angular-devkit/build-angular@^17.0.0", "@angular-devkit/build-angular@^17.3.17":
version "17.3.16" version "17.3.17"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-17.3.16.tgz#5c9032e2a10e4eab01862c9483729622423e395f" resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-17.3.17.tgz#29d6f2611a107d2234c568b53e572ecafd9c7e3f"
integrity sha512-5JiR1NK3MOwzipAn4UmvJ8yQa6NaBtHBWbLrY0Ps6a21kHWn42C+dpvVdlXN/ZZSpEll/nzA+b77zA3kDrGlKw== integrity sha512-0kLVwjLZ5v4uIaG0K6sHJxxppS0bvjNmxHkbybU8FBW3r5MOBQh/ApsiCQKQQ8GBrQz9qSJvLJH8lsb/uR8aPQ==
dependencies: dependencies:
"@ampproject/remapping" "2.3.0" "@ampproject/remapping" "2.3.0"
"@angular-devkit/architect" "0.1703.16" "@angular-devkit/architect" "0.1703.17"
"@angular-devkit/build-webpack" "0.1703.16" "@angular-devkit/build-webpack" "0.1703.17"
"@angular-devkit/core" "17.3.16" "@angular-devkit/core" "17.3.17"
"@babel/core" "7.26.10" "@babel/core" "7.26.10"
"@babel/generator" "7.26.10" "@babel/generator" "7.26.10"
"@babel/helper-annotate-as-pure" "7.25.9" "@babel/helper-annotate-as-pure" "7.25.9"
@@ -66,7 +66,7 @@
"@babel/preset-env" "7.26.9" "@babel/preset-env" "7.26.9"
"@babel/runtime" "7.26.10" "@babel/runtime" "7.26.10"
"@discoveryjs/json-ext" "0.5.7" "@discoveryjs/json-ext" "0.5.7"
"@ngtools/webpack" "17.3.16" "@ngtools/webpack" "17.3.17"
"@vitejs/plugin-basic-ssl" "1.1.0" "@vitejs/plugin-basic-ssl" "1.1.0"
ansi-colors "4.1.3" ansi-colors "4.1.3"
autoprefixer "10.4.18" autoprefixer "10.4.18"
@@ -78,7 +78,7 @@
css-loader "6.10.0" css-loader "6.10.0"
esbuild-wasm "0.20.1" esbuild-wasm "0.20.1"
fast-glob "3.3.2" fast-glob "3.3.2"
http-proxy-middleware "2.0.7" http-proxy-middleware "2.0.8"
https-proxy-agent "7.0.4" https-proxy-agent "7.0.4"
inquirer "9.2.15" inquirer "9.2.15"
jsonc-parser "3.2.1" jsonc-parser "3.2.1"
@@ -117,12 +117,12 @@
optionalDependencies: optionalDependencies:
esbuild "0.20.1" esbuild "0.20.1"
"@angular-devkit/build-webpack@0.1703.16": "@angular-devkit/build-webpack@0.1703.17":
version "0.1703.16" version "0.1703.17"
resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1703.16.tgz#1ee821a86dea719d9b09634776c9803af24cc965" resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1703.17.tgz#7ee01a8c53f6fcaa1928392aab9d119830f7684b"
integrity sha512-ybZr+2F4siu0MztyhSkzN3lIMF0YFeyMaoTygWKjMeGMrkdj4IOAHiR+le2dQ+W4RhwEwQwkV2lvyDJzbivWhg== integrity sha512-81RJe/WFQ1QOJA9du+jK41KaaWXmEWt3frtj9eseWSr+d+Ebt0JMblzM12A70qm7LoUvG48hSiimm7GmkzV3rw==
dependencies: dependencies:
"@angular-devkit/architect" "0.1703.16" "@angular-devkit/architect" "0.1703.17"
rxjs "7.8.1" rxjs "7.8.1"
"@angular-devkit/core@12.2.18", "@angular-devkit/core@^12.2.17": "@angular-devkit/core@12.2.18", "@angular-devkit/core@^12.2.17":
@@ -137,10 +137,10 @@
rxjs "6.6.7" rxjs "6.6.7"
source-map "0.7.3" source-map "0.7.3"
"@angular-devkit/core@17.3.16", "@angular-devkit/core@^17.0.0", "@angular-devkit/core@^17.1.0": "@angular-devkit/core@17.3.17", "@angular-devkit/core@^17.0.0", "@angular-devkit/core@^17.1.0":
version "17.3.16" version "17.3.17"
resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-17.3.16.tgz#fc40e767920aded257fb1166c77f4eba44ada460" resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-17.3.17.tgz#d10dd61727408cf54321c3b22ec881c510ad784f"
integrity sha512-3Dhb/pE3c6P9bDfYLhYb0ArTFYmYSx5QgEUVMuowXJtP/3EyU7lWB2kcuiBZgScxrhRLOiMGMPgcF9jVmvovug== integrity sha512-7aNVqS3rOGsSZYAOO44xl2KURwaoOP+EJhJs+LqOGOFpok2kd8YLf4CAMUossMF4H7HsJpgKwYqGrV5eXunrpw==
dependencies: dependencies:
ajv "8.12.0" ajv "8.12.0"
ajv-formats "2.1.1" ajv-formats "2.1.1"
@@ -158,12 +158,12 @@
ora "5.4.1" ora "5.4.1"
rxjs "6.6.7" rxjs "6.6.7"
"@angular-devkit/schematics@17.3.16": "@angular-devkit/schematics@17.3.17":
version "17.3.16" version "17.3.17"
resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-17.3.16.tgz#050d67e54777b0c936a0760b3c589795769190a0" resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-17.3.17.tgz#8f6918da99efff09ab33a2d32350164751b88811"
integrity sha512-EcKBdQ02RIwYLHrExOvtrj8FXtTT/Z0IQe8maUy+YkOWjJHsjpdRBOwi3JOICyjnkopOtkkHy/bxu5VKh6rL7A== integrity sha512-ZXsIJXZm0I0dNu1BqmjfEtQhnzqoupUHHZb4GHm5NeQHBFZctQlkkNxLUU27GVeBUwFgEmP7kFgSLlMPTGSL5g==
dependencies: dependencies:
"@angular-devkit/core" "17.3.16" "@angular-devkit/core" "17.3.17"
jsonc-parser "3.2.1" jsonc-parser "3.2.1"
magic-string "0.30.8" magic-string "0.30.8"
ora "5.4.1" ora "5.4.1"
@@ -243,15 +243,15 @@
optionalDependencies: optionalDependencies:
parse5 "^7.1.2" parse5 "^7.1.2"
"@angular/cli@^17.3.16": "@angular/cli@^17.3.17":
version "17.3.16" version "17.3.17"
resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-17.3.16.tgz#a7651e4bb1e2e276ee7c9d0cdef5197aa4594502" resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-17.3.17.tgz#15464e29db9a806b52d7574f46d1deb705838fbd"
integrity sha512-cG/+aAW7z/o8Tl75U6d+pa+zygV9cvdeY/vb6ve16o4MS6Ifwbls6L51gekYGdWpLl1QnWHLbZFz7+mOa7Um2w== integrity sha512-FgOvf9q5d23Cpa7cjP1FYti/v8S1FTm8DEkW3TY8lkkoxh3isu28GFKcLD1p/XF3yqfPkPVHToOFla5QwsEgBQ==
dependencies: dependencies:
"@angular-devkit/architect" "0.1703.16" "@angular-devkit/architect" "0.1703.17"
"@angular-devkit/core" "17.3.16" "@angular-devkit/core" "17.3.17"
"@angular-devkit/schematics" "17.3.16" "@angular-devkit/schematics" "17.3.17"
"@schematics/angular" "17.3.16" "@schematics/angular" "17.3.17"
"@yarnpkg/lockfile" "1.1.0" "@yarnpkg/lockfile" "1.1.0"
ansi-colors "4.1.3" ansi-colors "4.1.3"
ini "4.1.2" ini "4.1.2"
@@ -353,10 +353,10 @@
dependencies: dependencies:
tslib "^2.3.0" tslib "^2.3.0"
"@angular/ssr@^17.3.16": "@angular/ssr@^17.3.17":
version "17.3.16" version "17.3.17"
resolved "https://registry.yarnpkg.com/@angular/ssr/-/ssr-17.3.16.tgz#323524053a0216993db13a0b0492122775d4a5b7" resolved "https://registry.yarnpkg.com/@angular/ssr/-/ssr-17.3.17.tgz#b8f2f9488f6f142ffbf19356bf7cdde9ecb76a4a"
integrity sha512-GCtOaex3DIMy0qwYqg4F3NGxiXW9a2ZXbrwF3/Aby4+8QsHH2rilhrAn6OBZjgRpz0T1m/eOuPereAX/guKezw== integrity sha512-hRszFhFPh74n17cjhbRuR0igSwHm4ssB0LgkE4A5tBgYTSoWomRM6nmiyrk10OdQHlLOTFwdTIZ3aPDM5174Ow==
dependencies: dependencies:
critters "0.0.22" critters "0.0.22"
tslib "^2.3.0" tslib "^2.3.0"
@@ -2052,10 +2052,10 @@
dependencies: dependencies:
tslib "^2.0.0" tslib "^2.0.0"
"@ngtools/webpack@17.3.16": "@ngtools/webpack@17.3.17":
version "17.3.16" version "17.3.17"
resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-17.3.16.tgz#8e1dce0e964fab6a63f279f1301046a67ec41e61" resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-17.3.17.tgz#d0206192a2c1d25371be2eb35bdde16c84c07f13"
integrity sha512-Wxtiut1o9rj3+HumyXoYWg4iCuPDEt4nf1Y9bRCo3y5evEKp0ZTO7IFPpZLaZ0JGGrpROiQErjvHdDQBOuXwWQ== integrity sha512-LaO++U8DoqV36M0YLKhubc1+NqM8fyp5DN03k1uP9GvtRchP9+7bfG+IEEZiDFkCUh9lfzi1CiGvUHrN4MYcsA==
"@ngtools/webpack@^16.2.16": "@ngtools/webpack@^16.2.16":
version "16.2.16" version "16.2.16"
@@ -2463,13 +2463,13 @@
resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8"
integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==
"@schematics/angular@17.3.16": "@schematics/angular@17.3.17":
version "17.3.16" version "17.3.17"
resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-17.3.16.tgz#f90fdb1352424f1177f2ec7953d7dcb67afe1fa2" resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-17.3.17.tgz#48dc73c8de43b257e796e8ad887235c3ba7be677"
integrity sha512-Ts/cAZmxlIL+AOLbmBylCMjXdHeqWZE2IIYmP5334tQNERSharOlKbLIz5PeESmBDGpH9KRa0wSMK5KXI5ixnQ== integrity sha512-S5HwYem5Yjeceb5OLvforNcjfTMh2qsHnTP1BAYL81XPpqeg2udjAkJjKBxCwxMZSqdCMw3ne0eKppEYTaEZ+A==
dependencies: dependencies:
"@angular-devkit/core" "17.3.16" "@angular-devkit/core" "17.3.17"
"@angular-devkit/schematics" "17.3.16" "@angular-devkit/schematics" "17.3.17"
jsonc-parser "3.2.1" jsonc-parser "3.2.1"
"@schematics/angular@^12.2.17": "@schematics/angular@^12.2.17":
@@ -6303,10 +6303,10 @@ http-proxy-agent@^7.0.0:
agent-base "^7.1.0" agent-base "^7.1.0"
debug "^4.3.4" debug "^4.3.4"
http-proxy-middleware@2.0.7: http-proxy-middleware@2.0.8:
version "2.0.7" version "2.0.8"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.7.tgz#915f236d92ae98ef48278a95dedf17e991936ec6" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.8.tgz#0c3cc655df8213caa51a365f2b69aff377f7bfbf"
integrity sha512-fgVY8AV7qU7z/MmXJ/rxwbrtQH4jBQ9m7kp3llF0liB7glmFeVZFBepQb32T3y8n8k2+AEYuMPCpinYW+/CuRA== integrity sha512-/iazaeFPmL8KLA6QB7DFAU4O5j+9y/TA0D019MbLtPuFI56VK4BXFzM6j6QS9oGpScy8IIDH4S2LHv3zg/63Bw==
dependencies: dependencies:
"@types/http-proxy" "^1.17.8" "@types/http-proxy" "^1.17.8"
http-proxy "^1.18.1" http-proxy "^1.18.1"
@@ -10661,12 +10661,7 @@ tslib@^1.8.1, tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.0, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1: tslib@^2.0.0, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.8.1:
version "2.7.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01"
integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==
tslib@^2.8.1:
version "2.8.1" version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==