From b9590fd0c8310ad8762b5794d71644a688d6205c Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Mon, 3 Nov 2025 15:35:37 +0200 Subject: [PATCH 01/76] fix(storage-item-selector): allow users to add files from google drive on addon setup --- .../storage-item-selector.component.html | 9 +++++++++ .../storage-item-selector.component.ts | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.html b/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.html index 0d860844d..d2f04b986 100644 --- a/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.html +++ b/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.html @@ -44,6 +44,15 @@

[handleFolderSelection]="handleFolderSelection" [rootFolder]="selectedStorageItem()" > + @if (selectedStorageItem() !== null) { + + } } @else {
diff --git a/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts b/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts index b5fb14896..79ea944c5 100644 --- a/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts +++ b/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts @@ -22,6 +22,7 @@ import { OnInit, output, signal, + viewChild, } from '@angular/core'; import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; @@ -60,6 +61,8 @@ import { ResourceTypeInfoDialogComponent } from '../resource-type-info-dialog/re changeDetection: ChangeDetectionStrategy.OnPush, }) export class StorageItemSelectorComponent implements OnInit { + addFilesPicker = viewChild('filePicker'); + private destroyRef = inject(DestroyRef); private customDialogService = inject(CustomDialogService); private translateService = inject(TranslateService); @@ -236,6 +239,11 @@ export class StorageItemSelectorComponent implements OnInit { handleFolderSelection = (folder: StorageItem): void => { this.selectedStorageItem.set(folder); this.hasFolderChanged.set(folder?.itemId !== this.initiallySelectedStorageItem()?.itemId); + if (this.isGoogleFilePicker()) { + setTimeout(() => { + this.addFilesPicker()?.createPicker(); + }, 1000); + } }; private updateBreadcrumbs( From 187d2b2166127e280158aa056572327b5b6a3761 Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Tue, 4 Nov 2025 15:56:46 +0200 Subject: [PATCH 02/76] fix(storage-item-selector): replase set timeout with timer --- .../storage-item-selector.component.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts b/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts index 79ea944c5..597e887e2 100644 --- a/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts +++ b/src/app/shared/components/addons/storage-item-selector/storage-item-selector.component.ts @@ -10,6 +10,8 @@ import { InputText } from 'primeng/inputtext'; import { RadioButton } from 'primeng/radiobutton'; import { Skeleton } from 'primeng/skeleton'; +import { timer } from 'rxjs'; + import { ChangeDetectionStrategy, Component, @@ -240,9 +242,9 @@ export class StorageItemSelectorComponent implements OnInit { this.selectedStorageItem.set(folder); this.hasFolderChanged.set(folder?.itemId !== this.initiallySelectedStorageItem()?.itemId); if (this.isGoogleFilePicker()) { - setTimeout(() => { - this.addFilesPicker()?.createPicker(); - }, 1000); + timer(1000) + .pipe(takeUntilDestroyed(this.destroyRef)) + .subscribe(() => this.addFilesPicker()?.createPicker()); } }; From 57cea9cf3cedbcc5f952df29de8ef5caccd6bb3e Mon Sep 17 00:00:00 2001 From: mkovalua Date: Tue, 11 Nov 2025 19:07:03 +0200 Subject: [PATCH 03/76] not show osfstorage/addons dropdown for registration file tab --- .../files/pages/files/files.component.html | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/app/features/files/pages/files/files.component.html b/src/app/features/files/pages/files/files.component.html index c264f5b0c..543dc2985 100644 --- a/src/app/features/files/pages/files/files.component.html +++ b/src/app/features/files/pages/files/files.component.html @@ -9,21 +9,23 @@ }
- - -

{{ selectedOption.label }}

-
- -

{{ option.label }}

-
-
+ @if (!isRegistration()) { + + +

{{ selectedOption.label }}

+
+ +

{{ option.label }}

+
+
+ }
@if (filesSelection.length) { @if (!isMoveDialogOpened()) { From 8f7826ced2bdb9368885cf144e026dfb0c3b44f9 Mon Sep 17 00:00:00 2001 From: Bohdan Odintsov Date: Wed, 12 Nov 2025 15:21:09 +0200 Subject: [PATCH 04/76] fix(files): Fix empty when error from files provider --- src/app/features/files/store/files.state.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/app/features/files/store/files.state.ts b/src/app/features/files/store/files.state.ts index b9d718298..7a6ca3169 100644 --- a/src/app/features/files/store/files.state.ts +++ b/src/app/features/files/store/files.state.ts @@ -64,7 +64,15 @@ export class FilesState { isAnonymous: response.meta?.anonymous ?? false, }); }), - catchError((error) => handleSectionError(ctx, 'files', error)) + catchError((error) => + of([]).pipe( + tap(() => + ctx.patchState({ + files: { data: [], isLoading: false, error, totalCount: 0 }, + }) + ) + ) + ) ); } From f9073baac461ef9f83d50f8b59fa02ed58944520 Mon Sep 17 00:00:00 2001 From: Bohdan Odintsov Date: Wed, 12 Nov 2025 17:06:37 +0200 Subject: [PATCH 05/76] fix(files): Set to default state of files --- src/app/features/files/store/files.state.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/app/features/files/store/files.state.ts b/src/app/features/files/store/files.state.ts index 7a6ca3169..306c69598 100644 --- a/src/app/features/files/store/files.state.ts +++ b/src/app/features/files/store/files.state.ts @@ -64,15 +64,10 @@ export class FilesState { isAnonymous: response.meta?.anonymous ?? false, }); }), - catchError((error) => - of([]).pipe( - tap(() => - ctx.patchState({ - files: { data: [], isLoading: false, error, totalCount: 0 }, - }) - ) - ) - ) + catchError((error) => { + ctx.patchState({ files: FILES_STATE_DEFAULTS.files }); + return handleSectionError(ctx, 'files', error); + }) ); } From a5f1ed2aa598e0ed7f20cdfae92f09a7c549365f Mon Sep 17 00:00:00 2001 From: Oleh Paduchak Date: Fri, 14 Nov 2025 13:47:11 +0200 Subject: [PATCH 06/76] feat(contributors): delete contributors from all components --- .../contributors/contributors.component.ts | 50 ++++++++++++------- .../shared/components/contributors/index.ts | 1 + .../remove-contributor-dialog.component.html | 32 ++++++++++++ .../remove-contributor-dialog.component.scss | 0 ...emove-contributor-dialog.component.spec.ts | 22 ++++++++ .../remove-contributor-dialog.component.ts | 38 ++++++++++++++ .../shared/services/contributors.service.ts | 12 ++++- .../contributors/contributors.actions.ts | 3 +- .../stores/contributors/contributors.state.ts | 2 +- src/assets/i18n/en.json | 2 + 10 files changed, 139 insertions(+), 23 deletions(-) create mode 100644 src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.html create mode 100644 src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.scss create mode 100644 src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.spec.ts create mode 100644 src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.ts diff --git a/src/app/features/contributors/contributors.component.ts b/src/app/features/contributors/contributors.component.ts index 8c2ef4b23..4978b94ef 100644 --- a/src/app/features/contributors/contributors.component.ts +++ b/src/app/features/contributors/contributors.component.ts @@ -28,6 +28,7 @@ import { AddContributorDialogComponent, AddUnregisteredContributorDialogComponent, ContributorsTableComponent, + RemoveContributorDialogComponent, RequestAccessTableComponent, } from '@osf/shared/components/contributors'; import { SearchInputComponent } from '@osf/shared/components/search-input/search-input.component'; @@ -397,26 +398,37 @@ export class ContributorsComponent implements OnInit, OnDestroy { removeContributor(contributor: ContributorModel) { const isDeletingSelf = contributor.userId === this.currentUser()?.id; - this.customConfirmationService.confirmDelete({ - headerKey: 'project.contributors.removeDialog.title', - messageKey: 'project.contributors.removeDialog.message', - messageParams: { name: contributor.fullName }, - acceptLabelKey: 'common.buttons.remove', - onConfirm: () => { - this.actions - .deleteContributor(this.resourceId(), this.resourceType(), contributor.userId, isDeletingSelf) - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(() => { - this.toastService.showSuccess('project.contributors.removeDialog.successMessage', { - name: contributor.fullName, - }); + this.customDialogService + .open(RemoveContributorDialogComponent, { + header: 'project.contributors.removeDialog.title', + width: '448px', + data: { + messageKey: 'project.contributors.removeDialog.message', + messageParams: { name: contributor.fullName }, + }, + }) + .onClose.pipe( + filter((res) => res !== undefined), + switchMap((removeFromChildren: boolean) => + this.actions.deleteContributor( + this.resourceId(), + this.resourceType(), + contributor.userId, + isDeletingSelf, + removeFromChildren + ) + ), + takeUntilDestroyed(this.destroyRef) + ) + .subscribe(() => { + this.toastService.showSuccess('project.contributors.removeDialog.successMessage', { + name: contributor.fullName, + }); - if (isDeletingSelf) { - this.router.navigate(['/']); - } - }); - }, - }); + if (isDeletingSelf) { + this.router.navigate(['/']); + } + }); } loadMoreContributors(): void { diff --git a/src/app/shared/components/contributors/index.ts b/src/app/shared/components/contributors/index.ts index bd33928cd..5ab666f29 100644 --- a/src/app/shared/components/contributors/index.ts +++ b/src/app/shared/components/contributors/index.ts @@ -1,4 +1,5 @@ export * from './add-contributor-dialog/add-contributor-dialog.component'; export * from './add-unregistered-contributor-dialog/add-unregistered-contributor-dialog.component'; export * from './contributors-table/contributors-table.component'; +export * from './remove-contributor-dialog/remove-contributor-dialog.component'; export * from './request-access-table/request-access-table.component'; diff --git a/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.html b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.html new file mode 100644 index 000000000..838bd02f1 --- /dev/null +++ b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.html @@ -0,0 +1,32 @@ +
+

+ +
+ + + +
+
+ + +
+ +
+ + +
+
diff --git a/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.scss b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.spec.ts b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.spec.ts new file mode 100644 index 000000000..65273fab7 --- /dev/null +++ b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RemoveContributorDialogComponent } from './remove-contributor-dialog.component'; + +describe('RemoveContributorDialogComponent', () => { + let component: RemoveContributorDialogComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RemoveContributorDialogComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(RemoveContributorDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.ts b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.ts new file mode 100644 index 000000000..041d5c90c --- /dev/null +++ b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.ts @@ -0,0 +1,38 @@ +import { TranslatePipe } from '@ngx-translate/core'; + +import { Button } from 'primeng/button'; +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; +import { RadioButton } from 'primeng/radiobutton'; + +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +@Component({ + selector: 'osf-remove-contributor-dialog', + imports: [RadioButton, FormsModule, Button, TranslatePipe], + templateUrl: './remove-contributor-dialog.component.html', + styleUrl: './remove-contributor-dialog.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RemoveContributorDialogComponent { + readonly dialogRef = inject(DynamicDialogRef); + readonly config = inject(DynamicDialogConfig); + selectedOption = false; + + get messageKey(): string | undefined { + return this.config?.data?.messageKey as string | undefined; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get messageParams(): any { + return this.config?.data?.messageParams; + } + + confirm(): void { + this.dialogRef.close(this.selectedOption); + } + + cancel(): void { + this.dialogRef.close(); + } +} diff --git a/src/app/shared/services/contributors.service.ts b/src/app/shared/services/contributors.service.ts index c441b9ca0..60e4ab31b 100644 --- a/src/app/shared/services/contributors.service.ts +++ b/src/app/shared/services/contributors.service.ts @@ -170,8 +170,16 @@ export class ContributorsService { return this.jsonApiService.patch(baseUrl, contributorData); } - deleteContributor(resourceType: ResourceType, resourceId: string, userId: string): Observable { - const baseUrl = `${this.getBaseUrl(resourceType, resourceId)}/${userId}/`; + deleteContributor( + resourceType: ResourceType, + resourceId: string, + userId: string, + removeFromChildren = false + ): Observable { + let baseUrl = `${this.getBaseUrl(resourceType, resourceId)}/${userId}/`; + if (removeFromChildren) { + baseUrl = baseUrl.concat('?propagate_to_children=true'); + } return this.jsonApiService.delete(baseUrl); } diff --git a/src/app/shared/stores/contributors/contributors.actions.ts b/src/app/shared/stores/contributors/contributors.actions.ts index 2bb634cfc..d9f0d59c0 100644 --- a/src/app/shared/stores/contributors/contributors.actions.ts +++ b/src/app/shared/stores/contributors/contributors.actions.ts @@ -80,7 +80,8 @@ export class DeleteContributor { public resourceId: string | undefined | null, public resourceType: ResourceType | undefined, public contributorId: string, - public skipRefresh = false + public skipRefresh = false, + public removeFromChildren = false ) {} } diff --git a/src/app/shared/stores/contributors/contributors.state.ts b/src/app/shared/stores/contributors/contributors.state.ts index 0e738afec..9e45e7599 100644 --- a/src/app/shared/stores/contributors/contributors.state.ts +++ b/src/app/shared/stores/contributors/contributors.state.ts @@ -251,7 +251,7 @@ export class ContributorsState { }); return this.contributorsService - .deleteContributor(action.resourceType, action.resourceId, action.contributorId) + .deleteContributor(action.resourceType, action.resourceId, action.contributorId, action.removeFromChildren) .pipe( tap(() => { if (!action.skipRefresh) { diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 740df40fa..733ef0f6a 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -618,6 +618,8 @@ }, "removeDialog": { "title": "Remove contributor", + "thisProjectOnly": "This project only", + "thisProjectAndComponents": "This project and all it's components", "message": "Are you sure you want to remove {{name}} contributor?", "successMessage": "Contributor {{name}} successfully removed." }, From ecf95ca2dc740207068b902e53c46765d60badbf Mon Sep 17 00:00:00 2001 From: mkovalua Date: Fri, 14 Nov 2025 13:57:39 +0200 Subject: [PATCH 07/76] add pagination for registration files retrieval --- .../features/registries/store/handlers/files.handlers.ts | 4 ++-- src/app/shared/services/files.service.ts | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/app/features/registries/store/handlers/files.handlers.ts b/src/app/features/registries/store/handlers/files.handlers.ts index 478dd1e8e..f852f6be4 100644 --- a/src/app/features/registries/store/handlers/files.handlers.ts +++ b/src/app/features/registries/store/handlers/files.handlers.ts @@ -34,7 +34,7 @@ export class FilesHandlers { ); } - getProjectFiles(ctx: StateContext, { filesLink }: GetFiles) { + getProjectFiles(ctx: StateContext, { filesLink, page }: GetFiles) { const state = ctx.getState(); ctx.patchState({ files: { @@ -43,7 +43,7 @@ export class FilesHandlers { }, }); - return this.filesService.getFilesWithoutFiltering(filesLink).pipe( + return this.filesService.getFilesWithoutFiltering(filesLink, page).pipe( tap((response) => { ctx.patchState({ files: { diff --git a/src/app/shared/services/files.service.ts b/src/app/shared/services/files.service.ts index 988adbcc1..57640249d 100644 --- a/src/app/shared/services/files.service.ts +++ b/src/app/shared/services/files.service.ts @@ -85,9 +85,12 @@ export class FilesService { .pipe(map((response) => ({ files: FilesMapper.getFileFolders(response.data), meta: response.meta }))); } - getFilesWithoutFiltering(filesLink: string): Observable { + getFilesWithoutFiltering(filesLink: string, page = 1): Observable { + const params: Record = { + page: page.toString(), + } return this.jsonApiService - .get(filesLink) + .get(filesLink, params) .pipe(map((response) => FilesMapper.getFiles(response.data))); } From 698ad743b6070c5db845c866b0b3e648e25f9b7e Mon Sep 17 00:00:00 2001 From: mkovalua Date: Fri, 14 Nov 2025 14:01:10 +0200 Subject: [PATCH 08/76] trigger scroll action for registration files to call pagination --- .../components/files-control/files-control.component.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/features/registries/components/files-control/files-control.component.html b/src/app/features/registries/components/files-control/files-control.component.html index f2a233060..93fd6d2fa 100644 --- a/src/app/features/registries/components/files-control/files-control.component.html +++ b/src/app/features/registries/components/files-control/files-control.component.html @@ -46,6 +46,7 @@ [storage]="null" [currentFolder]="currentFolder()!" [isLoading]="isFilesLoading()" + [scrollHeight]="'200px'" [viewOnly]="filesViewOnly()" [resourceId]="projectId()" [provider]="provider()" From 7c6933b08e252366b850b247e8a3f96e07617aa8 Mon Sep 17 00:00:00 2001 From: mkovalua Date: Fri, 14 Nov 2025 14:38:46 +0200 Subject: [PATCH 09/76] take total files count from metadata --- .../store/preprint-stepper/preprint-stepper.state.ts | 6 +++--- .../features/registries/store/handlers/files.handlers.ts | 4 ++-- src/app/shared/services/files.service.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/features/preprints/store/preprint-stepper/preprint-stepper.state.ts b/src/app/features/preprints/store/preprint-stepper/preprint-stepper.state.ts index d63e44fad..c960a444b 100644 --- a/src/app/features/preprints/store/preprint-stepper/preprint-stepper.state.ts +++ b/src/app/features/preprints/store/preprint-stepper/preprint-stepper.state.ts @@ -308,12 +308,12 @@ export class PreprintStepperState { getProjectFilesByLink(ctx: StateContext, action: FetchProjectFilesByLink) { ctx.setState(patch({ projectFiles: patch({ isLoading: true }) })); - return this.fileService.getFilesWithoutFiltering(action.filesLink).pipe( - tap((files: FileModel[]) => { + return this.fileService.getFilesWithoutFiltering(action.filesLink, 1).pipe( + tap((response) => { ctx.setState( patch({ projectFiles: patch({ - data: files, + data: response.files, isLoading: false, }), }) diff --git a/src/app/features/registries/store/handlers/files.handlers.ts b/src/app/features/registries/store/handlers/files.handlers.ts index f852f6be4..0fa48c323 100644 --- a/src/app/features/registries/store/handlers/files.handlers.ts +++ b/src/app/features/registries/store/handlers/files.handlers.ts @@ -47,10 +47,10 @@ export class FilesHandlers { tap((response) => { ctx.patchState({ files: { - data: response, + data: response.files, isLoading: false, error: null, - totalCount: response.length, + totalCount: response.meta?.total ?? 0, }, }); }), diff --git a/src/app/shared/services/files.service.ts b/src/app/shared/services/files.service.ts index 57640249d..ca6eba59c 100644 --- a/src/app/shared/services/files.service.ts +++ b/src/app/shared/services/files.service.ts @@ -85,13 +85,13 @@ export class FilesService { .pipe(map((response) => ({ files: FilesMapper.getFileFolders(response.data), meta: response.meta }))); } - getFilesWithoutFiltering(filesLink: string, page = 1): Observable { + getFilesWithoutFiltering(filesLink: string, page = 1): Observable<{ files: FileModel[]; meta?: MetaJsonApi }> { const params: Record = { page: page.toString(), } return this.jsonApiService .get(filesLink, params) - .pipe(map((response) => FilesMapper.getFiles(response.data))); + .pipe(map((response) => ({ files: FilesMapper.getFiles(response.data), meta: response.meta }))); } uploadFile( From f17eb162e79786e147a69a151404283a3f0de8b1 Mon Sep 17 00:00:00 2001 From: Bohdan Odintsov Date: Fri, 14 Nov 2025 16:44:22 +0200 Subject: [PATCH 10/76] fix(providers): Make icon of provider be on center --- .../collections-discover.component.html | 6 ++++-- .../preprint-provider-hero.component.html | 7 ++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/app/features/collections/components/collections-discover/collections-discover.component.html b/src/app/features/collections/components/collections-discover/collections-discover.component.html index 875329e71..1e9261f03 100644 --- a/src/app/features/collections/components/collections-discover/collections-discover.component.html +++ b/src/app/features/collections/components/collections-discover/collections-discover.component.html @@ -4,6 +4,10 @@ class="collections-sub-header flex justify-content-between flex-column gap-4 mb-4 sm:mb-6 sm:gap-0 sm:flex-row" >
+

{{ collectionProvider()?.name }}

+
+ +
@let provider = collectionProvider(); @if (provider && provider.assets) { @@ -14,8 +18,6 @@ [src]="provider.assets.squareColorNoTransparent" /> } - -

{{ collectionProvider()?.name }}

diff --git a/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.html b/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.html index 8754ee628..7339576ee 100644 --- a/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.html +++ b/src/app/features/preprints/components/preprint-provider-hero/preprint-provider-hero.component.html @@ -3,6 +3,12 @@
@if (isPreprintProviderLoading()) { + } @else { +

{{ preprintProvider()!.name }}

+ } +
+
+ @if (isPreprintProviderLoading()) { } @else { -

{{ preprintProvider()!.name }}

}
From fc326a3d30ad8879797860716b405606111b1e4d Mon Sep 17 00:00:00 2001 From: futa-ikeda Date: Fri, 14 Nov 2025 14:57:37 -0500 Subject: [PATCH 11/76] feat(login): Add next param to login URL redirect --- src/app/core/services/auth.service.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/app/core/services/auth.service.ts b/src/app/core/services/auth.service.ts index c07b1e10d..871fa81b5 100644 --- a/src/app/core/services/auth.service.ts +++ b/src/app/core/services/auth.service.ts @@ -35,14 +35,15 @@ export class AuthService { navigateToSignIn(): void { this.loaderService.show(); - const loginUrl = `${this.casUrl}/login?${urlParam({ service: `${this.webUrl}/login` })}`; + const loginUrl = `${this.casUrl}/login?${urlParam({ service: `${this.webUrl}/login`, next: window.location.href })}`; window.location.href = loginUrl; } navigateToOrcidSignIn(): void { const loginUrl = `${this.casUrl}/login?${urlParam({ redirectOrcid: 'true', - service: `${this.webUrl}/login/?next=${encodeURIComponent(this.webUrl)}`, + service: `${this.webUrl}/login`, + next: window.location.href, })}`; window.location.href = loginUrl; } @@ -50,7 +51,8 @@ export class AuthService { navigateToInstitutionSignIn(): void { const loginUrl = `${this.casUrl}/login?${urlParam({ campaign: 'institution', - service: `${this.webUrl}/login/?next=${encodeURIComponent(this.webUrl)}`, + service: `${this.webUrl}/login`, + next: window.location.href, })}`; window.location.href = loginUrl; } From 1ac195e927d01ac3558cacf0a18070e0deb6065c Mon Sep 17 00:00:00 2001 From: mkovalua Date: Fri, 14 Nov 2025 22:39:12 +0200 Subject: [PATCH 12/76] show all available files of project when scrolling on registration creation --- .../components/files-control/files-control.component.html | 2 +- .../features/registries/store/handlers/files.handlers.ts | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app/features/registries/components/files-control/files-control.component.html b/src/app/features/registries/components/files-control/files-control.component.html index 93fd6d2fa..bf53c0a2f 100644 --- a/src/app/features/registries/components/files-control/files-control.component.html +++ b/src/app/features/registries/components/files-control/files-control.component.html @@ -46,7 +46,7 @@ [storage]="null" [currentFolder]="currentFolder()!" [isLoading]="isFilesLoading()" - [scrollHeight]="'200px'" + [scrollHeight]="'500px'" [viewOnly]="filesViewOnly()" [resourceId]="projectId()" [provider]="provider()" diff --git a/src/app/features/registries/store/handlers/files.handlers.ts b/src/app/features/registries/store/handlers/files.handlers.ts index 0fa48c323..4e793cd03 100644 --- a/src/app/features/registries/store/handlers/files.handlers.ts +++ b/src/app/features/registries/store/handlers/files.handlers.ts @@ -40,14 +40,18 @@ export class FilesHandlers { files: { ...state.files, isLoading: true, + error: null, + totalCount: 0, }, }); return this.filesService.getFilesWithoutFiltering(filesLink, page).pipe( tap((response) => { + const newData = page === 1 ? response.files : [...(state.files.data ?? []), ...response.files]; + ctx.patchState({ files: { - data: response.files, + data: newData, isLoading: false, error: null, totalCount: response.meta?.total ?? 0, From 75cc1eee3a77e1dbbd1def6a0f2242a98bc1bb1d Mon Sep 17 00:00:00 2001 From: mkovalua Date: Mon, 17 Nov 2025 14:23:04 +0200 Subject: [PATCH 13/76] fix linter issues --- src/app/shared/services/files.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/services/files.service.ts b/src/app/shared/services/files.service.ts index ca6eba59c..363009762 100644 --- a/src/app/shared/services/files.service.ts +++ b/src/app/shared/services/files.service.ts @@ -88,7 +88,7 @@ export class FilesService { getFilesWithoutFiltering(filesLink: string, page = 1): Observable<{ files: FileModel[]; meta?: MetaJsonApi }> { const params: Record = { page: page.toString(), - } + }; return this.jsonApiService .get(filesLink, params) .pipe(map((response) => ({ files: FilesMapper.getFiles(response.data), meta: response.meta }))); From 13607f3c36782a4f128d1e439b391ba532ae4d74 Mon Sep 17 00:00:00 2001 From: mkovalua Date: Mon, 17 Nov 2025 16:02:53 +0200 Subject: [PATCH 14/76] resolve CR comments --- .../preprint-stepper/preprint-stepper.state.ts | 2 +- .../registries/store/handlers/files.handlers.ts | 4 ++-- src/app/shared/services/files.service.ts | 13 +++++++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/app/features/preprints/store/preprint-stepper/preprint-stepper.state.ts b/src/app/features/preprints/store/preprint-stepper/preprint-stepper.state.ts index c960a444b..bd43281ba 100644 --- a/src/app/features/preprints/store/preprint-stepper/preprint-stepper.state.ts +++ b/src/app/features/preprints/store/preprint-stepper/preprint-stepper.state.ts @@ -313,7 +313,7 @@ export class PreprintStepperState { ctx.setState( patch({ projectFiles: patch({ - data: response.files, + data: response.data, isLoading: false, }), }) diff --git a/src/app/features/registries/store/handlers/files.handlers.ts b/src/app/features/registries/store/handlers/files.handlers.ts index 4e793cd03..e6b5d4f38 100644 --- a/src/app/features/registries/store/handlers/files.handlers.ts +++ b/src/app/features/registries/store/handlers/files.handlers.ts @@ -47,14 +47,14 @@ export class FilesHandlers { return this.filesService.getFilesWithoutFiltering(filesLink, page).pipe( tap((response) => { - const newData = page === 1 ? response.files : [...(state.files.data ?? []), ...response.files]; + const newData = page === 1 ? response.data : [...(state.files.data ?? []), ...response.data]; ctx.patchState({ files: { data: newData, isLoading: false, error: null, - totalCount: response.meta?.total ?? 0, + totalCount: response.totalCount, }, }); }), diff --git a/src/app/shared/services/files.service.ts b/src/app/shared/services/files.service.ts index 363009762..0fd3306e0 100644 --- a/src/app/shared/services/files.service.ts +++ b/src/app/shared/services/files.service.ts @@ -16,6 +16,7 @@ import { OsfFileRevision, PatchFileMetadata, } from '@osf/features/files/models'; +import { PaginatedData } from '@osf/shared/models/paginated-data.model'; import { FileKind } from '../enums/file-kind.enum'; import { AddonMapper } from '../mappers/addon.mapper'; @@ -85,13 +86,17 @@ export class FilesService { .pipe(map((response) => ({ files: FilesMapper.getFileFolders(response.data), meta: response.meta }))); } - getFilesWithoutFiltering(filesLink: string, page = 1): Observable<{ files: FileModel[]; meta?: MetaJsonApi }> { + getFilesWithoutFiltering(filesLink: string, page = 1): Observable> { const params: Record = { page: page.toString(), }; - return this.jsonApiService - .get(filesLink, params) - .pipe(map((response) => ({ files: FilesMapper.getFiles(response.data), meta: response.meta }))); + return this.jsonApiService.get(filesLink, params).pipe( + map((response) => ({ + data: FilesMapper.getFiles(response.data), + totalCount: response.meta.total, + pageSize: response.meta.per_page, + })) + ); } uploadFile( From 2ecce5f38069a7b3da0ba9511e83b5ec46878fce Mon Sep 17 00:00:00 2001 From: mkovalua Date: Mon, 17 Nov 2025 20:01:42 +0200 Subject: [PATCH 15/76] link user names on the contributor pages to OSF Profiles --- .../contributors-table/contributors-table.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/components/contributors/contributors-table/contributors-table.component.html b/src/app/shared/components/contributors/contributors-table/contributors-table.component.html index 9d8f37d3f..daaa9ba85 100644 --- a/src/app/shared/components/contributors/contributors-table/contributors-table.component.html +++ b/src/app/shared/components/contributors/contributors-table/contributors-table.component.html @@ -68,7 +68,7 @@

- {{ contributor.fullName }} + {{ contributor.fullName }}

From d1070ff2eb8cb8064cecf59715dfda32a6cd9027 Mon Sep 17 00:00:00 2001 From: futa-ikeda Date: Mon, 17 Nov 2025 15:24:38 -0500 Subject: [PATCH 16/76] fix(addons): Show addon icon in User Connected Addons page --- src/app/shared/mappers/addon.mapper.ts | 2 ++ .../services/addons/addons.service.spec.ts | 6 ++++-- .../shared/services/addons/addons.service.ts | 2 +- .../shared/stores/addons/addons.state.spec.ts | 17 ++++++++++------- .../addons/addons.authorized-storage.data.ts | 2 ++ 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/app/shared/mappers/addon.mapper.ts b/src/app/shared/mappers/addon.mapper.ts index c3a70ed48..bb6ab0331 100644 --- a/src/app/shared/mappers/addon.mapper.ts +++ b/src/app/shared/mappers/addon.mapper.ts @@ -56,6 +56,7 @@ export class AddonMapper { const displayName = (matchingService?.['display_name'] as string) || ''; const credentialsFormat = (matchingService?.['credentials_format'] as string) || ''; const supportedFeatures = (matchingService?.['supported_features'] as string[]) || []; + const iconUrl = (matchingService?.['icon_url'] as string) || ''; return { type: response.type, @@ -73,6 +74,7 @@ export class AddonMapper { externalServiceName, supportedFeatures, credentialsFormat, + iconUrl, providerName: displayName, }; } diff --git a/src/app/shared/services/addons/addons.service.spec.ts b/src/app/shared/services/addons/addons.service.spec.ts index 45355d7ba..5c4b05dcc 100644 --- a/src/app/shared/services/addons/addons.service.spec.ts +++ b/src/app/shared/services/addons/addons.service.spec.ts @@ -90,7 +90,7 @@ describe('Service: Addons', () => { }); const request = httpMock.expectOne( - 'http://addons.localhost:8000/user-references/reference-id/authorized_storage_accounts/?include=external-storage-service&fields%5Bexternal-storage-services%5D=external_service_name,credentials_format' + 'http://addons.localhost:8000/user-references/reference-id/authorized_storage_accounts/?include=external-storage-service&fields%5Bexternal-storage-services%5D=external_service_name,credentials_format,icon_url' ); expect(request.request.method).toBe('GET'); request.flush(getAddonsAuthorizedStorageData()); @@ -103,7 +103,8 @@ describe('Service: Addons', () => { authorizedCapabilities: ['ACCESS', 'UPDATE'], authorizedOperationNames: ['list_root_items', 'get_item_info', 'list_child_items'], credentialsAvailable: true, - credentialsFormat: '', + credentialsFormat: 'OAUTH2', + iconUrl: 'https://osf.io/assets/images/logo.svg', defaultRootFolder: '', displayName: 'Google Drive', externalServiceName: 'googledrive', @@ -151,6 +152,7 @@ describe('Service: Addons', () => { authorizedOperationNames: ['list_root_items', 'get_item_info', 'list_child_items'], credentialsAvailable: true, credentialsFormat: '', + iconUrl: '', defaultRootFolder: '', displayName: 'Google Drive', externalServiceName: '', diff --git a/src/app/shared/services/addons/addons.service.ts b/src/app/shared/services/addons/addons.service.ts index d2e5c6ef7..dadc7d3c2 100644 --- a/src/app/shared/services/addons/addons.service.ts +++ b/src/app/shared/services/addons/addons.service.ts @@ -79,7 +79,7 @@ export class AddonsService { getAuthorizedAddons(addonType: string, referenceId: string): Observable { const params = { - [`fields[external-${addonType}-services]`]: 'external_service_name,credentials_format', + [`fields[external-${addonType}-services]`]: 'external_service_name,credentials_format,icon_url', }; return this.jsonApiService .get< diff --git a/src/app/shared/stores/addons/addons.state.spec.ts b/src/app/shared/stores/addons/addons.state.spec.ts index ebfbf99a8..f45b974d1 100644 --- a/src/app/shared/stores/addons/addons.state.spec.ts +++ b/src/app/shared/stores/addons/addons.state.spec.ts @@ -210,7 +210,7 @@ describe('State: Addons', () => { expect(loading()).toBeTruthy(); const request = httpMock.expectOne( - 'http://addons.localhost:8000/user-references/reference-id/authorized_storage_accounts/?include=external-storage-service&fields%5Bexternal-storage-services%5D=external_service_name,credentials_format' + 'http://addons.localhost:8000/user-references/reference-id/authorized_storage_accounts/?include=external-storage-service&fields%5Bexternal-storage-services%5D=external_service_name,credentials_format,icon_url' ); expect(request.request.method).toBe('GET'); request.flush(getAddonsAuthorizedStorageData()); @@ -223,7 +223,7 @@ describe('State: Addons', () => { authorizedCapabilities: ['ACCESS', 'UPDATE'], authorizedOperationNames: ['list_root_items', 'get_item_info', 'list_child_items'], credentialsAvailable: true, - credentialsFormat: '', + credentialsFormat: 'OAUTH2', defaultRootFolder: '', displayName: 'Google Drive', externalServiceName: 'googledrive', @@ -233,6 +233,7 @@ describe('State: Addons', () => { providerName: '', supportedFeatures: [], type: 'authorized-storage-accounts', + iconUrl: 'https://osf.io/assets/images/logo.svg', }) ); @@ -259,7 +260,7 @@ describe('State: Addons', () => { expect(loading()).toBeTruthy(); const request = httpMock.expectOne( - 'http://addons.localhost:8000/user-references/reference-id/authorized_storage_accounts/?include=external-storage-service&fields%5Bexternal-storage-services%5D=external_service_name,credentials_format' + 'http://addons.localhost:8000/user-references/reference-id/authorized_storage_accounts/?include=external-storage-service&fields%5Bexternal-storage-services%5D=external_service_name,credentials_format,icon_url' ); expect(request.request.method).toBe('GET'); @@ -268,7 +269,7 @@ describe('State: Addons', () => { expect(result).toEqual({ data: [], error: - 'Http failure response for http://addons.localhost:8000/user-references/reference-id/authorized_storage_accounts/?include=external-storage-service&fields%5Bexternal-storage-services%5D=external_service_name,credentials_format: 500 Server Error', + 'Http failure response for http://addons.localhost:8000/user-references/reference-id/authorized_storage_accounts/?include=external-storage-service&fields%5Bexternal-storage-services%5D=external_service_name,credentials_format,icon_url: 500 Server Error', isLoading: false, isSubmitting: false, }); @@ -303,7 +304,6 @@ describe('State: Addons', () => { authorizedCapabilities: ['ACCESS', 'UPDATE'], authorizedOperationNames: ['list_root_items', 'get_item_info', 'list_child_items'], credentialsAvailable: true, - credentialsFormat: '', defaultRootFolder: '', displayName: 'Google Drive', externalServiceName: '', @@ -313,6 +313,8 @@ describe('State: Addons', () => { providerName: '', supportedFeatures: [], type: 'authorized-storage-accounts', + credentialsFormat: '', // No credentialsFormat in a PATCH response + iconUrl: '', // No iconUrl in a PATCH response }) ); @@ -339,7 +341,7 @@ describe('State: Addons', () => { expect(loading()).toBeTruthy(); let request = httpMock.expectOne( - 'http://addons.localhost:8000/user-references/reference-id/authorized_storage_accounts/?include=external-storage-service&fields%5Bexternal-storage-services%5D=external_service_name,credentials_format' + 'http://addons.localhost:8000/user-references/reference-id/authorized_storage_accounts/?include=external-storage-service&fields%5Bexternal-storage-services%5D=external_service_name,credentials_format,icon_url' ); expect(request.request.method).toBe('GET'); request.flush(getAddonsAuthorizedStorageData()); @@ -358,7 +360,6 @@ describe('State: Addons', () => { authorizedCapabilities: ['ACCESS', 'UPDATE'], authorizedOperationNames: ['list_root_items', 'get_item_info', 'list_child_items'], credentialsAvailable: true, - credentialsFormat: '', defaultRootFolder: '', displayName: 'Google Drive', externalServiceName: '', @@ -368,6 +369,8 @@ describe('State: Addons', () => { providerName: '', supportedFeatures: [], type: 'authorized-storage-accounts', + credentialsFormat: '', // No credentialsFormat in a PATCH response + iconUrl: '', // No iconUrl in a PATCH response }) ); diff --git a/src/testing/data/addons/addons.authorized-storage.data.ts b/src/testing/data/addons/addons.authorized-storage.data.ts index 4365c4574..2a7ea5d94 100644 --- a/src/testing/data/addons/addons.authorized-storage.data.ts +++ b/src/testing/data/addons/addons.authorized-storage.data.ts @@ -110,6 +110,8 @@ const AuthorizedStorage = { id: '8aeb85e9-3a73-426f-a89b-5624b4b9d418', attributes: { external_service_name: 'googledrive', + credentials_format: 'OAUTH2', + icon_url: 'https://osf.io/assets/images/logo.svg', }, links: { self: 'https://addons.staging4.osf.io/v1/external-storage-services/8aeb85e9-3a73-426f-a89b-5624b4b9d418', From 7d4056e2c441ff6e09c6e9901dcda4d85e716b12 Mon Sep 17 00:00:00 2001 From: Bohdan Odintsov Date: Tue, 18 Nov 2025 16:41:08 +0200 Subject: [PATCH 17/76] fix(mappers): make mappers use format bad encoding --- .../mappers/file-custom-metadata.mapper.ts | 5 +++-- .../files/mappers/resource-metadata.mapper.ts | 5 +++-- .../meetings/mappers/meetings.mapper.ts | 3 ++- .../metadata-description.component.html | 2 +- .../metadata-description.component.ts | 4 +--- .../metadata-title.component.html | 2 +- .../metadata-title.component.ts | 4 +--- .../metadata/mappers/metadata.mapper.ts | 5 +++-- .../mappers/preprint-moderation.mapper.ts | 5 +++-- .../mappers/registry-moderation.mapper.ts | 3 ++- .../preprints/mappers/preprints.mapper.ts | 9 +++++---- .../settings/mappers/settings.mapper.ts | 5 +++-- .../registry/mappers/linked-nodes.mapper.ts | 5 +++-- .../mappers/linked-registrations.mapper.ts | 5 +++-- .../mappers/registry-components.mapper.ts | 5 +++-- .../shared/mappers/activity-logs.mapper.ts | 16 +++++++++------- .../mappers/collections/collections.mapper.ts | 19 ++++++++++--------- .../institutions/institutions.mapper.ts | 5 +++-- src/app/shared/mappers/my-resources.mapper.ts | 4 +++- .../shared/mappers/nodes/base-node.mapper.ts | 7 ++++--- .../mappers/nodes/node-preprint.mapper.ts | 3 ++- .../mappers/projects/projects.mapper.ts | 9 +++++---- .../registration/page-schema.mapper.ts | 5 +++-- .../registration/registration-node.mapper.ts | 5 +++-- .../registration/registration.mapper.ts | 17 +++++++++-------- .../shared/mappers/search/search.mapper.ts | 3 ++- src/app/shared/mappers/wiki/wiki.mapper.ts | 3 ++- 27 files changed, 92 insertions(+), 71 deletions(-) diff --git a/src/app/features/files/mappers/file-custom-metadata.mapper.ts b/src/app/features/files/mappers/file-custom-metadata.mapper.ts index a347b32a4..3af893193 100644 --- a/src/app/features/files/mappers/file-custom-metadata.mapper.ts +++ b/src/app/features/files/mappers/file-custom-metadata.mapper.ts @@ -1,13 +1,14 @@ import { ApiData } from '@osf/shared/models/common/json-api.model'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { FileCustomMetadata, OsfFileCustomMetadata } from '../models'; export function MapFileCustomMetadata(data: ApiData): OsfFileCustomMetadata { return { id: data.id, - description: data.attributes.description, + description: replaceBadEncodedChars(data.attributes.description), language: data.attributes.language, resourceTypeGeneral: data.attributes.resource_type_general, - title: data.attributes.title, + title: replaceBadEncodedChars(data.attributes.title), }; } diff --git a/src/app/features/files/mappers/resource-metadata.mapper.ts b/src/app/features/files/mappers/resource-metadata.mapper.ts index cf89e485d..5fc0e6fc0 100644 --- a/src/app/features/files/mappers/resource-metadata.mapper.ts +++ b/src/app/features/files/mappers/resource-metadata.mapper.ts @@ -1,5 +1,6 @@ import { IdentifiersMapper } from '@osf/shared/mappers/identifiers.mapper'; import { ResourceMetadata } from '@osf/shared/models/resource-metadata.model'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { GetResourceCustomMetadataResponse } from '../models/get-resource-custom-metadata-response.model'; import { GetResourceShortInfoResponse } from '../models/get-resource-short-info-response.model'; @@ -9,8 +10,8 @@ export function MapResourceMetadata( customMetadata: GetResourceCustomMetadataResponse ): ResourceMetadata { return { - title: shortInfo.data.attributes.title, - description: shortInfo.data.attributes.description, + title: replaceBadEncodedChars(shortInfo.data.attributes.title), + description: replaceBadEncodedChars(shortInfo.data.attributes.description), dateCreated: new Date(shortInfo.data.attributes.date_created), dateModified: new Date(shortInfo.data.attributes.date_modified), funders: diff --git a/src/app/features/meetings/mappers/meetings.mapper.ts b/src/app/features/meetings/mappers/meetings.mapper.ts index 4807257e6..f967aa4bb 100644 --- a/src/app/features/meetings/mappers/meetings.mapper.ts +++ b/src/app/features/meetings/mappers/meetings.mapper.ts @@ -1,4 +1,5 @@ import { ResponseJsonApi } from '@osf/shared/models/common/json-api.model'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { MeetingGetResponseJsonApi, @@ -28,7 +29,7 @@ export class MeetingsMapper { return { data: response.data.map((item) => ({ id: item.id, - title: item.attributes.title, + title: replaceBadEncodedChars(item.attributes.title), dateCreated: item.attributes.date_created, authorName: item.attributes.author_name, downloadCount: item.attributes.download_count || 0, diff --git a/src/app/features/metadata/components/metadata-description/metadata-description.component.html b/src/app/features/metadata/components/metadata-description/metadata-description.component.html index f74c20da0..b1dd3143b 100644 --- a/src/app/features/metadata/components/metadata-description/metadata-description.component.html +++ b/src/app/features/metadata/components/metadata-description/metadata-description.component.html @@ -13,6 +13,6 @@

{{ 'project.overview.metadata.description' | translate }}

- {{ (description() | fixSpecialChar) || ('project.overview.metadata.noDescription' | translate) }} + {{ description() || ('project.overview.metadata.noDescription' | translate) }}

diff --git a/src/app/features/metadata/components/metadata-description/metadata-description.component.ts b/src/app/features/metadata/components/metadata-description/metadata-description.component.ts index e7cd92173..27a06c164 100644 --- a/src/app/features/metadata/components/metadata-description/metadata-description.component.ts +++ b/src/app/features/metadata/components/metadata-description/metadata-description.component.ts @@ -5,11 +5,9 @@ import { Card } from 'primeng/card'; import { ChangeDetectionStrategy, Component, input, output } from '@angular/core'; -import { FixSpecialCharPipe } from '@osf/shared/pipes/fix-special-char.pipe'; - @Component({ selector: 'osf-metadata-description', - imports: [Card, Button, FixSpecialCharPipe, TranslatePipe], + imports: [Card, Button, TranslatePipe], templateUrl: './metadata-description.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) diff --git a/src/app/features/metadata/components/metadata-title/metadata-title.component.html b/src/app/features/metadata/components/metadata-title/metadata-title.component.html index 91a677fd7..9f1e06f1a 100644 --- a/src/app/features/metadata/components/metadata-title/metadata-title.component.html +++ b/src/app/features/metadata/components/metadata-title/metadata-title.component.html @@ -13,6 +13,6 @@

{{ 'common.labels.title' | translate }}

- {{ title() | fixSpecialChar }} + {{ title() }}

diff --git a/src/app/features/metadata/components/metadata-title/metadata-title.component.ts b/src/app/features/metadata/components/metadata-title/metadata-title.component.ts index 5ab4d32ba..b1864575c 100644 --- a/src/app/features/metadata/components/metadata-title/metadata-title.component.ts +++ b/src/app/features/metadata/components/metadata-title/metadata-title.component.ts @@ -5,11 +5,9 @@ import { Card } from 'primeng/card'; import { ChangeDetectionStrategy, Component, input, output } from '@angular/core'; -import { FixSpecialCharPipe } from '@osf/shared/pipes/fix-special-char.pipe'; - @Component({ selector: 'osf-metadata-title', - imports: [Card, Button, FixSpecialCharPipe, TranslatePipe], + imports: [Card, Button, TranslatePipe], templateUrl: './metadata-title.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) diff --git a/src/app/features/metadata/mappers/metadata.mapper.ts b/src/app/features/metadata/mappers/metadata.mapper.ts index c55d1f5bd..5c166574d 100644 --- a/src/app/features/metadata/mappers/metadata.mapper.ts +++ b/src/app/features/metadata/mappers/metadata.mapper.ts @@ -1,5 +1,6 @@ import { IdentifiersMapper } from '@osf/shared/mappers/identifiers.mapper'; import { LicensesMapper } from '@osf/shared/mappers/licenses.mapper'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { CustomItemMetadataRecord, CustomMetadataJsonApi, MetadataJsonApi, MetadataModel } from '../models'; @@ -7,8 +8,8 @@ export class MetadataMapper { static fromMetadataApiResponse(response: MetadataJsonApi): MetadataModel { return { id: response.id, - title: response.attributes.title, - description: response.attributes.description, + title: replaceBadEncodedChars(response.attributes.title), + description: replaceBadEncodedChars(response.attributes.description), tags: response.attributes.tags, dateCreated: response.attributes.date_created, dateModified: response.attributes.date_modified, diff --git a/src/app/features/moderation/mappers/preprint-moderation.mapper.ts b/src/app/features/moderation/mappers/preprint-moderation.mapper.ts index b87c70438..5fb0cc752 100644 --- a/src/app/features/moderation/mappers/preprint-moderation.mapper.ts +++ b/src/app/features/moderation/mappers/preprint-moderation.mapper.ts @@ -1,6 +1,7 @@ import { UserMapper } from '@osf/shared/mappers/user'; import { ResponseJsonApi } from '@osf/shared/models/common/json-api.model'; import { PaginatedData } from '@osf/shared/models/paginated-data.model'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { PreprintProviderModerationInfo, @@ -64,7 +65,7 @@ export class PreprintModerationMapper { return { data: response.data.map((x) => ({ id: x.id, - title: x.attributes.title, + title: replaceBadEncodedChars(x.attributes.title), public: x.attributes.public, reviewsState: x.attributes.reviews_state, actions: [], @@ -86,7 +87,7 @@ export class PreprintModerationMapper { return { data: response.data.map((x) => ({ id: x.id, - title: x.embeds.target.data.attributes.title, + title: replaceBadEncodedChars(x.embeds.target.data.attributes.title), preprintId: x.embeds.target.data.id, actions: [], contributors: [], diff --git a/src/app/features/moderation/mappers/registry-moderation.mapper.ts b/src/app/features/moderation/mappers/registry-moderation.mapper.ts index 8c7113170..70ab5fdb4 100644 --- a/src/app/features/moderation/mappers/registry-moderation.mapper.ts +++ b/src/app/features/moderation/mappers/registry-moderation.mapper.ts @@ -1,5 +1,6 @@ import { UserMapper } from '@osf/shared/mappers/user'; import { PaginatedData } from '@osf/shared/models/paginated-data.model'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { RegistryDataJsonApi, @@ -13,7 +14,7 @@ export class RegistryModerationMapper { static fromResponse(response: RegistryDataJsonApi): RegistryModeration { return { id: response.id, - title: response.attributes.title, + title: replaceBadEncodedChars(response.attributes.title), reviewsState: response.attributes.reviews_state, revisionStatus: response.attributes.revision_state, public: response.attributes.public, diff --git a/src/app/features/preprints/mappers/preprints.mapper.ts b/src/app/features/preprints/mappers/preprints.mapper.ts index 283704403..51ead14da 100644 --- a/src/app/features/preprints/mappers/preprints.mapper.ts +++ b/src/app/features/preprints/mappers/preprints.mapper.ts @@ -2,6 +2,7 @@ import { StringOrNull } from '@osf/shared/helpers/types.helper'; import { IdentifiersMapper } from '@osf/shared/mappers/identifiers.mapper'; import { LicensesMapper } from '@osf/shared/mappers/licenses.mapper'; import { ApiData, JsonApiResponseWithMeta, ResponseJsonApi } from '@osf/shared/models/common/json-api.model'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { PreprintAttributesJsonApi, @@ -18,7 +19,7 @@ export class PreprintsMapper { return { data: { attributes: { - title: title, + title: replaceBadEncodedChars(title), description: abstract, }, relationships: { @@ -44,7 +45,7 @@ export class PreprintsMapper { dateWithdrawn: response.attributes.date_withdrawn, datePublished: response.attributes.date_published, dateLastTransitioned: response.attributes.date_last_transitioned, - title: response.attributes.title, + title: replaceBadEncodedChars(response.attributes.title), description: response.attributes.description, reviewsState: response.attributes.reviews_state, preprintDoiCreated: response.attributes.preprint_doi_created, @@ -102,7 +103,7 @@ export class PreprintsMapper { dateWithdrawn: data.attributes.date_withdrawn, datePublished: data.attributes.date_published, dateLastTransitioned: data.attributes.date_last_transitioned, - title: data.attributes.title, + title: replaceBadEncodedChars(data.attributes.title), description: data.attributes.description, reviewsState: data.attributes.reviews_state, preprintDoiCreated: data.attributes.preprint_doi_created, @@ -172,7 +173,7 @@ export class PreprintsMapper { data: response.data.map((preprintData) => { return { id: preprintData.id, - title: preprintData.attributes.title, + title: replaceBadEncodedChars(preprintData.attributes.title), dateModified: preprintData.attributes.date_modified, contributors: preprintData.embeds.bibliographic_contributors.data.map((contrData) => { return { diff --git a/src/app/features/project/settings/mappers/settings.mapper.ts b/src/app/features/project/settings/mappers/settings.mapper.ts index 3dec5f603..d2eef0d1d 100644 --- a/src/app/features/project/settings/mappers/settings.mapper.ts +++ b/src/app/features/project/settings/mappers/settings.mapper.ts @@ -1,6 +1,7 @@ import { UserPermissions } from '@osf/shared/enums/user-permissions.enum'; import { InstitutionsMapper } from '@osf/shared/mappers/institutions'; import { RegionsMapper } from '@osf/shared/mappers/regions'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { BaseNodeDataJsonApi } from '@shared/models/nodes/base-node-data-json-api.model'; import { @@ -30,8 +31,8 @@ export class SettingsMapper { static fromNodeResponse(data: BaseNodeDataJsonApi): NodeDetailsModel { return { id: data.id, - title: data.attributes.title, - description: data.attributes.description, + title: replaceBadEncodedChars(data.attributes.title), + description: replaceBadEncodedChars(data.attributes.description), isPublic: data.attributes.public, region: data.embeds?.region ? RegionsMapper.getRegion(data?.embeds?.region?.data) : null, affiliatedInstitutions: data.embeds?.affiliated_institutions diff --git a/src/app/features/registry/mappers/linked-nodes.mapper.ts b/src/app/features/registry/mappers/linked-nodes.mapper.ts index 441996057..372257e1e 100644 --- a/src/app/features/registry/mappers/linked-nodes.mapper.ts +++ b/src/app/features/registry/mappers/linked-nodes.mapper.ts @@ -1,4 +1,5 @@ import { ContributorsMapper } from '@osf/shared/mappers/contributors'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { LinkedNode, LinkedNodeJsonApi } from '../models'; @@ -6,8 +7,8 @@ export class LinkedNodesMapper { static fromApiResponse(apiNode: LinkedNodeJsonApi): LinkedNode { return { id: apiNode.id, - title: apiNode.attributes.title, - description: apiNode.attributes.description, + title: replaceBadEncodedChars(apiNode.attributes.title), + description: replaceBadEncodedChars(apiNode.attributes.description), category: apiNode.attributes.category, dateCreated: apiNode.attributes.date_created, dateModified: apiNode.attributes.date_modified, diff --git a/src/app/features/registry/mappers/linked-registrations.mapper.ts b/src/app/features/registry/mappers/linked-registrations.mapper.ts index c61b4e7da..72b763e19 100644 --- a/src/app/features/registry/mappers/linked-registrations.mapper.ts +++ b/src/app/features/registry/mappers/linked-registrations.mapper.ts @@ -1,4 +1,5 @@ import { ContributorsMapper } from '@osf/shared/mappers/contributors'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { LinkedRegistration, LinkedRegistrationJsonApi } from '../models'; @@ -6,8 +7,8 @@ export class LinkedRegistrationsMapper { static fromApiResponse(apiRegistration: LinkedRegistrationJsonApi): LinkedRegistration { return { id: apiRegistration.id, - title: apiRegistration.attributes.title, - description: apiRegistration.attributes.description, + title: replaceBadEncodedChars(apiRegistration.attributes.title), + description: replaceBadEncodedChars(apiRegistration.attributes.description), category: apiRegistration.attributes.category, dateCreated: apiRegistration.attributes.date_created, dateModified: apiRegistration.attributes.date_modified, diff --git a/src/app/features/registry/mappers/registry-components.mapper.ts b/src/app/features/registry/mappers/registry-components.mapper.ts index 5e8339323..55ac8b2f6 100644 --- a/src/app/features/registry/mappers/registry-components.mapper.ts +++ b/src/app/features/registry/mappers/registry-components.mapper.ts @@ -1,4 +1,5 @@ import { ContributorsMapper } from '@osf/shared/mappers/contributors'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { RegistryComponentJsonApi, RegistryComponentModel } from '../models'; @@ -6,8 +7,8 @@ export class RegistryComponentsMapper { static fromApiResponse(apiComponent: RegistryComponentJsonApi): RegistryComponentModel { return { id: apiComponent.id, - title: apiComponent.attributes.title, - description: apiComponent.attributes.description, + title: replaceBadEncodedChars(apiComponent.attributes.title), + description: replaceBadEncodedChars(apiComponent.attributes.description), category: apiComponent.attributes.category, dateCreated: apiComponent.attributes.date_created, dateModified: apiComponent.attributes.date_modified, diff --git a/src/app/shared/mappers/activity-logs.mapper.ts b/src/app/shared/mappers/activity-logs.mapper.ts index d0b7877a6..a4c1fc1c6 100644 --- a/src/app/shared/mappers/activity-logs.mapper.ts +++ b/src/app/shared/mappers/activity-logs.mapper.ts @@ -1,3 +1,5 @@ +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; + import { DEFAULT_TABLE_PARAMS } from '../constants/default-table-params.constants'; import { ActivityLog, LogContributor } from '../models/activity-logs/activity-logs.model'; import { ActivityLogJsonApi, LogContributorJsonApi } from '../models/activity-logs/activity-logs-json-api.model'; @@ -24,7 +26,7 @@ export class ActivityLogsMapper { paramsNode: params.params_node ? { id: params.params_node.id, - title: params.params_node.title, + title: replaceBadEncodedChars(params.params_node.title), } : { id: '', title: '' }, paramsProject: params.params_project, @@ -32,14 +34,14 @@ export class ActivityLogsMapper { ? { id: params.template_node.id, url: params.template_node.url, - title: params.template_node.title, + title: replaceBadEncodedChars(params.template_node.title), } : null, pointer: params.pointer ? { category: params.pointer.category, id: params.pointer.id, - title: params.pointer.title, + title: replaceBadEncodedChars(params.pointer.title), url: params.pointer.url, } : null, @@ -72,8 +74,8 @@ export class ActivityLogsMapper { ? { id: log.embeds.original_node.data.id, type: log.embeds.original_node.data.type, - title: log.embeds.original_node.data.attributes.title, - description: log.embeds.original_node.data.attributes.description, + title: replaceBadEncodedChars(log.embeds.original_node.data.attributes.title), + description: replaceBadEncodedChars(log.embeds.original_node.data.attributes.description), category: log.embeds.original_node.data.attributes.category, customCitation: log.embeds.original_node.data.attributes.custom_citation, dateCreated: log.embeds.original_node.data.attributes.date_created, @@ -119,8 +121,8 @@ export class ActivityLogsMapper { ? { id: log.embeds.linked_node.data.id, type: log.embeds.linked_node.data.type, - title: log.embeds.linked_node.data.attributes.title, - description: log.embeds.linked_node.data.attributes.description, + title: replaceBadEncodedChars(log.embeds.linked_node.data.attributes.title), + description: replaceBadEncodedChars(log.embeds.linked_node.data.attributes.description), category: log.embeds.linked_node.data.attributes.category, customCitation: log.embeds.linked_node.data.attributes.custom_citation, dateCreated: log.embeds.linked_node.data.attributes.date_created, diff --git a/src/app/shared/mappers/collections/collections.mapper.ts b/src/app/shared/mappers/collections/collections.mapper.ts index b0fb73392..88aa46f12 100644 --- a/src/app/shared/mappers/collections/collections.mapper.ts +++ b/src/app/shared/mappers/collections/collections.mapper.ts @@ -20,6 +20,7 @@ import { import { ResponseJsonApi } from '@osf/shared/models/common/json-api.model'; import { ContributorModel } from '@osf/shared/models/contributors/contributor.model'; import { PaginatedData } from '@osf/shared/models/paginated-data.model'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { UserMapper } from '../user'; @@ -28,8 +29,8 @@ export class CollectionsMapper { return { id: response.id, type: response.type, - name: response.attributes.name, - description: response.attributes.description, + name: replaceBadEncodedChars(response.attributes.name), + description: replaceBadEncodedChars(response.attributes.description), advisoryBoard: response.attributes.advisory_board, example: response.attributes.example, domain: response.attributes.domain, @@ -58,7 +59,7 @@ export class CollectionsMapper { brand: response.embeds.brand.data ? { id: response.embeds.brand.data.id, - name: response.embeds.brand.data.attributes.name, + name: replaceBadEncodedChars(response.embeds.brand.data.attributes.name), heroLogoImageUrl: response.embeds.brand.data.attributes.hero_logo_image, topNavLogoImageUrl: response.embeds.brand.data.attributes.topnav_logo_image, heroBackgroundImageUrl: response.embeds.brand.data.attributes.hero_background_image, @@ -74,7 +75,7 @@ export class CollectionsMapper { return { id: response.id, type: response.type, - title: response.attributes.title, + title: replaceBadEncodedChars(response.attributes.title), dateCreated: response.attributes.date_created, dateModified: response.attributes.date_modified, bookmarks: response.attributes.bookmarks, @@ -110,7 +111,7 @@ export class CollectionsMapper { dataType: submission.attributes.data_type, disease: submission.attributes.disease, gradeLevels: submission.attributes.grade_levels, - collectionTitle: submission.embeds.collection.data.attributes.title, + collectionTitle: replaceBadEncodedChars(submission.embeds.collection.data.attributes.title), collectionId: submission.embeds.collection.data.relationships.provider.data.id, }; } @@ -127,8 +128,8 @@ export class CollectionsMapper { type: submission.type, nodeId: submission.embeds.guid.data.id, nodeUrl: submission.embeds.guid.data.links.html, - title: submission.embeds.guid.data.attributes.title, - description: submission.embeds.guid.data.attributes.description, + title: replaceBadEncodedChars(submission.embeds.guid.data.attributes.title), + description: replaceBadEncodedChars(submission.embeds.guid.data.attributes.description), category: submission.embeds.guid.data.attributes.category, dateCreated: submission.embeds.guid.data.attributes.date_created, dateModified: submission.embeds.guid.data.attributes.date_modified, @@ -183,8 +184,8 @@ export class CollectionsMapper { type: submission.type, nodeId: submission.embeds.guid.data.id, nodeUrl: submission.embeds.guid.data.links.html, - title: submission.embeds.guid.data.attributes.title, - description: submission.embeds.guid.data.attributes.description, + title: replaceBadEncodedChars(submission.embeds.guid.data.attributes.title), + description: replaceBadEncodedChars(submission.embeds.guid.data.attributes.description), category: submission.embeds.guid.data.attributes.category, dateCreated: submission.embeds.guid.data.attributes.date_created, dateModified: submission.embeds.guid.data.attributes.date_modified, diff --git a/src/app/shared/mappers/institutions/institutions.mapper.ts b/src/app/shared/mappers/institutions/institutions.mapper.ts index 56cb294f4..66642d805 100644 --- a/src/app/shared/mappers/institutions/institutions.mapper.ts +++ b/src/app/shared/mappers/institutions/institutions.mapper.ts @@ -4,6 +4,7 @@ import { InstitutionsWithMetaJsonApiResponse, } from '@osf/shared/models/institutions/institution-json-api.model'; import { Institution, InstitutionsWithTotalCount } from '@osf/shared/models/institutions/institutions.models'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; export class InstitutionsMapper { static fromInstitutionsResponse(response: InstitutionsJsonApiResponse): Institution[] { @@ -14,8 +15,8 @@ export class InstitutionsMapper { return { id: data.id, type: data.type, - name: data.attributes.name, - description: data.attributes.description, + name: replaceBadEncodedChars(data.attributes.name), + description: replaceBadEncodedChars(data.attributes.description), iri: data.links.iri, rorIri: data.attributes.ror_iri, iris: data.attributes.iris, diff --git a/src/app/shared/mappers/my-resources.mapper.ts b/src/app/shared/mappers/my-resources.mapper.ts index b924d9a5c..7a2a5d5d2 100644 --- a/src/app/shared/mappers/my-resources.mapper.ts +++ b/src/app/shared/mappers/my-resources.mapper.ts @@ -1,3 +1,5 @@ +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; + import { MyResourcesItem, MyResourcesItemGetResponseJsonApi } from '../models/my-resources/my-resources.models'; import { ContributorsMapper } from './contributors'; @@ -7,7 +9,7 @@ export class MyResourcesMapper { return { id: response.id, type: response.type, - title: response.attributes.title, + title: replaceBadEncodedChars(response.attributes.title), dateCreated: response.attributes.date_created, dateModified: response.attributes.date_modified, isPublic: response.attributes.public, diff --git a/src/app/shared/mappers/nodes/base-node.mapper.ts b/src/app/shared/mappers/nodes/base-node.mapper.ts index e839fcf0f..fa0645bac 100644 --- a/src/app/shared/mappers/nodes/base-node.mapper.ts +++ b/src/app/shared/mappers/nodes/base-node.mapper.ts @@ -3,6 +3,7 @@ import { BaseNodeModel, NodeModel } from '@osf/shared/models/nodes/base-node.mod import { BaseNodeDataJsonApi } from '@osf/shared/models/nodes/base-node-data-json-api.model'; import { NodeShortInfoModel } from '@osf/shared/models/nodes/node-with-children.model'; import { PaginatedData } from '@osf/shared/models/paginated-data.model'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { ContributorsMapper } from '../contributors'; @@ -22,7 +23,7 @@ export class BaseNodeMapper { static getNodesWithChildren(data: BaseNodeDataJsonApi[], parentId: string): NodeShortInfoModel[] { return this.getAllDescendants(data, parentId).map((item) => ({ id: item.id, - title: item.attributes.title, + title: replaceBadEncodedChars(item.attributes.title), isPublic: item.attributes.public, permissions: item.attributes.current_user_permissions, parentId: item.relationships.parent?.data?.id, @@ -33,8 +34,8 @@ export class BaseNodeMapper { return { id: data.id, type: data.type, - title: data.attributes.title, - description: data.attributes.description, + title: replaceBadEncodedChars(data.attributes.title), + description: replaceBadEncodedChars(data.attributes.description), category: data.attributes.category, dateCreated: data.attributes.date_created, dateModified: data.attributes.date_modified, diff --git a/src/app/shared/mappers/nodes/node-preprint.mapper.ts b/src/app/shared/mappers/nodes/node-preprint.mapper.ts index da9df54bf..700581b8c 100644 --- a/src/app/shared/mappers/nodes/node-preprint.mapper.ts +++ b/src/app/shared/mappers/nodes/node-preprint.mapper.ts @@ -1,11 +1,12 @@ import { NodePreprintModel } from '@osf/shared/models/nodes/node-preprint.model'; import { NodePreprintDataJsonApi } from '@osf/shared/models/nodes/node-preprint-json-api.model'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; export class NodePreprintMapper { static getNodePreprint(data: NodePreprintDataJsonApi): NodePreprintModel { return { id: data.id, - title: data.attributes.title, + title: replaceBadEncodedChars(data.attributes.title), dateCreated: data.attributes.date_created, dateModified: data.attributes.date_modified, datePublished: data.attributes.date_published, diff --git a/src/app/shared/mappers/projects/projects.mapper.ts b/src/app/shared/mappers/projects/projects.mapper.ts index 51cbfa73e..cfe5faf1a 100644 --- a/src/app/shared/mappers/projects/projects.mapper.ts +++ b/src/app/shared/mappers/projects/projects.mapper.ts @@ -2,6 +2,7 @@ import { CollectionSubmissionMetadataPayloadJsonApi } from '@osf/features/collec import { ProjectMetadataUpdatePayload } from '@osf/shared/models/project-metadata-update-payload.model'; import { ProjectModel } from '@osf/shared/models/projects/projects.models'; import { ProjectJsonApi, ProjectsResponseJsonApi } from '@osf/shared/models/projects/projects-json-api.models'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; export class ProjectsMapper { static fromGetAllProjectsResponse(response: ProjectsResponseJsonApi): ProjectModel[] { @@ -12,7 +13,7 @@ export class ProjectsMapper { return { id: project.id, type: project.type, - title: project.attributes.title, + title: replaceBadEncodedChars(project.attributes.title), dateModified: project.attributes.date_modified, isPublic: project.attributes.public, licenseId: project.relationships.license?.data?.id || null, @@ -22,7 +23,7 @@ export class ProjectsMapper { copyrightHolders: project.attributes.node_license.copyright_holders.join(','), } : null, - description: project.attributes.description, + description: replaceBadEncodedChars(project.attributes.description), tags: project.attributes.tags || [], }; } @@ -41,8 +42,8 @@ export class ProjectsMapper { }, }, attributes: { - title: metadata.title, - description: metadata.description, + title: replaceBadEncodedChars(metadata.title), + description: replaceBadEncodedChars(metadata.description), tags: metadata.tags, ...(metadata.licenseOptions && { node_license: { diff --git a/src/app/shared/mappers/registration/page-schema.mapper.ts b/src/app/shared/mappers/registration/page-schema.mapper.ts index 84c57a97f..c42851d43 100644 --- a/src/app/shared/mappers/registration/page-schema.mapper.ts +++ b/src/app/shared/mappers/registration/page-schema.mapper.ts @@ -1,5 +1,6 @@ import { BlockType } from '@osf/shared/enums/block-type.enum'; import { FieldType } from '@osf/shared/enums/field-type.enum'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { PageSchema, Question, Section } from '@shared/models/registration/page-schema.model'; import { SchemaBlocksResponseJsonApi } from '@shared/models/registration/schema-blocks-json-api.model'; @@ -19,7 +20,7 @@ export class PageSchemaMapper { case BlockType.PageHeading: currentPage = { id: item.id, - title: item.attributes.display_text, + title: replaceBadEncodedChars(item.attributes.display_text), helpText: item.attributes.help_text, questions: [], }; @@ -31,7 +32,7 @@ export class PageSchemaMapper { if (currentPage) { currentSection = { id: item.id, - title: item.attributes.display_text, + title: replaceBadEncodedChars(item.attributes.display_text), helpText: item.attributes.help_text, questions: [], }; diff --git a/src/app/shared/mappers/registration/registration-node.mapper.ts b/src/app/shared/mappers/registration/registration-node.mapper.ts index fda7ddd86..9dcafab4d 100644 --- a/src/app/shared/mappers/registration/registration-node.mapper.ts +++ b/src/app/shared/mappers/registration/registration-node.mapper.ts @@ -1,4 +1,5 @@ import { CurrentResourceType } from '@osf/shared/enums/resource-type.enum'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { ProviderShortInfoModel } from '@shared/models/provider/provider.model'; import { RegistryProviderDetailsJsonApi } from '@shared/models/provider/registration-provider-json-api.model'; import { RegistrationNodeModel, RegistrationResponses } from '@shared/models/registration/registration-node.model'; @@ -26,7 +27,7 @@ export class RegistrationNodeMapper { dateModified: attributes.date_modified, dateRegistered: attributes.date_registered, dateWithdrawn: attributes.date_withdrawn, - description: attributes.description, + description: replaceBadEncodedChars(attributes.description), embargoed: attributes.embargoed, embargoEndDate: attributes.embargo_end_date, hasAnalyticCode: attributes.has_analytic_code, @@ -56,7 +57,7 @@ export class RegistrationNodeMapper { reviewsState: attributes.reviews_state, revisionState: attributes.revision_state, tags: attributes.tags || [], - title: attributes.title, + title: replaceBadEncodedChars(attributes.title), wikiEnabled: attributes.wiki_enabled, withdrawalJustification: attributes.withdrawal_justification, withdrawn: attributes.withdrawn, diff --git a/src/app/shared/mappers/registration/registration.mapper.ts b/src/app/shared/mappers/registration/registration.mapper.ts index 966b9fafa..43040ec85 100644 --- a/src/app/shared/mappers/registration/registration.mapper.ts +++ b/src/app/shared/mappers/registration/registration.mapper.ts @@ -1,4 +1,5 @@ import { RegistryStatus } from '@osf/shared/enums/registry-status.enum'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { DraftRegistrationModel } from '@shared/models/registration/draft-registration.model'; import { RegistrationModel } from '@shared/models/registration/registration.model'; import { RegistrationCard } from '@shared/models/registration/registration-card.model'; @@ -17,8 +18,8 @@ export class RegistrationMapper { static fromDraftRegistrationResponse(response: DraftRegistrationDataJsonApi): DraftRegistrationModel { return { id: response.id, - title: response.attributes.title, - description: response.attributes.description, + title: replaceBadEncodedChars(response.attributes.title), + description: replaceBadEncodedChars(response.attributes.description), registrationSchemaId: response.relationships.registration_schema?.data?.id || '', license: { id: response.relationships.license?.data?.id || '', @@ -34,13 +35,13 @@ export class RegistrationMapper { branchedFrom: response.embeds?.branched_from?.data ? { id: response.embeds.branched_from.data.id, - title: response.embeds.branched_from.data.attributes.title, + title: replaceBadEncodedChars(response.embeds.branched_from.data.attributes.title), filesLink: response.embeds?.branched_from?.data.relationships?.files?.links?.related?.href, type: response.embeds.branched_from.data.type, } : { id: response.relationships.branched_from?.data?.id || '', - title: response.attributes.title, + title: replaceBadEncodedChars(response.attributes.title), filesLink: response.relationships.branched_from?.links?.related.href + 'files/', type: response.relationships.branched_from?.data?.type, }, @@ -61,8 +62,8 @@ export class RegistrationMapper { static fromDraftToRegistrationCard(registration: DraftRegistrationDataJsonApi): RegistrationCard { return { id: registration.id, - title: registration.attributes.title, - description: registration.attributes.description || '', + title: replaceBadEncodedChars(registration.attributes.title), + description: replaceBadEncodedChars(registration.attributes.description) || '', status: RegistryStatus.None, dateCreated: registration.attributes.datetime_initiated, dateModified: registration.attributes.datetime_updated, @@ -77,8 +78,8 @@ export class RegistrationMapper { static fromRegistrationToRegistrationCard(registration: RegistrationDataJsonApi): RegistrationCard { return { id: registration.id, - title: registration.attributes.title, - description: registration.attributes.description || '', + title: replaceBadEncodedChars(registration.attributes.title), + description: replaceBadEncodedChars(registration.attributes.description) || '', status: MapRegistryStatus(registration.attributes), dateCreated: registration.attributes.date_created, dateModified: registration.attributes.date_modified, diff --git a/src/app/shared/mappers/search/search.mapper.ts b/src/app/shared/mappers/search/search.mapper.ts index 1ee0cd9a7..e0aad24e9 100644 --- a/src/app/shared/mappers/search/search.mapper.ts +++ b/src/app/shared/mappers/search/search.mapper.ts @@ -1,4 +1,5 @@ import { ResourceType } from '@shared/enums/resource-type.enum'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; import { IndexCardDataJsonApi, IndexCardSearchResponseJsonApi, @@ -34,7 +35,7 @@ export function MapResources(indexCardSearchResponseJsonApi: IndexCardSearchResp absoluteUrl: resourceMetadata['@id'], resourceType: ResourceType[resourceMetadata.resourceType[0]['@id'] as keyof typeof ResourceType], name: resourceMetadata.name?.[0]?.['@value'], - title: resourceMetadata.title?.[0]?.['@value'], + title: replaceBadEncodedChars(resourceMetadata.title?.[0]?.['@value']), fileName: resourceMetadata.fileName?.[0]?.['@value'], description: resourceMetadata.description?.[0]?.['@value'], diff --git a/src/app/shared/mappers/wiki/wiki.mapper.ts b/src/app/shared/mappers/wiki/wiki.mapper.ts index e79ea6c30..8d16aaedb 100644 --- a/src/app/shared/mappers/wiki/wiki.mapper.ts +++ b/src/app/shared/mappers/wiki/wiki.mapper.ts @@ -11,6 +11,7 @@ import { WikiVersionJsonApi, } from '@osf/shared/models/wiki/wiki.model'; import { ComponentWiki } from '@osf/shared/stores/wiki'; +import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.helper'; export class WikiMapper { private static translate: TranslateService; @@ -47,7 +48,7 @@ export class WikiMapper { static fromGetComponentsWikiResponse(response: ComponentsWikiGetResponse): ComponentWiki { return { id: response.id, - title: response.attributes.title, + title: replaceBadEncodedChars(response.attributes.title), list: response.embeds?.wikis?.data.map((wiki) => WikiMapper.fromGetWikiResponse(wiki)) || [], }; } From 6e50915cfeab74dc28d514fe3b7e68468b85a9ea Mon Sep 17 00:00:00 2001 From: Bohdan Odintsov Date: Wed, 19 Nov 2025 12:27:59 +0200 Subject: [PATCH 18/76] fix(format-bad-encoding): make method work with undefined values --- src/app/shared/helpers/format-bad-encoding.helper.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/shared/helpers/format-bad-encoding.helper.ts b/src/app/shared/helpers/format-bad-encoding.helper.ts index 8df1c1d44..ff4a3465b 100644 --- a/src/app/shared/helpers/format-bad-encoding.helper.ts +++ b/src/app/shared/helpers/format-bad-encoding.helper.ts @@ -1,3 +1,4 @@ export function replaceBadEncodedChars(text: string) { + if (!text) return ''; return text.replace(/&/gi, '&').replace(/</gi, '<').replace(/>/gi, '>'); } From 4a465d267b996b7fb3bee996ab7a0bb5e6d6e985 Mon Sep 17 00:00:00 2001 From: nsemets Date: Fri, 21 Nov 2025 12:37:52 +0200 Subject: [PATCH 19/76] fix(preprints): fixed accessibility issues --- .../my-reviewing-navigation.component.html | 2 +- ...eprint-recent-activity-list.component.html | 46 ++++++++++--------- ...eprint-recent-activity-list.component.scss | 1 + ...preprint-recent-activity-list.component.ts | 12 ++++- .../preprint-details.component.ts | 2 - .../resource-card.component.html | 8 ++-- .../horizontal-scroll-keyboard.directive.ts | 35 ++++++++++++++ src/styles/_base.scss | 5 ++ 8 files changed, 81 insertions(+), 30 deletions(-) create mode 100644 src/app/shared/directives/horizontal-scroll-keyboard.directive.ts diff --git a/src/app/features/moderation/components/my-reviewing-navigation/my-reviewing-navigation.component.html b/src/app/features/moderation/components/my-reviewing-navigation/my-reviewing-navigation.component.html index 9dc694473..566abfb5c 100644 --- a/src/app/features/moderation/components/my-reviewing-navigation/my-reviewing-navigation.component.html +++ b/src/app/features/moderation/components/my-reviewing-navigation/my-reviewing-navigation.component.html @@ -1,4 +1,4 @@ -

{{ provider().name }}

+

{{ provider().name }}

@for (tab of tabOptions; track $index) { diff --git a/src/app/features/moderation/components/preprint-recent-activity-list/preprint-recent-activity-list.component.html b/src/app/features/moderation/components/preprint-recent-activity-list/preprint-recent-activity-list.component.html index ed8d0f716..13ed801e2 100644 --- a/src/app/features/moderation/components/preprint-recent-activity-list/preprint-recent-activity-list.component.html +++ b/src/app/features/moderation/components/preprint-recent-activity-list/preprint-recent-activity-list.component.html @@ -6,31 +6,33 @@
} @else { -
-
- @for (item of reviews(); track $index) { -
-
- - {{ item.creator.name }} - {{ preprintReviewStatus[item.toState].label | translate: { name: item.preprint.name } }} - {{ item.provider.name }} -
- -

- {{ item.dateModified | date: 'MMM d, y h:mm a' }} -

+
+ @for (item of reviews(); track $index) { +
+
+ + {{ item.creator.name }} + {{ preprintReviewStatus[item.toState].label | translate: { name: item.preprint.name } }} + {{ item.provider.name }}
- } -
+ +

+ {{ item.dateModified | date: 'MMM d, y h:mm a' }} +

+
+ }
- @if (totalCount() > 10) { + @if (totalCount() > rows()) { (); first = signal(0); + rows = signal(10); readonly reviewStatusIcon = ReviewStatusIcon; readonly preprintReviewStatus = PreprintReviewStatus; diff --git a/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts b/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts index 31d8ecb41..8eeb2f6d4 100644 --- a/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts +++ b/src/app/features/preprints/pages/preprint-details/preprint-details.component.ts @@ -32,7 +32,6 @@ import { ResetState } from '@osf/features/files/store'; import { ReviewPermissions } from '@osf/shared/enums/review-permissions.enum'; import { pathJoin } from '@osf/shared/helpers/path-join.helper'; import { FixSpecialCharPipe } from '@osf/shared/pipes/fix-special-char.pipe'; -import { AnalyticsService } from '@osf/shared/services/analytics.service'; import { CustomDialogService } from '@osf/shared/services/custom-dialog.service'; import { DataciteService } from '@osf/shared/services/datacite/datacite.service'; import { MetaTagsService } from '@osf/shared/services/meta-tags.service'; @@ -101,7 +100,6 @@ export class PreprintDetailsComponent implements OnInit, OnDestroy { private readonly metaTags = inject(MetaTagsService); private readonly datePipe = inject(DatePipe); private readonly dataciteService = inject(DataciteService); - private readonly analyticsService = inject(AnalyticsService); private readonly prerenderReady = inject(PrerenderReadyService); private readonly environment = inject(ENVIRONMENT); diff --git a/src/app/shared/components/resource-card/resource-card.component.html b/src/app/shared/components/resource-card/resource-card.component.html index 3a73344dd..0f5d574f9 100644 --- a/src/app/shared/components/resource-card/resource-card.component.html +++ b/src/app/shared/components/resource-card/resource-card.component.html @@ -8,7 +8,7 @@

@for (affiliatedEntity of affiliatedEntities().slice(0, limit); track $index) {

{{ 'resourceCard.labels.from' | translate }}

{{ 'resourceCard.labels.from' | translate }}

(50); + + @HostListener('keydown', ['$event']) + onKeydown(event: KeyboardEvent): void { + const container = event.currentTarget as HTMLElement; + if (!container) { + return; + } + + switch (event.key) { + case 'ArrowRight': + event.preventDefault(); + container.scrollBy({ left: this.scrollAmount(), behavior: 'smooth' }); + break; + case 'ArrowLeft': + event.preventDefault(); + container.scrollBy({ left: -this.scrollAmount(), behavior: 'smooth' }); + break; + case 'Home': + event.preventDefault(); + container.scrollTo({ left: 0, behavior: 'smooth' }); + break; + case 'End': + event.preventDefault(); + container.scrollTo({ left: container.scrollWidth, behavior: 'smooth' }); + break; + } + } +} diff --git a/src/styles/_base.scss b/src/styles/_base.scss index 20e683fad..9a787f40b 100644 --- a/src/styles/_base.scss +++ b/src/styles/_base.scss @@ -92,4 +92,9 @@ margin-bottom: mix.rem(4px); font-weight: 400; } + + *:focus-visible { + outline: 1px solid var(--pr-blue-1); + outline-offset: 2px; + } } From 21ecd67051e0feb4e0b31598a99555a21b993121 Mon Sep 17 00:00:00 2001 From: mkovalua Date: Mon, 24 Nov 2025 16:24:11 +0200 Subject: [PATCH 20/76] avoid check because registration will have always at least one contributor and contributors may not load syncronically after step change --- .../features/registries/components/drafts/drafts.component.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/features/registries/components/drafts/drafts.component.ts b/src/app/features/registries/components/drafts/drafts.component.ts index 353456834..a1d9fd448 100644 --- a/src/app/features/registries/components/drafts/drafts.component.ts +++ b/src/app/features/registries/components/drafts/drafts.component.ts @@ -74,8 +74,7 @@ export class DraftsComponent implements OnDestroy { !this.draftRegistration()?.title || !this.draftRegistration()?.description || !this.registrationLicense() || - !this.selectedSubjects()?.length || - !this.initialContributors()?.length + !this.selectedSubjects()?.length ); }); From cf38e16008b7a4fb79f1d5800abbd7797abd464d Mon Sep 17 00:00:00 2001 From: nsemets Date: Mon, 24 Nov 2025 18:10:37 +0200 Subject: [PATCH 21/76] fix(scroll): removed directive for scroll --- ...eprint-recent-activity-list.component.html | 1 - ...preprint-recent-activity-list.component.ts | 11 +----- .../horizontal-scroll-keyboard.directive.ts | 35 ------------------- 3 files changed, 1 insertion(+), 46 deletions(-) delete mode 100644 src/app/shared/directives/horizontal-scroll-keyboard.directive.ts diff --git a/src/app/features/moderation/components/preprint-recent-activity-list/preprint-recent-activity-list.component.html b/src/app/features/moderation/components/preprint-recent-activity-list/preprint-recent-activity-list.component.html index 13ed801e2..d0bcde8c6 100644 --- a/src/app/features/moderation/components/preprint-recent-activity-list/preprint-recent-activity-list.component.html +++ b/src/app/features/moderation/components/preprint-recent-activity-list/preprint-recent-activity-list.component.html @@ -11,7 +11,6 @@ tabindex="0" role="region" [attr.aria-label]="'project.overview.recentActivity.title' | translate" - osfHorizontalScrollKeyboard > @for (item of reviews(); track $index) {
diff --git a/src/app/features/moderation/components/preprint-recent-activity-list/preprint-recent-activity-list.component.ts b/src/app/features/moderation/components/preprint-recent-activity-list/preprint-recent-activity-list.component.ts index 7d4645dfb..abd51ec05 100644 --- a/src/app/features/moderation/components/preprint-recent-activity-list/preprint-recent-activity-list.component.ts +++ b/src/app/features/moderation/components/preprint-recent-activity-list/preprint-recent-activity-list.component.ts @@ -9,22 +9,13 @@ import { ChangeDetectionStrategy, Component, input, output, signal } from '@angu import { CustomPaginatorComponent } from '@osf/shared/components/custom-paginator/custom-paginator.component'; import { IconComponent } from '@osf/shared/components/icon/icon.component'; -import { HorizontalScrollKeyboardDirective } from '@osf/shared/directives/horizontal-scroll-keyboard.directive'; import { PreprintReviewStatus, ReviewStatusIcon } from '../../constants'; import { PreprintReviewActionModel } from '../../models'; @Component({ selector: 'osf-preprint-recent-activity-list', - imports: [ - TableModule, - DatePipe, - TranslatePipe, - IconComponent, - Skeleton, - CustomPaginatorComponent, - HorizontalScrollKeyboardDirective, - ], + imports: [TableModule, DatePipe, TranslatePipe, IconComponent, Skeleton, CustomPaginatorComponent], templateUrl: './preprint-recent-activity-list.component.html', styleUrl: './preprint-recent-activity-list.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, diff --git a/src/app/shared/directives/horizontal-scroll-keyboard.directive.ts b/src/app/shared/directives/horizontal-scroll-keyboard.directive.ts deleted file mode 100644 index 090724592..000000000 --- a/src/app/shared/directives/horizontal-scroll-keyboard.directive.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Directive, HostListener, input } from '@angular/core'; - -@Directive({ - selector: '[osfHorizontalScrollKeyboard]', -}) -export class HorizontalScrollKeyboardDirective { - scrollAmount = input(50); - - @HostListener('keydown', ['$event']) - onKeydown(event: KeyboardEvent): void { - const container = event.currentTarget as HTMLElement; - if (!container) { - return; - } - - switch (event.key) { - case 'ArrowRight': - event.preventDefault(); - container.scrollBy({ left: this.scrollAmount(), behavior: 'smooth' }); - break; - case 'ArrowLeft': - event.preventDefault(); - container.scrollBy({ left: -this.scrollAmount(), behavior: 'smooth' }); - break; - case 'Home': - event.preventDefault(); - container.scrollTo({ left: 0, behavior: 'smooth' }); - break; - case 'End': - event.preventDefault(); - container.scrollTo({ left: container.scrollWidth, behavior: 'smooth' }); - break; - } - } -} From b7938e37074099eb02bfcc7ba8bf17a8ef813e23 Mon Sep 17 00:00:00 2001 From: Andriy Sheredko Date: Wed, 26 Nov 2025 09:34:33 +0200 Subject: [PATCH 22/76] feat(contributors): delete contributors from all components --- .../contributors/contributors.component.ts | 4 ++-- .../remove-contributor-dialog.component.html | 14 +++++++---- ...emove-contributor-dialog.component.spec.ts | 24 +++++++++++++++++++ .../remove-contributor-dialog.component.ts | 9 ++++--- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/src/app/features/contributors/contributors.component.ts b/src/app/features/contributors/contributors.component.ts index 4978b94ef..dd8db40e2 100644 --- a/src/app/features/contributors/contributors.component.ts +++ b/src/app/features/contributors/contributors.component.ts @@ -403,8 +403,8 @@ export class ContributorsComponent implements OnInit, OnDestroy { header: 'project.contributors.removeDialog.title', width: '448px', data: { - messageKey: 'project.contributors.removeDialog.message', - messageParams: { name: contributor.fullName }, + name: contributor.fullName, + hasChildren: !!this.resourceChildren()?.length, }, }) .onClose.pipe( diff --git a/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.html b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.html index 838bd02f1..fe28bcf57 100644 --- a/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.html +++ b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.html @@ -1,5 +1,5 @@
-

+

{{ 'project.contributors.removeDialog.message' | translate: { name: name } }}

@@ -7,7 +7,13 @@
- + + @@ -17,14 +23,14 @@ diff --git a/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.spec.ts b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.spec.ts index 65273fab7..b699581b8 100644 --- a/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.spec.ts +++ b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.spec.ts @@ -1,3 +1,5 @@ +import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; + import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RemoveContributorDialogComponent } from './remove-contributor-dialog.component'; @@ -5,10 +7,17 @@ import { RemoveContributorDialogComponent } from './remove-contributor-dialog.co describe('RemoveContributorDialogComponent', () => { let component: RemoveContributorDialogComponent; let fixture: ComponentFixture; + let dialogRef: DynamicDialogRef; beforeEach(async () => { + dialogRef = { close: jasmine.createSpy('close') } as any; + await TestBed.configureTestingModule({ imports: [RemoveContributorDialogComponent], + providers: [ + { provide: DynamicDialogRef, useValue: dialogRef }, + { provide: DynamicDialogConfig, useValue: { data: { name: 'John Doe', hasChildren: true } } }, + ], }).compileComponents(); fixture = TestBed.createComponent(RemoveContributorDialogComponent); @@ -19,4 +28,19 @@ describe('RemoveContributorDialogComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should pass name from config', () => { + expect(component.name).toBe('John Doe'); + }); + + it('should close dialog with selected option on confirm', () => { + component.selectedOption = true; + component.confirm(); + expect(dialogRef.close).toHaveBeenCalledWith(true); + }); + + it('should close dialog without value on cancel', () => { + component.cancel(); + expect(dialogRef.close).toHaveBeenCalledWith(); + }); }); diff --git a/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.ts b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.ts index 041d5c90c..c5515cf46 100644 --- a/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.ts +++ b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.ts @@ -19,13 +19,12 @@ export class RemoveContributorDialogComponent { readonly config = inject(DynamicDialogConfig); selectedOption = false; - get messageKey(): string | undefined { - return this.config?.data?.messageKey as string | undefined; + get name(): string | undefined { + return this.config?.data?.name; } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get messageParams(): any { - return this.config?.data?.messageParams; + get hasChildren(): boolean { + return this.config?.data?.hasChildren ?? false; } confirm(): void { From aa3979a5e6e6d7a8aea79121356cfcce0d7f2aa6 Mon Sep 17 00:00:00 2001 From: Andriy Sheredko Date: Wed, 26 Nov 2025 09:39:12 +0200 Subject: [PATCH 23/76] feat(contributors): fix linting --- .../remove-contributor-dialog.component.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.html b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.html index fe28bcf57..6b579178e 100644 --- a/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.html +++ b/src/app/shared/components/contributors/remove-contributor-dialog/remove-contributor-dialog.component.html @@ -12,7 +12,8 @@ [value]="true" [(ngModel)]="selectedOption" [disabled]="!hasChildren" - inputId="projectAll"> + inputId="projectAll" + >
-
+

{{ 'common.labels.contributors' | translate }}:

{ +import { DynamicDialogRefMock } from '@testing/mocks/dynamic-dialog-ref.mock'; +import { MOCK_NODE_WITH_ADMIN } from '@testing/mocks/node.mock'; +import { ToastServiceMock } from '@testing/mocks/toast.service.mock'; +import { OSFTestingModule } from '@testing/osf.testing.module'; +import { provideMockStore } from '@testing/providers/store-provider.mock'; + +describe('DeleteNodeLinkDialogComponent', () => { let component: DeleteNodeLinkDialogComponent; let fixture: ComponentFixture; + let store: jest.Mocked; + let dialogRef: jest.Mocked; + let dialogConfig: jest.Mocked; + let toastService: jest.Mocked; + + const mockProject = { ...MOCK_NODE_WITH_ADMIN, id: 'test-project-id' }; + const mockCurrentLink = { ...MOCK_NODE_WITH_ADMIN, id: 'linked-resource-id', title: 'Linked Resource' }; beforeEach(async () => { + dialogConfig = { + data: { currentLink: mockCurrentLink }, + } as jest.Mocked; + await TestBed.configureTestingModule({ - imports: [DeleteNodeLinkDialogComponent], + imports: [DeleteNodeLinkDialogComponent, OSFTestingModule], + providers: [ + DynamicDialogRefMock, + ToastServiceMock, + MockProvider(DynamicDialogConfig, dialogConfig), + provideMockStore({ + signals: [ + { selector: ProjectOverviewSelectors.getProject, value: mockProject }, + { selector: NodeLinksSelectors.getNodeLinksSubmitting, value: false }, + ], + }), + ], }).compileComponents(); + store = TestBed.inject(Store) as jest.Mocked; + store.dispatch = jest.fn().mockReturnValue(of(true)); fixture = TestBed.createComponent(DeleteNodeLinkDialogComponent); component = fixture.componentInstance; + dialogRef = TestBed.inject(DynamicDialogRef) as jest.Mocked; + toastService = TestBed.inject(ToastService) as jest.Mocked; fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should initialize currentProject selector', () => { + expect(component.currentProject()).toEqual(mockProject); }); + + it('should initialize isSubmitting selector', () => { + expect(component.isSubmitting()).toBe(false); + }); + + it('should initialize actions with deleteNodeLink mapping', () => { + expect(component.actions.deleteNodeLink).toBeDefined(); + }); + + it('should dispatch DeleteNodeLink action with correct parameters on successful deletion', () => { + component.handleDeleteNodeLink(); + + expect(store.dispatch).toHaveBeenCalledWith(expect.any(DeleteNodeLink)); + const call = (store.dispatch as jest.Mock).mock.calls.find((call) => call[0] instanceof DeleteNodeLink); + expect(call).toBeDefined(); + const action = call[0] as DeleteNodeLink; + expect(action.projectId).toBe('test-project-id'); + expect(action.linkedResource).toEqual(mockCurrentLink); + }); + + it('should show success toast on successful deletion', fakeAsync(() => { + component.handleDeleteNodeLink(); + tick(); + + expect(toastService.showSuccess).toHaveBeenCalledWith('project.overview.dialog.toast.deleteNodeLink.success'); + })); + + it('should close dialog with hasChanges true on successful deletion', fakeAsync(() => { + component.handleDeleteNodeLink(); + tick(); + + expect(dialogRef.close).toHaveBeenCalledWith({ hasChanges: true }); + })); }); diff --git a/src/app/features/project/overview/components/delete-node-link-dialog/delete-node-link-dialog.component.ts b/src/app/features/project/overview/components/delete-node-link-dialog/delete-node-link-dialog.component.ts index 5fd2ff58e..66f321915 100644 --- a/src/app/features/project/overview/components/delete-node-link-dialog/delete-node-link-dialog.component.ts +++ b/src/app/features/project/overview/components/delete-node-link-dialog/delete-node-link-dialog.component.ts @@ -8,7 +8,7 @@ import { DynamicDialogConfig, DynamicDialogRef } from 'primeng/dynamicdialog'; import { ChangeDetectionStrategy, Component, DestroyRef, inject } from '@angular/core'; import { ToastService } from '@osf/shared/services/toast.service'; -import { DeleteNodeLink, GetLinkedResources, NodeLinksSelectors } from '@osf/shared/stores/node-links'; +import { DeleteNodeLink, NodeLinksSelectors } from '@osf/shared/stores/node-links'; import { ProjectOverviewSelectors } from '../../store'; @@ -28,7 +28,7 @@ export class DeleteNodeLinkDialogComponent { currentProject = select(ProjectOverviewSelectors.getProject); isSubmitting = select(NodeLinksSelectors.getNodeLinksSubmitting); - actions = createDispatchMap({ deleteNodeLink: DeleteNodeLink, getLinkedResources: GetLinkedResources }); + actions = createDispatchMap({ deleteNodeLink: DeleteNodeLink }); handleDeleteNodeLink(): void { const project = this.currentProject(); @@ -38,9 +38,8 @@ export class DeleteNodeLinkDialogComponent { this.actions.deleteNodeLink(project.id, currentLink).subscribe({ next: () => { - this.dialogRef.close(); - this.actions.getLinkedResources(project.id); this.toastService.showSuccess('project.overview.dialog.toast.deleteNodeLink.success'); + this.dialogRef.close({ hasChanges: true }); }, }); } diff --git a/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.html b/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.html index 7b8e17baf..deb509690 100644 --- a/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.html +++ b/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.html @@ -39,15 +39,14 @@ @@ -84,15 +83,19 @@ {{ item.dateCreated | date: 'MMM d, y' }} {{ item.dateModified | date: 'MMM d, y' }} - @for (contributor of item.contributors; track contributor.id) { + @for (contributor of item.contributors.slice(0, 3); track contributor.id) { {{ contributor.fullName }}{{ $last ? '' : ', ' }} } + @if (item.contributors.length > 3) { + , {{ 'common.labels.and' | translate }} {{ item.contributors.length - 3 }} + {{ 'common.labels.more' | translate }} + } } @else { - + } diff --git a/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.spec.ts b/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.spec.ts index 14f9d04ba..f94d9f4c9 100644 --- a/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.spec.ts +++ b/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.spec.ts @@ -1,26 +1,185 @@ +import { Store } from '@ngxs/store'; + import { MockComponents } from 'ng-mocks'; +import { TablePageEvent } from 'primeng/table'; + import { ComponentFixture, TestBed } from '@angular/core/testing'; import { SearchInputComponent } from '@osf/shared/components/search-input/search-input.component'; +import { ResourceSearchMode } from '@osf/shared/enums/resource-search-mode.enum'; +import { ResourceType } from '@osf/shared/enums/resource-type.enum'; +import { MyResourcesSelectors } from '@osf/shared/stores/my-resources'; +import { NodeLinksSelectors } from '@osf/shared/stores/node-links'; +import { MyResourcesItem } from '@shared/models/my-resources/my-resources.models'; + +import { ProjectOverviewSelectors } from '../../store'; import { LinkResourceDialogComponent } from './link-resource-dialog.component'; -describe.skip('LinkProjectDialogComponent', () => { +import { DynamicDialogRefMock } from '@testing/mocks/dynamic-dialog-ref.mock'; +import { + MOCK_MY_RESOURCES_ITEM_PROJECT, + MOCK_MY_RESOURCES_ITEM_PROJECT_PRIVATE, + MOCK_MY_RESOURCES_ITEM_REGISTRATION, +} from '@testing/mocks/my-resources.mock'; +import { MOCK_NODE_WITH_ADMIN } from '@testing/mocks/node.mock'; +import { OSFTestingModule } from '@testing/osf.testing.module'; +import { provideMockStore } from '@testing/providers/store-provider.mock'; + +describe('LinkResourceDialogComponent', () => { let component: LinkResourceDialogComponent; let fixture: ComponentFixture; + let store: Store; + let dialogRef: { close: jest.Mock }; + + const mockProjects: MyResourcesItem[] = [MOCK_MY_RESOURCES_ITEM_PROJECT, MOCK_MY_RESOURCES_ITEM_PROJECT_PRIVATE]; + + const mockRegistrations: MyResourcesItem[] = [MOCK_MY_RESOURCES_ITEM_REGISTRATION]; + + const mockLinkedResources = [{ ...MOCK_NODE_WITH_ADMIN, id: 'project-1', title: 'Linked Project 1' }]; + + const mockCurrentProject = { ...MOCK_NODE_WITH_ADMIN, id: 'current-project-id' }; beforeEach(async () => { + dialogRef = { close: jest.fn() }; + await TestBed.configureTestingModule({ - imports: [LinkResourceDialogComponent, ...MockComponents(SearchInputComponent)], + imports: [LinkResourceDialogComponent, OSFTestingModule, ...MockComponents(SearchInputComponent)], + providers: [ + provideMockStore({ + signals: [ + { selector: MyResourcesSelectors.getProjects, value: mockProjects }, + { selector: MyResourcesSelectors.getProjectsLoading, value: false }, + { selector: MyResourcesSelectors.getRegistrations, value: mockRegistrations }, + { selector: MyResourcesSelectors.getRegistrationsLoading, value: false }, + { selector: MyResourcesSelectors.getTotalProjects, value: 2 }, + { selector: MyResourcesSelectors.getTotalRegistrations, value: 1 }, + { selector: NodeLinksSelectors.getNodeLinksSubmitting, value: false }, + { selector: NodeLinksSelectors.getLinkedResources, value: mockLinkedResources }, + { selector: ProjectOverviewSelectors.getProject, value: mockCurrentProject }, + ], + }), + { provide: DynamicDialogRefMock.provide, useValue: dialogRef }, + ], }).compileComponents(); fixture = TestBed.createComponent(LinkResourceDialogComponent); component = fixture.componentInstance; + store = TestBed.inject(Store); + fixture.detectChanges(); + }); + + it('should initialize with default values', () => { + expect(component.currentPage()).toBe(1); + expect(component.searchMode()).toBe(ResourceSearchMode.User); + expect(component.resourceType()).toBe(ResourceType.Project); + expect(component.searchControl.value).toBe(''); + }); + + it('should initialize skeleton data with correct length', () => { + expect(component.skeletonData.length).toBe(component.tableRows); + }); + + it('should compute currentResourceId from currentProject', () => { + expect(component.currentResourceId()).toBe('current-project-id'); + }); + + it('should compute isCurrentTableLoading for registrations', () => { + component.resourceType.set(ResourceType.Registration); fixture.detectChanges(); + expect(component.isCurrentTableLoading()).toBe(false); }); - it('should create', () => { - expect(component).toBeTruthy(); + it('should compute currentTotalCount for registrations', () => { + component.resourceType.set(ResourceType.Registration); + fixture.detectChanges(); + expect(component.currentTotalCount()).toBe(1); + }); + + it('should compute tableParams correctly', () => { + const params = component.tableParams(); + expect(params.rows).toBe(component.tableRows); + expect(params.firstRowIndex).toBe(0); + expect(params.paginator).toBe(false); + expect(params.totalRecords).toBe(2); + }); + + it('should compute linkedResourceIds as a Set', () => { + const linkedIds = component.linkedResourceIds(); + expect(linkedIds).toBeInstanceOf(Set); + expect(linkedIds.has('project-1')).toBe(true); + expect(linkedIds.has('project-2')).toBe(false); + }); + + it('should update searchMode and reset to first page', () => { + component.currentPage.set(2); + component.onSearchModeChange(ResourceSearchMode.All); + + expect(component.searchMode()).toBe(ResourceSearchMode.All); + expect(component.currentPage()).toBe(1); + }); + + it('should update resourceType and reset to first page', () => { + component.currentPage.set(2); + component.onObjectTypeChange(ResourceType.Registration); + + expect(component.resourceType()).toBe(ResourceType.Registration); + expect(component.currentPage()).toBe(1); + }); + + it('should update currentPage and trigger search', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const event: TablePageEvent = { + first: 6, + rows: 6, + }; + + component.onPageChange(event); + + expect(component.currentPage()).toBe(2); + expect(dispatchSpy).toHaveBeenCalled(); + }); + + it('should return true for linked items', () => { + expect(component.isItemLinked('project-1')).toBe(true); + }); + + it('should return false for non-linked items', () => { + expect(component.isItemLinked('project-2')).toBe(false); + }); + + it('should close the dialog', () => { + component.handleCloseDialog(); + expect(dialogRef.close).toHaveBeenCalled(); + }); + + it('should trigger search when searchMode changes', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + component.onSearchModeChange(ResourceSearchMode.All); + + expect(dispatchSpy).toHaveBeenCalled(); + }); + + it('should trigger search when resourceType changes', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + component.onObjectTypeChange(ResourceType.Registration); + + expect(dispatchSpy).toHaveBeenCalled(); + }); + + it('should handle page change with zero first value', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const event: TablePageEvent = { + first: 0, + rows: 6, + }; + + component.onPageChange(event); + + expect(component.currentPage()).toBe(1); + expect(dispatchSpy).toHaveBeenCalled(); }); }); diff --git a/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.ts b/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.ts index 7c880f723..fe5542a89 100644 --- a/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.ts +++ b/src/app/features/project/overview/components/link-resource-dialog/link-resource-dialog.component.ts @@ -26,12 +26,14 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { FormControl, FormsModule } from '@angular/forms'; import { SearchInputComponent } from '@osf/shared/components/search-input/search-input.component'; +import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants/default-table-params.constants'; import { ResourceSearchMode } from '@osf/shared/enums/resource-search-mode.enum'; import { ResourceType } from '@osf/shared/enums/resource-type.enum'; import { GetMyProjects, GetMyRegistrations, MyResourcesSelectors } from '@osf/shared/stores/my-resources'; -import { CreateNodeLink, DeleteNodeLink, GetLinkedResources, NodeLinksSelectors } from '@osf/shared/stores/node-links'; +import { CreateNodeLink, DeleteNodeLink, NodeLinksSelectors } from '@osf/shared/stores/node-links'; import { MyResourcesItem } from '@shared/models/my-resources/my-resources.models'; import { MyResourcesSearchFilters } from '@shared/models/my-resources/my-resources-search-filters.models'; +import { TableParameters } from '@shared/models/table-parameters.model'; import { ProjectOverviewSelectors } from '../../store'; @@ -67,7 +69,6 @@ export class LinkResourceDialogComponent { skeletonData: MyResourcesItem[] = Array.from({ length: this.tableRows }, () => ({}) as MyResourcesItem); - currentProject = select(ProjectOverviewSelectors.getProject); myProjects = select(MyResourcesSelectors.getProjects); isMyProjectsLoading = select(MyResourcesSelectors.getProjectsLoading); myRegistrations = select(MyResourcesSelectors.getRegistrations); @@ -76,10 +77,16 @@ export class LinkResourceDialogComponent { totalRegistrationsCount = select(MyResourcesSelectors.getTotalRegistrations); isNodeLinksSubmitting = select(NodeLinksSelectors.getNodeLinksSubmitting); linkedResources = select(NodeLinksSelectors.getLinkedResources); + hasChanges = select(NodeLinksSelectors.getNodeLinksHasChanges); + currentProject = select(ProjectOverviewSelectors.getProject); - currentTableItems = computed(() => - this.resourceType() === ResourceType.Project ? this.myProjects() : this.myRegistrations() - ); + currentResourceId = computed(() => this.currentProject()?.id); + + currentTableItems = computed(() => { + const items = this.resourceType() === ResourceType.Project ? this.myProjects() : this.myRegistrations(); + const currentId = this.currentResourceId(); + return currentId ? items.filter((item) => item.id !== currentId) : items; + }); isCurrentTableLoading = computed(() => this.resourceType() === ResourceType.Project ? this.isMyProjectsLoading() : this.isMyRegistrationsLoading() @@ -89,11 +96,17 @@ export class LinkResourceDialogComponent { this.resourceType() === ResourceType.Project ? this.totalProjectsCount() : this.totalRegistrationsCount() ); - isItemLinked = computed(() => { - const linkedResources = this.linkedResources(); - const linkedTargetIds = new Set(linkedResources.map((resource) => resource.id)); + tableParams = computed(() => ({ + ...DEFAULT_TABLE_PARAMS, + rows: this.tableRows, + firstRowIndex: (this.currentPage() - 1) * this.tableRows, + paginator: this.currentTotalCount() > this.tableRows, + totalRecords: this.currentTotalCount(), + })); - return (itemId: string) => linkedTargetIds.has(itemId); + linkedResourceIds = computed(() => { + const linkedResources = this.linkedResources(); + return new Set(linkedResources.map((resource) => resource.id)); }); actions = createDispatchMap({ @@ -101,23 +114,21 @@ export class LinkResourceDialogComponent { getRegistrations: GetMyRegistrations, createNodeLink: CreateNodeLink, deleteNodeLink: DeleteNodeLink, - getLinkedProjects: GetLinkedResources, }); constructor() { this.setupSearchEffect(); this.setupSearchSubscription(); - this.setupNodeLinksEffect(); } onSearchModeChange(mode: ResourceSearchMode): void { this.searchMode.set(mode); - this.currentPage.set(1); + this.resetToFirstPage(); } onObjectTypeChange(type: ResourceType): void { this.resourceType.set(type); - this.currentPage.set(1); + this.resetToFirstPage(); } onPageChange(event: TablePageEvent): void { @@ -126,19 +137,18 @@ export class LinkResourceDialogComponent { this.handleSearch(this.searchControl.value || '', this.searchMode(), this.resourceType()); } - setupSearchEffect() { - effect(() => { - this.currentPage.set(1); - this.handleSearch(this.searchControl.value || '', this.searchMode(), this.resourceType()); - }); + isItemLinked(itemId: string): boolean { + return this.linkedResourceIds().has(itemId); + } + + private resetToFirstPage(): void { + this.currentPage.set(1); } - setupNodeLinksEffect() { + setupSearchEffect() { effect(() => { - const currentProject = this.currentProject(); - if (currentProject) { - this.actions.getLinkedProjects(currentProject.id); - } + this.resetToFirstPage(); + this.handleSearch(this.searchControl.value || '', this.searchMode(), this.resourceType()); }); } @@ -146,31 +156,23 @@ export class LinkResourceDialogComponent { this.searchControl.valueChanges .pipe(debounceTime(300), distinctUntilChanged(), takeUntilDestroyed(this.destroyRef)) .subscribe((searchValue) => { - this.currentPage.set(1); + this.resetToFirstPage(); this.handleSearch(searchValue ?? '', this.searchMode(), this.resourceType()); }); } handleCloseDialog() { - const currentProjectId = this.currentProject()?.id; - - if (!currentProjectId) { - this.dialogRef.close(); - return; - } - - this.actions.getLinkedProjects(currentProjectId); - this.dialogRef.close(); + this.dialogRef.close({ hasChanges: this.hasChanges() }); } handleToggleNodeLink(resource: MyResourcesItem) { - const currentProjectId = this.currentProject()?.id; + const currentResourceId = this.currentResourceId(); - if (!currentProjectId) { + if (!currentResourceId) { return; } - const isCurrentlyLinked = this.isItemLinked()(resource.id); + const isCurrentlyLinked = this.isItemLinked(resource.id); if (isCurrentlyLinked) { const resources = this.linkedResources(); @@ -178,9 +180,9 @@ export class LinkResourceDialogComponent { if (!linkToDelete) return; - this.actions.deleteNodeLink(currentProjectId, linkToDelete); + this.actions.deleteNodeLink(currentResourceId, linkToDelete); } else { - this.actions.createNodeLink(currentProjectId, resource); + this.actions.createNodeLink(currentResourceId, resource); } } @@ -191,13 +193,13 @@ export class LinkResourceDialogComponent { }; untracked(() => { - const currentProjectId = this.currentProject()?.id; - if (!currentProjectId) return; + const currentResourceId = this.currentResourceId(); + if (!currentResourceId) return; if (resourceType === ResourceType.Project) { - this.actions.getProjects(this.currentPage(), this.tableRows, searchFilters, searchMode, currentProjectId); + this.actions.getProjects(this.currentPage(), this.tableRows, searchFilters, searchMode, undefined); } else if (resourceType === ResourceType.Registration) { - this.actions.getRegistrations(this.currentPage(), this.tableRows, searchFilters, searchMode, currentProjectId); + this.actions.getRegistrations(this.currentPage(), this.tableRows, searchFilters, searchMode, undefined); } }); } diff --git a/src/app/features/project/overview/components/linked-resources/linked-resources.component.html b/src/app/features/project/overview/components/linked-resources/linked-resources.component.html index b02a69622..550c84a5c 100644 --- a/src/app/features/project/overview/components/linked-resources/linked-resources.component.html +++ b/src/app/features/project/overview/components/linked-resources/linked-resources.component.html @@ -21,6 +21,7 @@

{{ linkedResource.title }}

+ @if (canEdit()) {
}
-
+

{{ 'common.labels.contributors' | translate }}:

@@ -53,4 +54,14 @@

} }

+ @if (hasMoreLinkedResources() && !isLinkedResourcesLoading()) { +
+ +
+ }
diff --git a/src/app/features/project/overview/components/linked-resources/linked-resources.component.spec.ts b/src/app/features/project/overview/components/linked-resources/linked-resources.component.spec.ts index 42f53a8f4..14e948bab 100644 --- a/src/app/features/project/overview/components/linked-resources/linked-resources.component.spec.ts +++ b/src/app/features/project/overview/components/linked-resources/linked-resources.component.spec.ts @@ -7,6 +7,7 @@ import { IconComponent } from '@osf/shared/components/icon/icon.component'; import { CustomDialogService } from '@osf/shared/services/custom-dialog.service'; import { NodeLinksSelectors } from '@osf/shared/stores/node-links'; +import { ProjectOverviewSelectors } from '../../store'; import { DeleteNodeLinkDialogComponent } from '../delete-node-link-dialog/delete-node-link-dialog.component'; import { LinkResourceDialogComponent } from '../link-resource-dialog/link-resource-dialog.component'; @@ -42,6 +43,9 @@ describe('LinkedProjectsComponent', () => { signals: [ { selector: NodeLinksSelectors.getLinkedResources, value: mockLinkedResources }, { selector: NodeLinksSelectors.getLinkedResourcesLoading, value: false }, + { selector: NodeLinksSelectors.hasMoreLinkedResources, value: false }, + { selector: NodeLinksSelectors.isLoadingMoreLinkedResources, value: false }, + { selector: ProjectOverviewSelectors.getProject, value: MOCK_NODE_WITH_ADMIN }, ], }), MockProvider(CustomDialogService, customDialogServiceMock), @@ -60,6 +64,7 @@ describe('LinkedProjectsComponent', () => { expect(customDialogServiceMock.open).toHaveBeenCalledWith(LinkResourceDialogComponent, { header: 'project.overview.dialog.linkProject.header', width: '850px', + closable: false, }); }); diff --git a/src/app/features/project/overview/components/linked-resources/linked-resources.component.ts b/src/app/features/project/overview/components/linked-resources/linked-resources.component.ts index eee5c3f0b..cd1d88944 100644 --- a/src/app/features/project/overview/components/linked-resources/linked-resources.component.ts +++ b/src/app/features/project/overview/components/linked-resources/linked-resources.component.ts @@ -1,18 +1,22 @@ -import { select } from '@ngxs/store'; +import { createDispatchMap, select } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; import { Button } from 'primeng/button'; import { Skeleton } from 'primeng/skeleton'; -import { ChangeDetectionStrategy, Component, inject, input } from '@angular/core'; +import { filter } from 'rxjs'; + +import { ChangeDetectionStrategy, Component, DestroyRef, inject, input } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { ContributorsListComponent } from '@osf/shared/components/contributors-list/contributors-list.component'; import { IconComponent } from '@osf/shared/components/icon/icon.component'; import { TruncatedTextComponent } from '@osf/shared/components/truncated-text/truncated-text.component'; import { CustomDialogService } from '@osf/shared/services/custom-dialog.service'; -import { NodeLinksSelectors } from '@osf/shared/stores/node-links'; +import { GetLinkedResources, LoadMoreLinkedResources, NodeLinksSelectors } from '@osf/shared/stores/node-links'; +import { ProjectOverviewSelectors } from '../../store'; import { DeleteNodeLinkDialogComponent } from '../delete-node-link-dialog/delete-node-link-dialog.component'; import { LinkResourceDialogComponent } from '../link-resource-dialog/link-resource-dialog.component'; @@ -25,28 +29,62 @@ import { LinkResourceDialogComponent } from '../link-resource-dialog/link-resour }) export class LinkedResourcesComponent { private customDialogService = inject(CustomDialogService); + private readonly destroyRef = inject(DestroyRef); canEdit = input.required(); linkedResources = select(NodeLinksSelectors.getLinkedResources); isLinkedResourcesLoading = select(NodeLinksSelectors.getLinkedResourcesLoading); + hasMoreLinkedResources = select(NodeLinksSelectors.hasMoreLinkedResources); + isLoadingMoreLinkedResources = select(NodeLinksSelectors.isLoadingMoreLinkedResources); + currentProject = select(ProjectOverviewSelectors.getProject); + + private readonly actions = createDispatchMap({ + getLinkedResources: GetLinkedResources, + loadMoreLinkedResources: LoadMoreLinkedResources, + }); openLinkProjectModal() { - this.customDialogService.open(LinkResourceDialogComponent, { - header: 'project.overview.dialog.linkProject.header', - width: '850px', - }); + const project = this.currentProject(); + + if (!project) return; + + this.customDialogService + .open(LinkResourceDialogComponent, { + header: 'project.overview.dialog.linkProject.header', + width: '850px', + closable: false, + }) + .onClose.pipe( + filter((data) => !!data?.hasChanges), + takeUntilDestroyed(this.destroyRef) + ) + .subscribe(() => this.actions.getLinkedResources(project.id)); } openDeleteResourceModal(resourceId: string): void { const currentLink = this.linkedResources().find((resource) => resource.id === resourceId); + const project = this.currentProject(); + + if (!currentLink || !project) return; + + this.customDialogService + .open(DeleteNodeLinkDialogComponent, { + header: 'project.overview.dialog.deleteNodeLink.header', + width: '650px', + data: { currentLink }, + }) + .onClose.pipe( + filter((data) => !!data?.hasChanges), + takeUntilDestroyed(this.destroyRef) + ) + .subscribe(() => this.actions.getLinkedResources(project.id)); + } - if (!currentLink) return; + loadMore(): void { + const project = this.currentProject(); + if (!project) return; - this.customDialogService.open(DeleteNodeLinkDialogComponent, { - header: 'project.overview.dialog.deleteNodeLink.header', - width: '650px', - data: { currentLink }, - }); + this.actions.loadMoreLinkedResources(project.id); } } diff --git a/src/app/shared/services/node-links.service.ts b/src/app/shared/services/node-links.service.ts index 3ced22bb2..870e9b7db 100644 --- a/src/app/shared/services/node-links.service.ts +++ b/src/app/shared/services/node-links.service.ts @@ -11,6 +11,7 @@ import { MyResourcesItem } from '../models/my-resources/my-resources.models'; import { NodeLinkJsonApi } from '../models/node-links/node-link-json-api.model'; import { NodeModel } from '../models/nodes/base-node.model'; import { NodesResponseJsonApi } from '../models/nodes/nodes-json-api.model'; +import { PaginatedData } from '../models/paginated-data.model'; import { JsonApiService } from './json-api.service'; @@ -60,25 +61,29 @@ export class NodeLinksService { ); } - fetchLinkedProjects(projectId: string): Observable { + fetchLinkedProjects(projectId: string, page: number, pageSize: number): Observable> { const params: Record = { embed: 'bibliographic_contributors', 'fields[users]': 'family_name,full_name,given_name,middle_name', + page, + 'page[size]': pageSize, }; return this.jsonApiService .get(`${this.apiUrl}/nodes/${projectId}/linked_nodes/`, params) - .pipe(map((response) => BaseNodeMapper.getNodesWithEmbedContributors(response.data))); + .pipe(map((response) => BaseNodeMapper.getNodesWithEmbedsAndTotalData(response))); } - fetchLinkedRegistrations(projectId: string): Observable { + fetchLinkedRegistrations(projectId: string, page: number, pageSize: number): Observable> { const params: Record = { embed: 'bibliographic_contributors', 'fields[users]': 'family_name,full_name,given_name,middle_name', + page, + 'page[size]': pageSize, }; return this.jsonApiService .get(`${this.apiUrl}/nodes/${projectId}/linked_registrations/`, params) - .pipe(map((response) => BaseNodeMapper.getNodesWithEmbedContributors(response.data))); + .pipe(map((response) => BaseNodeMapper.getNodesWithEmbedsAndTotalData(response))); } } diff --git a/src/app/shared/stores/node-links/node-links.actions.ts b/src/app/shared/stores/node-links/node-links.actions.ts index ed3d10145..9515fb1eb 100644 --- a/src/app/shared/stores/node-links/node-links.actions.ts +++ b/src/app/shared/stores/node-links/node-links.actions.ts @@ -1,3 +1,4 @@ +import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants/default-table-params.constants'; import { MyResourcesItem } from '@osf/shared/models/my-resources/my-resources.models'; import { NodeModel } from '@osf/shared/models/nodes/base-node.model'; @@ -13,6 +14,16 @@ export class CreateNodeLink { export class GetLinkedResources { static readonly type = '[Node Links] Get Linked Resources'; + constructor( + public projectId: string, + public page = 1, + public pageSize = DEFAULT_TABLE_PARAMS.rows + ) {} +} + +export class LoadMoreLinkedResources { + static readonly type = '[Node Links] Load More Linked Resources'; + constructor(public projectId: string) {} } diff --git a/src/app/shared/stores/node-links/node-links.model.ts b/src/app/shared/stores/node-links/node-links.model.ts index 5e892a858..1f65b0780 100644 --- a/src/app/shared/stores/node-links/node-links.model.ts +++ b/src/app/shared/stores/node-links/node-links.model.ts @@ -1,8 +1,18 @@ +import { DEFAULT_TABLE_PARAMS } from '@osf/shared/constants/default-table-params.constants'; import { NodeModel } from '@osf/shared/models/nodes/base-node.model'; import { AsyncStateModel } from '@osf/shared/models/store/async-state.model'; +export interface LinkedResourcesState extends AsyncStateModel { + page: number; + pageSize: number; + projectsTotalCount: number; + registrationsTotalCount: number; + isLoadingMore: boolean; + hasChanges: boolean; +} + export interface NodeLinksStateModel { - linkedResources: AsyncStateModel; + linkedResources: LinkedResourcesState; } export const NODE_LINKS_DEFAULTS: NodeLinksStateModel = { @@ -11,5 +21,11 @@ export const NODE_LINKS_DEFAULTS: NodeLinksStateModel = { isLoading: false, isSubmitting: false, error: null, + page: 1, + pageSize: DEFAULT_TABLE_PARAMS.rows, + projectsTotalCount: 0, + registrationsTotalCount: 0, + isLoadingMore: false, + hasChanges: false, }, }; diff --git a/src/app/shared/stores/node-links/node-links.selectors.ts b/src/app/shared/stores/node-links/node-links.selectors.ts index 1a6bdfd3d..e2e814db3 100644 --- a/src/app/shared/stores/node-links/node-links.selectors.ts +++ b/src/app/shared/stores/node-links/node-links.selectors.ts @@ -23,4 +23,22 @@ export class NodeLinksSelectors { static getLinkedResourcesSubmitting(state: NodeLinksStateModel) { return state.linkedResources.isSubmitting; } + + @Selector([NodeLinksState]) + static hasMoreLinkedResources(state: NodeLinksStateModel) { + const { page, pageSize, projectsTotalCount, registrationsTotalCount } = state.linkedResources; + const hasMoreProjects = projectsTotalCount > page * pageSize; + const hasMoreRegistrations = registrationsTotalCount > page * pageSize; + return hasMoreProjects || hasMoreRegistrations; + } + + @Selector([NodeLinksState]) + static isLoadingMoreLinkedResources(state: NodeLinksStateModel) { + return state.linkedResources.isLoadingMore; + } + + @Selector([NodeLinksState]) + static getNodeLinksHasChanges(state: NodeLinksStateModel) { + return state.linkedResources.hasChanges; + } } diff --git a/src/app/shared/stores/node-links/node-links.state.ts b/src/app/shared/stores/node-links/node-links.state.ts index 213a2cf4a..bbdbde0ea 100644 --- a/src/app/shared/stores/node-links/node-links.state.ts +++ b/src/app/shared/stores/node-links/node-links.state.ts @@ -1,12 +1,21 @@ import { Action, State, StateContext } from '@ngxs/store'; -import { catchError, forkJoin, tap, throwError } from 'rxjs'; +import { catchError, forkJoin, of, tap } from 'rxjs'; import { inject, Injectable } from '@angular/core'; +import { handleSectionError } from '@osf/shared/helpers/state-error.handler'; +import { NodeModel } from '@osf/shared/models/nodes/base-node.model'; +import { PaginatedData } from '@osf/shared/models/paginated-data.model'; import { NodeLinksService } from '@osf/shared/services/node-links.service'; -import { ClearNodeLinks, CreateNodeLink, DeleteNodeLink, GetLinkedResources } from './node-links.actions'; +import { + ClearNodeLinks, + CreateNodeLink, + DeleteNodeLink, + GetLinkedResources, + LoadMoreLinkedResources, +} from './node-links.actions'; import { NODE_LINKS_DEFAULTS, NodeLinksStateModel } from './node-links.model'; @State({ @@ -17,50 +26,108 @@ import { NODE_LINKS_DEFAULTS, NodeLinksStateModel } from './node-links.model'; export class NodeLinksState { nodeLinksService = inject(NodeLinksService); - @Action(CreateNodeLink) - createNodeLink(ctx: StateContext, action: CreateNodeLink) { + @Action(GetLinkedResources) + getLinkedResources(ctx: StateContext, action: GetLinkedResources) { const state = ctx.getState(); + + const page = action.page ?? state.linkedResources.page; + const pageSize = action.pageSize ?? state.linkedResources.pageSize; + ctx.patchState({ linkedResources: { ...state.linkedResources, - isSubmitting: true, + data: page === 1 ? [] : state.linkedResources.data, + isLoading: page === 1, + isLoadingMore: page > 1, + error: null, + hasChanges: false, }, }); - return this.nodeLinksService.createNodeLink(action.currentProjectId, action.resource).pipe( - tap(() => { - ctx.dispatch(new GetLinkedResources(action.currentProjectId)); + const emptyPaginatedData: PaginatedData = { + data: [], + totalCount: 0, + pageSize: pageSize, + }; + + const shouldFetchProjects = + page === 1 || + state.linkedResources.projectsTotalCount === 0 || + page <= Math.ceil(state.linkedResources.projectsTotalCount / pageSize); + + const shouldFetchRegistrations = + page === 1 || + state.linkedResources.registrationsTotalCount === 0 || + page <= Math.ceil(state.linkedResources.registrationsTotalCount / pageSize); + + const projectsObservable = shouldFetchProjects + ? this.nodeLinksService + .fetchLinkedProjects(action.projectId, page, pageSize) + .pipe(catchError(() => of(emptyPaginatedData))) + : of(emptyPaginatedData); + + const registrationsObservable = shouldFetchRegistrations + ? this.nodeLinksService + .fetchLinkedRegistrations(action.projectId, page, pageSize) + .pipe(catchError(() => of(emptyPaginatedData))) + : of(emptyPaginatedData); + + return forkJoin({ + linkedProjects: projectsObservable, + linkedRegistrations: registrationsObservable, + }).pipe( + tap(({ linkedProjects, linkedRegistrations }) => { + const data = + page === 1 + ? [...linkedProjects.data, ...linkedRegistrations.data] + : [...state.linkedResources.data, ...linkedProjects.data, ...linkedRegistrations.data]; + + ctx.patchState({ + linkedResources: { + ...state.linkedResources, + data, + isLoading: false, + isLoadingMore: false, + isSubmitting: false, + error: null, + page: page, + pageSize: pageSize, + projectsTotalCount: linkedProjects.totalCount, + registrationsTotalCount: linkedRegistrations.totalCount, + hasChanges: false, + }, + }); }), - catchError((error) => this.handleError(ctx, 'linkedResources', error)) + catchError((error) => handleSectionError(ctx, 'linkedResources', error)) ); } - @Action(GetLinkedResources) - getLinkedResources(ctx: StateContext, action: GetLinkedResources) { + @Action(LoadMoreLinkedResources) + loadMoreLinkedResources(ctx: StateContext, action: LoadMoreLinkedResources) { + const state = ctx.getState(); + const nextPage = state.linkedResources.page + 1; + const nextPageSize = state.linkedResources.pageSize; + + return ctx.dispatch(new GetLinkedResources(action.projectId, nextPage, nextPageSize)); + } + + @Action(CreateNodeLink) + createNodeLink(ctx: StateContext, action: CreateNodeLink) { const state = ctx.getState(); ctx.patchState({ linkedResources: { ...state.linkedResources, - isLoading: true, + isSubmitting: true, }, }); - return forkJoin({ - linkedProjects: this.nodeLinksService.fetchLinkedProjects(action.projectId), - linkedRegistrations: this.nodeLinksService.fetchLinkedRegistrations(action.projectId), - }).pipe( - tap(({ linkedProjects, linkedRegistrations }) => { - const combinedResources = [...linkedProjects, ...linkedRegistrations]; + return this.nodeLinksService.createNodeLink(action.currentProjectId, action.resource).pipe( + tap(() => { ctx.patchState({ - linkedResources: { - data: combinedResources, - isLoading: false, - isSubmitting: false, - error: null, - }, + linkedResources: { ...ctx.getState().linkedResources, isSubmitting: false, hasChanges: true }, }); }), - catchError((error) => this.handleError(ctx, 'linkedResources', error)) + catchError((error) => handleSectionError(ctx, 'linkedResources', error)) ); } @@ -72,23 +139,17 @@ export class NodeLinksState { linkedResources: { ...state.linkedResources, isSubmitting: true, + error: null, }, }); return this.nodeLinksService.deleteNodeLink(action.projectId, action.linkedResource).pipe( tap(() => { - const updatedResources = state.linkedResources.data.filter( - (resource) => resource.id !== action.linkedResource.id - ); ctx.patchState({ - linkedResources: { - data: updatedResources, - isLoading: false, - isSubmitting: false, - error: null, - }, + linkedResources: { ...ctx.getState().linkedResources, isSubmitting: false, hasChanges: true }, }); - }) + }), + catchError((error) => handleSectionError(ctx, 'linkedResources', error)) ); } @@ -96,16 +157,4 @@ export class NodeLinksState { clearNodeLinks(ctx: StateContext) { ctx.patchState(NODE_LINKS_DEFAULTS); } - - private handleError(ctx: StateContext, section: keyof NodeLinksStateModel, error: Error) { - ctx.patchState({ - [section]: { - ...ctx.getState()[section], - isLoading: false, - isSubmitting: false, - error: error.message, - }, - }); - return throwError(() => error); - } } diff --git a/src/app/shared/stores/wiki/wiki.state.ts b/src/app/shared/stores/wiki/wiki.state.ts index 6c0b109ca..050308677 100644 --- a/src/app/shared/stores/wiki/wiki.state.ts +++ b/src/app/shared/stores/wiki/wiki.state.ts @@ -1,9 +1,10 @@ import { Action, State, StateContext } from '@ngxs/store'; -import { catchError, map, tap, throwError } from 'rxjs'; +import { catchError, map, tap } from 'rxjs'; import { inject, Injectable } from '@angular/core'; +import { handleSectionError } from '@osf/shared/helpers/state-error.handler'; import { WikiService } from '@osf/shared/services/wiki.service'; import { @@ -52,7 +53,7 @@ export class WikiState { currentWikiId: wiki.id, }); }), - catchError((error) => this.handleError(ctx, error)) + catchError((error) => handleSectionError(ctx, 'wikiList', error)) ); } @@ -78,7 +79,7 @@ export class WikiState { currentWikiId: updatedWiki.id, }); }), - catchError((error) => this.handleError(ctx, error)) + catchError((error) => handleSectionError(ctx, 'wikiList', error)) ); } @@ -106,7 +107,7 @@ export class WikiState { currentWikiId: updatedList.length > 0 ? updatedList[0].id : '', }); }), - catchError((error) => this.handleError(ctx, error)) + catchError((error) => handleSectionError(ctx, 'wikiList', error)) ); } @@ -131,7 +132,7 @@ export class WikiState { }, }); }), - catchError((error) => this.handleError(ctx, error)) + catchError((error) => handleSectionError(ctx, 'homeWikiContent', error)) ); } @@ -182,7 +183,7 @@ export class WikiState { }); }), map((wiki) => wiki), - catchError((error) => this.handleError(ctx, error)) + catchError((error) => handleSectionError(ctx, 'wikiList', error)) ); } @@ -208,7 +209,7 @@ export class WikiState { }, }); }), - catchError((error) => this.handleError(ctx, error)) + catchError((error) => handleSectionError(ctx, 'componentsWikiList', error)) ); } @@ -233,23 +234,6 @@ export class WikiState { }); } - private handleError(ctx: StateContext, error: Error) { - ctx.patchState({ - homeWikiContent: { - ...ctx.getState().homeWikiContent, - isLoading: false, - error: error.message, - }, - wikiList: { - ...ctx.getState().wikiList, - isLoading: false, - isSubmitting: false, - error: error.message, - }, - }); - return throwError(() => error); - } - @Action(GetWikiVersions) getWikiVersions(ctx: StateContext, action: GetWikiVersions) { const state = ctx.getState(); @@ -271,7 +255,7 @@ export class WikiState { }, }); }), - catchError((error) => this.handleError(ctx, error)) + catchError((error) => handleSectionError(ctx, 'wikiVersions', error)) ); } @@ -292,7 +276,7 @@ export class WikiState { }, }); }), - catchError((error) => this.handleError(ctx, error)) + catchError((error) => handleSectionError(ctx, 'versionContent', error)) ); } @@ -318,7 +302,7 @@ export class WikiState { }, }); }), - catchError((error) => this.handleError(ctx, error)) + catchError((error) => handleSectionError(ctx, 'versionContent', error)) ); } @@ -343,7 +327,7 @@ export class WikiState { }, }); }), - catchError((error) => this.handleError(ctx, error)) + catchError((error) => handleSectionError(ctx, 'compareVersionContent', error)) ); } } diff --git a/src/testing/mocks/my-resources.mock.ts b/src/testing/mocks/my-resources.mock.ts new file mode 100644 index 000000000..fff22e697 --- /dev/null +++ b/src/testing/mocks/my-resources.mock.ts @@ -0,0 +1,39 @@ +import { MyResourcesItem } from '@shared/models/my-resources/my-resources.models'; + +import { MOCK_CONTRIBUTOR } from './contributors.mock'; + +export const MOCK_MY_RESOURCES_ITEM_PROJECT: MyResourcesItem = { + id: 'project-1', + type: 'nodes', + title: 'Test Project', + dateCreated: '2024-01-01T00:00:00.000Z', + dateModified: '2024-01-02T00:00:00.000Z', + isPublic: true, + contributors: [MOCK_CONTRIBUTOR], +}; + +export const MOCK_MY_RESOURCES_ITEM_PROJECT_PRIVATE: MyResourcesItem = { + id: 'project-2', + type: 'nodes', + title: 'Private Test Project', + dateCreated: '2024-01-03T00:00:00.000Z', + dateModified: '2024-01-04T00:00:00.000Z', + isPublic: false, + contributors: [], +}; + +export const MOCK_MY_RESOURCES_ITEM_REGISTRATION: MyResourcesItem = { + id: 'registration-1', + type: 'registrations', + title: 'Test Registration', + dateCreated: '2024-01-05T00:00:00.000Z', + dateModified: '2024-01-06T00:00:00.000Z', + isPublic: true, + contributors: [MOCK_CONTRIBUTOR], +}; + +export const MOCK_MY_RESOURCES_ITEMS: MyResourcesItem[] = [ + MOCK_MY_RESOURCES_ITEM_PROJECT, + MOCK_MY_RESOURCES_ITEM_PROJECT_PRIVATE, + MOCK_MY_RESOURCES_ITEM_REGISTRATION, +]; From b3bef660fea721612256237ad6cab434beb6b4eb Mon Sep 17 00:00:00 2001 From: nsemets Date: Mon, 1 Dec 2025 20:08:20 +0200 Subject: [PATCH 31/76] [ENG-9234] P10.1 - Bring back support for table of contents in wiki markdown #773 - Ticket: [ENG-9234] - Feature flag: n/a ## Summary of Changes 1. Added support for table of contents in wiki. --- angular.json | 2 + package-lock.json | 18 ++++++ package.json | 2 + src/@types/markdown-it-toc-done-right.d.ts | 27 +++++++++ .../markdown/markdown.component.html | 2 +- .../components/markdown/markdown.component.ts | 57 ++++++++++++++++++- 6 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 src/@types/markdown-it-toc-done-right.d.ts diff --git a/angular.json b/angular.json index 5108d40ce..7c60c3094 100644 --- a/angular.json +++ b/angular.json @@ -29,6 +29,8 @@ "cedar-embeddable-editor", "cedar-artifact-viewer", "markdown-it-video", + "markdown-it-anchor", + "markdown-it-toc-done-right", "ace-builds/src-noconflict/ext-language_tools", "@traptitech/markdown-it-katex", "@citation-js/core", diff --git a/package-lock.json b/package-lock.json index 451780058..ba1f04f4e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,8 @@ "chart.js": "^4.4.9", "diff": "^8.0.2", "markdown-it": "^14.1.0", + "markdown-it-anchor": "^9.2.0", + "markdown-it-toc-done-right": "^4.2.0", "markdown-it-video": "^0.6.3", "ngx-captcha": "^13.0.0", "ngx-cookie-service": "^19.1.2", @@ -18490,6 +18492,22 @@ "markdown-it": "bin/markdown-it.mjs" } }, + "node_modules/markdown-it-anchor": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-9.2.0.tgz", + "integrity": "sha512-sa2ErMQ6kKOA4l31gLGYliFQrMKkqSO0ZJgGhDHKijPf0pNFM9vghjAh3gn26pS4JDRs7Iwa9S36gxm3vgZTzg==", + "license": "Unlicense", + "peerDependencies": { + "@types/markdown-it": "*", + "markdown-it": "*" + } + }, + "node_modules/markdown-it-toc-done-right": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/markdown-it-toc-done-right/-/markdown-it-toc-done-right-4.2.0.tgz", + "integrity": "sha512-UB/IbzjWazwTlNAX0pvWNlJS8NKsOQ4syrXZQ/C72j+jirrsjVRT627lCaylrKJFBQWfRsPmIVQie8x38DEhAQ==", + "license": "MIT" + }, "node_modules/markdown-it-video": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/markdown-it-video/-/markdown-it-video-0.6.3.tgz", diff --git a/package.json b/package.json index 81b20effc..c77a86b06 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,8 @@ "chart.js": "^4.4.9", "diff": "^8.0.2", "markdown-it": "^14.1.0", + "markdown-it-anchor": "^9.2.0", + "markdown-it-toc-done-right": "^4.2.0", "markdown-it-video": "^0.6.3", "ngx-captcha": "^13.0.0", "ngx-cookie-service": "^19.1.2", diff --git a/src/@types/markdown-it-toc-done-right.d.ts b/src/@types/markdown-it-toc-done-right.d.ts new file mode 100644 index 000000000..b30608e7d --- /dev/null +++ b/src/@types/markdown-it-toc-done-right.d.ts @@ -0,0 +1,27 @@ +declare module 'markdown-it-toc-done-right' { + import { PluginWithOptions } from 'markdown-it'; + + export interface TocOptions { + placeholder: string; + slugify: (s: string) => string; + uniqueSlugStartIndex: number; + containerClass: string; + containerId: string; + listClass: string; + itemClass: string; + linkClass: string; + level: number | number[]; + listType: 'ol' | 'ul'; + format: (s: string) => string; + callback: (tocCode: string, ast: TocAst) => void; + } + + export interface TocAst { + l: number; + n: string; + c: TocAst[]; + } + + const markdownItTocDoneRight: PluginWithOptions>; + export default markdownItTocDoneRight; +} diff --git a/src/app/shared/components/markdown/markdown.component.html b/src/app/shared/components/markdown/markdown.component.html index 2a28f5f73..5216622ba 100644 --- a/src/app/shared/components/markdown/markdown.component.html +++ b/src/app/shared/components/markdown/markdown.component.html @@ -1 +1 @@ -
+
diff --git a/src/app/shared/components/markdown/markdown.component.ts b/src/app/shared/components/markdown/markdown.component.ts index 648f6e53b..47bf9d3c4 100644 --- a/src/app/shared/components/markdown/markdown.component.ts +++ b/src/app/shared/components/markdown/markdown.component.ts @@ -1,9 +1,22 @@ -import { ChangeDetectionStrategy, Component, computed, inject, input, Signal } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectionStrategy, + Component, + computed, + DestroyRef, + ElementRef, + inject, + input, + Signal, + ViewChild, +} from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { legacyImgSize } from '@mdit/plugin-img-size'; import markdownItKatex from '@traptitech/markdown-it-katex'; import MarkdownIt from 'markdown-it'; +import markdownItAnchor from 'markdown-it-anchor'; +import markdownItTocDoneRight from 'markdown-it-toc-done-right'; import markdownItVideo from 'markdown-it-video'; @Component({ @@ -13,11 +26,15 @@ import markdownItVideo from 'markdown-it-video'; styleUrl: './markdown.component.scss', changeDetection: ChangeDetectionStrategy.OnPush, }) -export class MarkdownComponent { +export class MarkdownComponent implements AfterViewInit { markdownText = input(''); + @ViewChild('container', { static: false }) containerRef?: ElementRef; + private md: MarkdownIt; private sanitizer = inject(DomSanitizer); + private destroyRef = inject(DestroyRef); + private clickHandler?: (event: MouseEvent) => void; renderedHtml: Signal = computed(() => { const result = this.md.render(this.markdownText()); @@ -39,6 +56,42 @@ export class MarkdownComponent { output: 'mathml', throwOnError: false, }) + .use(markdownItAnchor) + .use(markdownItTocDoneRight, { + placeholder: '@\\[toc\\]', + listType: 'ul', + }) .use(legacyImgSize); } + + ngAfterViewInit(): void { + this.setupClickHandler(); + } + + private setupClickHandler(): void { + if (!this.containerRef?.nativeElement) { + return; + } + + const container = this.containerRef.nativeElement; + + this.clickHandler = (event: MouseEvent) => { + const anchor = (event.target as HTMLElement).closest('a'); + if (!anchor?.hash) { + return; + } + + const targetElement = document.getElementById(anchor.hash.substring(1)); + if (targetElement) { + event.preventDefault(); + targetElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }; + + container.addEventListener('click', this.clickHandler); + + this.destroyRef.onDestroy(() => { + container.removeEventListener('click', this.clickHandler!); + }); + } } From 1f6bececcbae3ae41dfca20293d444df7181051c Mon Sep 17 00:00:00 2001 From: bodintsov Date: Mon, 1 Dec 2025 20:11:16 +0200 Subject: [PATCH 32/76] [ENG-9478] Feat/configure citation addons redirect (#798) - Ticket: [https://openscience.atlassian.net/browse/ENG-9478] - Feature flag: n/a ## Purpose Fix configuring citation addons to redirect a user to the proper page --- .../connect-configured-addon.component.ts | 8 +++++++- .../components/connect-addon/connect-addon.component.ts | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/app/features/project/project-addons/components/connect-configured-addon/connect-configured-addon.component.ts b/src/app/features/project/project-addons/components/connect-configured-addon/connect-configured-addon.component.ts index 26d157f23..116a6a183 100644 --- a/src/app/features/project/project-addons/components/connect-configured-addon/connect-configured-addon.component.ts +++ b/src/app/features/project/project-addons/components/connect-configured-addon/connect-configured-addon.component.ts @@ -192,7 +192,13 @@ export class ConnectConfiguredAddonComponent { complete: () => { const createdAddon = this.createdConfiguredAddon(); if (createdAddon) { - this.router.navigate([`${this.baseUrl()}/addons`]); + const addonType = this.addonTypeString()?.toLowerCase(); + this.router.navigate([`${this.baseUrl()}/addons`], { + queryParams: { + activeTab: 1, + addonType: addonType, + }, + }); this.toastService.showSuccess('settings.addons.toast.createSuccess', { addonName: AddonServiceNames[addon.externalServiceName as keyof typeof AddonServiceNames], }); diff --git a/src/app/features/settings/settings-addons/components/connect-addon/connect-addon.component.ts b/src/app/features/settings/settings-addons/components/connect-addon/connect-addon.component.ts index 4e6b67bad..cdd04e7e7 100644 --- a/src/app/features/settings/settings-addons/components/connect-addon/connect-addon.component.ts +++ b/src/app/features/settings/settings-addons/components/connect-addon/connect-addon.component.ts @@ -122,7 +122,13 @@ export class ConnectAddonComponent { } private showSuccessAndRedirect(createdAddon: AuthorizedAccountModel | null): void { - this.router.navigate([`${this.baseUrl()}/addons`]); + const addonType = this.addonTypeString()?.toLowerCase(); + this.router.navigate([`${this.baseUrl()}/addons`], { + queryParams: { + activeTab: 1, + addonType: addonType, + }, + }); this.toastService.showSuccess('settings.addons.toast.createSuccess', { addonName: AddonServiceNames[createdAddon?.externalServiceName as keyof typeof AddonServiceNames], }); From 000f563ae8dadd35a558f768a4496a4c5a6f9614 Mon Sep 17 00:00:00 2001 From: nsemets Date: Mon, 1 Dec 2025 20:32:24 +0200 Subject: [PATCH 33/76] [ENG-9164] [ENG-9241] Update markdown-it-atrules to work with new download links (#781) - Ticket: [ENG-9164] [ENG-9241] - Feature flag: n/a ## Summary of Changes 1. Add support for `atrules` for embed osf files in wiki. --- angular.json | 1 + package-lock.json | 4230 +++++++++++++++-- package.json | 1 + src/@types/markdown-it-atrules.d.ts | 6 + .../components/markdown/markdown.component.ts | 22 +- 5 files changed, 3916 insertions(+), 344 deletions(-) create mode 100644 src/@types/markdown-it-atrules.d.ts diff --git a/angular.json b/angular.json index 7c60c3094..ca15e5e62 100644 --- a/angular.json +++ b/angular.json @@ -33,6 +33,7 @@ "markdown-it-toc-done-right", "ace-builds/src-noconflict/ext-language_tools", "@traptitech/markdown-it-katex", + "@centerforopenscience/markdown-it-atrules", "@citation-js/core", "@citation-js/plugin-csl" ], diff --git a/package-lock.json b/package-lock.json index ba1f04f4e..266e54309 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "osf", - "version": "25.3.0", + "version": "25.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "osf", - "version": "25.3.0", + "version": "25.4.0", "dependencies": { "@angular/animations": "^19.2.0", "@angular/cdk": "^19.2.1", @@ -17,6 +17,7 @@ "@angular/platform-browser": "^19.2.0", "@angular/platform-browser-dynamic": "^19.2.0", "@angular/router": "^19.2.0", + "@centerforopenscience/markdown-it-atrules": "^0.1.1", "@citation-js/core": "^0.7.18", "@citation-js/plugin-csl": "^0.7.18", "@fortawesome/fontawesome-free": "^6.7.2", @@ -131,17 +132,17 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-19.2.17.tgz", - "integrity": "sha512-lbvzNoSjHlhP6bcHtFMlEQHG/Zxc1tTdwoelm4+AWPuQH4rGfoty4SXH4rr50SXVBUg9Zb4xZuChOYZmYKpGLQ==", + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-19.2.19.tgz", + "integrity": "sha512-uIxi6Vzss6+ycljVhkyPUPWa20w8qxJL9lEn0h6+sX/fhM8Djt0FHIuTQjoX58EoMaQ/1jrXaRaGimkbaFcG9A==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1902.17", - "@angular-devkit/build-webpack": "0.1902.17", - "@angular-devkit/core": "19.2.17", - "@angular/build": "19.2.17", + "@angular-devkit/architect": "0.1902.19", + "@angular-devkit/build-webpack": "0.1902.19", + "@angular-devkit/core": "19.2.19", + "@angular/build": "19.2.19", "@babel/core": "7.26.10", "@babel/generator": "7.26.10", "@babel/helper-annotate-as-pure": "7.25.9", @@ -152,7 +153,7 @@ "@babel/preset-env": "7.26.9", "@babel/runtime": "7.26.10", "@discoveryjs/json-ext": "0.6.3", - "@ngtools/webpack": "19.2.17", + "@ngtools/webpack": "19.2.19", "@vitejs/plugin-basic-ssl": "1.2.0", "ansi-colors": "4.1.3", "autoprefixer": "10.4.20", @@ -206,7 +207,7 @@ "@angular/localize": "^19.0.0 || ^19.2.0-next.0", "@angular/platform-server": "^19.0.0 || ^19.2.0-next.0", "@angular/service-worker": "^19.0.0 || ^19.2.0-next.0", - "@angular/ssr": "^19.2.17", + "@angular/ssr": "^19.2.19", "@web/test-runner": "^0.20.0", "browser-sync": "^3.0.2", "jest": "^29.5.0", @@ -256,6 +257,50 @@ } } }, + "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { + "version": "0.1902.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1902.19.tgz", + "integrity": "sha512-iexYDIYpGAeAU7T60bGcfrGwtq1bxpZixYxWuHYiaD1b5baQgNSfd1isGEOh37GgDNsf4In9i2LOLPm0wBdtgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.19", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.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": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.19.tgz", + "integrity": "sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.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/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -267,13 +312,13 @@ } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1902.17", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1902.17.tgz", - "integrity": "sha512-8NVJL7ujeTYKR1LgErkc5UN3EEoGYasqtu5AACXraFf9NLOw2p9N0+QY4cfjIwip1nyBp0RRzlBS4omGEymJCw==", + "version": "0.1902.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1902.19.tgz", + "integrity": "sha512-x2tlGg5CsUveFzuRuqeHknSbGirSAoRynEh+KqPRGK0G3WpMViW/M8SuVurecasegfIrDWtYZ4FnVxKqNbKwXQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1902.17", + "@angular-devkit/architect": "0.1902.19", "rxjs": "7.8.1" }, "engines": { @@ -286,6 +331,50 @@ "webpack-dev-server": "^5.0.2" } }, + "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { + "version": "0.1902.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1902.19.tgz", + "integrity": "sha512-iexYDIYpGAeAU7T60bGcfrGwtq1bxpZixYxWuHYiaD1b5baQgNSfd1isGEOh37GgDNsf4In9i2LOLPm0wBdtgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.19", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.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": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.19.tgz", + "integrity": "sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.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-webpack/node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -541,14 +630,14 @@ } }, "node_modules/@angular/build": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-19.2.17.tgz", - "integrity": "sha512-JrF9dSrsMip2xJzSz3zNoozBXu/OYg0bHuKfuPA/usPhz5AomJ2SQ2unvl6sDF00pTlgJohJMQ6SUHjylybn2g==", + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-19.2.19.tgz", + "integrity": "sha512-SFzQ1bRkNFiOVu+aaz+9INmts7tDUrsHLEr9HmARXr9qk5UmR8prlw39p2u+Bvi6/lCiJ18TZMQQl9mGyr63lg==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1902.17", + "@angular-devkit/architect": "0.1902.19", "@babel/core": "7.26.10", "@babel/helper-annotate-as-pure": "7.25.9", "@babel/helper-split-export-declaration": "7.24.7", @@ -571,7 +660,7 @@ "sass": "1.85.0", "semver": "7.7.1", "source-map-support": "0.5.21", - "vite": "6.3.6", + "vite": "6.4.1", "watchpack": "2.4.2" }, "engines": { @@ -588,7 +677,7 @@ "@angular/localize": "^19.0.0 || ^19.2.0-next.0", "@angular/platform-server": "^19.0.0 || ^19.2.0-next.0", "@angular/service-worker": "^19.0.0 || ^19.2.0-next.0", - "@angular/ssr": "^19.2.17", + "@angular/ssr": "^19.2.19", "karma": "^6.4.0", "less": "^4.2.0", "ng-packagr": "^19.0.0 || ^19.2.0-next.0", @@ -626,6 +715,60 @@ } } }, + "node_modules/@angular/build/node_modules/@angular-devkit/architect": { + "version": "0.1902.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1902.19.tgz", + "integrity": "sha512-iexYDIYpGAeAU7T60bGcfrGwtq1bxpZixYxWuHYiaD1b5baQgNSfd1isGEOh37GgDNsf4In9i2LOLPm0wBdtgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "19.2.19", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.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": "19.2.19", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.19.tgz", + "integrity": "sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.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/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/@angular/cdk": { "version": "19.2.19", "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.2.19.tgz", @@ -2821,6 +2964,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@centerforopenscience/markdown-it-atrules": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@centerforopenscience/markdown-it-atrules/-/markdown-it-atrules-0.1.1.tgz", + "integrity": "sha512-r3ZTJESVCpdU9tOQe2PVcUkJmrPA/1itvCtHpsJrhXMj/nY7JcESjgMSFEe432DcofXDzRF8kXW1XWQuH1BfMw==", + "license": "MIT", + "dependencies": { + "np": "^3.0.4" + } + }, "node_modules/@citation-js/core": { "version": "0.7.18", "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.18.tgz", @@ -4964,9 +5116,9 @@ } }, "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "license": "MIT", "dependencies": { @@ -5724,9 +5876,9 @@ } }, "node_modules/@jsonjoy.com/buffers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.0.0.tgz", - "integrity": "sha512-NDigYR3PHqCnQLXYyoLbnEdzMMvzeiCWo1KOut7Q0CoIqg9tUAPKJ1iq/2nFhc5kZtexzutNY0LFjdwWL3Dw3Q==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/buffers/-/buffers-1.2.1.tgz", + "integrity": "sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5758,19 +5910,20 @@ } }, "node_modules/@jsonjoy.com/json-pack": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.14.0.tgz", - "integrity": "sha512-LpWbYgVnKzphN5S6uss4M25jJ/9+m6q6UJoeN6zTkK4xAGhKsiBRPVeF7OYMWonn5repMQbE5vieRXcMUrKDKw==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.21.0.tgz", + "integrity": "sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==", "dev": true, "license": "Apache-2.0", "dependencies": { "@jsonjoy.com/base64": "^1.1.2", - "@jsonjoy.com/buffers": "^1.0.0", + "@jsonjoy.com/buffers": "^1.2.0", "@jsonjoy.com/codegen": "^1.0.0", - "@jsonjoy.com/json-pointer": "^1.0.1", + "@jsonjoy.com/json-pointer": "^1.0.2", "@jsonjoy.com/util": "^1.9.0", "hyperdyperid": "^1.2.0", - "thingies": "^2.5.0" + "thingies": "^2.5.0", + "tree-dump": "^1.1.0" }, "engines": { "node": ">=10.0" @@ -6449,9 +6602,9 @@ "license": "MIT" }, "node_modules/@ngtools/webpack": { - "version": "19.2.17", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-19.2.17.tgz", - "integrity": "sha512-HpbOLwS8tIW041UXcMqwfySqpZ9ztObH8U4NWKwjPBe0S5UDnF6doW2rS3GQm71hkiuB8sqbxOWz5I/NNvZFNQ==", + "version": "19.2.19", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-19.2.19.tgz", + "integrity": "sha512-R9aeTrOBiRVl8I698JWPniUAAEpSvzc8SUGWSM5UXWMcHnWqd92cOnJJ1aXDGJZKXrbhMhCBx9Dglmcks5IDpg==", "dev": true, "license": "MIT", "engines": { @@ -6697,9 +6850,9 @@ } }, "node_modules/@npmcli/package-json/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -7402,9 +7555,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", - "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", + "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", "cpu": [ "loong64" ], @@ -7444,9 +7597,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", - "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", + "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", "cpu": [ "ppc64" ], @@ -7472,9 +7625,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", - "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", + "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", "cpu": [ "riscv64" ], @@ -7528,9 +7681,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", - "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", + "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", "cpu": [ "arm64" ], @@ -7570,9 +7723,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", - "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", + "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", "cpu": [ "x64" ], @@ -7604,6 +7757,26 @@ "dev": true, "license": "MIT" }, + "node_modules/@samverschueren/stream-to-observable": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz", + "integrity": "sha512-c/qwwcHyafOQuVQJj0IlBjf5yYgBI7YPJ77k4fOJYesb41jio65eaJODRUmfYKhTOFBrIZ66kgvGPlNbjuoRdQ==", + "license": "MIT", + "dependencies": { + "any-observable": "^0.3.0" + }, + "engines": { + "node": ">=6" + }, + "peerDependenciesMeta": { + "rxjs": { + "optional": true + }, + "zen-observable": { + "optional": true + } + } + }, "node_modules/@schematics/angular": { "version": "19.2.17", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-19.2.17.tgz", @@ -7803,6 +7976,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/@sindresorhus/merge-streams": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", @@ -7836,6 +8018,18 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "license": "MIT", + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@thednp/event-listener": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/@thednp/event-listener/-/event-listener-2.0.10.tgz", @@ -8076,22 +8270,22 @@ "license": "MIT" }, "node_modules/@types/express": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", - "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz", + "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==", "dev": true, "license": "MIT", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", "@types/qs": "*", - "@types/serve-static": "*" + "@types/serve-static": "^1" } }, "node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", + "version": "4.19.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.7.tgz", + "integrity": "sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==", "dev": true, "license": "MIT", "dependencies": { @@ -8280,13 +8474,12 @@ "license": "MIT" }, "node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/mime": "^1", "@types/node": "*" } }, @@ -8301,15 +8494,26 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", + "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", "dev": true, "license": "MIT", "dependencies": { "@types/http-errors": "*", "@types/node": "*", - "@types/send": "*" + "@types/send": "<1" + } + }, + "node_modules/@types/serve-static/node_modules/@types/send": { + "version": "0.17.6", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", + "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" } }, "node_modules/@types/sockjs": { @@ -9430,6 +9634,58 @@ "@angular/core": "^19.0.0" } }, + "node_modules/ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha512-TdlOggdA/zURfMYa7ABC66j+oqfMew58KpJMbUlH3bcZP1b+cBHIHDDn5uH9INsxrHBPjsqM0tDB4jPTF/vgJA==", + "license": "ISC", + "dependencies": { + "string-width": "^2.0.0" + } + }, + "node_modules/ansi-align/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "license": "MIT", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ansi-align/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -9495,6 +9751,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/any-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", + "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -9578,6 +9843,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -9615,6 +9889,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "license": "MIT", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/array.prototype.findlastindex": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", @@ -9697,6 +9992,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -10028,7 +10332,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/base64-arraybuffer": { @@ -10240,21 +10543,162 @@ "pnpm": ">=8.6.0" } }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, + "node_modules/boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, + "node_modules/boxen/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/boxen/node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/boxen/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/boxen/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/boxen/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/boxen/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/boxen/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/boxen/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "license": "MIT", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/boxen/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/boxen/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -10364,6 +10808,12 @@ "dev": true, "license": "MIT" }, + "node_modules/builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "license": "MIT" + }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", @@ -10425,9 +10875,9 @@ } }, "node_modules/cacache/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -10486,11 +10936,11 @@ } }, "node_modules/cacache/node_modules/tar": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz", - "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", + "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", @@ -10512,6 +10962,63 @@ "node": ">=18" } }, + "node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cacheable-request/node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", + "license": "MIT" + }, + "node_modules/cacheable-request/node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -10591,6 +11098,29 @@ "node": ">=6" } }, + "node_modules/camelcase-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-4.2.0.tgz", + "integrity": "sha512-Ej37YKYbFUI8QiYlvj9YHb6/Z60dZyPJW0Cs8sFilMbd2lP0bw3ylAq9yJkK4lcTA2dID5fG8LjmJYbO7kWb7Q==", + "license": "MIT", + "dependencies": { + "camelcase": "^4.1.0", + "map-obj": "^2.0.0", + "quick-lru": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001745", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001745.tgz", @@ -10612,6 +11142,18 @@ ], "license": "CC-BY-4.0" }, + "node_modules/capture-stack-trace": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.2.tgz", + "integrity": "sha512-X/WM2UQs6VMHUtjUDnZTRI+i1crWteJySFzr9UpGoQa4WQffXVTTXuekjl7TjZRlcF2XfjgITT0HxZ9RnxeT0w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cedar-artifact-viewer": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/cedar-artifact-viewer/-/cedar-artifact-viewer-0.9.5.tgz", @@ -10775,6 +11317,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha512-3Fo5wu8Ytle8q9iCzS4D2MWVL2X7JVWRiS1BnXbTFDhS9c/REkM9vd1AmabsoZoY5/dGi5TT9iKL8Kb6DeBRQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", @@ -10998,6 +11549,18 @@ "node": ">=0.10.0" } }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -11016,6 +11579,15 @@ "dev": true, "license": "MIT" }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", @@ -11195,9 +11767,84 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, "license": "MIT" }, + "node_modules/configstore": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz", + "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", + "license": "BSD-2-Clause", + "dependencies": { + "dot-prop": "^4.2.1", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/configstore/node_modules/dot-prop": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", + "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", + "license": "MIT", + "dependencies": { + "is-obj": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/configstore/node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/configstore/node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/configstore/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/configstore/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/configstore/node_modules/write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "license": "ISC", + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, "node_modules/connect": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", @@ -11472,6 +12119,18 @@ "typescript": ">=5" } }, + "node_modules/create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==", + "license": "MIT", + "dependencies": { + "capture-stack-trace": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -11542,6 +12201,15 @@ "node": ">= 8" } }, + "node_modules/crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha512-GsVpkFPlycH7/fRR7Dhcmnoii54gV1nz7y4CWyeFS14N+JVBBhY+r8amRHE4BwSYal7BPTDp8isvAlCxyFt3Hg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/css-loader": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", @@ -11648,6 +12316,18 @@ "dev": true, "license": "MIT" }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng==", + "license": "MIT", + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/dargs": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz", @@ -11740,6 +12420,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-fns": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -11772,18 +12458,54 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/decimal.js": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "dev": true, - "license": "MIT" + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } }, "node_modules/dedent": { "version": "1.7.0", @@ -11800,6 +12522,15 @@ } } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -11818,9 +12549,9 @@ } }, "node_modules/default-browser": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", - "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.4.0.tgz", + "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==", "dev": true, "license": "MIT", "dependencies": { @@ -11835,9 +12566,9 @@ } }, "node_modules/default-browser-id": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", - "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", "dev": true, "license": "MIT", "engines": { @@ -11860,6 +12591,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "license": "MIT" + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -11909,6 +12646,109 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/del": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", + "integrity": "sha512-7yjqSoVSlJzA4t/VUwazuEagGeANEKB3f/aNI//06pfKgwoCb7f6Q1gETN1sZzYaj6chTQ0AhIwDiPdfOjko4A==", + "license": "MIT", + "dependencies": { + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/del/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/del/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/del/node_modules/globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", + "license": "MIT", + "dependencies": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/del/node_modules/p-map": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", + "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/del/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -11941,9 +12781,9 @@ } }, "node_modules/detect-libc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.1.tgz", - "integrity": "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -12135,6 +12975,12 @@ "dev": true, "license": "MIT" }, + "node_modules/duplexer3": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", + "license": "BSD-3-Clause" + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -12172,6 +13018,15 @@ "dev": true, "license": "ISC" }, + "node_modules/elegant-spinner": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", + "integrity": "sha512-B+ZM+RXvRqQaAmkMlO/oSe5nMUOaUnyfGYCEHoR8wrXsZR2mA0XVibsxV1bvTwxdRWah1PkQqso2EzhILGHtEQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/emitter-component": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", @@ -12275,7 +13130,6 @@ "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "dev": true, "license": "MIT", "dependencies": { "once": "^1.4.0" @@ -12355,7 +13209,6 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" @@ -13557,6 +14410,38 @@ "node": ">= 0.6" } }, + "node_modules/external-editor": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz", + "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==", + "license": "MIT", + "dependencies": { + "chardet": "^0.4.0", + "iconv-lite": "^0.4.17", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/external-editor/node_modules/chardet": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.4.2.tgz", + "integrity": "sha512-j/Toj7f1z98Hh2cYo2BVr85EpIRWqUi7rtRSGxh/cqUjqrnJe9l9UE7IUGd2vQ2p+kSHLkSzObQPZPLUC6TQwg==", + "license": "MIT" + }, + "node_modules/external-editor/node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fancy-log": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-2.0.0.tgz", @@ -13711,6 +14596,27 @@ "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", "license": "MIT" }, + "node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/figures/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -14029,7 +14935,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, "license": "ISC" }, "node_modules/fsevents": { @@ -14051,7 +14956,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -14219,16 +15123,22 @@ "node": ">=16" } }, + "node_modules/github-url-from-git": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/github-url-from-git/-/github-url-from-git-1.5.0.tgz", + "integrity": "sha512-WWOec4aRI7YAykQ9+BHmzjyNlkfJFG8QLXnDTsLz/kZefq7qkzdfo4p6fkYYMIq1aj+gZcQs/1HQhQh3DPPxlQ==", + "license": "MIT" + }, "node_modules/glob": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", - "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", - "minimatch": "^10.0.3", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -14257,9 +15167,9 @@ } }, "node_modules/glob-to-regex.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.0.1.tgz", - "integrity": "sha512-CG/iEvgQqfzoVsMUbxSJcwbG2JwyZ3naEqPkeltwl0BSS8Bp83k3xlGms+0QdWFUAwV+uvo80wNswKF6FWEkKg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/glob-to-regex.js/-/glob-to-regex.js-1.2.0.tgz", + "integrity": "sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -14281,11 +15191,11 @@ "license": "BSD-2-Clause" }, "node_modules/glob/node_modules/minimatch": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", - "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/brace-expansion": "^5.0.0" }, @@ -14322,6 +15232,24 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", + "license": "MIT", + "dependencies": { + "ini": "^1.3.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/global-dirs/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -14399,11 +15327,44 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/got/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, "license": "ISC" }, "node_modules/graphemer": { @@ -14478,6 +15439,27 @@ "node": ">=0.10.0" } }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -14559,11 +15541,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-yarn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-1.0.0.tgz", + "integrity": "sha512-UAI4b48aqrdez88CwMfC9s+gcJ25O1qg0/hS5eKOsIF5tOw2EYcgGsryYF6TEI5G8SeCYzFBt5Z04D/BDABYSQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -14774,7 +15764,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "dev": true, "license": "BSD-2-Clause" }, "node_modules/http-deceiver": { @@ -15092,6 +16081,15 @@ "node": ">=4" } }, + "node_modules/import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", @@ -15206,18 +16204,25 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz", + "integrity": "sha512-BYqTHXTGUIvg7t1r4sJNKcbDZkL92nkXA8YtRpbjFHRHGDL/NtUeiBJMeE60kIFN/Mg8ESaWQvftaYMGJzQZCQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", @@ -15228,7 +16233,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, "license": "ISC" }, "node_modules/ini": { @@ -15241,23 +16245,255 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "dev": true, + "node_modules/inquirer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-5.2.0.tgz", + "integrity": "sha512-E9BmnJbAKLPGonz0HeWHtbKf+EeSP93paWO3ZYoUpq/aowXvYGjjCSuashhXPpzbArIjBbji39THkxTz9ZeEUQ==", "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.1.0", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^5.5.2", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" }, "engines": { - "node": ">= 0.4" + "node": ">=6.0.0" } }, - "node_modules/ip-address": { - "version": "10.0.1", + "node_modules/inquirer/node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "license": "ISC" + }, + "node_modules/inquirer/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/inquirer/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", + "license": "ISC" + }, + "node_modules/inquirer/node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "license": "MIT", + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "license": "Apache-2.0", + "dependencies": { + "symbol-observable": "1.0.1" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/inquirer/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/inquirer/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "license": "MIT", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/inquirer/node_modules/symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha512-Kb3PrPYz4HanVF1LVGuAdW6LoVgIwjUYJGzFe7NDrBLCN4lsV/5J0MFurV+ygS4bRVwrCEt2c7MQ1R2a72oJDw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ip-address": { + "version": "10.0.1", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", "dev": true, @@ -15266,6 +16502,15 @@ "node": ">= 12" } }, + "node_modules/ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ipaddr.js": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", @@ -15298,7 +16543,6 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, "license": "MIT" }, "node_modules/is-async-function": { @@ -15380,11 +16624,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "license": "MIT", + "dependencies": { + "ci-info": "^1.5.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-ci/node_modules/ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "license": "MIT" + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -15547,6 +16808,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha512-ERNhMg+i/XgDwPIPF3u24qpajVreaiSuvpb1Uu0jugw7KKcxGyCX8cgp8P5fwTmAuXku6beDHHECdKArjlg7tw==", + "license": "MIT", + "dependencies": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -15596,6 +16870,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha512-9r39FIr3d+KD9SbX0sfMsHzb5PP3uimOiwr3YupUaUFG4W0l1U57Rx3utpttV7qz5U3jmrO5auUa04LU9pyHsg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -15633,6 +16916,60 @@ "node": ">=8" } }, + "node_modules/is-observable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-1.1.0.tgz", + "integrity": "sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==", + "license": "MIT", + "dependencies": { + "symbol-observable": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-observable/node_modules/symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-cwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", + "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-in-cwd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", + "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", + "license": "MIT", + "dependencies": { + "is-path-inside": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", + "license": "MIT", + "dependencies": { + "path-is-inside": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-plain-obj": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", @@ -15663,6 +17000,21 @@ "dev": true, "license": "MIT" }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "license": "MIT" + }, + "node_modules/is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -15682,6 +17034,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-scoped": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-scoped/-/is-scoped-1.0.0.tgz", + "integrity": "sha512-iT1y0qJcdqXnHe6SCtN9cOBPRiarw8Cy1EZkawW50dxO/7oHC6AYvs1tH4QbBbi7UC/vYY3BnRmbE0bFLwvUog==", + "license": "MIT", + "dependencies": { + "scoped-regex": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/is-set": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", @@ -15801,6 +17174,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-url-superb": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-url-superb/-/is-url-superb-3.0.0.tgz", + "integrity": "sha512-3faQP+wHCGDQT1qReM5zCPx2mxoal6DzbzquFlCYJLWyy4WPTved33ea2xFbX37z4NoriEwZGIYhFtx8RUB5wQ==", + "license": "MIT", + "dependencies": { + "url-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -15881,7 +17266,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, "node_modules/isobject": { @@ -15894,6 +17278,15 @@ "node": ">=0.10.0" } }, + "node_modules/issue-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/issue-regex/-/issue-regex-2.0.0.tgz", + "integrity": "sha512-flaQ/45dMqCYSMzBQI/h3bcto6T70uN7kjNnI8n3gQU6no5p+QcnMWBNXkraED0YvbUymxKaqdvgPa09RZQM5A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -17310,9 +18703,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -17451,8 +18844,14 @@ "dev": true, "license": "MIT" }, - "node_modules/json-parse-even-better-errors": { - "version": "4.0.0", + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "4.0.0", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz", "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==", "dev": true, @@ -17606,10 +19005,22 @@ "node": ">=6" } }, + "node_modules/latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha512-Be1YRHWWlZaSsrz2U+VInk+tO0EwLIyV+23RhWLINJYwg/UIikxjlj3MhH37/6/EDCAusjajvMkMMUXRaMWl/w==", + "license": "MIT", + "dependencies": { + "package-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/launch-editor": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.11.1.tgz", - "integrity": "sha512-SEET7oNfgSaB6Ym0jufAdCeo3meJVeCaaDyzRygy0xsp2BFKCprcfHljTq4QkzTLUxEKkFK6OK4811YM2oSrRg==", + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.12.0.tgz", + "integrity": "sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg==", "dev": true, "license": "MIT", "dependencies": { @@ -17948,6 +19359,743 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/listr": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/listr/-/listr-0.14.3.tgz", + "integrity": "sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==", + "license": "MIT", + "dependencies": { + "@samverschueren/stream-to-observable": "^0.3.0", + "is-observable": "^1.1.0", + "is-promise": "^2.1.0", + "is-stream": "^1.1.0", + "listr-silent-renderer": "^1.1.1", + "listr-update-renderer": "^0.5.0", + "listr-verbose-renderer": "^0.5.0", + "p-map": "^2.0.0", + "rxjs": "^6.3.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/listr-input": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/listr-input/-/listr-input-0.1.3.tgz", + "integrity": "sha512-dvjSD1MrWGXxxPixpMQlSBmkyqhJrPxGo30un25k/vlvFOWZj70AauU+YkEh7CA8vmpkE6Wde37DJDmqYqF39g==", + "license": "MIT", + "dependencies": { + "inquirer": "^3.3.0", + "rxjs": "^5.5.2", + "through": "^2.3.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "license": "ISC" + }, + "node_modules/listr-input/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/listr-input/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/listr-input/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/listr-input/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/inquirer": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-3.3.0.tgz", + "integrity": "sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.0", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^2.0.4", + "figures": "^2.0.0", + "lodash": "^4.3.0", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rx-lite": "^4.0.8", + "rx-lite-aggregates": "^4.0.8", + "string-width": "^2.1.0", + "strip-ansi": "^4.0.0", + "through": "^2.3.6" + } + }, + "node_modules/listr-input/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", + "license": "ISC" + }, + "node_modules/listr-input/node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "license": "MIT", + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "license": "Apache-2.0", + "dependencies": { + "symbol-observable": "1.0.1" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/listr-input/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/listr-input/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "license": "MIT", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-input/node_modules/symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha512-Kb3PrPYz4HanVF1LVGuAdW6LoVgIwjUYJGzFe7NDrBLCN4lsV/5J0MFurV+ygS4bRVwrCEt2c7MQ1R2a72oJDw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/listr-silent-renderer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz", + "integrity": "sha512-L26cIFm7/oZeSNVhWB6faeorXhMg4HNlb/dS/7jHhr708jxlXrtrBWo4YUxZQkc6dGoxEAe6J/D3juTRBUzjtA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-update-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz", + "integrity": "sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==", + "license": "MIT", + "dependencies": { + "chalk": "^1.1.3", + "cli-truncate": "^0.2.1", + "elegant-spinner": "^1.0.1", + "figures": "^1.7.0", + "indent-string": "^3.0.0", + "log-symbols": "^1.0.2", + "log-update": "^2.3.0", + "strip-ansi": "^3.0.1" + }, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "listr": "^0.14.2" + } + }, + "node_modules/listr-update-renderer/node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-update-renderer/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/listr-update-renderer/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/listr-update-renderer/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/listr-update-renderer/node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-update-renderer/node_modules/cli-truncate": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-0.2.1.tgz", + "integrity": "sha512-f4r4yJnbT++qUPI9NR4XLDLq41gQ+uqnPItWG0F5ZkehuNiTTa3EY0S4AqTSUOeJ7/zU41oWPQSNkW5BqPL9bg==", + "license": "MIT", + "dependencies": { + "slice-ansi": "0.0.4", + "string-width": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/listr-update-renderer/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/listr-update-renderer/node_modules/figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/listr-update-renderer/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "license": "MIT", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/listr-update-renderer/node_modules/log-symbols": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-1.0.2.tgz", + "integrity": "sha512-mmPrW0Fh2fxOzdBbFv4g1m6pR72haFLPJ2G5SJEELf1y+iaQrDG6cWCPjy54RHYbZAt7X+ls690Kw62AdWXBzQ==", + "license": "MIT", + "dependencies": { + "chalk": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/listr-update-renderer/node_modules/log-update": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", + "integrity": "sha512-vlP11XfFGyeNQlmEn9tJ66rEW1coA/79m5z6BCkudjbAGE83uhAcGYrBFwfs3AdLiLzGRusRPAbSPK9xZteCmg==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^3.0.0", + "cli-cursor": "^2.0.0", + "wrap-ansi": "^3.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-update-renderer/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-update-renderer/node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-update-renderer/node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "license": "MIT", + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-update-renderer/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/listr-update-renderer/node_modules/slice-ansi": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", + "integrity": "sha512-up04hB2hR92PgjpyU3y/eg91yIBILyjVY26NvvciY3EVVPjybkMszMpXQ9QAkcS3I5rtJBDLoTxxg+qvW8c7rw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/listr-update-renderer/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", + "license": "MIT", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/listr-update-renderer/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/listr-update-renderer/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/listr-update-renderer/node_modules/wrap-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", + "integrity": "sha512-iXR3tDXpbnTpzjKSylUJRkLuOrEC7hwEB221cgn6wtF8wpmz28puFXAEfPT5zrjM3wahygB//VuWEr1vTkDcNQ==", + "license": "MIT", + "dependencies": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-update-renderer/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-update-renderer/node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-update-renderer/node_modules/wrap-ansi/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "license": "MIT", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-update-renderer/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-verbose-renderer": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz", + "integrity": "sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==", + "license": "MIT", + "dependencies": { + "chalk": "^2.4.1", + "cli-cursor": "^2.1.0", + "date-fns": "^1.27.2", + "figures": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-verbose-renderer/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-verbose-renderer/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-verbose-renderer/node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-verbose-renderer/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/listr-verbose-renderer/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/listr-verbose-renderer/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/listr-verbose-renderer/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-verbose-renderer/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-verbose-renderer/node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-verbose-renderer/node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "license": "MIT", + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr-verbose-renderer/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/listr-verbose-renderer/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/listr/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/listr/node_modules/p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/listr/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/listr/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, "node_modules/listr2": { "version": "8.2.5", "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", @@ -18056,6 +20204,52 @@ "@lmdb/lmdb-win32-x64": "3.2.6" } }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -18096,7 +20290,6 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, "license": "MIT" }, "node_modules/lodash.camelcase": { @@ -18176,6 +20369,12 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.zip": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", + "integrity": "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==", + "license": "MIT" + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -18372,6 +20571,34 @@ "dev": true, "license": "MIT" }, + "node_modules/loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ==", + "license": "MIT", + "dependencies": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/loud-rejection/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -18468,6 +20695,15 @@ "tmpl": "1.0.5" } }, + "node_modules/map-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-2.0.0.tgz", + "integrity": "sha512-TzQSV2DiMYgoF5RycneKVUzIa9bQsj/B3tTgsE3dOGqlzHnGIDaC7XBE7grnA+8kZPnfqSGFe95VHc2oc0VFUQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/map-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", @@ -18567,9 +20803,9 @@ } }, "node_modules/memfs": { - "version": "4.47.0", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.47.0.tgz", - "integrity": "sha512-Xey8IZA57tfotV/TN4d6BmccQuhFP+CqRiI7TTNdipZdZBzF2WnzUcH//Cudw6X4zJiUbo/LTuU/HPA/iC/pNg==", + "version": "4.51.0", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.51.0.tgz", + "integrity": "sha512-4zngfkVM/GpIhC8YazOsM6E8hoB33NP0BCESPOA6z7qaL6umPJNqkO8CNYaLV2FB2MV6H1O3x2luHHOSqppv+A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -18721,6 +20957,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/mini-css-extract-plugin": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", @@ -18769,12 +21014,33 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minimist-options": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-3.0.2.tgz", + "integrity": "sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ==", + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/minimist-options/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -19219,6 +21485,12 @@ "font-awesome": ">=4.0.0" } }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "license": "MIT" + }, "node_modules/node-addon-api": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", @@ -19327,110 +21599,442 @@ "dev": true, "license": "BlueOak-1.0.0", "engines": { - "node": ">=18" + "node": ">=18" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/node-gyp/node_modules/tar": { + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", + "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", + "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/node-gyp/node_modules/yallist": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.21", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", + "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", + "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^3.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "license": "ISC" + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/np": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/np/-/np-3.1.0.tgz", + "integrity": "sha512-3HTje97SzbsvK9g61C72PpDk9AloaaTn0K7xHbx7jMrs9vJtCZqu7TWUGxrcYGiKRO/uFRn5SiRZfYB/gpL9Iw==", + "license": "MIT", + "dependencies": { + "@samverschueren/stream-to-observable": "^0.3.0", + "any-observable": "^0.3.0", + "chalk": "^2.3.0", + "del": "^3.0.0", + "execa": "^0.10.0", + "github-url-from-git": "^1.5.0", + "has-yarn": "^1.0.0", + "inquirer": "^5.2.0", + "is-scoped": "^1.0.0", + "issue-regex": "^2.0.0", + "listr": "^0.14.1", + "listr-input": "^0.1.1", + "log-symbols": "^2.1.0", + "meow": "^5.0.0", + "npm-name": "^5.0.0", + "p-timeout": "^2.0.1", + "read-pkg-up": "^3.0.0", + "rxjs": "^6.2.0", + "semver": "^5.2.0", + "split": "^1.0.0", + "terminal-link": "^1.1.0", + "update-notifier": "^2.1.0" + }, + "bin": { + "np": "cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/np/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/np/node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha512-FxAv7HpHrXbh3aPo4o2qxHay2lkLY3x5Mw3KeE4KQE8ysVfziWeRZDwcjauvwBSGEC/nXUPzZy8zeh4HokqOnw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/np/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/np/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/np/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/np/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/np/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/np/node_modules/execa": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.10.0.tgz", + "integrity": "sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^6.0.0", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/np/node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/np/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/node-gyp/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", - "dev": true, - "license": "ISC", + "node_modules/np/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "license": "MIT", "engines": { - "node": ">=16" + "node": ">=0.10.0" } }, - "node_modules/node-gyp/node_modules/tar": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.1.tgz", - "integrity": "sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==", - "dev": true, - "license": "ISC", + "node_modules/np/node_modules/log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "license": "MIT", "dependencies": { - "@isaacs/fs-minipass": "^4.0.0", - "chownr": "^3.0.0", - "minipass": "^7.1.2", - "minizlib": "^3.1.0", - "yallist": "^5.0.0" + "chalk": "^2.0.1" }, "engines": { - "node": ">=18" + "node": ">=4" } }, - "node_modules/node-gyp/node_modules/which": { + "node_modules/np/node_modules/meow": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz", - "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==", - "dev": true, - "license": "ISC", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", + "license": "MIT", "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" + "camelcase-keys": "^4.0.0", + "decamelize-keys": "^1.0.0", + "loud-rejection": "^1.0.0", + "minimist-options": "^3.0.1", + "normalize-package-data": "^2.3.4", + "read-pkg-up": "^3.0.0", + "redent": "^2.0.0", + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=6" } }, - "node_modules/node-gyp/node_modules/yallist": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", - "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", - "dev": true, - "license": "BlueOak-1.0.0", + "node_modules/np/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "license": "MIT", + "dependencies": { + "path-key": "^2.0.0" + }, "engines": { - "node": ">=18" + "node": ">=4" } }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", - "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", - "dev": true, - "license": "MIT" + "node_modules/np/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "node_modules/nopt": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz", - "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==", - "dev": true, - "license": "ISC", + "node_modules/np/node_modules/rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "license": "Apache-2.0", "dependencies": { - "abbrev": "^3.0.0" + "tslib": "^1.9.0" }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/np/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", "bin": { - "nopt": "bin/nopt.js" + "semver": "bin/semver" + } + }, + "node_modules/np/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": ">=0.10.0" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, + "node_modules/np/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, + "node_modules/np/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/np/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=4" + } + }, + "node_modules/np/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/np/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/np/node_modules/yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "license": "ISC", + "dependencies": { + "camelcase": "^4.1.0" } }, "node_modules/npm-bundled": { @@ -19459,6 +22063,54 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/npm-name": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/npm-name/-/npm-name-5.5.0.tgz", + "integrity": "sha512-l7/uyVfEi2e3ho+ovaJZC0xlbwzXNUz3RxkxpfcnLuoGKAuYoo9YoJ/uy18PsTD8IziugGHks4t/mGmBJEZ4Qg==", + "license": "MIT", + "dependencies": { + "got": "^9.6.0", + "is-scoped": "^2.1.0", + "is-url-superb": "^3.0.0", + "lodash.zip": "^4.2.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.1.0", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm-name/node_modules/is-scoped": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-scoped/-/is-scoped-2.1.0.tgz", + "integrity": "sha512-Cv4OpPTHAK9kHYzkzCrof3VJh7H/PrG2MBUMvvJebaaUMbqhm0YAtXnvh0I3Hnj2tMZWwrRROWLSgfJrKqWmlQ==", + "license": "MIT", + "dependencies": { + "scoped-regex": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm-name/node_modules/scoped-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scoped-regex/-/scoped-regex-2.1.0.tgz", + "integrity": "sha512-g3WxHrqSWCZHGHlSrF51VXFdjImhwvH8ZO/pryFH56Qi0cDsZfylQa/t0jCzVQFNbNvM00HfHjkDPEuarKDSWQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm-name/node_modules/validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "license": "ISC", + "dependencies": { + "builtins": "^1.0.3" + } + }, "node_modules/npm-normalize-package-bin": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz", @@ -19560,6 +22212,15 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/nwsapi": { "version": "2.2.22", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz", @@ -19571,7 +22232,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -19708,7 +22368,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -19916,6 +22575,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/own-keys": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", @@ -19934,6 +22602,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -20036,23 +22722,142 @@ "node": ">= 4" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, + "node_modules/p-timeout": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", + "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", + "license": "MIT", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha512-q/R5GrMek0vzgoomq6rm9OX+3PQve8sLwTirmK30YB3Cu0Bbt9OX9M/SIUnroN5BGJkzwGsFwDaRGD9EwBOlCA==", + "license": "MIT", + "dependencies": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/package-json/node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/package-json/node_modules/got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", + "license": "MIT", + "dependencies": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/package-json/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json/node_modules/prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json/node_modules/registry-auth-token": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", + "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "license": "MIT", + "dependencies": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/package-json/node_modules/registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", + "license": "MIT", + "dependencies": { + "rc": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/package-json/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/package-json/node_modules/url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA==", "license": "MIT", + "dependencies": { + "prepend-http": "^1.0.1" + }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, "node_modules/pacote": { "version": "20.0.0", "resolved": "https://registry.npmjs.org/pacote/-/pacote-20.0.0.tgz", @@ -20244,12 +23049,17 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", + "license": "(WTFPL OR MIT)" + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -20264,7 +23074,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, "license": "MIT" }, "node_modules/path-scurry": { @@ -20370,6 +23179,27 @@ "node": ">=6" } }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "license": "MIT", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pirates": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", @@ -20625,6 +23455,15 @@ "node": ">= 0.8.0" } }, + "node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/prettier": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz", @@ -20809,6 +23648,12 @@ "license": "MIT", "optional": true }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", + "license": "ISC" + }, "node_modules/psl": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", @@ -20826,7 +23671,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "dev": true, "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", @@ -21108,6 +23952,15 @@ ], "license": "MIT" }, + "node_modules/quick-lru": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "integrity": "sha512-tRS7sTgyxMXtLum8L65daJnHUhfDUgboRdcWW2bR9vBfrj2+O5HSMbQOJfJJjIVSPFqbBCF37FpwWXGitDc5tA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -21144,6 +23997,36 @@ "node": ">= 0.10" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -21151,6 +24034,121 @@ "dev": true, "license": "MIT" }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "license": "MIT", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -21180,6 +24178,19 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/redent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", + "integrity": "sha512-XNwrTx77JQCEMXTeb8movBKuK75MgH0RZkujNuDKCezemx/voapl9i2gCSi8WWm8+ox5ycJi1gxF22fR7c0Ciw==", + "license": "MIT", + "dependencies": { + "indent-string": "^3.0.0", + "strip-indent": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", @@ -21283,6 +24294,30 @@ "node": ">=4" } }, + "node_modules/registry-auth-token": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", + "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", + "license": "MIT", + "dependencies": { + "rc": "1.2.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "license": "MIT", + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/regjsgen": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", @@ -21341,7 +24376,6 @@ "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.16.0", @@ -21433,6 +24467,15 @@ "node": ">=10" } }, + "node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", + "license": "MIT", + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, "node_modules/restore-cursor": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", @@ -21499,7 +24542,6 @@ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -21512,7 +24554,6 @@ "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -21524,7 +24565,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -21545,7 +24585,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -21613,6 +24652,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -21637,6 +24685,19 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rx-lite": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", + "integrity": "sha512-Cun9QucwK6MIrp3mry/Y7hqD1oFqTYLQ4pGxaHTjIdaFDWRGGLikqp6u8LcWJnzpoALg9hap+JGk8sFIUuEGNA==" + }, + "node_modules/rx-lite-aggregates": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz", + "integrity": "sha512-3xPNZGW93oCjiO7PtKxRK6iOVYBWBvtf9QHDfU23Oc+dLIQmAV//UnyXV/yihv81VS/UqoQPk4NegS8EFi55Hg==", + "dependencies": { + "rx-lite": "*" + } + }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -21670,7 +24731,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, "license": "MIT" }, "node_modules/safe-push-apply": { @@ -21712,7 +24772,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "devOptional": true, "license": "MIT" }, "node_modules/sass": { @@ -21836,6 +24895,15 @@ } } }, + "node_modules/scoped-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/scoped-regex/-/scoped-regex-1.0.0.tgz", + "integrity": "sha512-90/gFvaP4jXL0rXPD8FS7tWgmkQDlxCjs9cs3r3G5hAnrODt94kIh4SDbH/gm3HosGTik0omdSPOh0KQyGqjlg==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", @@ -21870,6 +24938,27 @@ "node": ">=10" } }, + "node_modules/semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha512-gL8F8L4ORwsS0+iQ34yCYv///jsOq0ZL7WP55d1HnJ32o7tyFYEFQZQA22mrLIacZdU6xecaBBZ+uEiffGNyXw==", + "license": "MIT", + "dependencies": { + "semver": "^5.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/semver-diff/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/send": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", @@ -22819,7 +25908,6 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, "license": "Apache-2.0", "dependencies": { "spdx-expression-parse": "^3.0.0", @@ -22830,14 +25918,12 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "dev": true, "license": "CC-BY-3.0" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", @@ -22848,7 +25934,6 @@ "version": "3.0.22", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", - "dev": true, "license": "CC0-1.0" }, "node_modules/spdy": { @@ -22887,7 +25972,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", - "dev": true, "license": "MIT", "dependencies": { "through": "2" @@ -23258,6 +26342,15 @@ "node": ">=8" } }, + "node_modules/strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -23268,6 +26361,15 @@ "node": ">=6" } }, + "node_modules/strip-indent": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-2.0.0.tgz", + "integrity": "sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -23294,18 +26396,60 @@ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz", + "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==", + "license": "MIT", + "dependencies": { + "has-flag": "^2.0.0", + "supports-color": "^5.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=4" } }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -23507,6 +26651,163 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha512-7dPUZQGy/+m3/wjVz3ZW5dobSoD/02NxJpoXUX0WIyjfVS3l0c+b/+9phIDFA7FHzkYtwtMFgeGZ/Y8jVTeqQQ==", + "license": "MIT", + "dependencies": { + "execa": "^0.7.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/term-size/node_modules/cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "license": "MIT", + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/term-size/node_modules/execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/term-size/node_modules/get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/term-size/node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/term-size/node_modules/lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "license": "ISC", + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/term-size/node_modules/npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "license": "MIT", + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/term-size/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/term-size/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/term-size/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/term-size/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/term-size/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/term-size/node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", + "license": "ISC" + }, + "node_modules/terminal-link": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-1.3.0.tgz", + "integrity": "sha512-nFaWG/gs3brGi3opgWU2+dyFGbQ7tueSRYOBOD8URdDXCbAGqDEZzuskCc+okCClYcJFDPwn8e2mbv4FqAnWFA==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^3.2.0", + "supports-hyperlinks": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terminal-link/node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/terser": { "version": "5.39.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", @@ -23694,7 +26995,6 @@ "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, "license": "MIT" }, "node_modules/thunky": { @@ -23704,6 +27004,15 @@ "dev": true, "license": "MIT" }, + "node_modules/timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/tinyexec": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", @@ -23741,6 +27050,27 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tlds": { + "version": "1.261.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.261.0.tgz", + "integrity": "sha512-QXqwfEl9ddlGBaRFXIvNKK6OhipSiLXuRuLJX5DErz0o0Q0rYxulWLdFryTkV5PkdZct5iMInwYEGe/eR++1AA==", + "license": "MIT", + "bin": { + "tlds": "bin.js" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -23748,6 +27078,15 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -23847,6 +27186,15 @@ "tree-kill": "cli.js" } }, + "node_modules/trim-newlines": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-2.0.0.tgz", + "integrity": "sha512-MTBWv3jhVjTU7XR3IQHllbiJs8sc75a80OEhB6or/q7pLTWgQ0bMGQXXYQSrSuXe6WiKWDZ5txXY5P59a/coVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/trouter": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/trouter/-/trouter-2.0.1.tgz", @@ -24469,6 +27817,18 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha512-ODgiYu03y5g76A1I9Gt0/chLCzQjvzDy7DsZGsLOE/1MrF6wriEskSncj1+/C58Xk/kPZDppSctDybCwOSaGAg==", + "license": "MIT", + "dependencies": { + "crypto-random-string": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -24496,6 +27856,15 @@ "node": ">= 0.8" } }, + "node_modules/unzip-response": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha512-N0XH6lqDtFH84JxptQoZYmloF4nzrQqqrAymNj+/gW60AO2AZgOcf4O/nUXJcYfyQkqvMo9lSupBZmmgvuVXlw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -24527,6 +27896,98 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/update-notifier": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "license": "BSD-2-Clause", + "dependencies": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/update-notifier/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/update-notifier/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/update-notifier/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/update-notifier/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/update-notifier/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/update-notifier/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/update-notifier/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -24548,6 +28009,31 @@ "requires-port": "^1.0.0" } }, + "node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", + "license": "MIT", + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/url-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-regex/-/url-regex-5.0.0.tgz", + "integrity": "sha512-O08GjTiAFNsSlrUWfqF1jH0H1W3m35ZyadHrGv5krdnmPPoxP27oDTqux/579PtaroiSGm5yma6KT1mHFH6Y/g==", + "license": "MIT", + "dependencies": { + "ip-regex": "^4.1.0", + "tlds": "^1.203.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -24605,7 +28091,6 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", @@ -24648,9 +28133,9 @@ } }, "node_modules/vite": { - "version": "6.3.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz", - "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", "dependencies": { @@ -24723,9 +28208,9 @@ } }, "node_modules/vite/node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", - "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", + "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", "cpu": [ "arm" ], @@ -24737,9 +28222,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-android-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", - "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", + "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", "cpu": [ "arm64" ], @@ -24751,9 +28236,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", - "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", + "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", "cpu": [ "arm64" ], @@ -24765,9 +28250,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-darwin-x64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", - "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", + "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", "cpu": [ "x64" ], @@ -24779,9 +28264,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", - "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", + "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", "cpu": [ "arm64" ], @@ -24793,9 +28278,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", - "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", + "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", "cpu": [ "x64" ], @@ -24807,9 +28292,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", - "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", + "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", "cpu": [ "arm" ], @@ -24821,9 +28306,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", - "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", + "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", "cpu": [ "arm" ], @@ -24835,9 +28320,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", - "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", + "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", "cpu": [ "arm64" ], @@ -24849,9 +28334,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", - "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", + "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", "cpu": [ "arm64" ], @@ -24863,9 +28348,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", - "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", + "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", "cpu": [ "riscv64" ], @@ -24877,9 +28362,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", - "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", + "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", "cpu": [ "s390x" ], @@ -24891,9 +28376,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", - "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", + "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", "cpu": [ "x64" ], @@ -24905,9 +28390,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", - "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", + "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", "cpu": [ "x64" ], @@ -24919,9 +28404,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", - "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", + "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", "cpu": [ "arm64" ], @@ -24933,9 +28418,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", - "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", + "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", "cpu": [ "ia32" ], @@ -24947,9 +28432,9 @@ ] }, "node_modules/vite/node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", - "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", "cpu": [ "x64" ], @@ -24990,9 +28475,9 @@ } }, "node_modules/vite/node_modules/rollup": { - "version": "4.52.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", - "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", + "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, "license": "MIT", "dependencies": { @@ -25006,28 +28491,28 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.52.3", - "@rollup/rollup-android-arm64": "4.52.3", - "@rollup/rollup-darwin-arm64": "4.52.3", - "@rollup/rollup-darwin-x64": "4.52.3", - "@rollup/rollup-freebsd-arm64": "4.52.3", - "@rollup/rollup-freebsd-x64": "4.52.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", - "@rollup/rollup-linux-arm-musleabihf": "4.52.3", - "@rollup/rollup-linux-arm64-gnu": "4.52.3", - "@rollup/rollup-linux-arm64-musl": "4.52.3", - "@rollup/rollup-linux-loong64-gnu": "4.52.3", - "@rollup/rollup-linux-ppc64-gnu": "4.52.3", - "@rollup/rollup-linux-riscv64-gnu": "4.52.3", - "@rollup/rollup-linux-riscv64-musl": "4.52.3", - "@rollup/rollup-linux-s390x-gnu": "4.52.3", - "@rollup/rollup-linux-x64-gnu": "4.52.3", - "@rollup/rollup-linux-x64-musl": "4.52.3", - "@rollup/rollup-openharmony-arm64": "4.52.3", - "@rollup/rollup-win32-arm64-msvc": "4.52.3", - "@rollup/rollup-win32-ia32-msvc": "4.52.3", - "@rollup/rollup-win32-x64-gnu": "4.52.3", - "@rollup/rollup-win32-x64-msvc": "4.52.3", + "@rollup/rollup-android-arm-eabi": "4.53.3", + "@rollup/rollup-android-arm64": "4.53.3", + "@rollup/rollup-darwin-arm64": "4.53.3", + "@rollup/rollup-darwin-x64": "4.53.3", + "@rollup/rollup-freebsd-arm64": "4.53.3", + "@rollup/rollup-freebsd-x64": "4.53.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", + "@rollup/rollup-linux-arm-musleabihf": "4.53.3", + "@rollup/rollup-linux-arm64-gnu": "4.53.3", + "@rollup/rollup-linux-arm64-musl": "4.53.3", + "@rollup/rollup-linux-loong64-gnu": "4.53.3", + "@rollup/rollup-linux-ppc64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-gnu": "4.53.3", + "@rollup/rollup-linux-riscv64-musl": "4.53.3", + "@rollup/rollup-linux-s390x-gnu": "4.53.3", + "@rollup/rollup-linux-x64-gnu": "4.53.3", + "@rollup/rollup-linux-x64-musl": "4.53.3", + "@rollup/rollup-openharmony-arm64": "4.53.3", + "@rollup/rollup-win32-arm64-msvc": "4.53.3", + "@rollup/rollup-win32-ia32-msvc": "4.53.3", + "@rollup/rollup-win32-x64-gnu": "4.53.3", + "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" } }, @@ -25601,6 +29086,61 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/widest-line": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "license": "MIT", + "dependencies": { + "string-width": "^2.1.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/widest-line/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/widest-line/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "license": "MIT", + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/wildcard": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", @@ -25878,7 +29418,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, "license": "ISC" }, "node_modules/write-file-atomic": { @@ -25924,6 +29463,15 @@ } } }, + "node_modules/xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha512-1Dly4xqlulvPD3fZUQJLY+FUIeqN3N2MM3uqe4rCJftAvOjFa3jFGfctOgluGx4ahPbUCsZkmJILiP0Vi4T6lQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/xml-name-validator": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", diff --git a/package.json b/package.json index c77a86b06..513090c34 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@angular/platform-browser": "^19.2.0", "@angular/platform-browser-dynamic": "^19.2.0", "@angular/router": "^19.2.0", + "@centerforopenscience/markdown-it-atrules": "^0.1.1", "@citation-js/core": "^0.7.18", "@citation-js/plugin-csl": "^0.7.18", "@fortawesome/fontawesome-free": "^6.7.2", diff --git a/src/@types/markdown-it-atrules.d.ts b/src/@types/markdown-it-atrules.d.ts new file mode 100644 index 000000000..b1f706e6f --- /dev/null +++ b/src/@types/markdown-it-atrules.d.ts @@ -0,0 +1,6 @@ +declare module '@centerforopenscience/markdown-it-atrules' { + import { PluginSimple } from 'markdown-it'; + + const atrulesPlugin: PluginSimple; + export default atrulesPlugin; +} diff --git a/src/app/shared/components/markdown/markdown.component.ts b/src/app/shared/components/markdown/markdown.component.ts index 47bf9d3c4..4f513155d 100644 --- a/src/app/shared/components/markdown/markdown.component.ts +++ b/src/app/shared/components/markdown/markdown.component.ts @@ -12,6 +12,9 @@ import { } from '@angular/core'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; +import { ENVIRONMENT } from '@core/provider/environment.provider'; + +import markdownItAtrules from '@centerforopenscience/markdown-it-atrules'; import { legacyImgSize } from '@mdit/plugin-img-size'; import markdownItKatex from '@traptitech/markdown-it-katex'; import MarkdownIt from 'markdown-it'; @@ -33,6 +36,7 @@ export class MarkdownComponent implements AfterViewInit { private md: MarkdownIt; private sanitizer = inject(DomSanitizer); + private readonly environment = inject(ENVIRONMENT); private destroyRef = inject(DestroyRef); private clickHandler?: (event: MouseEvent) => void; @@ -48,14 +52,26 @@ export class MarkdownComponent implements AfterViewInit { typographer: true, breaks: true, }) - .use(markdownItVideo, { - youtube: { width: 560, height: 315 }, - vimeo: { width: 560, height: 315 }, + .use(markdownItAtrules, { + type: 'osf', + pattern: + /^http(?:s?):\/\/(?:www\.)?[a-zA-Z0-9 .:]{1,}\/render\?url=http(?:s?):\/\/[a-zA-Z0-9 .:]{1,}\/([a-zA-Z0-9]{5})\/\?action=download|(^[a-zA-Z0-9]{5}$)/, + format: (assetID: string) => { + const id = '__markdown-it-atrules-' + Date.now(); + const downloadUrl = `${this.environment.webUrl}/download/${assetID}/?direct&mode=render`; + const hostname = new URL(this.environment.webUrl).hostname; + const mfrUrl = `https://mfr.us.${hostname}/render?url=${encodeURIComponent(downloadUrl)}`; + return `
`; + }, }) .use(markdownItKatex, { output: 'mathml', throwOnError: false, }) + .use(markdownItVideo, { + youtube: { width: 560, height: 315 }, + vimeo: { width: 560, height: 315 }, + }) .use(markdownItAnchor) .use(markdownItTocDoneRight, { placeholder: '@\\[toc\\]', From 93c64665eb9cd10167f664064ce7858b2c134bf1 Mon Sep 17 00:00:00 2001 From: nsemets Date: Mon, 1 Dec 2025 20:35:50 +0200 Subject: [PATCH 34/76] [ENG-9802] Users unable to create a NEW CEDAR metadata template, and existing templates not able to be viewed #791 - Ticket: [ENG-9802] - Feature flag: n/a ## Summary of Changes 1. Fixed load of cedar pakcages. --- src/@types/cedar.d.ts | 2 + .../cedar-template-form.component.html | 36 ++++++------ .../cedar-template-form.component.spec.ts | 13 ++++- .../cedar-template-form.component.ts | 55 ++++++++++++------- 4 files changed, 67 insertions(+), 39 deletions(-) create mode 100644 src/@types/cedar.d.ts diff --git a/src/@types/cedar.d.ts b/src/@types/cedar.d.ts new file mode 100644 index 000000000..7416041cb --- /dev/null +++ b/src/@types/cedar.d.ts @@ -0,0 +1,2 @@ +declare module 'cedar-artifact-viewer'; +declare module 'cedar-embeddable-editor'; diff --git a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.html b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.html index 396b9a96e..3e6baa3d8 100644 --- a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.html +++ b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.html @@ -54,23 +54,27 @@

{{ 'project.metadata.addMetadata.notPublishedTe

}
- @if (readonly()) { - + @if (!cedarLoaded()) { + } @else { - + @if (readonly()) { + + } @else { + + } }
diff --git a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts index 15b023b33..e6fb6f0ed 100644 --- a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts +++ b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.spec.ts @@ -1,7 +1,11 @@ +import { MockComponent } from 'ng-mocks'; + +import { PLATFORM_ID } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CedarMetadataHelper } from '@osf/features/metadata/helpers'; import { CedarMetadataDataTemplateJsonApi } from '@osf/features/metadata/models'; +import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component'; import { CedarTemplateFormComponent } from './cedar-template-form.component'; @@ -16,7 +20,8 @@ describe('CedarTemplateFormComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [CedarTemplateFormComponent, OSFTestingModule], + imports: [CedarTemplateFormComponent, OSFTestingModule, MockComponent(LoadingSpinnerComponent)], + providers: [{ provide: PLATFORM_ID, useValue: 'browser' }], }).compileComponents(); fixture = TestBed.createComponent(CedarTemplateFormComponent); @@ -75,10 +80,14 @@ describe('CedarTemplateFormComponent', () => { expect(emitSpy).toHaveBeenCalled(); }); - it('should initialize form data with empty metadata when no existing record', () => { + it('should initialize form data with empty metadata when no existing record', async () => { fixture.componentRef.setInput('existingRecord', null); fixture.detectChanges(); + await (component as any).loadCedarLibraries(); + (component as any).initializeCedar(); + fixture.detectChanges(); + const expectedEmptyMetadata = CedarMetadataHelper.buildEmptyMetadata(); expect(component.formData()).toEqual(expectedEmptyMetadata); }); diff --git a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts index 16a562e69..6ce949170 100644 --- a/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts +++ b/src/app/features/metadata/components/cedar-template-form/cedar-template-form.component.ts @@ -6,7 +6,7 @@ import { Tooltip } from 'primeng/tooltip'; import { map, of } from 'rxjs'; -import { CommonModule } from '@angular/common'; +import { CommonModule, isPlatformBrowser } from '@angular/common'; import { ChangeDetectionStrategy, Component, @@ -16,6 +16,7 @@ import { inject, input, output, + PLATFORM_ID, signal, viewChild, ViewEncapsulation, @@ -24,9 +25,7 @@ import { toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute } from '@angular/router'; import { ENVIRONMENT } from '@core/provider/environment.provider'; - -import 'cedar-artifact-viewer'; -import 'cedar-embeddable-editor'; +import { LoadingSpinnerComponent } from '@osf/shared/components/loading-spinner/loading-spinner.component'; import { CEDAR_CONFIG, CEDAR_VIEWER_CONFIG } from '../../constants'; import { CedarMetadataHelper } from '../../helpers'; @@ -39,7 +38,7 @@ import { @Component({ selector: 'osf-cedar-template-form', - imports: [CommonModule, Button, TranslatePipe, Tooltip, Menu], + imports: [CommonModule, Button, TranslatePipe, Tooltip, Menu, LoadingSpinnerComponent], templateUrl: './cedar-template-form.component.html', styleUrl: './cedar-template-form.component.scss', schemas: [CUSTOM_ELEMENTS_SCHEMA], @@ -66,11 +65,15 @@ export class CedarTemplateFormComponent { private route = inject(ActivatedRoute); readonly environment = inject(ENVIRONMENT); + private platformId = inject(PLATFORM_ID); + readonly cedarLoaded = signal(false); readonly recordId = signal(''); readonly downloadUrl = signal(''); readonly schemaName = signal(''); + readonly fileGuid = toSignal(this.route.params.pipe(map((params) => params['fileGuid'])) ?? of(undefined)); + shareItems = [ { label: 'files.detail.actions.share.email', @@ -90,7 +93,7 @@ export class CedarTemplateFormComponent { effect(() => { const tpl = this.template(); if (tpl?.attributes?.template) { - this.initializeCedar(); + this.loadCedarLibraries().then(() => this.initializeCedar()); } }); @@ -98,7 +101,7 @@ export class CedarTemplateFormComponent { const record = this.existingRecord(); this.schemaName.set(record?.embeds?.template.data.attributes.schema_name || ''); if (record) { - this.initializeCedar(); + this.loadCedarLibraries().then(() => this.initializeCedar()); } }); } @@ -123,7 +126,30 @@ export class CedarTemplateFormComponent { this.validateCedarMetadata(); } - readonly fileGuid = toSignal(this.route.params.pipe(map((params) => params['fileGuid'])) ?? of(undefined)); + private initializeFormData(): void { + const template = this.template()?.attributes?.template; + if (!template) return; + const metadata = this.existingRecord()?.attributes?.metadata; + if (this.existingRecord()) { + const structuredMetadata = CedarMetadataHelper.buildStructuredMetadata(metadata); + this.formData.set(structuredMetadata); + } else { + this.formData.set(CedarMetadataHelper.buildEmptyMetadata()); + } + } + + private async loadCedarLibraries(): Promise { + if (!isPlatformBrowser(this.platformId) || this.cedarLoaded()) { + return; + } + + try { + await Promise.all([import('cedar-artifact-viewer'), import('cedar-embeddable-editor')]); + this.cedarLoaded.set(true); + } catch { + this.cedarLoaded.set(false); + } + } downloadMetadadaRecord() { if (this.fileGuid()) { @@ -173,19 +199,6 @@ export class CedarTemplateFormComponent { this.emitData.emit(finalData as CedarRecordDataBinding); } } - - private initializeFormData(): void { - const template = this.template()?.attributes?.template; - if (!template) return; - const metadata = this.existingRecord()?.attributes?.metadata; - if (this.existingRecord()) { - const structuredMetadata = CedarMetadataHelper.buildStructuredMetadata(metadata); - this.formData.set(structuredMetadata); - } else { - this.formData.set(CedarMetadataHelper.buildEmptyMetadata()); - } - } - handleEmailShare(): void { const url = window.location.href; window.location.href = `mailto:?subject=${this.schemaName()}&body=${url}`; From 22f687d40c5166b0ea253c2613745a9bce62a0fb Mon Sep 17 00:00:00 2001 From: nsemets Date: Tue, 2 Dec 2025 16:18:42 +0200 Subject: [PATCH 35/76] [ENG-9898] Recent activity improvements (#800) - Ticket: [ENG-9898] - Feature flag: n/a ## Purpose Make it reusable and maintainable. ## Summary of Changes 1. Improved recent activity. --- .../files/pages/files/files.component.ts | 2 +- .../files-widget/files-widget.component.html | 2 +- .../project-recent-activity.component.html | 14 + .../project-recent-activity.component.spec.ts | 199 ++++++++++++ .../project-recent-activity.component.ts | 54 ++++ .../recent-activity.component.html | 36 --- .../recent-activity.component.scss | 16 - .../recent-activity.component.spec.ts | 99 ------ .../recent-activity.component.ts | 57 ---- .../overview/project-overview.component.html | 2 +- .../project-overview.component.spec.ts | 20 +- .../overview/project-overview.component.ts | 19 +- ...egistration-recent-activity.component.html | 56 +--- ...stration-recent-activity.component.spec.ts | 298 ++++++++++-------- .../registration-recent-activity.component.ts | 54 ++-- .../recent-activity-list.component.html | 40 +++ .../recent-activity-list.component.scss | 8 + .../recent-activity-list.component.spec.ts | 140 ++++++++ .../recent-activity-list.component.ts | 31 ++ .../shared/mappers/activity-logs.mapper.ts | 6 +- .../activity-logs.service.spec.ts | 107 ++++--- .../activity-logs/activity-logs.service.ts | 58 ++-- .../activity-logs/activity-logs.actions.ts | 18 +- .../activity-logs.selectors.spec.ts | 102 +++++- .../activity-logs/activity-logs.selectors.ts | 8 +- .../activity-logs/activity-logs.state.spec.ts | 40 ++- .../activity-logs/activity-logs.state.ts | 51 +-- src/styles/components/nodes.scss | 6 + .../mocks/activity-log-with-display.mock.ts | 41 +++ 29 files changed, 993 insertions(+), 591 deletions(-) create mode 100644 src/app/features/project/overview/components/project-recent-activity/project-recent-activity.component.html create mode 100644 src/app/features/project/overview/components/project-recent-activity/project-recent-activity.component.spec.ts create mode 100644 src/app/features/project/overview/components/project-recent-activity/project-recent-activity.component.ts delete mode 100644 src/app/features/project/overview/components/recent-activity/recent-activity.component.html delete mode 100644 src/app/features/project/overview/components/recent-activity/recent-activity.component.scss delete mode 100644 src/app/features/project/overview/components/recent-activity/recent-activity.component.spec.ts delete mode 100644 src/app/features/project/overview/components/recent-activity/recent-activity.component.ts create mode 100644 src/app/shared/components/recent-activity/recent-activity-list.component.html create mode 100644 src/app/shared/components/recent-activity/recent-activity-list.component.scss create mode 100644 src/app/shared/components/recent-activity/recent-activity-list.component.spec.ts create mode 100644 src/app/shared/components/recent-activity/recent-activity-list.component.ts create mode 100644 src/testing/mocks/activity-log-with-display.mock.ts diff --git a/src/app/features/files/pages/files/files.component.ts b/src/app/features/files/pages/files/files.component.ts index 728831377..4bf8617cd 100644 --- a/src/app/features/files/pages/files/files.component.ts +++ b/src/app/features/files/pages/files/files.component.ts @@ -261,7 +261,7 @@ export class FilesComponent { ); constructor() { - this.activeRoute.parent?.parent?.parent?.params.subscribe((params) => { + this.activeRoute.parent?.parent?.parent?.params.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((params) => { if (params['id']) { this.resourceId.set(params['id']); } diff --git a/src/app/features/project/overview/components/files-widget/files-widget.component.html b/src/app/features/project/overview/components/files-widget/files-widget.component.html index 16ffccc9f..45d14eec0 100644 --- a/src/app/features/project/overview/components/files-widget/files-widget.component.html +++ b/src/app/features/project/overview/components/files-widget/files-widget.component.html @@ -18,7 +18,7 @@

{{ 'project.overview.files.filesPreview' | translate }}

@for (option of storageAddons(); track option.folder.id) { - + +

+ {{ 'project.overview.recentActivity.title' | translate }} +

+ + +
diff --git a/src/app/features/project/overview/components/project-recent-activity/project-recent-activity.component.spec.ts b/src/app/features/project/overview/components/project-recent-activity/project-recent-activity.component.spec.ts new file mode 100644 index 000000000..596f67dbc --- /dev/null +++ b/src/app/features/project/overview/components/project-recent-activity/project-recent-activity.component.spec.ts @@ -0,0 +1,199 @@ +import { Store } from '@ngxs/store'; + +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CurrentResourceType } from '@osf/shared/enums/resource-type.enum'; +import { ActivityLogsSelectors, ClearActivityLogs } from '@osf/shared/stores/activity-logs'; + +import { ProjectRecentActivityComponent } from './project-recent-activity.component'; + +import { MOCK_ACTIVITY_LOGS_WITH_DISPLAY } from '@testing/mocks/activity-log-with-display.mock'; +import { OSFTestingModule } from '@testing/osf.testing.module'; +import { provideMockStore } from '@testing/providers/store-provider.mock'; + +describe('ProjectRecentActivityComponent', () => { + let component: ProjectRecentActivityComponent; + let fixture: ComponentFixture; + let store: Store; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ProjectRecentActivityComponent, OSFTestingModule], + providers: [ + provideMockStore({ + signals: [ + { selector: ActivityLogsSelectors.getActivityLogs, value: [] }, + { selector: ActivityLogsSelectors.getActivityLogsTotalCount, value: 0 }, + { selector: ActivityLogsSelectors.getActivityLogsLoading, value: false }, + ], + }), + ], + }).compileComponents(); + + store = TestBed.inject(Store); + jest.spyOn(store, 'dispatch'); + + fixture = TestBed.createComponent(ProjectRecentActivityComponent); + component = fixture.componentInstance; + }); + + it('should initialize with default values', () => { + expect(component.pageSize()).toBe(5); + expect(component.currentPage()).toBe(1); + expect(component.firstIndex()).toBe(0); + }); + + it('should dispatch GetActivityLogs when projectId is provided', () => { + fixture.componentRef.setInput('projectId', 'project123'); + fixture.detectChanges(); + + expect(store.dispatch).toHaveBeenCalledWith( + expect.objectContaining({ + resourceId: 'project123', + resourceType: CurrentResourceType.Projects, + page: 1, + pageSize: 5, + }) + ); + }); + + it('should not dispatch when projectId is not provided', () => { + fixture.detectChanges(); + + expect(store.dispatch).not.toHaveBeenCalled(); + }); + + it('should dispatch GetActivityLogs when currentPage changes', () => { + fixture.componentRef.setInput('projectId', 'project123'); + fixture.detectChanges(); + + (store.dispatch as jest.Mock).mockClear(); + + component.currentPage.set(2); + fixture.detectChanges(); + + expect(store.dispatch).toHaveBeenCalledWith( + expect.objectContaining({ + resourceId: 'project123', + resourceType: CurrentResourceType.Projects, + page: 2, + pageSize: 5, + }) + ); + }); + + it('should update currentPage and dispatch on page change', () => { + fixture.componentRef.setInput('projectId', 'project123'); + fixture.detectChanges(); + + (store.dispatch as jest.Mock).mockClear(); + + component.onPageChange({ page: 1 } as any); + fixture.detectChanges(); + + expect(component.currentPage()).toBe(2); + expect(store.dispatch).toHaveBeenCalledWith( + expect.objectContaining({ + page: 2, + }) + ); + }); + + it('should not update currentPage when page is undefined', () => { + fixture.componentRef.setInput('projectId', 'project123'); + fixture.detectChanges(); + + const initialPage = component.currentPage(); + component.onPageChange({} as any); + + expect(component.currentPage()).toBe(initialPage); + }); + + it('should compute firstIndex correctly', () => { + component.currentPage.set(1); + expect(component.firstIndex()).toBe(0); + + component.currentPage.set(2); + expect(component.firstIndex()).toBe(5); + + component.currentPage.set(3); + expect(component.firstIndex()).toBe(10); + }); + + it('should clear store on destroy', () => { + fixture.componentRef.setInput('projectId', 'project123'); + fixture.detectChanges(); + + (store.dispatch as jest.Mock).mockClear(); + + fixture.destroy(); + + expect(store.dispatch).toHaveBeenCalledWith(expect.any(ClearActivityLogs)); + }); + + it('should return activity logs from selector', () => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [ProjectRecentActivityComponent, OSFTestingModule], + providers: [ + provideMockStore({ + signals: [ + { selector: ActivityLogsSelectors.getActivityLogs, value: MOCK_ACTIVITY_LOGS_WITH_DISPLAY }, + { selector: ActivityLogsSelectors.getActivityLogsTotalCount, value: 2 }, + { selector: ActivityLogsSelectors.getActivityLogsLoading, value: false }, + ], + }), + ], + }).compileComponents(); + + fixture = TestBed.createComponent(ProjectRecentActivityComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + expect(component.activityLogs()).toEqual(MOCK_ACTIVITY_LOGS_WITH_DISPLAY); + }); + + it('should return totalCount from selector', () => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [ProjectRecentActivityComponent, OSFTestingModule], + providers: [ + provideMockStore({ + signals: [ + { selector: ActivityLogsSelectors.getActivityLogs, value: [] }, + { selector: ActivityLogsSelectors.getActivityLogsTotalCount, value: 10 }, + { selector: ActivityLogsSelectors.getActivityLogsLoading, value: false }, + ], + }), + ], + }).compileComponents(); + + fixture = TestBed.createComponent(ProjectRecentActivityComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + expect(component.totalCount()).toBe(10); + }); + + it('should return isLoading from selector', () => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [ProjectRecentActivityComponent, OSFTestingModule], + providers: [ + provideMockStore({ + signals: [ + { selector: ActivityLogsSelectors.getActivityLogs, value: [] }, + { selector: ActivityLogsSelectors.getActivityLogsTotalCount, value: 0 }, + { selector: ActivityLogsSelectors.getActivityLogsLoading, value: true }, + ], + }), + ], + }).compileComponents(); + + fixture = TestBed.createComponent(ProjectRecentActivityComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + expect(component.isLoading()).toBe(true); + }); +}); diff --git a/src/app/features/project/overview/components/project-recent-activity/project-recent-activity.component.ts b/src/app/features/project/overview/components/project-recent-activity/project-recent-activity.component.ts new file mode 100644 index 000000000..5b4305004 --- /dev/null +++ b/src/app/features/project/overview/components/project-recent-activity/project-recent-activity.component.ts @@ -0,0 +1,54 @@ +import { createDispatchMap, select } from '@ngxs/store'; + +import { TranslatePipe } from '@ngx-translate/core'; + +import { PaginatorState } from 'primeng/paginator'; + +import { ChangeDetectionStrategy, Component, computed, effect, input, OnDestroy, signal } from '@angular/core'; + +import { RecentActivityListComponent } from '@osf/shared/components/recent-activity/recent-activity-list.component'; +import { CurrentResourceType } from '@osf/shared/enums/resource-type.enum'; +import { ActivityLogsSelectors, ClearActivityLogs, GetActivityLogs } from '@osf/shared/stores/activity-logs'; + +@Component({ + selector: 'osf-project-recent-activity', + imports: [RecentActivityListComponent, TranslatePipe], + templateUrl: './project-recent-activity.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ProjectRecentActivityComponent implements OnDestroy { + projectId = input(); + + pageSize = signal(5); + currentPage = signal(1); + + activityLogs = select(ActivityLogsSelectors.getActivityLogs); + totalCount = select(ActivityLogsSelectors.getActivityLogsTotalCount); + isLoading = select(ActivityLogsSelectors.getActivityLogsLoading); + + actions = createDispatchMap({ getActivityLogs: GetActivityLogs, clearActivityLogsStore: ClearActivityLogs }); + + firstIndex = computed(() => (this.currentPage() - 1) * this.pageSize()); + + constructor() { + effect(() => { + const projectId = this.projectId(); + const page = this.currentPage(); + + if (projectId) { + this.actions.getActivityLogs(projectId, CurrentResourceType.Projects, page, this.pageSize()); + } + }); + } + + ngOnDestroy(): void { + this.actions.clearActivityLogsStore(); + } + + onPageChange(event: PaginatorState) { + if (event.page !== undefined) { + const pageNumber = event.page + 1; + this.currentPage.set(pageNumber); + } + } +} diff --git a/src/app/features/project/overview/components/recent-activity/recent-activity.component.html b/src/app/features/project/overview/components/recent-activity/recent-activity.component.html deleted file mode 100644 index a8994a873..000000000 --- a/src/app/features/project/overview/components/recent-activity/recent-activity.component.html +++ /dev/null @@ -1,36 +0,0 @@ -
-

{{ 'project.overview.recentActivity.title' | translate }}

- - @if (!isLoading()) { - @if (formattedActivityLogs().length) { - @for (activityLog of formattedActivityLogs(); track activityLog.id) { -
-
- -
- } - } @else { -
- {{ 'project.overview.recentActivity.noActivity' | translate }} -
- } - - @if (totalCount() > pageSize()) { - - } - } @else { -
- - - - - -
- } -
diff --git a/src/app/features/project/overview/components/recent-activity/recent-activity.component.scss b/src/app/features/project/overview/components/recent-activity/recent-activity.component.scss deleted file mode 100644 index 3453a1a51..000000000 --- a/src/app/features/project/overview/components/recent-activity/recent-activity.component.scss +++ /dev/null @@ -1,16 +0,0 @@ -.activities { - border: 1px solid var(--grey-2); - border-radius: 0.75rem; - - &-activity { - border-bottom: 1px solid var(--grey-2); - - .activity-date { - min-width: 27%; - } - } - - &-description { - line-height: 1.5rem; - } -} diff --git a/src/app/features/project/overview/components/recent-activity/recent-activity.component.spec.ts b/src/app/features/project/overview/components/recent-activity/recent-activity.component.spec.ts deleted file mode 100644 index 1a57528e0..000000000 --- a/src/app/features/project/overview/components/recent-activity/recent-activity.component.spec.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { provideStore, Store } from '@ngxs/store'; - -import { TranslateService } from '@ngx-translate/core'; -import { MockComponent } from 'ng-mocks'; - -import { of } from 'rxjs'; - -import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; -import { provideHttpClientTesting } from '@angular/common/http/testing'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; - -import { CustomPaginatorComponent } from '@osf/shared/components/custom-paginator/custom-paginator.component'; -import { ActivityLogDisplayService } from '@osf/shared/services/activity-logs/activity-log-display.service'; -import { GetActivityLogs } from '@shared/stores/activity-logs'; -import { ActivityLogsState } from '@shared/stores/activity-logs/activity-logs.state'; - -import { RecentActivityComponent } from './recent-activity.component'; - -describe.skip('RecentActivityComponent', () => { - let fixture: ComponentFixture; - let store: Store; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [RecentActivityComponent, MockComponent(CustomPaginatorComponent)], - providers: [ - provideStore([ActivityLogsState]), - provideHttpClient(withInterceptorsFromDi()), - provideHttpClientTesting(), - { - provide: TranslateService, - useValue: { - instant: (k: string) => k, - get: () => of(''), - stream: () => of(''), - onLangChange: of({}), - onDefaultLangChange: of({}), - onTranslationChange: of({}), - }, - }, - { provide: ActivatedRoute, useValue: { snapshot: { params: { id: 'proj123' } }, parent: null } }, - { provide: ActivityLogDisplayService, useValue: { getActivityDisplay: jest.fn().mockReturnValue('FMT') } }, - ], - }).compileComponents(); - - store = TestBed.inject(Store); - store.reset({ - activityLogs: { - activityLogs: { data: [], isLoading: false, error: null, totalCount: 0 }, - }, - } as any); - - fixture = TestBed.createComponent(RecentActivityComponent); - fixture.componentRef.setInput('pageSize', 10); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(fixture.componentInstance).toBeTruthy(); - }); - - it('formats activity logs using ActivityLogDisplayService', () => { - store.reset({ - activityLogs: { - activityLogs: { - data: [{ id: 'log1', date: '2024-01-01T00:00:00Z' }], - isLoading: false, - error: null, - totalCount: 1, - }, - }, - } as any); - - fixture.detectChanges(); - - const formatted = fixture.componentInstance.formattedActivityLogs(); - expect(formatted.length).toBe(1); - expect(formatted[0].formattedActivity).toBe('FMT'); - }); - - it('dispatches GetActivityLogs with numeric page and pageSize on page change', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - fixture.componentInstance.onPageChange({ page: 2 } as any); - - expect(dispatchSpy).toHaveBeenCalled(); - const action = dispatchSpy.mock.calls.at(-1)?.[0] as GetActivityLogs; - - expect(action).toBeInstanceOf(GetActivityLogs); - expect(action.projectId).toBe('proj123'); - expect(action.page).toBe(3); - expect(action.pageSize).toBe(10); - }); - - it('computes firstIndex correctly', () => { - fixture.componentInstance['currentPage'].set(3); - expect(fixture.componentInstance['firstIndex']()).toBe(20); - }); -}); diff --git a/src/app/features/project/overview/components/recent-activity/recent-activity.component.ts b/src/app/features/project/overview/components/recent-activity/recent-activity.component.ts deleted file mode 100644 index eb2b434f4..000000000 --- a/src/app/features/project/overview/components/recent-activity/recent-activity.component.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { createDispatchMap, select } from '@ngxs/store'; - -import { TranslatePipe } from '@ngx-translate/core'; - -import { PaginatorState } from 'primeng/paginator'; -import { Skeleton } from 'primeng/skeleton'; - -import { DatePipe } from '@angular/common'; -import { ChangeDetectionStrategy, Component, computed, inject, input, signal } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; - -import { CustomPaginatorComponent } from '@osf/shared/components/custom-paginator/custom-paginator.component'; -import { ActivityLogDisplayService } from '@osf/shared/services/activity-logs/activity-log-display.service'; -import { ActivityLogsSelectors, GetActivityLogs } from '@osf/shared/stores/activity-logs'; - -@Component({ - selector: 'osf-recent-activity-list', - imports: [TranslatePipe, Skeleton, DatePipe, CustomPaginatorComponent], - templateUrl: './recent-activity.component.html', - styleUrl: './recent-activity.component.scss', - changeDetection: ChangeDetectionStrategy.OnPush, -}) -export class RecentActivityComponent { - private readonly activityLogDisplayService = inject(ActivityLogDisplayService); - private readonly route = inject(ActivatedRoute); - - readonly pageSize = input.required(); - currentPage = signal(1); - - activityLogs = select(ActivityLogsSelectors.getActivityLogs); - totalCount = select(ActivityLogsSelectors.getActivityLogsTotalCount); - isLoading = select(ActivityLogsSelectors.getActivityLogsLoading); - - firstIndex = computed(() => (this.currentPage() - 1) * this.pageSize()); - - actions = createDispatchMap({ getActivityLogs: GetActivityLogs }); - - formattedActivityLogs = computed(() => { - const logs = this.activityLogs(); - return logs.map((log) => ({ - ...log, - formattedActivity: this.activityLogDisplayService.getActivityDisplay(log), - })); - }); - - onPageChange(event: PaginatorState) { - if (event.page !== undefined) { - const pageNumber = event.page + 1; - this.currentPage.set(pageNumber); - - const projectId = this.route.snapshot.params['id'] || this.route.parent?.snapshot.params['id']; - if (projectId) { - this.actions.getActivityLogs(projectId, pageNumber, this.pageSize()); - } - } - } -} diff --git a/src/app/features/project/overview/project-overview.component.html b/src/app/features/project/overview/project-overview.component.html index 7837acf4d..28942c854 100644 --- a/src/app/features/project/overview/project-overview.component.html +++ b/src/app/features/project/overview/project-overview.component.html @@ -79,7 +79,7 @@ } } - +
diff --git a/src/app/features/project/overview/project-overview.component.spec.ts b/src/app/features/project/overview/project-overview.component.spec.ts index eff3c50f8..f6bb02721 100644 --- a/src/app/features/project/overview/project-overview.component.spec.ts +++ b/src/app/features/project/overview/project-overview.component.spec.ts @@ -18,7 +18,6 @@ import { Mode } from '@osf/shared/enums/mode.enum'; import { AnalyticsService } from '@osf/shared/services/analytics.service'; import { CustomDialogService } from '@osf/shared/services/custom-dialog.service'; import { ToastService } from '@osf/shared/services/toast.service'; -import { GetActivityLogs } from '@osf/shared/stores/activity-logs'; import { AddonsSelectors, ClearConfiguredAddons } from '@osf/shared/stores/addons'; import { GetBookmarksCollectionId } from '@osf/shared/stores/bookmarks'; import { ClearCollections, CollectionsSelectors } from '@osf/shared/stores/collections'; @@ -34,7 +33,7 @@ import { OverviewParentProjectComponent } from './components/overview-parent-pro import { OverviewWikiComponent } from './components/overview-wiki/overview-wiki.component'; import { ProjectOverviewMetadataComponent } from './components/project-overview-metadata/project-overview-metadata.component'; import { ProjectOverviewToolbarComponent } from './components/project-overview-toolbar/project-overview-toolbar.component'; -import { RecentActivityComponent } from './components/recent-activity/recent-activity.component'; +import { ProjectRecentActivityComponent } from './components/project-recent-activity/project-recent-activity.component'; import { ProjectOverviewModel } from './models'; import { ProjectOverviewComponent } from './project-overview.component'; import { ClearProjectOverview, GetComponents, GetProjectById, ProjectOverviewSelectors } from './store'; @@ -81,7 +80,7 @@ describe('ProjectOverviewComponent', () => { OverviewWikiComponent, OverviewComponentsComponent, LinkedResourcesComponent, - RecentActivityComponent, + ProjectRecentActivityComponent, ProjectOverviewToolbarComponent, ProjectOverviewMetadataComponent, FilesWidgetComponent, @@ -136,7 +135,6 @@ describe('ProjectOverviewComponent', () => { expect(store.dispatch).toHaveBeenCalledWith(expect.any(GetBookmarksCollectionId)); expect(store.dispatch).toHaveBeenCalledWith(expect.any(GetComponents)); expect(store.dispatch).toHaveBeenCalledWith(expect.any(GetLinkedResources)); - expect(store.dispatch).toHaveBeenCalledWith(expect.any(GetActivityLogs)); }); it('should dispatch actions when projectId exists in parent route params', () => { @@ -150,20 +148,6 @@ describe('ProjectOverviewComponent', () => { component.ngOnInit(); expect(store.dispatch).toHaveBeenCalledWith(expect.any(GetProjectById)); - expect(store.dispatch).toHaveBeenCalledWith(expect.any(GetActivityLogs)); - }); - - it('should dispatch GetActivityLogs with correct parameters', () => { - component.ngOnInit(); - - const activityLogsCall = (store.dispatch as jest.Mock).mock.calls.find( - (call) => call[0] instanceof GetActivityLogs - ); - expect(activityLogsCall).toBeDefined(); - const action = activityLogsCall[0] as GetActivityLogs; - expect(action.projectId).toBe('project-123'); - expect(action.page).toBe(1); - expect(action.pageSize).toBe(5); }); it('should return true for isModerationMode when query param mode is moderation', () => { diff --git a/src/app/features/project/overview/project-overview.component.ts b/src/app/features/project/overview/project-overview.component.ts index b4ee62c9a..07f6a046a 100644 --- a/src/app/features/project/overview/project-overview.component.ts +++ b/src/app/features/project/overview/project-overview.component.ts @@ -5,6 +5,8 @@ import { TranslatePipe } from '@ngx-translate/core'; import { Button } from 'primeng/button'; import { Message } from 'primeng/message'; +import { map, of } from 'rxjs'; + import { ChangeDetectionStrategy, Component, @@ -15,7 +17,7 @@ import { inject, OnInit, } from '@angular/core'; -import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; import { SubmissionReviewStatus } from '@osf/features/moderation/enums'; @@ -33,7 +35,6 @@ import { ResourceType } from '@osf/shared/enums/resource-type.enum'; import { hasViewOnlyParam } from '@osf/shared/helpers/view-only.helper'; import { CustomDialogService } from '@osf/shared/services/custom-dialog.service'; import { ToastService } from '@osf/shared/services/toast.service'; -import { GetActivityLogs } from '@osf/shared/stores/activity-logs'; import { AddonsSelectors, ClearConfiguredAddons, @@ -56,7 +57,7 @@ import { OverviewParentProjectComponent } from './components/overview-parent-pro import { OverviewWikiComponent } from './components/overview-wiki/overview-wiki.component'; import { ProjectOverviewMetadataComponent } from './components/project-overview-metadata/project-overview-metadata.component'; import { ProjectOverviewToolbarComponent } from './components/project-overview-toolbar/project-overview-toolbar.component'; -import { RecentActivityComponent } from './components/recent-activity/recent-activity.component'; +import { ProjectRecentActivityComponent } from './components/project-recent-activity/project-recent-activity.component'; import { SUBMISSION_REVIEW_STATUS_OPTIONS } from './constants'; import { ClearProjectOverview, @@ -81,7 +82,7 @@ import { OverviewWikiComponent, OverviewComponentsComponent, LinkedResourcesComponent, - RecentActivityComponent, + ProjectRecentActivityComponent, ProjectOverviewToolbarComponent, ProjectOverviewMetadataComponent, FilesWidgetComponent, @@ -126,7 +127,6 @@ export class ProjectOverviewComponent implements OnInit { getHomeWiki: GetHomeWiki, getComponents: GetComponents, getLinkedProjects: GetLinkedResources, - getActivityLogs: GetActivityLogs, getCollectionProvider: GetCollectionProvider, getCurrentReviewAction: GetSubmissionsReviewActions, @@ -143,8 +143,6 @@ export class ProjectOverviewComponent implements OnInit { getConfiguredCitationAddons: GetConfiguredCitationAddons, }); - readonly activityPageSize = 5; - readonly activityDefaultPage = 1; readonly SubmissionReviewStatusOptions = SUBMISSION_REVIEW_STATUS_OPTIONS; readonly isCollectionsRoute = computed(() => this.router.url.includes('/collections')); @@ -171,6 +169,10 @@ export class ProjectOverviewComponent implements OnInit { label: this.currentProject()?.title ?? '', })); + readonly projectId = toSignal( + this.route.parent?.params.pipe(map((params) => params['id'])) ?? of(undefined) + ); + constructor() { this.setupCollectionsEffects(); this.setupProjectEffects(); @@ -179,14 +181,13 @@ export class ProjectOverviewComponent implements OnInit { } ngOnInit(): void { - const projectId = this.route.snapshot.params['id'] || this.route.parent?.snapshot.params['id']; + const projectId = this.projectId(); if (projectId) { this.actions.getProject(projectId); this.actions.getBookmarksId(); this.actions.getComponents(projectId); this.actions.getLinkedProjects(projectId); - this.actions.getActivityLogs(projectId, this.activityDefaultPage, this.activityPageSize); } } diff --git a/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.html b/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.html index f46a02896..8934d6c84 100644 --- a/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.html +++ b/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.html @@ -1,50 +1,14 @@ -
-

+
+

{{ 'project.overview.recentActivity.title' | translate }}

- @if (!isLoading()) { -
- @for (activityLog of formattedActivityLogs(); track activityLog.id) { -
-
- - -
- } @empty { -
- {{ 'project.overview.recentActivity.noActivity' | translate }} -
- } -
- - @if (totalCount() > pageSize) { - - } - } @else { -
- - - - - -
- } +
diff --git a/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.spec.ts b/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.spec.ts index 18a25b67b..65ba6cf2a 100644 --- a/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.spec.ts +++ b/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.spec.ts @@ -1,47 +1,38 @@ -import { provideStore, Store } from '@ngxs/store'; +import { Store } from '@ngxs/store'; -import { TranslateService } from '@ngx-translate/core'; - -import { of } from 'rxjs'; - -import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; -import { provideHttpClientTesting } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute } from '@angular/router'; -import { ActivityLogDisplayService } from '@osf/shared/services/activity-logs/activity-log-display.service'; -import { ClearActivityLogsStore, GetRegistrationActivityLogs } from '@shared/stores/activity-logs'; -import { ActivityLogsState } from '@shared/stores/activity-logs/activity-logs.state'; +import { CurrentResourceType } from '@osf/shared/enums/resource-type.enum'; +import { ActivityLogsSelectors, ClearActivityLogs } from '@osf/shared/stores/activity-logs'; import { RegistrationRecentActivityComponent } from './registration-recent-activity.component'; +import { MOCK_ACTIVITY_LOGS_WITH_DISPLAY } from '@testing/mocks/activity-log-with-display.mock'; +import { OSFTestingModule } from '@testing/osf.testing.module'; +import { ActivatedRouteMockBuilder } from '@testing/providers/route-provider.mock'; +import { provideMockStore } from '@testing/providers/store-provider.mock'; + describe('RegistrationRecentActivityComponent', () => { + let component: RegistrationRecentActivityComponent; let fixture: ComponentFixture; let store: Store; beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [RegistrationRecentActivityComponent], + imports: [RegistrationRecentActivityComponent, OSFTestingModule], providers: [ - provideStore([ActivityLogsState]), - provideHttpClient(withInterceptorsFromDi()), - provideHttpClientTesting(), - { - provide: TranslateService, - useValue: { - instant: (k: string) => k, - get: () => of(''), - stream: () => of(''), - onLangChange: of({}), - onDefaultLangChange: of({}), - onTranslationChange: of({}), - }, - }, + provideMockStore({ + signals: [ + { selector: ActivityLogsSelectors.getActivityLogs, value: [] }, + { selector: ActivityLogsSelectors.getActivityLogsTotalCount, value: 0 }, + { selector: ActivityLogsSelectors.getActivityLogsLoading, value: false }, + ], + }), { - provide: ActivityLogDisplayService, - useValue: { getActivityDisplay: jest.fn(() => 'formatted') }, + provide: ActivatedRoute, + useValue: ActivatedRouteMockBuilder.create().withParams({ id: 'reg123' }).build(), }, - { provide: ActivatedRoute, useValue: { snapshot: { params: { id: 'reg123' } }, parent: null } }, ], }).compileComponents(); @@ -49,145 +40,196 @@ describe('RegistrationRecentActivityComponent', () => { jest.spyOn(store, 'dispatch'); fixture = TestBed.createComponent(RegistrationRecentActivityComponent); - fixture.detectChanges(); + component = fixture.componentInstance; }); - it('dispatches initial registration logs fetch', () => { - const dispatchSpy = store.dispatch as jest.Mock; - expect(dispatchSpy).toHaveBeenCalledWith(expect.any(GetRegistrationActivityLogs)); - const action = dispatchSpy.mock.calls.at(-1)?.[0] as GetRegistrationActivityLogs; - expect(action.registrationId).toBe('reg123'); - expect(action.page).toBe(1); + it('should initialize with default values', () => { + expect(component.pageSize).toBe(10); + expect(component.currentPage()).toBe(1); + expect(component.firstIndex()).toBe(0); }); - it('renders empty state when no logs and not loading', () => { - store.reset({ - activityLogs: { - activityLogs: { data: [], isLoading: false, error: null, totalCount: 0 }, - }, - } as any); + it('should dispatch GetActivityLogs when registrationId is available', () => { fixture.detectChanges(); - const empty = fixture.nativeElement.querySelector('[data-test="recent-activity-empty"]'); - expect(empty).toBeTruthy(); + expect(store.dispatch).toHaveBeenCalledWith( + expect.objectContaining({ + resourceId: 'reg123', + resourceType: CurrentResourceType.Registrations, + page: 1, + pageSize: 10, + }) + ); }); - it('renders item & paginator when logs exist and totalCount > pageSize', () => { - store.reset({ - activityLogs: { - activityLogs: { - data: [ - { - id: 'log1', - date: '2024-01-01T12:34:00Z', - formattedActivity: 'formatted', - }, + it('should not dispatch when registrationId is not available', () => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [RegistrationRecentActivityComponent, OSFTestingModule], + providers: [ + provideMockStore({ + signals: [ + { selector: ActivityLogsSelectors.getActivityLogs, value: [] }, + { selector: ActivityLogsSelectors.getActivityLogsTotalCount, value: 0 }, + { selector: ActivityLogsSelectors.getActivityLogsLoading, value: false }, ], - isLoading: false, - error: null, - totalCount: 25, + }), + { + provide: ActivatedRoute, + useValue: { parent: null } as Partial, }, - }, - } as any); - fixture.detectChanges(); + ], + }).compileComponents(); + + store = TestBed.inject(Store); + jest.spyOn(store, 'dispatch'); - const item = fixture.nativeElement.querySelector('[data-test="recent-activity-item"]'); - const content = fixture.nativeElement.querySelector('[data-test="recent-activity-item-content"]'); - const paginator = fixture.nativeElement.querySelector('[data-test="recent-activity-paginator"]'); - const dateText = fixture.nativeElement.querySelector('[data-test="recent-activity-item-date"]')?.textContent ?? ''; + fixture = TestBed.createComponent(RegistrationRecentActivityComponent); + component = fixture.componentInstance; + fixture.detectChanges(); - expect(item).toBeTruthy(); - expect(content?.innerHTML).toContain('formatted'); - expect(paginator).toBeTruthy(); - expect(dateText).toMatch(/\w{3} \d{1,2}, \d{4} \d{1,2}:\d{2} [AP]M/); + expect(store.dispatch).not.toHaveBeenCalled(); }); - it('does not render paginator when totalCount <= pageSize', () => { - store.reset({ - activityLogs: { - activityLogs: { - data: [{ id: 'log1', date: '2024-01-01T12:34:00Z', formattedActivity: 'formatted' }], - isLoading: false, - error: null, - totalCount: 10, - }, - }, - } as any); + it('should dispatch GetActivityLogs when currentPage changes', () => { + fixture.detectChanges(); + + (store.dispatch as jest.Mock).mockClear(); + + component.currentPage.set(2); fixture.detectChanges(); - const paginator = fixture.nativeElement.querySelector('[data-test="recent-activity-paginator"]'); - expect(paginator).toBeFalsy(); + expect(store.dispatch).toHaveBeenCalledWith( + expect.objectContaining({ + resourceId: 'reg123', + resourceType: CurrentResourceType.Registrations, + page: 2, + pageSize: 10, + }) + ); }); - it('dispatches on page change', () => { - const dispatchSpy = store.dispatch as jest.Mock; - dispatchSpy.mockClear(); + it('should update currentPage and dispatch on page change', () => { + fixture.detectChanges(); + + (store.dispatch as jest.Mock).mockClear(); - fixture.componentInstance.onPageChange({ page: 2 } as any); - expect(dispatchSpy).toHaveBeenCalledWith(expect.any(GetRegistrationActivityLogs)); + component.onPageChange({ page: 1 } as any); + fixture.detectChanges(); - const action = dispatchSpy.mock.calls.at(-1)?.[0] as GetRegistrationActivityLogs; - expect(action.page).toBe(3); + expect(component.currentPage()).toBe(2); + expect(store.dispatch).toHaveBeenCalledWith( + expect.objectContaining({ + page: 2, + }) + ); }); - it('does not dispatch when page change event has undefined page', () => { - const dispatchSpy = store.dispatch as jest.Mock; - dispatchSpy.mockClear(); + it('should not update currentPage when page is undefined', () => { + fixture.detectChanges(); - fixture.componentInstance.onPageChange({} as any); - expect(dispatchSpy).not.toHaveBeenCalled(); + const initialPage = component.currentPage(); + component.onPageChange({} as any); + + expect(component.currentPage()).toBe(initialPage); }); - it('computes firstIndex correctly after page change', () => { - fixture.componentInstance.onPageChange({ page: 1 } as any); - const firstIndex = (fixture.componentInstance as any)['firstIndex'](); - expect(firstIndex).toBe(10); + it('should compute firstIndex correctly', () => { + component.currentPage.set(1); + expect(component.firstIndex()).toBe(0); + + component.currentPage.set(2); + expect(component.firstIndex()).toBe(10); + + component.currentPage.set(3); + expect(component.firstIndex()).toBe(20); }); - it('clears store on destroy', () => { - const dispatchSpy = store.dispatch as jest.Mock; - dispatchSpy.mockClear(); + it('should clear store on destroy', () => { + fixture.detectChanges(); + + (store.dispatch as jest.Mock).mockClear(); fixture.destroy(); - expect(dispatchSpy).toHaveBeenCalledWith(expect.any(ClearActivityLogsStore)); + + expect(store.dispatch).toHaveBeenCalledWith(expect.any(ClearActivityLogs)); }); - it('shows skeleton while loading', () => { - store.reset({ - activityLogs: { - activityLogs: { data: [], isLoading: true, error: null, totalCount: 0 }, - }, - } as any); + it('should return activity logs from selector', () => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [RegistrationRecentActivityComponent, OSFTestingModule], + providers: [ + provideMockStore({ + signals: [ + { selector: ActivityLogsSelectors.getActivityLogs, value: MOCK_ACTIVITY_LOGS_WITH_DISPLAY }, + { selector: ActivityLogsSelectors.getActivityLogsTotalCount, value: 2 }, + { selector: ActivityLogsSelectors.getActivityLogsLoading, value: false }, + ], + }), + { + provide: ActivatedRoute, + useValue: ActivatedRouteMockBuilder.create().withParams({ id: 'reg123' }).build(), + }, + ], + }).compileComponents(); + fixture = TestBed.createComponent(RegistrationRecentActivityComponent); + component = fixture.componentInstance; fixture.detectChanges(); - expect(fixture.nativeElement.querySelector('[data-test="recent-activity-skeleton"]')).toBeTruthy(); - expect(fixture.nativeElement.querySelector('[data-test="recent-activity-list"]')).toBeFalsy(); - expect(fixture.nativeElement.querySelector('[data-test="recent-activity-paginator"]')).toBeFalsy(); + expect(component.activityLogs()).toEqual(MOCK_ACTIVITY_LOGS_WITH_DISPLAY); }); - it('renders expected ARIA roles/labels', () => { - store.reset({ - activityLogs: { - activityLogs: { - data: [{ id: 'log1', date: '2024-01-01T12:34:00Z', formattedActivity: 'formatted' }], - isLoading: false, - error: null, - totalCount: 1, + it('should return totalCount from selector', () => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [RegistrationRecentActivityComponent, OSFTestingModule], + providers: [ + provideMockStore({ + signals: [ + { selector: ActivityLogsSelectors.getActivityLogs, value: [] }, + { selector: ActivityLogsSelectors.getActivityLogsTotalCount, value: 15 }, + { selector: ActivityLogsSelectors.getActivityLogsLoading, value: false }, + ], + }), + { + provide: ActivatedRoute, + useValue: ActivatedRouteMockBuilder.create().withParams({ id: 'reg123' }).build(), }, - }, - } as any); + ], + }).compileComponents(); + + fixture = TestBed.createComponent(RegistrationRecentActivityComponent); + component = fixture.componentInstance; fixture.detectChanges(); - const region = fixture.nativeElement.querySelector('[role="region"]'); - const heading = fixture.nativeElement.querySelector('#recent-activity-title'); - const list = fixture.nativeElement.querySelector('[role="list"]'); - const listitem = fixture.nativeElement.querySelector('[role="listitem"]'); + expect(component.totalCount()).toBe(15); + }); + + it('should return isLoading from selector', () => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + imports: [RegistrationRecentActivityComponent, OSFTestingModule], + providers: [ + provideMockStore({ + signals: [ + { selector: ActivityLogsSelectors.getActivityLogs, value: [] }, + { selector: ActivityLogsSelectors.getActivityLogsTotalCount, value: 0 }, + { selector: ActivityLogsSelectors.getActivityLogsLoading, value: true }, + ], + }), + { + provide: ActivatedRoute, + useValue: ActivatedRouteMockBuilder.create().withParams({ id: 'reg123' }).build(), + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(RegistrationRecentActivityComponent); + component = fixture.componentInstance; + fixture.detectChanges(); - expect(region).toBeTruthy(); - expect(region.getAttribute('aria-labelledby')).toBe('recent-activity-title'); - expect(heading).toBeTruthy(); - expect(list).toBeTruthy(); - expect(listitem).toBeTruthy(); + expect(component.isLoading()).toBe(true); }); }); diff --git a/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.ts b/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.ts index 2ca855c53..eac381e8d 100644 --- a/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.ts +++ b/src/app/features/registry/pages/registration-recent-activity/registration-recent-activity.component.ts @@ -3,62 +3,62 @@ import { createDispatchMap, select } from '@ngxs/store'; import { TranslatePipe } from '@ngx-translate/core'; import { PaginatorState } from 'primeng/paginator'; -import { Skeleton } from 'primeng/skeleton'; -import { DatePipe } from '@angular/common'; -import { ChangeDetectionStrategy, Component, computed, inject, OnDestroy, signal } from '@angular/core'; +import { map, of } from 'rxjs'; + +import { ChangeDetectionStrategy, Component, computed, effect, inject, OnDestroy, signal } from '@angular/core'; +import { toSignal } from '@angular/core/rxjs-interop'; import { ActivatedRoute } from '@angular/router'; -import { ENVIRONMENT } from '@core/provider/environment.provider'; -import { CustomPaginatorComponent } from '@osf/shared/components/custom-paginator/custom-paginator.component'; -import { ACTIVITY_LOGS_DEFAULT_PAGE_SIZE } from '@shared/constants/activity-logs'; -import { - ActivityLogsSelectors, - ClearActivityLogsStore, - GetRegistrationActivityLogs, -} from '@shared/stores/activity-logs'; +import { RecentActivityListComponent } from '@osf/shared/components/recent-activity/recent-activity-list.component'; +import { ACTIVITY_LOGS_DEFAULT_PAGE_SIZE } from '@osf/shared/constants/activity-logs'; +import { CurrentResourceType } from '@osf/shared/enums/resource-type.enum'; +import { ActivityLogsSelectors, ClearActivityLogs, GetActivityLogs } from '@osf/shared/stores/activity-logs'; @Component({ selector: 'osf-registration-recent-activity', - imports: [TranslatePipe, DatePipe, CustomPaginatorComponent, Skeleton], + imports: [RecentActivityListComponent, TranslatePipe], templateUrl: './registration-recent-activity.component.html', changeDetection: ChangeDetectionStrategy.OnPush, }) export class RegistrationRecentActivityComponent implements OnDestroy { private readonly route = inject(ActivatedRoute); - readonly environment = inject(ENVIRONMENT); - readonly pageSize = this.environment.activityLogs?.pageSize ?? ACTIVITY_LOGS_DEFAULT_PAGE_SIZE; + readonly pageSize = ACTIVITY_LOGS_DEFAULT_PAGE_SIZE; - private readonly registrationId: string = (this.route.snapshot.params['id'] ?? - this.route.parent?.snapshot.params['id']) as string; + readonly registrationId = toSignal( + this.route.parent?.params.pipe(map((params) => params['id'])) ?? of(undefined) + ); currentPage = signal(1); - formattedActivityLogs = select(ActivityLogsSelectors.getFormattedActivityLogs); + activityLogs = select(ActivityLogsSelectors.getActivityLogs); totalCount = select(ActivityLogsSelectors.getActivityLogsTotalCount); isLoading = select(ActivityLogsSelectors.getActivityLogsLoading); firstIndex = computed(() => (this.currentPage() - 1) * this.pageSize); - actions = createDispatchMap({ - getRegistrationActivityLogs: GetRegistrationActivityLogs, - clearActivityLogsStore: ClearActivityLogsStore, - }); + actions = createDispatchMap({ getActivityLogs: GetActivityLogs, clearActivityLogsStore: ClearActivityLogs }); constructor() { - this.actions.getRegistrationActivityLogs(this.registrationId, 1, this.pageSize); + effect(() => { + const registrationId = this.registrationId(); + const page = this.currentPage(); + + if (registrationId) { + this.actions.getActivityLogs(registrationId, CurrentResourceType.Registrations, page, this.pageSize); + } + }); + } + + ngOnDestroy(): void { + this.actions.clearActivityLogsStore(); } onPageChange(event: PaginatorState) { if (event.page !== undefined) { const pageNumber = event.page + 1; this.currentPage.set(pageNumber); - this.actions.getRegistrationActivityLogs(this.registrationId, pageNumber, this.pageSize); } } - - ngOnDestroy(): void { - this.actions.clearActivityLogsStore(); - } } diff --git a/src/app/shared/components/recent-activity/recent-activity-list.component.html b/src/app/shared/components/recent-activity/recent-activity-list.component.html new file mode 100644 index 000000000..409006aa3 --- /dev/null +++ b/src/app/shared/components/recent-activity/recent-activity-list.component.html @@ -0,0 +1,40 @@ +@if (!isLoading()) { +
+ @for (activityLog of activityLogs(); track activityLog.id) { +
+
+ + +
+ } @empty { +
+ {{ 'project.overview.recentActivity.noActivity' | translate }} +
+ } +
+ + @if (totalCount() > pageSize()) { + + } +} @else { +
+ + + + + +
+} diff --git a/src/app/shared/components/recent-activity/recent-activity-list.component.scss b/src/app/shared/components/recent-activity/recent-activity-list.component.scss new file mode 100644 index 000000000..26131210d --- /dev/null +++ b/src/app/shared/components/recent-activity/recent-activity-list.component.scss @@ -0,0 +1,8 @@ +.activity-item { + border-bottom: 1px solid var(--grey-2); + line-height: 1.5rem; + + .activity-date { + min-width: 25%; + } +} diff --git a/src/app/shared/components/recent-activity/recent-activity-list.component.spec.ts b/src/app/shared/components/recent-activity/recent-activity-list.component.spec.ts new file mode 100644 index 000000000..7ac02a83b --- /dev/null +++ b/src/app/shared/components/recent-activity/recent-activity-list.component.spec.ts @@ -0,0 +1,140 @@ +import { MockComponent } from 'ng-mocks'; + +import { PaginatorState } from 'primeng/paginator'; + +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CustomPaginatorComponent } from '@osf/shared/components/custom-paginator/custom-paginator.component'; + +import { RecentActivityListComponent } from './recent-activity-list.component'; + +import { + makeActivityLogWithDisplay, + MOCK_ACTIVITY_LOGS_WITH_DISPLAY, +} from '@testing/mocks/activity-log-with-display.mock'; +import { OSFTestingModule } from '@testing/osf.testing.module'; + +describe('RecentActivityListComponent', () => { + let component: RecentActivityListComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RecentActivityListComponent, OSFTestingModule, MockComponent(CustomPaginatorComponent)], + }).compileComponents(); + + fixture = TestBed.createComponent(RecentActivityListComponent); + component = fixture.componentInstance; + fixture.componentRef.setInput('activityLogs', MOCK_ACTIVITY_LOGS_WITH_DISPLAY); + fixture.componentRef.setInput('isLoading', false); + fixture.componentRef.setInput('totalCount', 10); + fixture.componentRef.setInput('pageSize', 5); + fixture.componentRef.setInput('firstIndex', 0); + fixture.detectChanges(); + }); + + it('should have input values set correctly', () => { + expect(component.activityLogs()).toEqual(MOCK_ACTIVITY_LOGS_WITH_DISPLAY); + expect(component.isLoading()).toBe(false); + expect(component.totalCount()).toBe(10); + expect(component.pageSize()).toBe(5); + expect(component.firstIndex()).toBe(0); + }); + + it('should emit pageChange event when onPageChange is called', () => { + jest.spyOn(component.pageChange, 'emit'); + const mockEvent: PaginatorState = { page: 1, first: 5, rows: 5 }; + + component.onPageChange(mockEvent); + + expect(component.pageChange.emit).toHaveBeenCalledWith(mockEvent); + }); + + it('should handle empty activity logs', () => { + fixture.componentRef.setInput('activityLogs', []); + fixture.componentRef.setInput('totalCount', 0); + fixture.detectChanges(); + + expect(component.activityLogs()).toEqual([]); + expect(component.totalCount()).toBe(0); + }); + + it('should handle loading state', () => { + fixture.componentRef.setInput('isLoading', true); + fixture.detectChanges(); + + expect(component.isLoading()).toBe(true); + }); + + it('should update inputs dynamically', () => { + const newActivityLogs = [makeActivityLogWithDisplay({ id: 'log3', action: 'delete' })]; + + fixture.componentRef.setInput('activityLogs', newActivityLogs); + fixture.componentRef.setInput('isLoading', true); + fixture.componentRef.setInput('totalCount', 50); + fixture.componentRef.setInput('pageSize', 20); + fixture.componentRef.setInput('firstIndex', 20); + fixture.detectChanges(); + + expect(component.activityLogs()).toEqual(newActivityLogs); + expect(component.isLoading()).toBe(true); + expect(component.totalCount()).toBe(50); + expect(component.pageSize()).toBe(20); + expect(component.firstIndex()).toBe(20); + }); + + it('should handle PaginatorState with undefined or null values', () => { + jest.spyOn(component.pageChange, 'emit'); + const undefinedEvent: PaginatorState = { page: undefined, first: 0, rows: 5 }; + const nullEvent: PaginatorState = { page: null as any, first: null as any, rows: 5 }; + + component.onPageChange(undefinedEvent); + component.onPageChange(nullEvent); + + expect(component.pageChange.emit).toHaveBeenCalledWith(undefinedEvent); + expect(component.pageChange.emit).toHaveBeenCalledWith(nullEvent); + expect(component.pageChange.emit).toHaveBeenCalledTimes(2); + }); + + it('should handle activity logs without formattedActivity', () => { + const logsWithoutFormatted = [ + makeActivityLogWithDisplay({ id: 'log4', action: 'remove', formattedActivity: undefined }), + ]; + + fixture.componentRef.setInput('activityLogs', logsWithoutFormatted); + fixture.detectChanges(); + + expect(component.activityLogs()).toEqual(logsWithoutFormatted); + expect(component.activityLogs()[0].formattedActivity).toBeUndefined(); + }); + + it('should handle activity logs with different action types', () => { + const logsWithDifferentActions = [ + makeActivityLogWithDisplay({ id: 'log5', action: 'add', formattedActivity: 'Added activity' }), + makeActivityLogWithDisplay({ id: 'log6', action: 'remove', formattedActivity: 'Removed activity' }), + ]; + + fixture.componentRef.setInput('activityLogs', logsWithDifferentActions); + fixture.detectChanges(); + + expect(component.activityLogs()).toEqual(logsWithDifferentActions); + expect(component.activityLogs()[0].action).toBe('add'); + expect(component.activityLogs()[1].action).toBe('remove'); + }); + + it('should handle multiple consecutive page changes', () => { + jest.spyOn(component.pageChange, 'emit'); + const event1: PaginatorState = { page: 0, first: 0, rows: 5 }; + const event2: PaginatorState = { page: 1, first: 5, rows: 5 }; + const event3: PaginatorState = { page: 2, first: 10, rows: 5 }; + + component.onPageChange(event1); + component.onPageChange(event2); + component.onPageChange(event3); + + expect(component.pageChange.emit).toHaveBeenCalledTimes(3); + expect(component.pageChange.emit).toHaveBeenNthCalledWith(1, event1); + expect(component.pageChange.emit).toHaveBeenNthCalledWith(2, event2); + expect(component.pageChange.emit).toHaveBeenNthCalledWith(3, event3); + }); +}); diff --git a/src/app/shared/components/recent-activity/recent-activity-list.component.ts b/src/app/shared/components/recent-activity/recent-activity-list.component.ts new file mode 100644 index 000000000..78f20fb1f --- /dev/null +++ b/src/app/shared/components/recent-activity/recent-activity-list.component.ts @@ -0,0 +1,31 @@ +import { TranslatePipe } from '@ngx-translate/core'; + +import { PaginatorState } from 'primeng/paginator'; +import { Skeleton } from 'primeng/skeleton'; + +import { DatePipe } from '@angular/common'; +import { ChangeDetectionStrategy, Component, input, output } from '@angular/core'; + +import { CustomPaginatorComponent } from '@osf/shared/components/custom-paginator/custom-paginator.component'; +import { ActivityLogWithDisplay } from '@osf/shared/models/activity-logs/activity-log-with-display.model'; + +@Component({ + selector: 'osf-recent-activity-list', + imports: [TranslatePipe, DatePipe, CustomPaginatorComponent, Skeleton], + templateUrl: './recent-activity-list.component.html', + styleUrl: './recent-activity-list.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class RecentActivityListComponent { + activityLogs = input.required(); + isLoading = input.required(); + totalCount = input.required(); + pageSize = input.required(); + firstIndex = input.required(); + + pageChange = output(); + + onPageChange(event: PaginatorState): void { + this.pageChange.emit(event); + } +} diff --git a/src/app/shared/mappers/activity-logs.mapper.ts b/src/app/shared/mappers/activity-logs.mapper.ts index a4c1fc1c6..ba0caf8ea 100644 --- a/src/app/shared/mappers/activity-logs.mapper.ts +++ b/src/app/shared/mappers/activity-logs.mapper.ts @@ -3,7 +3,7 @@ import { replaceBadEncodedChars } from '@shared/helpers/format-bad-encoding.help import { DEFAULT_TABLE_PARAMS } from '../constants/default-table-params.constants'; import { ActivityLog, LogContributor } from '../models/activity-logs/activity-logs.model'; import { ActivityLogJsonApi, LogContributorJsonApi } from '../models/activity-logs/activity-logs-json-api.model'; -import { JsonApiResponseWithMeta, MetaAnonymousJsonApi } from '../models/common/json-api.model'; +import { ResponseJsonApi } from '../models/common/json-api.model'; import { PaginatedData } from '../models/paginated-data.model'; export class ActivityLogsMapper { @@ -154,9 +154,7 @@ export class ActivityLogsMapper { }; } - static fromGetActivityLogsResponse( - logs: JsonApiResponseWithMeta - ): PaginatedData { + static fromGetActivityLogsResponse(logs: ResponseJsonApi): PaginatedData { const isAnonymous = logs.meta.anonymous ?? false; return { data: logs.data.map((log) => this.fromActivityLogJsonApi(log, isAnonymous)), diff --git a/src/app/shared/services/activity-logs/activity-logs.service.spec.ts b/src/app/shared/services/activity-logs/activity-logs.service.spec.ts index 4b32f5ba7..2cb8979f4 100644 --- a/src/app/shared/services/activity-logs/activity-logs.service.spec.ts +++ b/src/app/shared/services/activity-logs/activity-logs.service.spec.ts @@ -1,6 +1,8 @@ import { HttpTestingController } from '@angular/common/http/testing'; import { inject, TestBed } from '@angular/core/testing'; +import { CurrentResourceType } from '@osf/shared/enums/resource-type.enum'; + import { ActivityLogDisplayService } from './activity-log-display.service'; import { ActivityLogsService } from './activity-logs.service'; @@ -15,6 +17,7 @@ import { OSFTestingStoreModule } from '@testing/osf.testing.module'; describe('Service: ActivityLogs', () => { let service: ActivityLogsService; const environment = EnvironmentTokenMock; + const apiBase = environment.useValue.apiDomainUrl; beforeEach(() => { TestBed.configureTestingModule({ @@ -27,31 +30,54 @@ describe('Service: ActivityLogs', () => { service = TestBed.inject(ActivityLogsService); }); - it('fetchRegistrationLogs maps + formats', inject([HttpTestingController], (httpMock: HttpTestingController) => { - let result: any; - service.fetchRegistrationLogs('reg1', 1, 10).subscribe((res) => (result = res)); - - const req = httpMock.expectOne(buildRegistrationLogsUrl('reg1', 1, 10, environment.useValue.apiDomainUrl)); - expect(req.request.method).toBe('GET'); - expect(req.request.params.get('page')).toBe('1'); - expect(req.request.params.get('page[size]')).toBe('10'); - - req.flush(getActivityLogsResponse()); - - expect(result.totalCount).toBe(2); - expect(result.data[0].formattedActivity).toBe('FMT'); - - httpMock.verify(); - })); - - it('fetchLogs maps + formats', inject([HttpTestingController], (httpMock: HttpTestingController) => { + it('fetchLogs for registrations maps + formats', inject( + [HttpTestingController], + (httpMock: HttpTestingController) => { + let result: any; + service.fetchLogs(CurrentResourceType.Registrations, 'reg1', 1, 10).subscribe((res) => (result = res)); + + const baseUrl = buildRegistrationLogsUrl('reg1', 1, 10, apiBase).split('?')[0]; + const req = httpMock.expectOne((request) => { + return request.url.startsWith(baseUrl) && request.method === 'GET'; + }); + expect(req.request.method).toBe('GET'); + expect(req.request.params.get('page')).toBe('1'); + expect(req.request.params.get('page[size]')).toBe('10'); + expect(req.request.params.getAll('embed[]')).toEqual([ + 'original_node', + 'user', + 'linked_node', + 'linked_registration', + 'template_node', + ]); + + req.flush(getActivityLogsResponse()); + + expect(result.totalCount).toBe(2); + expect(result.data[0].formattedActivity).toBe('FMT'); + + httpMock.verify(); + } + )); + + it('fetchLogs for projects maps + formats', inject([HttpTestingController], (httpMock: HttpTestingController) => { let result: any; - service.fetchLogs('proj1', 2, 5).subscribe((res) => (result = res)); + service.fetchLogs(CurrentResourceType.Projects, 'proj1', 2, 5).subscribe((res) => (result = res)); - const req = httpMock.expectOne(buildNodeLogsUrl('proj1', 2, 5, environment.useValue.apiDomainUrl)); + const baseUrl = buildNodeLogsUrl('proj1', 2, 5, apiBase).split('?')[0]; + const req = httpMock.expectOne((request) => { + return request.url.startsWith(baseUrl) && request.method === 'GET'; + }); expect(req.request.method).toBe('GET'); expect(req.request.params.get('page')).toBe('2'); expect(req.request.params.get('page[size]')).toBe('5'); + expect(req.request.params.getAll('embed[]')).toEqual([ + 'original_node', + 'user', + 'linked_node', + 'linked_registration', + 'template_node', + ]); req.flush(getActivityLogsResponse()); @@ -61,28 +87,37 @@ describe('Service: ActivityLogs', () => { httpMock.verify(); })); - it('fetchRegistrationLogs propagates error', inject([HttpTestingController], (httpMock: HttpTestingController) => { + it('fetchLogs for registrations propagates error', inject( + [HttpTestingController], + (httpMock: HttpTestingController) => { + let errorObj: any; + service.fetchLogs(CurrentResourceType.Registrations, 'reg2', 1, 10).subscribe({ + next: () => {}, + error: (e) => (errorObj = e), + }); + + const baseUrl = buildRegistrationLogsUrl('reg2', 1, 10, apiBase).split('?')[0]; + const req = httpMock.expectOne((request) => { + return request.url.startsWith(baseUrl) && request.method === 'GET'; + }); + req.flush({ errors: [{ detail: 'boom' }] }, { status: 500, statusText: 'Server Error' }); + + expect(errorObj).toBeTruthy(); + httpMock.verify(); + } + )); + + it('fetchLogs for projects propagates error', inject([HttpTestingController], (httpMock: HttpTestingController) => { let errorObj: any; - service.fetchRegistrationLogs('reg2', 1, 10).subscribe({ + service.fetchLogs(CurrentResourceType.Projects, 'proj500', 1, 10).subscribe({ next: () => {}, error: (e) => (errorObj = e), }); - const req = httpMock.expectOne(buildRegistrationLogsUrl('reg2', 1, 10, environment.useValue.apiDomainUrl)); - req.flush({ errors: [{ detail: 'boom' }] }, { status: 500, statusText: 'Server Error' }); - - expect(errorObj).toBeTruthy(); - httpMock.verify(); - })); - - it('fetchLogs propagates error', inject([HttpTestingController], (httpMock: HttpTestingController) => { - let errorObj: any; - service.fetchLogs('proj500', 1, 10).subscribe({ - next: () => {}, - error: (e) => (errorObj = e), + const baseUrl = buildNodeLogsUrl('proj500', 1, 10, apiBase).split('?')[0]; + const req = httpMock.expectOne((request) => { + return request.url.startsWith(baseUrl) && request.method === 'GET'; }); - - const req = httpMock.expectOne(buildNodeLogsUrl('proj500', 1, 10, environment.useValue.apiDomainUrl)); req.flush({ errors: [{ detail: 'boom' }] }, { status: 500, statusText: 'Server Error' }); expect(errorObj).toBeTruthy(); diff --git a/src/app/shared/services/activity-logs/activity-logs.service.ts b/src/app/shared/services/activity-logs/activity-logs.service.ts index 98f26c473..5f50ed5a0 100644 --- a/src/app/shared/services/activity-logs/activity-logs.service.ts +++ b/src/app/shared/services/activity-logs/activity-logs.service.ts @@ -4,11 +4,11 @@ import { map } from 'rxjs/operators'; import { inject, Injectable } from '@angular/core'; import { ENVIRONMENT } from '@core/provider/environment.provider'; +import { CurrentResourceType } from '@osf/shared/enums/resource-type.enum'; import { ActivityLogsMapper } from '@osf/shared/mappers/activity-logs.mapper'; import { ActivityLogWithDisplay } from '@osf/shared/models/activity-logs/activity-log-with-display.model'; -import { ActivityLog } from '@osf/shared/models/activity-logs/activity-logs.model'; import { ActivityLogJsonApi } from '@osf/shared/models/activity-logs/activity-logs-json-api.model'; -import { JsonApiResponseWithMeta, MetaAnonymousJsonApi } from '@osf/shared/models/common/json-api.model'; +import { ResponseJsonApi } from '@osf/shared/models/common/json-api.model'; import { PaginatedData } from '@osf/shared/models/paginated-data.model'; import { JsonApiService } from '../json-api.service'; @@ -18,42 +18,17 @@ import { ActivityLogDisplayService } from './activity-log-display.service'; @Injectable({ providedIn: 'root' }) export class ActivityLogsService { private jsonApiService = inject(JsonApiService); - private display = inject(ActivityLogDisplayService); + private activityDisplayService = inject(ActivityLogDisplayService); private readonly environment = inject(ENVIRONMENT); private readonly apiUrl = `${this.environment.apiDomainUrl}/v2`; - private formatActivities(result: PaginatedData): PaginatedData { - return { - ...result, - data: result.data.map((log) => ({ - ...log, - formattedActivity: this.display.getActivityDisplay(log), - })), - }; - } - - fetchLogs(projectId: string, page = 1, pageSize: number): Observable> { - const url = `${this.apiUrl}/nodes/${projectId}/logs/`; - const params: Record = { - 'embed[]': ['original_node', 'user', 'linked_node', 'linked_registration', 'template_node'], - page, - 'page[size]': pageSize, - }; - - return this.jsonApiService - .get>(url, params) - .pipe( - map((res) => ActivityLogsMapper.fromGetActivityLogsResponse(res)), - map((mapped) => this.formatActivities(mapped)) - ); - } - - fetchRegistrationLogs( - registrationId: string, + fetchLogs( + resourceType: CurrentResourceType.Projects | CurrentResourceType.Registrations, + resourceId: string, page = 1, pageSize: number ): Observable> { - const url = `${this.apiUrl}/registrations/${registrationId}/logs/`; + const url = `${this.apiUrl}/${resourceType}/${resourceId}/logs/`; const params: Record = { 'embed[]': ['original_node', 'user', 'linked_node', 'linked_registration', 'template_node'], page, @@ -61,10 +36,19 @@ export class ActivityLogsService { }; return this.jsonApiService - .get>(url, params) - .pipe( - map((res) => ActivityLogsMapper.fromGetActivityLogsResponse(res)), - map((mapped) => this.formatActivities(mapped)) - ); + .get>(url, params) + .pipe(map((res) => this.formatActivities(res))); + } + + private formatActivities(response: ResponseJsonApi): PaginatedData { + const mapped = ActivityLogsMapper.fromGetActivityLogsResponse(response); + + return { + ...mapped, + data: mapped.data.map((log) => ({ + ...log, + formattedActivity: this.activityDisplayService.getActivityDisplay(log), + })), + }; } } diff --git a/src/app/shared/stores/activity-logs/activity-logs.actions.ts b/src/app/shared/stores/activity-logs/activity-logs.actions.ts index 48197e5fc..695c4432e 100644 --- a/src/app/shared/stores/activity-logs/activity-logs.actions.ts +++ b/src/app/shared/stores/activity-logs/activity-logs.actions.ts @@ -1,22 +1,16 @@ +import { CurrentResourceType } from '@shared/enums/resource-type.enum'; + export class GetActivityLogs { static readonly type = '[ActivityLogs] Get Activity Logs'; constructor( - public projectId: string, - public page = 1, - public pageSize: number - ) {} -} - -export class GetRegistrationActivityLogs { - static readonly type = '[ActivityLogs] Get Registration Activity Logs'; - constructor( - public registrationId: string, + public resourceId: string, + public resourceType: CurrentResourceType.Projects | CurrentResourceType.Registrations, public page = 1, public pageSize: number ) {} } -export class ClearActivityLogsStore { - static readonly type = '[ActivityLogs] Clear Store'; +export class ClearActivityLogs { + static readonly type = '[ActivityLogs] Clear Activity Logs'; } diff --git a/src/app/shared/stores/activity-logs/activity-logs.selectors.spec.ts b/src/app/shared/stores/activity-logs/activity-logs.selectors.spec.ts index 78da1f606..6d18a2bf3 100644 --- a/src/app/shared/stores/activity-logs/activity-logs.selectors.spec.ts +++ b/src/app/shared/stores/activity-logs/activity-logs.selectors.spec.ts @@ -1,35 +1,121 @@ import { provideStore, Store } from '@ngxs/store'; +import { provideHttpClient } from '@angular/common/http'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; +import { ActivityLogDisplayService } from '@osf/shared/services/activity-logs/activity-log-display.service'; + import { ActivityLogsSelectors } from './activity-logs.selectors'; import { ActivityLogsState } from './activity-logs.state'; -describe.skip('ActivityLogsSelectors', () => { +import { + makeActivityLogWithDisplay, + MOCK_ACTIVITY_LOGS_WITH_DISPLAY, +} from '@testing/mocks/activity-log-with-display.mock'; + +describe('ActivityLogsSelectors', () => { let store: Store; beforeEach(() => { TestBed.configureTestingModule({ - providers: [provideStore([ActivityLogsState])], + providers: [ + provideStore([ActivityLogsState]), + provideHttpClient(), + provideHttpClientTesting(), + { + provide: ActivityLogDisplayService, + useValue: { getActivityDisplay: jest.fn().mockReturnValue('formatted') }, + }, + ], }); store = TestBed.inject(Store); }); - it('selects logs, formatted logs, totalCount, and loading', () => { + it('selects activity logs', () => { store.reset({ activityLogs: { activityLogs: { - data: [{ id: '1', date: '2024-01-01T00:00:00Z', formattedActivity: 'FMT' }], + data: MOCK_ACTIVITY_LOGS_WITH_DISPLAY, isLoading: false, error: null, - totalCount: 1, + totalCount: 2, + }, + }, + } as any); + + const logs = store.selectSnapshot(ActivityLogsSelectors.getActivityLogs); + expect(logs.length).toBe(2); + expect(logs).toEqual(MOCK_ACTIVITY_LOGS_WITH_DISPLAY); + expect(logs[0].formattedActivity).toBe('Test activity 1'); + expect(logs[1].formattedActivity).toBe('Test activity 2'); + }); + + it('selects total count', () => { + store.reset({ + activityLogs: { + activityLogs: { + data: MOCK_ACTIVITY_LOGS_WITH_DISPLAY, + isLoading: false, + error: null, + totalCount: 10, + }, + }, + } as any); + + expect(store.selectSnapshot(ActivityLogsSelectors.getActivityLogsTotalCount)).toBe(10); + }); + + it('selects loading state', () => { + store.reset({ + activityLogs: { + activityLogs: { + data: [], + isLoading: true, + error: null, + totalCount: 0, + }, + }, + } as any); + + expect(store.selectSnapshot(ActivityLogsSelectors.getActivityLogsLoading)).toBe(true); + }); + + it('selects empty activity logs', () => { + store.reset({ + activityLogs: { + activityLogs: { + data: [], + isLoading: false, + error: null, + totalCount: 0, }, }, } as any); - expect(store.selectSnapshot(ActivityLogsSelectors.getActivityLogs).length).toBe(1); - expect(store.selectSnapshot(ActivityLogsSelectors.getFormattedActivityLogs)[0].formattedActivity).toBe('FMT'); - expect(store.selectSnapshot(ActivityLogsSelectors.getActivityLogsTotalCount)).toBe(1); + const logs = store.selectSnapshot(ActivityLogsSelectors.getActivityLogs); + expect(logs.length).toBe(0); + expect(logs).toEqual([]); + expect(store.selectSnapshot(ActivityLogsSelectors.getActivityLogsTotalCount)).toBe(0); expect(store.selectSnapshot(ActivityLogsSelectors.getActivityLogsLoading)).toBe(false); }); + + it('selects activity logs with different states', () => { + const customLog = makeActivityLogWithDisplay({ id: 'custom-log', action: 'delete' }); + store.reset({ + activityLogs: { + activityLogs: { + data: [customLog], + isLoading: false, + error: null, + totalCount: 1, + }, + }, + } as any); + + const logs = store.selectSnapshot(ActivityLogsSelectors.getActivityLogs); + expect(logs.length).toBe(1); + expect(logs[0].id).toBe('custom-log'); + expect(logs[0].action).toBe('delete'); + }); }); diff --git a/src/app/shared/stores/activity-logs/activity-logs.selectors.ts b/src/app/shared/stores/activity-logs/activity-logs.selectors.ts index f6c9de8e4..3e93b7da0 100644 --- a/src/app/shared/stores/activity-logs/activity-logs.selectors.ts +++ b/src/app/shared/stores/activity-logs/activity-logs.selectors.ts @@ -1,19 +1,13 @@ import { Selector } from '@ngxs/store'; import { ActivityLogWithDisplay } from '@osf/shared/models/activity-logs/activity-log-with-display.model'; -import { ActivityLog } from '@osf/shared/models/activity-logs/activity-logs.model'; import { ActivityLogsStateModel } from './activity-logs.model'; import { ActivityLogsState } from './activity-logs.state'; export class ActivityLogsSelectors { @Selector([ActivityLogsState]) - static getActivityLogs(state: ActivityLogsStateModel): ActivityLog[] { - return state.activityLogs.data; - } - - @Selector([ActivityLogsState]) - static getFormattedActivityLogs(state: ActivityLogsStateModel): ActivityLogWithDisplay[] { + static getActivityLogs(state: ActivityLogsStateModel): ActivityLogWithDisplay[] { return state.activityLogs.data; } diff --git a/src/app/shared/stores/activity-logs/activity-logs.state.spec.ts b/src/app/shared/stores/activity-logs/activity-logs.state.spec.ts index 96dced6e2..d3536146b 100644 --- a/src/app/shared/stores/activity-logs/activity-logs.state.spec.ts +++ b/src/app/shared/stores/activity-logs/activity-logs.state.spec.ts @@ -4,9 +4,10 @@ import { provideHttpClient } from '@angular/common/http'; import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; import { inject, TestBed } from '@angular/core/testing'; +import { CurrentResourceType } from '@osf/shared/enums/resource-type.enum'; import { ActivityLogDisplayService } from '@osf/shared/services/activity-logs/activity-log-display.service'; -import { ClearActivityLogsStore, GetActivityLogs, GetRegistrationActivityLogs } from './activity-logs.actions'; +import { ClearActivityLogs, GetActivityLogs } from './activity-logs.actions'; import { ActivityLogsState } from './activity-logs.state'; import { @@ -16,9 +17,10 @@ import { } from '@testing/data/activity-logs/activity-logs.data'; import { EnvironmentTokenMock } from '@testing/mocks/environment.token.mock'; -describe.skip('State: ActivityLogs', () => { +describe('State: ActivityLogs', () => { let store: Store; const environment = EnvironmentTokenMock; + const apiBase = environment.useValue.apiDomainUrl; beforeEach(() => { TestBed.configureTestingModule({ @@ -41,13 +43,17 @@ describe.skip('State: ActivityLogs', () => { (httpMock: HttpTestingController) => { let snapshot: any; - store.dispatch(new GetRegistrationActivityLogs('reg123', 1, 10)).subscribe(() => { + store.dispatch(new GetActivityLogs('reg123', CurrentResourceType.Registrations, 1, 10)).subscribe(() => { snapshot = store.snapshot().activityLogs.activityLogs; }); expect(store.selectSnapshot((s: any) => s.activityLogs.activityLogs.isLoading)).toBe(true); - const req = httpMock.expectOne(buildRegistrationLogsUrl('reg123', 1, 10, environment.useValue.apiDomainUrl)); + const fullUrl = buildRegistrationLogsUrl('reg123', 1, 10, apiBase); + const urlPath = fullUrl.split('?')[0].replace(apiBase, ''); + const req = httpMock.expectOne((request) => { + return request.url.includes(urlPath) && request.method === 'GET'; + }); expect(req.request.method).toBe('GET'); req.flush(getActivityLogsResponse()); @@ -63,11 +69,15 @@ describe.skip('State: ActivityLogs', () => { it('handles error when loading registration logs', inject( [HttpTestingController], (httpMock: HttpTestingController) => { - store.dispatch(new GetRegistrationActivityLogs('reg500', 1, 10)).subscribe(); + store.dispatch(new GetActivityLogs('reg500', CurrentResourceType.Registrations, 1, 10)).subscribe(); expect(store.selectSnapshot((s: any) => s.activityLogs.activityLogs.isLoading)).toBe(true); - const req = httpMock.expectOne(buildRegistrationLogsUrl('reg500', 1, 10, environment.useValue.apiDomainUrl)); + const fullUrl = buildRegistrationLogsUrl('reg500', 1, 10, apiBase); + const urlPath = fullUrl.split('?')[0].replace(apiBase, ''); + const req = httpMock.expectOne((request) => { + return request.url.includes(urlPath) && request.method === 'GET'; + }); req.flush({ errors: [{ detail: 'boom' }] }, { status: 500, statusText: 'Server Error' }); const snap = store.snapshot().activityLogs.activityLogs; @@ -83,13 +93,17 @@ describe.skip('State: ActivityLogs', () => { [HttpTestingController], (httpMock: HttpTestingController) => { let snapshot: any; - store.dispatch(new GetActivityLogs('proj123', 1, 10)).subscribe(() => { + store.dispatch(new GetActivityLogs('proj123', CurrentResourceType.Projects, 1, 10)).subscribe(() => { snapshot = store.snapshot().activityLogs.activityLogs; }); expect(store.selectSnapshot((s: any) => s.activityLogs.activityLogs.isLoading)).toBe(true); - const req = httpMock.expectOne(buildNodeLogsUrl('proj123', 1, 10, environment.useValue.apiDomainUrl)); + const fullUrl = buildNodeLogsUrl('proj123', 1, 10, apiBase); + const urlPath = fullUrl.split('?')[0].replace(apiBase, ''); + const req = httpMock.expectOne((request) => { + return request.url.includes(urlPath) && request.method === 'GET'; + }); expect(req.request.method).toBe('GET'); req.flush(getActivityLogsResponse()); @@ -105,11 +119,15 @@ describe.skip('State: ActivityLogs', () => { it('handles error when loading project logs (nodes)', inject( [HttpTestingController], (httpMock: HttpTestingController) => { - store.dispatch(new GetActivityLogs('proj500', 1, 10)).subscribe(); + store.dispatch(new GetActivityLogs('proj500', CurrentResourceType.Projects, 1, 10)).subscribe(); expect(store.selectSnapshot((s: any) => s.activityLogs.activityLogs.isLoading)).toBe(true); - const req = httpMock.expectOne(buildNodeLogsUrl('proj500', 1, 10, environment.useValue.apiDomainUrl)); + const fullUrl = buildNodeLogsUrl('proj500', 1, 10, apiBase); + const urlPath = fullUrl.split('?')[0].replace(apiBase, ''); + const req = httpMock.expectOne((request) => { + return request.url.includes(urlPath) && request.method === 'GET'; + }); req.flush({ errors: [{ detail: 'boom' }] }, { status: 500, statusText: 'Server Error' }); const snap = store.snapshot().activityLogs.activityLogs; @@ -128,7 +146,7 @@ describe.skip('State: ActivityLogs', () => { }, } as any); - store.dispatch(new ClearActivityLogsStore()); + store.dispatch(new ClearActivityLogs()); const snap = store.snapshot().activityLogs.activityLogs; expect(snap.data).toEqual([]); expect(snap.totalCount).toBe(0); diff --git a/src/app/shared/stores/activity-logs/activity-logs.state.ts b/src/app/shared/stores/activity-logs/activity-logs.state.ts index e73bb69d2..f9baccd4c 100644 --- a/src/app/shared/stores/activity-logs/activity-logs.state.ts +++ b/src/app/shared/stores/activity-logs/activity-logs.state.ts @@ -1,13 +1,14 @@ import { Action, State, StateContext } from '@ngxs/store'; -import { catchError, EMPTY } from 'rxjs'; +import { catchError } from 'rxjs'; import { tap } from 'rxjs/operators'; import { inject, Injectable } from '@angular/core'; +import { handleSectionError } from '@osf/shared/helpers/state-error.handler'; import { ActivityLogsService } from '@osf/shared/services/activity-logs/activity-logs.service'; -import { ClearActivityLogsStore, GetActivityLogs, GetRegistrationActivityLogs } from './activity-logs.actions'; +import { ClearActivityLogs, GetActivityLogs } from './activity-logs.actions'; import { ACTIVITY_LOGS_STATE_DEFAULT, ActivityLogsStateModel } from './activity-logs.model'; @State({ @@ -24,43 +25,19 @@ export class ActivityLogsState { activityLogs: { data: [], isLoading: true, error: null, totalCount: 0 }, }); - return this.activityLogsService.fetchLogs(action.projectId, action.page, action.pageSize).pipe( - tap((res) => { - ctx.patchState({ - activityLogs: { data: res.data, isLoading: false, error: null, totalCount: res.totalCount }, - }); - }), - catchError((error) => { - ctx.patchState({ - activityLogs: { data: [], isLoading: false, error, totalCount: 0 }, - }); - return EMPTY; - }) - ); + return this.activityLogsService + .fetchLogs(action.resourceType, action.resourceId, action.page, action.pageSize) + .pipe( + tap((res) => { + ctx.patchState({ + activityLogs: { data: res.data, isLoading: false, error: null, totalCount: res.totalCount }, + }); + }), + catchError((error) => handleSectionError(ctx, 'activityLogs', error)) + ); } - @Action(GetRegistrationActivityLogs) - getRegistrationActivityLogs(ctx: StateContext, action: GetRegistrationActivityLogs) { - ctx.patchState({ - activityLogs: { data: [], isLoading: true, error: null, totalCount: 0 }, - }); - - return this.activityLogsService.fetchRegistrationLogs(action.registrationId, action.page, action.pageSize).pipe( - tap((res) => { - ctx.patchState({ - activityLogs: { data: res.data, isLoading: false, error: null, totalCount: res.totalCount }, - }); - }), - catchError((error) => { - ctx.patchState({ - activityLogs: { data: [], isLoading: false, error, totalCount: 0 }, - }); - return EMPTY; - }) - ); - } - - @Action(ClearActivityLogsStore) + @Action(ClearActivityLogs) clearActivityLogsStore(ctx: StateContext) { ctx.setState(ACTIVITY_LOGS_STATE_DEFAULT); } diff --git a/src/styles/components/nodes.scss b/src/styles/components/nodes.scss index 15b959ab4..87f4d0ccc 100644 --- a/src/styles/components/nodes.scss +++ b/src/styles/components/nodes.scss @@ -19,3 +19,9 @@ color: var(--dark-blue-1); } } + +.activity-item { + a { + font-weight: bold; + } +} diff --git a/src/testing/mocks/activity-log-with-display.mock.ts b/src/testing/mocks/activity-log-with-display.mock.ts new file mode 100644 index 000000000..293e7228f --- /dev/null +++ b/src/testing/mocks/activity-log-with-display.mock.ts @@ -0,0 +1,41 @@ +import { ActivityLogWithDisplay } from '@osf/shared/models/activity-logs/activity-log-with-display.model'; + +import structuredClone from 'structured-clone'; + +export function makeActivityLogWithDisplay(overrides: Partial = {}): ActivityLogWithDisplay { + return structuredClone({ + id: 'log1', + type: 'logs', + action: 'update', + date: '2024-01-01T00:00:00Z', + params: { + contributors: [], + paramsNode: { id: 'node1', title: 'Test Node' }, + paramsProject: null, + pointer: null, + }, + formattedActivity: 'Test activity', + ...overrides, + }); +} + +export const MOCK_ACTIVITY_LOGS_WITH_DISPLAY: ActivityLogWithDisplay[] = [ + makeActivityLogWithDisplay({ + id: 'log1', + action: 'update', + date: '2024-01-01T00:00:00Z', + formattedActivity: 'Test activity 1', + }), + makeActivityLogWithDisplay({ + id: 'log2', + action: 'create', + date: '2024-01-02T00:00:00Z', + params: { + contributors: [], + paramsNode: { id: 'node2', title: 'Test Node 2' }, + paramsProject: null, + pointer: null, + }, + formattedActivity: 'Test activity 2', + }), +]; From 46b137dbf20b406f673242e2def87451a2438ba7 Mon Sep 17 00:00:00 2001 From: nsemets Date: Wed, 3 Dec 2025 16:22:00 +0200 Subject: [PATCH 36/76] [ENG-9683] Preprint Detail: Authors do not have sufficient size or spacing. #785 - Ticket: [ENG-9683] - Feature flag: n/a ## Summary of Changes 1. Fixed contributors size. --- .../contributors-list/contributors-list.component.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/app/shared/components/contributors-list/contributors-list.component.html b/src/app/shared/components/contributors-list/contributors-list.component.html index a3f4ae9e8..8c8dcc7d5 100644 --- a/src/app/shared/components/contributors-list/contributors-list.component.html +++ b/src/app/shared/components/contributors-list/contributors-list.component.html @@ -12,7 +12,11 @@ @if (readonly() || contributor.isUnregisteredContributor || !contributor.id || contributor.deactivated) { {{ contributor.fullName }}{{ $last ? '' : ',' }} } @else { - + {{ contributor.fullName }}{{ $last ? '' : ',' }} } From 39e4d91bf3864bc2942bae20e981f07b1aff1a92 Mon Sep 17 00:00:00 2001 From: mkovalua Date: Wed, 3 Dec 2025 16:24:24 +0200 Subject: [PATCH 37/76] [ENG-9618] handle no affiliated user in admin table panel #801 - Ticket: https://openscience.atlassian.net/browse/ENG-9618 - Feature flag: n/a ## Purpose Dashboard shows blank for contributors column in projects tab. When clicked on the project guid, project is affiliated to institution and admin user is shown ## Summary of Changes It looks the affiliation process goes ok for new records https://openscience.atlassian.net/browse/ENG-9618?focusedCommentId=94164 , add info icon message for records with no affiliation --- .../admin-table/admin-table.component.html | 53 +++++++++++-------- .../admin-table/admin-table.component.ts | 2 + src/assets/i18n/en.json | 3 +- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/app/features/admin-institutions/components/admin-table/admin-table.component.html b/src/app/features/admin-institutions/components/admin-table/admin-table.component.html index 09457ede0..a6f3a4dfb 100644 --- a/src/app/features/admin-institutions/components/admin-table/admin-table.component.html +++ b/src/app/features/admin-institutions/components/admin-table/admin-table.component.html @@ -122,30 +122,39 @@ } @else if (col.isLink && col.isArray && isLinkArray(currentColumnField)) {
- @for (link of currentColumnField; track $index) { + @if (currentColumnField.length === 0 && col.field === 'creator') {
- - - {{ link.text + ($last ? '' : ',') }} - - @if (col.showIcon) { - - } +
+ } @else { + @for (link of currentColumnField; track $index) { +
+ + + {{ link.text + ($last ? '' : ',') }} + + @if (col.showIcon) { + + } +
+ } }
} @else { diff --git a/src/app/features/admin-institutions/components/admin-table/admin-table.component.ts b/src/app/features/admin-institutions/components/admin-table/admin-table.component.ts index 68d79dbb5..bd35734e2 100644 --- a/src/app/features/admin-institutions/components/admin-table/admin-table.component.ts +++ b/src/app/features/admin-institutions/components/admin-table/admin-table.component.ts @@ -20,6 +20,7 @@ import { TableIconClickEvent, } from '@osf/features/admin-institutions/models'; import { CustomPaginatorComponent } from '@osf/shared/components/custom-paginator/custom-paginator.component'; +import { InfoIconComponent } from '@osf/shared/components/info-icon/info-icon.component'; import { StopPropagationDirective } from '@osf/shared/directives/stop-propagation.directive'; import { PaginationLinksModel } from '@osf/shared/models/pagination-links.model'; import { SearchFilters } from '@osf/shared/models/search-filters.model'; @@ -43,6 +44,7 @@ import { DownloadType } from '../../enums'; StopPropagationDirective, DatePipe, NgClass, + InfoIconComponent, ], templateUrl: './admin-table.component.html', styleUrl: './admin-table.component.scss', diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 57c553cb8..ba89a6abf 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -2884,7 +2884,8 @@ "orcid": "ORCID", "noData": "No users found", "messageSent": "Message has been sent.", - "requestSent": "Request has been sent." + "requestSent": "Request has been sent.", + "notBibliographicOrAffiliatedUserText": "The user is not bibliographic or no longer affiliated with the institution" }, "projects": { "title": "Title", From 0bcd287d5b36c7e752248fb75b24d2e0acc1e50c Mon Sep 17 00:00:00 2001 From: nsemets Date: Wed, 3 Dec 2025 16:26:08 +0200 Subject: [PATCH 38/76] [ENG-9682] Preprint Detail: Download button does not have an accessible name #783 - Ticket: [ENG-9682] - Feature flag: n/a ## Purpose 1. Added arial label for download button. --- .../share-and-download/share-and-download.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.html b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.html index ad4b90756..303fcec3a 100644 --- a/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.html +++ b/src/app/features/preprints/components/preprint-details/share-and-download/share-and-download.component.html @@ -7,7 +7,7 @@ " tooltipPosition="bottom" (onClick)="download()" - [attr.aria-label]=" + [ariaLabel]=" 'preprints.details.share.downloadPreprint' | translate: { documentType: preprintProvider()?.preprintWord } " /> From fa8736d5b5d2ad3ed4b480da839b0a13d1101a0e Mon Sep 17 00:00:00 2001 From: nsemets Date: Wed, 3 Dec 2025 16:28:52 +0200 Subject: [PATCH 39/76] [ENG-9082] CAS Register: Form elements must have labels #786 - Ticket: [ENG-9082] - Feature flag: n/a ## Summary of Changes 1. Fixed forms labels and ids. --- .../reset-password.component.html | 4 ++-- .../auth/pages/sign-up/sign-up.component.html | 4 ++-- .../project-metadata-step.component.html | 2 +- .../add-component-dialog.component.html | 2 +- .../custom-step/custom-step.component.html | 2 +- .../registries-metadata-step.component.html | 4 ++-- .../registries-tags.component.html | 4 ++-- .../change-password.component.html | 6 +++--- .../two-factor-auth.component.html | 2 +- .../education-form.component.html | 20 +++++++++---------- .../employment-form.component.html | 14 ++++++------- .../social-form/social-form.component.html | 10 ++++++++-- .../components/license/license.component.html | 2 +- .../subjects/subjects.component.html | 2 +- 14 files changed, 42 insertions(+), 36 deletions(-) diff --git a/src/app/features/auth/pages/reset-password/reset-password.component.html b/src/app/features/auth/pages/reset-password/reset-password.component.html index 0115e3a39..5ec7f3b96 100644 --- a/src/app/features/auth/pages/reset-password/reset-password.component.html +++ b/src/app/features/auth/pages/reset-password/reset-password.component.html @@ -6,7 +6,7 @@

{{ 'auth.resetPassword.title' | translate }}

{{ 'auth.resetPassword.title' | translate }}

{{ 'auth.signUp.title' | translate }}

{{ 'auth.common.password.title' | translate }} {{ 'auth.signUp.title' | translate }}

- +