diff --git a/apps/angular-app/README.md b/apps/angular-app/README.md new file mode 100644 index 00000000..5634cef8 --- /dev/null +++ b/apps/angular-app/README.md @@ -0,0 +1,11 @@ +# Angular App + +## Instructions + +## Recommendations + +## Challenges + +### Session * + +## How to \ No newline at end of file diff --git a/apps/angular-app/src/app/app.component.html b/apps/angular-app/src/app/app.component.html index 0680b43f..9721b000 100644 --- a/apps/angular-app/src/app/app.component.html +++ b/apps/angular-app/src/app/app.component.html @@ -1 +1,2 @@ + diff --git a/apps/angular-app/src/app/app.component.ts b/apps/angular-app/src/app/app.component.ts index 74c1d539..1d489374 100644 --- a/apps/angular-app/src/app/app.component.ts +++ b/apps/angular-app/src/app/app.component.ts @@ -1,9 +1,11 @@ import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; +import { HeaderComponent } from './components/header/header.component'; + @Component({ standalone: true, - imports: [RouterModule], + imports: [HeaderComponent, RouterModule], selector: 'mfee-project-root', templateUrl: './app.component.html', styleUrl: './app.component.scss' diff --git a/apps/angular-app/src/app/app.config.ts b/apps/angular-app/src/app/app.config.ts index 0cf96af2..17b41722 100644 --- a/apps/angular-app/src/app/app.config.ts +++ b/apps/angular-app/src/app/app.config.ts @@ -1,8 +1,9 @@ +import { provideHttpClient } from '@angular/common/http'; import { ApplicationConfig } from '@angular/core'; import { provideRouter } from '@angular/router'; import { appRoutes } from './app.routes'; export const appConfig: ApplicationConfig = { - providers: [provideRouter(appRoutes)] + providers: [provideRouter(appRoutes), provideHttpClient()] }; diff --git a/apps/angular-app/src/app/app.routes.ts b/apps/angular-app/src/app/app.routes.ts index 8762dfe2..75bb3733 100644 --- a/apps/angular-app/src/app/app.routes.ts +++ b/apps/angular-app/src/app/app.routes.ts @@ -1,3 +1,22 @@ import { Route } from '@angular/router'; -export const appRoutes: Route[] = []; +import { HomeComponent } from './views/home/home.component'; +import { PageNotFoundComponent } from './views/page-not-found/page-not-found.component'; +import { PostComponent } from './views/post/post.component'; + +export const appRoutes: Route[] = [ + { + path: '', + pathMatch: 'full', + component: HomeComponent + }, + { + path: 'post/:id', + component: PostComponent + }, + { + path: '**', + component: PageNotFoundComponent + } +]; + diff --git a/apps/angular-app/src/app/components/add-post/add-post.component.html b/apps/angular-app/src/app/components/add-post/add-post.component.html new file mode 100644 index 00000000..4bf91363 --- /dev/null +++ b/apps/angular-app/src/app/components/add-post/add-post.component.html @@ -0,0 +1,3 @@ + diff --git a/apps/angular-app/src/app/components/add-post/add-post.component.scss b/apps/angular-app/src/app/components/add-post/add-post.component.scss new file mode 100644 index 00000000..79728ece --- /dev/null +++ b/apps/angular-app/src/app/components/add-post/add-post.component.scss @@ -0,0 +1,16 @@ +.mfee-add-post-button { + position: fixed; + width: 3rem; + height: 3rem; + border-radius: 99px; + background-color: orange; + border: none; + right: 1rem; + top: 3rem; + box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1); + cursor: pointer; + color: white; + display: flex; + align-items: center; + justify-content: center; +} diff --git a/apps/angular-app/src/app/components/add-post/add-post.component.spec.ts b/apps/angular-app/src/app/components/add-post/add-post.component.spec.ts new file mode 100644 index 00000000..27a3f263 --- /dev/null +++ b/apps/angular-app/src/app/components/add-post/add-post.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { AddPostComponent } from './add-post.component'; + +describe('AddPostComponent', () => { + let component: AddPostComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AddPostComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(AddPostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/angular-app/src/app/components/add-post/add-post.component.ts b/apps/angular-app/src/app/components/add-post/add-post.component.ts new file mode 100644 index 00000000..601c67d4 --- /dev/null +++ b/apps/angular-app/src/app/components/add-post/add-post.component.ts @@ -0,0 +1,19 @@ +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Output } from '@angular/core'; + +@Component({ + selector: 'mfee-add-post', + standalone: true, + imports: [CommonModule], + templateUrl: './add-post.component.html', + styleUrl: './add-post.component.scss' +}) +export class AddPostComponent { + @Output() clickAction: EventEmitter = new EventEmitter(); + + constructor() {} + + public addPost(): void { + this.clickAction.emit(); + } +} diff --git a/apps/angular-app/src/app/components/categories/categories.component.html b/apps/angular-app/src/app/components/categories/categories.component.html new file mode 100644 index 00000000..ec7d7a0f --- /dev/null +++ b/apps/angular-app/src/app/components/categories/categories.component.html @@ -0,0 +1,8 @@ +
+
    +
  • + {{ category.name }} +
  • +
+
\ No newline at end of file diff --git a/apps/angular-app/src/app/components/categories/categories.component.scss b/apps/angular-app/src/app/components/categories/categories.component.scss new file mode 100644 index 00000000..1271c17a --- /dev/null +++ b/apps/angular-app/src/app/components/categories/categories.component.scss @@ -0,0 +1,47 @@ +.mfee-category-container { + display: flex; + justify-content: center; +} + +.mfee-category { + list-style: none; + display: flex; + padding: 0; + + @media (max-width: 767px) { + flex-direction: column; + width: 95%; + } + + &__item { + padding: 0.6rem 1.2rem; + border: 1px solid #ccc; + border-bottom: none; + cursor: pointer; + + &:last-of-type { + border-bottom: 1px solid #ccc; + } + + @media (min-width: 767px) { + border-right: none; + border-bottom: 1px solid #ccc; + + &:first-of-type { + border-top-left-radius: 0.2rem; + border-bottom-left-radius: 0.2rem; + } + + &:last-of-type { + border-right: 1px solid #ccc; + border-top-right-radius: 0.2rem; + border-bottom-right-radius: 0.2rem; + } + } + + &--active, + &:hover { + background-color: #ccc; + } + } +} diff --git a/apps/angular-app/src/app/components/categories/categories.component.spec.ts b/apps/angular-app/src/app/components/categories/categories.component.spec.ts new file mode 100644 index 00000000..ccd269ad --- /dev/null +++ b/apps/angular-app/src/app/components/categories/categories.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CategoriesComponent } from './categories.component'; + +describe('CategoriesComponent', () => { + let component: CategoriesComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CategoriesComponent] + }).compileComponents(); + + fixture = TestBed.createComponent(CategoriesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/angular-app/src/app/components/categories/categories.component.ts b/apps/angular-app/src/app/components/categories/categories.component.ts new file mode 100644 index 00000000..a93c03bb --- /dev/null +++ b/apps/angular-app/src/app/components/categories/categories.component.ts @@ -0,0 +1,21 @@ +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; + +import { Category } from '../../models/Category'; + +@Component({ + selector: 'mfee-categories', + standalone: true, + imports: [CommonModule], + templateUrl: './categories.component.html', + styleUrl: './categories.component.scss' +}) +export class CategoriesComponent { + @Input() selectedCategory: string; + @Input() categories: Array; + @Output() categoryChange: EventEmitter = new EventEmitter(); + + setCategory(categoryId: string) { + this.categoryChange.emit(categoryId) + } +} diff --git a/apps/angular-app/src/app/components/header/header.component.html b/apps/angular-app/src/app/components/header/header.component.html new file mode 100644 index 00000000..9c004bb4 --- /dev/null +++ b/apps/angular-app/src/app/components/header/header.component.html @@ -0,0 +1,8 @@ +
+
+ [ + + ] +
+

Discovering the World

+
\ No newline at end of file diff --git a/apps/angular-app/src/app/components/header/header.component.scss b/apps/angular-app/src/app/components/header/header.component.scss new file mode 100644 index 00000000..b5ef0ad0 --- /dev/null +++ b/apps/angular-app/src/app/components/header/header.component.scss @@ -0,0 +1,22 @@ +.mfee-header { + display: flex; + flex-direction: column; + align-items: center; + padding: 1.5rem 0; + text-align: center; + + &__slogan { + color: orange; + font-size: 0.8rem; + } + + &__slogan-icon { + font-size: 2em; + padding: 0 0.5rem; + } + + &__title { + margin: 0; + font-size: 2.5rem; + } +} diff --git a/apps/angular-app/src/app/components/header/header.component.spec.ts b/apps/angular-app/src/app/components/header/header.component.spec.ts new file mode 100644 index 00000000..3096a746 --- /dev/null +++ b/apps/angular-app/src/app/components/header/header.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HeaderComponent } from './header.component'; + +describe('HeaderComponent', () => { + let component: HeaderComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [HeaderComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(HeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/angular-app/src/app/components/header/header.component.ts b/apps/angular-app/src/app/components/header/header.component.ts new file mode 100644 index 00000000..6d240ee4 --- /dev/null +++ b/apps/angular-app/src/app/components/header/header.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: 'mfee-header', + standalone: true, + imports: [CommonModule], + templateUrl: './header.component.html', + styleUrl: './header.component.scss', +}) +export class HeaderComponent {} diff --git a/apps/angular-app/src/app/components/post-grid/post-grid.component.html b/apps/angular-app/src/app/components/post-grid/post-grid.component.html new file mode 100644 index 00000000..26d5fbcb --- /dev/null +++ b/apps/angular-app/src/app/components/post-grid/post-grid.component.html @@ -0,0 +1,19 @@ +
+
+

{{ post.title }}

+ {{ post.comments.count }} Comments + forum +

{{ post.description }}

+ +
+
\ No newline at end of file diff --git a/apps/angular-app/src/app/components/post-grid/post-grid.component.scss b/apps/angular-app/src/app/components/post-grid/post-grid.component.scss new file mode 100644 index 00000000..abfd2011 --- /dev/null +++ b/apps/angular-app/src/app/components/post-grid/post-grid.component.scss @@ -0,0 +1,95 @@ +@import '../../shared/variables'; + +.mfee-grid-container { + display: flex; + flex-wrap: wrap; +} + +.mfee-grid-post { + $post: &; + + background-position: center; + background-repeat: no-repeat; + background-size: cover; + box-sizing: border-box; + color: #fff; + display: flex; + flex-direction: column; + justify-content: flex-end; + min-height: 40vh; + padding: 1.2rem; + text-decoration: none; + background-color: $color-gray; + + @media (min-width: 768px) { + width: 50%; + } + + &:hover { + #{$post}__actions { + display: flex; + } + } + + &__title { + } + + &__comments { + font-weight: lighter; + font-style: italic; + align-items: flex-end; + display: flex; + + i { + font-size: inherit; + margin-left: 0.4rem; + } + } + + &__description { + font-size: 0.875rem; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + + @media (min-width: 768px) { + width: 75%; + } + } + + &__footer { + display: flex; + justify-content: space-between; + min-height: 1.75rem; + } + + &__actions { + list-style: none; + padding: 0; + margin: 0; + display: none; + } + + &__actions-item { + width: 1.75rem; + height: 1.75rem; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + cursor: pointer; + color: #ccc; + } + } + + &__tag { + font-size: 0.875rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 3px; + align-items: flex-end; + display: flex; + } +} diff --git a/apps/angular-app/src/app/components/post-grid/post-grid.component.spec.ts b/apps/angular-app/src/app/components/post-grid/post-grid.component.spec.ts new file mode 100644 index 00000000..05eeac02 --- /dev/null +++ b/apps/angular-app/src/app/components/post-grid/post-grid.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { PostGridComponent } from './post-grid.component'; + +describe('PostGridComponent', () => { + let component: PostGridComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PostGridComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(PostGridComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/angular-app/src/app/components/post-grid/post-grid.component.ts b/apps/angular-app/src/app/components/post-grid/post-grid.component.ts new file mode 100644 index 00000000..2dc2517f --- /dev/null +++ b/apps/angular-app/src/app/components/post-grid/post-grid.component.ts @@ -0,0 +1,37 @@ +import { CommonModule } from '@angular/common'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Router } from '@angular/router'; + +import { Post } from '../../models/Post'; + +@Component({ + selector: 'mfee-post-grid', + standalone: true, + imports: [CommonModule], + templateUrl: './post-grid.component.html', + styleUrl: './post-grid.component.scss' +}) +export class PostGridComponent { + @Input() posts: Array; + @Output() deletePost: EventEmitter = new EventEmitter(); + @Output() editPost: EventEmitter = new EventEmitter(); + + constructor(private router: Router) {} + + public onClick(event, postId: string): void { + if ( + !event.target.className.includes('mfee-grid-post__actions-item') && + !event.target.parentNode.className.includes('mfee-grid-post__actions-item') + ) { + this.router.navigate(['/post', postId]); + } + } + + public onEditPost(postId: string): void { + this.editPost.emit(postId); + } + + public onDeletePost(postId: string): void { + this.deletePost.emit(postId); + } +} diff --git a/apps/angular-app/src/app/components/post-modal/post-modal.component.html b/apps/angular-app/src/app/components/post-modal/post-modal.component.html new file mode 100644 index 00000000..f73e8bc4 --- /dev/null +++ b/apps/angular-app/src/app/components/post-modal/post-modal.component.html @@ -0,0 +1,61 @@ +
+
+

{{ postModalService.title$ | async }}

+ +
+ + + + + + + + + +
+
+
\ No newline at end of file diff --git a/apps/angular-app/src/app/components/post-modal/post-modal.component.scss b/apps/angular-app/src/app/components/post-modal/post-modal.component.scss new file mode 100644 index 00000000..2456c850 --- /dev/null +++ b/apps/angular-app/src/app/components/post-modal/post-modal.component.scss @@ -0,0 +1,38 @@ +@import '../../shared/variables'; + +.mfee-modal { + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + + &__content { + background-color: white; + width: 90vw; + padding: 1.5rem; + + @media (min-width: 768px) { + width: 60vw; + } + } + + &__form { + display: flex; + flex-direction: column; + } + + &__title { + margin-top: 0; + text-align: center; + } + + &__footer { + display: flex; + justify-content: flex-end; + } +} diff --git a/apps/angular-app/src/app/components/post-modal/post-modal.component.spec.ts b/apps/angular-app/src/app/components/post-modal/post-modal.component.spec.ts new file mode 100644 index 00000000..30308527 --- /dev/null +++ b/apps/angular-app/src/app/components/post-modal/post-modal.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { PostModalComponent } from './post-modal.component'; + +describe('PostModalComponent', () => { + let component: PostModalComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PostModalComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(PostModalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/angular-app/src/app/components/post-modal/post-modal.component.ts b/apps/angular-app/src/app/components/post-modal/post-modal.component.ts new file mode 100644 index 00000000..e5065fce --- /dev/null +++ b/apps/angular-app/src/app/components/post-modal/post-modal.component.ts @@ -0,0 +1,97 @@ +import { CommonModule } from '@angular/common'; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; + +import { Observable, ReplaySubject, firstValueFrom, switchMap, take, takeUntil, tap } from 'rxjs'; +import { Category } from '../../models/Category'; +import { CategoryService } from '../../services/category/category.service'; +import { PostService } from '../../services/post/post.service'; +import { urlValidator } from '../../shared/url-validator'; +import { PostModalService } from './post-modal.service'; + +@Component({ + selector: 'mfee-post-modal', + standalone: true, + imports: [CommonModule, ReactiveFormsModule], + templateUrl: './post-modal.component.html', + styleUrl: './post-modal.component.scss' +}) +export class PostModalComponent implements OnInit, OnDestroy { + private destroyed$: ReplaySubject = new ReplaySubject(1); + public categories$: Observable; + + postForm = this.fb.group({ + title: ['', Validators.required], + description: ['', Validators.required], + category: ['', Validators.required], + image: ['', [Validators.required, urlValidator]] + }); + + constructor( + public postModalService: PostModalService, + private categoryService: CategoryService, + private postService: PostService, + private fb: FormBuilder + ) {} + + ngOnInit(): void { + this.categories$ = this.categoryService.getCategories(false); + + this.postModalService.post$.pipe(takeUntil(this.destroyed$)).subscribe((post) => { + this.postForm.setValue({ + title: post?.title ?? '', + description: post?.description ?? '', + category: post?.category.id ?? '', + image: post?.image ?? '' + }); + }); + } + + ngOnDestroy() { + this.destroyed$.next(true); + this.destroyed$.complete(); + } + + close(): void { + this.postModalService.close(); + } + + async onSubmit(): Promise { + await firstValueFrom( + this.postModalService.post$.pipe( + take(1), + switchMap((post) => { + const payload = { + ...this.postForm.value + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + + return this.postService.upsertPost(post ? { ...post, ...payload } : payload); + }), + tap(() => { + console.log('Update posts'); + // TODO : Update posts + }) + ) + ); + + this.postForm.reset(); + this.close(); + } + + get title() { + return this.postForm.get('title'); + } + + get description() { + return this.postForm.get('description'); + } + + get category() { + return this.postForm.get('category'); + } + + get image() { + return this.postForm.get('image'); + } +} diff --git a/apps/angular-app/src/app/components/post-modal/post-modal.service.spec.ts b/apps/angular-app/src/app/components/post-modal/post-modal.service.spec.ts new file mode 100644 index 00000000..2ad8a208 --- /dev/null +++ b/apps/angular-app/src/app/components/post-modal/post-modal.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { PostModalService } from './post-modal.service'; + +describe('PostModalService', () => { + let service: PostModalService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(PostModalService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/apps/angular-app/src/app/components/post-modal/post-modal.service.ts b/apps/angular-app/src/app/components/post-modal/post-modal.service.ts new file mode 100644 index 00000000..a1515a83 --- /dev/null +++ b/apps/angular-app/src/app/components/post-modal/post-modal.service.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; + +import { Post } from '../../models/Post'; + +@Injectable({ + providedIn: 'root' +}) +export class PostModalService { + private display: BehaviorSubject = new BehaviorSubject(false); + public display$: Observable = this.display.asObservable(); + + private title: BehaviorSubject = new BehaviorSubject(''); + public title$: Observable = this.title.asObservable(); + + private post: BehaviorSubject = new BehaviorSubject(null); + public post$: Observable = this.post.asObservable(); + + constructor() {} + + open(): void { + this.display.next(true); + } + + close(): void { + this.display.next(false); + } + + setInfo(title: string, post: Post = null): void { + this.title.next(title); + this.post.next(post); + } +} diff --git a/apps/angular-app/src/app/models/Category.ts b/apps/angular-app/src/app/models/Category.ts new file mode 100644 index 00000000..0df829f8 --- /dev/null +++ b/apps/angular-app/src/app/models/Category.ts @@ -0,0 +1,9 @@ +export type Category = { + id: string; + name: string; +}; + +export type GetCategoryResponse = { + _id: string; + name: string; +}; diff --git a/apps/angular-app/src/app/models/Comment.ts b/apps/angular-app/src/app/models/Comment.ts new file mode 100644 index 00000000..3f74e4a2 --- /dev/null +++ b/apps/angular-app/src/app/models/Comment.ts @@ -0,0 +1,11 @@ +export type Comment = { + id: string; + author: string; + content: string; +}; + +export type CommentResponse = { + _id: string; + author: string; + content: string; +}; diff --git a/apps/angular-app/src/app/models/Post.ts b/apps/angular-app/src/app/models/Post.ts new file mode 100644 index 00000000..94d33653 --- /dev/null +++ b/apps/angular-app/src/app/models/Post.ts @@ -0,0 +1,32 @@ +import { Category, GetCategoryResponse } from './Category'; +import { Comment, CommentResponse } from './Comment'; + +export type Post = { + id: string; + title: string; + image: string; + description: string; + category: Category; + comments: { + count: number; + data?: Array; + }; +}; + +export type GetPostsResponse = { + _id: string; + title: string; + image: string; + description: string; + category: GetCategoryResponse; + comments: Array; +}; + +export type GetPostResponse = { + _id: string; + title: string; + image: string; + description: string; + category: GetCategoryResponse; + comments: Array; +}; diff --git a/apps/angular-app/src/app/services/category/category.service.spec.ts b/apps/angular-app/src/app/services/category/category.service.spec.ts new file mode 100644 index 00000000..56585079 --- /dev/null +++ b/apps/angular-app/src/app/services/category/category.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { CategoryService } from './category.service'; + +describe('CategoryService', () => { + let service: CategoryService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(CategoryService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/apps/angular-app/src/app/services/category/category.service.ts b/apps/angular-app/src/app/services/category/category.service.ts new file mode 100644 index 00000000..cf55e192 --- /dev/null +++ b/apps/angular-app/src/app/services/category/category.service.ts @@ -0,0 +1,33 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable, map, take } from 'rxjs'; + +import { Category, GetCategoryResponse } from '../../models/Category'; + +@Injectable({ + providedIn: 'root' +}) +export class CategoryService { + private apiUrl = 'http://localhost:4200/api/categories'; + + constructor(private http: HttpClient) {} + + public getCategories(includeAll: boolean = true): Observable { + return this.http.get(this.apiUrl).pipe( + take(1), + map((categories) => { + const newCategories = categories.map((c) => ({ id: c._id, name: c.name })); + + return includeAll + ? [ + { + id: 'all', + name: 'All' + }, + ...newCategories + ] + : newCategories; + }) + ); + } +} diff --git a/apps/angular-app/src/app/services/post/post.service.spec.ts b/apps/angular-app/src/app/services/post/post.service.spec.ts new file mode 100644 index 00000000..913642b8 --- /dev/null +++ b/apps/angular-app/src/app/services/post/post.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { PostService } from './post.service'; + +describe('PostService', () => { + let service: PostService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(PostService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/apps/angular-app/src/app/services/post/post.service.ts b/apps/angular-app/src/app/services/post/post.service.ts new file mode 100644 index 00000000..b53575ae --- /dev/null +++ b/apps/angular-app/src/app/services/post/post.service.ts @@ -0,0 +1,118 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable, map, take } from 'rxjs'; + +import { Comment, CommentResponse } from '../../models/Comment'; +import { GetPostResponse, GetPostsResponse, Post } from '../../models/Post'; + +@Injectable({ + providedIn: 'root' +}) +export class PostService { + private postsApiUrl = 'http://localhost:4200/api/posts'; + + constructor(private http: HttpClient) {} + + public getPosts(selectedCategory: string): Observable> { + const url = selectedCategory === 'all' ? this.postsApiUrl : `${this.postsApiUrl}/category/${selectedCategory}`; + + return this.http.get(url).pipe( + take(1), + map((posts) => + posts.map((p) => ({ + id: p._id, + title: p.title, + image: p.image, + description: p.description, + category: { + id: p.category._id, + name: p.category.name + }, + comments: { + count: p.comments.length + } + })) + ) + ); + } + + public getPost(postId: string): Observable { + return this.http.get(`${this.postsApiUrl}/${postId}`).pipe( + take(1), + map((post) => ({ + id: post._id, + title: post.title, + image: post.image, + description: post.description, + category: { + id: post.category._id, + name: post.category.name + }, + comments: { + count: post.comments.length, + data: post.comments.map((c) => ({ + id: c._id, + author: c.author, + content: c.content + })) + } + })) + ); + } + + public upsertPost(post: Partial): Observable { + const format = (post: GetPostResponse) => ({ + id: post._id, + title: post.title, + image: post.image, + description: post.description, + category: { + id: post.category._id, + name: post.category.name + }, + comments: { + count: post.comments.length, + data: post.comments.map((c) => ({ + id: c._id, + author: c.author, + content: c.content + })) + } + }); + + if (post.id) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { id, comments, ...payload } = post; + // Edit post + return this.http.patch(`${this.postsApiUrl}/${post.id}`, payload).pipe( + take(1), + map((post) => format(post)) + ); + } + + // Create post + return this.http.post(this.postsApiUrl, post).pipe( + take(1), + map((post) => format(post)) + ); + } + + public deletePost(postId: string): Observable { + return this.http.delete(`${this.postsApiUrl}/${postId}`).pipe(take(1)); + } + + public addComment(postId: string, comment: string): Observable { + const payload = { + author: 'Anonymous', + content: comment + }; + return this.http.post(`${this.postsApiUrl}/${postId}/comments`, payload).pipe( + take(1), + map((comment) => ({ + id: comment._id, + author: comment.author, + content: comment.content + })) + ); + } +} diff --git a/apps/angular-app/src/app/shared/url-validator.ts b/apps/angular-app/src/app/shared/url-validator.ts new file mode 100644 index 00000000..a9559e90 --- /dev/null +++ b/apps/angular-app/src/app/shared/url-validator.ts @@ -0,0 +1,10 @@ +import { AbstractControl } from '@angular/forms'; + +export function urlValidator(control: AbstractControl): { [key: string]: boolean } | null { + const url = control.value; + + const URL_REGEXP = + /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/; + + return URL_REGEXP.test(url) ? null : { invalidUrl: true }; +} diff --git a/apps/angular-app/src/app/shared/variables.scss b/apps/angular-app/src/app/shared/variables.scss index e69de29b..02ef634a 100644 --- a/apps/angular-app/src/app/shared/variables.scss +++ b/apps/angular-app/src/app/shared/variables.scss @@ -0,0 +1,3 @@ +$color-blue: #3f3fc9; +$color-blue-light: #5b5bc0; +$color-gray: #ccc; diff --git a/apps/angular-app/src/app/views/home/home.component.html b/apps/angular-app/src/app/views/home/home.component.html new file mode 100644 index 00000000..417747b1 --- /dev/null +++ b/apps/angular-app/src/app/views/home/home.component.html @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/apps/angular-app/src/app/components/.gitkeep b/apps/angular-app/src/app/views/home/home.component.scss similarity index 100% rename from apps/angular-app/src/app/components/.gitkeep rename to apps/angular-app/src/app/views/home/home.component.scss diff --git a/apps/angular-app/src/app/views/home/home.component.spec.ts b/apps/angular-app/src/app/views/home/home.component.spec.ts new file mode 100644 index 00000000..5dd05d2c --- /dev/null +++ b/apps/angular-app/src/app/views/home/home.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HomeComponent } from './home.component'; + +describe('HomeComponent', () => { + let component: HomeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [HomeComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/angular-app/src/app/views/home/home.component.ts b/apps/angular-app/src/app/views/home/home.component.ts new file mode 100644 index 00000000..1786d9a6 --- /dev/null +++ b/apps/angular-app/src/app/views/home/home.component.ts @@ -0,0 +1,69 @@ +import { CommonModule } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; + +import { BehaviorSubject, Observable, catchError, of, switchMap, take, tap, withLatestFrom } from 'rxjs'; +import { AddPostComponent } from '../../components/add-post/add-post.component'; +import { CategoriesComponent } from '../../components/categories/categories.component'; +import { PostGridComponent } from '../../components/post-grid/post-grid.component'; +import { PostModalComponent } from '../../components/post-modal/post-modal.component'; +import { PostModalService } from '../../components/post-modal/post-modal.service'; +import { Category } from '../../models/Category'; +import { Post } from '../../models/Post'; +import { CategoryService } from '../../services/category/category.service'; +import { PostService } from '../../services/post/post.service'; + +@Component({ + selector: 'mfee-home', + standalone: true, + imports: [CommonModule, AddPostComponent, CategoriesComponent, PostGridComponent, PostModalComponent], + templateUrl: './home.component.html', + styleUrl: './home.component.scss' +}) +export class HomeComponent implements OnInit { + public categories$: Observable; + public posts$: Observable; + + private selectedCategory = new BehaviorSubject('all'); + public selectedCategory$ = this.selectedCategory.asObservable(); + + constructor(private postService: PostService, private categoryService: CategoryService, private postModalService: PostModalService) {} + + ngOnInit(): void { + this.categories$ = this.categoryService.getCategories(); + + this.posts$ = this.selectedCategory$.pipe(switchMap((selectedCategory) => this.postService.getPosts(selectedCategory))); + } + + public onCategoryChange(categoryId: string): void { + this.selectedCategory.next(categoryId); + } + + public onAddPost(): void { + this.postModalService.setInfo('Create Post'); + this.postModalService.open(); + } + + public onEditPost(postId: string): void { + this.postService.getPost(postId).subscribe((post) => { + this.postModalService.setInfo('Edit Post', post); + this.postModalService.open(); + }); + } + + public onDeletePost(postId: string): void { + this.postService + .deletePost(postId) + .pipe( + take(1), + withLatestFrom(this.selectedCategory$), + tap(([, selectedCategory]) => { + this.onCategoryChange(selectedCategory); + }), + catchError(() => { + // TODO : Show an error message to the user + return of(null); + }) + ) + .subscribe(); + } +} diff --git a/apps/angular-app/src/app/views/page-not-found/page-not-found.component.html b/apps/angular-app/src/app/views/page-not-found/page-not-found.component.html new file mode 100644 index 00000000..35fc2c27 --- /dev/null +++ b/apps/angular-app/src/app/views/page-not-found/page-not-found.component.html @@ -0,0 +1 @@ +

Ups! Something went wrong, we couldn't find this page.

diff --git a/apps/angular-app/src/app/models/.gitkeep b/apps/angular-app/src/app/views/page-not-found/page-not-found.component.scss similarity index 100% rename from apps/angular-app/src/app/models/.gitkeep rename to apps/angular-app/src/app/views/page-not-found/page-not-found.component.scss diff --git a/apps/angular-app/src/app/views/page-not-found/page-not-found.component.spec.ts b/apps/angular-app/src/app/views/page-not-found/page-not-found.component.spec.ts new file mode 100644 index 00000000..0ec6519d --- /dev/null +++ b/apps/angular-app/src/app/views/page-not-found/page-not-found.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { PageNotFoundComponent } from './page-not-found.component'; + +describe('PageNotFoundComponent', () => { + let component: PageNotFoundComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PageNotFoundComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(PageNotFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/angular-app/src/app/views/page-not-found/page-not-found.component.ts b/apps/angular-app/src/app/views/page-not-found/page-not-found.component.ts new file mode 100644 index 00000000..bba9d8ff --- /dev/null +++ b/apps/angular-app/src/app/views/page-not-found/page-not-found.component.ts @@ -0,0 +1,11 @@ +import { Component } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +@Component({ + selector: 'mfee-page-not-found', + standalone: true, + imports: [CommonModule], + templateUrl: './page-not-found.component.html', + styleUrl: './page-not-found.component.scss', +}) +export class PageNotFoundComponent {} diff --git a/apps/angular-app/src/app/views/post/post.component.html b/apps/angular-app/src/app/views/post/post.component.html new file mode 100644 index 00000000..736ce009 --- /dev/null +++ b/apps/angular-app/src/app/views/post/post.component.html @@ -0,0 +1,43 @@ +
+
+ +

{{ post.title }}

+
+
+
+

{{ post.description }}

+ +
+

Comments

+ +
+ + + +
+
    +
  • +
    + person + {{ comment.author }} +
    +

    {{ comment.content }}

    +
  • +
+
+
+
+
\ No newline at end of file diff --git a/apps/angular-app/src/app/views/post/post.component.scss b/apps/angular-app/src/app/views/post/post.component.scss new file mode 100644 index 00000000..ddef47cb --- /dev/null +++ b/apps/angular-app/src/app/views/post/post.component.scss @@ -0,0 +1,107 @@ +.mfee-post { + &__header { + display: flex; + color: white; + min-height: 50vh; + position: relative; + align-items: center; + justify-content: center; + background-position: center top; + background-size: cover; + background-repeat: no-repeat; + } + + &__actions { + position: absolute; + top: 0; + left: 0; + padding: 10px 0 0 10px; + } + + &__action { + display: flex; + align-items: center; + text-decoration: none; + color: white; + + &:hover { + color: #ccc; + } + } + + &__title { + font-size: 2.25rem; + text-align: center; + } + + &__content-wrapper { + background-color: #ececec; + display: flex; + } + + &__content { + padding: 0 1rem; + margin: 0 auto; + flex-grow: 1; + + @media (min-width: 576px) { + max-width: 540px; + } + + @media (min-width: 768px) { + max-width: 720px; + } + + @media (min-width: 992px) { + max-width: 960px; + } + + @media (min-width: 1200px) { + max-width: 1140px; + } + } + + &__description { + white-space: pre-line; + } + + &-comments { + margin: 0 auto; + + @media (min-width: 768px) { + max-width: 480px; + } + } + + &__comment-wrapper { + padding: 0; + list-style: none; + } + + &__comment { + background-color: white; + box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.1); + margin-bottom: 8px; + padding: 8px; + + &-name { + display: flex; + align-items: center; + font-weight: bold; + margin-bottom: 8px; + } + + &-avatar { + background-color: black; + color: white; + border-radius: 99px; + padding: 2px; + margin-right: 8px; + } + + &-description { + margin: 0; + margin-left: 36px; + } + } +} diff --git a/apps/angular-app/src/app/views/post/post.component.spec.ts b/apps/angular-app/src/app/views/post/post.component.spec.ts new file mode 100644 index 00000000..15c9dacd --- /dev/null +++ b/apps/angular-app/src/app/views/post/post.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { PostComponent } from './post.component'; + +describe('PostComponent', () => { + let component: PostComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PostComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(PostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/angular-app/src/app/views/post/post.component.ts b/apps/angular-app/src/app/views/post/post.component.ts new file mode 100644 index 00000000..67622978 --- /dev/null +++ b/apps/angular-app/src/app/views/post/post.component.ts @@ -0,0 +1,41 @@ +import { CommonModule } from '@angular/common'; +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms'; +import { ActivatedRoute, RouterLink } from '@angular/router'; +import { Observable, switchMap } from 'rxjs'; + +import { Post } from '../../models/Post'; +import { PostService } from '../../services/post/post.service'; + +@Component({ + selector: 'mfee-post', + standalone: true, + imports: [CommonModule, ReactiveFormsModule, RouterLink], + templateUrl: './post.component.html', + styleUrl: './post.component.scss' +}) +export class PostComponent implements OnInit { + public post$: Observable; + + commentForm = this.fb.group({ + comment: ['', Validators.required] + }); + + constructor(private route: ActivatedRoute, private fb: FormBuilder, private postService: PostService) {} + + ngOnInit(): void { + this.post$ = this.route.params.pipe(switchMap(({ id }) => this.postService.getPost(id))); + } + + onSubmit(postId: string) { + this.postService.addComment(postId, this.commentForm.value.comment).subscribe((comment) => { + // TODO : Add comment to UI + console.log(comment); + }); + this.commentForm.reset(); + } + + get comment() { + return this.commentForm.get('comment'); + } +} diff --git a/apps/angular-app/src/index.html b/apps/angular-app/src/index.html index da0603dd..f7be7978 100644 --- a/apps/angular-app/src/index.html +++ b/apps/angular-app/src/index.html @@ -7,6 +7,7 @@ + diff --git a/apps/angular-app/src/styles.scss b/apps/angular-app/src/styles.scss index cf488252..8884a5ec 100644 --- a/apps/angular-app/src/styles.scss +++ b/apps/angular-app/src/styles.scss @@ -4,3 +4,83 @@ body { font-family: 'Open Sans', sans-serif; } + +.mfee-button { + border: none; + background: $color-blue; + color: white; + font-size: 0.875rem; + padding: 0.5rem 1.5rem; + border-radius: 2px; + cursor: pointer; + + &:hover { + background-color: $color-blue-light; + } + + &--link { + background-color: transparent; + color: black; + + &:hover { + text-decoration: underline; + background-color: transparent; + } + } + + &:disabled { + color: $color-gray; + cursor: not-allowed; + } +} + +.mfee-form-control { + display: flex; + flex-direction: column; + margin-bottom: 1rem; + font-size: 0.75rem; + position: relative; + + &__input { + border: none; + border-bottom: 1px solid $color-gray; + outline: none !important; + margin-top: 2px; + padding: 0.25rem 0; + resize: none; + background-color: transparent; + + &:active, + &:focus { + border-bottom-color: $color-blue; + } + + &.ng-dirty.ng-invalid, + &.ng-touched.ng-invalid { + border-bottom-color: red; + } + } + + &__error { + position: absolute; + bottom: 0; + right: 0; + color: red; + } +} + +::-webkit-scrollbar { + width: 6px; + background-color: #f5f5f5; +} + +::-webkit-scrollbar-thumb { + border-radius: 10px; + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); + background-color: #555; +} + +::-webkit-scrollbar-track { + -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); + background-color: #f5f5f5; +} diff --git a/apps/api/.env b/apps/api/.env index 83114260..ecaf5a7c 100644 --- a/apps/api/.env +++ b/apps/api/.env @@ -1 +1,3 @@ +ACCESS_TOKEN_SECRET=[YOUR_ACCESS_TOKEN_SECRET_HERE] +REFRESH_TOKEN_SECRET=[YOUR_REFRESH_TOKEN_SECRET_HERE] MONGO_URL=[YOUR_MONGO_CONNECTION_STRING_HERE] \ No newline at end of file diff --git a/apps/api/README.md b/apps/api/README.md new file mode 100644 index 00000000..6066cc0e --- /dev/null +++ b/apps/api/README.md @@ -0,0 +1,55 @@ +# Node.js Express API + +## Instructions + +1. Fork [mfee-project repository](https://github.com/gus-code/mfee-project) +2. Make sure to fork all the branches (You need to unselect the checkbox of fork only main branch) +3. You can clone the repository to your local or you can create a codespace in github (We suggest to use your local because we will use postman to test it) +4. Checkout to branch `node/template` this is the starter boilerplate + - `git checkout node/template` +5. From here you can create your own branch (We suggest to name it `node/`) + - `git checkout -b node/` +6. We will be working with some examples during the sessions in this same repository, once we finish with the session you can get the example from the branch `node/session-*` and merge it to your branch (We will provide the branch after each session) + - `git merge node/session-*` +7. Each session branch will have the challenges to accomplish and the expected results. You can validate if your endpoint is correct by [running the postman collection](#run-postman-collection) +8. After finish the challenges you need to create a pull request to the base repository, you will have a branch with your EID (`node/`). If you don't know how to do it you can check this [quick guide](#create-pull-request) + +## Recommendations + +- Use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) +- Before make your commit format your files with prettier + +## Challenges + +### Session * + +## How to + +### Run postman collection + +1. Download postman collection from `apps/api/src/assets/mfee-node.postman_collection.json` +2. Import collection to postman +3. Configure url variable if needed. We set the default value to `http://localhost:3000` if you change the port you will need to update this, after updating the value you need to save it with `Ctrl + S` +![Postman - Variables](assets/postman-variables.png) +4. Once everything is in place you go to the folder you want to run and click "Run" +![Postman - Open tests](assets/postman-open-tests.png) +5. The endpoints will be displayed and you just need to click on "Run MFEE - Node.js" +![Postman - Run tests](assets/postman-run-tests.png) +6. After this a report will be show and all the test should pass +![Postman - Test results](assets/postman-test-results.png) + +### Create pull request + +1. Once you have your commit in place and you push your branch to your forked repository go to `Pull requests` option and then `Create pull request` +![GitHub - Create pull request](assets/github-create-pull-request.png) +2. Click under `compare across forks` to be able to select the main repository and make sure to select the following + - Base repository: `gus-code/mfee-project` + - Base: `node/` + - Head repository: `/mfee-project` + - Compare: `` + + ![GitHub - Pull request branches](assets/github-pull-request-branches.png) +3. Check that the files you worked on are in place and then click `Create pull request` +4. Add the title with the following format `feat(session-*): ` where * is the number of the session (01, 02, etc.). After that click again on `Create pull request` +![GitHub - Add title](assets/github-add-title.png) +5. After this we will review the PR, give feedback and merge it to your branch \ No newline at end of file diff --git a/apps/api/assets/github-add-title.png b/apps/api/assets/github-add-title.png new file mode 100644 index 00000000..616381f5 Binary files /dev/null and b/apps/api/assets/github-add-title.png differ diff --git a/apps/api/assets/github-create-pull-request.png b/apps/api/assets/github-create-pull-request.png new file mode 100644 index 00000000..e47691ae Binary files /dev/null and b/apps/api/assets/github-create-pull-request.png differ diff --git a/apps/api/assets/github-pull-request-branches.png b/apps/api/assets/github-pull-request-branches.png new file mode 100644 index 00000000..c1c341f6 Binary files /dev/null and b/apps/api/assets/github-pull-request-branches.png differ diff --git a/apps/api/assets/postman-open-tests.png b/apps/api/assets/postman-open-tests.png new file mode 100644 index 00000000..4c77c788 Binary files /dev/null and b/apps/api/assets/postman-open-tests.png differ diff --git a/apps/api/assets/postman-run-tests.png b/apps/api/assets/postman-run-tests.png new file mode 100644 index 00000000..a33980ef Binary files /dev/null and b/apps/api/assets/postman-run-tests.png differ diff --git a/apps/api/assets/postman-test-results.png b/apps/api/assets/postman-test-results.png new file mode 100644 index 00000000..94d56d08 Binary files /dev/null and b/apps/api/assets/postman-test-results.png differ diff --git a/apps/api/assets/postman-variables.png b/apps/api/assets/postman-variables.png new file mode 100644 index 00000000..803a5f1e Binary files /dev/null and b/apps/api/assets/postman-variables.png differ diff --git a/apps/api/src/assets/mfee-node.postman_collection.json b/apps/api/src/assets/mfee-node.postman_collection.json index 66942196..f1f9b9db 100644 --- a/apps/api/src/assets/mfee-node.postman_collection.json +++ b/apps/api/src/assets/mfee-node.postman_collection.json @@ -19,7 +19,7 @@ "exec": [ "const result = pm.response.json();\r", "\r", - "pm.collectionVariables.set('test_category_id', result._id);\r", + "pm.collectionVariables.set('test_category_id', result._id ?? result.id);\r", "\r", "pm.test('Status code is 201', function () {\r", " pm.response.to.have.status(201);\r", @@ -56,11 +56,12 @@ } }, "url": { - "raw": "{{url}}/categories", + "raw": "{{url}}/api/categories", "host": [ "{{url}}" ], "path": [ + "api", "categories" ] } @@ -82,7 +83,7 @@ "\r", "pm.test('New category in response', function () {\r", " const newCategoryId = pm.collectionVariables.get('test_category_id');\r", - " const index = result.findIndex(c => c._id === newCategoryId);\r", + " const index = result.findIndex(c => [c._id, c.id].includes(newCategoryId));\r", "\r", " pm.expect(index).to.not.equal(-1);\r", "});\r", @@ -96,11 +97,12 @@ "method": "GET", "header": [], "url": { - "raw": "{{url}}/categories", + "raw": "{{url}}/api/categories", "host": [ "{{url}}" ], "path": [ + "api", "categories" ] } @@ -151,11 +153,12 @@ } }, "url": { - "raw": "{{url}}/categories/:id", + "raw": "{{url}}/api/categories/:id", "host": [ "{{url}}" ], "path": [ + "api", "categories", ":id" ], @@ -176,8 +179,8 @@ "listen": "test", "script": { "exec": [ - "pm.test('Status code is 204', function () {\r", - " pm.response.to.have.status(204);\r", + "pm.test('Status code is 204 or 200', function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([204, 200]);\r", "});\r", "" ], @@ -189,11 +192,12 @@ "method": "DELETE", "header": [], "url": { - "raw": "{{url}}/categories/:id", + "raw": "{{url}}/api/categories/:id", "host": [ "{{url}}" ], "path": [ + "api", "categories", ":id" ], @@ -227,11 +231,12 @@ "method": "GET", "header": [], "url": { - "raw": "{{url}}/categories/:id", + "raw": "{{url}}/api/categories/:id", "host": [ "{{url}}" ], "path": [ + "api", "categories", ":id" ], @@ -245,6 +250,90 @@ }, "response": [] } + ], + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{accessToken}}", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "pm.sendRequest({", + " url: `${pm.collectionVariables.get('url')}/api/auth/register`,", + " method: 'POST',", + " header: {", + " 'Content-Type': 'application/json',", + " 'Accept': \"*/*\"", + " },", + " body: {", + " mode: 'raw',", + " raw: {", + " username: 'mfee-test',", + " password: 'Aa$123'", + " }", + " }", + "}, (error, response) => {", + " if (error) {", + " console.log(error);", + " }", + "", + " pm.test('Register user should return 201 or 409', (done) => {", + " const json = response.json();", + "", + " console.log(response, json);", + " pm.expect(response.code).to.be.oneOf([201, 409]);", + "", + " pm.sendRequest({", + " url: `${pm.collectionVariables.get('url')}/api/auth/login`,", + " method: 'POST',", + " header: {", + " 'Content-Type': 'application/json',", + " 'Accept': \"*/*\"", + " },", + " body: {", + " mode: 'raw',", + " raw: {", + " username: 'mfee-test',", + " password: 'Aa$123'", + " }", + " }", + " }, (error, response) => {", + " if (error) {", + " console.log(error);", + " }", + "", + " pm.test('Login user should get access token', () => {", + " const json = response.json();", + "", + " pm.expect(error).to.equal(null);", + " pm.collectionVariables.set('accessToken', json.accessToken);", + "", + " done();", + " });", + " });", + " });", + "});" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } ] }, { @@ -258,11 +347,12 @@ "script": { "exec": [ "pm.sendRequest({\r", - " url: `${pm.collectionVariables.get('url')}/categories`,\r", + " url: `${pm.collectionVariables.get('url')}/api/categories`,\r", " method: 'POST',\r", " header: {\r", " 'Content-Type': 'application/json',\r", - " 'Accept': \"*/*\"\r", + " 'Accept': \"*/*\",\r", + " 'Authorization': `Bearer ${pm.collectionVariables.get('accessToken')}`\r", " },\r", " body: {\r", " mode: 'raw',\r", @@ -280,12 +370,12 @@ "\r", " pm.expect(error).to.equal(null);\r", " pm.expect(json.name).to.equal('Other');\r", - " pm.collectionVariables.set('test_post_category_id', json._id);\r", + " pm.collectionVariables.set('test_post_category_id', json._id ?? json.id);\r", "\r", " pm.collectionVariables.set('test_post_title', 'Post Test Postman');\r", " pm.collectionVariables.set('test_post_image', 'https://images.unsplash.com/photo-1556276797-5086e6b45ff9?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=600&ixid=eyJhcHBfaWQiOjF9&ixlib=rb-1.2.1&q=80&w=800');\r", " pm.collectionVariables.set('test_post_description', 'Description from Postman');\r", - " pm.collectionVariables.set('test_post_category', json._id);\r", + " pm.collectionVariables.set('test_post_category', json._id ?? json.id);\r", "\r", " done();\r", " });\r", @@ -301,7 +391,7 @@ "exec": [ "const result = pm.response.json();\r", "\r", - "pm.collectionVariables.set('test_post_id', result._id);\r", + "pm.collectionVariables.set('test_post_id', result._id ?? result.id);\r", "\r", "pm.test('Status code is 201', function () {\r", " pm.response.to.have.status(201);\r", @@ -333,11 +423,12 @@ } }, "url": { - "raw": "{{url}}/posts", + "raw": "{{url}}/api/posts", "host": [ "{{url}}" ], "path": [ + "api", "posts" ] } @@ -389,11 +480,12 @@ } }, "url": { - "raw": "{{url}}/posts/:id/comments", + "raw": "{{url}}/api/posts/:id/comments", "host": [ "{{url}}" ], "path": [ + "api", "posts", ":id", "comments" @@ -425,7 +517,7 @@ " pm.expect(result.title).to.equal('Post Test Postman');\r", " pm.expect(result.image).to.equal('https://images.unsplash.com/photo-1556276797-5086e6b45ff9?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=600&ixid=eyJhcHBfaWQiOjF9&ixlib=rb-1.2.1&q=80&w=800');\r", " pm.expect(result.description).to.equal('Description from Postman');\r", - " pm.expect(result.category._id).to.equal(pm.collectionVariables.get('test_post_category_id'));\r", + " pm.expect(result.category._id ?? result.category.id).to.equal(pm.collectionVariables.get('test_post_category_id'));\r", " pm.expect(result.comments.length).to.equal(1);\r", "});" ], @@ -437,11 +529,12 @@ "method": "GET", "header": [], "url": { - "raw": "{{url}}/posts/:id", + "raw": "{{url}}/api/posts/:id", "host": [ "{{url}}" ], "path": [ + "api", "posts", ":id" ], @@ -498,11 +591,12 @@ } }, "url": { - "raw": "{{url}}/posts/:id", + "raw": "{{url}}/api/posts/:id", "host": [ "{{url}}" ], "path": [ + "api", "posts", ":id" ], @@ -531,7 +625,7 @@ "\r", "pm.test('Validate new content in response', function () {\r", " const newPostId = pm.collectionVariables.get('test_post_id');\r", - " const index = result.findIndex(c => c._id === newPostId);\r", + " const index = result.findIndex(c => (c._id ?? c.id) === newPostId);\r", " const post = result[index];\r", "\r", " pm.expect(index).to.not.equal(-1);\r", @@ -546,11 +640,12 @@ "method": "GET", "header": [], "url": { - "raw": "{{url}}/posts", + "raw": "{{url}}/api/posts", "host": [ "{{url}}" ], "path": [ + "api", "posts" ] } @@ -565,7 +660,7 @@ "script": { "exec": [ "pm.test('Status code is 204', function () {\r", - " pm.response.to.have.status(204);\r", + " pm.expect(pm.response.code).to.be.oneOf([204, 200]);\r", "});\r", "" ], @@ -577,11 +672,12 @@ "method": "DELETE", "header": [], "url": { - "raw": "{{url}}/posts/:id", + "raw": "{{url}}/api/posts/:id", "host": [ "{{url}}" ], "path": [ + "api", "posts", ":id" ], @@ -613,11 +709,12 @@ "});\r", "\r", "pm.sendRequest({\r", - " url: `${pm.collectionVariables.get('url')}/categories/${pm.collectionVariables.get('test_post_category_id')}`,\r", + " url: `${pm.collectionVariables.get('url')}/api/categories/${pm.collectionVariables.get('test_post_category_id')}`,\r", " method: 'DELETE',\r", " header: {\r", " 'Content-Type': 'application/json',\r", - " 'Accept': \"*/*\"\r", + " 'Accept': \"*/*\",\r", + " 'Authorization': `Bearer ${pm.collectionVariables.get('accessToken')}`\r", " }\r", "}, (error, response) => {\r", " if (error) {\r", @@ -640,11 +737,12 @@ "method": "GET", "header": [], "url": { - "raw": "{{url}}/posts/category/:category", + "raw": "{{url}}/api/posts/category/:category", "host": [ "{{url}}" ], "path": [ + "api", "posts", "category", ":category" @@ -659,6 +757,89 @@ }, "response": [] } + ], + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{accessToken}}", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "pm.sendRequest({", + " url: `${pm.collectionVariables.get('url')}/api/auth/register`,", + " method: 'POST',", + " header: {", + " 'Content-Type': 'application/json',", + " 'Accept': \"*/*\"", + " },", + " body: {", + " mode: 'raw',", + " raw: {", + " username: 'mfee-test',", + " password: 'Aa$123'", + " }", + " }", + "}, (error, response) => {", + " if (error) {", + " console.log(error);", + " }", + "", + " pm.test('Register user should be completed successfully', (done) => {", + " const json = response.json();", + "", + " pm.expect(response.code).to.be.oneOf([201, 409]);", + "", + " pm.sendRequest({", + " url: `${pm.collectionVariables.get('url')}/api/auth/login`,", + " method: 'POST',", + " header: {", + " 'Content-Type': 'application/json',", + " 'Accept': \"*/*\"", + " },", + " body: {", + " mode: 'raw',", + " raw: {", + " username: 'mfee-test',", + " password: 'Aa$123'", + " }", + " }", + " }, (error, response) => {", + " if (error) {", + " console.log(error);", + " }", + "", + " pm.test('Login user should get access token', () => {", + " const json = response.json();", + "", + " pm.expect(error).to.equal(null);", + " pm.collectionVariables.set('accessToken', json.accessToken);", + "", + " done();", + " });", + " });", + " });", + "});" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } ] } ], @@ -685,7 +866,12 @@ "variable": [ { "key": "url", - "value": "http://localhost:3000/api", + "value": "http://localhost:3000", + "type": "string" + }, + { + "key": "accessToken", + "value": "", "type": "string" }, { diff --git a/apps/angular-app/src/app/services/.gitkeep b/apps/api/src/config/.gitkeep similarity index 100% rename from apps/angular-app/src/app/services/.gitkeep rename to apps/api/src/config/.gitkeep diff --git a/apps/angular-app/src/app/views/.gitkeep b/apps/api/src/middleware/.gitkeep similarity index 100% rename from apps/angular-app/src/app/views/.gitkeep rename to apps/api/src/middleware/.gitkeep diff --git a/apps/react-app/README.md b/apps/react-app/README.md new file mode 100644 index 00000000..8b43a9a1 --- /dev/null +++ b/apps/react-app/README.md @@ -0,0 +1,11 @@ +# React App + +## Instructions + +## Recommendations + +## Challenges + +### Session * + +## How to \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c1ea404a..b27ba381 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,11 @@ "@angular/router": "~17.0.0", "@swc/helpers": "~0.5.2", "axios": "^1.0.0", + "bcrypt": "^5.1.1", + "cors": "^2.8.5", "express": "~4.18.1", + "helmet": "^7.1.0", + "jsonwebtoken": "^9.0.2", "mongoose": "^8.0.3", "normalize.css": "^8.0.1", "react": "18.2.0", @@ -5501,6 +5505,89 @@ "node": ">= 0.4" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@mole-inc/bin-wrapper": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/@mole-inc/bin-wrapper/-/bin-wrapper-8.0.1.tgz", @@ -9325,7 +9412,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -9358,6 +9444,11 @@ "node": ">= 8" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, "node_modules/arch": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", @@ -9378,6 +9469,18 @@ } ] }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -9947,8 +10050,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-js": { "version": "1.5.1", @@ -10003,6 +10105,24 @@ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/bcrypt/node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -10251,7 +10371,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -10587,6 +10706,11 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -10894,7 +11018,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, "engines": { "node": ">=10" } @@ -11054,6 +11177,14 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -11171,8 +11302,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/confusing-browser-globals": { "version": "1.0.11", @@ -11258,6 +11388,11 @@ "node": ">= 0.6" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -11426,7 +11561,6 @@ "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, "dependencies": { "object-assign": "^4", "vary": "^1" @@ -12011,6 +12145,11 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -12037,6 +12176,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -12273,6 +12420,14 @@ "node": ">= 0.8.0" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -12314,8 +12469,7 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/emojis-list": { "version": "3.0.0", @@ -12338,7 +12492,6 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, "optional": true, "dependencies": { "iconv-lite": "^0.6.2" @@ -12348,7 +12501,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "optional": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -14063,8 +14215,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -14115,6 +14266,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -14197,7 +14367,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -14235,7 +14404,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -14438,6 +14606,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "node_modules/hasown": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", @@ -14475,6 +14648,14 @@ "he": "bin/he" } }, + "node_modules/helmet": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", + "integrity": "sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/hosted-git-info": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", @@ -14938,7 +15119,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -15260,7 +15440,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -16707,6 +16886,57 @@ "node >= 0.2.0" ] }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsonwebtoken/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -16722,6 +16952,25 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kareem": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", @@ -17079,12 +17328,42 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, "node_modules/lodash.isfinite": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==", "dev": true }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -17097,6 +17376,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -17565,7 +17849,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -17578,7 +17861,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -17589,8 +17871,7 @@ "node_modules/minizlib/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/mitt": { "version": "1.2.0", @@ -17886,6 +18167,44 @@ "dev": true, "optional": true }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -18189,6 +18508,17 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -18299,7 +18629,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, "engines": { "node": ">=0.10.0" } @@ -18446,7 +18775,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, "dependencies": { "wrappy": "1" } @@ -18836,7 +19164,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -20090,7 +20417,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -20417,7 +20743,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -20743,7 +21068,6 @@ "version": "7.5.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "dev": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -20785,7 +21109,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -20796,8 +21119,7 @@ "node_modules/semver/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/send": { "version": "0.18.0", @@ -20947,6 +21269,11 @@ "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", "dev": true }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, "node_modules/set-function-length": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", @@ -21049,8 +21376,7 @@ "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==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sigstore": { "version": "2.1.0", @@ -21474,7 +21800,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -21496,7 +21821,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -21590,7 +21914,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -21906,7 +22229,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", - "dev": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -21939,7 +22261,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, "dependencies": { "minipass": "^3.0.0" }, @@ -21951,7 +22272,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -21963,7 +22283,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -21972,7 +22291,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -21983,8 +22301,7 @@ "node_modules/tar/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/terser": { "version": "5.24.0", @@ -22840,8 +23157,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/utils-merge": { "version": "1.0.1", @@ -23606,6 +23922,14 @@ "node": ">=8" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/wildcard": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", @@ -23650,8 +23974,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/write-file-atomic": { "version": "4.0.2", @@ -27293,6 +27616,69 @@ "call-bind": "^1.0.2" } }, + "@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "requires": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "requires": { + "abbrev": "1" + } + } + } + }, "@mole-inc/bin-wrapper": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/@mole-inc/bin-wrapper/-/bin-wrapper-8.0.1.tgz", @@ -30090,8 +30476,7 @@ "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "4.3.0", @@ -30112,12 +30497,26 @@ "picomatch": "^2.0.4" } }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, "arch": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", "dev": true }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, "arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -30531,8 +30930,7 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "base64-js": { "version": "1.5.1", @@ -30569,6 +30967,22 @@ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, + "bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "requires": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "dependencies": { + "node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + } + } + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -30772,7 +31186,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -31034,6 +31447,11 @@ "ieee754": "^1.1.13" } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -31256,8 +31674,7 @@ "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, "chrome-trace-event": { "version": "1.0.3", @@ -31368,6 +31785,11 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + }, "colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -31472,8 +31894,7 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "confusing-browser-globals": { "version": "1.0.11", @@ -31546,6 +31967,11 @@ "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", "dev": true }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -31671,7 +32097,6 @@ "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, "requires": { "object-assign": "^4", "vary": "^1" @@ -32083,6 +32508,11 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -32099,6 +32529,11 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" }, + "detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==" + }, "detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -32270,6 +32705,14 @@ "chalk": "4.1.2" } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -32299,8 +32742,7 @@ "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "emojis-list": { "version": "3.0.0", @@ -32317,7 +32759,6 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, "optional": true, "requires": { "iconv-lite": "^0.6.2" @@ -32327,7 +32768,6 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, "optional": true, "requires": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -33640,8 +34080,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { "version": "2.3.3", @@ -33673,6 +34112,22 @@ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, "gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -33728,7 +34183,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -33742,7 +34196,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -33904,6 +34357,11 @@ "has-symbols": "^1.0.2" } }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "hasown": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", @@ -33935,6 +34393,11 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "helmet": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", + "integrity": "sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==" + }, "hosted-git-info": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", @@ -34274,7 +34737,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -34502,8 +34964,7 @@ "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-generator-fn": { "version": "2.1.0", @@ -35573,6 +36034,46 @@ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true }, + "jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } + }, "jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -35585,6 +36086,25 @@ "object.values": "^1.1.6" } }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "kareem": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", @@ -35851,12 +36371,42 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, "lodash.isfinite": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==", "dev": true }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -35869,6 +36419,11 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -36237,7 +36792,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -36247,7 +36801,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -36255,8 +36808,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -36466,6 +37018,35 @@ "dev": true, "optional": true }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } + } + }, "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", @@ -36688,6 +37269,17 @@ "path-key": "^3.0.0" } }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -36774,8 +37366,7 @@ "object-assign": { "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 + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-inspect": { "version": "1.13.1", @@ -36877,7 +37468,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, "requires": { "wrappy": "1" } @@ -37171,8 +37761,7 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, "path-key": { "version": "3.1.1", @@ -38025,7 +38614,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -38284,7 +38872,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "requires": { "glob": "^7.1.3" } @@ -38507,7 +39094,6 @@ "version": "7.5.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "dev": true, "requires": { "lru-cache": "^6.0.0" }, @@ -38516,7 +39102,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -38524,8 +39109,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -38680,6 +39264,11 @@ "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", "dev": true }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, "set-function-length": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", @@ -38761,8 +39350,7 @@ "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==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "sigstore": { "version": "2.1.0", @@ -39110,7 +39698,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "requires": { "safe-buffer": "~5.2.0" } @@ -39129,7 +39716,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -39201,7 +39787,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "requires": { "ansi-regex": "^5.0.1" } @@ -39410,7 +39995,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", - "dev": true, "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -39424,7 +40008,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, "requires": { "minipass": "^3.0.0" }, @@ -39433,7 +40016,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -39443,20 +40025,17 @@ "minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" }, "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -40052,8 +40631,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "utils-merge": { "version": "1.0.1", @@ -40530,6 +41108,14 @@ "stackback": "0.0.2" } }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "wildcard": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", @@ -40561,8 +41147,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "write-file-atomic": { "version": "4.0.2", diff --git a/package.json b/package.json index adbb7479..221fc6d9 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,11 @@ "@angular/router": "~17.0.0", "@swc/helpers": "~0.5.2", "axios": "^1.0.0", + "bcrypt": "^5.1.1", + "cors": "^2.8.5", "express": "~4.18.1", + "helmet": "^7.1.0", + "jsonwebtoken": "^9.0.2", "mongoose": "^8.0.3", "normalize.css": "^8.0.1", "react": "18.2.0",