From 5eeb3a99354be3721a2b34e8c8a5409c11d5fa0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=8C=8C=EB=9E=91?= <77425729+summerlunaa@users.noreply.github.com> Date: Thu, 18 Aug 2022 20:43:49 +0900 Subject: [PATCH] [release] v0.1.0 (#508) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 구글 캘린더 이벤트 조회 기능 추가 * feat: ExternalCalendarService 로직 정리 * refactor: 일정 우선순위 커스텀훅 view 사이즈 증가 및 리팩토링 * feat: 일정 더보기 기능 구현 * style: 일정 추가 버튼 스타일 수정 * fix: 시작 페이지 애니메이션 초기값 수정 * refactor: api 관련 상수 분리 * feat: access token 유효성 검사 기능 구현 * feat: 메인 페이지 접속 시 access token의 유효성 검사 * refactor: 로컬 스토리지 관련 상수 분리 * fix: 구글 로그인 에러 수정 * refactor: access token의 유효성을 검사하는 커스텀 훅 구현 및 분리 * refactor: 전역 사용자 상태 setter 함수명 변경 * refactor: hook 호출 순서 변경 * feat: access token 유효성 검사 결과에 따라 전역 사이드 바 상태 변경 * feat: 로그인 사용자용 페이지 접속 시 access token의 유효성 검사 * refactor: 불필요한 access token 타입 제거 * refactor: 컬럼명을 member_id에서 members_id로 변경 * feat: prod 환경에서 사용될 DDL 작성 * feat: prod 환경 db 설정 변경 * ci: 개별 클래스의 라인 커버리지가 75% 이하라면, 빌드가 실패하도록 설정 * fix: Jpa 오류를 해결하기 위한 getter 제거 * feat: 카테고리 삭제 시 연관된 스케줄도 삭제한다. * fix: 회원과 연관된 구독 정보들을 모두 삭제하도록 수정 * refactor: queryClient useErrorBoundary 디폴트 옵션 추가 * refactor: 에러 바운더리 컴포넌트 타입 수정 * fix: 에러 바운더리 컴포넌트 children prop 타입 수정 * feat: 통합 일정을 관리하기 위한 IntegrationSchedule 추가 * feat: spring jdbc 의존성 추가 * feat: 누락된 DB 컬럼 이름 추가 * feat: 통합 스케줄 형식으로 조회하기 위한 Dao 추가 * feat: 기존 로직 이전 및 테스트 이전 * feat: 불필요 파일 제거 * test: 불필요 메서드 삭제 및 누락된 테스트 추가 * feat: 소나큐브 오류원인이 되는 build.gradle 파일 수정 * chore: 젠킨스 CD 시점에 소나큐브를 수행 하도록 수정 * feat: Not Found 페이지 컴포넌트 구현 * fix: access token 유효성 검사 로직 error 처리 수정 * refactor: 불필요한 코드 제거 * feat: 입력창 공통 컴포넌트에 value props 추가 * feat: 일정 시작 및 종료 날짜 자동 설정 기능 구현 * fix: 종일 일정 수정 시 날짜 기본값이 보이지 않는 에러 수정 * ci: 프론트엔드 개발 서버 스크립트 추가 Co-authored-by: Daye Lee <508yeah@gmail.com> * feat(Category): 카테고리 타입 필드를 추가한다 * feat(Category): 외부 캘린더를 저장하기 위한 table 생성 * refactor: 네이밍 및 필드 타입 변경 * feat: 외부 카테고리를 저장하는 기능 구현 * test: ExternalService 테스트 구현 * test: CategorySubscriptionService 테스트 구현 * test: ExternalCalendarController 검증 추가 * test: ExternalCalendar 인수테스트 추가 * refactor: 오타 수정 * fix: 프론트엔드 개발 서버 discord web hook 환경 변수 변경 Co-authored-by: Daye Lee <508yeah@gmail.com> * chore: 불필요한 소나큐브 관련 xml 파일 제거 * chore: 프로덕션을 위한 DDL 파일 수정 * feat: 서브모듈 업데이트 * feat: data.sql 제거 * feat: schema.sql 갱신 * feat: 서브모듈 갱신 * refactor: 개인 카테고리 이름 변경 * feat: 개인 카테고리는 삭제할 수 없다 * feat: 개인 카테고리는 이름을 수정할 수 없다 * feat: 외부 캘린더 조회 반환 타입 변경 * feat: 외부 일정 및 개인 일정 조회 로직 개선 * feat: 외부 일정 및 개인 일정 조회 로직 개선 * feat: response에 categoryType 추가 * test: 커버리지를 위한 테스트 코드 수정 * feat: data.sql 제거 * test: 누락된 문서화 추가 * feat: GoogleExternalCalendarClient getExternalCalendars 요청 방식 변경 * feat: google 일정 종일 판단 로직 수정 * feat: NotFound 페이지 진입 시 사이드바 감추기 * style: NotFound 페이지 스타일 수정 * chore: 코드 스타일 수정 * feat: 날짜 단건 조회 모달 컴포넌트 구현 * style: 카테고리 페이지 버튼 스타일링 수정 * feat: 구글 캘린더 가져오기 모달 UI 구현 * feat: 구글 캘린더 연동 상수 및 api 구현 * feat: 입력 제어 커스텀훅에 select 태그를 위한 기능 추가 * feat: 구글 캘린더 연동 시 유효성 검사 * refactor: 타입 분리 및 불필요한 옵션 제거 * chore: react query client retry option 추가 * ci: 프론트엔드 배포 파이프라인 스크립트 수정 * feat: 프론트엔드 dev 서버 대응을 위한 CORS 규칙 업데이트 * feat: 프론트엔드 dev 서버 대응을 위한 oauth redirect uri 변경 * ci: 캐시 제거 방법 변경 * ci: 워딩 수정 * fix: 카테고리 타입이 잘못 들어온 경우를 처리한다 * fix: 외부 연동 카테고리에는 일정을 등록할 수 없다 * fix: 전체 일반 카테고리 조회 시 외부 연동 카테고리는 제외한다 * test: CategoryType 테스트 추가 * refactor: 던지는 예외 변경 * test: CategoryType 테스트 추가 * refactor: 메서드명 수정 * refactor: 메서드명 수정 * refactor: 메서드명 수정 * chore: script 수정 * feat: 외부 카테고리 중복 저장 시 예외를 던지도록 수정 * test: 외부 캘린더 중복 저장 시 예외 추가 * fix: 불필요 개행 제거 * feat: 외부 일정 종일 판단 수정 * ci: 프론트엔드 패키지 매니저를 yarn으로 변경 * feat: oauth uri를 생성할 때 redirect uri를 외부에서 직접 전달받도록 개선 * feat: Access token을 생성할 때 redirect URI를 함께 보내도록 수정 * fix: 로그인 요청 수정 * refactor: 카테고리 이름 유효성 검사를 위한 상수 분리 * feat: 특정 문자열과 같은지 비교하는 유효성 검사 함수 구현 * feat: 카테고리 이름 유효성 검사 커스텀훅 구현 및 적용 * feat: 구글 캘린더 가져오기를 위한 카테고리 생성 유효성 검사 수정 * feat: 카테고리 생성 시 body에 categoryType 추가 * fix: 일정 제목이 null인 경우 디폴트 값이 들어가도록 수정 * refactor: 디폴트값 수정 * refactor: memo에 notnull 검증 추가 * refactor: 외부로 부터 연동한 카테고리도 삭제 가능하도록 변경 * refactor: 외부 카테고리를 삭제하는 로직의 순서 변경 * refactor: access token 유효성 검사를 페이지에서 분리 * refactor: ExternalCategoryDetailRepository의 불필요한 메서드 제거 * refactor: 변경된 일정 id 타입 대응 * fix: 일정 변경 에러 변경 * feat: 개인정보처리방침 페이지 컴포넌트 구현 * feat: footer 컴포넌트 구현 * style: 개인정보처리방침의 margin 변경 * feat: 카테고리 조회 시 카테고리 타입도 함께 전달하도록 개선 * feat: 구글 연동 일정 편집 및 삭제 권한 해제 * feat: 카테고리과 일정 타입에 따라 구독, 삭제, 편집 권한 개별 부여 * refactor: 툴팁 메세지 상수 수정 * fix: 객체 추가가 불가능한 불변 객체 수정 * fix: 겹치는 복수일정에서 렌더링되지 않는 일정 더보기 에러 수정 * fix: 달력 뷰를 넘어가는 장기 일정의 렌더링 오류 수정 * feat: Period를 다른 Period로 자르는 기능 구현 * feat: 일정 목록을 통해 비어있는 기간 목록을 계산하는 기능 구현 * feat: 카테고리를 통해 해당 카테고리를 구독하는 모든 유저의 기간을 만족하는 일정을 통해 겹치지 않는 기간을 계산하는 기능을 서비스에 구현 * docs: 일정 조율 API 문서 작성 * test: 인수 테스트 작성 * refactor: 컨트롤러 분리 * refactor: SchedulerService 리팩토링 * feat: IntegrationSchedule 대응 * test: CategoryType 대응 * docs: adoc 경로 변경 * chore: TODO 추가 * refactor: 개행 추가 * refactor: 구독ID로 멤버를 찾을 때 MemberRepository 대신 SubscriptionRepository를 사용하도록 개선 * refactor: 구독자 멤버의 ID목록을 반환하도록 메서드 개선 * feat: 중복되는 카테고리 제거 * refactor: isNotOverlapped 가독성 개선 * feat: 날짜뿐만 아니라 시간까지 일정 조율 범위를 조정할 수 있도록 개선 * fix: 일정 수정 시 빈 제목을 넣을 수 있도록 변경 * refactor: 사용하지 않는 dto 삭제 및 검증 어노테이션 추가 * fix: 일정 제목이 null인 경우 디폴트 값이 들어가도록 수정 * fix: 일정 삭제 시 달력에 바로 반영 * feat: 카테고리 타입에 따라 description 추가 * feat: 공백 특수 문자 제거 및 나의 카테고리 목록에도 description 추가 * refactor: react query cache key 상수 분리 * fix: 에러 메시지 조건부 렌더링 * feat: 일정 조율 기능 구현 * feat: 일정 조율 페이지 컴포넌트 구현 * feat: 내비게이션 바에 일정 조율 메뉴 추가 * style: 카테고리 페이지 컴포넌트 스타일 변경 * feat: 시간 형식 변경 * style: 시간 목록의 overflow 스타일 변경 * fix: 시간에서 시작 날짜와 종료 날짜 분리 * fix: 시간 형식 변경 * fix: 카테고리 목록에서 일반 카테고리만 필터링 * fix: 복수 일정의 우선순위 에러 해결 * feat: 구독 카테고리 필터링 시 일정 로딩을 위한 스피너 추가 * refactor: 불필요한 코드 정리 * fix: 프로필 수정 시 카테고리 생성자에 바로 반영 * feat: 입력 필드에 label 추가 * style: label 스타일 수정 * style: 버튼 사이즈 수정 * feat: 일정 제목이 없는 경우 '제목없음' 렌더링 * refactor: 상수 변경 * docs: 자신의 구독 목록 조회 API 문서 추가 * feat: 외부 일정을 포함하여 일정 조율할 수 있도록 개선 * feat: 일정 추가 시 카테고리 디폴트 값 설정 및 스타일 변경 * style: 입력 필드 에러메세지 스타일 수정 * feat: 구글 캘린더를 일정 조율 대상에 포함시키도록 개선 * docs: build.gradle의 소나큐브 코드 커버리지를 위한 설정 추가 * docs: build.gradle의 소나큐브 코드 커버리지를 위한 설정 변경 * docs: build.gradle의 소나큐브 코드 커버리지를 위한 설정 추가변경 * docs: build.gradle의 소나큐브 코드 커버리지를 위한 설정 jacoco 설정변경 * style: 카테고리 페이지 모드 선택 버튼 토글로 변경 * fix: access token 유효성 검사 성공 시의 전역 사이드 바 상태 변경 * fix: 404 페이지 접속 시 access token의 유효성 검사 * fix: 사이드 바 컴포넌트 조건부 렌더링 방식 변경 * fix: 전역 사이드 바 default 상태 변경 * style: 시작 페이지 콘텐츠 수정 * style: 구독 카테고리 목록 스켈레톤 UI 수정 * fix: 누락된 사이드 바 애니메이션 추가 * fix: 구글 캘린더 가져오기 버튼을 사이드 바로 이동 * style: 카테고리 페이지 헤더 스타일 변경 Co-authored-by: hyeonic Co-authored-by: mat <59357153+hyeonic@users.noreply.github.com> Co-authored-by: jhy979 Co-authored-by: jhy979 <32920566+jhy979@users.noreply.github.com> Co-authored-by: Daye Lee <508yeah@gmail.com> Co-authored-by: devHudi Co-authored-by: koo Co-authored-by: 구동희 <71062817+gudonghee2000@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/bug-template.md | 3 +- .github/ISSUE_TEMPLATE/feature-template.md | 3 +- .github/PULL_REQUEST_TEMPLATE.md | 3 +- .github/workflows/backend-ci.yml | 39 + .github/workflows/frontend-ci.yml | 38 + .gitmodules | 3 + README.md | 6 + backend/.gitignore | 6 + backend/build.gradle | 151 +- backend/src/docs/asciidoc/auth.adoc | 59 + backend/src/docs/asciidoc/category.adoc | 145 + .../src/docs/asciidoc/external-calendar.adoc | 47 + backend/src/docs/asciidoc/index.adoc | 13 + backend/src/docs/asciidoc/member.adoc | 53 + backend/src/docs/asciidoc/schedule.adoc | 137 + backend/src/docs/asciidoc/subscription.adoc | 123 + .../com/allog/dallog/DallogApplication.java | 7 +- .../domain/auth/application/AuthService.java | 79 + .../auth/application/JwtTokenProvider.java | 66 + .../domain/auth/application/OAuthClient.java | 11 + .../domain/auth/application/OAuthUri.java | 7 + .../auth/application/TokenProvider.java | 10 + .../dallog/domain/auth/domain/OAuthToken.java | 53 + .../auth/domain/OAuthTokenRepository.java | 13 + .../dallog/domain/auth/dto/LoginMember.java | 17 + .../dallog/domain/auth/dto/OAuthMember.java | 33 + .../domain/auth/dto/request/TokenRequest.java | 29 + .../response/OAuthAccessTokenResponse.java | 40 + .../auth/dto/response/OAuthUriResponse.java | 18 + .../auth/dto/response/TokenResponse.java | 17 + .../EmptyAuthorizationHeaderException.java | 12 + .../auth/exception/InvalidTokenException.java | 12 + .../auth/exception/NoPermissionException.java | 12 + .../exception/NoSuchOAuthTokenException.java | 12 + .../category/application/CategoryService.java | 164 + .../domain/category/domain/Category.java | 113 + .../category/domain/CategoryRepository.java | 26 + .../domain/category/domain/CategoryType.java | 16 + .../domain/ExternalCategoryDetail.java | 49 + .../ExternalCategoryDetailRepository.java | 14 + .../dto/request/CategoryCreateRequest.java | 29 + .../dto/request/CategoryUpdateRequest.java | 20 + .../ExternalCategoryCreateRequest.java | 28 + .../dto/response/CategoriesResponse.java | 33 + .../dto/response/CategoryResponse.java | 51 + .../DuplicatedExternalCategoryException.java | 12 + .../exception/InvalidCategoryException.java | 12 + .../exception/NoSuchCategoryException.java | 12 + ...NoSuchExternalCategoryDetailException.java | 12 + .../dallog/domain/common/BaseEntity.java | 30 + .../application/CalendarService.java | 140 + .../CategorySubscriptionService.java | 37 + .../application/RegisterService.java | 58 + .../application/SchedulerService.java | 54 + .../application/ExternalCalendarClient.java | 15 + .../application/ExternalCalendarService.java | 40 + .../dto/ExternalCalendar.java | 23 + .../dto/ExternalCalendarsResponse.java | 19 + .../dao/IntegrationScheduleDao.java | 61 + .../domain/IntegrationSchedule.java | 120 + .../domain/IntegrationScheduleComparator.java | 45 + .../domain/IntegrationSchedules.java | 24 + .../integrationschedule/domain/Period.java | 96 + .../domain/ScheduleType.java | 30 + .../domain/TypedSchedules.java | 30 + .../member/application/MemberService.java | 79 + .../dallog/domain/member/domain/Member.java | 92 + .../member/domain/MemberRepository.java | 11 + .../domain/member/domain/SocialType.java | 6 + .../domain/member/dto/MemberResponse.java | 50 + .../member/dto/MemberUpdateRequest.java | 20 + .../exception/InvalidMemberException.java | 12 + .../exception/NoSuchMemberException.java | 12 + .../schedule/application/ScheduleService.java | 68 + .../domain/schedule/domain/Schedule.java | 112 + .../schedule/domain/ScheduleRepository.java | 9 + .../schedule/domain/scheduler/Scheduler.java | 40 + .../dto/request/DateRangeRequest.java | 33 + .../dto/request/ScheduleCreateRequest.java | 53 + .../dto/request/ScheduleUpdateRequest.java | 48 + .../dto/response/MemberScheduleResponse.java | 68 + .../dto/response/MemberScheduleResponses.java | 51 + .../schedule/dto/response/PeriodResponse.java | 23 + .../dto/response/ScheduleResponse.java | 59 + .../exception/InvalidScheduleException.java | 12 + .../exception/NoSuchScheduleException.java | 12 + .../application/SubscriptionService.java | 132 + .../domain/subscription/domain/Color.java | 53 + .../domain/ColorPickerStrategy.java | 7 + .../subscription/domain/Subscription.java | 76 + .../domain/SubscriptionRepository.java | 17 + .../request/SubscriptionUpdateRequest.java | 37 + .../dto/response/SubscriptionResponse.java | 46 + .../dto/response/SubscriptionsResponse.java | 19 + .../exception/ExistSubscriptionException.java | 12 + .../InvalidSubscriptionException.java | 12 + .../NoSuchSubscriptionException.java | 12 + .../allog/dallog/global/config/JpaConfig.java | 9 + .../global/config/PropertiesConfig.java | 10 + .../allog/dallog/global/config/WebConfig.java | 37 + .../config/properties/GoogleProperties.java | 70 + .../dallog/global/error/ControllerAdvice.java | 116 + .../global/error/dto/ErrorReportRequest.java | 31 + .../global/error/dto/ErrorResponse.java | 14 + .../infrastructure/log/DiscordAppender.java | 107 + .../log/dto/DiscordWebhookRequest.java | 25 + .../dallog/infrastructure/log/dto/Embed.java | 41 + .../dallog/infrastructure/log/dto/Field.java | 29 + .../client/GoogleExternalCalendarClient.java | 117 + .../oauth/client/GoogleOAuthClient.java | 123 + .../dto/GoogleCalendarEventResponse.java | 101 + .../dto/GoogleCalendarEventsResponse.java | 70 + .../oauth/dto/GoogleCalendarListResponse.java | 44 + .../oauth/dto/GoogleCalendarResponse.java | 111 + .../oauth/dto/GoogleDateFormat.java | 29 + .../oauth/dto/GoogleTokenResponse.java | 52 + .../infrastructure/oauth/dto/UserInfo.java | 29 + .../oauth/exception/OAuthException.java | 16 + .../oauth/uri/GoogleOAuthUri.java | 26 + .../presentation/CategoryController.java | 77 + .../ExternalCalendarController.java | 44 + .../dallog/presentation/MemberController.java | 47 + .../presentation/ScheduleController.java | 72 + .../presentation/SchedulerController.java | 30 + .../presentation/SubscriptionController.java | 59 + .../presentation/auth/AuthController.java | 45 + .../auth/AuthenticationPrincipal.java | 11 + ...thenticationPrincipalArgumentResolver.java | 35 + .../auth/AuthorizationExtractor.java | 28 + .../src/main/resources/application.properties | 1 - backend/src/main/resources/config | 1 + backend/src/main/resources/db/prod/schema.sql | 68 + backend/src/main/resources/logback-spring.xml | 62 + .../allog/dallog/DallogApplicationTests.java | 13 - .../dallog/acceptance/AcceptanceTest.java | 25 + .../dallog/acceptance/AuthAcceptanceTest.java | 67 + .../acceptance/CategoryAcceptanceTest.java | 208 + .../ExternalCalendarAcceptanceTest.java | 73 + .../acceptance/MemberAcceptanceTest.java | 80 + .../acceptance/ScheduleAcceptanceTest.java | 91 + .../acceptance/SchedulerAcceptanceTest.java | 46 + .../SubscriptionAcceptanceTest.java | 168 + .../fixtures/AuthAcceptanceFixtures.java | 53 + .../fixtures/CategoryAcceptanceFixtures.java | 73 + .../fixtures/CommonAcceptanceFixtures.java | 30 + .../fixtures/MemberAcceptanceFixtures.java | 28 + .../fixtures/ScheduleAcceptanceFixtures.java | 60 + .../SubscriptionAcceptanceFixtures.java | 20 + .../allog/dallog/common/DatabaseCleaner.java | 39 + .../common/annotation/RepositoryTest.java | 10 + .../dallog/common/annotation/ServiceTest.java | 19 + .../common/config/ExternalApiConfig.java | 23 + .../dallog/common/config/TokenConfig.java | 17 + .../dallog/common/fixtures/AuthFixtures.java | 51 + .../common/fixtures/CategoryFixtures.java | 91 + .../fixtures/ExternalCalendarFixtures.java | 14 + .../fixtures/ExternalCategoryFixtures.java | 19 + .../fixtures/IntegrationScheduleFixtures.java | 25 + .../common/fixtures/MemberFixtures.java | 59 + .../common/fixtures/OAuthTokenFixtures.java | 13 + .../common/fixtures/ScheduleFixtures.java | 71 + .../common/fixtures/SubscriptionFixtures.java | 35 + .../auth/application/AuthServiceTest.java | 75 + .../application/JwtTokenProviderTest.java | 63 + .../auth/application/StubTokenProvider.java | 61 + .../auth/domain/OAuthTokenRepositoryTest.java | 91 + .../domain/auth/domain/OAuthTokenTest.java | 40 + .../application/CategoryServiceTest.java | 455 + .../domain/CategoryRepositoryTest.java | 188 + .../domain/category/domain/CategoryTest.java | 100 + .../category/domain/CategoryTypeTest.java | 36 + .../application/CalendarServiceTest.java | 192 + .../CategorySubscriptionServiceTest.java | 58 + .../application/RegisterServiceTest.java | 77 + .../application/SchedulerServiceTest.java | 186 + .../ExternalCalendarServiceTest.java | 40 + .../dao/IntegrationScheduleDaoTest.java | 358 + .../domain/IntegrationScheduleTest.java | 131 + .../domain/IntegrationSchedulesTest.java | 98 + .../domain/PeriodTest.java | 174 + .../member/application/MemberServiceTest.java | 127 + .../member/domain/MemberRepositoryTest.java | 47 + .../domain/member/domain/MemberTest.java | 69 + .../application/ScheduleServiceTest.java | 292 + .../domain/ScheduleRepositoryTest.java | 78 + .../domain/schedule/domain/ScheduleTest.java | 55 + .../domain/scheduler/SchedulerTest.java | 81 + .../application/SubscriptionServiceTest.java | 222 + .../domain/subscription/domain/ColorTest.java | 53 + .../domain/SubscriptionRepositoryTest.java | 165 + .../subscription/domain/SubscriptionTest.java | 74 + .../client/StubExternalCalendarClient.java | 29 + .../oauth/client/StubOAuthClient.java | 30 + .../presentation/AuthControllerTest.java | 120 + .../presentation/CategoryControllerTest.java | 438 + .../dallog/presentation/ControllerTest.java | 19 + .../ExternalCalendarControllerTest.java | 134 + .../presentation/MemberControllerTest.java | 148 + .../presentation/ScheduleControllerTest.java | 368 + .../presentation/SchedulerControllerTest.java | 81 + .../SubscriptionControllerTest.java | 282 + backend/src/test/resources/application.yml | 51 + backend/~/Desktop/jacoco/index.xml | 1 + frontend/.eslintrc.json | 20 + frontend/.gitignore | 3 + frontend/.prettierrc.json | 27 + frontend/.storybook/main.js | 23 + frontend/.storybook/preview-body.html | 5 + frontend/.storybook/preview.js | 35 + frontend/babel.config.js | 9 + frontend/jest.config.js | 6 + frontend/package.json | 81 + frontend/public/mockServiceWorker.js | 364 + frontend/setupFile.js | 5 + frontend/src/@types/calendar.ts | 8 + frontend/src/@types/category.ts | 18 + frontend/src/@types/custom.d.ts | 4 + frontend/src/@types/emotion.d.ts | 16 + frontend/src/@types/googleCalendar.ts | 13 + frontend/src/@types/index.ts | 20 + frontend/src/@types/profile.ts | 13 + frontend/src/@types/schedule.ts | 20 + frontend/src/@types/scheduler.ts | 6 + frontend/src/@types/subscription.ts | 10 + frontend/src/App.tsx | 60 + frontend/src/api/category.ts | 74 + frontend/src/api/googleCalendar.ts | 33 + frontend/src/api/index.ts | 7 + frontend/src/api/login.ts | 40 + frontend/src/api/profile.ts | 32 + frontend/src/api/schedule.ts | 63 + frontend/src/api/scheduler.ts | 33 + frontend/src/api/subscription.ts | 64 + frontend/src/assets/dallog_black.png | Bin 0 -> 7116 bytes frontend/src/assets/dallog_color.png | Bin 0 -> 23937 bytes frontend/src/assets/dallog_white.png | Bin 0 -> 7447 bytes frontend/src/assets/how_to_use_1.png | Bin 0 -> 175449 bytes frontend/src/assets/how_to_use_2.png | Bin 0 -> 146407 bytes frontend/src/assets/how_to_use_3.png | Bin 0 -> 215547 bytes .../@common/Button/Button.stories.tsx | 16 + .../@common/Button/Button.styles.ts | 23 + .../components/@common/Button/Button.test.tsx | 25 + .../src/components/@common/Button/Button.tsx | 28 + .../@common/ErrorBoundary/ErrorBoundary.tsx | 49 + .../@common/Fieldset/Fieldset.stories.tsx | 27 + .../@common/Fieldset/Fieldset.styles.ts | 45 + .../@common/Fieldset/Fieldset.test.tsx | 25 + .../components/@common/Fieldset/Fieldset.tsx | 62 + .../@common/ModalPortal/ModalPortal.styles.ts | 27 + .../@common/ModalPortal/ModalPortal.tsx | 41 + .../@common/PageLayout/PageLayout.styles.ts | 14 + .../@common/PageLayout/PageLayout.tsx | 17 + .../@common/Skeleton/Skeleton.stories.tsx | 20 + .../@common/Skeleton/Skeleton.styles.ts | 31 + .../components/@common/Skeleton/Skeleton.tsx | 21 + .../@common/Spinner/Spinner.stories.tsx | 13 + .../@common/Spinner/Spinner.styles.ts | 45 + .../components/@common/Spinner/Spinner.tsx | 22 + .../CategoryAddModal.stories.tsx | 12 + .../CategoryAddModal.styles.ts | 78 + .../CategoryAddModal/CategoryAddModal.tsx | 93 + .../CategoryList.fallback.stories.tsx | 14 + .../CategoryList/CategoryList.fallback.tsx | 32 + .../CategoryList/CategoryList.stories.tsx | 15 + .../CategoryList/CategoryList.styles.ts | 34 + .../components/CategoryList/CategoryList.tsx | 104 + .../CategoryModifyModal.styles.ts | 70 + .../CategoryModifyModal.tsx | 94 + .../components/DateModal/DateModal.styles.ts | 86 + .../src/components/DateModal/DateModal.tsx | 106 + .../FilterCategoryItem.styles.ts | 112 + .../FilterCategoryItem/FilterCategoryItem.tsx | 128 + .../FilterCategoryList.fallback.stories.tsx | 16 + .../FilterCategoryList.fallback.tsx | 22 + .../FilterCategoryList.styles.ts | 85 + .../FilterCategoryList/FilterCategoryList.tsx | 72 + .../src/components/Footer/Footer.styles.ts | 20 + frontend/src/components/Footer/Footer.tsx | 28 + .../GoogleImportModal.styles.ts | 58 + .../GoogleImportModal/GoogleImportModal.tsx | 132 + .../MyCategoryItem/MyCategoryItem.style.ts | 63 + .../MyCategoryItem/MyCategoryItem.tsx | 101 + .../MyCategoryList.fallback.tsx | 33 + .../MyCategoryList/MyCategoryList.styles.ts | 47 + .../MyCategoryList/MyCategoryList.tsx | 41 + .../src/components/NavBar/NavBar.stories.tsx | 12 + .../src/components/NavBar/NavBar.styles.ts | 86 + frontend/src/components/NavBar/NavBar.tsx | 107 + .../Profile/Profile.fallback.stories.tsx | 15 + .../components/Profile/Profile.fallback.tsx | 18 + .../src/components/Profile/Profile.styles.ts | 129 + frontend/src/components/Profile/Profile.tsx | 133 + .../components/ProtectRoute/ProtectRoute.tsx | 21 + .../components/PublicRoute/PublicRoute.tsx | 15 + .../ScheduleAddButton.stories.tsx | 17 + .../ScheduleAddButton.styles.ts | 20 + .../ScheduleAddButton/ScheduleAddButton.tsx | 23 + .../ScheduleAddModal.stories.tsx | 20 + .../ScheduleAddModal.styles.ts | 116 + .../ScheduleAddModal/ScheduleAddModal.tsx | 222 + .../ScheduleModal/ScheduleModal.styles.ts | 119 + .../ScheduleModal/ScheduleModal.tsx | 155 + .../ScheduleModifyModal.styles.ts | 123 + .../ScheduleModifyModal.tsx | 196 + .../src/components/SideBar/SideBar.styles.ts | 19 + frontend/src/components/SideBar/SideBar.tsx | 26 + .../components/SnackBar/SnackBar.styles.ts | 45 + frontend/src/components/SnackBar/SnackBar.tsx | 33 + .../SubscribedCategoryItem.styles.ts | 59 + .../SubscribedCategoryItem.tsx | 75 + .../UnsubscribedCategoryItem.styles.ts | 32 + .../UnsubscribedCategoryItem.tsx | 66 + frontend/src/constants/api.ts | 29 + frontend/src/constants/category.ts | 7 + frontend/src/constants/date.ts | 8 + frontend/src/constants/index.ts | 28 + frontend/src/constants/message.ts | 16 + frontend/src/constants/style.ts | 30 + frontend/src/constants/validate.ts | 17 + frontend/src/hooks/useCalendar.ts | 67 + frontend/src/hooks/useControlledInput.ts | 21 + frontend/src/hooks/useIntersect.ts | 35 + frontend/src/hooks/useSchedulePriority.ts | 121 + frontend/src/hooks/useSnackBar.ts | 15 + frontend/src/hooks/useToggle.ts | 13 + frontend/src/hooks/useUserValue.ts | 38 + frontend/src/hooks/useValidateCategory.ts | 41 + frontend/src/hooks/useValidateSchedule.ts | 84 + frontend/src/index.html | 12 + frontend/src/index.tsx | 27 + frontend/src/mocks/browser.ts | 5 + frontend/src/mocks/data.ts | 445 + frontend/src/mocks/handlers.ts | 263 + frontend/src/pages/AuthPage/AuthPage.tsx | 47 + .../pages/CalendarPage/CalendarPage.styles.ts | 245 + .../src/pages/CalendarPage/CalendarPage.tsx | 443 + .../pages/CategoryPage/CategoryPage.styles.ts | 108 + .../src/pages/CategoryPage/CategoryPage.tsx | 100 + frontend/src/pages/MainPage/MainPage.tsx | 18 + .../pages/NotFoundPage/NotFoundPage.styles.ts | 33 + .../src/pages/NotFoundPage/NotFoundPage.tsx | 36 + .../PrivacyPolicyPage.styles.ts | 22 + .../PrivacyPolicyPage/PrivacyPolicyPage.tsx | 158 + .../SchedulingPage/SchedulingPage.styles.ts | 124 + .../pages/SchedulingPage/SchedulingPage.tsx | 153 + .../src/pages/StartPage/StartPage.styles.ts | 286 + frontend/src/pages/StartPage/StartPage.tsx | 153 + frontend/src/recoil/atoms/index.ts | 26 + frontend/src/recoil/selectors/index.ts | 13 + frontend/src/styles/GlobalStyle.tsx | 48 + frontend/src/styles/theme.ts | 61 + frontend/src/utils/date.ts | 165 + frontend/src/utils/index.ts | 34 + frontend/src/utils/storage.ts | 15 + frontend/src/validation/index.ts | 17 + frontend/tsconfig.json | 19 + frontend/webpack.config.js | 62 + frontend/yarn.lock | 13329 ++++++++++++++++ jenkins/backend-dev.jenkinsfile | 64 + jenkins/backend-prod.jenkinsfile | 48 + jenkins/frontend-dev.jenkinsfile | 53 + jenkins/frontend-prod.jenkinsfile | 53 + 362 files changed, 35160 insertions(+), 34 deletions(-) create mode 100644 .github/workflows/backend-ci.yml create mode 100644 .github/workflows/frontend-ci.yml create mode 100644 .gitmodules create mode 100644 backend/src/docs/asciidoc/auth.adoc create mode 100644 backend/src/docs/asciidoc/category.adoc create mode 100644 backend/src/docs/asciidoc/external-calendar.adoc create mode 100644 backend/src/docs/asciidoc/index.adoc create mode 100644 backend/src/docs/asciidoc/member.adoc create mode 100644 backend/src/docs/asciidoc/schedule.adoc create mode 100644 backend/src/docs/asciidoc/subscription.adoc create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/application/AuthService.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/application/JwtTokenProvider.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/application/OAuthClient.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/application/OAuthUri.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/application/TokenProvider.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/domain/OAuthToken.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/domain/OAuthTokenRepository.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/dto/LoginMember.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/dto/OAuthMember.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/dto/request/TokenRequest.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/dto/response/OAuthAccessTokenResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/dto/response/OAuthUriResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/dto/response/TokenResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/exception/EmptyAuthorizationHeaderException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/exception/InvalidTokenException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/exception/NoPermissionException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/auth/exception/NoSuchOAuthTokenException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/application/CategoryService.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/domain/Category.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/domain/CategoryRepository.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/domain/CategoryType.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetail.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetailRepository.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/dto/request/CategoryCreateRequest.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/dto/request/CategoryUpdateRequest.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/dto/request/ExternalCategoryCreateRequest.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/dto/response/CategoriesResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/dto/response/CategoryResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/exception/DuplicatedExternalCategoryException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/exception/InvalidCategoryException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/exception/NoSuchCategoryException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/category/exception/NoSuchExternalCategoryDetailException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/common/BaseEntity.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/composition/application/CalendarService.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/composition/application/CategorySubscriptionService.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/composition/application/RegisterService.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/composition/application/SchedulerService.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarClient.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarService.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/externalcalendar/dto/ExternalCalendar.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/externalcalendar/dto/ExternalCalendarsResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDao.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedule.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleComparator.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedules.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/Period.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/ScheduleType.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/TypedSchedules.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/member/application/MemberService.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/member/domain/Member.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/member/domain/MemberRepository.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/member/domain/SocialType.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/member/dto/MemberResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/member/dto/MemberUpdateRequest.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/member/exception/InvalidMemberException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/member/exception/NoSuchMemberException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/schedule/application/ScheduleService.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/schedule/domain/Schedule.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/schedule/domain/ScheduleRepository.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/schedule/domain/scheduler/Scheduler.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/DateRangeRequest.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/ScheduleCreateRequest.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/ScheduleUpdateRequest.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponses.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/PeriodResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/ScheduleResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/schedule/exception/InvalidScheduleException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/schedule/exception/NoSuchScheduleException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/subscription/application/SubscriptionService.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/subscription/domain/Color.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/subscription/domain/ColorPickerStrategy.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/subscription/domain/Subscription.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepository.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/subscription/dto/request/SubscriptionUpdateRequest.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/subscription/dto/response/SubscriptionResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/subscription/dto/response/SubscriptionsResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/subscription/exception/ExistSubscriptionException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/subscription/exception/InvalidSubscriptionException.java create mode 100644 backend/src/main/java/com/allog/dallog/domain/subscription/exception/NoSuchSubscriptionException.java create mode 100644 backend/src/main/java/com/allog/dallog/global/config/JpaConfig.java create mode 100644 backend/src/main/java/com/allog/dallog/global/config/PropertiesConfig.java create mode 100644 backend/src/main/java/com/allog/dallog/global/config/WebConfig.java create mode 100644 backend/src/main/java/com/allog/dallog/global/config/properties/GoogleProperties.java create mode 100644 backend/src/main/java/com/allog/dallog/global/error/ControllerAdvice.java create mode 100644 backend/src/main/java/com/allog/dallog/global/error/dto/ErrorReportRequest.java create mode 100644 backend/src/main/java/com/allog/dallog/global/error/dto/ErrorResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/log/DiscordAppender.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/log/dto/DiscordWebhookRequest.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Embed.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Field.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleExternalCalendarClient.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleOAuthClient.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventsResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarListResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleDateFormat.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleTokenResponse.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/UserInfo.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/oauth/exception/OAuthException.java create mode 100644 backend/src/main/java/com/allog/dallog/infrastructure/oauth/uri/GoogleOAuthUri.java create mode 100644 backend/src/main/java/com/allog/dallog/presentation/CategoryController.java create mode 100644 backend/src/main/java/com/allog/dallog/presentation/ExternalCalendarController.java create mode 100644 backend/src/main/java/com/allog/dallog/presentation/MemberController.java create mode 100644 backend/src/main/java/com/allog/dallog/presentation/ScheduleController.java create mode 100644 backend/src/main/java/com/allog/dallog/presentation/SchedulerController.java create mode 100644 backend/src/main/java/com/allog/dallog/presentation/SubscriptionController.java create mode 100644 backend/src/main/java/com/allog/dallog/presentation/auth/AuthController.java create mode 100644 backend/src/main/java/com/allog/dallog/presentation/auth/AuthenticationPrincipal.java create mode 100644 backend/src/main/java/com/allog/dallog/presentation/auth/AuthenticationPrincipalArgumentResolver.java create mode 100644 backend/src/main/java/com/allog/dallog/presentation/auth/AuthorizationExtractor.java delete mode 100644 backend/src/main/resources/application.properties create mode 160000 backend/src/main/resources/config create mode 100644 backend/src/main/resources/db/prod/schema.sql create mode 100644 backend/src/main/resources/logback-spring.xml delete mode 100644 backend/src/test/java/com/allog/dallog/DallogApplicationTests.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/AcceptanceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/AuthAcceptanceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/CategoryAcceptanceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/ExternalCalendarAcceptanceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/MemberAcceptanceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/ScheduleAcceptanceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/SchedulerAcceptanceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/SubscriptionAcceptanceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/fixtures/AuthAcceptanceFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/fixtures/CategoryAcceptanceFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/fixtures/CommonAcceptanceFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/fixtures/MemberAcceptanceFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/fixtures/ScheduleAcceptanceFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/acceptance/fixtures/SubscriptionAcceptanceFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/common/DatabaseCleaner.java create mode 100644 backend/src/test/java/com/allog/dallog/common/annotation/RepositoryTest.java create mode 100644 backend/src/test/java/com/allog/dallog/common/annotation/ServiceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/common/config/ExternalApiConfig.java create mode 100644 backend/src/test/java/com/allog/dallog/common/config/TokenConfig.java create mode 100644 backend/src/test/java/com/allog/dallog/common/fixtures/AuthFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/common/fixtures/CategoryFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/common/fixtures/ExternalCalendarFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/common/fixtures/ExternalCategoryFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/common/fixtures/IntegrationScheduleFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/common/fixtures/MemberFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/common/fixtures/OAuthTokenFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/common/fixtures/ScheduleFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/common/fixtures/SubscriptionFixtures.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/auth/application/AuthServiceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/auth/application/JwtTokenProviderTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/auth/application/StubTokenProvider.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/auth/domain/OAuthTokenRepositoryTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/auth/domain/OAuthTokenTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/category/application/CategoryServiceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryRepositoryTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryTypeTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/composition/application/CalendarServiceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/composition/application/CategorySubscriptionServiceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/composition/application/RegisterServiceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/composition/application/SchedulerServiceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarServiceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDaoTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedulesTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/PeriodTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/member/application/MemberServiceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/member/domain/MemberRepositoryTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/member/domain/MemberTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/schedule/application/ScheduleServiceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/schedule/domain/ScheduleRepositoryTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/schedule/domain/ScheduleTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/schedule/domain/scheduler/SchedulerTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/subscription/application/SubscriptionServiceTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/subscription/domain/ColorTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepositoryTest.java create mode 100644 backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionTest.java create mode 100644 backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubExternalCalendarClient.java create mode 100644 backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubOAuthClient.java create mode 100644 backend/src/test/java/com/allog/dallog/presentation/AuthControllerTest.java create mode 100644 backend/src/test/java/com/allog/dallog/presentation/CategoryControllerTest.java create mode 100644 backend/src/test/java/com/allog/dallog/presentation/ControllerTest.java create mode 100644 backend/src/test/java/com/allog/dallog/presentation/ExternalCalendarControllerTest.java create mode 100644 backend/src/test/java/com/allog/dallog/presentation/MemberControllerTest.java create mode 100644 backend/src/test/java/com/allog/dallog/presentation/ScheduleControllerTest.java create mode 100644 backend/src/test/java/com/allog/dallog/presentation/SchedulerControllerTest.java create mode 100644 backend/src/test/java/com/allog/dallog/presentation/SubscriptionControllerTest.java create mode 100644 backend/src/test/resources/application.yml create mode 100644 backend/~/Desktop/jacoco/index.xml create mode 100644 frontend/.eslintrc.json create mode 100644 frontend/.gitignore create mode 100644 frontend/.prettierrc.json create mode 100644 frontend/.storybook/main.js create mode 100644 frontend/.storybook/preview-body.html create mode 100644 frontend/.storybook/preview.js create mode 100644 frontend/babel.config.js create mode 100644 frontend/jest.config.js create mode 100644 frontend/package.json create mode 100644 frontend/public/mockServiceWorker.js create mode 100644 frontend/setupFile.js create mode 100644 frontend/src/@types/calendar.ts create mode 100644 frontend/src/@types/category.ts create mode 100644 frontend/src/@types/custom.d.ts create mode 100644 frontend/src/@types/emotion.d.ts create mode 100644 frontend/src/@types/googleCalendar.ts create mode 100644 frontend/src/@types/index.ts create mode 100644 frontend/src/@types/profile.ts create mode 100644 frontend/src/@types/schedule.ts create mode 100644 frontend/src/@types/scheduler.ts create mode 100644 frontend/src/@types/subscription.ts create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/api/category.ts create mode 100644 frontend/src/api/googleCalendar.ts create mode 100644 frontend/src/api/index.ts create mode 100644 frontend/src/api/login.ts create mode 100644 frontend/src/api/profile.ts create mode 100644 frontend/src/api/schedule.ts create mode 100644 frontend/src/api/scheduler.ts create mode 100644 frontend/src/api/subscription.ts create mode 100644 frontend/src/assets/dallog_black.png create mode 100644 frontend/src/assets/dallog_color.png create mode 100644 frontend/src/assets/dallog_white.png create mode 100644 frontend/src/assets/how_to_use_1.png create mode 100644 frontend/src/assets/how_to_use_2.png create mode 100644 frontend/src/assets/how_to_use_3.png create mode 100644 frontend/src/components/@common/Button/Button.stories.tsx create mode 100644 frontend/src/components/@common/Button/Button.styles.ts create mode 100644 frontend/src/components/@common/Button/Button.test.tsx create mode 100644 frontend/src/components/@common/Button/Button.tsx create mode 100644 frontend/src/components/@common/ErrorBoundary/ErrorBoundary.tsx create mode 100644 frontend/src/components/@common/Fieldset/Fieldset.stories.tsx create mode 100644 frontend/src/components/@common/Fieldset/Fieldset.styles.ts create mode 100644 frontend/src/components/@common/Fieldset/Fieldset.test.tsx create mode 100644 frontend/src/components/@common/Fieldset/Fieldset.tsx create mode 100644 frontend/src/components/@common/ModalPortal/ModalPortal.styles.ts create mode 100644 frontend/src/components/@common/ModalPortal/ModalPortal.tsx create mode 100644 frontend/src/components/@common/PageLayout/PageLayout.styles.ts create mode 100644 frontend/src/components/@common/PageLayout/PageLayout.tsx create mode 100644 frontend/src/components/@common/Skeleton/Skeleton.stories.tsx create mode 100644 frontend/src/components/@common/Skeleton/Skeleton.styles.ts create mode 100644 frontend/src/components/@common/Skeleton/Skeleton.tsx create mode 100644 frontend/src/components/@common/Spinner/Spinner.stories.tsx create mode 100644 frontend/src/components/@common/Spinner/Spinner.styles.ts create mode 100644 frontend/src/components/@common/Spinner/Spinner.tsx create mode 100644 frontend/src/components/CategoryAddModal/CategoryAddModal.stories.tsx create mode 100644 frontend/src/components/CategoryAddModal/CategoryAddModal.styles.ts create mode 100644 frontend/src/components/CategoryAddModal/CategoryAddModal.tsx create mode 100644 frontend/src/components/CategoryList/CategoryList.fallback.stories.tsx create mode 100644 frontend/src/components/CategoryList/CategoryList.fallback.tsx create mode 100644 frontend/src/components/CategoryList/CategoryList.stories.tsx create mode 100644 frontend/src/components/CategoryList/CategoryList.styles.ts create mode 100644 frontend/src/components/CategoryList/CategoryList.tsx create mode 100644 frontend/src/components/CategoryModifyModal/CategoryModifyModal.styles.ts create mode 100644 frontend/src/components/CategoryModifyModal/CategoryModifyModal.tsx create mode 100644 frontend/src/components/DateModal/DateModal.styles.ts create mode 100644 frontend/src/components/DateModal/DateModal.tsx create mode 100644 frontend/src/components/FilterCategoryItem/FilterCategoryItem.styles.ts create mode 100644 frontend/src/components/FilterCategoryItem/FilterCategoryItem.tsx create mode 100644 frontend/src/components/FilterCategoryList/FilterCategoryList.fallback.stories.tsx create mode 100644 frontend/src/components/FilterCategoryList/FilterCategoryList.fallback.tsx create mode 100644 frontend/src/components/FilterCategoryList/FilterCategoryList.styles.ts create mode 100644 frontend/src/components/FilterCategoryList/FilterCategoryList.tsx create mode 100644 frontend/src/components/Footer/Footer.styles.ts create mode 100644 frontend/src/components/Footer/Footer.tsx create mode 100644 frontend/src/components/GoogleImportModal/GoogleImportModal.styles.ts create mode 100644 frontend/src/components/GoogleImportModal/GoogleImportModal.tsx create mode 100644 frontend/src/components/MyCategoryItem/MyCategoryItem.style.ts create mode 100644 frontend/src/components/MyCategoryItem/MyCategoryItem.tsx create mode 100644 frontend/src/components/MyCategoryList/MyCategoryList.fallback.tsx create mode 100644 frontend/src/components/MyCategoryList/MyCategoryList.styles.ts create mode 100644 frontend/src/components/MyCategoryList/MyCategoryList.tsx create mode 100644 frontend/src/components/NavBar/NavBar.stories.tsx create mode 100644 frontend/src/components/NavBar/NavBar.styles.ts create mode 100644 frontend/src/components/NavBar/NavBar.tsx create mode 100644 frontend/src/components/Profile/Profile.fallback.stories.tsx create mode 100644 frontend/src/components/Profile/Profile.fallback.tsx create mode 100644 frontend/src/components/Profile/Profile.styles.ts create mode 100644 frontend/src/components/Profile/Profile.tsx create mode 100644 frontend/src/components/ProtectRoute/ProtectRoute.tsx create mode 100644 frontend/src/components/PublicRoute/PublicRoute.tsx create mode 100644 frontend/src/components/ScheduleAddButton/ScheduleAddButton.stories.tsx create mode 100644 frontend/src/components/ScheduleAddButton/ScheduleAddButton.styles.ts create mode 100644 frontend/src/components/ScheduleAddButton/ScheduleAddButton.tsx create mode 100644 frontend/src/components/ScheduleAddModal/ScheduleAddModal.stories.tsx create mode 100644 frontend/src/components/ScheduleAddModal/ScheduleAddModal.styles.ts create mode 100644 frontend/src/components/ScheduleAddModal/ScheduleAddModal.tsx create mode 100644 frontend/src/components/ScheduleModal/ScheduleModal.styles.ts create mode 100644 frontend/src/components/ScheduleModal/ScheduleModal.tsx create mode 100644 frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.styles.ts create mode 100644 frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.tsx create mode 100644 frontend/src/components/SideBar/SideBar.styles.ts create mode 100644 frontend/src/components/SideBar/SideBar.tsx create mode 100644 frontend/src/components/SnackBar/SnackBar.styles.ts create mode 100644 frontend/src/components/SnackBar/SnackBar.tsx create mode 100644 frontend/src/components/SubscribedCategoryItem/SubscribedCategoryItem.styles.ts create mode 100644 frontend/src/components/SubscribedCategoryItem/SubscribedCategoryItem.tsx create mode 100644 frontend/src/components/UnsubscribedCategoryItem/UnsubscribedCategoryItem.styles.ts create mode 100644 frontend/src/components/UnsubscribedCategoryItem/UnsubscribedCategoryItem.tsx create mode 100644 frontend/src/constants/api.ts create mode 100644 frontend/src/constants/category.ts create mode 100644 frontend/src/constants/date.ts create mode 100644 frontend/src/constants/index.ts create mode 100644 frontend/src/constants/message.ts create mode 100644 frontend/src/constants/style.ts create mode 100644 frontend/src/constants/validate.ts create mode 100644 frontend/src/hooks/useCalendar.ts create mode 100644 frontend/src/hooks/useControlledInput.ts create mode 100644 frontend/src/hooks/useIntersect.ts create mode 100644 frontend/src/hooks/useSchedulePriority.ts create mode 100644 frontend/src/hooks/useSnackBar.ts create mode 100644 frontend/src/hooks/useToggle.ts create mode 100644 frontend/src/hooks/useUserValue.ts create mode 100644 frontend/src/hooks/useValidateCategory.ts create mode 100644 frontend/src/hooks/useValidateSchedule.ts create mode 100644 frontend/src/index.html create mode 100644 frontend/src/index.tsx create mode 100644 frontend/src/mocks/browser.ts create mode 100644 frontend/src/mocks/data.ts create mode 100644 frontend/src/mocks/handlers.ts create mode 100644 frontend/src/pages/AuthPage/AuthPage.tsx create mode 100644 frontend/src/pages/CalendarPage/CalendarPage.styles.ts create mode 100644 frontend/src/pages/CalendarPage/CalendarPage.tsx create mode 100644 frontend/src/pages/CategoryPage/CategoryPage.styles.ts create mode 100644 frontend/src/pages/CategoryPage/CategoryPage.tsx create mode 100644 frontend/src/pages/MainPage/MainPage.tsx create mode 100644 frontend/src/pages/NotFoundPage/NotFoundPage.styles.ts create mode 100644 frontend/src/pages/NotFoundPage/NotFoundPage.tsx create mode 100644 frontend/src/pages/PrivacyPolicyPage/PrivacyPolicyPage.styles.ts create mode 100644 frontend/src/pages/PrivacyPolicyPage/PrivacyPolicyPage.tsx create mode 100644 frontend/src/pages/SchedulingPage/SchedulingPage.styles.ts create mode 100644 frontend/src/pages/SchedulingPage/SchedulingPage.tsx create mode 100644 frontend/src/pages/StartPage/StartPage.styles.ts create mode 100644 frontend/src/pages/StartPage/StartPage.tsx create mode 100644 frontend/src/recoil/atoms/index.ts create mode 100644 frontend/src/recoil/selectors/index.ts create mode 100644 frontend/src/styles/GlobalStyle.tsx create mode 100644 frontend/src/styles/theme.ts create mode 100644 frontend/src/utils/date.ts create mode 100644 frontend/src/utils/index.ts create mode 100644 frontend/src/utils/storage.ts create mode 100644 frontend/src/validation/index.ts create mode 100644 frontend/tsconfig.json create mode 100644 frontend/webpack.config.js create mode 100644 frontend/yarn.lock create mode 100644 jenkins/backend-dev.jenkinsfile create mode 100644 jenkins/backend-prod.jenkinsfile create mode 100644 jenkins/frontend-dev.jenkinsfile create mode 100644 jenkins/frontend-prod.jenkinsfile diff --git a/.github/ISSUE_TEMPLATE/bug-template.md b/.github/ISSUE_TEMPLATE/bug-template.md index 0c0d4d81..baf919c0 100644 --- a/.github/ISSUE_TEMPLATE/bug-template.md +++ b/.github/ISSUE_TEMPLATE/bug-template.md @@ -1,7 +1,7 @@ --- name: Bug Template about: 버그를 이슈에 등록한다. -title: "" +title: '이슈의 제목을 입력해주세요!' labels: '' assignees: '' --- @@ -16,4 +16,3 @@ assignees: '' ## 📸 스크린샷 ## 👄 참고 사항 - diff --git a/.github/ISSUE_TEMPLATE/feature-template.md b/.github/ISSUE_TEMPLATE/feature-template.md index 752e91d4..b34d74cb 100644 --- a/.github/ISSUE_TEMPLATE/feature-template.md +++ b/.github/ISSUE_TEMPLATE/feature-template.md @@ -1,7 +1,7 @@ --- name: Feature Template about: 구현할 기능을 이슈에 등록한다. -title: "" +title: '이슈의 제목을 입력해주세요!' labels: '' assignees: '' --- @@ -17,4 +17,3 @@ assignees: '' ## 📄 참고 사항 ## ⏰ 예상 소요 기간 - diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3af222a0..e5f724db 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,10 @@ +- [ ] 🔀 PR 제목의 형식을 잘 작성했나요? e.g. `[feat] PR을 등록한다.` - [ ] 💯 테스트는 잘 통과했나요? - [ ] 🏗️ 빌드는 성공했나요? - [ ] 🧹 불필요한 코드는 제거했나요? - [ ] 💭 이슈는 등록했나요? - [ ] 🏷️ 라벨은 등록했나요? +- [ ] 💻 git rebase를 사용했나요? - [ ] 🌈 알록달록한가요? ## 작업 내용 @@ -12,4 +14,3 @@ ## 주의사항 Closes #{이슈 번호} - diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml new file mode 100644 index 00000000..8d58dd5e --- /dev/null +++ b/.github/workflows/backend-ci.yml @@ -0,0 +1,39 @@ +name: Backend CI + +on: + pull_request: + branches: + - main + - develop + paths: + - backend/** + - .github/** # Github Actions 작업을 위한 포함 + +jobs: + build: + runs-on: ubuntu-latest + + defaults: + run: + working-directory: ./backend + + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK 11 + uses: actions/setup-java@v3 + with: + java-version: "11" + distribution: "temurin" + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew build + + - name: Publish Unit Test Results + uses: EnricoMi/publish-unit-test-result-action@v1 + if: ${{ always() }} + with: + files: build/test-results/**/*.xml diff --git a/.github/workflows/frontend-ci.yml b/.github/workflows/frontend-ci.yml new file mode 100644 index 00000000..81475110 --- /dev/null +++ b/.github/workflows/frontend-ci.yml @@ -0,0 +1,38 @@ +name: Frontend CI + +on: + pull_request: + branches: + - main + - develop + paths: + - frontend/** + - .github/** # Github Actions 작업을 위한 포함 + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./frontend + + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v2 + with: + node-version: "14" + + - name: Install node packages + run: npm install + + - name: Check lint + run: npm run check-lint + + - name: Check prettier + run: npm run check-prettier + + - name: Build + run: npm run dev-build + + - name: Component test + run: npm run test diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..4f39dc5d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "backend/src/main/resources/config"] + path = backend/src/main/resources/config + url = https://github.com/dallog/config.git diff --git a/README.md b/README.md index 098cf0a2..51809af0 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,8 @@ # 2022-dallog +## 알록달록🌈 팀원 소개 + +| Backend | Backend | Backend | Backend | Frontend | Frontend | +| :------------------------------------------: | :------------------------------------------------: | :----------------------------------------------: | :------------------------------------------: | :--------------------------------------------: | :-----------------------------------------: | +| ![](https://github.com/hyeonic.png?size=120) | ![](https://github.com/gudonghee2000.png?size=120) | ![](https://github.com/summerlunaa.png?size=120) | ![](https://github.com/devHudi.png?size=120) | ![](https://github.com/daaaayeah.png?size=120) | ![](https://github.com/jhy979.png?size=120) | +| [매트(최기현)](https://github.com/hyeonic) | [리버(구동희)](https://github.com/gudonghee2000) | [파랑(이하은)](https://github.com/summerlunaa) | [후디(조동현)](https://github.com/devHudi) | [티거(이다예)](https://github.com/daaaayeah) | [나인(장호영)](https://github.com/jhy979) | diff --git a/backend/.gitignore b/backend/.gitignore index c2065bc2..dcd2aed6 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -35,3 +35,9 @@ out/ ### VS Code ### .vscode/ + +### resources ### +/src/main/resources/static/docs + +### logs ### +logs/ diff --git a/backend/build.gradle b/backend/build.gradle index fc7408c2..7f26c83a 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -1,25 +1,154 @@ plugins { - id 'org.springframework.boot' version '2.7.1' - id 'io.spring.dependency-management' version '1.0.11.RELEASE' - id 'java' + id 'org.springframework.boot' version '2.7.1' + id 'io.spring.dependency-management' version '1.0.11.RELEASE' + id 'org.asciidoctor.jvm.convert' version '3.3.2' + id 'org.sonarqube' version '3.3' + id 'java' + id 'jacoco' } group = 'com.allog' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' +ext { + snippetsDir = file('build/generated-snippets') +} + +configurations { + asciidoctorExtensions +} + repositories { - mavenCentral() + mavenCentral() } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - implementation 'org.springframework.boot:spring-boot-starter-validation' - implementation 'org.springframework.boot:spring-boot-starter-web' - runtimeOnly 'com.h2database:h2' - testImplementation 'org.springframework.boot:spring-boot-starter-test' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-validation' + implementation 'org.springframework.boot:spring-boot-starter-web' + + runtimeOnly 'mysql:mysql-connector-java' + runtimeOnly 'com.h2database:h2' + + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'io.rest-assured:rest-assured:4.4.0' + + // JWT를 위한 의존성 + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + + // Restdocs를 위한 의존성 + asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor' + testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' + + // Sonarqube를 위한 의존성 + implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.3' +} + +test { + outputs.dir snippetsDir + useJUnitPlatform() + finalizedBy 'jacocoTestReport' +} + +jacoco { + toolVersion = "0.8.8" +} + +jacocoTestReport { + reports { + xml.enabled true + csv.enabled true + html.enabled true + + xml.destination file("${buildDir}/jacoco/index.xml") + csv.destination file("${buildDir}/jacoco/index.csv") + html.destination file("${buildDir}/jacoco/index.html") + } + + afterEvaluate { + classDirectories.setFrom( + files(classDirectories.files.collect { + fileTree(dir: it, excludes: [ + '**/*Application*', + '**/*Exception*', + '**/dto/**', + '**/infrastructure/**', + '**/global/config/**', + '**/BaseEntity*', + '**/ControllerAdvice*', + '**/AuthorizationExtractor*' + ]) + }) + ) + } + + finalizedBy 'jacocoTestCoverageVerification' +} + +jacocoTestCoverageVerification { + violationRules { + rule { + enabled = true + element = "CLASS" + + // 모든 클래스 각각 라인 커버리지 75% 만족시 빌드 성공 + limit { + counter = 'LINE' + value = 'COVEREDRATIO' + minimum = 0.75 + } + + excludes = [ + '*.*Application', + '*.*Exception', + '*.dto.*', + '*.infrastructure.*', + '*.global.config.*', + '*.BaseEntity', + '*.ControllerAdvice', + '*.AuthorizationExtractor' + ] + } + } +} + +sonarqube { + properties { + property 'sonar.projectKey', 'dallog' + property "sonar.sources", "src" + property "sonar.language", "java" + property "sonar.sourceEncoding", "UTF-8" + property "sonar.profile", "Sonar way" + property "sonar.java.binaries", "${buildDir}/classes" + property "sonar.test.inclusions", "**/*Test.java" + property 'sonar.exclusions', '**/jacoco/**' + property 'sonar.coverage.exclusions', '**/test/**/*, **/*Application*, **/global/config/**, **/dto/**, **/*Exception*, **/infrastructure/**, **/BaseEntity*, **/ControllerAdvice*, **/AuthorizationExtractor*' + property "sonar.coverage.jacoco.xmlReportPaths", "${buildDir}/jacoco/index.xml" + } +} + +asciidoctor { + configurations 'asciidoctorExtensions' + baseDirFollowsSourceFile() + inputs.dir snippetsDir + dependsOn test +} + +asciidoctor.doFirst { + delete file('src/main/resources/static/docs') +} + +task copyDocument(type: Copy) { + dependsOn asciidoctor + + from "${asciidoctor.outputDir}" + into file("src/main/resources/static/docs") } -tasks.named('test') { - useJUnitPlatform() +bootJar { + dependsOn copyDocument } diff --git a/backend/src/docs/asciidoc/auth.adoc b/backend/src/docs/asciidoc/auth.adoc new file mode 100644 index 00000000..c785188a --- /dev/null +++ b/backend/src/docs/asciidoc/auth.adoc @@ -0,0 +1,59 @@ +== Auth(인증) + +=== OAuth 로그인 링크 생성 + +==== Request + +include::{snippets}/auth/link/http-request.adoc[] + +==== Path Parameters + +include::{snippets}/auth/link/path-parameters.adoc[] + +==== Request Parameters + +include::{snippets}/auth/link/request-parameters.adoc[] + +==== Response + +include::{snippets}/auth/link/http-response.adoc[] + +==== ResponseFields + +include::{snippets}/auth/link/response-fields.adoc[] + +=== OAuth 로그인 + +==== Request + +include::{snippets}/auth/token/http-request.adoc[] + +==== PathParameters + +include::{snippets}/auth/token/path-parameters.adoc[] + +==== RequestFields + +include::{snippets}/auth/token/request-fields.adoc[] + +==== Response + +include::{snippets}/auth/token/http-response.adoc[] + +=== OAuth 로그인 : Resource Server 에러 + +==== Request + +include::{snippets}/auth/exception/token/http-request.adoc[] + +==== PathParameters + +include::{snippets}/auth/exception/token/path-parameters.adoc[] + +==== RequestFields + +include::{snippets}/auth/exception/token/request-fields.adoc[] + +==== Response + +include::{snippets}/auth/exception/token/http-response.adoc[] diff --git a/backend/src/docs/asciidoc/category.adoc b/backend/src/docs/asciidoc/category.adoc new file mode 100644 index 00000000..91f9ceed --- /dev/null +++ b/backend/src/docs/asciidoc/category.adoc @@ -0,0 +1,145 @@ +== Category(카테고리) + +=== 카테고리 생성 + +==== Request + +include::{snippets}/categories/save/http-request.adoc[] + +==== Response + +include::{snippets}/categories/save/http-response.adoc[] + +=== 카테고리 생성 (유효하지 않은 카테고리 이름) + +==== Request + +include::{snippets}/categories/save/badRequest/http-request.adoc[] + +==== Response + +include::{snippets}/categories/save/badRequest/http-response.adoc[] + +=== 전체 카테고리 조회 + +==== Request + +include::{snippets}/categories/findAll/http-request.adoc[] + +==== RequestParameters + +include::{snippets}/categories/findAll/request-parameters.adoc[] + +==== Response + +include::{snippets}/categories/findAll/http-response.adoc[] + +=== 카테고리 제목 조회 + +==== Request + +include::{snippets}/categories/findAllLikeName/http-request.adoc[] + +==== RequestParameters + +include::{snippets}/categories/findAllLikeName/request-parameters.adoc[] + +==== Response + +include::{snippets}/categories/findAllLikeName/http-response.adoc[] + +=== 자신이 생성한 카테고리 조회 + +==== Request + +include::{snippets}/categories/findMine/http-request.adoc[] + +==== RequestParameters + +include::{snippets}/categories/findMine/request-parameters.adoc[] + +==== Response + +include::{snippets}/categories/findMine/response-body.adoc[] + +=== ID를 통한 카테고리 단건 조회 + +==== Request + +include::{snippets}/categories/findById/http-request.adoc[] + +==== PathParameters + +include::{snippets}/categories/findById/path-parameters.adoc[] + +==== Response + +include::{snippets}/categories/findById/http-response.adoc[] + +=== ID를 통한 카테고리 단건 조회 (존재하지 않는 경우) + +==== Request + +include::{snippets}/categories/findById/notFound/http-request.adoc[] + +==== Response + +include::{snippets}/categories/findById/notFound/http-response.adoc[] + +=== 카테고리 수정 + +==== Request + +include::{snippets}/categories/update/http-request.adoc[] + +==== PathParameters + +include::{snippets}/categories/update/path-parameters.adoc[] + +==== Response + +include::{snippets}/categories/update/http-response.adoc[] + +=== 카테고리 수정 (존재하지 않는 경우) + +==== Request + +include::{snippets}/categories/update/notFound/http-request.adoc[] + +==== Response + +include::{snippets}/categories/update/notFound/http-response.adoc[] + +=== 카테고리 수정 (유효하지 않은 카테고리 이름) + +==== Request + +include::{snippets}/categories/update/badRequest/http-request.adoc[] + +==== Response + +include::{snippets}/categories/update/badRequest/http-response.adoc[] + +=== 카테고리 삭제 + +==== Request + +include::{snippets}/categories/delete/http-request.adoc[] + +==== PathParameters + +include::{snippets}/categories/delete/path-parameters.adoc[] + +==== Response + +include::{snippets}/categories/delete/http-response.adoc[] + +=== 카테고리 삭제 (존재하지 않는 경우) + +==== Request + +include::{snippets}/categories/delete/notFound/http-request.adoc[] + +==== Response + +include::{snippets}/categories/delete/notFound/http-response.adoc[] diff --git a/backend/src/docs/asciidoc/external-calendar.adoc b/backend/src/docs/asciidoc/external-calendar.adoc new file mode 100644 index 00000000..72e3bc0b --- /dev/null +++ b/backend/src/docs/asciidoc/external-calendar.adoc @@ -0,0 +1,47 @@ +== External Calendar (외부 캘린더) + +=== 자신의 외부 캘린더 조회 + +==== Request + +include::{snippets}/external-calendars/get/http-request.adoc[] + +==== Response + +include::{snippets}/external-calendars/get/http-response.adoc[] + +=== 자신의 외부 캘린더 저장 + +==== Request + +include::{snippets}/external-calendars/save/http-request.adoc[] + +==== Request Headers + +include::{snippets}/external-calendars/save/request-headers.adoc[] + +==== Request Body + +include::{snippets}/external-calendars/save/request-fields.adoc[] + +==== Response + +include::{snippets}/external-calendars/save/http-response.adoc[] + +=== 자신의 외부 캘린더 중복 저장 시 예외 발생 + +==== Request + +include::{snippets}/external-calendars/duplicated-save/http-request.adoc[] + +==== Request Headers + +include::{snippets}/external-calendars/duplicated-save/request-headers.adoc[] + +==== Request Body + +include::{snippets}/external-calendars/duplicated-save/request-fields.adoc[] + +==== Response + +include::{snippets}/external-calendars/duplicated-save/http-response.adoc[] diff --git a/backend/src/docs/asciidoc/index.adoc b/backend/src/docs/asciidoc/index.adoc new file mode 100644 index 00000000..c7357dcc --- /dev/null +++ b/backend/src/docs/asciidoc/index.adoc @@ -0,0 +1,13 @@ += 달록 API 문서 +:doctype: book +:icons: font +:source-highlighter: highlightjs +:toc: left +:toclevels: 3 + +include::auth.adoc[] +include::member.adoc[] +include::category.adoc[] +include::schedule.adoc[] +include::subscription.adoc[] +include::external-calendar.adoc[] diff --git a/backend/src/docs/asciidoc/member.adoc b/backend/src/docs/asciidoc/member.adoc new file mode 100644 index 00000000..ece68ebb --- /dev/null +++ b/backend/src/docs/asciidoc/member.adoc @@ -0,0 +1,53 @@ +== Member(회원) + +=== 내 정보 조회 + +==== Request + +include::{snippets}/members/me/http-request.adoc[] + +==== RequestHeaders + +include::{snippets}/members/me/request-headers.adoc[] + +==== Response + +include::{snippets}/members/me/http-response.adoc[] + +=== 삭제된 회원 정보 조회 + +==== Request + +include::{snippets}/members/exception/notfound/http-request.adoc[] + +==== Response + +include::{snippets}/members/exception/notfound/http-response.adoc[] + +=== 내 정보 수정 + +==== Request + +include::{snippets}/members/update/http-request.adoc[] + +==== RequestHeaders + +include::{snippets}/members/update/request-headers.adoc[] + +==== Request Parameters + +include::{snippets}/members/update/request-body.adoc[] + +==== Response + +include::{snippets}/members/update/http-response.adoc[] + +=== 회원 탈퇴 + +==== Request + +include::{snippets}/members/delete/http-request.adoc[] + +==== Response + +include::{snippets}/members/delete/http-response.adoc[] diff --git a/backend/src/docs/asciidoc/schedule.adoc b/backend/src/docs/asciidoc/schedule.adoc new file mode 100644 index 00000000..46ea60b8 --- /dev/null +++ b/backend/src/docs/asciidoc/schedule.adoc @@ -0,0 +1,137 @@ +== Schedule(일정) + +=== 회원 일정 목록 조회 + +==== Request + +include::{snippets}/schedules/findAllByMember/http-request.adoc[] + +==== RequestParameters + +include::{snippets}/schedules/findAllByMember/request-parameters.adoc[] + +==== Response + +include::{snippets}/schedules/findAllByMember/http-response.adoc[] + +=== 회원 일정 및 외부 일정 목록 조회 + +==== Request + +include::{snippets}/schedules/findAllByMemberWithExternalSchedule/http-request.adoc[] + +==== RequestParameters + +include::{snippets}/schedules/findAllByMemberWithExternalSchedule/request-parameters.adoc[] + +==== Response + +include::{snippets}/schedules/findAllByMemberWithExternalSchedule/http-response.adoc[] + +=== 일정 등록 + +==== Request + +include::{snippets}/schedules/save/http-request.adoc[] + +==== Response + +include::{snippets}/schedules/save/http-response.adoc[] + +=== 일정 생성 (카테고리 권한 없음) + +==== Response + +include::{snippets}/schedules/save/forbidden/http-response.adoc[] + +=== 일정 생성 (카테고리가 존재하지 않음) + +==== Response + +include::{snippets}/schedules/save/notfound/http-response.adoc[] + +=== 일정 단건 조회 + +==== Request + +include::{snippets}/schedules/findone/http-request.adoc[] + +==== Response + +include::{snippets}/schedules/findone/http-response.adoc[] + +=== 일정 단건 조회 (일정이 존재하지 않음) + +==== Response + +include::{snippets}/schedules/findone/notfound/http-response.adoc[] + +=== 일정 수정 + +==== Request + +include::{snippets}/schedules/update/http-request.adoc[] + +==== Path Variable + +include::{snippets}/schedules/update/path-parameters.adoc[] + +==== Response + +include::{snippets}/schedules/update/http-response.adoc[] + +=== 일정 수정 (카테고리 권한 없음) + +==== Response + +include::{snippets}/schedules/update/forbidden/http-response.adoc[] + +=== 일정 수정 (카테고리가 존재하지 않음) + +==== Response + +include::{snippets}/schedules/update/notfound/http-response.adoc[] + +=== 일정 제거 + +==== Request + +include::{snippets}/schedules/delete/http-request.adoc[] + +==== Path Variable + +include::{snippets}/schedules/delete/path-parameters.adoc[] + +==== Response + +include::{snippets}/schedules/delete/http-response.adoc[] + +=== 일정 제거 (카테고리 권한 없음) + +==== Response + +include::{snippets}/schedules/delete/forbidden/http-response.adoc[] + +=== 일정 제거 (카테고리가 존재하지 않음) + +==== Response + +include::{snippets}/schedules/delete/notfound/http-response.adoc[] + +=== 일정 조율 + +카테고리 ID를 전달하면, 해당 카테고리를 구독중인 유저의 모든 체크한 구독 정보를 바탕으로 일정이 없는 시간대를 반환합니다. + +==== Request + +include::{snippets}/scheduler/category/available-periods/http-request.adoc[] + +==== Parameters + +include::{snippets}/scheduler/category/available-periods/path-parameters.adoc[] + +include::{snippets}/scheduler/category/available-periods/request-parameters.adoc[] + +==== Response + +include::{snippets}/scheduler/category/available-periods/http-response.adoc[] diff --git a/backend/src/docs/asciidoc/subscription.adoc b/backend/src/docs/asciidoc/subscription.adoc new file mode 100644 index 00000000..93dbcedc --- /dev/null +++ b/backend/src/docs/asciidoc/subscription.adoc @@ -0,0 +1,123 @@ +== Subscription(구독) + +=== 구독 등록 + +==== Request + +include::{snippets}/subscription/save/http-request.adoc[] + +==== Path Parameters + +include::{snippets}/subscription/save/path-parameters.adoc[] + +==== Request Headers + +include::{snippets}/subscription/save/request-headers.adoc[] + +==== Response + +include::{snippets}/subscription/save/http-response.adoc[] + +=== 자신의 구독 목록 조회 + +==== Request + +include::{snippets}/subscription/findMine/http-request.adoc[] + +==== Response + +include::{snippets}/subscription/findMine/http-response.adoc[] + +=== 중복된 구독 등록 + +==== Request + +include::{snippets}/subscription/exist/http-request.adoc[] + +==== Path Parameters + +include::{snippets}/subscription/exist/path-parameters.adoc[] + +==== Request Headers + +include::{snippets}/subscription/exist/request-headers.adoc[] + +==== Response + +include::{snippets}/subscription/exist/http-response.adoc[] + +==== Request + +include::{snippets}/subscription/me/http-request.adoc[] + +==== Request Headers + +include::{snippets}/subscription/me/request-headers.adoc[] + +==== Response + +include::{snippets}/subscription/me/http-response.adoc[] + +=== 3자의 개인 카테고리 구독 요청시 + +==== Response + +include::{snippets}/subscription/private-category/http-response.adoc[] + +=== 내 구독 정보 수정 + +==== Request + +include::{snippets}/subscription/update/http-request.adoc[] + +==== Path Parameters + +include::{snippets}/subscription/update/path-parameters.adoc[] + +==== Request Headers + +include::{snippets}/subscription/update/request-headers.adoc[] + +==== Request Parameters + +include::{snippets}/subscription/update/request-body.adoc[] + +==== Response + +include::{snippets}/subscription/update/http-response.adoc[] + +=== 내 구독 정보 삭제 + +==== Request + +include::{snippets}/subscription/delete/http-request.adoc[] + +==== Path Parameters + +include::{snippets}/subscription/delete/path-parameters.adoc[] + +==== Request Headers + +include::{snippets}/subscription/delete/request-headers.adoc[] + +==== Response + +include::{snippets}/subscription/delete/http-response.adoc[] + +=== 내 구독 정보가 아닌 구독 삭제 + +==== Request + +include::{snippets}/subscription/permission/http-request.adoc[] + +==== Path Parameters + +include::{snippets}/subscription/permission/path-parameters.adoc[] + +==== Request Headers + +include::{snippets}/subscription/permission/request-headers.adoc[] + +==== Response + +include::{snippets}/subscription/permission/http-response.adoc[] diff --git a/backend/src/main/java/com/allog/dallog/DallogApplication.java b/backend/src/main/java/com/allog/dallog/DallogApplication.java index 12175e2c..0d989070 100644 --- a/backend/src/main/java/com/allog/dallog/DallogApplication.java +++ b/backend/src/main/java/com/allog/dallog/DallogApplication.java @@ -6,8 +6,7 @@ @SpringBootApplication public class DallogApplication { - public static void main(String[] args) { - SpringApplication.run(DallogApplication.class, args); - } - + public static void main(String[] args) { + SpringApplication.run(DallogApplication.class, args); + } } diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/application/AuthService.java b/backend/src/main/java/com/allog/dallog/domain/auth/application/AuthService.java new file mode 100644 index 00000000..fdcb2dd1 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/application/AuthService.java @@ -0,0 +1,79 @@ +package com.allog.dallog.domain.auth.application; + +import com.allog.dallog.domain.auth.domain.OAuthToken; +import com.allog.dallog.domain.auth.domain.OAuthTokenRepository; +import com.allog.dallog.domain.auth.dto.OAuthMember; +import com.allog.dallog.domain.auth.dto.request.TokenRequest; +import com.allog.dallog.domain.auth.dto.response.TokenResponse; +import com.allog.dallog.domain.auth.exception.NoSuchOAuthTokenException; +import com.allog.dallog.domain.composition.application.RegisterService; +import com.allog.dallog.domain.member.application.MemberService; +import com.allog.dallog.domain.member.domain.Member; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(readOnly = true) +@Service +public class AuthService { + + private final OAuthUri oAuthUri; + private final OAuthClient oAuthClient; + private final MemberService memberService; + private final RegisterService registerService; + private final OAuthTokenRepository oAuthTokenRepository; + private final TokenProvider tokenProvider; + + public AuthService(final OAuthUri oAuthUri, final OAuthClient oAuthClient, final MemberService memberService, + final RegisterService registerService, final OAuthTokenRepository oAuthTokenRepository, + final TokenProvider tokenProvider) { + this.oAuthUri = oAuthUri; + this.oAuthClient = oAuthClient; + this.memberService = memberService; + this.registerService = registerService; + this.oAuthTokenRepository = oAuthTokenRepository; + this.tokenProvider = tokenProvider; + } + + public String generateGoogleLink(final String redirectUri) { + return oAuthUri.generate(redirectUri); + } + + @Transactional + public TokenResponse generateToken(final TokenRequest tokenRequest) { + String code = tokenRequest.getCode(); + String redirectUri = tokenRequest.getRedirectUri(); + + OAuthMember oAuthMember = oAuthClient.getOAuthMember(code, redirectUri); + Member foundMember = getMember(oAuthMember); + + OAuthToken oAuthToken = getOAuthToken(oAuthMember, foundMember); + oAuthToken.change(oAuthMember.getRefreshToken()); + String accessToken = tokenProvider.createToken(String.valueOf(foundMember.getId())); + + return new TokenResponse(accessToken); + } + + private Member getMember(final OAuthMember oAuthMember) { + if (!memberService.existsByEmail(oAuthMember.getEmail())) { + registerService.register(oAuthMember); + } + + return memberService.getByEmail(oAuthMember.getEmail()); + } + + private OAuthToken getOAuthToken(final OAuthMember oAuthMember, final Member foundMember) { + if (!oAuthTokenRepository.existsByMemberId(foundMember.getId())) { + oAuthTokenRepository.save(new OAuthToken(foundMember, oAuthMember.getRefreshToken())); + } + + return oAuthTokenRepository.findByMemberId(foundMember.getId()) + .orElseThrow(NoSuchOAuthTokenException::new); + } + + public Long extractMemberId(final String accessToken) { + tokenProvider.validateToken(accessToken); + Long memberId = Long.valueOf(tokenProvider.getPayload(accessToken)); + memberService.validateExistsMember(memberId); + return memberId; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/application/JwtTokenProvider.java b/backend/src/main/java/com/allog/dallog/domain/auth/application/JwtTokenProvider.java new file mode 100644 index 00000000..f6e57cca --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/application/JwtTokenProvider.java @@ -0,0 +1,66 @@ +package com.allog.dallog.domain.auth.application; + +import com.allog.dallog.domain.auth.exception.InvalidTokenException; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import javax.crypto.SecretKey; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class JwtTokenProvider implements TokenProvider { + + private final SecretKey key; + private final long validityInMilliseconds; + + public JwtTokenProvider(@Value("${security.jwt.token.secret-key}") final String secretKey, + @Value("${security.jwt.token.expire-length}") final long validityInMilliseconds) { + this.key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)); + this.validityInMilliseconds = validityInMilliseconds; + } + + @Override + public String createToken(final String payload) { + Date now = new Date(); + Date validity = new Date(now.getTime() + validityInMilliseconds); + + return Jwts.builder() + .setSubject(payload) + .setIssuedAt(now) + .setExpiration(validity) + .signWith(key, SignatureAlgorithm.HS256) + .compact(); + } + + @Override + public String getPayload(final String token) { + return Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token) + .getBody() + .getSubject(); + } + + @Override + public void validateToken(final String token) { + try { + Jws claims = Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token); + + claims.getBody() + .getExpiration() + .before(new Date()); + } catch (JwtException | IllegalArgumentException e) { + throw new InvalidTokenException("권한이 없습니다."); + } + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/application/OAuthClient.java b/backend/src/main/java/com/allog/dallog/domain/auth/application/OAuthClient.java new file mode 100644 index 00000000..a21239e0 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/application/OAuthClient.java @@ -0,0 +1,11 @@ +package com.allog.dallog.domain.auth.application; + +import com.allog.dallog.domain.auth.dto.OAuthMember; +import com.allog.dallog.domain.auth.dto.response.OAuthAccessTokenResponse; + +public interface OAuthClient { + + OAuthMember getOAuthMember(final String code, final String redirectUri); + + OAuthAccessTokenResponse getAccessToken(final String refreshToken); +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/application/OAuthUri.java b/backend/src/main/java/com/allog/dallog/domain/auth/application/OAuthUri.java new file mode 100644 index 00000000..d14cc469 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/application/OAuthUri.java @@ -0,0 +1,7 @@ +package com.allog.dallog.domain.auth.application; + +@FunctionalInterface +public interface OAuthUri { + + String generate(final String redirectUri); +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/application/TokenProvider.java b/backend/src/main/java/com/allog/dallog/domain/auth/application/TokenProvider.java new file mode 100644 index 00000000..e5105806 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/application/TokenProvider.java @@ -0,0 +1,10 @@ +package com.allog.dallog.domain.auth.application; + +public interface TokenProvider { + + String createToken(final String payload); + + String getPayload(final String token); + + void validateToken(final String token); +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/domain/OAuthToken.java b/backend/src/main/java/com/allog/dallog/domain/auth/domain/OAuthToken.java new file mode 100644 index 00000000..d3fc29d0 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/domain/OAuthToken.java @@ -0,0 +1,53 @@ +package com.allog.dallog.domain.auth.domain; + +import com.allog.dallog.domain.common.BaseEntity; +import com.allog.dallog.domain.member.domain.Member; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +@Table(name = "oauth_tokens") +@Entity +public class OAuthToken extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "members_id", nullable = false) + private Member member; + + @Column(name = "refresh_token", nullable = false) + private String refreshToken; + + protected OAuthToken() { + } + + public OAuthToken(final Member member, final String refreshToken) { + this.member = member; + this.refreshToken = refreshToken; + } + + public void change(final String refreshToken) { + this.refreshToken = refreshToken; + } + + public Long getId() { + return id; + } + + public Member getMember() { + return member; + } + + public String getRefreshToken() { + return refreshToken; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/domain/OAuthTokenRepository.java b/backend/src/main/java/com/allog/dallog/domain/auth/domain/OAuthTokenRepository.java new file mode 100644 index 00000000..c95cf2f8 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/domain/OAuthTokenRepository.java @@ -0,0 +1,13 @@ +package com.allog.dallog.domain.auth.domain; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface OAuthTokenRepository extends JpaRepository { + + boolean existsByMemberId(final Long memberId); + + Optional findByMemberId(final Long memberId); + + void deleteByMemberId(final Long memberId); +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/dto/LoginMember.java b/backend/src/main/java/com/allog/dallog/domain/auth/dto/LoginMember.java new file mode 100644 index 00000000..c414adca --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/dto/LoginMember.java @@ -0,0 +1,17 @@ +package com.allog.dallog.domain.auth.dto; + +public class LoginMember { + + private Long id; + + private LoginMember() { + } + + public LoginMember(final Long id) { + this.id = id; + } + + public Long getId() { + return id; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/dto/OAuthMember.java b/backend/src/main/java/com/allog/dallog/domain/auth/dto/OAuthMember.java new file mode 100644 index 00000000..c0894f38 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/dto/OAuthMember.java @@ -0,0 +1,33 @@ +package com.allog.dallog.domain.auth.dto; + +public class OAuthMember { + + private final String email; + private final String displayName; + private final String profileImageUrl; + private final String refreshToken; + + public OAuthMember(final String email, final String displayName, final String profileImageUrl, + final String refreshToken) { + this.email = email; + this.displayName = displayName; + this.profileImageUrl = profileImageUrl; + this.refreshToken = refreshToken; + } + + public String getEmail() { + return email; + } + + public String getDisplayName() { + return displayName; + } + + public String getProfileImageUrl() { + return profileImageUrl; + } + + public String getRefreshToken() { + return refreshToken; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/dto/request/TokenRequest.java b/backend/src/main/java/com/allog/dallog/domain/auth/dto/request/TokenRequest.java new file mode 100644 index 00000000..3b84326c --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/dto/request/TokenRequest.java @@ -0,0 +1,29 @@ +package com.allog.dallog.domain.auth.dto.request; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +public class TokenRequest { + + @NotBlank(message = "인가 코드는 공백일 수 없습니다.") + private String code; + + @NotNull(message = "Null일 수 없습니다.") + private String redirectUri; + + private TokenRequest() { + } + + public TokenRequest(final String code, final String redirectUri) { + this.code = code; + this.redirectUri = redirectUri; + } + + public String getCode() { + return code; + } + + public String getRedirectUri() { + return redirectUri; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/dto/response/OAuthAccessTokenResponse.java b/backend/src/main/java/com/allog/dallog/domain/auth/dto/response/OAuthAccessTokenResponse.java new file mode 100644 index 00000000..9b8539b1 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/dto/response/OAuthAccessTokenResponse.java @@ -0,0 +1,40 @@ +package com.allog.dallog.domain.auth.dto.response; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class OAuthAccessTokenResponse { + + private String accessToken; + private String expiresIn; + private String scope; + private String tokenType; + + private OAuthAccessTokenResponse() { + } + + public OAuthAccessTokenResponse(final String accessToken, final String expiresIn, final String scope, + final String tokenType) { + this.accessToken = accessToken; + this.expiresIn = expiresIn; + this.scope = scope; + this.tokenType = tokenType; + } + + public String getAccessToken() { + return accessToken; + } + + public String getExpiresIn() { + return expiresIn; + } + + public String getScope() { + return scope; + } + + public String getTokenType() { + return tokenType; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/dto/response/OAuthUriResponse.java b/backend/src/main/java/com/allog/dallog/domain/auth/dto/response/OAuthUriResponse.java new file mode 100644 index 00000000..1a4dcf31 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/dto/response/OAuthUriResponse.java @@ -0,0 +1,18 @@ +package com.allog.dallog.domain.auth.dto.response; + +// OAuth 인증 URI(소셜 로그인 링크)를 전달하는 DTO +public class OAuthUriResponse { + + private String oAuthUri; + + private OAuthUriResponse() { + } + + public OAuthUriResponse(final String oAuthUri) { + this.oAuthUri = oAuthUri; + } + + public String getoAuthUri() { + return oAuthUri; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/dto/response/TokenResponse.java b/backend/src/main/java/com/allog/dallog/domain/auth/dto/response/TokenResponse.java new file mode 100644 index 00000000..16ce9455 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/dto/response/TokenResponse.java @@ -0,0 +1,17 @@ +package com.allog.dallog.domain.auth.dto.response; + +public class TokenResponse { + + private String accessToken; + + private TokenResponse() { + } + + public TokenResponse(final String accessToken) { + this.accessToken = accessToken; + } + + public String getAccessToken() { + return accessToken; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/exception/EmptyAuthorizationHeaderException.java b/backend/src/main/java/com/allog/dallog/domain/auth/exception/EmptyAuthorizationHeaderException.java new file mode 100644 index 00000000..af2611f8 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/exception/EmptyAuthorizationHeaderException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.auth.exception; + +public class EmptyAuthorizationHeaderException extends RuntimeException { + + public EmptyAuthorizationHeaderException(final String message) { + super(message); + } + + public EmptyAuthorizationHeaderException() { + this("header에 Authorization이 존재하지 않습니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/exception/InvalidTokenException.java b/backend/src/main/java/com/allog/dallog/domain/auth/exception/InvalidTokenException.java new file mode 100644 index 00000000..db514ee4 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/exception/InvalidTokenException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.auth.exception; + +public class InvalidTokenException extends RuntimeException { + + public InvalidTokenException(final String message) { + super(message); + } + + public InvalidTokenException() { + this("유효하지 않은 토큰입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/exception/NoPermissionException.java b/backend/src/main/java/com/allog/dallog/domain/auth/exception/NoPermissionException.java new file mode 100644 index 00000000..52f85e18 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/exception/NoPermissionException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.auth.exception; + +public class NoPermissionException extends RuntimeException { + + public NoPermissionException(final String message) { + super(message); + } + + public NoPermissionException() { + this("권한이 없는 요청 입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/auth/exception/NoSuchOAuthTokenException.java b/backend/src/main/java/com/allog/dallog/domain/auth/exception/NoSuchOAuthTokenException.java new file mode 100644 index 00000000..fc093a3b --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/auth/exception/NoSuchOAuthTokenException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.auth.exception; + +public class NoSuchOAuthTokenException extends RuntimeException { + + public NoSuchOAuthTokenException(final String message) { + super(message); + } + + public NoSuchOAuthTokenException() { + this("존재하지 않는 OAuthToken 입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/application/CategoryService.java b/backend/src/main/java/com/allog/dallog/domain/category/application/CategoryService.java new file mode 100644 index 00000000..898ca2aa --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/application/CategoryService.java @@ -0,0 +1,164 @@ +package com.allog.dallog.domain.category.application; + +import static com.allog.dallog.domain.category.domain.CategoryType.NORMAL; + +import com.allog.dallog.domain.auth.exception.NoPermissionException; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.CategoryRepository; +import com.allog.dallog.domain.category.domain.CategoryType; +import com.allog.dallog.domain.category.domain.ExternalCategoryDetail; +import com.allog.dallog.domain.category.domain.ExternalCategoryDetailRepository; +import com.allog.dallog.domain.category.dto.request.CategoryCreateRequest; +import com.allog.dallog.domain.category.dto.request.CategoryUpdateRequest; +import com.allog.dallog.domain.category.dto.request.ExternalCategoryCreateRequest; +import com.allog.dallog.domain.category.dto.response.CategoriesResponse; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.category.exception.DuplicatedExternalCategoryException; +import com.allog.dallog.domain.category.exception.InvalidCategoryException; +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import com.allog.dallog.domain.member.exception.NoSuchMemberException; +import com.allog.dallog.domain.schedule.domain.ScheduleRepository; +import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(readOnly = true) +@Service +public class CategoryService { + + private final CategoryRepository categoryRepository; + private final ExternalCategoryDetailRepository externalCategoryDetailRepository; + private final MemberRepository memberRepository; + private final SubscriptionRepository subscriptionRepository; + private final ScheduleRepository scheduleRepository; + + public CategoryService(final CategoryRepository categoryRepository, + final ExternalCategoryDetailRepository externalCategoryDetailRepository, + final MemberRepository memberRepository, final SubscriptionRepository subscriptionRepository, + final ScheduleRepository scheduleRepository) { + this.categoryRepository = categoryRepository; + this.externalCategoryDetailRepository = externalCategoryDetailRepository; + this.memberRepository = memberRepository; + this.subscriptionRepository = subscriptionRepository; + this.scheduleRepository = scheduleRepository; + } + + @Transactional + public CategoryResponse save(final Long memberId, final CategoryCreateRequest request) { + Member member = memberRepository.findById(memberId) + .orElseThrow(NoSuchMemberException::new); + Category newCategory = new Category(request.getName(), member, + CategoryType.valueOf(request.getCategoryType().toUpperCase())); + categoryRepository.save(newCategory); + return new CategoryResponse(newCategory); + } + + @Transactional + public CategoryResponse save(final Long memberId, final ExternalCategoryCreateRequest request) { + List categories = categoryRepository.findByMemberId(memberId); + validateDuplicateExternalCategory(request.getExternalId(), categories); + + CategoryResponse response = save(memberId, new CategoryCreateRequest(request.getName(), CategoryType.GOOGLE)); + Category category = getCategory(response.getId()); + + externalCategoryDetailRepository.save(new ExternalCategoryDetail(category, request.getExternalId())); + + return response; + } + + private void validateDuplicateExternalCategory(final String externalId, final List categories) { + List externalCategories = categories.stream() + .filter(Category::isExternal) + .collect(Collectors.toList()); + + if (!externalCategories.isEmpty() + && externalCategoryDetailRepository.existsByExternalIdAndCategoryIn(externalId, externalCategories)) { + throw new DuplicatedExternalCategoryException(); + } + } + + public CategoriesResponse findNormalByName(final String name, final Pageable pageable) { + List categories + = categoryRepository.findByNameContainingAndCategoryType(name, NORMAL, pageable).getContent(); + + return new CategoriesResponse(pageable.getPageNumber(), categories); + } + + public CategoriesResponse findMineByName(final Long memberId, final String name, final Pageable pageable) { + List categories = categoryRepository.findByMemberIdLikeCategoryName(memberId, name, pageable) + .getContent(); + + return new CategoriesResponse(pageable.getPageNumber(), categories); + } + + public CategoryResponse findById(final Long id) { + return new CategoryResponse(getCategory(id)); + } + + public Category getCategory(final Long id) { + return categoryRepository.findById(id) + .orElseThrow(NoSuchCategoryException::new); + } + + @Transactional + public void update(final Long memberId, final Long categoryId, final CategoryUpdateRequest request) { + Category category = getCategory(categoryId); + validatePermission(memberId, categoryId); + + category.changeName(request.getName()); + } + + @Transactional + public void deleteById(final Long memberId, final Long categoryId) { + validateCategoryExisting(categoryId); + validatePermission(memberId, categoryId); + validatePersonalCategory(categoryId); + + scheduleRepository.deleteByCategoryIdIn(List.of(categoryId)); + subscriptionRepository.deleteByCategoryIdIn(List.of(categoryId)); + externalCategoryDetailRepository.deleteByCategoryId(categoryId); + categoryRepository.deleteById(categoryId); + } + + private void validatePersonalCategory(final Long categoryId) { + Category category = getCategory(categoryId); + if (category.isPersonal()) { + throw new InvalidCategoryException("내 일정 카테고리는 삭제할 수 없습니다."); + } + } + + private void validateCategoryExisting(final Long categoryId) { + if (!categoryRepository.existsById(categoryId)) { + throw new NoSuchCategoryException("존재하지 않는 카테고리를 삭제할 수 없습니다."); + } + } + + private void validatePermission(final Long memberId, final Long categoryId) { + if (!categoryRepository.existsByIdAndMemberId(categoryId, memberId)) { + throw new NoPermissionException(); + } + } + + @Transactional + public void deleteByMemberId(final Long memberId) { + List categoryIds = categoryRepository.findByMemberId(memberId) + .stream() + .map(Category::getId) + .collect(Collectors.toList()); + + scheduleRepository.deleteByCategoryIdIn(categoryIds); + subscriptionRepository.deleteByCategoryIdIn(categoryIds); + categoryRepository.deleteByMemberId(memberId); + } + + public void validateCreatorBy(final Long memberId, final Category category) { + if (!category.isCreator(memberId)) { + throw new NoPermissionException(); + } + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/domain/Category.java b/backend/src/main/java/com/allog/dallog/domain/category/domain/Category.java new file mode 100644 index 00000000..e4cc2867 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/domain/Category.java @@ -0,0 +1,113 @@ +package com.allog.dallog.domain.category.domain; + +import static com.allog.dallog.domain.category.domain.CategoryType.GOOGLE; +import static com.allog.dallog.domain.category.domain.CategoryType.PERSONAL; + +import com.allog.dallog.domain.category.exception.InvalidCategoryException; +import com.allog.dallog.domain.common.BaseEntity; +import com.allog.dallog.domain.member.domain.Member; +import java.util.Objects; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Table(name = "categories") +@Entity +public class Category extends BaseEntity { + + public static final int MAX_NAME_LENGTH = 20; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "name", nullable = false) + private String name; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "members_id", nullable = false) + private Member member; + + @Enumerated(value = EnumType.STRING) + @Column(name = "category_type", nullable = false) + private CategoryType categoryType; + + protected Category() { + } + + public Category(final String name, final Member member) { + validateNameLength(name); + this.name = name; + this.member = member; + this.categoryType = CategoryType.NORMAL; + } + + public Category(final String name, final Member member, final CategoryType categoryType) { + validateNameLength(name); + this.name = name; + this.member = member; + this.categoryType = categoryType; + } + + public void changeName(final String name) { + validatePersonal(); + validateNameLength(name); + this.name = name; + } + + private void validatePersonal() { + if (isPersonal()) { + throw new InvalidCategoryException("내 일정은 수정할 수 없습니다."); + } + } + + private void validateNameLength(final String name) { + if (name.isBlank()) { + throw new InvalidCategoryException("카테고리 이름은 공백일 수 없습니다."); + } + if (name.length() > MAX_NAME_LENGTH) { + throw new InvalidCategoryException("카테고리 이름의 길이는 20을 초과할 수 없습니다."); + } + } + + public boolean isCreator(final Long memberId) { + return Objects.equals(member.getId(), memberId); + } + + public boolean isPersonal() { + return categoryType == PERSONAL; + } + + public boolean isInternal() { + return categoryType != GOOGLE; + } + + public boolean isExternal() { + return categoryType == GOOGLE; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public Member getMember() { + return member; + } + + public CategoryType getCategoryType() { + return categoryType; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/domain/CategoryRepository.java b/backend/src/main/java/com/allog/dallog/domain/category/domain/CategoryRepository.java new file mode 100644 index 00000000..203b2279 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/domain/CategoryRepository.java @@ -0,0 +1,26 @@ +package com.allog.dallog.domain.category.domain; + +import java.util.List; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +public interface CategoryRepository extends JpaRepository { + + @Query("SELECT c " + + "FROM Category c " + + "WHERE c.name LIKE %:name% AND c.categoryType = :categoryType") + Slice findByNameContainingAndCategoryType(final String name, final CategoryType categoryType, final Pageable pageable); + + @Query("SELECT c " + + "FROM Category c " + + "WHERE c.member.id = :memberId AND c.name LIKE %:name%") + Slice findByMemberIdLikeCategoryName(final Long memberId, final String name, final Pageable pageable); + + List findByMemberId(Long memberId); + + boolean existsByIdAndMemberId(Long id, Long memberId); + + void deleteByMemberId(Long memberId); +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/domain/CategoryType.java b/backend/src/main/java/com/allog/dallog/domain/category/domain/CategoryType.java new file mode 100644 index 00000000..87060ada --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/domain/CategoryType.java @@ -0,0 +1,16 @@ +package com.allog.dallog.domain.category.domain; + +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; + +public enum CategoryType { + + NORMAL, PERSONAL, GOOGLE; + + public static CategoryType from(final String value) { + try { + return CategoryType.valueOf(value.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new NoSuchCategoryException("(" + value + ")는 존재하지 않는 카테고리 타입입니다."); + } + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetail.java b/backend/src/main/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetail.java new file mode 100644 index 00000000..7c93616d --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetail.java @@ -0,0 +1,49 @@ +package com.allog.dallog.domain.category.domain; + +import com.allog.dallog.domain.common.BaseEntity; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.OneToOne; +import javax.persistence.Table; + +@Table(name = "external_category_details") +@Entity +public class ExternalCategoryDetail extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "categories_id", nullable = false) + private Category category; + + @Column(name = "external_id", nullable = false) + private String externalId; + + protected ExternalCategoryDetail() { + } + + public ExternalCategoryDetail(final Category category, final String externalId) { + this.category = category; + this.externalId = externalId; + } + + public Long getId() { + return id; + } + + public Category getCategory() { + return category; + } + + public String getExternalId() { + return externalId; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetailRepository.java b/backend/src/main/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetailRepository.java new file mode 100644 index 00000000..767a66ff --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/domain/ExternalCategoryDetailRepository.java @@ -0,0 +1,14 @@ +package com.allog.dallog.domain.category.domain; + +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ExternalCategoryDetailRepository extends JpaRepository { + + Optional findByCategoryId(final Long categoryId); + + boolean existsByExternalIdAndCategoryIn(final String externalId, final List categories); + + void deleteByCategoryId(final Long categoryId); +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/dto/request/CategoryCreateRequest.java b/backend/src/main/java/com/allog/dallog/domain/category/dto/request/CategoryCreateRequest.java new file mode 100644 index 00000000..d6bd4467 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/dto/request/CategoryCreateRequest.java @@ -0,0 +1,29 @@ +package com.allog.dallog.domain.category.dto.request; + +import com.allog.dallog.domain.category.domain.CategoryType; +import javax.validation.constraints.NotBlank; + +public class CategoryCreateRequest { + + @NotBlank(message = "공백일 수 없습니다.") + private String name; + + @NotBlank(message = "공백일 수 없습니다.") + private String categoryType; + + private CategoryCreateRequest() { + } + + public CategoryCreateRequest(final String name, final CategoryType categoryType) { + this.name = name; + this.categoryType = categoryType.name(); + } + + public String getName() { + return name; + } + + public String getCategoryType() { + return categoryType; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/dto/request/CategoryUpdateRequest.java b/backend/src/main/java/com/allog/dallog/domain/category/dto/request/CategoryUpdateRequest.java new file mode 100644 index 00000000..9baafa36 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/dto/request/CategoryUpdateRequest.java @@ -0,0 +1,20 @@ +package com.allog.dallog.domain.category.dto.request; + +import javax.validation.constraints.NotBlank; + +public class CategoryUpdateRequest { + + @NotBlank(message = "공백일 수 없습니다.") + private String name; + + private CategoryUpdateRequest() { + } + + public CategoryUpdateRequest(final String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/dto/request/ExternalCategoryCreateRequest.java b/backend/src/main/java/com/allog/dallog/domain/category/dto/request/ExternalCategoryCreateRequest.java new file mode 100644 index 00000000..0b4bc775 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/dto/request/ExternalCategoryCreateRequest.java @@ -0,0 +1,28 @@ +package com.allog.dallog.domain.category.dto.request; + +import javax.validation.constraints.NotBlank; + +public class ExternalCategoryCreateRequest { + + @NotBlank(message = "공백일 수 없습니다.") + private String externalId; + + @NotBlank(message = "공백일 수 없습니다.") + private String name; + + private ExternalCategoryCreateRequest() { + } + + public ExternalCategoryCreateRequest(final String externalId, final String name) { + this.externalId = externalId; + this.name = name; + } + + public String getExternalId() { + return externalId; + } + + public String getName() { + return name; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/dto/response/CategoriesResponse.java b/backend/src/main/java/com/allog/dallog/domain/category/dto/response/CategoriesResponse.java new file mode 100644 index 00000000..71e99893 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/dto/response/CategoriesResponse.java @@ -0,0 +1,33 @@ +package com.allog.dallog.domain.category.dto.response; + +import com.allog.dallog.domain.category.domain.Category; +import java.util.List; +import java.util.stream.Collectors; + +public class CategoriesResponse { + + private int page; + private List categories; + + public CategoriesResponse() { + } + + public CategoriesResponse(final int page, final List categories) { + this.page = page; + this.categories = convertToResponses(categories); + } + + private List convertToResponses(final List categories) { + return categories.stream() + .map(CategoryResponse::new) + .collect(Collectors.toList()); + } + + public int getPage() { + return page; + } + + public List getCategories() { + return categories; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/dto/response/CategoryResponse.java b/backend/src/main/java/com/allog/dallog/domain/category/dto/response/CategoryResponse.java new file mode 100644 index 00000000..6ef31a04 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/dto/response/CategoryResponse.java @@ -0,0 +1,51 @@ +package com.allog.dallog.domain.category.dto.response; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.member.dto.MemberResponse; +import java.time.LocalDateTime; + +public class CategoryResponse { + + private Long id; + private String name; + private String categoryType; + private MemberResponse creator; + private LocalDateTime createdAt; + + private CategoryResponse() { + } + + public CategoryResponse(final Category category) { + this(category.getId(), category.getName(), category.getCategoryType().name(), + new MemberResponse(category.getMember()), category.getCreatedAt()); + } + + public CategoryResponse(final Long id, final String name, final String categoryType, final MemberResponse creator, + final LocalDateTime createdAt) { + this.id = id; + this.name = name; + this.categoryType = categoryType; + this.creator = creator; + this.createdAt = createdAt; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getCategoryType() { + return categoryType; + } + + public MemberResponse getCreator() { + return creator; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/exception/DuplicatedExternalCategoryException.java b/backend/src/main/java/com/allog/dallog/domain/category/exception/DuplicatedExternalCategoryException.java new file mode 100644 index 00000000..9bfe6a77 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/exception/DuplicatedExternalCategoryException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.category.exception; + +public class DuplicatedExternalCategoryException extends RuntimeException { + + public DuplicatedExternalCategoryException(final String message) { + super(message); + } + + public DuplicatedExternalCategoryException() { + this("이미 저장된 연동 카테고리입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/exception/InvalidCategoryException.java b/backend/src/main/java/com/allog/dallog/domain/category/exception/InvalidCategoryException.java new file mode 100644 index 00000000..d133cd9f --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/exception/InvalidCategoryException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.category.exception; + +public class InvalidCategoryException extends RuntimeException { + + public InvalidCategoryException(final String message) { + super(message); + } + + public InvalidCategoryException() { + this("잘못된 카테고리입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/exception/NoSuchCategoryException.java b/backend/src/main/java/com/allog/dallog/domain/category/exception/NoSuchCategoryException.java new file mode 100644 index 00000000..0f08a63f --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/exception/NoSuchCategoryException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.category.exception; + +public class NoSuchCategoryException extends RuntimeException { + + public NoSuchCategoryException(final String message) { + super(message); + } + + public NoSuchCategoryException() { + this("존재하지 않는 카테고리입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/category/exception/NoSuchExternalCategoryDetailException.java b/backend/src/main/java/com/allog/dallog/domain/category/exception/NoSuchExternalCategoryDetailException.java new file mode 100644 index 00000000..4a9ed5b5 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/category/exception/NoSuchExternalCategoryDetailException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.category.exception; + +public class NoSuchExternalCategoryDetailException extends RuntimeException { + + public NoSuchExternalCategoryDetailException(final String message) { + super(message); + } + + public NoSuchExternalCategoryDetailException() { + this("존재하지 않는 외부 카테고리 정보 입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/common/BaseEntity.java b/backend/src/main/java/com/allog/dallog/domain/common/BaseEntity.java new file mode 100644 index 00000000..1df01b5a --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/common/BaseEntity.java @@ -0,0 +1,30 @@ +package com.allog.dallog.domain.common; + +import java.time.LocalDateTime; +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public abstract class BaseEntity { + + @CreatedDate + @Column(name = "created_at", nullable = false, updatable = false) + private LocalDateTime createdAt; + + @LastModifiedDate + @Column(name = "updated_at", nullable = false) + private LocalDateTime updatedAt; + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public LocalDateTime getUpdatedAt() { + return updatedAt; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/composition/application/CalendarService.java b/backend/src/main/java/com/allog/dallog/domain/composition/application/CalendarService.java new file mode 100644 index 00000000..7da8dc06 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/composition/application/CalendarService.java @@ -0,0 +1,140 @@ +package com.allog.dallog.domain.composition.application; + +import com.allog.dallog.domain.auth.application.OAuthClient; +import com.allog.dallog.domain.auth.domain.OAuthToken; +import com.allog.dallog.domain.auth.domain.OAuthTokenRepository; +import com.allog.dallog.domain.auth.exception.NoSuchOAuthTokenException; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.ExternalCategoryDetail; +import com.allog.dallog.domain.category.domain.ExternalCategoryDetailRepository; +import com.allog.dallog.domain.category.exception.NoSuchExternalCategoryDetailException; +import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarClient; +import com.allog.dallog.domain.integrationschedule.dao.IntegrationScheduleDao; +import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.integrationschedule.domain.TypedSchedules; +import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; +import com.allog.dallog.domain.schedule.dto.response.MemberScheduleResponses; +import com.allog.dallog.domain.subscription.application.SubscriptionService; +import com.allog.dallog.domain.subscription.domain.Subscription; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +// TODO: 전체 리팩토링 +@Transactional(readOnly = true) +@Service +public class CalendarService { + + private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; + + private final SubscriptionService subscriptionService; + private final IntegrationScheduleDao integrationScheduleDao; + private final ExternalCategoryDetailRepository externalCategoryDetailRepository; + private final OAuthTokenRepository oAuthTokenRepository; + private final OAuthClient oAuthClient; + private final ExternalCalendarClient externalCalendarClient; + + public CalendarService(final SubscriptionService subscriptionService, + final IntegrationScheduleDao integrationScheduleDao, + final ExternalCategoryDetailRepository externalCategoryDetailRepository, + final OAuthTokenRepository oAuthTokenRepository, final OAuthClient oAuthClient, + final ExternalCalendarClient externalCalendarClient) { + this.subscriptionService = subscriptionService; + this.integrationScheduleDao = integrationScheduleDao; + this.externalCategoryDetailRepository = externalCategoryDetailRepository; + this.oAuthTokenRepository = oAuthTokenRepository; + this.oAuthClient = oAuthClient; + this.externalCalendarClient = externalCalendarClient; + } + + public List getSchedulesOfSubscriberIds(final List subscriberIds, + final DateRangeRequest dateRange) { + return subscriberIds.stream() + .flatMap(subscriberId -> getIntegrationSchedulesByMemberIdAndSubscriptions(subscriberId, + dateRange).stream()) + .distinct() + .collect(Collectors.toList()); + } + + public MemberScheduleResponses findSchedulesByMemberId(final Long memberId, + final DateRangeRequest dateRange) { + List subscriptions = subscriptionService.getAllByMemberId(memberId); + List integrationSchedules = getIntegrationSchedulesByMemberIdAndSubscriptions(memberId, + dateRange); + return new MemberScheduleResponses(subscriptions, new TypedSchedules(integrationSchedules)); + } + + private List getIntegrationSchedulesByMemberIdAndSubscriptions(final Long memberId, + final DateRangeRequest dateRange) { + List subscriptions = subscriptionService.getAllByMemberId(memberId); + List internalCategoryIds = findCheckedInternalCategories(subscriptions); + + List integrationSchedules = new ArrayList<>( + integrationScheduleDao.findByCategoryIdInAndBetween( + internalCategoryIds, dateRange.getStartDateTime(), dateRange.getEndDateTime())); + + List externalCategoryDetails = findCheckedExternalCategoryDetails(subscriptions); + if (!externalCategoryDetails.isEmpty()) { + integrationSchedules.addAll(getExternalSchedules(memberId, dateRange, externalCategoryDetails)); + } + + return integrationSchedules; + } + + private List findCheckedInternalCategories(final List subscriptions) { + return findCheckedSubscriptions(subscriptions) + .stream() + .map(Subscription::getCategory) + .filter(Category::isInternal) + .map(Category::getId) + .collect(Collectors.toList()); + } + + private List findCheckedExternalCategoryDetails(final List subscriptions) { + return findCheckedSubscriptions(subscriptions) + .stream() + .map(Subscription::getCategory) + .filter(Category::isExternal) + .map(this::getExternalCategoryDetail) + .collect(Collectors.toList()); + } + + private List findCheckedSubscriptions(final List subscriptions) { + return subscriptions.stream() + .filter(Subscription::isChecked) + .collect(Collectors.toList()); + } + + private ExternalCategoryDetail getExternalCategoryDetail(final Category category) { + return externalCategoryDetailRepository.findByCategoryId(category.getId()) + .orElseThrow(NoSuchExternalCategoryDetailException::new); + } + + private List getExternalSchedules(final Long memberId, final DateRangeRequest request, + final List externalCategories) { + String refreshToken = getOAuthToken(memberId).getRefreshToken(); + String accessToken = oAuthClient.getAccessToken(refreshToken).getAccessToken(); + + return externalCategories.stream() + .flatMap(externalCategory -> flatIntegrationSchedules(request, accessToken, externalCategory)) + .collect(Collectors.toList()); + } + + private OAuthToken getOAuthToken(final Long memberId) { + return oAuthTokenRepository.findByMemberId(memberId).orElseThrow(NoSuchOAuthTokenException::new); + } + + private Stream flatIntegrationSchedules(final DateRangeRequest dateRangeRequest, + final String accessToken, + final ExternalCategoryDetail externalCategory) { + String startDateTime = dateRangeRequest.getStartDateTime().format(DateTimeFormatter.ofPattern(DATE_FORMAT)); + String endDateTime = dateRangeRequest.getEndDateTime().format(DateTimeFormatter.ofPattern(DATE_FORMAT)); + + return externalCalendarClient.getExternalCalendarSchedules(accessToken, externalCategory.getCategory().getId(), + externalCategory.getExternalId(), startDateTime, endDateTime).stream(); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/composition/application/CategorySubscriptionService.java b/backend/src/main/java/com/allog/dallog/domain/composition/application/CategorySubscriptionService.java new file mode 100644 index 00000000..62199bdd --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/composition/application/CategorySubscriptionService.java @@ -0,0 +1,37 @@ +package com.allog.dallog.domain.composition.application; + +import com.allog.dallog.domain.category.application.CategoryService; +import com.allog.dallog.domain.category.dto.request.CategoryCreateRequest; +import com.allog.dallog.domain.category.dto.request.ExternalCategoryCreateRequest; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.subscription.application.SubscriptionService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(readOnly = true) +@Service +public class CategorySubscriptionService { + + private final CategoryService categoryService; + private final SubscriptionService subscriptionService; + + public CategorySubscriptionService(final CategoryService categoryService, + final SubscriptionService subscriptionService) { + this.categoryService = categoryService; + this.subscriptionService = subscriptionService; + } + + @Transactional + public CategoryResponse save(final Long memberId, final CategoryCreateRequest request) { + CategoryResponse response = categoryService.save(memberId, request); + subscriptionService.save(memberId, response.getId()); + return response; + } + + @Transactional + public CategoryResponse save(final Long memberId, final ExternalCategoryCreateRequest request) { + CategoryResponse response = categoryService.save(memberId, request); + subscriptionService.save(memberId, response.getId()); + return response; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/composition/application/RegisterService.java b/backend/src/main/java/com/allog/dallog/domain/composition/application/RegisterService.java new file mode 100644 index 00000000..74b83626 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/composition/application/RegisterService.java @@ -0,0 +1,58 @@ +package com.allog.dallog.domain.composition.application; + +import static com.allog.dallog.domain.category.domain.CategoryType.PERSONAL; + +import com.allog.dallog.domain.auth.dto.OAuthMember; +import com.allog.dallog.domain.category.application.CategoryService; +import com.allog.dallog.domain.category.dto.request.CategoryCreateRequest; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.member.application.MemberService; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.SocialType; +import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.subscription.application.SubscriptionService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(readOnly = true) +@Service +public class RegisterService { + + private static final String PERSONAL_CATEGORY_NAME = "내 일정"; + + private final MemberService memberService; + private final CategoryService categoryService; + private final SubscriptionService subscriptionService; + + public RegisterService(final MemberService memberService, final CategoryService categoryService, + final SubscriptionService subscriptionService) { + this.memberService = memberService; + this.categoryService = categoryService; + this.subscriptionService = subscriptionService; + } + + @Transactional + public MemberResponse register(final OAuthMember oAuthMember) { + MemberResponse memberResponse = createMember(oAuthMember); + Long categoryId = createPersonalCategory(memberResponse.getId()).getId(); + subscriptionService.save(memberResponse.getId(), categoryId); + return memberResponse; + } + + private MemberResponse createMember(final OAuthMember oAuthMember) { + Member member = new Member(oAuthMember.getEmail(), oAuthMember.getDisplayName(), + oAuthMember.getProfileImageUrl(), SocialType.GOOGLE); + return memberService.save(member); + } + + private CategoryResponse createPersonalCategory(final Long memberId) { + CategoryCreateRequest categoryCreateRequest = new CategoryCreateRequest(PERSONAL_CATEGORY_NAME, PERSONAL); + return categoryService.save(memberId, categoryCreateRequest); + } + + @Transactional + public void deleteByMemberId(final Long memberId) { + categoryService.deleteByMemberId(memberId); + memberService.deleteById(memberId); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/composition/application/SchedulerService.java b/backend/src/main/java/com/allog/dallog/domain/composition/application/SchedulerService.java new file mode 100644 index 00000000..7352f517 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/composition/application/SchedulerService.java @@ -0,0 +1,54 @@ +package com.allog.dallog.domain.composition.application; + +import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.integrationschedule.domain.Period; +import com.allog.dallog.domain.member.application.MemberService; +import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.schedule.domain.scheduler.Scheduler; +import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; +import com.allog.dallog.domain.schedule.dto.response.PeriodResponse; +import com.allog.dallog.domain.subscription.application.SubscriptionService; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(readOnly = true) +@Service +public class SchedulerService { + + private final CalendarService calendarService; + private final SubscriptionService subscriptionService; + private final MemberService memberService; + + public SchedulerService(final CalendarService calendarService, final SubscriptionService subscriptionService, + final MemberService memberService) { + this.calendarService = calendarService; + this.subscriptionService = subscriptionService; + this.memberService = memberService; + } + + public List getAvailablePeriods(final Long categoryId, final DateRangeRequest dateRange) { + List subscriberIds = getSubscriberIds(categoryId); + List schedules = calendarService.getSchedulesOfSubscriberIds(subscriberIds, dateRange); + + Scheduler scheduler = new Scheduler(schedules, dateRange.getStartDateTime(), dateRange.getEndDateTime()); + + return convertPeriodsToPeriodResponses(scheduler.getPeriods()); + } + + private List getSubscriberIds(final Long categoryId) { + List subscriptions = subscriptionService.findByCategoryId(categoryId); + return subscriptions.stream() + .map(subscriptionResponse -> memberService.findBySubscriptionId(subscriptionResponse.getId())) + .map(MemberResponse::getId) + .collect(Collectors.toList()); + } + + private List convertPeriodsToPeriodResponses(final List periods) { + return periods.stream() + .map(PeriodResponse::new) + .collect(Collectors.toList()); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarClient.java b/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarClient.java new file mode 100644 index 00000000..d550a16e --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarClient.java @@ -0,0 +1,15 @@ +package com.allog.dallog.domain.externalcalendar.application; + +import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendar; +import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import java.util.List; + +public interface ExternalCalendarClient { + + List getExternalCalendars(final String accessToken); + + List getExternalCalendarSchedules(final String accessToken, + final Long internalCategoryId, + final String externalCalendarId, + final String startDateTime, final String endDateTime); +} diff --git a/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarService.java b/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarService.java new file mode 100644 index 00000000..1dfca38f --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarService.java @@ -0,0 +1,40 @@ +package com.allog.dallog.domain.externalcalendar.application; + +import com.allog.dallog.domain.auth.application.OAuthClient; +import com.allog.dallog.domain.auth.domain.OAuthToken; +import com.allog.dallog.domain.auth.domain.OAuthTokenRepository; +import com.allog.dallog.domain.auth.dto.response.OAuthAccessTokenResponse; +import com.allog.dallog.domain.auth.exception.NoSuchOAuthTokenException; +import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendar; +import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendarsResponse; +import java.util.List; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(readOnly = true) +@Service +public class ExternalCalendarService { + + private final OAuthClient oAuthClient; + private final ExternalCalendarClient externalCalendarClient; + private final OAuthTokenRepository oAuthTokenRepository; + + public ExternalCalendarService(final OAuthClient oAuthClient, final ExternalCalendarClient externalCalendarClient, + final OAuthTokenRepository oAuthTokenRepository) { + this.oAuthClient = oAuthClient; + this.externalCalendarClient = externalCalendarClient; + this.oAuthTokenRepository = oAuthTokenRepository; + } + + public ExternalCalendarsResponse findByMemberId(final Long memberId) { + OAuthToken oAuthToken = oAuthTokenRepository.findByMemberId(memberId) + .orElseThrow(NoSuchOAuthTokenException::new); + + OAuthAccessTokenResponse oAuthAccessTokenResponse = oAuthClient.getAccessToken(oAuthToken.getRefreshToken()); + String oAuthAccessToken = oAuthAccessTokenResponse.getAccessToken(); + + List externalCalendars = externalCalendarClient.getExternalCalendars(oAuthAccessToken); + + return new ExternalCalendarsResponse(externalCalendars); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/externalcalendar/dto/ExternalCalendar.java b/backend/src/main/java/com/allog/dallog/domain/externalcalendar/dto/ExternalCalendar.java new file mode 100644 index 00000000..48a76d1e --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/externalcalendar/dto/ExternalCalendar.java @@ -0,0 +1,23 @@ +package com.allog.dallog.domain.externalcalendar.dto; + +public class ExternalCalendar { + + private String calendarId; + private String summary; + + private ExternalCalendar() { + } + + public ExternalCalendar(final String calendarId, final String summary) { + this.calendarId = calendarId; + this.summary = summary; + } + + public String getCalendarId() { + return calendarId; + } + + public String getSummary() { + return summary; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/externalcalendar/dto/ExternalCalendarsResponse.java b/backend/src/main/java/com/allog/dallog/domain/externalcalendar/dto/ExternalCalendarsResponse.java new file mode 100644 index 00000000..6f45a9de --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/externalcalendar/dto/ExternalCalendarsResponse.java @@ -0,0 +1,19 @@ +package com.allog.dallog.domain.externalcalendar.dto; + +import java.util.List; + +public class ExternalCalendarsResponse { + + private List externalCalendars; + + private ExternalCalendarsResponse() { + } + + public ExternalCalendarsResponse(final List externalCalendars) { + this.externalCalendars = externalCalendars; + } + + public List getExternalCalendars() { + return externalCalendars; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDao.java b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDao.java new file mode 100644 index 00000000..38805d4a --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDao.java @@ -0,0 +1,61 @@ +package com.allog.dallog.domain.integrationschedule.dao; + +import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.integrationschedule.domain.Period; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.stereotype.Repository; + +@Repository +public class IntegrationScheduleDao { + + private final NamedParameterJdbcTemplate jdbcTemplate; + + public IntegrationScheduleDao(final NamedParameterJdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + public List findByCategoryIdInAndBetween(final List categoryIds, + final LocalDateTime startDateTime, + final LocalDateTime endDateTime) { + if (categoryIds.isEmpty()) { + return Collections.emptyList(); + } + + String sql = "SELECT s.id as id, c.id as categoryId, s.title as title, " + + "s.start_date_time as startDateTime, s.end_date_time as endDateTime, " + + "s.memo as memo, c.category_type as categoryType " + + "FROM schedules s " + + "JOIN categories c ON s.categories_id = c.id " + + "WHERE c.id IN (:categoryIds) " + + "AND s.start_date_time <= :endDateTime " + + "AND s.end_date_time >= :startDateTime "; + + MapSqlParameterSource parameterSource = new MapSqlParameterSource() + .addValue("categoryIds", categoryIds) + .addValue("startDateTime", startDateTime.toString()) + .addValue("endDateTime", endDateTime.toString()); + + return jdbcTemplate.query(sql, parameterSource, generateIntegrationSchedule()); + } + + private RowMapper generateIntegrationSchedule() { + return (resultSet, rowNum) -> { + Long id = resultSet.getLong("id"); + Long categoryId = resultSet.getLong("categoryId"); + String title = resultSet.getString("title"); + LocalDateTime startDateTime = resultSet.getTimestamp("startDateTime").toLocalDateTime(); + LocalDateTime endDateTime = resultSet.getTimestamp("endDateTime").toLocalDateTime(); + String memo = resultSet.getString("memo"); + String categoryType = resultSet.getString("categoryType"); + + Period period = new Period(startDateTime, endDateTime); + + return new IntegrationSchedule(String.valueOf(id), categoryId, title, period, memo, categoryType); + }; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedule.java b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedule.java new file mode 100644 index 00000000..190c84b8 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedule.java @@ -0,0 +1,120 @@ +package com.allog.dallog.domain.integrationschedule.domain; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; +import com.allog.dallog.domain.subscription.domain.Color; +import com.allog.dallog.domain.subscription.domain.Subscription; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; + +public class IntegrationSchedule { + + private static final int ONE_DAY = 1; + private static final int MIDNIGHT_HOUR = 23; + private static final int MIDNIGHT_MINUTE = 59; + + private final String id; + private final Long categoryId; + private final String title; + private final Period period; + private final String memo; + private final String categoryType; + + public IntegrationSchedule(final String id, final Long categoryId, final String title, + final LocalDateTime startDateTime, final LocalDateTime endDateTime, final String memo, + final String categoryType) { + this(id, categoryId, title, new Period(startDateTime, endDateTime), memo, categoryType); + } + + public IntegrationSchedule(final String id, final Long categoryId, final String title, final Period period, + final String memo, final String categoryType) { + this.id = id; + this.categoryId = categoryId; + this.title = title; + this.period = period; + this.memo = memo; + this.categoryType = categoryType; + } + + public boolean isLongTerms() { + return period.calculateDayDifference() >= ONE_DAY; + } + + public boolean isAllDays() { + return period.calculateDayDifference() < ONE_DAY + && period.calculateHourDifference() == MIDNIGHT_HOUR + && period.calculateMinuteDifference() == MIDNIGHT_MINUTE; + } + + public boolean isFewHours() { + return !isAllDays() + && period.calculateDayDifference() < ONE_DAY; + } + + public Color findSubscriptionColor(final List subscriptions) { + return subscriptions.stream() + .filter(this::isSameCategory) + .findAny() + .orElseThrow(() -> new NoSuchCategoryException("구독하지 않은 카테고리 입니다.")) + .getColor(); + } + + private boolean isSameCategory(final Subscription subscription) { + Category category = subscription.getCategory(); + return category.getId().equals(categoryId); + } + + public String getId() { + return id; + } + + public Long getCategoryId() { + return categoryId; + } + + public String getTitle() { + return title; + } + + public LocalDateTime getStartDateTime() { + return period.getStartDateTime(); + } + + public LocalDateTime getEndDateTime() { + return period.getEndDateTime(); + } + + public Period getPeriod() { + return period; + } + + public String getMemo() { + return memo; + } + + public String getCategoryType() { + return categoryType; + } + + // 중복 일정을 판별하기 위한 equals & hashCode + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + IntegrationSchedule that = (IntegrationSchedule) o; + return Objects.equals(id, that.id) && Objects.equals(categoryId, that.categoryId) + && Objects.equals(title, that.title) && Objects.equals(period, that.period) + && Objects.equals(memo, that.memo) && Objects.equals(categoryType, that.categoryType); + } + + @Override + public int hashCode() { + return Objects.hash(id, categoryId, title, period, memo, categoryType); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleComparator.java b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleComparator.java new file mode 100644 index 00000000..70d33096 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleComparator.java @@ -0,0 +1,45 @@ +package com.allog.dallog.domain.integrationschedule.domain; + +import java.time.LocalDateTime; +import java.util.Comparator; + +public class IntegrationScheduleComparator implements Comparator { + + private static final int BEFORE = -1; + private static final int AFTER = 1; + private static final int SAME = 0; + + @Override + public int compare(final IntegrationSchedule firstSchedule, final IntegrationSchedule secondSchedule) { + LocalDateTime firstScheduleStartDateTime = firstSchedule.getStartDateTime(); + LocalDateTime secondScheduleStartDateTime = secondSchedule.getStartDateTime(); + + if (firstScheduleStartDateTime.isBefore(secondScheduleStartDateTime)) { + return BEFORE; + } + if (firstScheduleStartDateTime.isAfter(secondScheduleStartDateTime)) { + return AFTER; + } + if (firstScheduleStartDateTime.isEqual(secondScheduleStartDateTime)) { + return compareEndDateTime(firstSchedule, secondSchedule); + } + return SAME; + } + + private int compareEndDateTime(IntegrationSchedule firstSchedule, IntegrationSchedule secondSchedule) { + LocalDateTime firstScheduleEndDateTime = firstSchedule.getEndDateTime(); + LocalDateTime secondScheduleEndDateTime = secondSchedule.getEndDateTime(); + + if (firstScheduleEndDateTime.isBefore(secondScheduleEndDateTime)) { + return AFTER; + } + if (firstScheduleEndDateTime.isAfter(secondScheduleEndDateTime)) { + return BEFORE; + } + return compareByTitle(firstSchedule, secondSchedule); + } + + private int compareByTitle(IntegrationSchedule firstSchedule, IntegrationSchedule secondSchedule) { + return firstSchedule.getTitle().compareTo(secondSchedule.getTitle()); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedules.java b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedules.java new file mode 100644 index 00000000..b6d42044 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedules.java @@ -0,0 +1,24 @@ +package com.allog.dallog.domain.integrationschedule.domain; + +import java.util.ArrayList; +import java.util.List; + +public class IntegrationSchedules { + + private static final IntegrationScheduleComparator COMPARATOR = new IntegrationScheduleComparator(); + + private final List values; + + public IntegrationSchedules() { + this.values = new ArrayList<>(); + } + + public void add(final IntegrationSchedule integrationSchedule) { + values.add(integrationSchedule); + } + + public List getSortedValues() { + values.sort(COMPARATOR); + return List.copyOf(values); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/Period.java b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/Period.java new file mode 100644 index 00000000..769ae36c --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/Period.java @@ -0,0 +1,96 @@ +package com.allog.dallog.domain.integrationschedule.domain; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class Period { + + private static final int ONE_HOUR = 60; + + private final LocalDateTime startDateTime; + private final LocalDateTime endDateTime; + + public Period(final LocalDateTime startDateTime, final LocalDateTime endDateTime) { + this.startDateTime = startDateTime; + this.endDateTime = endDateTime; + } + + public long calculateDayDifference() { + LocalDate startDate = LocalDate.from(startDateTime); + LocalDate endDate = LocalDate.from(endDateTime); + return ChronoUnit.DAYS.between(startDate, endDate); + } + + public long calculateHourDifference() { + LocalTime startTime = LocalTime.from(startDateTime); + LocalTime endTime = LocalTime.from(endDateTime); + return ChronoUnit.HOURS.between(startTime, endTime); + } + + public long calculateMinuteDifference() { + LocalTime startTime = LocalTime.from(startDateTime); + LocalTime endTime = LocalTime.from(endDateTime); + return ChronoUnit.MINUTES.between(startTime, endTime) % ONE_HOUR; + } + + public List slice(final Period otherPeriod) { + if (isNotOverlapped(otherPeriod)) { + return List.of(this); + } + + return sliceByOtherPeriod(otherPeriod); + } + + private List sliceByOtherPeriod(final Period otherPeriod) { + List periods = new ArrayList<>(); + if (startDateTime.isBefore(otherPeriod.startDateTime)) { + periods.add(new Period(startDateTime, otherPeriod.startDateTime)); + } + + if (otherPeriod.endDateTime.isBefore(endDateTime)) { + periods.add(new Period(otherPeriod.endDateTime, endDateTime)); + } + + return periods; + } + + private boolean isNotOverlapped(final Period otherPeriod) { + boolean farFromLeftSideOfBase = otherPeriod.endDateTime.isBefore(startDateTime); + // other가 좌측 방향으로 멀리 떨어져 겹치지 않을때 + + boolean farFromRightSideOfBase = otherPeriod.startDateTime.isAfter(endDateTime); + // other가 우측 방향으로 멀리 떨어져 겹치지 않을때 + + return farFromLeftSideOfBase || farFromRightSideOfBase; + } + + public LocalDateTime getStartDateTime() { + return startDateTime; + } + + public LocalDateTime getEndDateTime() { + return endDateTime; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Period period = (Period) o; + return Objects.equals(startDateTime, period.startDateTime) && Objects.equals(endDateTime, period.endDateTime); + } + + @Override + public int hashCode() { + return Objects.hash(startDateTime, endDateTime); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/ScheduleType.java b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/ScheduleType.java new file mode 100644 index 00000000..1605103c --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/ScheduleType.java @@ -0,0 +1,30 @@ +package com.allog.dallog.domain.integrationschedule.domain; + +import java.util.Arrays; +import java.util.function.Predicate; + +public enum ScheduleType { + + LONG_TERMS("longTerms", IntegrationSchedule::isLongTerms), + ALL_DAYS("allDays", IntegrationSchedule::isAllDays), + FEW_HOURS("fewHours", IntegrationSchedule::isFewHours); + + private final String name; + private final Predicate isMatch; + + ScheduleType(final String name, final Predicate isMatch) { + this.name = name; + this.isMatch = isMatch; + } + + public static ScheduleType from(final IntegrationSchedule integrationSchedule) { + return Arrays.stream(values()) + .filter(type -> type.isMatch.test(integrationSchedule)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("일치하는 일정 종류가 존재하지 않습니다.")); + } + + public String getName() { + return name; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/TypedSchedules.java b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/TypedSchedules.java new file mode 100644 index 00000000..de5ad6c4 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/integrationschedule/domain/TypedSchedules.java @@ -0,0 +1,30 @@ +package com.allog.dallog.domain.integrationschedule.domain; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TypedSchedules { + + private Map values; + + public TypedSchedules(final List integrationSchedules) { + initializeValues(); + for (IntegrationSchedule integrationSchedule : integrationSchedules) { + ScheduleType scheduleType = ScheduleType.from(integrationSchedule); + IntegrationSchedules sortedSchedules = values.get(scheduleType); + sortedSchedules.add(integrationSchedule); + } + } + + private void initializeValues() { + this.values = new HashMap<>(); + for (ScheduleType type : ScheduleType.values()) { + values.put(type, new IntegrationSchedules()); + } + } + + public IntegrationSchedules getSortedSchedules(final ScheduleType scheduleType) { + return values.get(scheduleType); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/member/application/MemberService.java b/backend/src/main/java/com/allog/dallog/domain/member/application/MemberService.java new file mode 100644 index 00000000..53cd1307 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/member/application/MemberService.java @@ -0,0 +1,79 @@ +package com.allog.dallog.domain.member.application; + +import com.allog.dallog.domain.auth.domain.OAuthTokenRepository; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.member.dto.MemberUpdateRequest; +import com.allog.dallog.domain.member.exception.NoSuchMemberException; +import com.allog.dallog.domain.subscription.domain.Subscription; +import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; +import com.allog.dallog.domain.subscription.exception.NoSuchSubscriptionException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(readOnly = true) +@Service +public class MemberService { + + private final MemberRepository memberRepository; + private final SubscriptionRepository subscriptionRepository; + private final OAuthTokenRepository oAuthTokenRepository; + + public MemberService(final MemberRepository memberRepository, final SubscriptionRepository subscriptionRepository, + final OAuthTokenRepository oAuthTokenRepository) { + this.memberRepository = memberRepository; + this.subscriptionRepository = subscriptionRepository; + this.oAuthTokenRepository = oAuthTokenRepository; + } + + @Transactional + public MemberResponse save(final Member member) { + Member newMember = memberRepository.save(member); + return new MemberResponse(newMember); + } + + public MemberResponse findById(final Long id) { + return new MemberResponse(getMember(id)); + } + + public MemberResponse findBySubscriptionId(final Long subscriptionId) { + Subscription subscription = subscriptionRepository.findById(subscriptionId) + .orElseThrow(NoSuchSubscriptionException::new); + + Member member = subscription.getMember(); + return new MemberResponse(member); + } + + @Transactional + public void update(final Long id, final MemberUpdateRequest request) { + Member member = getMember(id); + member.change(request.getDisplayName()); + } + + @Transactional + public void deleteById(final Long id) { + oAuthTokenRepository.deleteByMemberId(id); + memberRepository.deleteById(id); + } + + public Member getMember(final Long id) { + return memberRepository.findById(id) + .orElseThrow(NoSuchMemberException::new); + } + + public Member getByEmail(final String email) { + return memberRepository.findByEmail(email) + .orElseThrow(NoSuchMemberException::new); + } + + public boolean existsByEmail(final String email) { + return memberRepository.existsByEmail(email); + } + + public void validateExistsMember(final Long id) { + if (!memberRepository.existsById(id)) { + throw new NoSuchMemberException("존재하지 않는 회원입니다."); + } + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/member/domain/Member.java b/backend/src/main/java/com/allog/dallog/domain/member/domain/Member.java new file mode 100644 index 00000000..17bf3518 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/member/domain/Member.java @@ -0,0 +1,92 @@ +package com.allog.dallog.domain.member.domain; + +import com.allog.dallog.domain.common.BaseEntity; +import com.allog.dallog.domain.member.exception.InvalidMemberException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +@Table(name = "members") +@Entity +public class Member extends BaseEntity { + + private static final Pattern EMAIL_PATTERN = Pattern.compile("^[a-z0-9._-]+@[a-z]+[.]+[a-z]{2,3}$"); + private static final int MAX_DISPLAY_NAME_LENGTH = 10; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "email", nullable = false) + private String email; + + @Column(name = "display_name", nullable = false) + private String displayName; + + @Column(name = "profile_image_url", nullable = false) + private String profileImageUrl; + + @Enumerated(value = EnumType.STRING) + @Column(name = "social_type", nullable = false) + private SocialType socialType; + + protected Member() { + } + + public Member(final String email, final String displayName, final String profileImageUrl, + final SocialType socialType) { + validateEmail(email); + validateDisplayName(displayName); + + this.email = email; + this.displayName = displayName; + this.profileImageUrl = profileImageUrl; + this.socialType = socialType; + } + + private void validateEmail(final String email) { + Matcher matcher = EMAIL_PATTERN.matcher(email); + if (!matcher.matches()) { + throw new InvalidMemberException("이메일 형식이 올바르지 않습니다."); + } + } + + private void validateDisplayName(final String displayName) { + if (displayName.isEmpty() || displayName.length() > MAX_DISPLAY_NAME_LENGTH) { + throw new InvalidMemberException("이름 형식이 올바르지 않습니다."); + } + } + + public void change(final String displayName) { + validateDisplayName(displayName); + this.displayName = displayName; + } + + public Long getId() { + return id; + } + + public String getEmail() { + return email; + } + + public String getDisplayName() { + return displayName; + } + + public String getProfileImageUrl() { + return profileImageUrl; + } + + public SocialType getSocialType() { + return socialType; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/member/domain/MemberRepository.java b/backend/src/main/java/com/allog/dallog/domain/member/domain/MemberRepository.java new file mode 100644 index 00000000..602d671b --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/member/domain/MemberRepository.java @@ -0,0 +1,11 @@ +package com.allog.dallog.domain.member.domain; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MemberRepository extends JpaRepository { + + Optional findByEmail(final String email); + + boolean existsByEmail(final String email); +} diff --git a/backend/src/main/java/com/allog/dallog/domain/member/domain/SocialType.java b/backend/src/main/java/com/allog/dallog/domain/member/domain/SocialType.java new file mode 100644 index 00000000..4dcb0249 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/member/domain/SocialType.java @@ -0,0 +1,6 @@ +package com.allog.dallog.domain.member.domain; + +public enum SocialType { + + GOOGLE, GITHUB; +} diff --git a/backend/src/main/java/com/allog/dallog/domain/member/dto/MemberResponse.java b/backend/src/main/java/com/allog/dallog/domain/member/dto/MemberResponse.java new file mode 100644 index 00000000..84038b9e --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/member/dto/MemberResponse.java @@ -0,0 +1,50 @@ +package com.allog.dallog.domain.member.dto; + +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.SocialType; + +public class MemberResponse { + + private Long id; + private String email; + private String displayName; + private String profileImageUrl; + private SocialType socialType; + + private MemberResponse() { + } + + public MemberResponse(final Long id, final String email, final String displayName, final String profileImageUrl, + final SocialType socialType) { + this.id = id; + this.email = email; + this.displayName = displayName; + this.profileImageUrl = profileImageUrl; + this.socialType = socialType; + } + + public MemberResponse(final Member member) { + this(member.getId(), member.getEmail(), member.getDisplayName(), member.getProfileImageUrl(), + member.getSocialType()); + } + + public Long getId() { + return id; + } + + public String getEmail() { + return email; + } + + public String getDisplayName() { + return displayName; + } + + public String getProfileImageUrl() { + return profileImageUrl; + } + + public SocialType getSocialType() { + return socialType; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/member/dto/MemberUpdateRequest.java b/backend/src/main/java/com/allog/dallog/domain/member/dto/MemberUpdateRequest.java new file mode 100644 index 00000000..e3e9ad48 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/member/dto/MemberUpdateRequest.java @@ -0,0 +1,20 @@ +package com.allog.dallog.domain.member.dto; + +import javax.validation.constraints.NotBlank; + +public class MemberUpdateRequest { + + @NotBlank(message = "공백일 수 없습니다.") + private String displayName; + + private MemberUpdateRequest() { + } + + public MemberUpdateRequest(final String displayName) { + this.displayName = displayName; + } + + public String getDisplayName() { + return displayName; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/member/exception/InvalidMemberException.java b/backend/src/main/java/com/allog/dallog/domain/member/exception/InvalidMemberException.java new file mode 100644 index 00000000..e7dc859c --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/member/exception/InvalidMemberException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.member.exception; + +public class InvalidMemberException extends RuntimeException { + + public InvalidMemberException(final String message) { + super(message); + } + + public InvalidMemberException() { + this("잘못된 회원의 정보입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/member/exception/NoSuchMemberException.java b/backend/src/main/java/com/allog/dallog/domain/member/exception/NoSuchMemberException.java new file mode 100644 index 00000000..11193954 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/member/exception/NoSuchMemberException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.member.exception; + +public class NoSuchMemberException extends RuntimeException { + + public NoSuchMemberException(final String message) { + super(message); + } + + public NoSuchMemberException() { + this("존재하지 않는 회원입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/application/ScheduleService.java b/backend/src/main/java/com/allog/dallog/domain/schedule/application/ScheduleService.java new file mode 100644 index 00000000..e4771bc5 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/application/ScheduleService.java @@ -0,0 +1,68 @@ +package com.allog.dallog.domain.schedule.application; + +import com.allog.dallog.domain.auth.exception.NoPermissionException; +import com.allog.dallog.domain.category.application.CategoryService; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.schedule.domain.Schedule; +import com.allog.dallog.domain.schedule.domain.ScheduleRepository; +import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; +import com.allog.dallog.domain.schedule.dto.request.ScheduleUpdateRequest; +import com.allog.dallog.domain.schedule.dto.response.ScheduleResponse; +import com.allog.dallog.domain.schedule.exception.NoSuchScheduleException; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(readOnly = true) +@Service +public class ScheduleService { + + private final ScheduleRepository scheduleRepository; + private final CategoryService categoryService; + + public ScheduleService(final ScheduleRepository scheduleRepository, final CategoryService categoryService) { + this.scheduleRepository = scheduleRepository; + this.categoryService = categoryService; + } + + @Transactional + public ScheduleResponse save(final Long memberId, final Long categoryId, final ScheduleCreateRequest request) { + Category category = categoryService.getCategory(categoryId); + categoryService.validateCreatorBy(memberId, category); + validateCategoryType(category); + + Schedule schedule = scheduleRepository.save(request.toEntity(category)); + return new ScheduleResponse(schedule); + } + + private static void validateCategoryType(final Category category) { + if (category.isExternal()) { + throw new NoPermissionException("외부 연동 카테고리에는 일정을 추가할 수 없습니다."); + } + } + + public ScheduleResponse findById(final Long id) { + Schedule schedule = getSchedule(id); + + return new ScheduleResponse(schedule); + } + + private Schedule getSchedule(Long id) { + return scheduleRepository.findById(id) + .orElseThrow(NoSuchScheduleException::new); + } + + @Transactional + public void update(final Long id, final Long memberId, final ScheduleUpdateRequest request) { + Schedule schedule = getSchedule(id); + categoryService.validateCreatorBy(memberId, schedule.getCategory()); + schedule.change(request.getTitle(), request.getStartDateTime(), request.getEndDateTime(), request.getMemo()); + } + + @Transactional + public void deleteById(final Long id, final Long memberId) { + Schedule schedule = getSchedule(id); + + categoryService.validateCreatorBy(memberId, schedule.getCategory()); + scheduleRepository.deleteById(id); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/domain/Schedule.java b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/Schedule.java new file mode 100644 index 00000000..078552b1 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/Schedule.java @@ -0,0 +1,112 @@ +package com.allog.dallog.domain.schedule.domain; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.common.BaseEntity; +import com.allog.dallog.domain.schedule.exception.InvalidScheduleException; +import java.time.LocalDateTime; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Table(name = "schedules") +@Entity +public class Schedule extends BaseEntity { + + private static final int MAX_TITLE_LENGTH = 20; + private static final int MAX_MEMO_LENGTH = 255; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "categories_id", nullable = false) + private Category category; + + @Column(name = "title", nullable = false) + private String title; + + @Column(name = "start_date_time", nullable = false) + private LocalDateTime startDateTime; + + @Column(name = "end_date_time", nullable = false) + private LocalDateTime endDateTime; + + @Column(name = "memo", nullable = false) + private String memo; + + protected Schedule() { + } + + public Schedule(final Category category, final String title, final LocalDateTime startDateTime, + final LocalDateTime endDateTime, final String memo) { + validateTitleLength(title); + validatePeriod(startDateTime, endDateTime); + validateMemoLength(memo); + this.category = category; + this.title = title; + this.startDateTime = startDateTime; + this.endDateTime = endDateTime; + this.memo = memo; + } + + public void change(final String title, final LocalDateTime startDateTime, final LocalDateTime endDateTime, + final String memo) { + validateTitleLength(title); + validatePeriod(startDateTime, endDateTime); + validateMemoLength(memo); + this.title = title; + this.startDateTime = startDateTime; + this.endDateTime = endDateTime; + this.memo = memo; + } + + private void validateTitleLength(final String title) { + if (title.length() > MAX_TITLE_LENGTH) { + throw new InvalidScheduleException("일정 제목의 길이는 20을 초과할 수 없습니다."); + } + } + + private void validatePeriod(final LocalDateTime startDateTime, final LocalDateTime endDateTime) { + if (startDateTime.isAfter(endDateTime)) { + throw new InvalidScheduleException("종료일시가 시작일시보다 이전일 수 없습니다."); + } + } + + private void validateMemoLength(final String memo) { + if (memo.length() > MAX_MEMO_LENGTH) { + throw new InvalidScheduleException("일정 메모의 길이는 255를 초과할 수 없습니다."); + } + } + + public Long getId() { + return id; + } + + public String getTitle() { + return title; + } + + public LocalDateTime getStartDateTime() { + return startDateTime; + } + + public LocalDateTime getEndDateTime() { + return endDateTime; + } + + public String getMemo() { + return memo; + } + + public Category getCategory() { + return category; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/domain/ScheduleRepository.java b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/ScheduleRepository.java new file mode 100644 index 00000000..a45e93bf --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/ScheduleRepository.java @@ -0,0 +1,9 @@ +package com.allog.dallog.domain.schedule.domain; + +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ScheduleRepository extends JpaRepository { + + void deleteByCategoryIdIn(final List categoryIds); +} diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/domain/scheduler/Scheduler.java b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/scheduler/Scheduler.java new file mode 100644 index 00000000..4b39a6e8 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/domain/scheduler/Scheduler.java @@ -0,0 +1,40 @@ +package com.allog.dallog.domain.schedule.domain.scheduler; + +import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.integrationschedule.domain.Period; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +public class Scheduler { + + private final List schedules; + private final LocalDateTime startDateTime; + private final LocalDateTime endDateTime; + + public Scheduler(final List schedules, final LocalDateTime startDateTime, + final LocalDateTime endDate) { + this.schedules = schedules; + this.startDateTime = startDateTime; + this.endDateTime = endDate; + } + + public List getPeriods() { + List periods = new ArrayList<>(); + Period initialBasePeriod = new Period(startDateTime, endDateTime); + periods.add(initialBasePeriod); + + for (IntegrationSchedule schedule : schedules) { + slicePeriod(periods, schedule); + } + + return periods; + } + + private void slicePeriod(final List periods, final IntegrationSchedule schedule) { + for (Period period : List.copyOf(periods)) { + periods.remove(period); + periods.addAll(period.slice(schedule.getPeriod())); + } + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/DateRangeRequest.java b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/DateRangeRequest.java new file mode 100644 index 00000000..e3f56afa --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/DateRangeRequest.java @@ -0,0 +1,33 @@ +package com.allog.dallog.domain.schedule.dto.request; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class DateRangeRequest { + + private static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm"; + + private LocalDateTime startDateTime; + private LocalDateTime endDateTime; + + public DateRangeRequest(final String startDateTime, final String endDateTime) { + this.startDateTime = LocalDateTime.parse(startDateTime, DateTimeFormatter.ofPattern(DATE_FORMAT)); + this.endDateTime = LocalDateTime.parse(endDateTime, DateTimeFormatter.ofPattern(DATE_FORMAT)); + } + + // TODO: 리팩토링 + public static DateRangeRequest of(final LocalDateTime startDateTime, final LocalDateTime endDateTime) { + String startDateTimeFormat = startDateTime.format(DateTimeFormatter.ofPattern(DATE_FORMAT)); + String endDateTimeFormat = endDateTime.format(DateTimeFormatter.ofPattern(DATE_FORMAT)); + + return new DateRangeRequest(startDateTimeFormat, endDateTimeFormat); + } + + public LocalDateTime getStartDateTime() { + return startDateTime; + } + + public LocalDateTime getEndDateTime() { + return endDateTime; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/ScheduleCreateRequest.java b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/ScheduleCreateRequest.java new file mode 100644 index 00000000..239434e4 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/ScheduleCreateRequest.java @@ -0,0 +1,53 @@ +package com.allog.dallog.domain.schedule.dto.request; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.schedule.domain.Schedule; +import java.time.LocalDateTime; +import javax.validation.constraints.NotNull; +import org.springframework.format.annotation.DateTimeFormat; + +public class ScheduleCreateRequest { + + @NotNull(message = "Null일 수 없습니다.") + private String title; + + @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm") + private LocalDateTime startDateTime; + + @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm") + private LocalDateTime endDateTime; + + @NotNull(message = "Null일 수 없습니다.") + private String memo; + + private ScheduleCreateRequest() { + } + + public ScheduleCreateRequest(final String title, final LocalDateTime startDateTime, final LocalDateTime endDateTime, + final String memo) { + this.title = title; + this.startDateTime = startDateTime; + this.endDateTime = endDateTime; + this.memo = memo; + } + + public Schedule toEntity(final Category category) { + return new Schedule(category, title, startDateTime, endDateTime, memo); + } + + public String getTitle() { + return title; + } + + public LocalDateTime getStartDateTime() { + return startDateTime; + } + + public LocalDateTime getEndDateTime() { + return endDateTime; + } + + public String getMemo() { + return memo; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/ScheduleUpdateRequest.java b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/ScheduleUpdateRequest.java new file mode 100644 index 00000000..3744671b --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/request/ScheduleUpdateRequest.java @@ -0,0 +1,48 @@ +package com.allog.dallog.domain.schedule.dto.request; + +import java.time.LocalDateTime; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import org.springframework.format.annotation.DateTimeFormat; + +public class ScheduleUpdateRequest { + + @NotNull(message = "Null일 수 없습니다.") + private String title; + + @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm") + private LocalDateTime startDateTime; + + @DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm") + private LocalDateTime endDateTime; + + @NotNull(message = "Null일 수 없습니다.") + private String memo; + + private ScheduleUpdateRequest() { + } + + public ScheduleUpdateRequest(final String title, final LocalDateTime startDateTime, final LocalDateTime endDateTime, + final String memo) { + this.title = title; + this.startDateTime = startDateTime; + this.endDateTime = endDateTime; + this.memo = memo; + } + + public String getTitle() { + return title; + } + + public LocalDateTime getStartDateTime() { + return startDateTime; + } + + public LocalDateTime getEndDateTime() { + return endDateTime; + } + + public String getMemo() { + return memo; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponse.java b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponse.java new file mode 100644 index 00000000..82299165 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponse.java @@ -0,0 +1,68 @@ +package com.allog.dallog.domain.schedule.dto.response; + +import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.subscription.domain.Color; +import java.time.LocalDateTime; + +public class MemberScheduleResponse { + + private final String id; + private final String title; + private final LocalDateTime startDateTime; + private final LocalDateTime endDateTime; + private final String memo; + private final Long categoryId; + private final String colorCode; + private final String categoryType; + + public MemberScheduleResponse(final IntegrationSchedule integrationSchedule, final Color color) { + this(integrationSchedule.getId(), integrationSchedule.getTitle(), integrationSchedule.getStartDateTime(), + integrationSchedule.getEndDateTime(), integrationSchedule.getMemo(), + integrationSchedule.getCategoryId(), color.getColorCode(), integrationSchedule.getCategoryType()); + } + + public MemberScheduleResponse(final String id, final String title, final LocalDateTime startDateTime, + final LocalDateTime endDateTime, final String memo, final Long categoryId, + final String colorCode, final String categoryType) { + this.id = id; + this.title = title; + this.startDateTime = startDateTime; + this.endDateTime = endDateTime; + this.memo = memo; + this.categoryId = categoryId; + this.colorCode = colorCode; + this.categoryType = categoryType; + } + + public String getId() { + return id; + } + + public String getTitle() { + return title; + } + + public LocalDateTime getStartDateTime() { + return startDateTime; + } + + public LocalDateTime getEndDateTime() { + return endDateTime; + } + + public String getMemo() { + return memo; + } + + public Long getCategoryId() { + return categoryId; + } + + public String getColorCode() { + return colorCode; + } + + public String getCategoryType() { + return categoryType; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponses.java b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponses.java new file mode 100644 index 00000000..0f63ec4c --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/MemberScheduleResponses.java @@ -0,0 +1,51 @@ +package com.allog.dallog.domain.schedule.dto.response; + +import com.allog.dallog.domain.integrationschedule.domain.ScheduleType; +import com.allog.dallog.domain.integrationschedule.domain.TypedSchedules; +import com.allog.dallog.domain.subscription.domain.Subscription; +import java.util.List; +import java.util.stream.Collectors; + +public class MemberScheduleResponses { + + // TODO: 리팩토링 + private final List longTerms; + private final List allDays; + private final List fewHours; + + public MemberScheduleResponses(final List longTerms, + final List allDays, + final List fewHours) { + this.longTerms = longTerms; + this.allDays = allDays; + this.fewHours = fewHours; + } + + public MemberScheduleResponses(final List subscriptions, final TypedSchedules typedSchedules) { + this.longTerms = getColoredScheduleResponses(ScheduleType.LONG_TERMS, subscriptions, typedSchedules); + this.allDays = getColoredScheduleResponses(ScheduleType.ALL_DAYS, subscriptions, typedSchedules); + this.fewHours = getColoredScheduleResponses(ScheduleType.FEW_HOURS, subscriptions, typedSchedules); + } + + private List getColoredScheduleResponses(final ScheduleType scheduleType, + final List subscriptions, + final TypedSchedules typedSchedules) { + return typedSchedules.getSortedSchedules(scheduleType) + .getSortedValues() + .stream() + .map(schedule -> new MemberScheduleResponse(schedule, schedule.findSubscriptionColor(subscriptions))) + .collect(Collectors.toList()); + } + + public List getLongTerms() { + return longTerms; + } + + public List getAllDays() { + return allDays; + } + + public List getFewHours() { + return fewHours; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/PeriodResponse.java b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/PeriodResponse.java new file mode 100644 index 00000000..3d79d5e0 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/PeriodResponse.java @@ -0,0 +1,23 @@ +package com.allog.dallog.domain.schedule.dto.response; + +import com.allog.dallog.domain.integrationschedule.domain.Period; +import java.time.LocalDateTime; + +public class PeriodResponse { + + private final LocalDateTime startDateTime; + private final LocalDateTime endDateTime; + + public PeriodResponse(final Period period) { + this.startDateTime = period.getStartDateTime(); + this.endDateTime = period.getEndDateTime(); + } + + public LocalDateTime getStartDateTime() { + return startDateTime; + } + + public LocalDateTime getEndDateTime() { + return endDateTime; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/ScheduleResponse.java b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/ScheduleResponse.java new file mode 100644 index 00000000..ad1003ae --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/dto/response/ScheduleResponse.java @@ -0,0 +1,59 @@ +package com.allog.dallog.domain.schedule.dto.response; + +import com.allog.dallog.domain.schedule.domain.Schedule; +import java.time.LocalDateTime; + +public class ScheduleResponse { + + private final Long id; + private final Long categoryId; + private final String title; + private final LocalDateTime startDateTime; + private final LocalDateTime endDateTime; + private final String memo; + private final String categoryType; + + public ScheduleResponse(final Schedule schedule) { + this(schedule.getId(), schedule.getCategory().getId(), schedule.getTitle(), schedule.getStartDateTime(), + schedule.getEndDateTime(), schedule.getMemo(), schedule.getCategory().getCategoryType().name()); + } + + public ScheduleResponse(final Long id, final Long categoryId, final String title, final LocalDateTime startDateTime, + final LocalDateTime endDateTime, final String memo, final String categoryType) { + this.id = id; + this.categoryId = categoryId; + this.title = title; + this.startDateTime = startDateTime; + this.endDateTime = endDateTime; + this.memo = memo; + this.categoryType = categoryType; + } + + public Long getId() { + return id; + } + + public Long getCategoryId() { + return categoryId; + } + + public String getTitle() { + return title; + } + + public LocalDateTime getStartDateTime() { + return startDateTime; + } + + public LocalDateTime getEndDateTime() { + return endDateTime; + } + + public String getMemo() { + return memo; + } + + public String getCategoryType() { + return categoryType; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/exception/InvalidScheduleException.java b/backend/src/main/java/com/allog/dallog/domain/schedule/exception/InvalidScheduleException.java new file mode 100644 index 00000000..ac5c6a39 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/exception/InvalidScheduleException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.schedule.exception; + +public class InvalidScheduleException extends RuntimeException { + + public InvalidScheduleException(final String message) { + super(message); + } + + public InvalidScheduleException() { + this("잘못된 일정입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/schedule/exception/NoSuchScheduleException.java b/backend/src/main/java/com/allog/dallog/domain/schedule/exception/NoSuchScheduleException.java new file mode 100644 index 00000000..b3ad53b4 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/schedule/exception/NoSuchScheduleException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.schedule.exception; + +public class NoSuchScheduleException extends RuntimeException { + + public NoSuchScheduleException(final String message) { + super(message); + } + + public NoSuchScheduleException() { + this("존재하지 않는 일정입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/application/SubscriptionService.java b/backend/src/main/java/com/allog/dallog/domain/subscription/application/SubscriptionService.java new file mode 100644 index 00000000..85b3f064 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/application/SubscriptionService.java @@ -0,0 +1,132 @@ +package com.allog.dallog.domain.subscription.application; + +import com.allog.dallog.domain.auth.exception.NoPermissionException; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.CategoryRepository; +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import com.allog.dallog.domain.member.exception.NoSuchMemberException; +import com.allog.dallog.domain.subscription.domain.Color; +import com.allog.dallog.domain.subscription.domain.ColorPickerStrategy; +import com.allog.dallog.domain.subscription.domain.Subscription; +import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; +import com.allog.dallog.domain.subscription.dto.request.SubscriptionUpdateRequest; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionsResponse; +import com.allog.dallog.domain.subscription.exception.ExistSubscriptionException; +import com.allog.dallog.domain.subscription.exception.NoSuchSubscriptionException; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(readOnly = true) +@Service +public class SubscriptionService { + + private static final ColorPickerStrategy PICK_RANDOM_STRATEGY + = () -> ThreadLocalRandom.current().nextInt(Color.values().length); + + private final SubscriptionRepository subscriptionRepository; + private final MemberRepository memberRepository; + private final CategoryRepository categoryRepository; + + public SubscriptionService(final SubscriptionRepository subscriptionRepository, + final MemberRepository memberRepository, + final CategoryRepository categoryRepository) { + this.subscriptionRepository = subscriptionRepository; + this.memberRepository = memberRepository; + this.categoryRepository = categoryRepository; + } + + @Transactional + public SubscriptionResponse save(final Long memberId, final Long categoryId) { + validateAlreadyExists(memberId, categoryId); + + Member member = memberRepository.findById(memberId) + .orElseThrow(NoSuchMemberException::new); + Category category = categoryRepository.findById(categoryId) + .orElseThrow(NoSuchCategoryException::new); + validatePermission(memberId, category); + + Color color = Color.pickAny(PICK_RANDOM_STRATEGY); + Subscription subscription = subscriptionRepository.save(new Subscription(member, category, color)); + return new SubscriptionResponse(subscription); + } + + private void validateAlreadyExists(final Long memberId, final Long categoryId) { + if (subscriptionRepository.existsByMemberIdAndCategoryId(memberId, categoryId)) { + throw new ExistSubscriptionException(); + } + } + + private void validatePermission(final Long memberId, final Category category) { + if (category.isPersonal() && !category.isCreator(memberId)) { + throw new NoPermissionException("구독 권한이 없는 카테고리입니다."); + } + } + + public SubscriptionsResponse findByMemberId(final Long memberId) { + List subscriptions = subscriptionRepository.findByMemberId(memberId); + + List subscriptionResponses = subscriptions.stream() + .map(SubscriptionResponse::new) + .collect(Collectors.toList()); + + return new SubscriptionsResponse(subscriptionResponses); + } + + public SubscriptionResponse findById(final Long id) { + Subscription subscription = getSubscription(id); + + return new SubscriptionResponse(subscription); + } + + public List findByCategoryId(final Long categoryId) { + return subscriptionRepository.findByCategoryId(categoryId) + .stream() + .map(SubscriptionResponse::new) + .collect(Collectors.toList()); + } + + public List getAllByMemberId(final Long memberId) { + return subscriptionRepository.findByMemberId(memberId); + } + + @Transactional + public void update(final Long id, final Long memberId, final SubscriptionUpdateRequest request) { + validateSubscriptionPermission(id, memberId); + + Subscription subscription = getSubscription(id); + subscription.change(request.getColor(), request.isChecked()); + } + + private Subscription getSubscription(final Long id) { + return subscriptionRepository.findById(id) + .orElseThrow(NoSuchSubscriptionException::new); + } + + @Transactional + public void deleteById(final Long id, final Long memberId) { + Subscription subscription = getSubscription(id); + + validateSubscriptionPermission(id, memberId); + validateCategoryCreator(subscription.getCategory(), memberId); + + subscriptionRepository.deleteById(id); + } + + private void validateSubscriptionPermission(final Long id, final Long memberId) { + if (!subscriptionRepository.existsByIdAndMemberId(id, memberId)) { + throw new NoPermissionException(); + } + } + + private void validateCategoryCreator(final Category category, final Long memberId) { + if (category.isCreator(memberId)) { + throw new NoPermissionException("내가 만든 카테고리는 구독 취소 할 수 없습니다."); + } + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Color.java b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Color.java new file mode 100644 index 00000000..7dabec95 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Color.java @@ -0,0 +1,53 @@ +package com.allog.dallog.domain.subscription.domain; + +import com.allog.dallog.domain.subscription.exception.InvalidSubscriptionException; +import java.util.Arrays; + +public enum Color { + + COLOR_1("#AD1457"), + COLOR_2("#D81B60"), + COLOR_3("#D50000"), + COLOR_4("#E67C73"), + COLOR_5("#F4511E"), + COLOR_6("#EF6C00"), + COLOR_7("#F09300"), + COLOR_8("#F6BF26"), + COLOR_9("#E4C441"), + COLOR_10("#C0CA33"), + COLOR_11("#7CB342"), + COLOR_12("#33B679"), + COLOR_13("#0B8043"), + COLOR_14("#009688"), + COLOR_15("#039BE5"), + COLOR_16("#4285F4"), + COLOR_17("#3F51B5"), + COLOR_18("#7986CB"), + COLOR_19("#B39DDB"), + COLOR_20("#9E69AF"), + COLOR_21("#8E24AA"), + COLOR_22("#795548"), + COLOR_23("#616161"), + COLOR_24("#A79B8E"); + + private final String colorCode; + + Color(final String colorCode) { + this.colorCode = colorCode; + } + + public static Color pickAny(ColorPickerStrategy strategy) { + return Color.values()[strategy.pickNumber()]; + } + + public static Color from(final String colorCode) { + return Arrays.stream(Color.values()) + .filter(color -> color.getColorCode().equals(colorCode.toUpperCase())) + .findFirst() + .orElseThrow(() -> new InvalidSubscriptionException("(" + colorCode + ")는 사용할 수 없는 색상입니다.")); + } + + public String getColorCode() { + return colorCode; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/domain/ColorPickerStrategy.java b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/ColorPickerStrategy.java new file mode 100644 index 00000000..3365655f --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/ColorPickerStrategy.java @@ -0,0 +1,7 @@ +package com.allog.dallog.domain.subscription.domain; + +@FunctionalInterface +public interface ColorPickerStrategy { + + int pickNumber(); +} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Subscription.java b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Subscription.java new file mode 100644 index 00000000..6379880d --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/Subscription.java @@ -0,0 +1,76 @@ +package com.allog.dallog.domain.subscription.domain; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.common.BaseEntity; +import com.allog.dallog.domain.member.domain.Member; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +@Table(name = "subscriptions") +@Entity +public class Subscription extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "members_id", nullable = false) + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "categories_id", nullable = false) + private Category category; + + @Enumerated(value = EnumType.STRING) + @Column(name = "color", nullable = false) + private Color color; + + @Column(name = "checked", nullable = false) + private boolean checked; + + protected Subscription() { + } + + public Subscription(final Member member, final Category category, final Color color) { + this.member = member; + this.category = category; + this.color = color; + this.checked = true; + } + + public void change(final Color color, final boolean checked) { + this.color = color; + this.checked = checked; + } + + public Long getId() { + return id; + } + + public Member getMember() { + return member; + } + + public Category getCategory() { + return category; + } + + public Color getColor() { + return color; + } + + public boolean isChecked() { + return checked; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepository.java b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepository.java new file mode 100644 index 00000000..f6217765 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepository.java @@ -0,0 +1,17 @@ +package com.allog.dallog.domain.subscription.domain; + +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface SubscriptionRepository extends JpaRepository { + + boolean existsByMemberIdAndCategoryId(final Long memberId, final Long categoryId); + + List findByMemberId(final Long memberId); + + List findByCategoryId(final Long categoryId); + + boolean existsByIdAndMemberId(final Long id, final Long memberId); + + void deleteByCategoryIdIn(final List categoryIds); +} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/dto/request/SubscriptionUpdateRequest.java b/backend/src/main/java/com/allog/dallog/domain/subscription/dto/request/SubscriptionUpdateRequest.java new file mode 100644 index 00000000..c1e62cbb --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/dto/request/SubscriptionUpdateRequest.java @@ -0,0 +1,37 @@ +package com.allog.dallog.domain.subscription.dto.request; + +import com.allog.dallog.domain.subscription.domain.Color; +import com.fasterxml.jackson.annotation.JsonIgnore; +import javax.validation.constraints.NotBlank; + +public class SubscriptionUpdateRequest { + + @NotBlank(message = "공백일 수 없습니다.") + private String colorCode; + private boolean checked; + + private SubscriptionUpdateRequest() { + } + + public SubscriptionUpdateRequest(final Color color, final boolean checked) { + this(color.getColorCode(), checked); + } + + public SubscriptionUpdateRequest(final String colorCode, final boolean checked) { + this.colorCode = colorCode; + this.checked = checked; + } + + public String getColorCode() { + return colorCode; + } + + @JsonIgnore + public Color getColor() { + return Color.from(colorCode); + } + + public boolean isChecked() { + return checked; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/dto/response/SubscriptionResponse.java b/backend/src/main/java/com/allog/dallog/domain/subscription/dto/response/SubscriptionResponse.java new file mode 100644 index 00000000..6a9fff15 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/dto/response/SubscriptionResponse.java @@ -0,0 +1,46 @@ +package com.allog.dallog.domain.subscription.dto.response; + +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.subscription.domain.Color; +import com.allog.dallog.domain.subscription.domain.Subscription; + +public class SubscriptionResponse { + + private Long id; + private CategoryResponse category; + private String colorCode; + + private boolean checked; + + private SubscriptionResponse() { + } + + public SubscriptionResponse(final Subscription subscription) { + this(subscription.getId(), new CategoryResponse(subscription.getCategory()), subscription.getColor(), + subscription.isChecked()); + } + + public SubscriptionResponse(final Long id, final CategoryResponse category, final Color color, + final boolean checked) { + this.id = id; + this.category = category; + this.colorCode = color.getColorCode(); + this.checked = checked; + } + + public Long getId() { + return id; + } + + public CategoryResponse getCategory() { + return category; + } + + public String getColorCode() { + return colorCode; + } + + public boolean isChecked() { + return checked; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/dto/response/SubscriptionsResponse.java b/backend/src/main/java/com/allog/dallog/domain/subscription/dto/response/SubscriptionsResponse.java new file mode 100644 index 00000000..25487189 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/dto/response/SubscriptionsResponse.java @@ -0,0 +1,19 @@ +package com.allog.dallog.domain.subscription.dto.response; + +import java.util.List; + +public class SubscriptionsResponse { + + private List subscriptions; + + private SubscriptionsResponse() { + } + + public SubscriptionsResponse(final List subscriptions) { + this.subscriptions = subscriptions; + } + + public List getSubscriptions() { + return subscriptions; + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/exception/ExistSubscriptionException.java b/backend/src/main/java/com/allog/dallog/domain/subscription/exception/ExistSubscriptionException.java new file mode 100644 index 00000000..260a893b --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/exception/ExistSubscriptionException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.subscription.exception; + +public class ExistSubscriptionException extends RuntimeException { + + public ExistSubscriptionException(final String message) { + super(message); + } + + public ExistSubscriptionException() { + this("이미 존재하는 구독 정보 입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/exception/InvalidSubscriptionException.java b/backend/src/main/java/com/allog/dallog/domain/subscription/exception/InvalidSubscriptionException.java new file mode 100644 index 00000000..f6677b69 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/exception/InvalidSubscriptionException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.subscription.exception; + +public class InvalidSubscriptionException extends RuntimeException { + + public InvalidSubscriptionException(final String message) { + super(message); + } + + public InvalidSubscriptionException() { + this("유효하지 않은 구독 정보입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/domain/subscription/exception/NoSuchSubscriptionException.java b/backend/src/main/java/com/allog/dallog/domain/subscription/exception/NoSuchSubscriptionException.java new file mode 100644 index 00000000..054aaf18 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/domain/subscription/exception/NoSuchSubscriptionException.java @@ -0,0 +1,12 @@ +package com.allog.dallog.domain.subscription.exception; + +public class NoSuchSubscriptionException extends RuntimeException { + + public NoSuchSubscriptionException(final String message) { + super(message); + } + + public NoSuchSubscriptionException() { + this("존재하지 않는 구독 정보입니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/global/config/JpaConfig.java b/backend/src/main/java/com/allog/dallog/global/config/JpaConfig.java new file mode 100644 index 00000000..6397a069 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/global/config/JpaConfig.java @@ -0,0 +1,9 @@ +package com.allog.dallog.global.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@Configuration +@EnableJpaAuditing +public class JpaConfig { +} diff --git a/backend/src/main/java/com/allog/dallog/global/config/PropertiesConfig.java b/backend/src/main/java/com/allog/dallog/global/config/PropertiesConfig.java new file mode 100644 index 00000000..f2160bb6 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/global/config/PropertiesConfig.java @@ -0,0 +1,10 @@ +package com.allog.dallog.global.config; + +import com.allog.dallog.global.config.properties.GoogleProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableConfigurationProperties(GoogleProperties.class) +public class PropertiesConfig { +} diff --git a/backend/src/main/java/com/allog/dallog/global/config/WebConfig.java b/backend/src/main/java/com/allog/dallog/global/config/WebConfig.java new file mode 100644 index 00000000..12bb5e20 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/global/config/WebConfig.java @@ -0,0 +1,37 @@ +package com.allog.dallog.global.config; + +import com.allog.dallog.presentation.auth.AuthenticationPrincipalArgumentResolver; +import java.util.List; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + private final List allowOriginUrlPatterns; + private final AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver; + + public WebConfig(@Value("${cors.allow-origin.urls}") final List allowOriginUrlPatterns, + final AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver) { + this.allowOriginUrlPatterns = allowOriginUrlPatterns; + this.authenticationPrincipalArgumentResolver = authenticationPrincipalArgumentResolver; + } + + @Override + public void addCorsMappings(CorsRegistry registry) { + String[] patterns = allowOriginUrlPatterns.stream() + .toArray(String[]::new); + + registry.addMapping("/**") + .allowedMethods("*") + .allowedOriginPatterns(patterns); + } + + @Override + public void addArgumentResolvers(List argumentResolvers) { + argumentResolvers.add(authenticationPrincipalArgumentResolver); + } +} diff --git a/backend/src/main/java/com/allog/dallog/global/config/properties/GoogleProperties.java b/backend/src/main/java/com/allog/dallog/global/config/properties/GoogleProperties.java new file mode 100644 index 00000000..ac040d3d --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/global/config/properties/GoogleProperties.java @@ -0,0 +1,70 @@ +package com.allog.dallog.global.config.properties; + +import java.util.List; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConstructorBinding; + +@ConfigurationProperties("oauth.google") +@ConstructorBinding +public class GoogleProperties { + + private final String clientId; + private final String clientSecret; + private final String redirectUri; + private final String oAuthEndPoint; + private final String responseType; + private final List scopes; + private final String tokenUri; + private final String accessType; + private final String prompt; + + public GoogleProperties(final String clientId, final String clientSecret, final String redirectUri, + final String oAuthEndPoint, final String responseType, final List scopes, + final String tokenUri, final String accessType, final String prompt) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.redirectUri = redirectUri; + this.oAuthEndPoint = oAuthEndPoint; + this.responseType = responseType; + this.scopes = scopes; + this.tokenUri = tokenUri; + this.accessType = accessType; + this.prompt = prompt; + } + + public String getClientId() { + return clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public String getRedirectUri() { + return redirectUri; + } + + public String getOAuthEndPoint() { + return oAuthEndPoint; + } + + public String getResponseType() { + return responseType; + } + + public List getScopes() { + return scopes; + } + + public String getTokenUri() { + return tokenUri; + } + + public String getAccessType() { + return accessType; + } + + public String getPrompt() { + return prompt; + } +} diff --git a/backend/src/main/java/com/allog/dallog/global/error/ControllerAdvice.java b/backend/src/main/java/com/allog/dallog/global/error/ControllerAdvice.java new file mode 100644 index 00000000..d023ac4e --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/global/error/ControllerAdvice.java @@ -0,0 +1,116 @@ +package com.allog.dallog.global.error; + +import com.allog.dallog.domain.auth.exception.EmptyAuthorizationHeaderException; +import com.allog.dallog.domain.auth.exception.InvalidTokenException; +import com.allog.dallog.domain.auth.exception.NoPermissionException; +import com.allog.dallog.domain.auth.exception.NoSuchOAuthTokenException; +import com.allog.dallog.domain.category.exception.DuplicatedExternalCategoryException; +import com.allog.dallog.domain.category.exception.InvalidCategoryException; +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; +import com.allog.dallog.domain.member.exception.InvalidMemberException; +import com.allog.dallog.domain.member.exception.NoSuchMemberException; +import com.allog.dallog.domain.schedule.exception.InvalidScheduleException; +import com.allog.dallog.domain.schedule.exception.NoSuchScheduleException; +import com.allog.dallog.domain.subscription.exception.ExistSubscriptionException; +import com.allog.dallog.domain.subscription.exception.InvalidSubscriptionException; +import com.allog.dallog.domain.subscription.exception.NoSuchSubscriptionException; +import com.allog.dallog.global.error.dto.ErrorReportRequest; +import com.allog.dallog.global.error.dto.ErrorResponse; +import com.allog.dallog.infrastructure.oauth.exception.OAuthException; +import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; + +@RestControllerAdvice +public class ControllerAdvice { + + private static final Logger log = LoggerFactory.getLogger(ControllerAdvice.class); + private static final String INVALID_DTO_FIELD_ERROR_MESSAGE_FORMAT = "%s 필드는 %s (전달된 값: %s)"; + + @ExceptionHandler({ + InvalidCategoryException.class, + InvalidMemberException.class, + InvalidScheduleException.class, + InvalidSubscriptionException.class, + ExistSubscriptionException.class, + DuplicatedExternalCategoryException.class + }) + public ResponseEntity handleInvalidData(final RuntimeException e) { + ErrorResponse errorResponse = new ErrorResponse(e.getMessage()); + return ResponseEntity.badRequest().body(errorResponse); + } + + @ExceptionHandler({ + EmptyAuthorizationHeaderException.class, + InvalidTokenException.class + }) + public ResponseEntity handleInvalidAuthorization(final RuntimeException e) { + ErrorResponse errorResponse = new ErrorResponse(e.getMessage()); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(errorResponse); + } + + @ExceptionHandler(NoPermissionException.class) + public ResponseEntity handleNoPermission(final NoPermissionException e) { + ErrorResponse errorResponse = new ErrorResponse(e.getMessage()); + return ResponseEntity.status(HttpStatus.FORBIDDEN).body(errorResponse); + } + + @ExceptionHandler({ + NoSuchCategoryException.class, + NoSuchMemberException.class, + NoSuchSubscriptionException.class, + NoSuchScheduleException.class, + NoSuchOAuthTokenException.class + }) + public ResponseEntity handleNoSuchData(final RuntimeException e) { + ErrorResponse errorResponse = new ErrorResponse(e.getMessage()); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(errorResponse); + } + + @ExceptionHandler(OAuthException.class) + public ResponseEntity handleOAuthException(final RuntimeException e) { + log.error("OAuth 통신 과정에서 에러가 발생했습니다.", e); + ErrorResponse errorResponse = new ErrorResponse("OAuth 통신 과정에서 에러가 발생했습니다."); + return ResponseEntity.internalServerError().body(errorResponse); + } + + @ExceptionHandler(HttpMessageNotReadableException.class) + public ResponseEntity handleInvalidRequestBody() { + ErrorResponse errorResponse = new ErrorResponse("잘못된 형식의 Request Body 입니다."); + return ResponseEntity.badRequest().body(errorResponse); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity handleInvalidDtoField(final MethodArgumentNotValidException e) { + FieldError firstFieldError = e.getFieldErrors().get(0); + String errorMessage = String.format(INVALID_DTO_FIELD_ERROR_MESSAGE_FORMAT, firstFieldError.getField(), + firstFieldError.getDefaultMessage(), firstFieldError.getRejectedValue()); + + ErrorResponse errorResponse = new ErrorResponse(errorMessage); + return ResponseEntity.badRequest().body(errorResponse); + } + + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public ResponseEntity handleTypeMismatch() { + ErrorResponse errorResponse = new ErrorResponse("잘못된 데이터 타입입니다."); + return ResponseEntity.badRequest().body(errorResponse); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleUnexpectedException(final Exception e, + final HttpServletRequest request) { + ErrorReportRequest errorReport = new ErrorReportRequest(request, e); + log.error(errorReport.getLogMessage(), e); + + ErrorResponse errorResponse = new ErrorResponse("예상하지 못한 서버 에러가 발생했습니다."); + return ResponseEntity.internalServerError().body(errorResponse); + } +} diff --git a/backend/src/main/java/com/allog/dallog/global/error/dto/ErrorReportRequest.java b/backend/src/main/java/com/allog/dallog/global/error/dto/ErrorReportRequest.java new file mode 100644 index 00000000..06000c30 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/global/error/dto/ErrorReportRequest.java @@ -0,0 +1,31 @@ +package com.allog.dallog.global.error.dto; + +import javax.servlet.http.HttpServletRequest; + +public class ErrorReportRequest { + + private static final String ERROR_REPORT_FORMAT = "[%s] %s"; + + private final HttpServletRequest request; + private final Exception exception; + + public ErrorReportRequest(final HttpServletRequest request, final Exception exception) { + this.request = request; + this.exception = exception; + } + + public String getLogMessage() { + String requestUri = request.getRequestURI(); + String requestMethod = request.getMethod(); + + return String.format(ERROR_REPORT_FORMAT, requestMethod, requestUri); + } + + public HttpServletRequest getRequest() { + return request; + } + + public Exception getException() { + return exception; + } +} diff --git a/backend/src/main/java/com/allog/dallog/global/error/dto/ErrorResponse.java b/backend/src/main/java/com/allog/dallog/global/error/dto/ErrorResponse.java new file mode 100644 index 00000000..37032ddc --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/global/error/dto/ErrorResponse.java @@ -0,0 +1,14 @@ +package com.allog.dallog.global.error.dto; + +public class ErrorResponse { + + private final String message; + + public ErrorResponse(final String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/log/DiscordAppender.java b/backend/src/main/java/com/allog/dallog/infrastructure/log/DiscordAppender.java new file mode 100644 index 00000000..0f23218b --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/log/DiscordAppender.java @@ -0,0 +1,107 @@ +package com.allog.dallog.infrastructure.log; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.StackTraceElementProxy; +import ch.qos.logback.core.UnsynchronizedAppenderBase; +import com.allog.dallog.infrastructure.log.dto.DiscordWebhookRequest; +import com.allog.dallog.infrastructure.log.dto.Embed; +import com.allog.dallog.infrastructure.log.dto.Field; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +public class DiscordAppender extends UnsynchronizedAppenderBase { + + private static final String TITLE_FORMAT = "[%s] %s"; + private static final String DESCRIPTION_FORMAT = "%s: %s"; + private static final RestTemplate CLIENT; + + static { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setConnectTimeout(3000); + CLIENT = new RestTemplate(factory); + } + + private String username; + private String embedsColor; + private int stackTraceMaxSize; + private String webhookUri; + + @Override + protected void append(final ILoggingEvent eventObject) { + if (!Objects.isNull(webhookUri) && !webhookUri.isEmpty()) { + String title = getTitle(eventObject); + List embeds = getEmbeds(title, embedsColor, eventObject); + DiscordWebhookRequest request = new DiscordWebhookRequest(username, embeds); + + CLIENT.postForEntity(webhookUri, request, Void.class); + } + } + + private String getTitle(final ILoggingEvent eventObject) { + return String.format(TITLE_FORMAT, eventObject.getLevel(), eventObject.getMessage()); + } + + private List getEmbeds(final String title, final String embedsColor, final ILoggingEvent eventObject) { + if (Objects.isNull(eventObject.getThrowableProxy())) { + return List.of(new Embed(title, embedsColor)); + } + + IThrowableProxy throwableProxy = eventObject.getThrowableProxy(); + String description = getDescription(throwableProxy); + List fields = getFields(throwableProxy); + + return List.of(new Embed(title, description, embedsColor, fields)); + } + + private String getDescription(final IThrowableProxy throwableProxy) { + return String.format(DESCRIPTION_FORMAT, throwableProxy.getClassName(), throwableProxy.getMessage()); + } + + private List getFields(final IThrowableProxy throwableProxy) { + List stackTraces = Arrays.stream(throwableProxy.getStackTraceElementProxyArray()) + .map(StackTraceElementProxy::getSTEAsString) + .limit(stackTraceMaxSize) + .collect(Collectors.toList()); + + return stackTraces.stream() + .map(Field::from) + .collect(Collectors.toList()); + } + + public String getUsername() { + return username; + } + + public void setUsername(final String username) { + this.username = username; + } + + public String getEmbedsColor() { + return embedsColor; + } + + public void setEmbedsColor(final String embedsColor) { + this.embedsColor = embedsColor; + } + + public int getStackTraceMaxSize() { + return stackTraceMaxSize; + } + + public void setStackTraceMaxSize(final int stackTraceMaxSize) { + this.stackTraceMaxSize = stackTraceMaxSize; + } + + public String getWebhookUri() { + return webhookUri; + } + + public void setWebhookUri(final String webhookUri) { + this.webhookUri = webhookUri; + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/log/dto/DiscordWebhookRequest.java b/backend/src/main/java/com/allog/dallog/infrastructure/log/dto/DiscordWebhookRequest.java new file mode 100644 index 00000000..484ebb8b --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/log/dto/DiscordWebhookRequest.java @@ -0,0 +1,25 @@ +package com.allog.dallog.infrastructure.log.dto; + +import java.util.List; + +public class DiscordWebhookRequest { + + private String username; + private List embeds; + + private DiscordWebhookRequest() { + } + + public DiscordWebhookRequest(final String username, final List embeds) { + this.username = username; + this.embeds = embeds; + } + + public String getUsername() { + return username; + } + + public List getEmbeds() { + return embeds; + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Embed.java b/backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Embed.java new file mode 100644 index 00000000..76849155 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Embed.java @@ -0,0 +1,41 @@ +package com.allog.dallog.infrastructure.log.dto; + +import java.util.List; + +public class Embed { + + private String title; + private String description; + private String color; + private List fields; + + private Embed() { + } + + public Embed(final String title, final String color) { + this(title, null, color, null); + } + + public Embed(final String title, final String description, final String color, final List fields) { + this.title = title; + this.description = description; + this.color = color; + this.fields = fields; + } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public String getColor() { + return color; + } + + public List getFields() { + return fields; + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Field.java b/backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Field.java new file mode 100644 index 00000000..a80c41d4 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/log/dto/Field.java @@ -0,0 +1,29 @@ +package com.allog.dallog.infrastructure.log.dto; + +public class Field { + + private String name; + private String value; + + private Field() { + } + + private Field(final String name, final String value) { + this.name = name; + this.value = value; + } + + public static Field from(final String steAsString) { + String name = steAsString.substring(steAsString.indexOf("(") + 1, steAsString.indexOf(")")); + String value = steAsString.substring(0, steAsString.indexOf("(")); + return new Field(name, value); + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleExternalCalendarClient.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleExternalCalendarClient.java new file mode 100644 index 00000000..2fefc06e --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleExternalCalendarClient.java @@ -0,0 +1,117 @@ +package com.allog.dallog.infrastructure.oauth.client; + +import com.allog.dallog.domain.category.domain.CategoryType; +import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarClient; +import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendar; +import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import com.allog.dallog.infrastructure.oauth.dto.GoogleCalendarEventResponse; +import com.allog.dallog.infrastructure.oauth.dto.GoogleCalendarEventsResponse; +import com.allog.dallog.infrastructure.oauth.dto.GoogleCalendarListResponse; +import com.allog.dallog.infrastructure.oauth.exception.OAuthException; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +@Component +public class GoogleExternalCalendarClient implements ExternalCalendarClient { + + private static final String CALENDAR_LIST_REQUEST_URI = "https://www.googleapis.com/calendar/v3/users/me/calendarList"; + private static final String CALENDAR_EVENTS_REQUEST_URI = "https://www.googleapis.com/calendar/v3/calendars/{calendarId}/events?singleEvents=true&timeMax={timeMax}&timeMin={timeMin}"; + private static final String ACCEPT_HEADER_NAME = "Accept"; + + private final RestTemplate restTemplate; + + public GoogleExternalCalendarClient(final RestTemplateBuilder restTemplateBuilder) { + this.restTemplate = restTemplateBuilder.build(); + } + + @Override + public List getExternalCalendars(final String accessToken) { + HttpEntity request = new HttpEntity<>(generateCalendarRequestHeaders(accessToken)); + GoogleCalendarListResponse response = fetchGoogleCalendarList(request).getBody(); + + return response.getItems() + .stream() + .map(item -> new ExternalCalendar(item.getId(), item.getSummary())) + .collect(Collectors.toList()); + } + + private ResponseEntity fetchGoogleCalendarList(final HttpEntity request) { + try { + return restTemplate.exchange(CALENDAR_LIST_REQUEST_URI, HttpMethod.GET, request, + GoogleCalendarListResponse.class); + } catch (RestClientException e) { + throw new OAuthException(e); + } + } + + @Override + public List getExternalCalendarSchedules(final String accessToken, + final Long internalCategoryId, + final String calendarId, + final String startDateTime, + final String endDateTime) { + HttpEntity request = new HttpEntity<>(generateCalendarRequestHeaders(accessToken)); + + Map uriVariables = generateEventsVariables(calendarId, startDateTime, endDateTime); + GoogleCalendarEventsResponse response = fetchGoogleCalendarEvents(uriVariables, request).getBody(); + + return response.getItems() + .stream() + .map(event -> parseIntegrationSchedule(internalCategoryId, event)) + .collect(Collectors.toList()); + } + + private HttpHeaders generateCalendarRequestHeaders(final String accessToken) { + HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(accessToken); + headers.set(ACCEPT_HEADER_NAME, MediaType.APPLICATION_JSON_VALUE); + return headers; + } + + private Map generateEventsVariables(final String externalCalendarId, final String startDateTime, + final String endDateTime) { + return Map.of( + "calendarId", externalCalendarId, + "timeMax", endDateTime + "Z", + "timeMin", startDateTime + "Z" + ); + } + + private ResponseEntity fetchGoogleCalendarEvents( + final Map uriVariables, final HttpEntity request) { + try { + return restTemplate.exchange(CALENDAR_EVENTS_REQUEST_URI, HttpMethod.GET, request, + GoogleCalendarEventsResponse.class, uriVariables); + } catch (RestClientException e) { + throw new OAuthException(e); + } + } + + private IntegrationSchedule parseIntegrationSchedule(final Long internalCategoryId, + final GoogleCalendarEventResponse event) { + LocalDateTime startDateTime = event.getStartDateTime(); + LocalDateTime endDateTime = event.getEndDateTime(); + if (isAllDay(startDateTime, endDateTime)) { + endDateTime = endDateTime.minusMinutes(1); + } + + return new IntegrationSchedule(event.getId(), internalCategoryId, event.getSummary(), startDateTime, + endDateTime, event.getDescription(), CategoryType.GOOGLE.name()); + } + + private boolean isAllDay(final LocalDateTime startDateTime, final LocalDateTime endDateTime) { + return startDateTime.getHour() == 0 && startDateTime.getMinute() == 0 + && endDateTime.getHour() == 0 && endDateTime.getMinute() == 0; + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleOAuthClient.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleOAuthClient.java new file mode 100644 index 00000000..e5a106d0 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/client/GoogleOAuthClient.java @@ -0,0 +1,123 @@ +package com.allog.dallog.infrastructure.oauth.client; + +import com.allog.dallog.domain.auth.application.OAuthClient; +import com.allog.dallog.domain.auth.dto.OAuthMember; +import com.allog.dallog.domain.auth.dto.response.OAuthAccessTokenResponse; +import com.allog.dallog.global.config.properties.GoogleProperties; +import com.allog.dallog.infrastructure.oauth.dto.GoogleTokenResponse; +import com.allog.dallog.infrastructure.oauth.dto.UserInfo; +import com.allog.dallog.infrastructure.oauth.exception.OAuthException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +@Component +public class GoogleOAuthClient implements OAuthClient { + + private static final String JWT_DELIMITER = "\\."; + + private final GoogleProperties properties; + private final RestTemplate restTemplate; + private final ObjectMapper objectMapper; + + public GoogleOAuthClient(final GoogleProperties properties, final RestTemplateBuilder restTemplateBuilder, + final ObjectMapper objectMapper) { + this.properties = properties; + this.restTemplate = restTemplateBuilder.build(); + this.objectMapper = objectMapper; + } + + @Override + public OAuthMember getOAuthMember(final String code, final String redirectUri) { + GoogleTokenResponse googleTokenResponse = requestGoogleToken(code, redirectUri); + String payload = getPayload(googleTokenResponse.getIdToken()); + UserInfo userInfo = parseUserInfo(payload); + + String refreshToken = googleTokenResponse.getRefreshToken(); + return new OAuthMember(userInfo.getEmail(), userInfo.getName(), userInfo.getPicture(), refreshToken); + } + + private GoogleTokenResponse requestGoogleToken(final String code, final String redirectUri) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap params = generateTokenParams(code, redirectUri); + + HttpEntity> request = new HttpEntity<>(params, headers); + return fetchGoogleToken(request).getBody(); + } + + private MultiValueMap generateTokenParams(final String code, final String redirectUri) { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("client_id", properties.getClientId()); + params.add("client_secret", properties.getClientSecret()); + params.add("code", code); + params.add("grant_type", "authorization_code"); + params.add("redirect_uri", redirectUri); + return params; + } + + private ResponseEntity fetchGoogleToken( + final HttpEntity> request) { + try { + return restTemplate.postForEntity(properties.getTokenUri(), request, GoogleTokenResponse.class); + } catch (RestClientException e) { + throw new OAuthException(e); + } + } + + private String getPayload(final String jwt) { + return jwt.split(JWT_DELIMITER)[1]; + } + + private UserInfo parseUserInfo(final String payload) { + String decodedPayload = decodeJwtPayload(payload); + try { + return objectMapper.readValue(decodedPayload, UserInfo.class); + } catch (JsonProcessingException e) { + throw new OAuthException("id 토큰을 읽을 수 없습니다."); + } + } + + private String decodeJwtPayload(final String payload) { + return new String(Base64.getUrlDecoder().decode(payload), StandardCharsets.UTF_8); + } + + @Override + public OAuthAccessTokenResponse getAccessToken(final String refreshToken) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + MultiValueMap params = generateAccessTokenParams(refreshToken); + + HttpEntity> request = new HttpEntity<>(params, headers); + return fetchGoogleAccessToken(request).getBody(); + } + + private MultiValueMap generateAccessTokenParams(final String refreshToken) { + MultiValueMap params = new LinkedMultiValueMap<>(); + params.add("client_id", properties.getClientId()); + params.add("client_secret", properties.getClientSecret()); + params.add("refresh_token", refreshToken); + params.add("grant_type", "refresh_token"); + return params; + } + + private ResponseEntity fetchGoogleAccessToken( + final HttpEntity> request) { + try { + return restTemplate.postForEntity(properties.getTokenUri(), request, OAuthAccessTokenResponse.class); + } catch (RestClientException e) { + throw new OAuthException(e); + } + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventResponse.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventResponse.java new file mode 100644 index 00000000..cecbdbb0 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventResponse.java @@ -0,0 +1,101 @@ +package com.allog.dallog.infrastructure.oauth.dto; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Objects; + +public class GoogleCalendarEventResponse { + + private String kind; + private String etag; + private String id; + private String status; + private String htmlLink; + private String summary = ""; + private String description = ""; + private String location; + private GoogleDateFormat start; + private GoogleDateFormat end; + private String recurringEventId; + + private GoogleCalendarEventResponse() { + } + + public GoogleCalendarEventResponse(final String kind, final String etag, final String id, final String status, + final String htmlLink, final String summary, final String description, + final String location, final GoogleDateFormat start, final GoogleDateFormat end, + final String recurringEventId) { + this.kind = kind; + this.etag = etag; + this.id = id; + this.status = status; + this.htmlLink = htmlLink; + this.summary = summary; + this.description = description; + this.location = location; + this.start = start; + this.end = end; + this.recurringEventId = recurringEventId; + } + + public LocalDateTime getStartDateTime() { + if (Objects.isNull(start.getDate())) { + return LocalDateTime.parse(start.getDateTime().substring(0, 19)); + } + + return LocalDateTime.of(LocalDate.parse(start.getDate()), LocalTime.MIN); + } + + public LocalDateTime getEndDateTime() { + if (Objects.isNull(end.getDate())) { + return LocalDateTime.parse(end.getDateTime().substring(0, 19)); + } + + return LocalDateTime.of(LocalDate.parse(end.getDate()), LocalTime.MIN); + } + + public String getKind() { + return kind; + } + + public String getEtag() { + return etag; + } + + public String getId() { + return id; + } + + public String getStatus() { + return status; + } + + public String getHtmlLink() { + return htmlLink; + } + + public String getSummary() { + return summary; + } + + public String getDescription() { + return description; + } + + public String getLocation() { + return location; + } + + public GoogleDateFormat getStart() { + return start; + } + + public GoogleDateFormat getEnd() { + return end; + } + + public String getRecurringEventId() { + return recurringEventId; + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventsResponse.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventsResponse.java new file mode 100644 index 00000000..ffb3fcda --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarEventsResponse.java @@ -0,0 +1,70 @@ +package com.allog.dallog.infrastructure.oauth.dto; + +import java.util.List; + +public class GoogleCalendarEventsResponse { + + private String kind; + private String etag; + private String summary; + private String description; + private String timeZone; + private String accessRole; + private String nextPageToken; + private String nextSyncToken; + private List items; + + private GoogleCalendarEventsResponse() { + } + + public GoogleCalendarEventsResponse(final String kind, final String etag, final String summary, + final String description, final String timeZone, final String accessRole, + final String nextPageToken, final String nextSyncToken, + final List items) { + this.kind = kind; + this.etag = etag; + this.summary = summary; + this.description = description; + this.timeZone = timeZone; + this.accessRole = accessRole; + this.nextPageToken = nextPageToken; + this.nextSyncToken = nextSyncToken; + this.items = items; + } + + public String getKind() { + return kind; + } + + public String getEtag() { + return etag; + } + + public String getSummary() { + return summary; + } + + public String getDescription() { + return description; + } + + public String getTimeZone() { + return timeZone; + } + + public String getAccessRole() { + return accessRole; + } + + public String getNextPageToken() { + return nextPageToken; + } + + public String getNextSyncToken() { + return nextSyncToken; + } + + public List getItems() { + return items; + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarListResponse.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarListResponse.java new file mode 100644 index 00000000..61d92a56 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarListResponse.java @@ -0,0 +1,44 @@ +package com.allog.dallog.infrastructure.oauth.dto; + +import java.util.List; + +public class GoogleCalendarListResponse { + + private String kind; + private String etag; + private String nextPageToken; + private String nextSyncToken; + private List items; + + private GoogleCalendarListResponse() { + } + + public GoogleCalendarListResponse(final String kind, final String etag, final String nextPageToken, + final String nextSyncToken, final List items) { + this.kind = kind; + this.etag = etag; + this.nextPageToken = nextPageToken; + this.nextSyncToken = nextSyncToken; + this.items = items; + } + + public String getKind() { + return kind; + } + + public String getEtag() { + return etag; + } + + public String getNextPageToken() { + return nextPageToken; + } + + public String getNextSyncToken() { + return nextSyncToken; + } + + public List getItems() { + return items; + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarResponse.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarResponse.java new file mode 100644 index 00000000..d43cf782 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleCalendarResponse.java @@ -0,0 +1,111 @@ +package com.allog.dallog.infrastructure.oauth.dto; + +public class GoogleCalendarResponse { + + private String kind; + private String etag; + private String id; + private String summary; + private String description; + private String location; + private String timeZone; + private String summaryOverride; + private String colorId; + private String backgroundColor; + private String foregroundColor; + private boolean hidden; + private boolean selected; + private String accessRole; + private boolean primary; + private boolean deleted; + + private GoogleCalendarResponse() { + } + + public GoogleCalendarResponse(final String kind, final String etag, final String id, final String summary, + final String description, final String location, final String timeZone, + final String summaryOverride, final String colorId, final String backgroundColor, + final String foregroundColor, final boolean hidden, final boolean selected, + final String accessRole, final boolean primary, final boolean deleted) { + this.kind = kind; + this.etag = etag; + this.id = id; + this.summary = summary; + this.description = description; + this.location = location; + this.timeZone = timeZone; + this.summaryOverride = summaryOverride; + this.colorId = colorId; + this.backgroundColor = backgroundColor; + this.foregroundColor = foregroundColor; + this.hidden = hidden; + this.selected = selected; + this.accessRole = accessRole; + this.primary = primary; + this.deleted = deleted; + } + + public String getKind() { + return kind; + } + + public String getEtag() { + return etag; + } + + public String getId() { + return id; + } + + public String getSummary() { + return summary; + } + + public String getDescription() { + return description; + } + + public String getLocation() { + return location; + } + + public String getTimeZone() { + return timeZone; + } + + public String getSummaryOverride() { + return summaryOverride; + } + + public String getColorId() { + return colorId; + } + + public String getBackgroundColor() { + return backgroundColor; + } + + public String getForegroundColor() { + return foregroundColor; + } + + public boolean isHidden() { + return hidden; + } + + public boolean isSelected() { + return selected; + } + + public String getAccessRole() { + return accessRole; + } + + public boolean isPrimary() { + return primary; + } + + public boolean isDeleted() { + return deleted; + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleDateFormat.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleDateFormat.java new file mode 100644 index 00000000..cd30178d --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleDateFormat.java @@ -0,0 +1,29 @@ +package com.allog.dallog.infrastructure.oauth.dto; + +public class GoogleDateFormat { + + private String date; + private String dateTime; + private String timeZone; + + private GoogleDateFormat() { + } + + public GoogleDateFormat(final String date, final String dateTime, final String timeZone) { + this.date = date; + this.dateTime = dateTime; + this.timeZone = timeZone; + } + + public String getDate() { + return date; + } + + public String getDateTime() { + return dateTime; + } + + public String getTimeZone() { + return timeZone; + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleTokenResponse.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleTokenResponse.java new file mode 100644 index 00000000..ec979965 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/GoogleTokenResponse.java @@ -0,0 +1,52 @@ +package com.allog.dallog.infrastructure.oauth.dto; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class GoogleTokenResponse { + + private String accessToken; + private String refreshToken; + private String idToken; + private String expiresIn; + private String tokenType; + private String scope; + + private GoogleTokenResponse() { + } + + public GoogleTokenResponse(final String accessToken, final String refreshToken, final String idToken, + final String expiresIn, final String scope, final String tokenType) { + this.accessToken = accessToken; + this.refreshToken = refreshToken; + this.idToken = idToken; + this.expiresIn = expiresIn; + this.scope = scope; + this.tokenType = tokenType; + } + + public String getAccessToken() { + return accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public String getIdToken() { + return idToken; + } + + public String getExpiresIn() { + return expiresIn; + } + + public String getScope() { + return scope; + } + + public String getTokenType() { + return tokenType; + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/UserInfo.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/UserInfo.java new file mode 100644 index 00000000..44616426 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/dto/UserInfo.java @@ -0,0 +1,29 @@ +package com.allog.dallog.infrastructure.oauth.dto; + +public class UserInfo { + + private String email; + private String name; + private String picture; + + private UserInfo() { + } + + public UserInfo(final String email, final String name, final String picture) { + this.email = email; + this.name = name; + this.picture = picture; + } + + public String getEmail() { + return email; + } + + public String getName() { + return name; + } + + public String getPicture() { + return picture; + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/exception/OAuthException.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/exception/OAuthException.java new file mode 100644 index 00000000..cc5dbd47 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/exception/OAuthException.java @@ -0,0 +1,16 @@ +package com.allog.dallog.infrastructure.oauth.exception; + +public class OAuthException extends RuntimeException { + + public OAuthException(final Exception e) { + super(e); + } + + public OAuthException(final String message) { + super(message); + } + + public OAuthException() { + this("Oauth 서버와의 통신 과정에서 문제가 발생했습니다."); + } +} diff --git a/backend/src/main/java/com/allog/dallog/infrastructure/oauth/uri/GoogleOAuthUri.java b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/uri/GoogleOAuthUri.java new file mode 100644 index 00000000..28d153a0 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/infrastructure/oauth/uri/GoogleOAuthUri.java @@ -0,0 +1,26 @@ +package com.allog.dallog.infrastructure.oauth.uri; + +import com.allog.dallog.domain.auth.application.OAuthUri; +import com.allog.dallog.global.config.properties.GoogleProperties; +import org.springframework.stereotype.Component; + +@Component +public class GoogleOAuthUri implements OAuthUri { + + private final GoogleProperties properties; + + public GoogleOAuthUri(final GoogleProperties properties) { + this.properties = properties; + } + + @Override + public String generate(final String redirectUri) { + return properties.getOAuthEndPoint() + "?" + + "client_id=" + properties.getClientId() + "&" + + "redirect_uri=" + redirectUri + "&" + + "response_type=code&" + + "scope=" + String.join(" ", properties.getScopes()) + "&" + + "access_type=" + properties.getAccessType() + "&" + + "prompt=" + properties.getPrompt(); + } +} diff --git a/backend/src/main/java/com/allog/dallog/presentation/CategoryController.java b/backend/src/main/java/com/allog/dallog/presentation/CategoryController.java new file mode 100644 index 00000000..7d18729f --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/presentation/CategoryController.java @@ -0,0 +1,77 @@ +package com.allog.dallog.presentation; + +import com.allog.dallog.domain.auth.dto.LoginMember; +import com.allog.dallog.domain.category.application.CategoryService; +import com.allog.dallog.domain.category.dto.request.CategoryCreateRequest; +import com.allog.dallog.domain.category.dto.request.CategoryUpdateRequest; +import com.allog.dallog.domain.category.dto.response.CategoriesResponse; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.composition.application.CategorySubscriptionService; +import com.allog.dallog.presentation.auth.AuthenticationPrincipal; +import java.net.URI; +import javax.validation.Valid; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api/categories") +@RestController +public class CategoryController { + + private final CategoryService categoryService; + private final CategorySubscriptionService categorySubscriptionService; + + public CategoryController(final CategoryService categoryService, + final CategorySubscriptionService categorySubscriptionService) { + this.categoryService = categoryService; + this.categorySubscriptionService = categorySubscriptionService; + } + + @PostMapping + public ResponseEntity save(@AuthenticationPrincipal final LoginMember loginMember, + @Valid @RequestBody final CategoryCreateRequest request) { + CategoryResponse categoryResponse = categorySubscriptionService.save(loginMember.getId(), request); + return ResponseEntity.created(URI.create("/api/categories/" + categoryResponse.getId())).body(categoryResponse); + } + + @GetMapping + public ResponseEntity findAllByName(@RequestParam(defaultValue = "") final String name, + final Pageable pageable) { + return ResponseEntity.ok(categoryService.findNormalByName(name, pageable)); + } + + @GetMapping("/me") + public ResponseEntity findMineByName(@AuthenticationPrincipal final LoginMember loginMember, + @RequestParam(defaultValue = "") final String name, + final Pageable pageable) { + return ResponseEntity.ok(categoryService.findMineByName(loginMember.getId(), name, pageable)); + } + + @GetMapping("/{categoryId}") + public ResponseEntity findById(@PathVariable final Long categoryId) { + return ResponseEntity.ok().body(categoryService.findById(categoryId)); + } + + @PatchMapping("/{categoryId}") + public ResponseEntity update(@AuthenticationPrincipal final LoginMember loginMember, + @PathVariable final Long categoryId, + @RequestBody final CategoryUpdateRequest request) { + categoryService.update(loginMember.getId(), categoryId, request); + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/{categoryId}") + public ResponseEntity delete(@AuthenticationPrincipal final LoginMember loginMember, + @PathVariable final Long categoryId) { + categoryService.deleteById(loginMember.getId(), categoryId); + return ResponseEntity.noContent().build(); + } +} diff --git a/backend/src/main/java/com/allog/dallog/presentation/ExternalCalendarController.java b/backend/src/main/java/com/allog/dallog/presentation/ExternalCalendarController.java new file mode 100644 index 00000000..239b0d94 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/presentation/ExternalCalendarController.java @@ -0,0 +1,44 @@ +package com.allog.dallog.presentation; + +import com.allog.dallog.domain.auth.dto.LoginMember; +import com.allog.dallog.domain.category.dto.request.ExternalCategoryCreateRequest; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.composition.application.CategorySubscriptionService; +import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarService; +import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendarsResponse; +import com.allog.dallog.presentation.auth.AuthenticationPrincipal; +import java.net.URI; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api/external-calendars/me") +@RestController +public class ExternalCalendarController { + + private final ExternalCalendarService externalCalendarService; + private final CategorySubscriptionService categorySubscriptionService; + + public ExternalCalendarController(final ExternalCalendarService externalCalendarService, + final CategorySubscriptionService categorySubscriptionService) { + this.externalCalendarService = externalCalendarService; + this.categorySubscriptionService = categorySubscriptionService; + } + + @GetMapping + public ResponseEntity getExternalCalendar( + @AuthenticationPrincipal final LoginMember loginMember) { + + return ResponseEntity.ok(externalCalendarService.findByMemberId(loginMember.getId())); + } + + @PostMapping + public ResponseEntity save(@AuthenticationPrincipal final LoginMember loginMember, + @RequestBody final ExternalCategoryCreateRequest request) { + CategoryResponse response = categorySubscriptionService.save(loginMember.getId(), request); + return ResponseEntity.created(URI.create("/api/categories/" + response.getId())).body(response); + } +} diff --git a/backend/src/main/java/com/allog/dallog/presentation/MemberController.java b/backend/src/main/java/com/allog/dallog/presentation/MemberController.java new file mode 100644 index 00000000..d07b7ba9 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/presentation/MemberController.java @@ -0,0 +1,47 @@ +package com.allog.dallog.presentation; + +import com.allog.dallog.domain.auth.dto.LoginMember; +import com.allog.dallog.domain.composition.application.RegisterService; +import com.allog.dallog.domain.member.application.MemberService; +import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.member.dto.MemberUpdateRequest; +import com.allog.dallog.presentation.auth.AuthenticationPrincipal; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api/members") +@RestController +public class MemberController { + + private final MemberService memberService; + private final RegisterService registerService; + + public MemberController(final MemberService memberService, final RegisterService registerService) { + this.memberService = memberService; + this.registerService = registerService; + } + + @GetMapping("/me") + public ResponseEntity findMe(@AuthenticationPrincipal final LoginMember loginMember) { + MemberResponse response = memberService.findById(loginMember.getId()); + return ResponseEntity.ok(response); + } + + @PatchMapping("/me") + public ResponseEntity update(@AuthenticationPrincipal LoginMember loginMember, + @RequestBody final MemberUpdateRequest request) { + memberService.update(loginMember.getId(), request); + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/me") + public ResponseEntity delete(@AuthenticationPrincipal final LoginMember loginMember) { + registerService.deleteByMemberId(loginMember.getId()); + return ResponseEntity.noContent().build(); + } +} diff --git a/backend/src/main/java/com/allog/dallog/presentation/ScheduleController.java b/backend/src/main/java/com/allog/dallog/presentation/ScheduleController.java new file mode 100644 index 00000000..4d95e609 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/presentation/ScheduleController.java @@ -0,0 +1,72 @@ +package com.allog.dallog.presentation; + +import com.allog.dallog.domain.auth.dto.LoginMember; +import com.allog.dallog.domain.composition.application.CalendarService; +import com.allog.dallog.domain.schedule.application.ScheduleService; +import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; +import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; +import com.allog.dallog.domain.schedule.dto.request.ScheduleUpdateRequest; +import com.allog.dallog.domain.schedule.dto.response.MemberScheduleResponses; +import com.allog.dallog.domain.schedule.dto.response.ScheduleResponse; +import com.allog.dallog.presentation.auth.AuthenticationPrincipal; +import java.net.URI; +import javax.validation.Valid; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api") +@RestController +public class ScheduleController { + + private final ScheduleService scheduleService; + private final CalendarService calendarService; + + public ScheduleController(final ScheduleService scheduleService, final CalendarService calendarService) { + this.scheduleService = scheduleService; + this.calendarService = calendarService; + } + + @PostMapping("/categories/{categoryId}/schedules") + public ResponseEntity save(@AuthenticationPrincipal final LoginMember loginMember, + @PathVariable final Long categoryId, + @Valid @RequestBody final ScheduleCreateRequest request) { + ScheduleResponse response = scheduleService.save(loginMember.getId(), categoryId, request); + return ResponseEntity.created(URI.create("/api/schedules/" + response.getId())).body(response); + } + + @GetMapping("/members/me/schedules") + public ResponseEntity findSchedulesByMemberId( + @AuthenticationPrincipal final LoginMember loginMember, @ModelAttribute DateRangeRequest request) { + MemberScheduleResponses response = calendarService.findSchedulesByMemberId(loginMember.getId(), request); + return ResponseEntity.ok(response); + } + + @GetMapping("/schedules/{scheduleId}") + public ResponseEntity findById(@PathVariable final Long scheduleId) { + ScheduleResponse response = scheduleService.findById(scheduleId); + return ResponseEntity.ok(response); + } + + @PatchMapping("/schedules/{scheduleId}") + public ResponseEntity update(@AuthenticationPrincipal final LoginMember loginMember, + @PathVariable final Long scheduleId, + @Valid @RequestBody final ScheduleUpdateRequest request) { + scheduleService.update(scheduleId, loginMember.getId(), request); + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/schedules/{scheduleId}") + public ResponseEntity delete(@AuthenticationPrincipal final LoginMember loginMember, + @PathVariable final Long scheduleId) { + scheduleService.deleteById(scheduleId, loginMember.getId()); + return ResponseEntity.noContent().build(); + } +} diff --git a/backend/src/main/java/com/allog/dallog/presentation/SchedulerController.java b/backend/src/main/java/com/allog/dallog/presentation/SchedulerController.java new file mode 100644 index 00000000..f82c4f73 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/presentation/SchedulerController.java @@ -0,0 +1,30 @@ +package com.allog.dallog.presentation; + +import com.allog.dallog.domain.composition.application.SchedulerService; +import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; +import com.allog.dallog.domain.schedule.dto.response.PeriodResponse; +import java.util.List; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api/scheduler") +@RestController +public class SchedulerController { + + private final SchedulerService schedulerService; + + public SchedulerController(final SchedulerService schedulerService) { + this.schedulerService = schedulerService; + } + + @GetMapping("/categories/{categoryId}/available-periods") + public ResponseEntity> scheduleByCategory(@PathVariable final Long categoryId, + @ModelAttribute DateRangeRequest dateRange) { + List periods = schedulerService.getAvailablePeriods(categoryId, dateRange); + return ResponseEntity.ok(periods); + } +} diff --git a/backend/src/main/java/com/allog/dallog/presentation/SubscriptionController.java b/backend/src/main/java/com/allog/dallog/presentation/SubscriptionController.java new file mode 100644 index 00000000..2cdc5cc2 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/presentation/SubscriptionController.java @@ -0,0 +1,59 @@ +package com.allog.dallog.presentation; + +import com.allog.dallog.domain.auth.dto.LoginMember; +import com.allog.dallog.domain.subscription.application.SubscriptionService; +import com.allog.dallog.domain.subscription.dto.request.SubscriptionUpdateRequest; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionsResponse; +import com.allog.dallog.presentation.auth.AuthenticationPrincipal; +import java.net.URI; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api/members/me") +@RestController +public class SubscriptionController { + + private final SubscriptionService subscriptionService; + + public SubscriptionController(final SubscriptionService subscriptionService) { + this.subscriptionService = subscriptionService; + } + + @PostMapping("/categories/{categoryId}/subscriptions") + public ResponseEntity save(@AuthenticationPrincipal final LoginMember loginMember, + @PathVariable final Long categoryId) { + SubscriptionResponse response = subscriptionService.save(loginMember.getId(), categoryId); + return ResponseEntity.created( + URI.create("/api/members/me/categories/" + categoryId + "/subscriptions/" + response.getId())) + .body(response); + } + + @GetMapping("/subscriptions") + public ResponseEntity findMine(@AuthenticationPrincipal final LoginMember loginMember) { + SubscriptionsResponse response = subscriptionService.findByMemberId(loginMember.getId()); + return ResponseEntity.ok(response); + } + + @PatchMapping("/subscriptions/{subscriptionId}") + public ResponseEntity update(@AuthenticationPrincipal final LoginMember loginMember, + @PathVariable final Long subscriptionId, + @RequestBody final SubscriptionUpdateRequest request) { + subscriptionService.update(subscriptionId, loginMember.getId(), request); + return ResponseEntity.noContent().build(); + } + + @DeleteMapping("/subscriptions/{subscriptionId}") + public ResponseEntity deleteById(@AuthenticationPrincipal final LoginMember loginMember, + @PathVariable final Long subscriptionId) { + subscriptionService.deleteById(subscriptionId, loginMember.getId()); + return ResponseEntity.noContent().build(); + } +} diff --git a/backend/src/main/java/com/allog/dallog/presentation/auth/AuthController.java b/backend/src/main/java/com/allog/dallog/presentation/auth/AuthController.java new file mode 100644 index 00000000..41aeec7b --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/presentation/auth/AuthController.java @@ -0,0 +1,45 @@ +package com.allog.dallog.presentation.auth; + +import com.allog.dallog.domain.auth.application.AuthService; +import com.allog.dallog.domain.auth.dto.LoginMember; +import com.allog.dallog.domain.auth.dto.request.TokenRequest; +import com.allog.dallog.domain.auth.dto.response.OAuthUriResponse; +import com.allog.dallog.domain.auth.dto.response.TokenResponse; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api/auth") +@RestController +public class AuthController { + + private final AuthService authService; + + public AuthController(final AuthService authService) { + this.authService = authService; + } + + @GetMapping("/{oauthProvider}/oauth-uri") + public ResponseEntity generateLink(@PathVariable final String oauthProvider, + @RequestParam final String redirectUri) { + OAuthUriResponse oAuthUriResponse = new OAuthUriResponse(authService.generateGoogleLink(redirectUri)); + return ResponseEntity.ok(oAuthUriResponse); + } + + @PostMapping("/{oauthProvider}/token") + public ResponseEntity generateToken(@PathVariable final String oauthProvider, + @RequestBody final TokenRequest tokenRequest) { + TokenResponse tokenResponse = authService.generateToken(tokenRequest); + return ResponseEntity.ok(tokenResponse); + } + + @GetMapping("/validate/token") + public ResponseEntity validateToken(@AuthenticationPrincipal final LoginMember loginMember) { + return ResponseEntity.ok().build(); + } +} diff --git a/backend/src/main/java/com/allog/dallog/presentation/auth/AuthenticationPrincipal.java b/backend/src/main/java/com/allog/dallog/presentation/auth/AuthenticationPrincipal.java new file mode 100644 index 00000000..33f6000e --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/presentation/auth/AuthenticationPrincipal.java @@ -0,0 +1,11 @@ +package com.allog.dallog.presentation.auth; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface AuthenticationPrincipal { +} diff --git a/backend/src/main/java/com/allog/dallog/presentation/auth/AuthenticationPrincipalArgumentResolver.java b/backend/src/main/java/com/allog/dallog/presentation/auth/AuthenticationPrincipalArgumentResolver.java new file mode 100644 index 00000000..39515f70 --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/presentation/auth/AuthenticationPrincipalArgumentResolver.java @@ -0,0 +1,35 @@ +package com.allog.dallog.presentation.auth; + +import com.allog.dallog.domain.auth.application.AuthService; +import com.allog.dallog.domain.auth.dto.LoginMember; +import javax.servlet.http.HttpServletRequest; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +@Component +public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver { + + private final AuthService authService; + + public AuthenticationPrincipalArgumentResolver(final AuthService authService) { + this.authService = authService; + } + + @Override + public boolean supportsParameter(final MethodParameter parameter) { + return parameter.hasParameterAnnotation(AuthenticationPrincipal.class); + } + + @Override + public Object resolveArgument(final MethodParameter parameter, final ModelAndViewContainer mavContainer, + final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) { + HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class); + String accessToken = AuthorizationExtractor.extract(request); + Long id = authService.extractMemberId(accessToken); + return new LoginMember(id); + } +} diff --git a/backend/src/main/java/com/allog/dallog/presentation/auth/AuthorizationExtractor.java b/backend/src/main/java/com/allog/dallog/presentation/auth/AuthorizationExtractor.java new file mode 100644 index 00000000..2c660f7a --- /dev/null +++ b/backend/src/main/java/com/allog/dallog/presentation/auth/AuthorizationExtractor.java @@ -0,0 +1,28 @@ +package com.allog.dallog.presentation.auth; + +import com.allog.dallog.domain.auth.exception.EmptyAuthorizationHeaderException; +import com.allog.dallog.domain.auth.exception.InvalidTokenException; +import java.util.Objects; +import javax.servlet.http.HttpServletRequest; +import org.springframework.http.HttpHeaders; + +public class AuthorizationExtractor { + + private static final String BEARER_TYPE = "Bearer "; + + public static String extract(final HttpServletRequest request) { + String authorizationHeader = request.getHeader(HttpHeaders.AUTHORIZATION); + if (Objects.isNull(authorizationHeader)) { + throw new EmptyAuthorizationHeaderException(); + } + + validateAuthorizationFormat(authorizationHeader); + return authorizationHeader.substring(BEARER_TYPE.length()).trim(); + } + + private static void validateAuthorizationFormat(final String authorizationHeader) { + if (!authorizationHeader.toLowerCase().startsWith(BEARER_TYPE.toLowerCase())) { + throw new InvalidTokenException("token 형식이 잘못 되었습니다."); + } + } +} diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties deleted file mode 100644 index 8b137891..00000000 --- a/backend/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/backend/src/main/resources/config b/backend/src/main/resources/config new file mode 160000 index 00000000..443f5d49 --- /dev/null +++ b/backend/src/main/resources/config @@ -0,0 +1 @@ +Subproject commit 443f5d49fc7f4e0122752e2ce1def3d27e9f92ba diff --git a/backend/src/main/resources/db/prod/schema.sql b/backend/src/main/resources/db/prod/schema.sql new file mode 100644 index 00000000..67d0d66f --- /dev/null +++ b/backend/src/main/resources/db/prod/schema.sql @@ -0,0 +1,68 @@ +CREATE TABLE IF NOT EXISTS members ( + id BIGINT AUTO_INCREMENT, + email VARCHAR(255) NOT NULL, + display_name VARCHAR(255) NOT NULL, + profile_image_url VARCHAR(255) NOT NULL, + social_type VARCHAR(255) NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id) +); + +CREATE TABLE IF NOT EXISTS categories ( + id BIGINT AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + members_id BIGINT NOT NULL, + category_type VARCHAR(255) NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id), + FOREIGN KEY (members_id) REFERENCES members (id) +); + +CREATE TABLE IF NOT EXISTS subscriptions ( + id BIGINT AUTO_INCREMENT, + color VARCHAR(255) NOT NULL, + checked boolean NOT NULL, + members_id BIGINT NOT NULL, + categories_id BIGINT NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id), + FOREIGN KEY (members_id) REFERENCES members (id), + FOREIGN KEY (categories_id) REFERENCES categories (id) +); + +CREATE TABLE IF NOT EXISTS schedules ( + id BIGINT AUTO_INCREMENT, + title VARCHAR(255) NOT NULL, + start_date_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + end_date_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + memo VARCHAR(255) NOT NULL, + categories_id BIGINT NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id), + FOREIGN KEY (categories_id) REFERENCES categories (id) +); + +CREATE TABLE IF NOT EXISTS oauth_tokens ( + id BIGINT AUTO_INCREMENT, + refresh_token VARCHAR(255) NOT NULL, + members_id BIGINT NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id), + FOREIGN KEY (members_id) REFERENCES members (id) +); + +CREATE TABLE IF NOT EXISTS external_category_details ( + id BIGINT AUTO_INCREMENT, + categories_id BIGINT NOT NULL, + external_id VARCHAR(255) NOT NULL, + created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (id), + FOREIGN KEY (categories_id) REFERENCES categories (id) +); + diff --git a/backend/src/main/resources/logback-spring.xml b/backend/src/main/resources/logback-spring.xml new file mode 100644 index 00000000..cfcc2772 --- /dev/null +++ b/backend/src/main/resources/logback-spring.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + ${FILE_LOG_PATTERN} + + ${LOG_PATH}/dallog.log + + ${LOG_FILE} + 10MB + 10 + 100MB + + + + + + + + ${USERNAME} + 15744574 + 5 + ${WEBHOOK_URI} + + + + + + ERROR + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/src/test/java/com/allog/dallog/DallogApplicationTests.java b/backend/src/test/java/com/allog/dallog/DallogApplicationTests.java deleted file mode 100644 index 06fb7334..00000000 --- a/backend/src/test/java/com/allog/dallog/DallogApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.allog.dallog; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class DallogApplicationTests { - - @Test - void contextLoads() { - } - -} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/AcceptanceTest.java b/backend/src/test/java/com/allog/dallog/acceptance/AcceptanceTest.java new file mode 100644 index 00000000..77baa342 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/AcceptanceTest.java @@ -0,0 +1,25 @@ +package com.allog.dallog.acceptance; + +import com.allog.dallog.common.DatabaseCleaner; +import com.allog.dallog.common.config.ExternalApiConfig; +import io.restassured.RestAssured; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.server.LocalServerPort; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = ExternalApiConfig.class) +abstract class AcceptanceTest { + + @LocalServerPort + private int port; + + @Autowired + private DatabaseCleaner databaseCleaner; + + @BeforeEach + void setUp() { + RestAssured.port = port; + databaseCleaner.execute(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/AuthAcceptanceTest.java b/backend/src/test/java/com/allog/dallog/acceptance/AuthAcceptanceTest.java new file mode 100644 index 00000000..61dd0866 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/AuthAcceptanceTest.java @@ -0,0 +1,67 @@ +package com.allog.dallog.acceptance; + +import static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.OAuth_인증_URI를_생성한다; +import static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생성하고_토큰을_반환한다; +import static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생성한다; +import static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.토큰이_유효한지_검증한다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_200이_반환된다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_401이_반환된다; +import static com.allog.dallog.common.fixtures.AuthFixtures.GOOGLE_PROVIDER; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_MEMBER_인증_코드; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.common.config.TokenConfig; +import com.allog.dallog.domain.auth.dto.response.OAuthUriResponse; +import com.allog.dallog.domain.auth.dto.response.TokenResponse; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +@Import(TokenConfig.class) +@DisplayName("인증 관련 기능") +public class AuthAcceptanceTest extends AcceptanceTest { + + @DisplayName("구글 OAuth 인증 URI를 생성하여 반환한다.") + @Test + void 구글_OAuth_인증_URI를_생성하여_반환한다() { + // given & when + ExtractableResponse response = OAuth_인증_URI를_생성한다(GOOGLE_PROVIDER); + OAuthUriResponse oAuthUriResponse = response.as(OAuthUriResponse.class); + + // then + assertAll(() -> { + 상태코드_200이_반환된다(response); + assertThat(oAuthUriResponse.getoAuthUri()).contains("https://"); + }); + } + + @DisplayName("최초 사용자거나 기존에 존재하는 회원인 경우 200을 발급한다.") + @Test + void 최초_사용자거나_기존에_존재하는_회원인_경우_200을_발급한다() { + // given & when + ExtractableResponse response = 자체_토큰을_생성한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + TokenResponse tokenResponse = response.as(TokenResponse.class); + + // then + assertAll(() -> { + 상태코드_200이_반환된다(response); + assertThat(tokenResponse.getAccessToken()).isNotEmpty(); + }); + } + + @DisplayName("만료된 토큰으로 웹페이지를 로드하면 상태코드 401을 반환한다.") + @Test + void 만료된_토큰으로_웹페이지를_로드하면_상태코드_401을_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + + // when + ExtractableResponse response = 토큰이_유효한지_검증한다(accessToken); + + // then + 상태코드_401이_반환된다(response); + } +} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/CategoryAcceptanceTest.java b/backend/src/test/java/com/allog/dallog/acceptance/CategoryAcceptanceTest.java new file mode 100644 index 00000000..640de049 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/CategoryAcceptanceTest.java @@ -0,0 +1,208 @@ +package com.allog.dallog.acceptance; + +import static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생성하고_토큰을_반환한다; +import static com.allog.dallog.acceptance.fixtures.CategoryAcceptanceFixtures.id를_통해_카테고리를_가져온다; +import static com.allog.dallog.acceptance.fixtures.CategoryAcceptanceFixtures.내가_등록한_카테고리를_삭제한다; +import static com.allog.dallog.acceptance.fixtures.CategoryAcceptanceFixtures.내가_등록한_카테고리를_수정한다; +import static com.allog.dallog.acceptance.fixtures.CategoryAcceptanceFixtures.내가_등록한_카테고리를_제목과_페이징을_통해_조회한다; +import static com.allog.dallog.acceptance.fixtures.CategoryAcceptanceFixtures.새로운_카테고리를_등록한다; +import static com.allog.dallog.acceptance.fixtures.CategoryAcceptanceFixtures.카테고리를_제목과_페이징을_통해_조회한다; +import static com.allog.dallog.acceptance.fixtures.CategoryAcceptanceFixtures.카테고리를_페이징을_통해_조회한다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_200이_반환된다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_201이_반환된다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_204가_반환된다; +import static com.allog.dallog.common.fixtures.AuthFixtures.GOOGLE_PROVIDER; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_MEMBER_인증_코드; +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.매트_아고라_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.후디_JPA_스터디_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.내_일정_생성_요청; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.domain.category.dto.response.CategoriesResponse; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("카테고리 관련 기능") +public class CategoryAcceptanceTest extends AcceptanceTest { + + @DisplayName("정상적인 카테고리 정보를 등록하면 상태코드 201을 반환한다.") + @Test + void 정상적인_카테고리_정보를_등록하면_상태코드_201을_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + + // when + ExtractableResponse response = 새로운_카테고리를_등록한다(accessToken, 매트_아고라_생성_요청); + + // then + 상태코드_201이_반환된다(response); + } + + @DisplayName("개인 카테고리를 생성하면 201을 반환한다.") + @Test + void 개인_카테고리를_생성하면_201을_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + + // when + ExtractableResponse response = 새로운_카테고리를_등록한다(accessToken, 내_일정_생성_요청); + + // then + 상태코드_201이_반환된다(response); + } + + @DisplayName("카테고리를 등록하고 페이징을 통해 나누어 조회한다.") + @Test + void 카테고리를_등록하고_페이징을_통해_나누어_조회한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청); + 새로운_카테고리를_등록한다(accessToken, BE_일정_생성_요청); + 새로운_카테고리를_등록한다(accessToken, FE_일정_생성_요청); + 새로운_카테고리를_등록한다(accessToken, 매트_아고라_생성_요청); + 새로운_카테고리를_등록한다(accessToken, 후디_JPA_스터디_생성_요청); + + // when + ExtractableResponse response = 카테고리를_페이징을_통해_조회한다(1, 3); + CategoriesResponse categoriesResponse = response.as(CategoriesResponse.class); + + // then + assertAll(() -> { + 상태코드_200이_반환된다(response); + assertThat(categoriesResponse.getPage()).isEqualTo(1); + assertThat(categoriesResponse.getCategories()).hasSize(2); + }); + } + + @DisplayName("카테고리를 등록하고 카테고리 제목과 페이징을 통해 나누어 조회한다.") + @Test + void 카테고리를_등록하고_카테고리_제목과_페이징을_통해_나누어_조회한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청); + 새로운_카테고리를_등록한다(accessToken, BE_일정_생성_요청); + 새로운_카테고리를_등록한다(accessToken, FE_일정_생성_요청); + 새로운_카테고리를_등록한다(accessToken, 매트_아고라_생성_요청); + 새로운_카테고리를_등록한다(accessToken, 후디_JPA_스터디_생성_요청); + + // when + ExtractableResponse response = 카테고리를_제목과_페이징을_통해_조회한다("일", 0, 3); + CategoriesResponse categoriesResponse = response.as(CategoriesResponse.class); + + // then + assertAll(() -> { + 상태코드_200이_반환된다(response); + assertThat(categoriesResponse.getPage()).isEqualTo(0); + assertThat(categoriesResponse.getCategories()).hasSize(3); + }); + } + + @DisplayName("등록된 개인 카테고리는 카테고리 목록에서 조회할 수 없다.") + @Test + void 등록된_개인_카테고리는_카테고리_목록에서_조회할_수_없다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + /* 공개 카테고리 */ + 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청); + 새로운_카테고리를_등록한다(accessToken, BE_일정_생성_요청); + 새로운_카테고리를_등록한다(accessToken, FE_일정_생성_요청); + /* 개인 카테고리 */ + 새로운_카테고리를_등록한다(accessToken, 내_일정_생성_요청); + + // when + ExtractableResponse response = 카테고리를_제목과_페이징을_통해_조회한다("", 0, 10); + CategoriesResponse categoriesResponse = response.as(CategoriesResponse.class); + + // then + assertAll(() -> { + 상태코드_200이_반환된다(response); + assertThat(categoriesResponse.getCategories()).hasSize(3); + }); + } + + @DisplayName("카테고리를 등록하고 내가 등록한 카테고리를 페이징을 통해 나누어 조회한다.") + @Test + void 카테고리를_등록하고_내가_등록한_카테고리를_페이징을_통해_나누어_조회한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청); + 새로운_카테고리를_등록한다(accessToken, BE_일정_생성_요청); + 새로운_카테고리를_등록한다(accessToken, FE_일정_생성_요청); + + // when + ExtractableResponse response + = 내가_등록한_카테고리를_제목과_페이징을_통해_조회한다(accessToken, "", 0, 3); + CategoriesResponse categoriesResponse = response.as(CategoriesResponse.class); + + // then + assertAll(() -> { + 상태코드_200이_반환된다(response); + assertThat(categoriesResponse.getPage()).isEqualTo(0); + assertThat(categoriesResponse.getCategories()).hasSize(3); + }); + } + + @DisplayName("카테고리를 등록하고 내가 등록한 카테고리를 제목과 페이징을 통해 나누어 조회한다.") + @Test + void 카테고리를_등록하고_내가_등록한_카테고리를_제목과_페이징을_통해_나누어_조회한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청); + 새로운_카테고리를_등록한다(accessToken, BE_일정_생성_요청); + 새로운_카테고리를_등록한다(accessToken, FE_일정_생성_요청); + 새로운_카테고리를_등록한다(accessToken, 매트_아고라_생성_요청); + 새로운_카테고리를_등록한다(accessToken, 후디_JPA_스터디_생성_요청); + + // when + ExtractableResponse response + = 내가_등록한_카테고리를_제목과_페이징을_통해_조회한다(accessToken, "일", 0, 2); + CategoriesResponse categoriesResponse = response.as(CategoriesResponse.class); + + // then + assertAll(() -> { + 상태코드_200이_반환된다(response); + assertThat(categoriesResponse.getPage()).isEqualTo(0); + assertThat(categoriesResponse.getCategories()).hasSize(2); + }); + } + + @DisplayName("카테고리를 등록하고 내가 등록한 카테고리를 수정하면 상태코드 204를 반환한다.") + @Test + void 카테고리를_등록하고_내가_등록한_카테고리를_수정하면_상태코드_204를_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + CategoryResponse savedCategory = 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청).as(CategoryResponse.class); + String newCategoryName = "우테코 공통 일정"; + + // when + ExtractableResponse response = 내가_등록한_카테고리를_수정한다(accessToken, savedCategory.getId(), newCategoryName); + CategoryResponse categoryResponse = id를_통해_카테고리를_가져온다(savedCategory.getId()).as(CategoryResponse.class); + + // then + assertAll(() -> { + 상태코드_204가_반환된다(response); + assertThat(categoryResponse.getName()).isEqualTo(newCategoryName); + }); + } + + @DisplayName("카테고리를 등록하고 내가 등록한 카테고리를 삭제하면 상태코드 204를 반환한다.") + @Test + void 카테고리를_등록하고_내가_등록한_카테고리를_삭제하면_상태코드_204를_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + CategoryResponse savedCategory = 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청).as(CategoryResponse.class); + + // when + ExtractableResponse response = 내가_등록한_카테고리를_삭제한다(accessToken, savedCategory.getId()); + + // then + 상태코드_204가_반환된다(response); + } +} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/ExternalCalendarAcceptanceTest.java b/backend/src/test/java/com/allog/dallog/acceptance/ExternalCalendarAcceptanceTest.java new file mode 100644 index 00000000..27b680f3 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/ExternalCalendarAcceptanceTest.java @@ -0,0 +1,73 @@ +package com.allog.dallog.acceptance; + +import static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생성하고_토큰을_반환한다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_200이_반환된다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_201이_반환된다; +import static com.allog.dallog.common.fixtures.AuthFixtures.GOOGLE_PROVIDER; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_MEMBER_인증_코드; +import static com.allog.dallog.common.fixtures.ExternalCategoryFixtures.우아한테크코스_생성_요청; +import static com.allog.dallog.common.fixtures.ExternalCategoryFixtures.우아한테크코스_이름; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendarsResponse; +import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +@DisplayName("외부 캘린더 관련 기능") +class ExternalCalendarAcceptanceTest extends AcceptanceTest { + + @DisplayName("자신의 외부 캘린더 리스트를 조회하면 200을 반환한다.") + @Test + void 자신의_외부_캘린더_리스트를_조회하면_200을_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + + // when + ExtractableResponse response = RestAssured.given().log().all() + .auth().oauth2(accessToken) + .accept(MediaType.APPLICATION_JSON_VALUE) + .when().get("/api/external-calendars/me") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); + ExternalCalendarsResponse externalCalendarsResponse = response.as(ExternalCalendarsResponse.class); + + // then + assertAll(() -> { + 상태코드_200이_반환된다(response); + assertThat(externalCalendarsResponse.getExternalCalendars()).hasSize(3); + }); + } + + @DisplayName("외부 캘린더를 추가하면 201을 반환한다.") + @Test + void 외부_캘린더를_추가하면_201을_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + + // when + ExtractableResponse response = RestAssured.given().log().all() + .auth().oauth2(accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .accept(MediaType.APPLICATION_JSON_VALUE) + .body(우아한테크코스_생성_요청) + .when().post("/api/external-calendars/me") + .then().log().all() + .statusCode(HttpStatus.CREATED.value()) + .extract(); + CategoryResponse categoryResponse = response.as(CategoryResponse.class); + + // then + assertAll(() -> { + 상태코드_201이_반환된다(response); + assertThat(categoryResponse.getName()).isEqualTo(우아한테크코스_이름); + }); + } +} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/MemberAcceptanceTest.java b/backend/src/test/java/com/allog/dallog/acceptance/MemberAcceptanceTest.java new file mode 100644 index 00000000..efb9d351 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/MemberAcceptanceTest.java @@ -0,0 +1,80 @@ +package com.allog.dallog.acceptance; + +import static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생성하고_토큰을_반환한다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_200이_반환된다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_204가_반환된다; +import static com.allog.dallog.acceptance.fixtures.MemberAcceptanceFixtures.자신의_정보를_조회한다; +import static com.allog.dallog.acceptance.fixtures.MemberAcceptanceFixtures.회원_탈퇴_한다; +import static com.allog.dallog.common.fixtures.AuthFixtures.GOOGLE_PROVIDER; +import static com.allog.dallog.common.fixtures.AuthFixtures.MEMBER_이름; +import static com.allog.dallog.common.fixtures.AuthFixtures.MEMBER_이메일; +import static com.allog.dallog.common.fixtures.AuthFixtures.MEMBER_프로필; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_MEMBER_인증_코드; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.member.dto.MemberUpdateRequest; +import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; + +@DisplayName("회원 관련 기능") +public class MemberAcceptanceTest extends AcceptanceTest { + + @DisplayName("등록된 회원이 자신의 정보를 조회하면 상태코드 200을 반환한다.") + @Test + void 등록된_회원이_자신의_정보를_조회하면_상태코드_200_을_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + + // when + ExtractableResponse response = 자신의_정보를_조회한다(accessToken); + MemberResponse memberResponse = response.as(MemberResponse.class); + + // then + assertAll(() -> { + 상태코드_200이_반환된다(response); + assertThat(memberResponse.getEmail()).isEqualTo(MEMBER_이메일); + assertThat(memberResponse.getDisplayName()).isEqualTo(MEMBER_이름); + assertThat(memberResponse.getProfileImageUrl()).isEqualTo(MEMBER_프로필); + }); + } + + @DisplayName("등록된 회원이 자신의 이름을 변경하면 상태코드 204를 반환한다.") + @Test + void 등록된_회원이_자신의_이름을_변경하면_상태코드_204를_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + String 패트_이름 = "패트"; + MemberUpdateRequest request = new MemberUpdateRequest(패트_이름); + + // when + ExtractableResponse response = RestAssured.given().log().all() + .auth().oauth2(accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(request) + .when().patch("/api/members/me") + .then().log().all() + .extract(); + + // then + 상태코드_204가_반환된다(response); + } + + @DisplayName("등록된 회원이 성공적으로 회원 탈퇴하면 상태코드 204를 반환한다.") + @Test + void 등록된_회원이_성공적으로_회원_탈퇴하면_상태코드_204를_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + + // when + ExtractableResponse response = 회원_탈퇴_한다(accessToken); + + // then + 상태코드_204가_반환된다(response); + } +} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/ScheduleAcceptanceTest.java b/backend/src/test/java/com/allog/dallog/acceptance/ScheduleAcceptanceTest.java new file mode 100644 index 00000000..3cefdf3c --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/ScheduleAcceptanceTest.java @@ -0,0 +1,91 @@ +package com.allog.dallog.acceptance; + +import static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생성하고_토큰을_반환한다; +import static com.allog.dallog.acceptance.fixtures.CategoryAcceptanceFixtures.새로운_카테고리를_등록한다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_200이_반환된다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_201이_반환된다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_204가_반환된다; +import static com.allog.dallog.acceptance.fixtures.ScheduleAcceptanceFixtures.새로운_일정을_등록한다; +import static com.allog.dallog.acceptance.fixtures.ScheduleAcceptanceFixtures.일정_아이디로_일정을_단건_조회한다; +import static com.allog.dallog.acceptance.fixtures.ScheduleAcceptanceFixtures.일정을_삭제한다; +import static com.allog.dallog.acceptance.fixtures.ScheduleAcceptanceFixtures.일정을_수정한다; +import static com.allog.dallog.common.fixtures.AuthFixtures.GOOGLE_PROVIDER; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_MEMBER_인증_코드; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_시작일시; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_제목; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_종료일시; + +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.schedule.dto.request.ScheduleUpdateRequest; +import com.allog.dallog.domain.schedule.dto.response.ScheduleResponse; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +@DisplayName("일정 관련 기능") +class ScheduleAcceptanceTest extends AcceptanceTest { + + @DisplayName("정상적인 일정정보를 등록하면 상태코드 201을 반환한다.") + @Test + void 정상적인_일정정보를_등록하면_상태코드_201을_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + CategoryResponse 공통_일정_응답 = 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청).as(CategoryResponse.class); + + // when + ExtractableResponse response = 새로운_일정을_등록한다(accessToken, 공통_일정_응답.getId()); + + // then + 상태코드_201이_반환된다(response); + } + + @DisplayName("일정 ID로 일정을 단건조회_하면 상태코드 200을 반환한다.") + @Test + void 일정_ID로_일정을_단건조회_하면_상태코드_200을_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + CategoryResponse 공통_일정_응답 = 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청).as(CategoryResponse.class); + ScheduleResponse 알록달록_회의 = 새로운_일정을_등록한다(accessToken, 공통_일정_응답.getId()).as(ScheduleResponse.class); + + // when + ExtractableResponse response = 일정_아이디로_일정을_단건_조회한다(accessToken, 알록달록_회의.getId()); + + // then + 상태코드_200이_반환된다(response); + } + + @DisplayName("일정을 수정하면 상태코드 204를 반환한다.") + @Test + void 일정을_수정하면_상태코드_204를_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + CategoryResponse 공통_일정_응답 = 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청).as(CategoryResponse.class); + ScheduleResponse 알록달록_회의 = 새로운_일정을_등록한다(accessToken, 공통_일정_응답.getId()).as(ScheduleResponse.class); + + ScheduleUpdateRequest 일정_수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + + // when + ExtractableResponse response = 일정을_수정한다(accessToken, 알록달록_회의.getId(), 일정_수정_요청); + + // then + 상태코드_204가_반환된다(response); + } + + @DisplayName("일정을 삭제하면 상태코드 204를 반환한다.") + @Test + void 일정을_삭제하면_상태코드_204를_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + CategoryResponse 공통_일정_응답 = 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청).as(CategoryResponse.class); + ScheduleResponse 알록달록_회의 = 새로운_일정을_등록한다(accessToken, 공통_일정_응답.getId()).as(ScheduleResponse.class); + + // when + ExtractableResponse response = 일정을_삭제한다(accessToken, 알록달록_회의.getId()); + + // then + 상태코드_204가_반환된다(response); + } +} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/SchedulerAcceptanceTest.java b/backend/src/test/java/com/allog/dallog/acceptance/SchedulerAcceptanceTest.java new file mode 100644 index 00000000..d64e17f7 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/SchedulerAcceptanceTest.java @@ -0,0 +1,46 @@ +package com.allog.dallog.acceptance; + +import static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생성하고_토큰을_반환한다; +import static com.allog.dallog.acceptance.fixtures.CategoryAcceptanceFixtures.새로운_카테고리를_등록한다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_200이_반환된다; +import static com.allog.dallog.acceptance.fixtures.ScheduleAcceptanceFixtures.새로운_일정을_등록한다; +import static com.allog.dallog.common.fixtures.AuthFixtures.GOOGLE_PROVIDER; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_MEMBER_인증_코드; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; + +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.schedule.dto.response.ScheduleResponse; +import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; + +@DisplayName("일정 관련 기능") +public class SchedulerAcceptanceTest extends AcceptanceTest { + + @DisplayName("일정 조율을 받아오면 상태코드 200을 반환한다.") + @Test + void 일정_조율을_받아오면_상태코드_200을_반환한다() { + // given + String accessToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + + CategoryResponse 공통_일정_응답 = 새로운_카테고리를_등록한다(accessToken, 공통_일정_생성_요청).as(CategoryResponse.class); + + 새로운_일정을_등록한다(accessToken, 공통_일정_응답.getId()).as(ScheduleResponse.class); + + // when + ExtractableResponse response = RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .queryParam("startDateTime", "2022-07-01T00:00") + .queryParam("endDateTime", "2022-07-31T00:00") + .auth().oauth2(accessToken) + .when().get("/api/scheduler/categories/{categoryId}/available-periods", 공통_일정_응답.getId()) + .then().log().all() + .extract(); + + // then + 상태코드_200이_반환된다(response); + } +} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/SubscriptionAcceptanceTest.java b/backend/src/test/java/com/allog/dallog/acceptance/SubscriptionAcceptanceTest.java new file mode 100644 index 00000000..ac376396 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/SubscriptionAcceptanceTest.java @@ -0,0 +1,168 @@ +package com.allog.dallog.acceptance; + +import static com.allog.dallog.acceptance.fixtures.AuthAcceptanceFixtures.자체_토큰을_생성하고_토큰을_반환한다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_201이_반환된다; +import static com.allog.dallog.acceptance.fixtures.CommonAcceptanceFixtures.상태코드_204가_반환된다; +import static com.allog.dallog.acceptance.fixtures.SubscriptionAcceptanceFixtures.구독_목록을_조회한다; +import static com.allog.dallog.common.fixtures.AuthFixtures.GOOGLE_PROVIDER; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_CREATOR_인증_코드; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_MEMBER_인증_코드; +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.domain.category.dto.request.CategoryCreateRequest; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.subscription.domain.Color; +import com.allog.dallog.domain.subscription.dto.request.SubscriptionUpdateRequest; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionsResponse; +import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +@DisplayName("구독 관련 기능") +public class SubscriptionAcceptanceTest extends AcceptanceTest { + + @DisplayName("인증된 회원이 카테고리를 구독하면 201을 반환한다.") + @Test + void 인증된_회원이_카테고리를_구독하면_201을_반환한다() { + // given + String memberToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + String creatorToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_CREATOR_인증_코드); + CategoryResponse 공통_일정 = 새로운_카테고리를_등록한다(creatorToken, 공통_일정_생성_요청); + + // when + ExtractableResponse response = RestAssured.given().log().all() + .auth().oauth2(memberToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when().post("/api/members/me/categories/{categoryId}/subscriptions", 공통_일정.getId()) + .then().log().all() + .statusCode(HttpStatus.CREATED.value()) + .extract(); + + // then + 상태코드_201이_반환된다(response); + } + + @DisplayName("인증된 회원이 구독 목록을 조회하면 200을 반환한다.") + @Test + void 인증된_회원이_구독_목록을_조회하면_200을_반환한다() { + // given + String memberToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + String creatorToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_CREATOR_인증_코드); + + CategoryResponse 공통_일정 = 새로운_카테고리를_등록한다(creatorToken, 공통_일정_생성_요청); + CategoryResponse BE_일정 = 새로운_카테고리를_등록한다(creatorToken, BE_일정_생성_요청); + CategoryResponse FE_일정 = 새로운_카테고리를_등록한다(creatorToken, FE_일정_생성_요청); + + 카테고리를_구독한다(memberToken, 공통_일정.getId()); + 카테고리를_구독한다(memberToken, BE_일정.getId()); + 카테고리를_구독한다(memberToken, FE_일정.getId()); + + // when + ExtractableResponse response = 구독_목록을_조회한다(memberToken); + SubscriptionsResponse subscriptionsResponse = response.as(SubscriptionsResponse.class); + + // then + assertAll(() -> { + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + assertThat(subscriptionsResponse.getSubscriptions()).hasSize(4); // 개인 카테고리 1 + given에서 등록한 카테고리 3 + }); + } + + @DisplayName("인증된 회원이 자신의 구독 정보를 수정할 경우 204를 반환한다.") + @Test + void 인증된_회원이_자신의_구독_정보를_수정할_경우_204를_반환한다() { + // given + String memberToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + String creatorToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_CREATOR_인증_코드); + + CategoryResponse 공통_일정 = 새로운_카테고리를_등록한다(creatorToken, 공통_일정_생성_요청); + + SubscriptionResponse subscriptionResponse = 카테고리를_구독한다(memberToken, 공통_일정.getId()); + SubscriptionUpdateRequest request = new SubscriptionUpdateRequest(Color.COLOR_1, true); + + // when + ExtractableResponse response = RestAssured.given().log().all() + .auth().oauth2(memberToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(request) + .when().patch("/api/members/me/subscriptions/{subscriptionId}", subscriptionResponse.getId()) + .then().log().all() + .statusCode(HttpStatus.NO_CONTENT.value()) + .extract(); + + SubscriptionsResponse subscriptionsResponse = 구독_목록을_조회한다(memberToken).as(SubscriptionsResponse.class); + + // then + List subscriptions = subscriptionsResponse.getSubscriptions(); + SubscriptionResponse foundSubscriptionResponse = subscriptions.stream() + .filter(subscription -> subscriptionResponse.getId().equals(subscription.getId())) + .findAny() + .get(); + + assertAll(() -> { + 상태코드_204가_반환된다(response); + assertThat(foundSubscriptionResponse.getColorCode()).isEqualTo(request.getColorCode()); + assertThat(foundSubscriptionResponse.isChecked()).isTrue(); + }); + } + + @DisplayName("구독을 취소할 경우 204를 반환한다.") + @Test + void 구독을_취소할_경우_204를_반환한다() { + // given + String memberToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_MEMBER_인증_코드); + String creatorToken = 자체_토큰을_생성하고_토큰을_반환한다(GOOGLE_PROVIDER, STUB_CREATOR_인증_코드); + + CategoryResponse 공통_일정 = 새로운_카테고리를_등록한다(creatorToken, 공통_일정_생성_요청); + CategoryResponse BE_일정 = 새로운_카테고리를_등록한다(creatorToken, BE_일정_생성_요청); + CategoryResponse FE_일정 = 새로운_카테고리를_등록한다(creatorToken, FE_일정_생성_요청); + + SubscriptionResponse subscriptionResponse = 카테고리를_구독한다(memberToken, 공통_일정.getId()); + 카테고리를_구독한다(memberToken, BE_일정.getId()); + 카테고리를_구독한다(memberToken, FE_일정.getId()); + + // when + ExtractableResponse response = RestAssured.given().log().all() + .auth().oauth2(memberToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when().delete("/api/members/me/subscriptions/{subscriptionId}", subscriptionResponse.getId()) + .then().log().all() + .statusCode(HttpStatus.NO_CONTENT.value()) + .extract(); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.NO_CONTENT.value()); + } + + private CategoryResponse 새로운_카테고리를_등록한다(final String accessToken, final CategoryCreateRequest request) { + return RestAssured.given().log().all() + .auth().oauth2(accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(request) + .when().post("/api/categories") + .then().log().all() + .extract() + .as(CategoryResponse.class); + } + + private SubscriptionResponse 카테고리를_구독한다(final String accessToken, final Long categoryId) { + return RestAssured.given().log().all() + .auth().oauth2(accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when().post("/api/members/me/categories/{categoryId}/subscriptions", categoryId) + .then().log().all() + .statusCode(HttpStatus.CREATED.value()) + .extract() + .as(SubscriptionResponse.class); + } +} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/fixtures/AuthAcceptanceFixtures.java b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/AuthAcceptanceFixtures.java new file mode 100644 index 00000000..fac4d4f7 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/AuthAcceptanceFixtures.java @@ -0,0 +1,53 @@ +package com.allog.dallog.acceptance.fixtures; + +import com.allog.dallog.domain.auth.dto.request.TokenRequest; +import com.allog.dallog.domain.auth.dto.response.TokenResponse; +import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +public class AuthAcceptanceFixtures { + + public static ExtractableResponse OAuth_인증_URI를_생성한다(final String oauthProvider) { + return RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when().get("/api/auth/{oauthProvider}/oauth-uri?redirectUri={redirectUri}", oauthProvider, + "https://dallog.me/oauth") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); + } + + public static ExtractableResponse 자체_토큰을_생성한다(final String oauthProvider, final String code) { + return RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(new TokenRequest(code, "https://dallog.me/oauth")) + .when().post("/api/auth/{oauthProvider}/token", oauthProvider) + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); + } + + public static String 자체_토큰을_생성하고_토큰을_반환한다(final String oauthProvider, final String code) { + TokenResponse tokenResponse = RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(new TokenRequest(code, "https://dallog.me/oauth")) + .when().post("/api/auth/{oauthProvider}/token", oauthProvider) + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract() + .as(TokenResponse.class); + + return tokenResponse.getAccessToken(); + } + + public static ExtractableResponse 토큰이_유효한지_검증한다(final String accessToken) { + return RestAssured.given().log().all() + .auth().oauth2(accessToken) + .when().get("/api/auth/validate/token") + .then().log().all() + .extract(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/fixtures/CategoryAcceptanceFixtures.java b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/CategoryAcceptanceFixtures.java new file mode 100644 index 00000000..e984b2c6 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/CategoryAcceptanceFixtures.java @@ -0,0 +1,73 @@ +package com.allog.dallog.acceptance.fixtures; + +import com.allog.dallog.domain.category.dto.request.CategoryCreateRequest; +import com.allog.dallog.domain.category.dto.request.CategoryUpdateRequest; +import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.springframework.http.MediaType; + +public class CategoryAcceptanceFixtures { + + public static ExtractableResponse 새로운_카테고리를_등록한다(final String accessToken, + final CategoryCreateRequest request) { + return RestAssured.given().log().all() + .auth().oauth2(accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(request) + .when().post("/api/categories") + .then().log().all() + .extract(); + } + + public static ExtractableResponse 카테고리를_페이징을_통해_조회한다(final int page, final int size) { + return RestAssured.given().log().all() + .when().get("/api/categories?page={page}&size={size}", page, size) + .then().log().all() + .extract(); + } + + public static ExtractableResponse 카테고리를_제목과_페이징을_통해_조회한다(final String name, final int page, + final int size) { + return RestAssured.given().log().all() + .when().get("/api/categories?name={name}&page={page}&size={size}", name, page, size) + .then().log().all() + .extract(); + } + + public static ExtractableResponse 내가_등록한_카테고리를_제목과_페이징을_통해_조회한다( + final String accessToken, final String name, final int page, final int size) { + return RestAssured.given().log().all() + .auth().oauth2(accessToken) + .when().get("/api/categories/me?name={name}&page={page}&size={size}", name, page, size) + .then().log().all() + .extract(); + } + + public static ExtractableResponse id를_통해_카테고리를_가져온다(final Long id) { + return RestAssured.given().log().all() + .when().get("/api/categories/{categoryId}", id) + .then().log().all() + .extract(); + } + + public static ExtractableResponse 내가_등록한_카테고리를_수정한다(final String accessToken, final Long id, + final String name) { + CategoryUpdateRequest request = new CategoryUpdateRequest(name); + return RestAssured.given().log().all() + .auth().oauth2(accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(request) + .when().patch("/api/categories/{categoryId}", id) + .then().log().all() + .extract(); + } + + public static ExtractableResponse 내가_등록한_카테고리를_삭제한다(final String accessToken, final Long id) { + return RestAssured.given().log().all() + .auth().oauth2(accessToken) + .when().delete("/api/categories/{categoryId}", id) + .then().log().all() + .extract(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/fixtures/CommonAcceptanceFixtures.java b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/CommonAcceptanceFixtures.java new file mode 100644 index 00000000..8af91895 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/CommonAcceptanceFixtures.java @@ -0,0 +1,30 @@ +package com.allog.dallog.acceptance.fixtures; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.springframework.http.HttpStatus; + +public class CommonAcceptanceFixtures { + + public static void 상태코드_200이_반환된다(final ExtractableResponse response) { + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK.value()); + } + + public static void 상태코드_201이_반환된다(final ExtractableResponse response) { + assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value()); + } + + public static void 상태코드_204가_반환된다(final ExtractableResponse response) { + assertThat(response.statusCode()).isEqualTo(HttpStatus.NO_CONTENT.value()); + } + + public static void 상태코드_404가_반환된다(final ExtractableResponse response) { + assertThat(response.statusCode()).isEqualTo(HttpStatus.NOT_FOUND.value()); + } + + public static void 상태코드_401이_반환된다(final ExtractableResponse response) { + assertThat(response.statusCode()).isEqualTo(HttpStatus.UNAUTHORIZED.value()); + } +} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/fixtures/MemberAcceptanceFixtures.java b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/MemberAcceptanceFixtures.java new file mode 100644 index 00000000..44b246c8 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/MemberAcceptanceFixtures.java @@ -0,0 +1,28 @@ +package com.allog.dallog.acceptance.fixtures; + +import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +public class MemberAcceptanceFixtures { + + public static ExtractableResponse 자신의_정보를_조회한다(final String token) { + return RestAssured.given().log().all() + .auth().oauth2(token) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when().get("/api/members/me") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); + } + + public static ExtractableResponse 회원_탈퇴_한다(final String token) { + return RestAssured.given().log().all() + .auth().oauth2(token) + .when().delete("/api/members/me") + .then().log().all() + .extract(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/fixtures/ScheduleAcceptanceFixtures.java b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/ScheduleAcceptanceFixtures.java new file mode 100644 index 00000000..c2b49763 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/ScheduleAcceptanceFixtures.java @@ -0,0 +1,60 @@ +package com.allog.dallog.acceptance.fixtures; + +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_제목; + +import com.allog.dallog.domain.schedule.dto.request.ScheduleUpdateRequest; +import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import java.util.HashMap; +import java.util.Map; +import org.springframework.http.MediaType; + +public class ScheduleAcceptanceFixtures { + + public static ExtractableResponse 새로운_일정을_등록한다(final String accessToken, final Long categoryId) { + + Map params = new HashMap(); + params.put("title", 알록달록_회의_제목); + params.put("startDateTime", "2022-07-04T13:00"); + params.put("endDateTime", "2022-07-05T16:00"); + params.put("memo", 알록달록_회의_메모); + + return RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .auth().oauth2(accessToken) + .body(params) + .when().post("/api/categories/{categoryId}/schedules", categoryId) + .then().log().all() + .extract(); + } + + public static ExtractableResponse 일정을_수정한다(final String accessToken, final Long scheduleId, final + ScheduleUpdateRequest request) { + return RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .auth().oauth2(accessToken) + .body(request) + .when().patch("/api/schedules/{scheduleId}", scheduleId) + .then().log().all() + .extract(); + } + + public static ExtractableResponse 일정을_삭제한다(final String accessToken, final Long scheduleId) { + return RestAssured.given().log().all() + .auth().oauth2(accessToken) + .when().delete("/api/schedules/{scheduleId}", scheduleId) + .then().log().all() + .extract(); + } + + public static ExtractableResponse 일정_아이디로_일정을_단건_조회한다(final String accessToken, final Long scheduleId) { + return RestAssured.given().log().all() + .contentType(MediaType.APPLICATION_JSON_VALUE) + .auth().oauth2(accessToken) + .when().get("/api/schedules/{scheduleId}", scheduleId) + .then().log().all() + .extract(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/acceptance/fixtures/SubscriptionAcceptanceFixtures.java b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/SubscriptionAcceptanceFixtures.java new file mode 100644 index 00000000..fe85ba32 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/acceptance/fixtures/SubscriptionAcceptanceFixtures.java @@ -0,0 +1,20 @@ +package com.allog.dallog.acceptance.fixtures; + +import io.restassured.RestAssured; +import io.restassured.response.ExtractableResponse; +import io.restassured.response.Response; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; + +public class SubscriptionAcceptanceFixtures { + + public static ExtractableResponse 구독_목록을_조회한다(final String accessToken) { + return RestAssured.given().log().all() + .auth().oauth2(accessToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .when().get("/api/members/me/subscriptions") + .then().log().all() + .statusCode(HttpStatus.OK.value()) + .extract(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/common/DatabaseCleaner.java b/backend/src/test/java/com/allog/dallog/common/DatabaseCleaner.java new file mode 100644 index 00000000..7151314a --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/DatabaseCleaner.java @@ -0,0 +1,39 @@ +package com.allog.dallog.common; + +import java.util.List; +import java.util.stream.Collectors; +import javax.persistence.EntityManager; +import javax.persistence.Table; +import javax.persistence.metamodel.Type; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +public class DatabaseCleaner { + + private final EntityManager entityManager; + private final List tableNames; + + public DatabaseCleaner(final EntityManager entityManager) { + this.entityManager = entityManager; + this.tableNames = entityManager.getMetamodel() + .getEntities() + .stream() + .map(Type::getJavaType) + .map(javaType -> javaType.getAnnotation(Table.class)) + .map(Table::name) + .collect(Collectors.toList()); + } + + @Transactional + public void execute() { + entityManager.flush(); + entityManager.createNativeQuery("SET foreign_key_checks = 0").executeUpdate(); + + for (String tableName : tableNames) { + entityManager.createNativeQuery("TRUNCATE TABLE " + tableName).executeUpdate(); + } + + entityManager.createNativeQuery("SET foreign_key_checks = 1").executeUpdate(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/common/annotation/RepositoryTest.java b/backend/src/test/java/com/allog/dallog/common/annotation/RepositoryTest.java new file mode 100644 index 00000000..d2370dcd --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/annotation/RepositoryTest.java @@ -0,0 +1,10 @@ +package com.allog.dallog.common.annotation; + +import com.allog.dallog.global.config.JpaConfig; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.context.annotation.Import; + +@DataJpaTest +@Import(JpaConfig.class) +public class RepositoryTest { +} diff --git a/backend/src/test/java/com/allog/dallog/common/annotation/ServiceTest.java b/backend/src/test/java/com/allog/dallog/common/annotation/ServiceTest.java new file mode 100644 index 00000000..5d24814d --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/annotation/ServiceTest.java @@ -0,0 +1,19 @@ +package com.allog.dallog.common.annotation; + +import com.allog.dallog.common.DatabaseCleaner; +import com.allog.dallog.common.config.ExternalApiConfig; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(classes = ExternalApiConfig.class) +public class ServiceTest { + + @Autowired + private DatabaseCleaner databaseCleaner; + + @BeforeEach + void setUp() { + databaseCleaner.execute(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/common/config/ExternalApiConfig.java b/backend/src/test/java/com/allog/dallog/common/config/ExternalApiConfig.java new file mode 100644 index 00000000..774dd827 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/config/ExternalApiConfig.java @@ -0,0 +1,23 @@ +package com.allog.dallog.common.config; + +import com.allog.dallog.domain.auth.application.OAuthClient; +import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarClient; +import com.allog.dallog.infrastructure.oauth.client.StubExternalCalendarClient; +import com.allog.dallog.infrastructure.oauth.client.StubOAuthClient; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +@TestConfiguration +public class ExternalApiConfig { + + // SpringBootTest 환경에서 OAuthClient 실제 객체 대신 테스트 더블을 빈으로 등록하기 위한 코드 + @Bean + public OAuthClient oAuthClient() { + return new StubOAuthClient(); + } + + @Bean + public ExternalCalendarClient externalCalendarClient() { + return new StubExternalCalendarClient(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/common/config/TokenConfig.java b/backend/src/test/java/com/allog/dallog/common/config/TokenConfig.java new file mode 100644 index 00000000..8cc6cbd5 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/config/TokenConfig.java @@ -0,0 +1,17 @@ +package com.allog.dallog.common.config; + +import static com.allog.dallog.common.fixtures.AuthFixtures.더미_시크릿_키; + +import com.allog.dallog.domain.auth.application.StubTokenProvider; +import com.allog.dallog.domain.auth.application.TokenProvider; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +@TestConfiguration +public class TokenConfig { + + @Bean + public TokenProvider tokenProvider() { + return new StubTokenProvider(더미_시크릿_키); + } +} diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/AuthFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/AuthFixtures.java new file mode 100644 index 00000000..3b166c3f --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/AuthFixtures.java @@ -0,0 +1,51 @@ +package com.allog.dallog.common.fixtures; + +import com.allog.dallog.domain.auth.dto.OAuthMember; +import com.allog.dallog.domain.auth.dto.request.TokenRequest; +import com.allog.dallog.domain.auth.dto.response.TokenResponse; + +public class AuthFixtures { + + public static final String GOOGLE_PROVIDER = "google"; + public static final String OAUTH_PROVIDER = "oauthProvider"; + + public static final String STUB_MEMBER_인증_코드 = "member authorization code"; + public static final String STUB_CREATOR_인증_코드 = "creator authorization code"; + + public static final String 더미_엑세스_토큰 = "aaaaa.bbbbb.ccccc"; + public static final String 토큰_정보 = "Bearer " + 더미_엑세스_토큰; + public static final String OAuth_로그인_링크 = "https://accounts.google.com/o/oauth2/v2/auth"; + + public static final String MEMBER_이메일 = "member@email.com"; + public static final String MEMBER_이름 = "member"; + public static final String MEMBER_프로필 = "/member.png"; + public static final String MEMBER_REFRESH_TOKEN = "aaaaaaaaaa.bbbbbbbbbb.ccccccccc"; + + public static final String CREATOR_이메일 = "creator@email.com"; + public static final String CREATOR_이름 = "creator"; + public static final String CREATOR_프로필 = "/creator.png"; + public static final String CREATOR_REFRESH_TOKEN = "aaaaaaaaaa.bbbbbbbbbb.ccccccccc"; + + public static final String 더미_시크릿_키 = "asdfasarspofjkosdfasdjkflikasndflkasndsdfjkadsnfkjasdn"; + + public static final String STUB_OAUTH_ACCESS_TOKEN = "aaaaaaaaaa.bbbbbbbbbb.cccccccccc"; + public static final String STUB_OAUTH_EXPIRES_IN = "3599"; + public static final String STUB_OAUTH_SCOPE = "openid"; + public static final String STUB_OAUTH_TOKEN_TYPE = "Bearer"; + + public static TokenRequest MEMBER_인증_코드_토큰_요청() { + return new TokenRequest(STUB_MEMBER_인증_코드, "https://dallog.me/oauth"); + } + + public static TokenResponse MEMBER_인증_코드_토큰_응답() { + return new TokenResponse(STUB_MEMBER_인증_코드); + } + + public static OAuthMember STUB_OAUTH_MEMBER() { + return new OAuthMember(MEMBER_이메일, MEMBER_이름, MEMBER_프로필, MEMBER_REFRESH_TOKEN); + } + + public static OAuthMember STUB_OAUTH_CREATOR() { + return new OAuthMember(CREATOR_이메일, CREATOR_이름, CREATOR_프로필, CREATOR_REFRESH_TOKEN); + } +} diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/CategoryFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/CategoryFixtures.java new file mode 100644 index 00000000..24e71dc3 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/CategoryFixtures.java @@ -0,0 +1,91 @@ +package com.allog.dallog.common.fixtures; + +import static com.allog.dallog.domain.category.domain.CategoryType.GOOGLE; +import static com.allog.dallog.domain.category.domain.CategoryType.NORMAL; +import static com.allog.dallog.domain.category.domain.CategoryType.PERSONAL; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.dto.request.CategoryCreateRequest; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.dto.MemberResponse; +import java.time.LocalDateTime; + +public class CategoryFixtures { + + /* 공통 일정 카테고리 */ + public static final String 공통_일정_이름 = "공통 일정"; + public static final CategoryCreateRequest 공통_일정_생성_요청 = new CategoryCreateRequest(공통_일정_이름, NORMAL); + + /* BE 일정 카테고리 */ + public static final String BE_일정_이름 = "BE 일정"; + public static final CategoryCreateRequest BE_일정_생성_요청 = new CategoryCreateRequest(BE_일정_이름, NORMAL); + + /* FE 일정 카테고리 */ + public static final String FE_일정_이름 = "FE 일정"; + public static final CategoryCreateRequest FE_일정_생성_요청 = new CategoryCreateRequest(FE_일정_이름, NORMAL); + + /* 매트 아고라 카테고리 */ + public static final String 매트_아고라_이름 = "매트 아고라"; + public static final CategoryCreateRequest 매트_아고라_생성_요청 = new CategoryCreateRequest(매트_아고라_이름, NORMAL); + + /* 후디 JPA 스터디 카테고리 */ + public static final String 후디_JPA_스터디_이름 = "후디 JPA 스터디"; + public static final CategoryCreateRequest 후디_JPA_스터디_생성_요청 = new CategoryCreateRequest(후디_JPA_스터디_이름, NORMAL); + + /* 내 일정 카테고리 */ + public static final String 내_일정_이름 = "내 일정"; + public static final CategoryCreateRequest 내_일정_생성_요청 = new CategoryCreateRequest(내_일정_이름, PERSONAL); + + /* 우아한테크코스 외부 일정 카테고리 */ + public static final String 우아한테크코스_이름 = "우아한테크코스"; + public static final CategoryCreateRequest 우아한테크코스_외부_일정_생성_요청 = new CategoryCreateRequest(우아한테크코스_이름, GOOGLE); + + public static Category 공통_일정(final Member creator) { + return new Category(공통_일정_이름, creator); + } + + public static Category BE_일정(final Member creator) { + return new Category(BE_일정_이름, creator); + } + + public static Category FE_일정(final Member creator) { + return new Category(FE_일정_이름, creator); + } + + public static Category 매트_아고라(final Member creator) { + return new Category(매트_아고라_이름, creator); + } + + public static Category 후디_JPA_스터디(final Member creator) { + return new Category(후디_JPA_스터디_이름, creator); + } + + public static Category 내_일정(final Member creator) { + return new Category(내_일정_이름, creator, PERSONAL); + } + + public static Category 우아한테크코스_일정(final Member creator) { + return new Category(우아한테크코스_이름, creator, GOOGLE); + } + + public static CategoryResponse 공통_일정_응답(final MemberResponse creatorResponse) { + return new CategoryResponse(1L, 공통_일정_이름, NORMAL.name(), creatorResponse, LocalDateTime.now()); + } + + public static CategoryResponse BE_일정_응답(final MemberResponse creatorResponse) { + return new CategoryResponse(2L, BE_일정_이름, NORMAL.name(), creatorResponse, LocalDateTime.now()); + } + + public static CategoryResponse FE_일정_응답(final MemberResponse creatorResponse) { + return new CategoryResponse(3L, FE_일정_이름, NORMAL.name(), creatorResponse, LocalDateTime.now()); + } + + public static CategoryResponse 매트_아고라_응답(final MemberResponse creatorResponse) { + return new CategoryResponse(4L, 매트_아고라_이름, NORMAL.name(), creatorResponse, LocalDateTime.now()); + } + + public static CategoryResponse 후디_JPA_스터디_응답(final MemberResponse creatorResponse) { + return new CategoryResponse(5L, 후디_JPA_스터디_이름, NORMAL.name(), creatorResponse, LocalDateTime.now()); + } +} diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/ExternalCalendarFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/ExternalCalendarFixtures.java new file mode 100644 index 00000000..e5b7db10 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/ExternalCalendarFixtures.java @@ -0,0 +1,14 @@ +package com.allog.dallog.common.fixtures; + +import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendar; + +public class ExternalCalendarFixtures { + + public static final ExternalCalendar 대한민국_공휴일 = new ExternalCalendar( + "ko.south_korea#holiday@group.v.calendar.google.com", "대한민국 공휴일"); + + public static final ExternalCalendar 우아한테크코스 = new ExternalCalendar( + "en.south_korea#holiday@group.v.calendar.google.com", "우아한테크코스"); + + public static final ExternalCalendar 내_일정 = new ExternalCalendar("example@email.com", "내 일정"); +} diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/ExternalCategoryFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/ExternalCategoryFixtures.java new file mode 100644 index 00000000..d05001b6 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/ExternalCategoryFixtures.java @@ -0,0 +1,19 @@ +package com.allog.dallog.common.fixtures; + +import com.allog.dallog.domain.category.dto.request.ExternalCategoryCreateRequest; + +public class ExternalCategoryFixtures { + + public static final String 대한민국_공휴일_이름 = "대한민국 공휴일"; + public static final String 우아한테크코스_이름 = "우아한테크코스"; + public static final String 내_일정_이름 = "내 일정"; + + public static final ExternalCategoryCreateRequest 대한민국_공휴일_생성_요청 = new ExternalCategoryCreateRequest( + "ko.south_korea#holiday@group.v.calendar.google.com", 대한민국_공휴일_이름); + + public static final ExternalCategoryCreateRequest 우아한테크코스_생성_요청 = new ExternalCategoryCreateRequest( + "en.south_korea#holiday@group.v.calendar.google.com", 우아한테크코스_이름); + + public static final ExternalCategoryCreateRequest 내_일정_생성_요청 = new ExternalCategoryCreateRequest( + "example@email.com", 내_일정_이름); +} diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/IntegrationScheduleFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/IntegrationScheduleFixtures.java new file mode 100644 index 00000000..c6d7005f --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/IntegrationScheduleFixtures.java @@ -0,0 +1,25 @@ +package com.allog.dallog.common.fixtures; + +import com.allog.dallog.domain.category.domain.CategoryType; +import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.integrationschedule.domain.Period; +import java.time.LocalDateTime; + +public class IntegrationScheduleFixtures { + + public static final IntegrationSchedule 점심_식사 = new IntegrationSchedule("1", 2L, "점심 식사", + new Period(LocalDateTime.of(2022, 8, 16, 11, 00), LocalDateTime.of(2022, 8, 16, 13, 00)), "", + CategoryType.NORMAL.name()); + + public static final IntegrationSchedule 달록_여행 = new IntegrationSchedule("2", 2L, "달록 여행", + new Period(LocalDateTime.of(2022, 8, 24, 00, 00), LocalDateTime.of(2022, 8, 25, 23, 59)), "", + CategoryType.NORMAL.name()); + + public static final IntegrationSchedule 레벨3_방학 = new IntegrationSchedule("gsgadfgqwrtqwerfgasdasdasd", 1L, + "레벨3 방학", new Period(LocalDateTime.of(2022, 8, 20, 00, 00), LocalDateTime.of(2022, 8, 20, 00, 00)), "", + CategoryType.GOOGLE.name()); + + public static final IntegrationSchedule 포수타 = new IntegrationSchedule("asgasgasfgadfgdf", 1L, + "포수타", new Period(LocalDateTime.of(2022, 8, 12, 14, 00), LocalDateTime.of(2022, 8, 12, 14, 30)), "", + CategoryType.GOOGLE.name()); +} diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/MemberFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/MemberFixtures.java new file mode 100644 index 00000000..359b8f99 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/MemberFixtures.java @@ -0,0 +1,59 @@ +package com.allog.dallog.common.fixtures; + +import static com.allog.dallog.domain.member.domain.SocialType.GOOGLE; + +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.dto.MemberResponse; + +public class MemberFixtures { + + /* 관리자 */ + public static final String 관리자_이메일 = "dallog.admin@gmail.com"; + public static final String 관리자_이름 = "관리자"; + public static final String 관리자_프로필 = "/admin.png"; + public static final MemberResponse 관리자_응답 = new MemberResponse(1L, 관리자_이메일, 관리자_이름, 관리자_프로필, GOOGLE); + + /* 파랑 */ + public static final String 파랑_이메일 = "parang@email.com"; + public static final String 파랑_이름 = "파랑"; + public static final String 파랑_프로필 = "/parang.png"; + public static final MemberResponse 파랑_응답 = new MemberResponse(2L, 파랑_이메일, 파랑_이름, 파랑_프로필, GOOGLE); + + /* 리버 */ + public static final String 리버_이메일 = "leaver@email.com"; + public static final String 리버_이름 = "리버"; + public static final String 리버_프로필 = "/leaver.png"; + public static final MemberResponse 리버_응답 = new MemberResponse(3L, 리버_이메일, 리버_이름, 리버_프로필, GOOGLE); + + /* 후디 */ + public static final String 후디_이메일 = "devhudi@gmail.com"; + public static final String 후디_이름 = "후디"; + public static final String 후디_프로필 = "/hudi.png"; + public static final MemberResponse 후디_응답 = new MemberResponse(4L, 후디_이메일, 후디_이름, 후디_프로필, GOOGLE); + + /* 매트 */ + public static final String 매트_이메일 = "dev.hyeonic@gmail.com"; + public static final String 매트_이름 = "매트"; + public static final String 매트_프로필 = "/mat.png"; + public static final MemberResponse 매트_응답 = new MemberResponse(5L, 매트_이메일, 매트_이름, 매트_프로필, GOOGLE); + + public static Member 관리자() { + return new Member(관리자_이메일, 관리자_이름, 관리자_프로필, GOOGLE); + } + + public static Member 파랑() { + return new Member(파랑_이메일, 파랑_이름, 파랑_프로필, GOOGLE); + } + + public static Member 리버() { + return new Member(리버_이메일, 리버_이름, 리버_프로필, GOOGLE); + } + + public static Member 후디() { + return new Member(후디_이메일, 후디_이름, 후디_프로필, GOOGLE); + } + + public static Member 매트() { + return new Member(매트_이메일, 매트_이름, 매트_프로필, GOOGLE); + } +} diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/OAuthTokenFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/OAuthTokenFixtures.java new file mode 100644 index 00000000..86041645 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/OAuthTokenFixtures.java @@ -0,0 +1,13 @@ +package com.allog.dallog.common.fixtures; + +import com.allog.dallog.domain.auth.domain.OAuthToken; +import com.allog.dallog.domain.member.domain.Member; + +public class OAuthTokenFixtures { + + public static final String REFRESH_TOKEN = "adasdqwrwggsdfsdfaasfadfsdvsvzsdrw"; + + public static OAuthToken OAUTH_TOKEN(final Member member) { + return new OAuthToken(member, REFRESH_TOKEN); + } +} diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/ScheduleFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/ScheduleFixtures.java new file mode 100644 index 00000000..4f478972 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/ScheduleFixtures.java @@ -0,0 +1,71 @@ +package com.allog.dallog.common.fixtures; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.schedule.domain.Schedule; +import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; +import com.allog.dallog.domain.schedule.dto.response.ScheduleResponse; +import java.time.LocalDateTime; + +public class ScheduleFixtures { + + /* 날짜 */ + public static final LocalDateTime 날짜_2022년_7월_1일_0시_0분 = LocalDateTime.of(2022, 7, 1, 0, 0); + public static final LocalDateTime 날짜_2022년_7월_7일_16시_0분 = LocalDateTime.of(2022, 7, 7, 16, 0); + public static final LocalDateTime 날짜_2022년_7월_10일_0시_0분 = LocalDateTime.of(2022, 7, 10, 0, 0); + public static final LocalDateTime 날짜_2022년_7월_10일_11시_59분 = LocalDateTime.of(2022, 7, 10, 23, 59); + public static final LocalDateTime 날짜_2022년_7월_15일_16시_0분 = LocalDateTime.of(2022, 7, 15, 16, 0); + public static final LocalDateTime 날짜_2022년_7월_16일_16시_0분 = LocalDateTime.of(2022, 7, 16, 16, 0); + public static final LocalDateTime 날짜_2022년_7월_16일_16시_1분 = LocalDateTime.of(2022, 7, 16, 16, 1); + public static final LocalDateTime 날짜_2022년_7월_16일_18시_0분 = LocalDateTime.of(2022, 7, 16, 18, 0); + public static final LocalDateTime 날짜_2022년_7월_16일_20시_0분 = LocalDateTime.of(2022, 7, 16, 20, 0); + public static final LocalDateTime 날짜_2022년_7월_17일_23시_59분 = LocalDateTime.of(2022, 7, 17, 23, 59); + public static final LocalDateTime 날짜_2022년_7월_20일_0시_0분 = LocalDateTime.of(2022, 7, 20, 0, 0); + public static final LocalDateTime 날짜_2022년_7월_20일_11시_59분 = LocalDateTime.of(2022, 7, 20, 23, 59); + public static final LocalDateTime 날짜_2022년_7월_27일_0시_0분 = LocalDateTime.of(2022, 7, 27, 0, 0); + public static final LocalDateTime 날짜_2022년_7월_27일_11시_59분 = LocalDateTime.of(2022, 7, 27, 23, 59); + public static final LocalDateTime 날짜_2022년_7월_31일_0시_0분 = LocalDateTime.of(2022, 7, 31, 0, 0); + public static final LocalDateTime 날짜_2022년_8월_15일_14시_0분 = LocalDateTime.of(2022, 8, 15, 14, 0); + public static final LocalDateTime 날짜_2022년_8월_15일_17시_0분 = LocalDateTime.of(2022, 8, 15, 17, 0); + public static final LocalDateTime 날짜_2022년_8월_15일_23시_59분 = LocalDateTime.of(2022, 8, 15, 23, 59); + + /* 알록달록 회의 */ + public static final String 알록달록_회의_제목 = "알록달록 회의"; + public static final LocalDateTime 알록달록_회의_시작일시 = LocalDateTime.of(2022, 7, 15, 16, 0); + public static final LocalDateTime 알록달록_회의_종료일시 = LocalDateTime.of(2022, 7, 16, 16, 0); + public static final String 알록달록_회의_메모 = "알록달록 회의가 있어요"; + public static final ScheduleCreateRequest 알록달록_회의_생성_요청 = new ScheduleCreateRequest(알록달록_회의_제목, 알록달록_회의_시작일시, + 알록달록_회의_종료일시, 알록달록_회의_메모); + public static final ScheduleResponse 알록달록_회의_응답 = new ScheduleResponse(1L, 1L, 알록달록_회의_제목, 알록달록_회의_시작일시, + 알록달록_회의_종료일시, 알록달록_회의_메모, "NORMAL"); + + /* 알록달록 회식 */ + public static final String 알록달록_회식_제목 = "알록달록 회식"; + public static final LocalDateTime 알록달록_회식_시작일시 = LocalDateTime.of(2022, 7, 7, 16, 0); + public static final LocalDateTime 알록달록_회식_종료일시 = LocalDateTime.of(2022, 7, 9, 16, 0); + public static final String 알록달록_회식_메모 = "알록달록 회식이 있어요"; + public static final ScheduleCreateRequest 알록달록_회식_생성_요청 = new ScheduleCreateRequest(알록달록_회식_제목, 알록달록_회식_시작일시, + 알록달록_회식_종료일시, 알록달록_회식_메모); + + /* 레벨 인터뷰 */ + public static final String 레벨_인터뷰_제목 = "레벨 인터뷰"; + public static final LocalDateTime 레벨_인터뷰_시작일시 = LocalDateTime.of(2022, 8, 7, 13, 0); + public static final LocalDateTime 레벨_인터뷰_종료일시 = LocalDateTime.of(2022, 8, 7, 15, 0); + public static final String 레벨_인터뷰_메모 = "레벨 인터뷰가 예정되어 있습니다."; + public static final ScheduleCreateRequest 레벨_인터뷰_생성_요청 = new ScheduleCreateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, + 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + + public static final String 매고라_제목 = "매고라"; + public static final String 매고라_메모 = "매고라가 예정되어 있습니다."; + + public static Schedule 알록달록_회의(final Category category) { + return new Schedule(category, 알록달록_회의_제목, 알록달록_회의_시작일시, 알록달록_회의_종료일시, 알록달록_회의_메모); + } + + public static Schedule 알록달록_회식(final Category category) { + return new Schedule(category, 알록달록_회식_제목, 알록달록_회식_시작일시, 알록달록_회식_종료일시, 알록달록_회식_메모); + } + + public static Schedule 레벨_인터뷰(final Category category) { + return new Schedule(category, 레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + } +} diff --git a/backend/src/test/java/com/allog/dallog/common/fixtures/SubscriptionFixtures.java b/backend/src/test/java/com/allog/dallog/common/fixtures/SubscriptionFixtures.java new file mode 100644 index 00000000..97cd231a --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/common/fixtures/SubscriptionFixtures.java @@ -0,0 +1,35 @@ +package com.allog.dallog.common.fixtures; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.subscription.domain.Color; +import com.allog.dallog.domain.subscription.domain.Subscription; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; + +public class SubscriptionFixtures { + + public static Subscription 색상1_구독(final Member member, final Category category) { + return new Subscription(member, category, Color.COLOR_1); + } + + public static SubscriptionResponse 색상1_구독_응답(final CategoryResponse categoryResponse) { + return new SubscriptionResponse(1L, categoryResponse, Color.COLOR_1, true); + } + + public static Subscription 색상2_구독(final Member member, final Category category) { + return new Subscription(member, category, Color.COLOR_2); + } + + public static SubscriptionResponse 색상2_구독_응답(final CategoryResponse categoryResponse) { + return new SubscriptionResponse(2L, categoryResponse, Color.COLOR_2, true); + } + + public static Subscription 색상3_구독(final Member member, final Category category) { + return new Subscription(member, category, Color.COLOR_3); + } + + public static SubscriptionResponse 색상3_구독_응답(final CategoryResponse categoryResponse) { + return new SubscriptionResponse(3L, categoryResponse, Color.COLOR_3, true); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/auth/application/AuthServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/auth/application/AuthServiceTest.java new file mode 100644 index 00000000..3de5700a --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/auth/application/AuthServiceTest.java @@ -0,0 +1,75 @@ +package com.allog.dallog.domain.auth.application; + +import static com.allog.dallog.common.fixtures.AuthFixtures.MEMBER_이메일; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_MEMBER_인증_코드; +import static org.assertj.core.api.Assertions.assertThat; + +import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.domain.auth.dto.request.TokenRequest; +import com.allog.dallog.domain.auth.dto.response.TokenResponse; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class AuthServiceTest extends ServiceTest { + + @Autowired + private AuthService authService; + + @Autowired + private MemberRepository memberRepository; + + @DisplayName("구글 로그인을 위한 링크를 생성한다.") + @Test + void 구글_로그인을_위한_링크를_생성한다() { + // given & when + String redirectUri = "https://dallog.me/oauth"; + String link = authService.generateGoogleLink(redirectUri); + + // then + assertThat(link).isNotEmpty(); + } + + @DisplayName("토큰 생성을 하면 OAuth 서버에서 인증 후 토큰을 반환한다") + @Test + void 토큰_생성을_하면_OAuth_서버에서_인증_후_토큰을_반환한다() { + // given & when + TokenRequest tokenRequest = new TokenRequest(STUB_MEMBER_인증_코드, "https://dallog.me/oauth"); + TokenResponse actual = authService.generateToken(tokenRequest); + + // then + assertThat(actual.getAccessToken()).isNotEmpty(); + } + + @DisplayName("Authorization Code를 받으면 회원이 데이터베이스에 저장된다.") + @Test + void Authorization_Code를_받으면_회원이_데이터베이스에_저장된다() { + // given + TokenRequest tokenRequest = new TokenRequest(STUB_MEMBER_인증_코드, "https://dallog.me/oauth"); + authService.generateToken(tokenRequest); + + // when & then + assertThat(memberRepository.existsByEmail(MEMBER_이메일)).isTrue(); + // SutbOAuthClient가 반환하는 OAuthMember의 이메일 + } + + @DisplayName("이미 가입된 회원에 대한 Authorization Code를 전달받으면 추가로 유저가 생성되지 않는다") + @Test + void 이미_가입된_회원에_대한_Authorization_Code를_전달받으면_추가로_유저가_생성되지_않는다() { + // 이미 가입된 유저가 소셜 로그인 버튼을 클릭했을 경우엔 회원가입 과정이 생략되고, 곧바로 access token이 발급되어야 한다. + + // given + TokenRequest tokenRequest = new TokenRequest(STUB_MEMBER_인증_코드, "https://dallog.me/oauth"); + authService.generateToken(tokenRequest); + + // when & then + authService.generateToken(tokenRequest); + List actual = memberRepository.findAll(); + + // then + assertThat(actual).hasSize(1); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/auth/application/JwtTokenProviderTest.java b/backend/src/test/java/com/allog/dallog/domain/auth/application/JwtTokenProviderTest.java new file mode 100644 index 00000000..08b938ef --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/auth/application/JwtTokenProviderTest.java @@ -0,0 +1,63 @@ +package com.allog.dallog.domain.auth.application; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.allog.dallog.domain.auth.exception.InvalidTokenException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class JwtTokenProviderTest { + + private static final String JWT_SECRET_KEY = "A".repeat(32); // Secret Key는 최소 32바이트 이상이어야함. + private static final int JWT_EXPIRE_LENGTH = 3600; + private static final String PAYLOAD = "payload"; + + private final JwtTokenProvider jwtTokenProvider = new JwtTokenProvider(JWT_SECRET_KEY, JWT_EXPIRE_LENGTH); + + @DisplayName("JWT 토큰을 생성한다.") + @Test + void JWT_토큰을_생성한다() { + // given & when + String actual = jwtTokenProvider.createToken(PAYLOAD); + + // then + assertThat(actual.split("\\.")).hasSize(3); + } + + @DisplayName("JWT 토큰의 Payload를 가져온다.") + @Test + void JWT_토큰의_Payload를_가져온다() { + // given + String token = jwtTokenProvider.createToken(PAYLOAD); + + // when + String actual = jwtTokenProvider.getPayload(token); + + // then + assertThat(actual).isEqualTo(PAYLOAD); + } + + @DisplayName("validateToken 메서드는 만료된 토큰을 전달하면 예외를 던진다.") + @Test + void validateToken_메서드는_만료된_토큰을_전달하면_예외를_던진다() { + // given + TokenProvider expiredJwtTokenProvider = new JwtTokenProvider(JWT_SECRET_KEY, 0); + String expiredToken = expiredJwtTokenProvider.createToken(PAYLOAD); + + // when & then + assertThatThrownBy(() -> jwtTokenProvider.validateToken(expiredToken)) + .isInstanceOf(InvalidTokenException.class); + } + + @DisplayName("validateToken 메서드는 유효하지 않은 토큰을 전달하면 예외를 던진다.") + @Test + void validateToken_메서드는_유효하지_않은_토큰을_전달하면_예외를_던진다() { + // given + String malformedToken = "malformed"; + + // when & then + assertThatThrownBy(() -> jwtTokenProvider.validateToken(malformedToken)) + .isInstanceOf(InvalidTokenException.class); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/auth/application/StubTokenProvider.java b/backend/src/test/java/com/allog/dallog/domain/auth/application/StubTokenProvider.java new file mode 100644 index 00000000..a4c8418b --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/auth/application/StubTokenProvider.java @@ -0,0 +1,61 @@ +package com.allog.dallog.domain.auth.application; + +import com.allog.dallog.domain.auth.exception.InvalidTokenException; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import javax.crypto.SecretKey; + +public class StubTokenProvider implements TokenProvider { + + private final SecretKey key; + private final long validityInMilliseconds = 0; + + public StubTokenProvider(final String secretKey) { + this.key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public String createToken(final String payload) { + Date now = new Date(); + Date validity = new Date(now.getTime() + validityInMilliseconds); + + return Jwts.builder() + .setSubject(payload) + .setIssuedAt(now) + .setExpiration(validity) + .signWith(key, SignatureAlgorithm.HS256) + .compact(); + } + + @Override + public String getPayload(final String token) { + return Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token) + .getBody() + .getSubject(); + } + + @Override + public void validateToken(final String token) { + try { + Jws claims = Jwts.parserBuilder() + .setSigningKey(key) + .build() + .parseClaimsJws(token); + + claims.getBody() + .getExpiration() + .before(new Date()); + } catch (JwtException | IllegalArgumentException e) { + throw new InvalidTokenException("권한이 없습니다."); + } + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/auth/domain/OAuthTokenRepositoryTest.java b/backend/src/test/java/com/allog/dallog/domain/auth/domain/OAuthTokenRepositoryTest.java new file mode 100644 index 00000000..3a86801a --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/auth/domain/OAuthTokenRepositoryTest.java @@ -0,0 +1,91 @@ +package com.allog.dallog.domain.auth.domain; + +import static com.allog.dallog.common.fixtures.MemberFixtures.매트; +import static com.allog.dallog.common.fixtures.OAuthTokenFixtures.REFRESH_TOKEN; +import static org.assertj.core.api.Assertions.assertThat; + +import com.allog.dallog.common.annotation.RepositoryTest; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import java.util.Optional; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class OAuthTokenRepositoryTest extends RepositoryTest { + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private OAuthTokenRepository oAuthTokenRepository; + + @DisplayName("member id의 OAuthToken이 존재할 경우 true를 반환한다.") + @Test + void member_id의_OAuthToken이_존재할_경우_true를_반환한다() { + // given + Member 매트 = memberRepository.save(매트()); + + String refreshToken = REFRESH_TOKEN; + oAuthTokenRepository.save(new OAuthToken(매트, refreshToken)); + + // when + boolean actual = oAuthTokenRepository.existsByMemberId(매트.getId()); + + // then + assertThat(actual).isTrue(); + } + + @DisplayName("member id의 OAuthToken이 존재하지 않을 경우 false를 반환한다.") + @Test + void member_id의_OAuthToken이_존재하지_않을_경우_false를_반환한다() { + // given & when + boolean actual = oAuthTokenRepository.existsByMemberId(0L); + + // then + assertThat(actual).isFalse(); + } + + @DisplayName("member id의 OAuthToken이 존재할 경우 Optional은 비어있지 않다.") + @Test + void member_id의_OAuthToken이_존재할_경우_Optional은_비어있지_않다() { + // given + Member 매트 = memberRepository.save(매트()); + + String refreshToken = REFRESH_TOKEN; + oAuthTokenRepository.save(new OAuthToken(매트, refreshToken)); + + // when + Optional actual = oAuthTokenRepository.findByMemberId(매트.getId()); + + // then + assertThat(actual).isNotEmpty(); + } + + @DisplayName("member id의 OAuthToken이 존재하지 않을 경우 비어있다.") + @Test + void member_id의_OAuthToken이_존재하지_않을_경우_비어있다() { + // given & when + Optional actual = oAuthTokenRepository.findByMemberId(0L); + + // then + assertThat(actual).isEmpty(); + } + + @DisplayName("member id의 OAuthToke을 삭제한다.") + @Test + void member_id의_OAuthToken을_삭제한다() { + // given + Member 매트 = memberRepository.save(매트()); + + String refreshToken = REFRESH_TOKEN; + oAuthTokenRepository.save(new OAuthToken(매트, refreshToken)); + + // when + oAuthTokenRepository.deleteByMemberId(매트.getId()); + + // then + Optional actual = oAuthTokenRepository.findByMemberId(매트.getId()); + assertThat(actual).isEmpty(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/auth/domain/OAuthTokenTest.java b/backend/src/test/java/com/allog/dallog/domain/auth/domain/OAuthTokenTest.java new file mode 100644 index 00000000..f7b7c309 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/auth/domain/OAuthTokenTest.java @@ -0,0 +1,40 @@ +package com.allog.dallog.domain.auth.domain; + +import static com.allog.dallog.common.fixtures.MemberFixtures.매트; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import com.allog.dallog.domain.member.domain.Member; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class OAuthTokenTest { + + @DisplayName("OAuth token을 생성한다.") + @Test + void OAuth_token을_생성한다() { + // given + Member 매트 = 매트(); + String refreshToken = "adasaegsfadasdasfgfgrgredksgdffa"; + + // when & then + assertDoesNotThrow(() -> new OAuthToken(매트, refreshToken)); + } + + @DisplayName("refresh token을 교체한다.") + @Test + void refresh_token을_교체한다() { + // given + Member 매트 = 매트(); + String refreshToken = "adasaegsfadasdasfgfgrgredksgdffa"; + OAuthToken oAuthToken = new OAuthToken(매트, refreshToken); + + String updatedRefreshToken = "dfgsbnskjglnafgkajfnakfjgngejlkrqgn"; + + // when + oAuthToken.change(updatedRefreshToken); + + // then + assertThat(oAuthToken.getRefreshToken()).isEqualTo(updatedRefreshToken); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/category/application/CategoryServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/category/application/CategoryServiceTest.java new file mode 100644 index 00000000..36e4380a --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/category/application/CategoryServiceTest.java @@ -0,0 +1,455 @@ +package com.allog.dallog.domain.category.application; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_이름; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_이름; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_이름; +import static com.allog.dallog.common.fixtures.CategoryFixtures.내_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.매트_아고라_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.매트_아고라_이름; +import static com.allog.dallog.common.fixtures.CategoryFixtures.우아한테크코스_외부_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.후디_JPA_스터디_생성_요청; +import static com.allog.dallog.common.fixtures.ExternalCategoryFixtures.대한민국_공휴일_생성_요청; +import static com.allog.dallog.common.fixtures.ExternalCategoryFixtures.대한민국_공휴일_이름; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.MemberFixtures.리버; +import static com.allog.dallog.common.fixtures.MemberFixtures.매트; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_생성_요청; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회식_생성_요청; +import static com.allog.dallog.domain.category.domain.CategoryType.GOOGLE; +import static com.allog.dallog.domain.category.domain.CategoryType.NORMAL; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.common.fixtures.CategoryFixtures; +import com.allog.dallog.domain.auth.exception.NoPermissionException; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.CategoryRepository; +import com.allog.dallog.domain.category.dto.request.CategoryCreateRequest; +import com.allog.dallog.domain.category.dto.request.CategoryUpdateRequest; +import com.allog.dallog.domain.category.dto.response.CategoriesResponse; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.category.exception.DuplicatedExternalCategoryException; +import com.allog.dallog.domain.category.exception.InvalidCategoryException; +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; +import com.allog.dallog.domain.member.application.MemberService; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.schedule.application.ScheduleService; +import com.allog.dallog.domain.schedule.dto.response.ScheduleResponse; +import com.allog.dallog.domain.schedule.exception.NoSuchScheduleException; +import com.allog.dallog.domain.subscription.application.SubscriptionService; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; +import com.allog.dallog.domain.subscription.exception.NoSuchSubscriptionException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; + +class CategoryServiceTest extends ServiceTest { + + @Autowired + private CategoryService categoryService; + + @Autowired + private CategoryRepository categoryRepository; + + @Autowired + private SubscriptionService subscriptionService; + + @Autowired + private ScheduleService scheduleService; + + @Autowired + private MemberService memberService; + + @Autowired + private MemberRepository memberRepository; + + @DisplayName("새로운 카테고리를 생성한다.") + @Test + void 새로운_카테고리를_생성한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + // when + CategoryResponse response = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + + // then + assertThat(response.getName()).isEqualTo(공통_일정_이름); + } + + @DisplayName("새로운 개인 카테고리를 생성한다.") + @Test + void 새로운_개인_카테고리를_생성한다() { + // given + Member 후디 = memberRepository.save(후디()); + + // when + CategoryResponse 내_일정_응답 = categoryService.save(후디.getId(), 내_일정_생성_요청); + Category 내_일정 = categoryRepository.findById(내_일정_응답.getId()).get(); + + // then + assertAll(() -> { + assertThat(내_일정.getName()).isEqualTo(CategoryFixtures.내_일정_이름); + assertThat(내_일정.isPersonal()).isTrue(); + }); + } + + @DisplayName("새로운 카테고리를 생성 할 떄 이름이 공백이거나 길이가 20을 초과하는 경우 예외를 던진다.") + @ParameterizedTest + @ValueSource(strings = {"", "일이삼사오육칠팔구십일이삼사오육칠팔구십일", "알록달록 알록달록 알록달록 알록달록 알록달록 알록달록 카테고리"}) + void 새로운_카테고리를_생성_할_때_이름이_공백이거나_길이가_20을_초과하는_경우_예외를_던진다(final String name) { + // given + CategoryCreateRequest request = new CategoryCreateRequest(name, NORMAL); + Member 관리자 = memberRepository.save(관리자()); + + // when & then + assertThatThrownBy(() -> categoryService.save(관리자.getId(), request)) + .isInstanceOf(InvalidCategoryException.class); + } + + @DisplayName("새로운 외부 카테고리를 생성한다.") + @Test + void 새로운_외부_카테고리를_생성한다() { + // given + Member 후디 = memberRepository.save(후디()); + + // when + CategoryResponse 후디_대한민국_공휴일_카테고리_응답 = categoryService.save(후디.getId(), 대한민국_공휴일_생성_요청); + Category 후디_대한민국_공휴일_카테고리 = categoryRepository.findById(후디_대한민국_공휴일_카테고리_응답.getId()).get(); + + // then + assertAll(() -> { + assertThat(후디_대한민국_공휴일_카테고리.getName()).isEqualTo(대한민국_공휴일_이름); + assertThat(후디_대한민국_공휴일_카테고리.getCategoryType()).isEqualTo(GOOGLE); + }); + } + + @DisplayName("중복된 외부 카테고리를 저장하는 경우 예외를 던진다.") + @Test + void 중복된_외부_카테고리를_저장하는_경우_예외를_던진다() { + // given + Member 후디 = memberRepository.save(후디()); + + // when + categoryService.save(후디.getId(), 대한민국_공휴일_생성_요청); + + // then + assertThatThrownBy(() -> categoryService.save(후디.getId(), 대한민국_공휴일_생성_요청)) + .isInstanceOf(DuplicatedExternalCategoryException.class); + } + + @DisplayName("페이지와 제목을 받아 해당하는 구간의 카테고리를 가져온다.") + @Test + void 페이지와_제목을_받아_해당하는_구간의_카테고리를_가져온다() { + // given + Member 관리자 = memberRepository.save(관리자()); + Long 관리자_ID = 관리자.getId(); + categoryService.save(관리자_ID, 공통_일정_생성_요청); + categoryService.save(관리자_ID, BE_일정_생성_요청); + categoryService.save(관리자_ID, FE_일정_생성_요청); + categoryService.save(관리자_ID, 매트_아고라_생성_요청); + categoryService.save(관리자_ID, 후디_JPA_스터디_생성_요청); + + PageRequest request = PageRequest.of(0, 3); + + // when + CategoriesResponse response = categoryService.findNormalByName("일", request); + + // then + assertThat(response.getCategories()) + .hasSize(3) + .extracting(CategoryResponse::getName) + .contains(공통_일정_이름, BE_일정_이름, FE_일정_이름); + } + + @DisplayName("개인 카테고리는 전체 조회 대상에서 제외된다.") + @Test + void 개인_카테고리는_전체_조회_대상에서_제외된다() { + // given + MemberResponse 후디 = memberService.save(후디()); // 후디의 개인 카테고리가 생성된다 + MemberResponse 리버 = memberService.save(리버()); // 리버의 개인 카테고리가 생성된다 + categoryService.save(후디.getId(), 내_일정_생성_요청); + + // when + CategoriesResponse response = categoryService.findNormalByName("", PageRequest.of(0, 10)); + + // then + assertThat(response.getCategories()).hasSize(0); + } + + @DisplayName("회원 id와 페이지를 기반으로 카테고리를 가져온다.") + @Test + void 회원_id와_페이지를_기반으로_카테고리를_가져온다() { + // given + Member 관리자 = memberRepository.save(관리자()); + Long 관리자_ID = 관리자.getId(); + categoryService.save(관리자_ID, 공통_일정_생성_요청); + categoryService.save(관리자_ID, BE_일정_생성_요청); + categoryService.save(관리자_ID, FE_일정_생성_요청); + categoryService.save(관리자_ID, 매트_아고라_생성_요청); + categoryService.save(관리자_ID, 후디_JPA_스터디_생성_요청); + + PageRequest request = PageRequest.of(1, 2); + + // when + CategoriesResponse response = categoryService.findMineByName(관리자_ID, "", request); + + // then + assertThat(response.getCategories()) + .hasSize(2) + .extracting(CategoryResponse::getName) + .contains(FE_일정_이름, 매트_아고라_이름); + } + + @DisplayName("회원 id와 제목과 페이지를 받아 해당하는 구간의 카테고리를 가져온다.") + @Test + void 회원_id와_제목과_페이지를_받아_해당하는_구간의_카테고리를_가져온다() { + // given + Member 관리자 = memberRepository.save(관리자()); + Long 관리자_ID = 관리자.getId(); + categoryService.save(관리자_ID, 공통_일정_생성_요청); + categoryService.save(관리자_ID, BE_일정_생성_요청); + categoryService.save(관리자_ID, FE_일정_생성_요청); + categoryService.save(관리자_ID, 매트_아고라_생성_요청); + categoryService.save(관리자_ID, 후디_JPA_스터디_생성_요청); + + PageRequest request = PageRequest.of(0, 3); + + // when + CategoriesResponse response = categoryService.findMineByName(관리자_ID, "일", request); + + // then + assertThat(response.getCategories()) + .hasSize(3) + .extracting(CategoryResponse::getName) + .contains(공통_일정_이름, BE_일정_이름, FE_일정_이름); + } + + @DisplayName("id를 통해 카테고리를 단건 조회한다.") + @Test + void id를_통해_카테고리를_단건_조회한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + + // when & then + CategoryResponse 조회한_공통_일정 = categoryService.findById(공통_일정.getId()); + + assertAll(() -> { + assertThat(조회한_공통_일정.getId()).isEqualTo(공통_일정.getId()); + assertThat(조회한_공통_일정.getName()).isEqualTo(공통_일정.getName()); + }); + } + + @DisplayName("id를 통해 카테고리를 단건 조회할 때 카테고리가 존재하지 않다면 예외가 발생한다.") + @Test + void id를_통해_카테고리를_단건_조회할_때_카테고리가_존재하지_않다면_예외가_발생한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + + // when & then + assertThatThrownBy(() -> categoryService.findById(공통_일정.getId() + 1)) + .isInstanceOf(NoSuchCategoryException.class); + } + + @DisplayName("회원과 카테고리 id를 통해 카테고리를 수정한다.") + @Test + void 회원과_카테고리_id를_통해_카테고리를_수정한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + + String 우테코_공통_일정_이름 = "우테코 공통 일정"; + CategoryUpdateRequest categoryUpdateRequest = new CategoryUpdateRequest(우테코_공통_일정_이름); + + // when + categoryService.update(관리자.getId(), 공통_일정.getId(), categoryUpdateRequest); + Category category = categoryService.getCategory(공통_일정.getId()); + + //then + assertThat(category.getName()).isEqualTo(우테코_공통_일정_이름); + } + + @DisplayName("존재하지 않는 카테고리를 수정할 경우 예외를 던진다.") + @Test + void 존재하지_않는_카테고리를_수정할_경우_예외를_던진다() { + // given + Member 관리자 = memberRepository.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + CategoryUpdateRequest categoryUpdateRequest = new CategoryUpdateRequest(BE_일정_이름); + + // when & then + assertThatThrownBy(() -> categoryService.update(관리자.getId(), 공통_일정.getId() + 1, categoryUpdateRequest)) + .isInstanceOf(NoSuchCategoryException.class); + } + + @DisplayName("자신이 만들지 않은 카테고리를 수정할 경우 예외를 던진다.") + @Test + void 자신이_만들지_않은_카테고리를_수정할_경우_예외를_던진다() { + // given + Member 관리자 = memberRepository.save(관리자()); + Member 매트 = memberRepository.save(매트()); + + CategoryResponse savedCategory = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + CategoryUpdateRequest categoryUpdateRequest = new CategoryUpdateRequest("우테코 공통 일정"); + + // when & then + assertThatThrownBy(() -> categoryService.update(매트.getId(), savedCategory.getId(), categoryUpdateRequest)) + .isInstanceOf(NoPermissionException.class); + } + + @DisplayName("회원과 카테고리 id를 통해 카테고리를 삭제한다.") + @Test + void 회원과_카테고리_id를_통해_카테고리를_삭제한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + + // when + categoryService.deleteById(관리자.getId(), 공통_일정.getId()); + + //then + assertThatThrownBy(() -> categoryService.getCategory(공통_일정.getId())) + .isInstanceOf(NoSuchCategoryException.class); + } + + @DisplayName("존재하지 않는 카테고리를 삭제할 경우 예외를 던진다.") + @Test + void 존재하지_않는_카테고리를_삭제할_경우_예외를_던진다() { + // given + Member 관리자 = memberRepository.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + + // when & then + assertThatThrownBy(() -> categoryService.deleteById(관리자.getId(), 공통_일정.getId() + 1)) + .isInstanceOf(NoSuchCategoryException.class); + } + + @DisplayName("자신이 만들지 않은 카테고리를 삭제할 경우 예외를 던진다.") + @Test + void 자신이_만들지_않은_카테고리를_삭제할_경우_예외를_던진다() { + // given + Member 관리자 = memberRepository.save(관리자()); + Member 매트 = memberRepository.save(매트()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + + // when & then + assertThatThrownBy( + () -> categoryService.deleteById(매트.getId(), 공통_일정.getId())) + .isInstanceOf(NoPermissionException.class); + } + + @DisplayName("카테고리 삭제 시 연관된 일정 엔티티도 모두 제거된다.") + @Test + void 카테고리_삭제_시_연관된_일정_엔티티도_모두_제거된다() { + // given + Member 관리자 = memberRepository.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + ScheduleResponse 알록달록_회식 = scheduleService.save(관리자.getId(), 공통_일정.getId(), 알록달록_회식_생성_요청); + ScheduleResponse 레벨_인터뷰 = scheduleService.save(관리자.getId(), 공통_일정.getId(), 레벨_인터뷰_생성_요청); + + // when + categoryService.deleteById(관리자.getId(), 공통_일정.getId()); + + // then + assertAll(() -> { + assertThatThrownBy(() -> scheduleService.findById(알록달록_회식.getId())) + .isInstanceOf(NoSuchScheduleException.class); + assertThatThrownBy(() -> scheduleService.findById(레벨_인터뷰.getId())) + .isInstanceOf(NoSuchScheduleException.class); + }); + } + + @DisplayName("카테고리 삭제 시 연관된 구독 엔티티도 모두 제거된다.") + @Test + void 카테고리_삭제_시_연관된_구독_엔티티도_모두_제거된다() { + // given + Member 관리자 = memberRepository.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + + Member 후디 = memberRepository.save(후디()); + SubscriptionResponse 구독 = subscriptionService.save(후디.getId(), 공통_일정.getId()); + + // when + categoryService.deleteById(관리자.getId(), 공통_일정.getId()); + + // then + assertThatThrownBy(() -> subscriptionService.findById(구독.getId())) + .isInstanceOf(NoSuchSubscriptionException.class); + } + + @DisplayName("개인 카테고리는 삭제할 수 없다.") + @Test + void 개인_카테고리는_삭제할_수_없다() { + // given + Member 관리자 = memberRepository.save(관리자()); + CategoryResponse 내_일정 = categoryService.save(관리자.getId(), 내_일정_생성_요청); + subscriptionService.save(관리자.getId(), 내_일정.getId()); + + // when & then + assertThatThrownBy(() -> categoryService.deleteById(관리자.getId(), 내_일정.getId())) + .isInstanceOf(InvalidCategoryException.class); + } + + @DisplayName("특정 회원의 카테고리를 전부 삭제한다.") + @Test + void 특정_회원의_카테고리를_전부_삭제한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + + // when + categoryService.deleteByMemberId(관리자.getId()); + + //then + assertThatThrownBy(() -> categoryService.getCategory(공통_일정.getId())) + .isInstanceOf(NoSuchCategoryException.class); + } + + @DisplayName("특정 회원의 카테고리를 삭제할 때 연관된 일정도 삭제한다.") + @Test + void 특정_회원의_카테고리를_삭제할_때_연관된_일정도_삭제한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + ScheduleResponse 알록달록_회식 = scheduleService.save(관리자.getId(), 공통_일정.getId(), 알록달록_회식_생성_요청); + ScheduleResponse 레벨_인터뷰 = scheduleService.save(관리자.getId(), 공통_일정.getId(), 레벨_인터뷰_생성_요청); + + // when + categoryService.deleteByMemberId(관리자.getId()); + + // then + assertAll(() -> { + assertThatThrownBy(() -> scheduleService.findById(알록달록_회식.getId())) + .isInstanceOf(NoSuchScheduleException.class); + assertThatThrownBy(() -> scheduleService.findById(레벨_인터뷰.getId())) + .isInstanceOf(NoSuchScheduleException.class); + }); + } + + @DisplayName("외부 캘린더의 카테고리를 삭제한다.") + @Test + void 외부_캘린더의_카테고리를_삭제한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + CategoryResponse 우아한테크코스_외부_일정 = categoryService.save(관리자.getId(), 우아한테크코스_외부_일정_생성_요청); + + // when + categoryService.deleteById(관리자.getId(), 우아한테크코스_외부_일정.getId()); + + // then + assertThatThrownBy(() -> categoryService.findById(우아한테크코스_외부_일정.getId())) + .isInstanceOf(NoSuchCategoryException.class); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryRepositoryTest.java b/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryRepositoryTest.java new file mode 100644 index 00000000..c2be8d78 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryRepositoryTest.java @@ -0,0 +1,188 @@ +package com.allog.dallog.domain.category.domain; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_이름; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_이름; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_이름; +import static com.allog.dallog.common.fixtures.CategoryFixtures.우아한테크코스_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.내_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.매트_아고라; +import static com.allog.dallog.common.fixtures.CategoryFixtures.후디_JPA_스터디; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static com.allog.dallog.domain.category.domain.CategoryType.NORMAL; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.common.annotation.RepositoryTest; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import java.util.List; +import java.util.Objects; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Slice; + +class CategoryRepositoryTest extends RepositoryTest { + + @Autowired + private CategoryRepository categoryRepository; + + @Autowired + private MemberRepository memberRepository; + + @DisplayName("카테고리 이름과 타입과 페이징을 활용하여 해당하는 카테고리를 조회한다.") + @Test + void 카테고리_이름과_타입과_페이징을_활용하여_해당하는_카테고리를_조회한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + categoryRepository.save(공통_일정(관리자)); + categoryRepository.save(BE_일정(관리자)); + categoryRepository.save(FE_일정(관리자)); + categoryRepository.save(매트_아고라(관리자)); + categoryRepository.save(후디_JPA_스터디(관리자)); + categoryRepository.save(내_일정(관리자)); + categoryRepository.save(우아한테크코스_일정(관리자)); + + PageRequest pageRequest = PageRequest.of(0, 5); + + // when + Slice actual = categoryRepository.findByNameContainingAndCategoryType("일", NORMAL, pageRequest); + + // then + assertThat(actual.getContent()).hasSize(3) + .extracting(Category::getName) + .contains(공통_일정_이름, BE_일정_이름, FE_일정_이름); + } + + @DisplayName("카테고리 이름 검색 결과가 존재하지 않는 경우 아무것도 조회 하지 않는다.") + @Test + void 카테고리_이름_검색_결과가_존재하지_않는_경우_아무것도_조회_하지_않는다() { + // given + Member 관리자 = memberRepository.save(관리자()); + categoryRepository.save(공통_일정(관리자)); + categoryRepository.save(BE_일정(관리자)); + categoryRepository.save(FE_일정(관리자)); + categoryRepository.save(매트_아고라(관리자)); + categoryRepository.save(후디_JPA_스터디(관리자)); + + PageRequest pageRequest = PageRequest.of(0, 5); + + // when + Slice actual = categoryRepository.findByNameContainingAndCategoryType("파랑", NORMAL, pageRequest); + + // then + assertThat(actual.getContent()).hasSize(0); + } + + @DisplayName("특정 멤버가 생성한 카테고리를 카테고리 이름과 페이징을 통해 조회한다.") + @Test + void 특정_멤버가_생성한_카테고리를_카테고리_이름과_페이징을_통해_조회한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + categoryRepository.save(공통_일정(관리자)); + categoryRepository.save(BE_일정(관리자)); + categoryRepository.save(FE_일정(관리자)); + categoryRepository.save(매트_아고라(관리자)); + categoryRepository.save(후디_JPA_스터디(관리자)); + + Member 후디 = memberRepository.save(후디()); + categoryRepository.save(후디_JPA_스터디(후디)); + + PageRequest pageRequest = PageRequest.of(0, 8); + + // when + Slice categories = categoryRepository.findByMemberIdLikeCategoryName(관리자.getId(), "일", pageRequest); + + // then + assertAll(() -> { + assertThat(categories.getContent()).hasSize(3) + .extracting(Category::getName) + .containsExactlyInAnyOrder(공통_일정_이름, BE_일정_이름, FE_일정_이름); + assertThat( + categories.getContent().stream() + .map(Category::getCreatedAt) + .allMatch(Objects::nonNull)) + .isTrue(); + }); + } + + @DisplayName("특정 멤버가 생성한 카테고리를 조회한다.") + @Test + void 특정_멤버가_생성한_카테고리를_조회한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + categoryRepository.save(공통_일정(관리자)); + categoryRepository.save(BE_일정(관리자)); + categoryRepository.save(FE_일정(관리자)); + + Member 후디 = memberRepository.save(후디()); + categoryRepository.save(후디_JPA_스터디(후디)); + + // when + List categories = categoryRepository.findByMemberId(관리자.getId()); + + // then + assertAll(() -> { + assertThat(categories).hasSize(3) + .extracting(Category::getName) + .containsExactlyInAnyOrder(공통_일정_이름, BE_일정_이름, FE_일정_이름); + assertThat( + categories.stream() + .map(Category::getCreatedAt) + .allMatch(Objects::nonNull)) + .isTrue(); + }); + } + + + @DisplayName("카테고리 id와 회원의 id가 모두 일치하는 카테고리가 있으면 true를 반환한다.") + @Test + void 카테고리_id와_회원의_id가_모두_일치하는_카테고리가_있으면_true를_반환한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + Category 공통_일정 = categoryRepository.save(공통_일정(관리자)); + + // when + boolean actual = categoryRepository.existsByIdAndMemberId(공통_일정.getId(), 관리자.getId()); + + // then + assertThat(actual).isTrue(); + } + + @DisplayName("카테고리 id와 회원의 id가 모두 일치하는 카테고리가 없으면 false를 반환한다.") + @Test + void 카테고리_id와_회원의_id가_모두_일치하는_카테고리가_없으면_false를_반환한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + Category 공통_일정 = categoryRepository.save(공통_일정(관리자)); + + // when + boolean actual = categoryRepository.existsByIdAndMemberId(공통_일정.getId(), 0L); + + // then + assertThat(actual).isFalse(); + } + + @DisplayName("특정 회원이 만든 카테고리를 모두 삭제한다") + @Test + void 특정_회원이_만든_카테고리를_모두_삭제한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + categoryRepository.save(공통_일정(관리자)); + categoryRepository.save(BE_일정(관리자)); + + PageRequest pageRequest = PageRequest.of(0, 2); + + // when + categoryRepository.deleteByMemberId(관리자.getId()); + + // then + assertThat(categoryRepository.findByMemberIdLikeCategoryName(관리자.getId(), "", pageRequest)) + .hasSize(0); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryTest.java b/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryTest.java new file mode 100644 index 00000000..815935db --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryTest.java @@ -0,0 +1,100 @@ +package com.allog.dallog.domain.category.domain; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.우아한테크코스_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.내_일정; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import com.allog.dallog.domain.category.exception.InvalidCategoryException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class CategoryTest { + + @DisplayName("카테고리를 생성한다.") + @Test + void 카테고리를_생성한다() { + // given + String name = "BE 공식일정"; + + // when & then + assertDoesNotThrow(() -> new Category(name, 후디())); + } + + @DisplayName("카테고리 이름이 공백인 경우 예외를 던진다.") + @Test + void 카테고리_이름이_공백인_경우_예외를_던진다() { + // given + String name = ""; + + // when & then + assertThatThrownBy(() -> new Category(name, 후디())) + .isInstanceOf(InvalidCategoryException.class); + } + + @DisplayName("카테고리 이름의 길이가 20을 초과하는 경우 예외를 던진다.") + @ParameterizedTest + @ValueSource(strings = {"일이삼사오육칠팔구십일이삼사오육칠팔구십일", + "알록달록 알록달록 알록달록 알록달록 알록달록 알록달록 카테고리"}) + void 카테고리_이름의_길이가_20을_초과하는_경우_예외를_던진다(final String name) { + // given & when & then + assertThatThrownBy(() -> new Category(name, 후디())) + .isInstanceOf(InvalidCategoryException.class); + } + + @DisplayName("개인 카테고리의 이름을 수정하는 경우 예외를 던진다.") + @Test + void 개인_카테고리의_이름을_수정하는_경우_예외를_던진다() { + // given + Category 내_일정 = 내_일정(관리자()); + + // when & then + assertThatThrownBy(() -> 내_일정.changeName("바꿀 이름")) + .isInstanceOf(InvalidCategoryException.class); + } + + @DisplayName("제공된 멤버의 ID와 카테고리를 생성한 멤버의 ID가 일치하지 않으면 false를 반환한다.") + @Test + void 제공된_멤버의_ID와_카테고리를_생성한_멤버의_ID가_일치하지_않으면_false를_반환한다() { + // given + Category BE_일정 = BE_일정(관리자()); + + // when + boolean actual = BE_일정.isCreator(999L); + + // then + assertThat(actual).isFalse(); + } + + @DisplayName("개인 카테고리면 true를 반환한다.") + @Test + void 개인_카테고리면_true를_반환한다() { + // given + Category 내_일정 = 내_일정(관리자()); + + // when + boolean actual = 내_일정.isPersonal(); + + // then + assertThat(actual).isTrue(); + } + + @DisplayName("외부 연동 카테고리면 true를 반환한다.") + @Test + void 외부_연동_카테고리면_true를_반환한다() { + // given + Category 우아한테크코스_일정 = 우아한테크코스_일정(관리자()); + + // when + boolean actual = 우아한테크코스_일정.isExternal(); + + // then + assertThat(actual).isTrue(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryTypeTest.java b/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryTypeTest.java new file mode 100644 index 00000000..43572c79 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/category/domain/CategoryTypeTest.java @@ -0,0 +1,36 @@ +package com.allog.dallog.domain.category.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +public class CategoryTypeTest { + + @DisplayName("카테고리 종류를 가져온다.") + @ParameterizedTest + @EnumSource + void 카테고리_종류를_가져온다(final CategoryType categoryType) { + // given & when & then + assertAll(() -> { + assertThat(CategoryType.from(categoryType.name())).isEqualTo(categoryType); + assertThat(CategoryType.from(categoryType.name().toLowerCase())).isEqualTo(categoryType); + }); + } + + @DisplayName("존재하지 않는 카테고리 종류인 경우 예외를 던진다.") + @Test + void 존재하지_않는_카테고리_종류인_경우_예외를_던진다() { + // given + String notExistingCategoryType = "존재하지 않는 카테고리 종류"; + + // when & then + assertThatThrownBy(() -> CategoryType.from(notExistingCategoryType)) + .isInstanceOf(NoSuchCategoryException.class); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/composition/application/CalendarServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/composition/application/CalendarServiceTest.java new file mode 100644 index 00000000..52762016 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/composition/application/CalendarServiceTest.java @@ -0,0 +1,192 @@ +package com.allog.dallog.domain.composition.application; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.우아한테크코스_외부_일정_생성_요청; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.MemberFixtures.리버; +import static com.allog.dallog.common.fixtures.MemberFixtures.매트; +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_1분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_18시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_20시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_1일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_31일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_7일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_14시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_17시_0분; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.domain.auth.domain.OAuthToken; +import com.allog.dallog.domain.auth.domain.OAuthTokenRepository; +import com.allog.dallog.domain.category.application.CategoryService; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.ExternalCategoryDetail; +import com.allog.dallog.domain.category.domain.ExternalCategoryDetailRepository; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.member.application.MemberService; +import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.schedule.application.ScheduleService; +import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; +import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; +import com.allog.dallog.domain.schedule.dto.response.MemberScheduleResponse; +import com.allog.dallog.domain.schedule.dto.response.MemberScheduleResponses; +import com.allog.dallog.domain.subscription.application.SubscriptionService; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class CalendarServiceTest extends ServiceTest { + + @Autowired + private CalendarService calendarService; + + @Autowired + private MemberService memberService; + + @Autowired + private CategoryService categoryService; + + @Autowired + private OAuthTokenRepository oAuthTokenRepository; + + @Autowired + private ExternalCategoryDetailRepository externalCategoryDetailRepository; + + @Autowired + private SubscriptionService subscriptionService; + + @Autowired + private ScheduleService scheduleService; + + @DisplayName("시작일시와 종료일시로 유저의 캘린더를 일정 유형에 따라 분류하고 정렬하여 반환한다.") + @Test + void 시작일시와_종료일시로_유저의_캘린더를_일정_유형에_따라_분류하고_정렬하여_반환한다() { + // given + MemberResponse 후디 = memberService.save(후디()); + oAuthTokenRepository.save(new OAuthToken(memberService.getMember(후디.getId()), "wegwefaasdasdasda")); + + CategoryResponse BE_일정_응답 = categoryService.save(후디.getId(), BE_일정_생성_요청); + Category BE_일정 = categoryService.getCategory(BE_일정_응답.getId()); + + subscriptionService.save(후디.getId(), BE_일정.getId()); + + /* 장기간 일정 */ + scheduleService.save(후디.getId(), BE_일정.getId(), + new ScheduleCreateRequest("장기간 첫번째", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); + scheduleService.save(후디.getId(), BE_일정.getId(), + new ScheduleCreateRequest("장기간 두번째", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_31일_0시_0분, "")); + scheduleService.save(후디.getId(), BE_일정.getId(), + new ScheduleCreateRequest("장기간 세번째", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_16일_16시_1분, "")); + scheduleService.save(후디.getId(), BE_일정.getId(), + new ScheduleCreateRequest("장기간 네번째", 날짜_2022년_7월_7일_16시_0분, 날짜_2022년_7월_15일_16시_0분, "")); + scheduleService.save(후디.getId(), BE_일정.getId(), + new ScheduleCreateRequest("장기간 다섯번째", 날짜_2022년_7월_31일_0시_0분, 날짜_2022년_8월_15일_17시_0분, "")); + + /* 종일 일정 */ + scheduleService.save(후디.getId(), BE_일정.getId(), + new ScheduleCreateRequest("종일 첫번째", 날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_10일_11시_59분, "")); + scheduleService.save(후디.getId(), BE_일정.getId(), + new ScheduleCreateRequest("종일 두번째", 날짜_2022년_7월_20일_0시_0분, 날짜_2022년_7월_20일_11시_59분, "")); + scheduleService.save(후디.getId(), BE_일정.getId(), + new ScheduleCreateRequest("종일 세번째", 날짜_2022년_7월_27일_0시_0분, 날짜_2022년_7월_27일_11시_59분, "")); + + /* 몇시간 일정 */ + scheduleService.save(후디.getId(), BE_일정.getId(), + new ScheduleCreateRequest("몇시간 첫번째", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_20시_0분, "")); + scheduleService.save(후디.getId(), BE_일정.getId(), + new ScheduleCreateRequest("몇시간 두번째", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_18시_0분, "")); + scheduleService.save(후디.getId(), BE_일정.getId(), + new ScheduleCreateRequest("몇시간 세번째", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_16시_1분, "")); + scheduleService.save(후디.getId(), BE_일정.getId(), + new ScheduleCreateRequest("몇시간 네번째", 날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_16일_18시_0분, "")); + + CategoryResponse 우아한테크코스_외부_일정_응답 = categoryService.save(후디.getId(), 우아한테크코스_외부_일정_생성_요청); + Category 우아한테크코스 = categoryService.getCategory(우아한테크코스_외부_일정_응답.getId()); + externalCategoryDetailRepository.save(new ExternalCategoryDetail(우아한테크코스, "dfggsdfasdasadsgs")); + + subscriptionService.save(후디.getId(), 우아한테크코스.getId()); + + // when + MemberScheduleResponses memberScheduleResponses = calendarService.findSchedulesByMemberId(후디.getId(), + new DateRangeRequest("2022-07-01T00:00", "2022-08-15T23:59")); + + // then + assertAll(() -> { + assertThat(memberScheduleResponses.getLongTerms()).extracting(MemberScheduleResponse::getTitle) + .contains("장기간 첫번째", "장기간 두번째", "장기간 세번째", "장기간 네번째", "장기간 다섯번째"); + assertThat(memberScheduleResponses.getAllDays()).extracting(MemberScheduleResponse::getTitle) + .contains("종일 첫번째", "종일 두번째", "종일 세번째"); + assertThat(memberScheduleResponses.getFewHours()).extracting(MemberScheduleResponse::getTitle) + .contains("몇시간 첫번째", "몇시간 두번째", "몇시간 세번째", "몇시간 네번째"); + }); + } + + // TODO: 외부 일정을 잘 가져오는지를 테스트 해야함 + @DisplayName("카테고리를 구독하는 유저들의 모든 내부 일정을 가져온다.") + @Test + void 카테고리를_구독하는_유저들의_모든_내부_일정을_가져온다() { + // given + MemberResponse 관리자 = memberService.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + CategoryResponse BE_일정 = categoryService.save(관리자.getId(), BE_일정_생성_요청); + CategoryResponse FE_일정 = categoryService.save(관리자.getId(), FE_일정_생성_요청); + + /* 카테고리에 일정 추가 */ + scheduleService.save(관리자.getId(), 공통_일정.getId(), + new ScheduleCreateRequest("공통 일정 1", 날짜_2022년_7월_7일_16시_0분, 날짜_2022년_7월_10일_0시_0분, "")); + scheduleService.save(관리자.getId(), 공통_일정.getId(), + new ScheduleCreateRequest("공통 일정 2", 날짜_2022년_7월_10일_11시_59분, 날짜_2022년_7월_15일_16시_0분, "")); + scheduleService.save(관리자.getId(), 공통_일정.getId(), + new ScheduleCreateRequest("공통 일정 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_16시_1분, "")); + scheduleService.save(관리자.getId(), BE_일정.getId(), + new ScheduleCreateRequest("백엔드 일정 1", 날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_16일_20시_0분, "")); + scheduleService.save(관리자.getId(), BE_일정.getId(), + new ScheduleCreateRequest("백엔드 일정 2", 날짜_2022년_7월_16일_20시_0분, 날짜_2022년_7월_20일_0시_0분, "")); + scheduleService.save(관리자.getId(), BE_일정.getId(), + new ScheduleCreateRequest("백엔드 일정 3", 날짜_2022년_7월_20일_11시_59분, 날짜_2022년_7월_27일_0시_0분, "")); + scheduleService.save(관리자.getId(), FE_일정.getId(), + new ScheduleCreateRequest("프론트엔드 일정 1", 날짜_2022년_7월_27일_11시_59분, 날짜_2022년_7월_31일_0시_0분, "")); + scheduleService.save(관리자.getId(), FE_일정.getId(), + new ScheduleCreateRequest("프론트엔드 일정 2", 날짜_2022년_7월_31일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); + + /* 카테고리 구독 */ + MemberResponse 후디 = memberService.save(후디()); + MemberResponse 파랑 = memberService.save(파랑()); + MemberResponse 매트 = memberService.save(매트()); + MemberResponse 리버 = memberService.save(리버()); + + subscriptionService.save(후디.getId(), 공통_일정.getId()); + subscriptionService.save(파랑.getId(), 공통_일정.getId()); + subscriptionService.save(매트.getId(), 공통_일정.getId()); + subscriptionService.save(리버.getId(), 공통_일정.getId()); + + subscriptionService.save(매트.getId(), BE_일정.getId()); + subscriptionService.save(매트.getId(), FE_일정.getId()); + subscriptionService.save(리버.getId(), FE_일정.getId()); + + // when + List actual = calendarService.getSchedulesOfSubscriberIds( + List.of(후디.getId(), 파랑.getId(), 매트.getId(), 리버.getId()), + new DateRangeRequest("2022-07-07T16:00", "2022-08-15T14:00")); + + // then + assertThat(actual.stream().map(IntegrationSchedule::getTitle)) + .containsExactly("공통 일정 1", "공통 일정 2", "공통 일정 3", "백엔드 일정 1", "백엔드 일정 2", "백엔드 일정 3", "프론트엔드 일정 1", + "프론트엔드 일정 2"); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/composition/application/CategorySubscriptionServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/composition/application/CategorySubscriptionServiceTest.java new file mode 100644 index 00000000..975e3fee --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/composition/application/CategorySubscriptionServiceTest.java @@ -0,0 +1,58 @@ +package com.allog.dallog.domain.composition.application; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; +import static com.allog.dallog.common.fixtures.ExternalCategoryFixtures.대한민국_공휴일_생성_요청; +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; +import static org.assertj.core.api.Assertions.assertThat; + +import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.domain.member.application.MemberService; +import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.subscription.application.SubscriptionService; +import com.allog.dallog.domain.subscription.domain.Subscription; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class CategorySubscriptionServiceTest extends ServiceTest { + + @Autowired + private CategorySubscriptionService categorySubscriptionService; + + @Autowired + private MemberService memberService; + + @Autowired + private SubscriptionService subscriptionService; + + @DisplayName("카테고리 생성 시 자동으로 구독한다.") + @Test + void 카테고리_생성_시_자동으로_구독한다() { + // given + MemberResponse 파랑 = memberService.save(파랑()); + + // when + categorySubscriptionService.save(파랑.getId(), 공통_일정_생성_요청); + + List subscriptions = subscriptionService.getAllByMemberId(파랑.getId()); + + // then + assertThat(subscriptions).hasSize(1); + } + + @DisplayName("외부 카테고리 생성 시 자동으로 구독한다.") + @Test + void 외부_카테고리_생성_시_자동으로_구독한다() { + // given + MemberResponse 파랑 = memberService.save(파랑()); + + // when + categorySubscriptionService.save(파랑.getId(), 대한민국_공휴일_생성_요청); + + List subscriptions = subscriptionService.getAllByMemberId(파랑.getId()); + + // then + assertThat(subscriptions).hasSize(1); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/composition/application/RegisterServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/composition/application/RegisterServiceTest.java new file mode 100644 index 00000000..90200807 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/composition/application/RegisterServiceTest.java @@ -0,0 +1,77 @@ +package com.allog.dallog.domain.composition.application; + +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_MEMBER; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.domain.auth.dto.OAuthMember; +import com.allog.dallog.domain.category.application.CategoryService; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; +import com.allog.dallog.domain.member.application.MemberService; +import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.member.exception.NoSuchMemberException; +import com.allog.dallog.domain.subscription.application.SubscriptionService; +import com.allog.dallog.domain.subscription.domain.Subscription; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class RegisterServiceTest extends ServiceTest { + + @Autowired + private RegisterService registerService; + + @Autowired + private MemberService memberService; + + @Autowired + private CategoryService categoryService; + + @Autowired + private SubscriptionService subscriptionService; + + @DisplayName("유저 생성 시 개인 카테고리를 자동으로 생성하고 구독한다.") + @Test + void 유저_생성_시_개인_카테고리를_자동으로_생성하고_구독한다() { + // given & when + OAuthMember member = STUB_OAUTH_MEMBER(); + MemberResponse memberResponse = registerService.register(member); + + List subscriptions = subscriptionService.getAllByMemberId(memberResponse.getId()); + + // then + assertAll(() -> { + assertThat(memberResponse.getEmail()).isEqualTo(member.getEmail()); + assertThat(subscriptions).hasSize(1); + }); + } + + @DisplayName("유저 삭제 시 연관된 구독과 카테고리를 순차적으로 삭제한다.") + @Test + void 유저_삭제_시_연관된_구독과_카테고리를_순차적으로_삭제한다() { + // given + OAuthMember member = STUB_OAUTH_MEMBER(); + MemberResponse memberResponse = registerService.register(member); + Long memberId = memberResponse.getId(); + + CategoryResponse categoryResponse = categoryService.save(memberId, 공통_일정_생성_요청); + subscriptionService.save(memberId, categoryResponse.getId()); + + // when + registerService.deleteByMemberId(memberId); + + // then + assertAll(() -> { + assertThatThrownBy(() -> memberService.getMember(memberId)) + .isInstanceOf(NoSuchMemberException.class); + assertThatThrownBy(() -> categoryService.getCategory(memberId)) + .isInstanceOf(NoSuchCategoryException.class); + assertThat(subscriptionService.getAllByMemberId(memberId)).hasSize(0); + }); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/composition/application/SchedulerServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/composition/application/SchedulerServiceTest.java new file mode 100644 index 00000000..f8294715 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/composition/application/SchedulerServiceTest.java @@ -0,0 +1,186 @@ +package com.allog.dallog.domain.composition.application; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.MemberFixtures.리버; +import static com.allog.dallog.common.fixtures.MemberFixtures.매트; +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_1분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_18시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_20시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_1일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_31일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_7일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_14시_0분; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.domain.category.application.CategoryService; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.member.application.MemberService; +import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.schedule.application.ScheduleService; +import com.allog.dallog.domain.schedule.dto.request.DateRangeRequest; +import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; +import com.allog.dallog.domain.schedule.dto.response.PeriodResponse; +import com.allog.dallog.domain.subscription.application.SubscriptionService; +import com.allog.dallog.domain.subscription.domain.Color; +import com.allog.dallog.domain.subscription.dto.request.SubscriptionUpdateRequest; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class SchedulerServiceTest extends ServiceTest { + + @Autowired + private SchedulerService schedulerService; + + @Autowired + private ScheduleService scheduleService; + + @Autowired + private CategoryService categoryService; + + @Autowired + private SubscriptionService subscriptionService; + + @Autowired + private MemberService memberService; + + @DisplayName("비어있는 기간 목록을 반환한다.") + @Test + void 비어있는_기간_목록을_반환한다() { + // given + /* 관리자 및 카테고리 생성 */ + MemberResponse 관리자 = memberService.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + CategoryResponse BE_일정 = categoryService.save(관리자.getId(), BE_일정_생성_요청); + CategoryResponse FE_일정 = categoryService.save(관리자.getId(), FE_일정_생성_요청); + + /* 카테고리에 일정 추가 */ + scheduleService.save(관리자.getId(), 공통_일정.getId(), + new ScheduleCreateRequest("공통 일정 1", 날짜_2022년_7월_7일_16시_0분, 날짜_2022년_7월_10일_0시_0분, "")); + scheduleService.save(관리자.getId(), 공통_일정.getId(), + new ScheduleCreateRequest("공통 일정 2", 날짜_2022년_7월_10일_11시_59분, 날짜_2022년_7월_15일_16시_0분, "")); + scheduleService.save(관리자.getId(), 공통_일정.getId(), + new ScheduleCreateRequest("공통 일정 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_16시_1분, "")); + scheduleService.save(관리자.getId(), BE_일정.getId(), + new ScheduleCreateRequest("백엔드 일정 1", 날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_16일_20시_0분, "")); + scheduleService.save(관리자.getId(), BE_일정.getId(), + new ScheduleCreateRequest("백엔드 일정 2", 날짜_2022년_7월_16일_20시_0분, 날짜_2022년_7월_20일_0시_0분, "")); + scheduleService.save(관리자.getId(), BE_일정.getId(), + new ScheduleCreateRequest("백엔드 일정 3", 날짜_2022년_7월_20일_11시_59분, 날짜_2022년_7월_27일_0시_0분, "")); + scheduleService.save(관리자.getId(), FE_일정.getId(), + new ScheduleCreateRequest("프론트엔드 일정 1", 날짜_2022년_7월_27일_11시_59분, 날짜_2022년_7월_31일_0시_0분, "")); + scheduleService.save(관리자.getId(), FE_일정.getId(), + new ScheduleCreateRequest("프론트엔드 일정 2", 날짜_2022년_7월_31일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); + + /* 카테고리 구독 */ + MemberResponse 후디 = memberService.save(후디()); + MemberResponse 파랑 = memberService.save(파랑()); + MemberResponse 매트 = memberService.save(매트()); + MemberResponse 리버 = memberService.save(리버()); + + subscriptionService.save(후디.getId(), 공통_일정.getId()); + subscriptionService.save(파랑.getId(), 공통_일정.getId()); + subscriptionService.save(매트.getId(), 공통_일정.getId()); + subscriptionService.save(리버.getId(), 공통_일정.getId()); + + subscriptionService.save(매트.getId(), BE_일정.getId()); + subscriptionService.save(매트.getId(), FE_일정.getId()); + subscriptionService.save(리버.getId(), FE_일정.getId()); + + // when + List actual = schedulerService.getAvailablePeriods(공통_일정.getId(), + new DateRangeRequest("2022-07-01T00:00", "2022-08-31T00:00")); + + // then + assertAll(() -> { + assertThat(actual).hasSize(7); + assertThat(actual.stream().map(PeriodResponse::getStartDateTime)).containsExactly(날짜_2022년_7월_1일_0시_0분, + 날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_1분, 날짜_2022년_7월_20일_0시_0분, + 날짜_2022년_7월_27일_0시_0분, 날짜_2022년_8월_15일_14시_0분); + assertThat(actual.stream().map(PeriodResponse::getEndDateTime)).containsExactly(날짜_2022년_7월_7일_16시_0분, + 날짜_2022년_7월_10일_11시_59분, 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_20일_11시_59분, + 날짜_2022년_7월_27일_11시_59분, LocalDateTime.of(2022, 8, 31, 0, 0)); + }); + } + + @DisplayName("체크하지 않은 구독은 일정 산정 대상에서 제외된다.") + @Test + void 체크하지_않은_구독은_일정_산정_대상에서_제외된다() { + // given + /* 관리자 및 카테고리 생성 */ + MemberResponse 관리자 = memberService.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + CategoryResponse BE_일정 = categoryService.save(관리자.getId(), BE_일정_생성_요청); + CategoryResponse FE_일정 = categoryService.save(관리자.getId(), FE_일정_생성_요청); + + /* 카테고리에 일정 추가 */ + scheduleService.save(관리자.getId(), 공통_일정.getId(), + new ScheduleCreateRequest("공통 일정 1", 날짜_2022년_7월_7일_16시_0분, 날짜_2022년_7월_10일_0시_0분, "")); + scheduleService.save(관리자.getId(), 공통_일정.getId(), + new ScheduleCreateRequest("공통 일정 2", 날짜_2022년_7월_10일_11시_59분, 날짜_2022년_7월_15일_16시_0분, "")); + scheduleService.save(관리자.getId(), 공통_일정.getId(), + new ScheduleCreateRequest("공통 일정 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_16시_1분, "")); + scheduleService.save(관리자.getId(), BE_일정.getId(), + new ScheduleCreateRequest("백엔드 일정 1", 날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_16일_20시_0분, "")); + scheduleService.save(관리자.getId(), BE_일정.getId(), + new ScheduleCreateRequest("백엔드 일정 2", 날짜_2022년_7월_16일_20시_0분, 날짜_2022년_7월_20일_0시_0분, "")); + scheduleService.save(관리자.getId(), BE_일정.getId(), + new ScheduleCreateRequest("백엔드 일정 3", 날짜_2022년_7월_20일_11시_59분, 날짜_2022년_7월_27일_0시_0분, "")); + scheduleService.save(관리자.getId(), FE_일정.getId(), + new ScheduleCreateRequest("프론트엔드 일정 1", 날짜_2022년_7월_27일_11시_59분, 날짜_2022년_7월_31일_0시_0분, "")); + scheduleService.save(관리자.getId(), FE_일정.getId(), + new ScheduleCreateRequest("프론트엔드 일정 2", 날짜_2022년_7월_31일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); + + /* 카테고리 구독 */ + MemberResponse 후디 = memberService.save(후디()); + MemberResponse 파랑 = memberService.save(파랑()); + MemberResponse 매트 = memberService.save(매트()); + MemberResponse 리버 = memberService.save(리버()); + + subscriptionService.save(후디.getId(), 공통_일정.getId()); + subscriptionService.save(파랑.getId(), 공통_일정.getId()); + subscriptionService.save(매트.getId(), 공통_일정.getId()); + subscriptionService.save(리버.getId(), 공통_일정.getId()); + subscriptionService.save(매트.getId(), BE_일정.getId()); + + SubscriptionResponse 매트_FE_일정_구독 = subscriptionService.save(매트.getId(), FE_일정.getId()); + SubscriptionResponse 리버_FE_일정_구독 = subscriptionService.save(리버.getId(), FE_일정.getId()); + subscriptionService.update(매트_FE_일정_구독.getId(), 매트.getId(), + new SubscriptionUpdateRequest(Color.COLOR_1, false)); + subscriptionService.update(리버_FE_일정_구독.getId(), 리버.getId(), + new SubscriptionUpdateRequest(Color.COLOR_1, false)); + + // when + List actual = schedulerService.getAvailablePeriods(공통_일정.getId(), + new DateRangeRequest("2022-07-01T00:00", "2022-08-31T00:00")); + + // then + assertAll(() -> { + assertThat(actual).hasSize(6); + assertThat(actual.stream().map(PeriodResponse::getStartDateTime)).containsExactly(날짜_2022년_7월_1일_0시_0분, + 날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_1분, 날짜_2022년_7월_20일_0시_0분, + 날짜_2022년_7월_27일_0시_0분); + assertThat(actual.stream().map(PeriodResponse::getEndDateTime)).containsExactly(날짜_2022년_7월_7일_16시_0분, + 날짜_2022년_7월_10일_11시_59분, 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_20일_11시_59분, + LocalDateTime.of(2022, 8, 31, 0, 0)); + }); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarServiceTest.java new file mode 100644 index 00000000..6357a0a2 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/externalcalendar/application/ExternalCalendarServiceTest.java @@ -0,0 +1,40 @@ +package com.allog.dallog.domain.externalcalendar.application; + +import static com.allog.dallog.common.fixtures.OAuthTokenFixtures.OAUTH_TOKEN; +import static org.assertj.core.api.Assertions.assertThat; + +import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.common.fixtures.MemberFixtures; +import com.allog.dallog.domain.auth.domain.OAuthTokenRepository; +import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendarsResponse; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class ExternalCalendarServiceTest extends ServiceTest { + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private OAuthTokenRepository oAuthTokenRepository; + + @Autowired + private ExternalCalendarService externalCalendarService; + + @DisplayName("member id를 활용하여 외부 캘린더 리스트를 조회한다.") + @Test + void member_id를_활용하여_외부_캘린더_리스트를_조회한다() { + // given + Member 매트 = memberRepository.save(MemberFixtures.매트()); + oAuthTokenRepository.save(OAUTH_TOKEN(매트)); + + // when + ExternalCalendarsResponse actual = externalCalendarService.findByMemberId(매트.getId()); + + // then + assertThat(actual.getExternalCalendars()).hasSize(3); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDaoTest.java b/backend/src/test/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDaoTest.java new file mode 100644 index 00000000..00d341ea --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/integrationschedule/dao/IntegrationScheduleDaoTest.java @@ -0,0 +1,358 @@ +package com.allog.dallog.domain.integrationschedule.dao; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.매트_아고라; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_1분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_18시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_20시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_17일_23시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_1일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_31일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_7일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_14시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_17시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_23시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_제목; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.매고라_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.매고라_제목; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회식_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회식_제목; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_제목; +import static org.assertj.core.api.Assertions.assertThat; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.CategoryRepository; +import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import com.allog.dallog.domain.schedule.domain.Schedule; +import com.allog.dallog.domain.schedule.domain.ScheduleRepository; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class IntegrationScheduleDaoTest { + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private ScheduleRepository scheduleRepository; + + @Autowired + private CategoryRepository categoryRepository; + + @Autowired + private IntegrationScheduleDao integrationScheduleDao; + + @DisplayName("카테코리와 시작일시, 종료일시를 전달하면 그 사이에 해당하는 일정을 조회한다.") + @Test + void 카테고리와_시작일시_종료일시를_전달하면_그_사이에_해당하는_일정을_조회한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + + // when + List actual = integrationScheduleDao.findByCategoryIdInAndBetween(List.of(BE_일정.getId()), + 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_31일_0시_0분); + + // then + assertThat(actual).hasSize(1); + } + + @DisplayName("조회하기 위한 category id의 크기가 0인 경우 빈 리스트를 반환한다.") + @Test + void 조회하기_위한_category_id의_크기가_0인_경우_빈_리스트를_반환한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + + List categoryIds = Collections.emptyList(); + LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDateTime = 날짜_2022년_7월_31일_0시_0분; + + // when + List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, + startDateTime, endDateTime); + + // then + assertThat(actual).hasSize(0); + } + + @DisplayName("카테고리가 여러 개 일 때, 카테고리와 시작일시, 종료일시를 전달하면 그 사이에 해당하는 일정을 조회한다.") + @Test + void 카테고리가_여러_개_일_때_카테고리와_시작일시_종료일시를_전달하면_그_사이에_해당하는_일정을_조회한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + Category FE_일정 = categoryRepository.save(FE_일정(관리자)); + Category 매트_아고라 = categoryRepository.save(매트_아고라(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + Schedule 레벨_인터뷰 = new Schedule(FE_일정, 레벨_인터뷰_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 레벨_인터뷰_메모); + + Schedule 매고라 = new Schedule(매트_아고라, 매고라_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 매고라_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + scheduleRepository.save(레벨_인터뷰); + scheduleRepository.save(매고라); + + List categoryIds = List.of(BE_일정.getId()); + LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDateTime = 날짜_2022년_7월_31일_0시_0분; + + // when + List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, + startDateTime, endDateTime); + + // then + assertThat(actual).hasSize(1); + } + + @DisplayName("카테고리가 여러 개 일 때, 카테고리 id 리스트와 시작일시, 종료일시를 전달하면 그 사이에 해당하는 일정을 조회한다.") + @Test + void 카테고리가_여러_개_일_때_카테고리_id_리스트와_시작일시_종료일시를_전달하면_그_사이에_해당하는_일정을_조회한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + Category FE_일정 = categoryRepository.save(FE_일정(관리자)); + Category 매트_아고라 = categoryRepository.save(매트_아고라(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + Schedule 레벨_인터뷰 = new Schedule(FE_일정, 레벨_인터뷰_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 레벨_인터뷰_메모); + + Schedule 매고라 = new Schedule(매트_아고라, 매고라_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 매고라_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + scheduleRepository.save(레벨_인터뷰); + scheduleRepository.save(매고라); + + List categoryIds = List.of(BE_일정.getId(), FE_일정.getId(), 매트_아고라.getId()); + LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDateTime = 날짜_2022년_7월_31일_0시_0분; + + // when + List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, + startDateTime, endDateTime); + + // then + assertThat(actual).hasSize(3); + } + + @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 시작날짜가 종료일시와 같으면 조회한다.") + @Test + void 시작일시와_종료일시를_전달할_때_일정의_시작일시와_같으면_조회된다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + + List categoryIds = List.of(BE_일정.getId()); + LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDateTime = 날짜_2022년_7월_15일_16시_0분; + + // when + List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, + startDateTime, endDateTime); + + // then + assertThat(actual).hasSize(1); + } + + @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 시작날짜가 종료일시 이후이면 조회되지 않는다.") + @Test + void 카테고리와_시작일시_종료일시를_전달할_때_일정의_시작날짜가_종료일시_이후이면_조회되지_않는다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + + List categoryIds = List.of(BE_일정.getId()); + LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDateTime = 날짜_2022년_7월_7일_16시_0분; + + // when + List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, + startDateTime, endDateTime); + + // then + assertThat(actual).hasSize(0); + } + + @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 종료날짜가 시작일시와 같으면 조회된다.") + @Test + void 카테고리와_시작일시와_종료일시를_전달할_때_일정의_종료날짜가_시작일시와_같으면_조회된다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + + List categoryIds = List.of(BE_일정.getId()); + LocalDateTime startDateTime = 날짜_2022년_7월_16일_16시_0분; + LocalDateTime endDateTime = 날짜_2022년_7월_31일_0시_0분; + + // when + List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, + startDateTime, endDateTime); + + // then + assertThat(actual).hasSize(1); + } + + @DisplayName("카테고리와 시작일시, 종료일시를 전달할 때 일정의 종료날짜가 시작일시 이전이면 조회되지 않는다.") + @Test + void 카테고리와_시작일시와_종료일시를_전달할_때_일정의_종료날짜가_시작일시_이전이면_조회되지_않는다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + + Schedule 알록달록_회의 = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, 알록달록_회의_메모); + Schedule 알록달록_회식 = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, 알록달록_회식_메모); + + scheduleRepository.save(알록달록_회의); + scheduleRepository.save(알록달록_회식); + + List categoryIds = List.of(BE_일정.getId()); + LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDateTime = 날짜_2022년_7월_7일_16시_0분; + + // when + List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, + startDateTime, endDateTime); + + // then + assertThat(actual).hasSize(0); + } + + @DisplayName("시작일시와 종료일시로 특정 카테고리의 일정을 조회한다.") + @Test + void 시작일시와_종료일시로_특정_카테고리의_일정을_조회한다() { + // given + Member 후디 = memberRepository.save(후디()); + + Category BE_일정 = categoryRepository.save(BE_일정(후디)); + Category FE_일정 = categoryRepository.save(FE_일정(후디)); + Category 공통_일정 = categoryRepository.save(공통_일정(후디)); + + /* BE 일정 */ + scheduleRepository.save(new Schedule(BE_일정, "BE 1", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); + scheduleRepository.save(new Schedule(BE_일정, "BE 2", 날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_10일_11시_59분, "")); + scheduleRepository.save(new Schedule(BE_일정, "BE 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_20시_0분, "")); + + /* FE 일정 */ + scheduleRepository.save(new Schedule(FE_일정, "FE 1", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_31일_0시_0분, "")); + scheduleRepository.save(new Schedule(FE_일정, "FE 2", 날짜_2022년_7월_20일_0시_0분, 날짜_2022년_7월_20일_11시_59분, "")); + scheduleRepository.save(new Schedule(FE_일정, "FE 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_18시_0분, "")); + + /* 공통 일정 */ + scheduleRepository.save(new Schedule(공통_일정, "공통 1", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_16일_16시_1분, "")); + scheduleRepository.save(new Schedule(공통_일정, "공통 2", 날짜_2022년_7월_27일_0시_0분, 날짜_2022년_7월_27일_11시_59분, "")); + scheduleRepository.save(new Schedule(공통_일정, "공통 3", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_16시_1분, "")); + + List categoryIds = List.of(BE_일정.getId(), FE_일정.getId()); + LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDateTime = 날짜_2022년_8월_15일_23시_59분; + + // when + List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, + startDateTime, endDateTime); + + // then + assertThat(actual) + .extracting(IntegrationSchedule::getTitle) + .containsOnly("BE 1", "BE 2", "BE 3", "FE 1", "FE 2", "FE 3"); + } + + @DisplayName("시작일시와 종료일시로 특정 카테고리의 일정을 조회할 때 범위 밖의 일정은 제외된다.") + @Test + void 시작일시와_종료일시로_특정_카테고리의_일정을_조회할_때_범위_밖의_일정은_제외된다() { + // given + Member 후디 = memberRepository.save(후디()); + + Category BE_일정 = categoryRepository.save(BE_일정(후디)); + Category FE_일정 = categoryRepository.save(FE_일정(후디)); + + /* BE 일정 */ + scheduleRepository.save(new Schedule(BE_일정, "BE 1 포함", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_8월_15일_14시_0분, "")); + scheduleRepository.save(new Schedule(BE_일정, "BE 2 포함", 날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_10일_11시_59분, "")); + scheduleRepository.save(new Schedule(BE_일정, "BE 3 포함", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_20시_0분, "")); + scheduleRepository.save(new Schedule(BE_일정, "BE 3 미포함", 날짜_2022년_7월_31일_0시_0분, 날짜_2022년_8월_15일_17시_0분, "")); + + /* FE 일정 */ + scheduleRepository.save(new Schedule(FE_일정, "FE 1 포함", 날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_31일_0시_0분, "")); + scheduleRepository.save(new Schedule(FE_일정, "FE 2 포함", 날짜_2022년_7월_16일_16시_0분, 날짜_2022년_7월_16일_18시_0분, "")); + scheduleRepository.save(new Schedule(FE_일정, "FE 3 미포함", 날짜_2022년_7월_20일_0시_0분, 날짜_2022년_7월_20일_11시_59분, "")); + + List categoryIds = List.of(BE_일정.getId(), FE_일정.getId()); + LocalDateTime startDateTime = 날짜_2022년_7월_1일_0시_0분; + LocalDateTime endDateTime = 날짜_2022년_7월_17일_23시_59분; + + // when + List actual = integrationScheduleDao.findByCategoryIdInAndBetween(categoryIds, + startDateTime, endDateTime); + + // then + assertThat(actual).extracting(IntegrationSchedule::getTitle) + .containsOnly("BE 1 포함", "BE 2 포함", "BE 3 포함", "FE 1 포함", "FE 2 포함"); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleTest.java b/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleTest.java new file mode 100644 index 00000000..213af9a4 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationScheduleTest.java @@ -0,0 +1,131 @@ +package com.allog.dallog.domain.integrationschedule.domain; + +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_시작일시; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_제목; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_종료일시; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import com.allog.dallog.domain.category.domain.CategoryType; +import java.time.LocalDateTime; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class IntegrationScheduleTest { + + @DisplayName("일정을 생성한다.") + @Test + void 일정을_생성한다() { + // given + String id = "1"; + Long categoryId = 1L; + + // when & then + assertDoesNotThrow( + () -> new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, 알록달록_회의_시작일시, 알록달록_회의_종료일시, 알록달록_회의_메모, + CategoryType.NORMAL.name())); + } + + @DisplayName("LongTerm인지 확인 할 떄, 일정의 시작일시와 종료일시가 다르면 true를 반환한다.") + @Test + void LongTerm인지_확인_할_떄_일정의_시작일시와_종료일시가_다르면_true를_반환한다() { + // given + String id = "1"; + Long categoryId = 1L; + IntegrationSchedule integrationSchedule = new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, + LocalDateTime.of(2022, 7, 1, 0, 1), + LocalDateTime.of(2022, 7, 2, 0, 0), 알록달록_회의_메모, CategoryType.NORMAL.name()); + + // when + boolean actual = integrationSchedule.isLongTerms(); + + // then + assertThat(actual).isTrue(); + } + + @DisplayName("LongTerm인지 확인 할 떄, 일정의 시작일시와 종료일시가 같으면 false를 반환한다.") + @Test + void LongTerm인지_확인_할_때_일정의_시작일시와_종료일시가_다르면_false를_반환한다() { + // given + String id = "1"; + Long categoryId = 1L; + IntegrationSchedule integrationSchedule = new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, + LocalDateTime.of(2022, 7, 1, 0, 1), + LocalDateTime.of(2022, 7, 1, 23, 59), 알록달록_회의_메모, CategoryType.NORMAL.name()); + + // when + boolean actual = integrationSchedule.isLongTerms(); + + // then + assertThat(actual).isFalse(); + } + + @DisplayName("AllDays인지 확인 할 떄, 일정의 시작일시와 종료일시가 같고 자정이면 true를 반환한다.") + @Test + void AllDays인지_확인_할_때_일정의_시작일시와_종료일시가_같고_자정이면_true를_반환한다() { + // given + String id = "1"; + Long categoryId = 1L; + IntegrationSchedule integrationSchedule = new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, + LocalDateTime.of(2022, 7, 1, 0, 0), + LocalDateTime.of(2022, 7, 1, 23, 59), 알록달록_회의_메모, CategoryType.NORMAL.name()); + + // when + boolean actual = integrationSchedule.isAllDays(); + + // then + assertThat(actual).isTrue(); + } + + @DisplayName("AllDays인지 확인 할 떄, 일정의 시작일시와 종료일시가 같지만 자정이 아니면 false를 반환한다.") + @Test + void AllDays인지_확인_할_때_일정의_시작일시와_종료일시가_같지만_자정이_아니면_false를_반환한다() { + // given + String id = "1"; + Long categoryId = 1L; + IntegrationSchedule integrationSchedule = new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, + LocalDateTime.of(2022, 7, 1, 0, 0), + LocalDateTime.of(2022, 7, 1, 11, 58), 알록달록_회의_메모, CategoryType.NORMAL.name()); + + // when + boolean actual = integrationSchedule.isAllDays(); + + // then + assertThat(actual).isFalse(); + } + + @DisplayName("FewHours인지 확인 할 떄, 일정의 시작일시와 종료일시가 같고 자정이 아니면 true를 반환한다.") + @Test + void FewHours인지_확인_할_때_일정의_시작일시와_종료일시가_같고_자정이_아니면_true를_반환한다() { + // given + String id = "1"; + Long categoryId = 1L; + IntegrationSchedule integrationSchedule = new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, + LocalDateTime.of(2022, 7, 1, 0, 0), + LocalDateTime.of(2022, 7, 1, 11, 58), 알록달록_회의_메모, CategoryType.NORMAL.name()); + + // when + boolean actual = integrationSchedule.isFewHours(); + + // then + assertThat(actual).isTrue(); + } + + @DisplayName("FewHours인지 확인 할 떄, 일정의 시작일시와 종료일시가 같지만 자정이면 false를 반환한다.") + @Test + void FewHours인지_확인_할_때_일정의_시작일시와_종료일시가_같지만_자정이면_false를_반환한다() { + // given + String id = "1"; + Long categoryId = 1L; + IntegrationSchedule integrationSchedule = new IntegrationSchedule(id, categoryId, 알록달록_회의_제목, + LocalDateTime.of(2022, 7, 1, 0, 0), + LocalDateTime.of(2022, 7, 1, 23, 59), 알록달록_회의_메모, CategoryType.NORMAL.name()); + + // when + boolean actual = integrationSchedule.isFewHours(); + + // then + assertThat(actual).isFalse(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedulesTest.java b/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedulesTest.java new file mode 100644 index 00000000..6e0a05f1 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/IntegrationSchedulesTest.java @@ -0,0 +1,98 @@ +package com.allog.dallog.domain.integrationschedule.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.allog.dallog.domain.category.domain.CategoryType; +import java.time.LocalDateTime; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class IntegrationSchedulesTest { + + @DisplayName("겹치는 일정이 하나도 없을 때, 일정 시작일시가 빠른 순서대로 정렬된다.") + @Test + void 겹치는_일정이_하나도_없을_때_일정_시작일시가_빠른_순서대로_정렬된다() { + // given + Long categoryId = 1L; + IntegrationSchedule 첫번째로_정렬되어야_하는_일정 = new IntegrationSchedule("1", categoryId, "일정1", + LocalDateTime.of(2022, 3, 1, 0, 0), + LocalDateTime.of(2022, 3, 2, 0, 0), "일정1", CategoryType.NORMAL.name()); + + IntegrationSchedule 두번째로_정렬되어야_하는_일정 = new IntegrationSchedule("2", categoryId, "일정2", + LocalDateTime.of(2022, 3, 3, 0, 0), + LocalDateTime.of(2022, 3, 4, 0, 0), "일정2", CategoryType.NORMAL.name()); + + IntegrationSchedule 세번째로_정렬되어야_하는_일정 = new IntegrationSchedule("3", categoryId, "일정3", + LocalDateTime.of(2022, 3, 5, 0, 0), + LocalDateTime.of(2022, 3, 7, 0, 0), "일정3", CategoryType.NORMAL.name()); + + // when + IntegrationSchedules integrationSchedules = new IntegrationSchedules(); + integrationSchedules.add(세번째로_정렬되어야_하는_일정); + integrationSchedules.add(두번째로_정렬되어야_하는_일정); + integrationSchedules.add(첫번째로_정렬되어야_하는_일정); + + // then + assertThat(integrationSchedules.getSortedValues()) + .extracting(IntegrationSchedule::getTitle) + .containsExactly("일정1", "일정2", "일정3"); + } + + @DisplayName("일정의 시작일시가 겹친다면, 일정 종료일시가 느린 순서대로 정렬된다.") + @Test + void 일정의_시작일시가_겹친다면_일정_종료일시가_느린_순서대로_정렬된다() { + // given + Long categoryId = 1L; + IntegrationSchedule 첫번째로_정렬되어야_하는_일정 = new IntegrationSchedule("1", categoryId, "일정1", + LocalDateTime.of(2022, 3, 1, 0, 0), + LocalDateTime.of(2022, 3, 10, 0, 0), "일정1", CategoryType.NORMAL.name()); + + IntegrationSchedule 두번째로_정렬되어야_하는_일정 = new IntegrationSchedule("2", categoryId, "일정2", + LocalDateTime.of(2022, 3, 1, 0, 0), + LocalDateTime.of(2022, 3, 7, 0, 0), "일정2", CategoryType.NORMAL.name()); + + IntegrationSchedule 세번째로_정렬되어야_하는_일정 = new IntegrationSchedule("3", categoryId, "일정3", + LocalDateTime.of(2022, 3, 5, 0, 0), + LocalDateTime.of(2022, 3, 5, 0, 0), "일정3", CategoryType.NORMAL.name()); + + // when + IntegrationSchedules integrationSchedules = new IntegrationSchedules(); + integrationSchedules.add(두번째로_정렬되어야_하는_일정); + integrationSchedules.add(세번째로_정렬되어야_하는_일정); + integrationSchedules.add(첫번째로_정렬되어야_하는_일정); + + // then + assertThat(integrationSchedules.getSortedValues()) + .extracting(IntegrationSchedule::getTitle) + .containsExactly("일정1", "일정2", "일정3"); + } + + @DisplayName("일정의 시작일시가 겹치고, 종료일시도 겹칠때는 일정의 제목을 사전기준 오름차순으로 정렬된다.") + @Test + void 일정의_시작일시가_겹치고_종료일시도_겹칠때는_일정의_제목을_사전기준_오름차순으로_정렬된다() { + // given + Long categoryId = 1L; + IntegrationSchedule 첫번째로_정렬되어야_하는_일정 = new IntegrationSchedule("1", categoryId, "가", + LocalDateTime.of(2022, 3, 1, 0, 0), + LocalDateTime.of(2022, 3, 10, 0, 0), "일정1", CategoryType.NORMAL.name()); + + IntegrationSchedule 두번째로_정렬되어야_하는_일정 = new IntegrationSchedule("2", categoryId, "나", + LocalDateTime.of(2022, 3, 1, 0, 0), + LocalDateTime.of(2022, 3, 10, 0, 0), "일정2", CategoryType.NORMAL.name()); + + IntegrationSchedule 세번째로_정렬되어야_하는_일정 = new IntegrationSchedule("3", categoryId, "다", + LocalDateTime.of(2022, 3, 1, 0, 0), + LocalDateTime.of(2022, 3, 10, 0, 0), "일정3", CategoryType.NORMAL.name()); + + // when + IntegrationSchedules integrationSchedules = new IntegrationSchedules(); + integrationSchedules.add(세번째로_정렬되어야_하는_일정); + integrationSchedules.add(두번째로_정렬되어야_하는_일정); + integrationSchedules.add(첫번째로_정렬되어야_하는_일정); + + // then + assertThat(integrationSchedules.getSortedValues()) + .extracting(IntegrationSchedule::getTitle) + .containsExactly("가", "나", "다"); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/PeriodTest.java b/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/PeriodTest.java new file mode 100644 index 00000000..42fe1469 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/integrationschedule/domain/PeriodTest.java @@ -0,0 +1,174 @@ +package com.allog.dallog.domain.integrationschedule.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class PeriodTest { + + @DisplayName("시작일시와 종료일시의 날짜 차이를 반환한다.") + @Test + void 시작일시와_종료일시의_날짜_차이를_반환한다() { + // given + LocalDateTime startDateTime = LocalDateTime.of(2022, 1, 2, 0, 0); + LocalDateTime endDateTime = LocalDateTime.of(2022, 1, 4, 0, 0); + + Period period = new Period(startDateTime, endDateTime); + + // when + long dayDifference = period.calculateDayDifference(); + + // then + assertThat(dayDifference).isEqualTo(2); + } + + @DisplayName("시작일시와 종료일시의 시간 차이를 반환한다.") + @Test + void 시작일시와_종료일시의_시간_차이를_반환한다() { + // given + LocalDateTime startDateTime = LocalDateTime.of(2022, 1, 2, 11, 0); + LocalDateTime endDateTime = LocalDateTime.of(2022, 1, 4, 23, 0); + + Period period = new Period(startDateTime, endDateTime); + + // when + long hourDifference = period.calculateHourDifference(); + + // then + assertThat(hourDifference).isEqualTo(12); + } + + @DisplayName("시작일시와 종료일시의 분 차이를 반환한다.") + @Test + void 시작일시와_종료일시의_분_차이를_반환한다() { + // given + LocalDateTime startDateTime = LocalDateTime.of(2022, 1, 2, 11, 17); + LocalDateTime endDateTime = LocalDateTime.of(2022, 1, 4, 11, 19); + + Period period = new Period(startDateTime, endDateTime); + + // when + long minuteDifference = period.calculateMinuteDifference(); + + // then + assertThat(minuteDifference).isEqualTo(2); + } + + @DisplayName("기간 뺄셈시 상대 기간이 우측에 걸쳐있을 때의 결과를 계산한다.") + @Test + void 기간_뺄셈시_상대_기간이_우측에_걸쳐있을_때의_결과를_계산한다() { + // given + LocalDateTime baseStartDateTime = LocalDateTime.of(2022, 8, 1, 0, 0); + LocalDateTime baseEndDateTime = LocalDateTime.of(2022, 8, 2, 23, 59); + Period basePeriod = new Period(baseStartDateTime, baseEndDateTime); + + LocalDateTime otherStartDateTime = LocalDateTime.of(2022, 8, 1, 18, 0); + LocalDateTime otherEndDateTime = LocalDateTime.of(2022, 8, 3, 18, 0); + Period otherPeriod = new Period(otherStartDateTime, otherEndDateTime); + + // when + List actual = basePeriod.slice(otherPeriod); + + // then + assertAll(() -> { + assertThat(actual).hasSize(1); + assertThat(actual.get(0).getStartDateTime()).isEqualTo(baseStartDateTime); + assertThat(actual.get(0).getEndDateTime()).isEqualTo(otherStartDateTime); + }); + } + + @DisplayName("기간 뺄셈시 상대 기간이 좌측에 걸쳐있을 때의 결과를 계산한다.") + @Test + void 기간_뺄셈시_상대_기간이_좌측에_걸쳐있을_때의_결과를_계산한다() { + // given + LocalDateTime baseStartDateTime = LocalDateTime.of(2022, 8, 2, 0, 0); + LocalDateTime baseEndDateTime = LocalDateTime.of(2022, 8, 3, 23, 59); + Period basePeriod = new Period(baseStartDateTime, baseEndDateTime); + + LocalDateTime otherStartDateTime = LocalDateTime.of(2022, 8, 1, 18, 0); + LocalDateTime otherEndDateTime = LocalDateTime.of(2022, 8, 2, 18, 0); + Period otherPeriod = new Period(otherStartDateTime, otherEndDateTime); + + // when + List actual = basePeriod.slice(otherPeriod); + + // then + assertAll(() -> { + assertThat(actual).hasSize(1); + assertThat(actual.get(0).getStartDateTime()).isEqualTo(otherEndDateTime); + assertThat(actual.get(0).getEndDateTime()).isEqualTo(baseEndDateTime); + }); + } + + @DisplayName("기간 뺄셈시 상대 기간이 안쪽에 포함될때 결과를 계산한다.") + @Test + void 기간_뺄셈시_상대_기간이_안쪽에_포함될때_결과를_계산한다() { + // given + LocalDateTime baseStartDateTime = LocalDateTime.of(2022, 8, 1, 0, 0); + LocalDateTime baseEndDateTime = LocalDateTime.of(2022, 8, 3, 23, 59); + Period basePeriod = new Period(baseStartDateTime, baseEndDateTime); + + LocalDateTime otherStartDateTime = LocalDateTime.of(2022, 8, 1, 18, 0); + LocalDateTime otherEndDateTime = LocalDateTime.of(2022, 8, 2, 18, 0); + Period otherPeriod = new Period(otherStartDateTime, otherEndDateTime); + + // when + List actual = basePeriod.slice(otherPeriod); + + // then + assertAll(() -> { + assertThat(actual).hasSize(2); + assertThat(actual.get(0).getStartDateTime()).isEqualTo(baseStartDateTime); + assertThat(actual.get(0).getEndDateTime()).isEqualTo(otherStartDateTime); + assertThat(actual.get(1).getStartDateTime()).isEqualTo(otherEndDateTime); + assertThat(actual.get(1).getEndDateTime()).isEqualTo(baseEndDateTime); + }); + } + + @DisplayName("기간 뺄셈시 상대 기간과 완벽히 일치하면 빈 리스트를 반환한다.") + @Test + void 기간_뺄셈시_상대_기간과_완벽히_일치하면_빈_리스트를_반환한다() { + // given + LocalDateTime baseStartDateTime = LocalDateTime.of(2022, 8, 1, 0, 0); + LocalDateTime baseEndDateTime = LocalDateTime.of(2022, 8, 3, 23, 59); + Period basePeriod = new Period(baseStartDateTime, baseEndDateTime); + + LocalDateTime otherStartDateTime = LocalDateTime.of(2022, 8, 1, 0, 0); + LocalDateTime otherEndDateTime = LocalDateTime.of(2022, 8, 3, 23, 59); + Period otherPeriod = new Period(otherStartDateTime, otherEndDateTime); + + // when + List actual = basePeriod.slice(otherPeriod); + + // then + assertAll(() -> { + assertThat(actual).hasSize(0); + }); + } + + @DisplayName("기간 뺄셈시 상대 기간과 겹치지 않으면 자기자신을 리스트로 반환한다.") + @Test + void 기간_뺄셈시_상대_기간과_겹치지_않으면_자기자신을_리스트로_반환한다() { + // given + LocalDateTime baseStartDateTime = LocalDateTime.of(2022, 8, 1, 0, 0); + LocalDateTime baseEndDateTime = LocalDateTime.of(2022, 8, 2, 0, 0); + Period basePeriod = new Period(baseStartDateTime, baseEndDateTime); + + LocalDateTime otherStartDateTime = LocalDateTime.of(2022, 8, 3, 0, 0); + LocalDateTime otherEndDateTime = LocalDateTime.of(2022, 8, 3, 23, 59); + Period otherPeriod = new Period(otherStartDateTime, otherEndDateTime); + + // when + List actual = basePeriod.slice(otherPeriod); + + // then + assertAll(() -> { + assertThat(actual).hasSize(1); + assertThat(actual.get(0)).isEqualTo(basePeriod); + }); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/member/application/MemberServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/member/application/MemberServiceTest.java new file mode 100644 index 00000000..7dcea0a5 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/member/application/MemberServiceTest.java @@ -0,0 +1,127 @@ +package com.allog.dallog.domain.member.application; + +import static com.allog.dallog.common.fixtures.MemberFixtures.리버_이메일; +import static com.allog.dallog.common.fixtures.MemberFixtures.매트; +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑_이메일; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.member.dto.MemberUpdateRequest; +import com.allog.dallog.domain.member.exception.NoSuchMemberException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class MemberServiceTest extends ServiceTest { + + @Autowired + private MemberService memberService; + + @DisplayName("회원을 저장한다.") + @Test + void 회원을_저장한다() { + // given & when + MemberResponse 파랑 = memberService.save(파랑()); + + // then + assertThat(파랑).isNotNull(); + } + + @DisplayName("이메일로 회원을 찾는다.") + @Test + void 이메일로_회원을_찾는다() { + // given + MemberResponse 파랑 = memberService.save(파랑()); + + // when + Member actual = memberService.getByEmail(파랑_이메일); + + // then + assertThat(actual.getId()).isEqualTo(파랑.getId()); + } + + @DisplayName("id를 통해 회원을 단건 조회한다.") + @Test + void id를_통해_회원을_단건_조회한다() { + // given + MemberResponse 파랑 = memberService.save(파랑()); + + // when & then + assertThat(memberService.findById(파랑.getId())) + .usingRecursiveComparison() + .isEqualTo(파랑); + } + + @DisplayName("회원의 이름을 수정한다.") + @Test + void 회원의_이름을_수정한다() { + // given + MemberResponse 매트 = memberService.save(매트()); + + String 패트_이름 = "패트"; + MemberUpdateRequest 매트_수정_요청 = new MemberUpdateRequest(패트_이름); + + // when + memberService.update(매트.getId(), 매트_수정_요청); + + // then + MemberResponse actual = memberService.findById(매트.getId()); + assertThat(actual.getDisplayName()).isEqualTo(패트_이름); + } + + @DisplayName("회원을 제거한다.") + @Test + void 회원을_제거한다() { + // given + MemberResponse 후디 = memberService.save(후디()); + + // when + memberService.deleteById(후디.getId()); + + // then + assertThatThrownBy(() -> memberService.findById(후디.getId())) + .isInstanceOf(NoSuchMemberException.class); + } + + @DisplayName("주어진 이메일로 가입된 회원이 있으면 true를 반환한다.") + @Test + void 주어진_이메일로_가입된_회원이_있으면_true를_반환한다() { + // given + memberService.save(파랑()); + + // when + boolean actual = memberService.existsByEmail(파랑_이메일); + + // then + assertThat(actual).isTrue(); + } + + @DisplayName("주어진 이메일로 가입된 회원이 없으면 false를 반환한다.") + @Test + void 주어진_이메일로_가입된_회원이_없으면_false를_반환한다() { + // given + memberService.save(파랑()); + + // when + boolean actual = memberService.existsByEmail(리버_이메일); + + // then + assertThat(actual).isFalse(); + } + + @DisplayName("회원이 존재하지 않으면 예외를 던진다.") + @Test + void 회원이_존재하지_않으면_예외를_던진다() { + // given + Long id = 0L; + + // when & then + assertThatThrownBy(() -> memberService.validateExistsMember(id)) + .isInstanceOf(NoSuchMemberException.class); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/member/domain/MemberRepositoryTest.java b/backend/src/test/java/com/allog/dallog/domain/member/domain/MemberRepositoryTest.java new file mode 100644 index 00000000..e63ffef3 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/member/domain/MemberRepositoryTest.java @@ -0,0 +1,47 @@ +package com.allog.dallog.domain.member.domain; + +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑_이메일; +import static org.assertj.core.api.Assertions.assertThat; + +import com.allog.dallog.common.annotation.RepositoryTest; +import com.allog.dallog.domain.category.domain.CategoryRepository; +import com.allog.dallog.domain.subscription.domain.SubscriptionRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class MemberRepositoryTest extends RepositoryTest { + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private CategoryRepository categoryRepository; + + @Autowired + private SubscriptionRepository subscriptionRepository; + + @DisplayName("이메일을 통해 회원을 찾는다.") + @Test + void 이메일을_통해_회원을_찾는다() { + // given + Member 파랑 = memberRepository.save(파랑()); + + // when + Member actual = memberRepository.findByEmail(파랑_이메일).get(); + + // then + assertThat(actual.getId()).isEqualTo(파랑.getId()); + } + + @DisplayName("중복된 이메일이 존재하는 경우 true를 반환한다.") + @Test + void 중복된_이메일이_존재하는_경우_true를_반환한다() { + // given + memberRepository.save(파랑()); + + // when & then + assertThat(memberRepository.existsByEmail(파랑_이메일)).isTrue(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/member/domain/MemberTest.java b/backend/src/test/java/com/allog/dallog/domain/member/domain/MemberTest.java new file mode 100644 index 00000000..60f8d0ea --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/member/domain/MemberTest.java @@ -0,0 +1,69 @@ +package com.allog.dallog.domain.member.domain; + +import static com.allog.dallog.common.fixtures.MemberFixtures.매트; +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑_이름; +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑_이메일; +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑_프로필; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import com.allog.dallog.domain.member.exception.InvalidMemberException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class MemberTest { + + @DisplayName("회원을 생성한다.") + @Test + void 회원을_생성한다() { + // given & when & then + assertDoesNotThrow(() -> new Member(파랑_이메일, 파랑_이름, 파랑_프로필, SocialType.GOOGLE)); + } + + @DisplayName("회원의 email이 형식이 맞지 않으면 예외를 던진다.") + @ParameterizedTest + @ValueSource(strings = {"dev.hyeonic@", "dev.hyeonicgmail.com", "dev.hyeonic@gmail", "@gmail.com", "dev.hyeonic"}) + void 회원의_email이_형식이_맞지_않으면_예외를_던진다(final String email) { + // given & when & then + assertThatThrownBy(() -> new Member(email, 파랑_이름, 파랑_프로필, SocialType.GOOGLE)) + .isInstanceOf(InvalidMemberException.class); + } + + @DisplayName("회원의 이름이 1 ~ 10 사이가 아닌 경우 예외를 던진다.") + @ParameterizedTest + @ValueSource(strings = {"", "일이삼사오육칠팔구십일"}) + void 회원의_이름이_1_에서_10_사이가_아닌_경우_예외를_던진다(final String displayName) { + // given & when & then + assertThatThrownBy(() -> new Member(파랑_이메일, displayName, 파랑_프로필, SocialType.GOOGLE)) + .isInstanceOf(InvalidMemberException.class); + } + + @DisplayName("회원의 이름을 변경한다.") + @Test + void 회원의_이름을_변경한다() { + // given + Member member = 매트(); + String 패트_이름 = "패트"; + + // when + member.change(패트_이름); + + // then + assertThat(member.getDisplayName()).isEqualTo(패트_이름); + } + + @DisplayName("변경하기 위한 회원의 이름이 1 ~ 10 사이가 아닌 경우 예외를 던진다.") + @ParameterizedTest + @ValueSource(strings = {"", "일이삼사오육칠팔구십일"}) + void 변경하기_위한_회원의_이름이_1_에서_10_사이가_아닌_경우_예외를_던진다(final String displayName) { + // given + Member member = 매트(); + + // when & then + assertThatThrownBy(() -> member.change(displayName)) + .isInstanceOf(InvalidMemberException.class); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/schedule/application/ScheduleServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/schedule/application/ScheduleServiceTest.java new file mode 100644 index 00000000..ec9653dc --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/schedule/application/ScheduleServiceTest.java @@ -0,0 +1,292 @@ +package com.allog.dallog.domain.schedule.application; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.ExternalCategoryFixtures.대한민국_공휴일_생성_요청; +import static com.allog.dallog.common.fixtures.MemberFixtures.리버; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_1일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_31일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_시작일시; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_제목; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_종료일시; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_생성_요청; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_시작일시; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_제목; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_종료일시; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.domain.auth.exception.NoPermissionException; +import com.allog.dallog.domain.category.application.CategoryService; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; +import com.allog.dallog.domain.member.application.MemberService; +import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; +import com.allog.dallog.domain.schedule.dto.request.ScheduleUpdateRequest; +import com.allog.dallog.domain.schedule.dto.response.ScheduleResponse; +import com.allog.dallog.domain.schedule.exception.InvalidScheduleException; +import com.allog.dallog.domain.schedule.exception.NoSuchScheduleException; +import java.time.LocalDateTime; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class ScheduleServiceTest extends ServiceTest { + + @Autowired + private ScheduleService scheduleService; + + @Autowired + private CategoryService categoryService; + + @Autowired + private MemberService memberService; + + @DisplayName("새로운 일정을 생성한다.") + @Test + void 새로운_일정을_생성한다() { + // given & when + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + ScheduleResponse 알록달록_회의 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + + // then + assertThat(알록달록_회의.getTitle()).isEqualTo(알록달록_회의_제목); + } + + @DisplayName("새로운 일정을 생성 할 떄 일정 제목의 길이가 50을 초과하는 경우 예외를 던진다.") + @Test + void 새로운_일정을_생성_할_때_일정_제목의_길이가_50을_초과하는_경우_예외를_던진다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + + String 잘못된_일정_제목 = "일이삼사오육칠팔구십일이삼사오육칠팔구십일일이삼사오육칠팔구십일이삼사오육칠팔구십일일이삼사오육칠팔구십일"; + ScheduleCreateRequest 잘못된_일정_생성_요청 = new ScheduleCreateRequest(잘못된_일정_제목, 알록달록_회의_시작일시, 알록달록_회의_종료일시, + 알록달록_회의_메모); + + // when & then + assertThatThrownBy(() -> scheduleService.save(후디.getId(), BE_일정.getId(), 잘못된_일정_생성_요청)). + isInstanceOf(InvalidScheduleException.class); + } + + @DisplayName("새로운 일정을 생성 할 떄 일정 메모의 길이가 255를 초과하는 경우 예외를 던진다.") + @Test + void 새로운_일정을_생성_할_때_일정_메모의_길이가_255를_초과하는_경우_예외를_던진다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + + String 잘못된_일정_메모 = "1".repeat(256); + ScheduleCreateRequest 잘못된_일정_생성_요청 = new ScheduleCreateRequest(알록달록_회의_제목, 알록달록_회의_시작일시, 알록달록_회의_종료일시, + 잘못된_일정_메모); + + // when & then + assertThatThrownBy(() -> scheduleService.save(후디.getId(), BE_일정.getId(), 잘못된_일정_생성_요청)). + isInstanceOf(InvalidScheduleException.class); + } + + @DisplayName("새로운 일정을 생성 할 떄 종료일시가 시작일시 이전이라면 예외를 던진다.") + @Test + void 새로운_일정을_생성_할_때_종료일시가_시작일시_이전이라면_예외를_던진다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + + LocalDateTime 시작일시 = 날짜_2022년_7월_15일_16시_0분; + LocalDateTime 종료일시 = 날짜_2022년_7월_1일_0시_0분; + ScheduleCreateRequest 일정_생성_요청 = new ScheduleCreateRequest(알록달록_회의_제목, 시작일시, 종료일시, 알록달록_회의_메모); + + // when & then + assertThatThrownBy(() -> scheduleService.save(후디.getId(), BE_일정.getId(), 일정_생성_요청)). + isInstanceOf(InvalidScheduleException.class); + } + + @DisplayName("일정 생성 요청자가 카테고리의 생성자가 아닌경우 예외를 던진다") + @Test + void 일정_생성_요청자가_카테고리의_생성자가_아닌경우_예외를_던진다() { + // given + MemberResponse 리버 = memberService.save(리버()); + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + + LocalDateTime 시작일시 = 날짜_2022년_7월_15일_16시_0분; + LocalDateTime 종료일시 = 날짜_2022년_7월_31일_0시_0분; + ScheduleCreateRequest 일정_생성_요청 = new ScheduleCreateRequest(알록달록_회의_제목, 시작일시, 종료일시, 알록달록_회의_메모); + + // when & then + assertThatThrownBy(() -> scheduleService.save(리버.getId(), BE_일정.getId(), 일정_생성_요청)). + isInstanceOf(NoPermissionException.class); + } + + @DisplayName("일정 생성시 전달한 카테고리가 존재하지 않는다면 예외를 던진다.") + @Test + void 일정_생성시_전달한_카테고리가_존재하지_않는다면_예외를_던진다() { + // given + MemberResponse 후디 = memberService.save(후디()); + + LocalDateTime 시작일시 = 날짜_2022년_7월_15일_16시_0분; + LocalDateTime 종료일시 = 날짜_2022년_7월_31일_0시_0분; + ScheduleCreateRequest 일정_생성_요청 = new ScheduleCreateRequest(알록달록_회의_제목, 시작일시, 종료일시, 알록달록_회의_메모); + + // when & then + assertThatThrownBy(() -> scheduleService.save(후디.getId(), 0L, 일정_생성_요청)). + isInstanceOf(NoSuchCategoryException.class); + } + + @DisplayName("일정 생성시 전달한 카테고리가 외부 연동 카테고리라면 예외를 던진다.") + @Test + void 일정_생성시_전달한_카테고리가_외부_연동_카테고리라면_예외를_던진다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse 대한민국_공휴일 = categoryService.save(후디.getId(), 대한민국_공휴일_생성_요청); + + LocalDateTime 시작일시 = 날짜_2022년_7월_15일_16시_0분; + LocalDateTime 종료일시 = 날짜_2022년_7월_31일_0시_0분; + ScheduleCreateRequest 일정_생성_요청 = new ScheduleCreateRequest(알록달록_회의_제목, 시작일시, 종료일시, 알록달록_회의_메모); + + // when & then + assertThatThrownBy(() -> scheduleService.save(후디.getId(), 대한민국_공휴일.getId(), 일정_생성_요청)). + isInstanceOf(NoPermissionException.class); + } + + @DisplayName("일정의 ID로 단건 일정을 조회한다.") + @Test + void 일정의_ID로_단건_일정을_조회한다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + ScheduleResponse 알록달록_회의 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + + // when + ScheduleResponse response = scheduleService.findById(알록달록_회의.getId()); + + // then + assertAll(() -> { + assertThat(response.getId()).isEqualTo(알록달록_회의.getId()); + assertThat(response.getTitle()).isEqualTo(알록달록_회의_제목); + assertThat(response.getStartDateTime()).isEqualTo(알록달록_회의_시작일시); + assertThat(response.getEndDateTime()).isEqualTo(알록달록_회의_종료일시); + assertThat(response.getMemo()).isEqualTo(알록달록_회의_메모); + }); + } + + @DisplayName("존재하지 않는 일정 ID로 단건 일정을 조회하면 예외를 던진다.") + @Test + void 존재하지_않는_일정_ID로_단건_일정을_조회하면_예외를_던진다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + Long 잘못된_아이디 = 0L; + + // when & then + assertThatThrownBy(() -> scheduleService.findById(잘못된_아이디)); + } + + @DisplayName("일정을 수정한다.") + @Test + void 일정을_수정한다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + ScheduleResponse 기존_일정 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + + ScheduleUpdateRequest 일정_수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + + // when + scheduleService.update(기존_일정.getId(), 후디.getId(), 일정_수정_요청); + + // then + ScheduleResponse actual = scheduleService.findById(기존_일정.getId()); + assertAll( + () -> { + assertThat(actual.getId()).isEqualTo(기존_일정.getId()); + assertThat(actual.getTitle()).isEqualTo(레벨_인터뷰_제목); + assertThat(actual.getStartDateTime()).isEqualTo(레벨_인터뷰_시작일시); + assertThat(actual.getEndDateTime()).isEqualTo(레벨_인터뷰_종료일시); + assertThat(actual.getMemo()).isEqualTo(레벨_인터뷰_메모); + } + ); + } + + @DisplayName("일정 수정 시 일정의 카테고리에 대한 권한이 없을 경우 예외가 발생한다.") + @Test + void 일정_수정_시_일정의_카테고리에_대한_권한이_없을_경우_예외가_발생한다() { + // given + MemberResponse 리버 = memberService.save(리버()); + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + ScheduleResponse 기존_일정 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + + ScheduleUpdateRequest 일정_수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + + // when & then + assertThatThrownBy(() -> scheduleService.update(기존_일정.getId(), 리버.getId(), 일정_수정_요청)) + .isInstanceOf(NoPermissionException.class); + } + + @DisplayName("일정 수정 시 존재하지 않은 일정일 경우 예외가 발생한다.") + @Test + void 일정_수정_시_존재하지_않은_일정일_경우_예외가_발생한다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + ScheduleResponse 기존_일정 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + + ScheduleUpdateRequest 일정_수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + + // when & then + assertThatThrownBy(() -> scheduleService.update(기존_일정.getId() + 1, 후디.getId(), 일정_수정_요청)) + .isInstanceOf(NoSuchScheduleException.class); + } + + @DisplayName("일정을 삭제한다.") + @Test + void 일정을_삭제한다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + ScheduleResponse 알록달록_회의 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + + // when + scheduleService.deleteById(알록달록_회의.getId(), 후디.getId()); + + // then + assertThatThrownBy(() -> scheduleService.findById(알록달록_회의.getId())) + .isInstanceOf(NoSuchScheduleException.class); + } + + @DisplayName("일정 삭제 시 일정의 카테고리에 대한 권한이 없을 경우 예외가 발생한다.") + @Test + void 일정_삭제_시_일정의_카테고리에_대한_권한이_없을_경우_예외가_발생한다() { + // given + MemberResponse 리버 = memberService.save(리버()); + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + ScheduleResponse 알록달록_회의 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + + // when & then + assertThatThrownBy(() -> scheduleService.deleteById(알록달록_회의.getId(), 리버.getId())) + .isInstanceOf(NoPermissionException.class); + } + + @DisplayName("일정 삭제 시 존재하지 않은 일정일 경우 예외가 발생한다.") + @Test + void 일정_삭제_시_존재하지_않은_일정일_경우_예외가_발생한다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + ScheduleResponse 알록달록_회의 = scheduleService.save(후디.getId(), BE_일정.getId(), 알록달록_회의_생성_요청); + + // when & then + assertThatThrownBy(() -> scheduleService.deleteById(알록달록_회의.getId() + 1, 후디.getId())) + .isInstanceOf(NoSuchScheduleException.class); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/schedule/domain/ScheduleRepositoryTest.java b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/ScheduleRepositoryTest.java new file mode 100644 index 00000000..3ac9064a --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/ScheduleRepositoryTest.java @@ -0,0 +1,78 @@ +package com.allog.dallog.domain.schedule.domain; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_14시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_17시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회식_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회식_제목; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_제목; +import static org.assertj.core.api.Assertions.assertThat; + +import com.allog.dallog.common.annotation.RepositoryTest; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.CategoryRepository; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class ScheduleRepositoryTest extends RepositoryTest { + + @Autowired + private ScheduleRepository scheduleRepository; + + @Autowired + private CategoryRepository categoryRepository; + + @Autowired + private MemberRepository memberRepository; + + @DisplayName("특정 카테고리들에 속한 일정을 전부 삭제한다") + @Test + void 특정_카테고리들에_속한_일정을_전부_삭제한다() { + // given + Member 관리자 = 관리자(); + memberRepository.save(관리자); + + Category BE_일정 = BE_일정(관리자); + Category FE_일정 = FE_일정(관리자); + Category 공통_일정 = 공통_일정(관리자); + categoryRepository.save(BE_일정); + categoryRepository.save(FE_일정); + categoryRepository.save(공통_일정); + + Schedule 알록달록_회의_BE = new Schedule(BE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, + 알록달록_회의_메모); + Schedule 알록달록_회식_BE = new Schedule(BE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, + 알록달록_회식_메모); + Schedule 알록달록_회의_FE = new Schedule(FE_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, + 알록달록_회의_메모); + Schedule 알록달록_회식_FE = new Schedule(FE_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, + 알록달록_회식_메모); + Schedule 알록달록_회의_공통 = new Schedule(공통_일정, 알록달록_회의_제목, 날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분, + 알록달록_회의_메모); + Schedule 알록달록_회식_공통 = new Schedule(공통_일정, 알록달록_회식_제목, 날짜_2022년_8월_15일_14시_0분, 날짜_2022년_8월_15일_17시_0분, + 알록달록_회식_메모); + + scheduleRepository.save(알록달록_회의_BE); + scheduleRepository.save(알록달록_회식_BE); + scheduleRepository.save(알록달록_회의_FE); + scheduleRepository.save(알록달록_회식_FE); + scheduleRepository.save(알록달록_회의_공통); + scheduleRepository.save(알록달록_회식_공통); + + // when + scheduleRepository.deleteByCategoryIdIn(List.of(BE_일정.getId(), FE_일정.getId(), 공통_일정.getId())); + + // then + assertThat(scheduleRepository.findAll()).hasSize(0); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/schedule/domain/ScheduleTest.java b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/ScheduleTest.java new file mode 100644 index 00000000..2b456c5d --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/ScheduleTest.java @@ -0,0 +1,55 @@ +package com.allog.dallog.domain.schedule.domain; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_시작일시; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_제목; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_종료일시; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.schedule.exception.InvalidScheduleException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class ScheduleTest { + + @DisplayName("일정을 생성한다.") + @Test + void 일정을_생성한다() { + // given + Category BE_일정_카테고리 = BE_일정(관리자()); + + // when & then + assertDoesNotThrow(() -> new Schedule(BE_일정_카테고리, 알록달록_회의_제목, 알록달록_회의_시작일시, 알록달록_회의_종료일시, 알록달록_회의_메모)); + } + + @DisplayName("일정 제목의 길이가 50을 초과하는 경우 예외를 던진다.") + @ParameterizedTest + @ValueSource(strings = {"일이삼사오육칠팔구십일이삼사오육칠팔구십일일이삼사오육칠팔구십일이삼사오육칠팔구십일일이삼사오육칠팔구십일", + "알록달록 알록달록 알록달록 알록달록 알록달록 알록달록 알록달록 알록달록 알록달록 알록달록 알록달록 회의"}) + void 일정_제목의_길이가_50을_초과하는_경우_예외를_던진다(final String 잘못된_일정_제목) { + //given + Category BE_일정_카테고리 = BE_일정(관리자()); + + // when & then + assertThatThrownBy(() -> new Schedule(BE_일정_카테고리, 잘못된_일정_제목, 알록달록_회의_시작일시, 알록달록_회의_종료일시, 알록달록_회의_메모)) + .isInstanceOf(InvalidScheduleException.class); + } + + @DisplayName("일정 메모의 길이가 255를 초과하는 경우 예외를 던진다.") + @Test + void 일정_메모의_길이가_255를_초과하는_경우_예외를_던진다() { + // given + String 잘못된_메모 = "1".repeat(256); + Category BE_일정_카테고리 = BE_일정(관리자()); + + // when & then + assertThatThrownBy(() -> new Schedule(BE_일정_카테고리, 알록달록_회의_제목, 알록달록_회의_시작일시, 알록달록_회의_종료일시, 잘못된_메모)) + .isInstanceOf(InvalidScheduleException.class); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/schedule/domain/scheduler/SchedulerTest.java b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/scheduler/SchedulerTest.java new file mode 100644 index 00000000..d25c0648 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/schedule/domain/scheduler/SchedulerTest.java @@ -0,0 +1,81 @@ +package com.allog.dallog.domain.schedule.domain.scheduler; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_1분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_18시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_20시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_1일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_31일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_7일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_8월_15일_14시_0분; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import com.allog.dallog.domain.integrationschedule.domain.Period; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class SchedulerTest { + + @DisplayName("겹치지 않는 기간을 계산한다.") + @Test + void 겹치지_않는_기간을_계산한다() { + // given + /* 사람들의 일정 목록 */ + Category 공통_일정 = 공통_일정(관리자()); + String 일정_제목 = "일정 제목"; + String 일정_메모 = "일정 메모"; + + IntegrationSchedule 일정1 = new IntegrationSchedule("1", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_7일_16시_0분, + 날짜_2022년_7월_10일_0시_0분, 일정_메모, "NORMAL"); + IntegrationSchedule 일정2 = new IntegrationSchedule("2", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_10일_11시_59분, + 날짜_2022년_7월_15일_16시_0분, 일정_메모, "NORMAL"); + IntegrationSchedule 일정3 = new IntegrationSchedule("3", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_16일_16시_0분, + 날짜_2022년_7월_16일_16시_1분, 일정_메모, "NORMAL"); + IntegrationSchedule 일정4 = new IntegrationSchedule("4", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_16일_18시_0분, + 날짜_2022년_7월_16일_20시_0분, 일정_메모, "NORMAL"); + IntegrationSchedule 일정5 = new IntegrationSchedule("5", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_16일_20시_0분, + 날짜_2022년_7월_20일_0시_0분, 일정_메모, "NORMAL"); + IntegrationSchedule 일정6 = new IntegrationSchedule("6", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_20일_11시_59분, + 날짜_2022년_7월_27일_0시_0분, 일정_메모, "NORMAL"); + IntegrationSchedule 일정7 = new IntegrationSchedule("7", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_27일_11시_59분, + 날짜_2022년_7월_31일_0시_0분, 일정_메모, "NORMAL"); + IntegrationSchedule 일정8 = new IntegrationSchedule("8", 공통_일정.getId(), 일정_제목, 날짜_2022년_7월_31일_0시_0분, + 날짜_2022년_8월_15일_14시_0분, 일정_메모, "NORMAL"); + + List 일정_목록 = List.of(일정1, 일정2, 일정3, 일정4, 일정5, 일정6, 일정7, 일정8); + + // when + LocalDateTime startDateTime = LocalDateTime.of(2022, 7, 1, 0, 0); + LocalDateTime endDateTime = LocalDateTime.of(2022, 8, 31, 0, 0); + Scheduler scheduler = new Scheduler(일정_목록, startDateTime, endDateTime); + List actual = scheduler.getPeriods(); + + // then + assertAll(() -> { + assertThat(actual).hasSize(7); + assertThat(actual).containsExactly( + new Period(날짜_2022년_7월_1일_0시_0분, 날짜_2022년_7월_7일_16시_0분), + new Period(날짜_2022년_7월_10일_0시_0분, 날짜_2022년_7월_10일_11시_59분), + new Period(날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분), + new Period(날짜_2022년_7월_16일_16시_1분, 날짜_2022년_7월_16일_18시_0분), + new Period(날짜_2022년_7월_20일_0시_0분, 날짜_2022년_7월_20일_11시_59분), + new Period(날짜_2022년_7월_27일_0시_0분, 날짜_2022년_7월_27일_11시_59분), + new Period(날짜_2022년_8월_15일_14시_0분, endDateTime) + ); + }); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/subscription/application/SubscriptionServiceTest.java b/backend/src/test/java/com/allog/dallog/domain/subscription/application/SubscriptionServiceTest.java new file mode 100644 index 00000000..d0d815fc --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/subscription/application/SubscriptionServiceTest.java @@ -0,0 +1,222 @@ +package com.allog.dallog.domain.subscription.application; + + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_이름; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.내_일정_생성_요청; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.MemberFixtures.매트; +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + +import com.allog.dallog.common.annotation.ServiceTest; +import com.allog.dallog.domain.auth.exception.NoPermissionException; +import com.allog.dallog.domain.category.application.CategoryService; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.member.application.MemberService; +import com.allog.dallog.domain.member.dto.MemberResponse; +import com.allog.dallog.domain.subscription.domain.Color; +import com.allog.dallog.domain.subscription.dto.request.SubscriptionUpdateRequest; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionsResponse; +import com.allog.dallog.domain.subscription.exception.ExistSubscriptionException; +import com.allog.dallog.domain.subscription.exception.InvalidSubscriptionException; +import com.allog.dallog.domain.subscription.exception.NoSuchSubscriptionException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; + +class SubscriptionServiceTest extends ServiceTest { + + @Autowired + private MemberService memberService; + + @Autowired + private CategoryService categoryService; + + @Autowired + private SubscriptionService subscriptionService; + + @DisplayName("새로운 구독을 생성한다.") + @Test + void 새로운_구독을_생성한다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + + // when + SubscriptionResponse response = subscriptionService.save(후디.getId(), BE_일정.getId()); + + // then + assertThat(response.getCategory().getName()).isEqualTo(BE_일정_이름); + } + + @DisplayName("자신이 생성하지 않은 개인 카테고리를 구독시 예외가 발생한다.") + @Test + void 자신이_생성하지_않은_개인_카테고리를_구독시_예외가_발생한다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse 후디_개인_학습_일정 = categoryService.save(후디.getId(), 내_일정_생성_요청); + + MemberResponse 매트 = memberService.save(매트()); + + // when & then + assertThatThrownBy(() -> subscriptionService.save(매트.getId(), 후디_개인_학습_일정.getId())) + .isInstanceOf(NoPermissionException.class) + .hasMessage("구독 권한이 없는 카테고리입니다."); + } + + @DisplayName("이미 존재하는 구독 정보를 저장할 경우 예외를 던진다.") + @Test + void 이미_존재하는_구독_정보를_저장할_경우_예외를_던진다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + subscriptionService.save(후디.getId(), BE_일정.getId()); + + // when & then + assertThatThrownBy(() -> subscriptionService.save(후디.getId(), BE_일정.getId())) + .isInstanceOf(ExistSubscriptionException.class); + } + + @DisplayName("구독 id를 기반으로 단건 조회한다.") + @Test + void 구독_id를_기반으로_단건_조회한다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + SubscriptionResponse 빨간색_구독 = subscriptionService.save(후디.getId(), BE_일정.getId()); + + // when + SubscriptionResponse foundResponse = subscriptionService.findById(빨간색_구독.getId()); + + // then + assertAll(() -> { + assertThat(foundResponse.getId()).isEqualTo(빨간색_구독.getId()); + assertThat(foundResponse.getCategory().getId()).isEqualTo(BE_일정.getId()); + }); + } + + @DisplayName("존재하지 않는 구독 정보인 경우 예외를 던진다.") + @Test + void 존재하지_않는_구독_정보인_경우_예외를_던진다() { + // given & when & then + assertThatThrownBy(() -> subscriptionService.findById(0L)) + .isInstanceOf(NoSuchSubscriptionException.class); + } + + @DisplayName("회원 정보를 기반으로 구독 정보를 조회한다.") + @Test + void 회원_정보를_기반으로_구독_정보를_조회한다() { + // given + MemberResponse 관리자 = memberService.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + CategoryResponse BE_일정 = categoryService.save(관리자.getId(), BE_일정_생성_요청); + CategoryResponse FE_일정 = categoryService.save(관리자.getId(), FE_일정_생성_요청); + + MemberResponse 후디 = memberService.save(후디()); + subscriptionService.save(후디.getId(), 공통_일정.getId()); + subscriptionService.save(후디.getId(), BE_일정.getId()); + subscriptionService.save(후디.getId(), FE_일정.getId()); + + // when + SubscriptionsResponse subscriptionsResponse = subscriptionService.findByMemberId(후디.getId()); + + // then + assertThat(subscriptionsResponse.getSubscriptions()).hasSize(3); + } + + @DisplayName("구독 정보를 수정한다.") + @Test + void 구독_정보를_수정한다() { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + SubscriptionResponse response = subscriptionService.save(후디.getId(), BE_일정.getId()); + Color color = Color.COLOR_1; + + // when + SubscriptionUpdateRequest request = new SubscriptionUpdateRequest(color, true); + subscriptionService.update(response.getId(), 후디.getId(), request); + + // then + assertAll(() -> { + assertThat(request.getColor()).isEqualTo(color); + assertThat(request.isChecked()).isTrue(); + }); + } + + @DisplayName("구독 정보 수정 시 존재하지 않는 색상인 경우 예외를 던진다.") + @ParameterizedTest + @ValueSource(strings = {"#111", "#1111", "#11111", "123456", "#**1234", "##12345", "334172#", "#00FF00"}) + void 구독_정보_수정_시_존재하지_않는_색상인_경우_예외를_던진다(final String colorCode) { + // given + MemberResponse 후디 = memberService.save(후디()); + CategoryResponse BE_일정 = categoryService.save(후디.getId(), BE_일정_생성_요청); + SubscriptionResponse response = subscriptionService.save(후디.getId(), BE_일정.getId()); + + // when + SubscriptionUpdateRequest request = new SubscriptionUpdateRequest(colorCode, true); + + // then + assertThatThrownBy(() -> subscriptionService.update(response.getId(), 후디.getId(), request)) + .isInstanceOf(InvalidSubscriptionException.class); + } + + @DisplayName("구독 정보를 삭제한다.") + @Test + void 구독_정보를_삭제한다() { + // given + MemberResponse 관리자 = memberService.save(관리자()); + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + CategoryResponse BE_일정 = categoryService.save(관리자.getId(), BE_일정_생성_요청); + CategoryResponse FE_일정 = categoryService.save(관리자.getId(), FE_일정_생성_요청); + + MemberResponse 후디 = memberService.save(후디()); + SubscriptionResponse response = subscriptionService.save(후디.getId(), 공통_일정.getId()); + subscriptionService.save(후디.getId(), BE_일정.getId()); + subscriptionService.save(후디.getId(), FE_일정.getId()); + + // when + subscriptionService.deleteById(response.getId(), 후디.getId()); + + // then + assertThat(subscriptionService.findByMemberId(후디.getId()).getSubscriptions()).hasSize(2); + } + + @DisplayName("자신의 구독 정보가 아닌 구독을 삭제할 경우 예외를 던진다.") + @Test + void 자신의_구독_정보가_아닌_구독을_삭제할_경우_예외를_던진다() { + // given + MemberResponse 관리자 = memberService.save(관리자()); + MemberResponse 파랑 = memberService.save(파랑()); + + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + SubscriptionResponse 공통_일정_구독 = subscriptionService.save(파랑.getId(), 공통_일정.getId()); + + // when & then + assertThatThrownBy(() -> subscriptionService.deleteById(공통_일정_구독.getId(), 관리자.getId())) + .isInstanceOf(NoPermissionException.class); + } + + @DisplayName("자신이 만든 카테고리에 대한 구독을 삭제할 경우 예외를 던진다") + @Test + void 자신이_만든_카테고리에_대한_구독을_삭제할_경우_예외를_던진다() { + // given + MemberResponse 관리자 = memberService.save(관리자()); + + CategoryResponse 공통_일정 = categoryService.save(관리자.getId(), 공통_일정_생성_요청); + SubscriptionResponse 공통_일정_구독 = subscriptionService.save(관리자.getId(), 공통_일정.getId()); + + // when & then + assertThatThrownBy(() -> subscriptionService.deleteById(공통_일정_구독.getId(), 관리자.getId())) + .isInstanceOf(NoPermissionException.class); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/subscription/domain/ColorTest.java b/backend/src/test/java/com/allog/dallog/domain/subscription/domain/ColorTest.java new file mode 100644 index 00000000..cf6aeae8 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/subscription/domain/ColorTest.java @@ -0,0 +1,53 @@ +package com.allog.dallog.domain.subscription.domain; + + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import com.allog.dallog.domain.subscription.exception.InvalidSubscriptionException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; + +class ColorTest { + + @DisplayName("랜덤으로 색상을 가져온다.") + @Test + void 랜덤으로_색상을_가져온다() { + // given + ColorPickerStrategy testStrategy = () -> 0; + + // when & then + assertThat(Color.pickAny(testStrategy)).isEqualTo(Color.COLOR_1); + } + + @DisplayName("color code에 맞는 색상을 가져온다.") + @ParameterizedTest + @EnumSource + void color_code에_맞는_색상을_가져온다(final Color color) { + // given & when & then + assertThat(Color.from(color.getColorCode())).isEqualTo(color); + } + + @DisplayName("소문자로 들어온 color code도 가져온다.") + @ParameterizedTest + @EnumSource + void 소문자로_들어온_color_code도_가져온다(final Color color) { + // given + String lowerColorCode = color.getColorCode().toLowerCase(); + + // when & then + assertThat(Color.from(lowerColorCode)).isEqualTo(color); + } + + @DisplayName("존재하지 않는 color code인 경우 예외가 발생한다.") + @ParameterizedTest + @ValueSource(strings = {"#asdfe", "#adfqwerse"}) + void 존재하지_않는_color_code인_경우_예외가_발생한다(final String colorCode) { + // given & when & then + assertThatThrownBy(() -> Color.from(colorCode)) + .isInstanceOf(InvalidSubscriptionException.class); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepositoryTest.java b/backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepositoryTest.java new file mode 100644 index 00000000..2061e292 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionRepositoryTest.java @@ -0,0 +1,165 @@ +package com.allog.dallog.domain.subscription.domain; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.MemberFixtures.매트; +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static com.allog.dallog.common.fixtures.SubscriptionFixtures.색상1_구독; +import static com.allog.dallog.common.fixtures.SubscriptionFixtures.색상2_구독; +import static com.allog.dallog.common.fixtures.SubscriptionFixtures.색상3_구독; +import static org.assertj.core.api.Assertions.assertThat; + +import com.allog.dallog.common.annotation.RepositoryTest; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.domain.CategoryRepository; +import com.allog.dallog.domain.member.domain.Member; +import com.allog.dallog.domain.member.domain.MemberRepository; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +class SubscriptionRepositoryTest extends RepositoryTest { + + @Autowired + private MemberRepository memberRepository; + + @Autowired + private CategoryRepository categoryRepository; + + @Autowired + private SubscriptionRepository subscriptionRepository; + + @DisplayName("존재하지 않는 카테고리를 확인할 경우 true를 반환한다.") + @Test + void 존재하지_않는_카테고리를_확인할_경우_true를_반환한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + Category 공통_일정 = categoryRepository.save(공통_일정(관리자)); + + Member 매트 = memberRepository.save(매트()); + + // when + boolean actual = subscriptionRepository.existsByMemberIdAndCategoryId(매트.getId(), 공통_일정.getId()); + + // then + assertThat(actual).isFalse(); + } + + @DisplayName("이미 존재하는 카테고리를 확인할 경우 true를 반환한다.") + @Test + void 이미_존재하는_카테고리를_확인할_경우_true를_반환한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + Category 공통_일정 = categoryRepository.save(공통_일정(관리자)); + + Member 매트 = memberRepository.save(매트()); + subscriptionRepository.save(색상1_구독(매트, 공통_일정)); + + // when + boolean actual = subscriptionRepository.existsByMemberIdAndCategoryId(매트.getId(), 공통_일정.getId()); + + // then + assertThat(actual).isTrue(); + } + + @DisplayName("회원 정보를 기반으로 구독 정보를 조회한다.") + @Test + void 회원_정보를_기반으로_구독_정보를_조회한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + Category 공통_일정 = categoryRepository.save(공통_일정(관리자)); + Category BE_일정 = categoryRepository.save(BE_일정(관리자)); + Category FE_일정 = categoryRepository.save(FE_일정(관리자)); + + Member 후디 = memberRepository.save(후디()); + subscriptionRepository.save(색상1_구독(후디, 공통_일정)); + subscriptionRepository.save(색상2_구독(후디, BE_일정)); + subscriptionRepository.save(색상3_구독(후디, FE_일정)); + + // when + List subscriptions = subscriptionRepository.findByMemberId(후디.getId()); + + // then + assertThat(subscriptions).hasSize(3); + } + + @DisplayName("회원의 구독 정보가 존재하지 않는 경우 빈 리스트가 조회된다.") + @Test + void 회원의_구독_정보가_존재하지_않는_경우_빈_리스트가_조회된다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + // when + List subscriptions = subscriptionRepository.findByMemberId(관리자.getId()); + + // then + assertThat(subscriptions).isEmpty(); + } + + @DisplayName("회원의 특정 구독 정보 여부를 확인한다.") + @Test + void 회원의_특정_구독_정보_여부를_확인한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + Category 공통_일정 = categoryRepository.save(공통_일정(관리자)); + + Member 후디 = memberRepository.save(후디()); + Subscription 색상1_구독 = 색상1_구독(후디, 공통_일정); + subscriptionRepository.save(색상1_구독); + + // when + boolean actual = subscriptionRepository.existsByIdAndMemberId(색상1_구독.getId(), 후디.getId()); + + // then + assertThat(actual).isTrue(); + } + + @DisplayName("회원의 존재하지 않는 구독 정보 여부를 확인한다.") + @Test + void 회원의_존재하지_않는_구독_정보_여부를_확인한다() { + // given + Member 관리자 = memberRepository.save(관리자()); + + // when + boolean actual = subscriptionRepository.existsByIdAndMemberId(0L, 관리자.getId()); + + // then + assertThat(actual).isFalse(); + } + + @DisplayName("특정 카테고리들에 속한 구독을 전부 삭제한다") + @Test + void 특정_카테고리들에_속한_구독을_전부_삭제한다() { + // given + Member 관리자 = 관리자(); + memberRepository.save(관리자); + Member 파랑 = 파랑(); + memberRepository.save(파랑); + + Category BE_일정 = BE_일정(관리자); + Category FE_일정 = FE_일정(관리자); + Category 공통_일정 = 공통_일정(관리자); + categoryRepository.save(BE_일정); + categoryRepository.save(FE_일정); + categoryRepository.save(공통_일정); + + subscriptionRepository.save(색상1_구독(관리자, BE_일정)); + subscriptionRepository.save(색상2_구독(관리자, FE_일정)); + subscriptionRepository.save(색상3_구독(관리자, 공통_일정)); + subscriptionRepository.save(색상1_구독(파랑, BE_일정)); + subscriptionRepository.save(색상2_구독(파랑, FE_일정)); + subscriptionRepository.save(색상3_구독(파랑, 공통_일정)); + + // when + subscriptionRepository.deleteByCategoryIdIn(List.of( + BE_일정.getId(), FE_일정.getId(), 공통_일정.getId() + )); + + // then + assertThat(subscriptionRepository.findAll()).hasSize(0); + } +} diff --git a/backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionTest.java b/backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionTest.java new file mode 100644 index 00000000..5045321d --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/domain/subscription/domain/SubscriptionTest.java @@ -0,0 +1,74 @@ +package com.allog.dallog.domain.subscription.domain; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.매트_아고라; +import static com.allog.dallog.common.fixtures.CategoryFixtures.후디_JPA_스터디; +import static com.allog.dallog.common.fixtures.MemberFixtures.매트; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static com.allog.dallog.common.fixtures.SubscriptionFixtures.색상1_구독; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.member.domain.Member; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class SubscriptionTest { + + @DisplayName("구독을 생성한다.") + @Test + void 구독을_생성한다() { + // given + Member 후디 = 후디(); + Category 후디_JPA_스터디 = 후디_JPA_스터디(후디); + Color color = Color.COLOR_1; + + // when & then + assertDoesNotThrow(() -> new Subscription(후디, 후디_JPA_스터디, color)); + } + + @DisplayName("구독이 생성되면 기본적으로 체크된다.") + @Test + void 구독이_생성되면_기본적으로_체크된다() { + // given + Member 매트 = 매트(); + Category 매트_아고라 = 매트_아고라(매트); + Color color = Color.COLOR_1; + + // when + Subscription actual = new Subscription(매트, 매트_아고라, color); + + // then + assertThat(actual.isChecked()).isTrue(); + } + + @DisplayName("구독의 색 정보를 수정한다.") + @Test + void 구독의_색_정보를_수정한다() { + // given + Member 매트 = 매트(); + Category 매트_아고라 = 매트_아고라(매트); + + // when + Subscription actual = 색상1_구독(매트, 매트_아고라); + actual.change(Color.COLOR_1, actual.isChecked()); + + // then + assertThat(actual.getColor()).isEqualTo(Color.COLOR_1); + } + + @DisplayName("구독의 체크 유무를 수정한다.") + @Test + void 구독의_체크_유무를_수정한다() { + // given + Member 매트 = 매트(); + Category 매트_아고라 = 매트_아고라(매트); + + // when + Subscription actual = 색상1_구독(매트, 매트_아고라); + actual.change(actual.getColor(), !actual.isChecked()); + + // then + assertThat(actual.isChecked()).isFalse(); + } +} diff --git a/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubExternalCalendarClient.java b/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubExternalCalendarClient.java new file mode 100644 index 00000000..d26f7013 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubExternalCalendarClient.java @@ -0,0 +1,29 @@ +package com.allog.dallog.infrastructure.oauth.client; + +import static com.allog.dallog.common.fixtures.ExternalCalendarFixtures.내_일정; +import static com.allog.dallog.common.fixtures.ExternalCalendarFixtures.대한민국_공휴일; +import static com.allog.dallog.common.fixtures.ExternalCalendarFixtures.우아한테크코스; +import static com.allog.dallog.common.fixtures.IntegrationScheduleFixtures.레벨3_방학; +import static com.allog.dallog.common.fixtures.IntegrationScheduleFixtures.포수타; + +import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarClient; +import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendar; +import com.allog.dallog.domain.integrationschedule.domain.IntegrationSchedule; +import java.util.List; + +public class StubExternalCalendarClient implements ExternalCalendarClient { + + @Override + public List getExternalCalendars(final String accessToken) { + return List.of(대한민국_공휴일, 우아한테크코스, 내_일정); + } + + @Override + public List getExternalCalendarSchedules(final String accessToken, + final Long internalCategoryId, + final String externalCalendarId, + final String startDateTime, + final String endDateTime) { + return List.of(포수타, 레벨3_방학); + } +} diff --git a/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubOAuthClient.java b/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubOAuthClient.java new file mode 100644 index 00000000..0b8654a5 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/infrastructure/oauth/client/StubOAuthClient.java @@ -0,0 +1,30 @@ +package com.allog.dallog.infrastructure.oauth.client; + +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_MEMBER_인증_코드; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_ACCESS_TOKEN; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_CREATOR; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_EXPIRES_IN; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_MEMBER; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_SCOPE; +import static com.allog.dallog.common.fixtures.AuthFixtures.STUB_OAUTH_TOKEN_TYPE; + +import com.allog.dallog.domain.auth.application.OAuthClient; +import com.allog.dallog.domain.auth.dto.OAuthMember; +import com.allog.dallog.domain.auth.dto.response.OAuthAccessTokenResponse; + +public class StubOAuthClient implements OAuthClient { + + @Override + public OAuthMember getOAuthMember(final String code, final String redirectUri) { + if (code.equals(STUB_MEMBER_인증_코드)) { + return STUB_OAUTH_MEMBER(); + } + return STUB_OAUTH_CREATOR(); + } + + @Override + public OAuthAccessTokenResponse getAccessToken(final String refreshToken) { + return new OAuthAccessTokenResponse(STUB_OAUTH_ACCESS_TOKEN, STUB_OAUTH_EXPIRES_IN, STUB_OAUTH_SCOPE, + STUB_OAUTH_TOKEN_TYPE); + } +} diff --git a/backend/src/test/java/com/allog/dallog/presentation/AuthControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/AuthControllerTest.java new file mode 100644 index 00000000..c253c6ee --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/presentation/AuthControllerTest.java @@ -0,0 +1,120 @@ +package com.allog.dallog.presentation; + +import static com.allog.dallog.common.fixtures.AuthFixtures.MEMBER_인증_코드_토큰_요청; +import static com.allog.dallog.common.fixtures.AuthFixtures.MEMBER_인증_코드_토큰_응답; +import static com.allog.dallog.common.fixtures.AuthFixtures.OAUTH_PROVIDER; +import static com.allog.dallog.common.fixtures.AuthFixtures.OAuth_로그인_링크; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.allog.dallog.domain.auth.application.AuthService; +import com.allog.dallog.infrastructure.oauth.exception.OAuthException; +import com.allog.dallog.presentation.auth.AuthController; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +@WebMvcTest(AuthController.class) +class AuthControllerTest extends ControllerTest { + + @MockBean + private AuthService authService; + + @DisplayName("OAuth 소셜 로그인을 위한 링크와 상태코드 200을 반환한다.") + @Test + void OAuth_소셜_로그인을_위한_링크와_상태코드_200을_반환한다() throws Exception { + // given + given(authService.generateGoogleLink(any())).willReturn(OAuth_로그인_링크); + + // when & then + mockMvc.perform(get("/api/auth/{oauthProvider}/oauth-uri?redirectUri={redirectUri}", OAUTH_PROVIDER, + "https://dallog.me/oauth")) + .andDo(print()) + .andDo(document("auth/link", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("oauthProvider").description("OAuth 로그인 제공자") + ), + requestParameters( + parameterWithName("redirectUri").description("OAuth Redirect URI") + ), + responseFields( + fieldWithPath("oAuthUri").type(JsonFieldType.STRING).description("OAuth 소셜 로그인 링크") + ) + )) + .andExpect(status().isOk()); + } + + @DisplayName("OAuth 로그인을 하면 token과 상태코드 200을 반환한다.") + @Test + void OAuth_로그인을_하면_token과_상태코드_200을_반환한다() throws Exception { + // given +// TokenRequest tokenRequest = new TokenRequest(STUB_MEMBER_인증_코드, "https://dallog.me/oauth"); + given(authService.generateToken(any())).willReturn(MEMBER_인증_코드_토큰_응답()); + + // when & then + mockMvc.perform(post("/api/auth/{oauthProvider}/token", OAUTH_PROVIDER) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(MEMBER_인증_코드_토큰_요청()))) + .andDo(print()) + .andDo(document("auth/token", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("oauthProvider").description("OAuth 로그인 제공자") + ), + requestFields( + fieldWithPath("code").type(JsonFieldType.STRING).description("OAuth 로그인 인증 코드"), + fieldWithPath("redirectUri").type(JsonFieldType.STRING) + .description("OAuth Redirect URI") + ) + )) + .andExpect(status().isOk()); + } + + @DisplayName("OAuth 로그인 과정에서 Resource Server 에러가 발생하면 상태코드 500을 반환한다.") + @Test + void OAuth_로그인_과정에서_Resource_Server_에러가_발생하면_상태코드_500을_반환한다() throws Exception { + // given + given(authService.generateToken(any())).willThrow(new OAuthException()); + + // when & then + mockMvc.perform(post("/api/auth/{oauthProvider}/token", OAUTH_PROVIDER) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(MEMBER_인증_코드_토큰_요청()))) + .andDo(print()) + .andDo(document("auth/exception/token", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("oauthProvider").description("OAuth 로그인 제공자") + ), + requestFields( + fieldWithPath("code").type(JsonFieldType.STRING).description("OAuth 로그인 인증 코드"), + fieldWithPath("redirectUri").type(JsonFieldType.STRING) + .description("OAuth Redirect URI") + ) + )) + .andExpect(status().isInternalServerError()); + } +} diff --git a/backend/src/test/java/com/allog/dallog/presentation/CategoryControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/CategoryControllerTest.java new file mode 100644 index 00000000..780bf34f --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/presentation/CategoryControllerTest.java @@ -0,0 +1,438 @@ +package com.allog.dallog.presentation; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_생성_요청; +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_응답; +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_이름; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정; +import static com.allog.dallog.common.fixtures.CategoryFixtures.매트_아고라; +import static com.allog.dallog.common.fixtures.CategoryFixtures.후디_JPA_스터디; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자; +import static com.allog.dallog.common.fixtures.MemberFixtures.매트; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디_응답; +import static com.allog.dallog.domain.category.domain.CategoryType.NORMAL; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.willDoNothing; +import static org.mockito.BDDMockito.willThrow; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.allog.dallog.domain.auth.application.AuthService; +import com.allog.dallog.domain.category.application.CategoryService; +import com.allog.dallog.domain.category.domain.Category; +import com.allog.dallog.domain.category.dto.request.CategoryCreateRequest; +import com.allog.dallog.domain.category.dto.request.CategoryUpdateRequest; +import com.allog.dallog.domain.category.dto.response.CategoriesResponse; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.category.exception.InvalidCategoryException; +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; +import com.allog.dallog.domain.composition.application.CategorySubscriptionService; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; + +@WebMvcTest(CategoryController.class) +class CategoryControllerTest extends ControllerTest { + + private static final String AUTHORIZATION_HEADER_NAME = "Authorization"; + private static final String AUTHORIZATION_HEADER_VALUE = "Bearer aaaaaaaa.bbbbbbbb.cccccccc"; + private static final String INVALID_CATEGORY_NAME = "20글자를 초과하는 유효하지 않은 카테고리 이름"; + private static final String CATEGORY_NAME_OVER_LENGTH_EXCEPTION_MESSAGE = "카테고리 이름의 길이는 20을 초과할 수 없습니다."; + + @MockBean + private AuthService authService; + + @MockBean + private CategoryService categoryService; + + @MockBean + private CategorySubscriptionService categorySubscriptionService; + + @DisplayName("카테고리를 생성한다.") + @Test + void 카테고리를_생성한다() throws Exception { + // given + CategoryResponse 카테고리 = BE_일정_응답(후디_응답); + given(categorySubscriptionService.save(any(), any(CategoryCreateRequest.class))).willReturn(카테고리); + + // when & then + mockMvc.perform(post("/api/categories") + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(BE_일정_생성_요청)) + ) + .andDo(print()) + .andDo(document("categories/save", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰")) + ) + ) + .andExpect(status().isCreated()); + } + + @DisplayName("잘못된 이름 형식으로 카테고리를 생성하면 400 Bad Request가 발생한다.") + @Test + void 잘못된_이름_형식으로_카테고리를_생성하면_400_Bad_Request가_발생한다() throws Exception { + // given + CategoryCreateRequest 잘못된_카테고리_생성_요청 = new CategoryCreateRequest(INVALID_CATEGORY_NAME, NORMAL); + + willThrow(new InvalidCategoryException(CATEGORY_NAME_OVER_LENGTH_EXCEPTION_MESSAGE)) + .given(categorySubscriptionService) + .save(any(), any(CategoryCreateRequest.class)); + + // when & then + mockMvc.perform(post("/api/categories") + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(잘못된_카테고리_생성_요청)) + ) + .andDo(print()) + .andDo(document("categories/save/badRequest", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰")) + ) + ) + .andExpect(status().isBadRequest()); + } + + @DisplayName("생성된 카테고리를 전부 조회한다.") + @Test + void 생성된_카테고리를_전부_조회한다() throws Exception { + // given + int page = 0; + int size = 10; + + List 일정_목록 = List.of(공통_일정(관리자()), BE_일정(관리자()), FE_일정(관리자()), 후디_JPA_스터디(후디()), 매트_아고라(매트())); + CategoriesResponse categoriesResponse = new CategoriesResponse(page, 일정_목록); + given(categoryService.findNormalByName(any(), any())).willReturn(categoriesResponse); + + // when & then + mockMvc.perform(get("/api/categories?page={page}&size={size}", page, size) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + ) + .andDo(print()) + .andDo(document("categories/findAll", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestParameters( + parameterWithName("page").description("페이지 번호"), + parameterWithName("size").description("페이지 크기") + ) + ) + ) + .andExpect(status().isOk()); + } + + @DisplayName("카테고리 제목을 활용하여 조회한다.") + @Test + void 카테고리_제목을_활용하여_조회한다() throws Exception { + // given + int page = 0; + int size = 10; + + List 일정_목록 = List.of(공통_일정(관리자()), BE_일정(관리자()), FE_일정(관리자())); + CategoriesResponse categoriesResponse = new CategoriesResponse(page, 일정_목록); + given(categoryService.findNormalByName(any(), any())).willReturn(categoriesResponse); + + // when & then + mockMvc.perform(get("/api/categories?name={name}&page={page}&size={size}", "E", page, size) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + ) + .andDo(print()) + .andDo(document("categories/findAllLikeName", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestParameters( + parameterWithName("name").description("카테고리 검색어"), + parameterWithName("page").description("페이지 번호"), + parameterWithName("size").description("페이지 크기") + ) + ) + ) + .andExpect(status().isOk()); + } + + @DisplayName("내 카테고리를 전부 조회한다.") + @Test + void 내_카테고리를_전부_조회한다() throws Exception { + // given + int page = 0; + int size = 10; + + List 일정_목록 = List.of(공통_일정(관리자()), BE_일정(관리자()), FE_일정(관리자())); + CategoriesResponse categoriesResponse = new CategoriesResponse(page, 일정_목록); + given(categoryService.findMineByName(any(), any(), any())).willReturn(categoriesResponse); + + // when & then + mockMvc.perform(get("/api/categories/me?name={name}&page={page}&size={size}", "", page, size) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + ) + .andDo(print()) + .andDo(document("categories/findMine", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestParameters( + parameterWithName("name").description("카테고리 검색어"), + parameterWithName("page").description("페이지 번호"), + parameterWithName("size").description("페이지 크기") + ) + ) + ) + .andExpect(status().isOk()); + } + + @DisplayName("내 카테고리를 제목을 활용하여 조회한다.") + @Test + void 내_카테고리를_제목을_활용하여_조회한다() throws Exception { + // given + int page = 0; + int size = 10; + + List 일정_목록 = List.of(공통_일정(관리자()), BE_일정(관리자()), FE_일정(관리자())); + CategoriesResponse categoriesResponse = new CategoriesResponse(page, 일정_목록); + given(categoryService.findMineByName(any(), any(), any())).willReturn(categoriesResponse); + + // when & then + mockMvc.perform(get("/api/categories/me?name={name}&page={page}&size={size}", "E", page, size) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + ) + .andDo(print()) + .andDo(document("categories/findMineLikeName", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestParameters( + parameterWithName("name").description("카테고리 검색어"), + parameterWithName("page").description("페이지 번호"), + parameterWithName("size").description("페이지 크기") + ) + ) + ) + .andExpect(status().isOk()); + } + + @DisplayName("카테고리 ID로 카테고리를 단건 조회한다.") + @Test + void 카테고리_ID로_카테고리를_단건_조회한다() throws Exception { + // given + Long categoryId = 1L; + CategoryResponse BE_일정_응답 = BE_일정_응답(후디_응답); + given(categoryService.findById(any())).willReturn(BE_일정_응답); + + // when & then + mockMvc.perform(RestDocumentationRequestBuilders.get("/api/categories/{categoryId}", categoryId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + ) + .andDo(print()) + .andDo(document("categories/findById", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("categoryId").description("카테고리 ID") + ) + ) + ) + .andExpect(status().isOk()); + } + + @DisplayName("카테고리 ID로 카테고리를 단건 조회시 존재하지 않으면 404 Not Found가 발생한다.") + @Test + void 카테고리_ID로_카테고리를_단건_조회시_존재하지_않으면_404_Not_Found를_반환한다() throws Exception { + // given + Long categoryId = 1L; + given(categoryService.findById(any())) + .willThrow(new NoSuchCategoryException()); + + // when & then + mockMvc.perform(RestDocumentationRequestBuilders.get("/api/categories/{categoryId}", categoryId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + ) + .andDo(print()) + .andDo(document("categories/findById/notFound", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("categoryId").description("카테고리 ID") + ) + ) + ) + .andExpect(status().isNotFound()); + } + + @DisplayName("카테고리를 수정한다.") + @Test + void 카테고리를_수정한다() throws Exception { + // given + Long categoryId = 1L; + willDoNothing() + .given(categoryService) + .update(any(), any(), any()); + CategoryUpdateRequest 카테고리_수정_요청 = new CategoryUpdateRequest(BE_일정_이름); + + // when & then + mockMvc.perform(patch("/api/categories/{categoryId}", categoryId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .content(objectMapper.writeValueAsString(카테고리_수정_요청)) + ) + .andDo(print()) + .andDo(document("categories/update", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("categoryId").description("카테고리 ID") + ) + ) + ) + .andExpect(status().isNoContent()); + } + + @DisplayName("카테고리 수정 시 존재하지 않으면 404 Not Found가 발생한다.") + @Test + void 카테고리_수정_시_존재하지_않으면_404_Not_Found를_반환한다() throws Exception { + // given + Long categoryId = 1L; + willThrow(NoSuchCategoryException.class) + .willDoNothing() + .given(categoryService) + .update(any(), any(), any()); + CategoryUpdateRequest 카테고리_수정_요청 = new CategoryUpdateRequest(BE_일정_이름); + + // when & then + mockMvc.perform(patch("/api/categories/{categoryId}", categoryId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .content(objectMapper.writeValueAsString(카테고리_수정_요청)) + ) + .andDo(print()) + .andDo(document("categories/update/notFound", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("categoryId").description("카테고리 ID") + ) + ) + ) + .andExpect(status().isNotFound()); + } + + @DisplayName("잘못된 이름 형식으로 카테고리를 수정하면 400 Bad Request가 발생한다.") + @Test + void 잘못된_이름_형식으로_카테고리를_수정하면_400_Bad_Request가_발생한다() throws Exception { + // given + Long categoryId = 1L; + willThrow(new InvalidCategoryException(CATEGORY_NAME_OVER_LENGTH_EXCEPTION_MESSAGE)) + .willDoNothing() + .given(categoryService) + .update(any(), any(), any()); + CategoryUpdateRequest 카테고리_수정_요청 = new CategoryUpdateRequest(INVALID_CATEGORY_NAME); + + // when & then + mockMvc.perform(patch("/api/categories/{categoryId}", categoryId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .content(objectMapper.writeValueAsString(카테고리_수정_요청)) + ) + .andDo(print()) + .andDo(document("categories/update/badRequest", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("categoryId").description("카테고리 ID") + ) + ) + ) + .andExpect(status().isBadRequest()); + } + + @DisplayName("카테고리를 제거한다.") + @Test + void 카테고리를_제거한다() throws Exception { + // given + Long categoryId = 1L; + willDoNothing() + .given(categoryService) + .deleteById(any(), any()); + + // when & then + mockMvc.perform(delete("/api/categories/{categoryId}", categoryId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + ) + .andDo(print()) + .andDo(document("categories/delete", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("categoryId").description("카테고리 ID") + ) + ) + ) + .andExpect(status().isNoContent()); + } + + @DisplayName("카테고리 제거 시 존재하지 않으면 404 Not Found가 발생한다") + @Test + void 카테고리_제거_시_존재하지_않으면_404_Not_Found가_발생한다() throws Exception { + // given + Long categoryId = 1L; + willThrow(new NoSuchCategoryException("존재하지 않는 카테고리를 삭제할 수 없습니다.")) + .willDoNothing() + .given(categoryService) + .deleteById(any(), any()); + + // when & then + mockMvc.perform(delete("/api/categories/{categoryId}", categoryId) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + ) + .andDo(print()) + .andDo(document("categories/delete/notFound", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("categoryId").description("카테고리 ID") + ) + ) + ) + .andExpect(status().isNotFound()); + } +} diff --git a/backend/src/test/java/com/allog/dallog/presentation/ControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/ControllerTest.java new file mode 100644 index 00000000..fac37efe --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/presentation/ControllerTest.java @@ -0,0 +1,19 @@ +package com.allog.dallog.presentation; + +import com.allog.dallog.common.config.ExternalApiConfig; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.context.annotation.Import; +import org.springframework.test.web.servlet.MockMvc; + +@AutoConfigureRestDocs +@Import(ExternalApiConfig.class) +abstract class ControllerTest { + + @Autowired + protected MockMvc mockMvc; + + @Autowired + protected ObjectMapper objectMapper; +} diff --git a/backend/src/test/java/com/allog/dallog/presentation/ExternalCalendarControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/ExternalCalendarControllerTest.java new file mode 100644 index 00000000..14fde50a --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/presentation/ExternalCalendarControllerTest.java @@ -0,0 +1,134 @@ +package com.allog.dallog.presentation; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_응답; +import static com.allog.dallog.common.fixtures.ExternalCalendarFixtures.대한민국_공휴일; +import static com.allog.dallog.common.fixtures.ExternalCalendarFixtures.우아한테크코스; +import static com.allog.dallog.common.fixtures.ExternalCategoryFixtures.우아한테크코스_생성_요청; +import static com.allog.dallog.common.fixtures.MemberFixtures.후디_응답; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.willThrow; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.allog.dallog.domain.auth.application.AuthService; +import com.allog.dallog.domain.category.dto.request.ExternalCategoryCreateRequest; +import com.allog.dallog.domain.category.exception.DuplicatedExternalCategoryException; +import com.allog.dallog.domain.composition.application.CategorySubscriptionService; +import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarService; +import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendar; +import com.allog.dallog.domain.externalcalendar.dto.ExternalCalendarsResponse; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +@WebMvcTest(ExternalCalendarController.class) +class ExternalCalendarControllerTest extends ControllerTest { + + private static final String AUTHORIZATION_HEADER_NAME = "Authorization"; + private static final String AUTHORIZATION_HEADER_VALUE = "Bearer aaaaaaaa.bbbbbbbb.cccccccc"; + + @MockBean + private AuthService authService; + + @MockBean + private ExternalCalendarService externalCalendarService; + + @MockBean + private CategorySubscriptionService categorySubscriptionService; + + @DisplayName("외부 캘린더의 일정을 조회하면 상태코드 200을 반환한다.") + @Test + void 외부_캘린더의_일정을_조회하면_상태코드_200을_반환한다() throws Exception { + // given + List ExternalCalendars = List.of(대한민국_공휴일, 우아한테크코스, 대한민국_공휴일); + given(externalCalendarService.findByMemberId(any())).willReturn( + new ExternalCalendarsResponse(ExternalCalendars)); + + // when & then + mockMvc.perform(get("/api/external-calendars/me") + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON) + ) + .andDo(print()) + .andDo(document("external-calendars/get", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ) + )) + .andExpect(status().isOk()); + } + + @DisplayName("외부 캘린더를 카테고리로 저장하면 상태코드 201을 반환한다.") + @Test + void 외부_캘린더를_카테고리로_저장하면_상태코드_201을_반환한다() throws Exception { + // given + given(categorySubscriptionService.save(any(), any(ExternalCategoryCreateRequest.class))).willReturn( + 공통_일정_응답(후디_응답)); + + // when & then + mockMvc.perform(post("/api/external-calendars/me") + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(우아한테크코스_생성_요청)) + ) + .andDo(print()) + .andDo(document("external-calendars/save", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ), + requestFields( + fieldWithPath("externalId").type(JsonFieldType.STRING).description("외부 캘린더 id"), + fieldWithPath("name").type(JsonFieldType.STRING).description("캘린더 이름") + ))) + .andExpect(status().isCreated()); + } + + @DisplayName("외부 캘린더를 카테고리로 저장하면 상태코드 201을 반환한다.") + @Test + void 외부_캘린더를_중복하여_저장하면_상태코드_400을_반환한다() throws Exception { + // given + willThrow(new DuplicatedExternalCategoryException()) + .given(categorySubscriptionService) + .save(any(), any(ExternalCategoryCreateRequest.class)); + + // when & then + mockMvc.perform(post("/api/external-calendars/me") + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(우아한테크코스_생성_요청)) + ) + .andDo(print()) + .andDo(document("external-calendars/duplicated-save", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ), + requestFields( + fieldWithPath("externalId").type(JsonFieldType.STRING).description("외부 캘린더 id"), + fieldWithPath("name").type(JsonFieldType.STRING).description("캘린더 이름") + ))) + .andExpect(status().isBadRequest()); + } +} diff --git a/backend/src/test/java/com/allog/dallog/presentation/MemberControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/MemberControllerTest.java new file mode 100644 index 00000000..d060262d --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/presentation/MemberControllerTest.java @@ -0,0 +1,148 @@ +package com.allog.dallog.presentation; + +import static com.allog.dallog.common.fixtures.AuthFixtures.더미_엑세스_토큰; +import static com.allog.dallog.common.fixtures.AuthFixtures.토큰_정보; +import static com.allog.dallog.common.fixtures.MemberFixtures.파랑_응답; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.willDoNothing; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.allog.dallog.domain.auth.application.AuthService; +import com.allog.dallog.domain.composition.application.RegisterService; +import com.allog.dallog.domain.member.application.MemberService; +import com.allog.dallog.domain.member.dto.MemberUpdateRequest; +import com.allog.dallog.domain.member.exception.NoSuchMemberException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +@WebMvcTest(MemberController.class) +class MemberControllerTest extends ControllerTest { + + private static final String AUTHORIZATION_HEADER_NAME = "Authorization"; + private static final String AUTHORIZATION_HEADER_VALUE = "Bearer aaaaaaaa.bbbbbbbb.cccccccc"; + + @MockBean + private AuthService authService; + + @MockBean + private MemberService memberService; + + @MockBean + private RegisterService registerService; + + @DisplayName("자신의 회원 정보를 조회한다.") + @Test + void 자신의_회원_정보를_조회한다() throws Exception { + //given + given(memberService.findById(파랑_응답.getId())).willReturn(파랑_응답); + given(authService.extractMemberId(더미_엑세스_토큰)).willReturn(파랑_응답.getId()); + + // when & then + mockMvc.perform(get("/api/members/me") + .header(AUTHORIZATION_HEADER_NAME, 토큰_정보) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + ) + .andDo(print()) + .andDo(document("members/me", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ) + )) + .andExpect(status().isOk()); + } + + @DisplayName("존재하지 않는 회원의 정보를 조회하려고 하면 예외를 발생한다.") + @Test + void 존재하지_않는_회원의_정보를_조회하려고_하면_예외를_발생한다() throws Exception { + // given + given(memberService.findById(0L)).willThrow(new NoSuchMemberException()); + + // when & then + mockMvc.perform(get("/api/members/me") + .header(AUTHORIZATION_HEADER_NAME, 토큰_정보) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + ) + .andDo(print()) + .andDo(document("members/exception/notfound", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ) + )) + .andExpect(status().isNotFound()); + } + + @DisplayName("등록된 회원이 자신의 이름을 수정한다.") + @Test + void 등록된_회원이_자신의_이름을_수정한다() throws Exception { + // given + willDoNothing() + .given(memberService) + .update(any(), any()); + MemberUpdateRequest 회원_수정_요청 = new MemberUpdateRequest("패트"); + + // when & then + mockMvc.perform(patch("/api/members/me") + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .content(objectMapper.writeValueAsString(회원_수정_요청)) + ) + .andDo(print()) + .andDo(document("members/update", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ), + requestFields( + fieldWithPath("displayName").type(JsonFieldType.STRING).description("수정할 이름") + ))) + .andExpect(status().isNoContent()); + } + + @DisplayName("등록된 회원이 회원탈퇴 한다.") + @Test + void 등록된_회원이_회원탈퇴_한다() throws Exception { + // given + willDoNothing() + .given(registerService) + .deleteByMemberId(any()); + + // when & then + mockMvc.perform(delete("/api/members/me") + .accept(MediaType.APPLICATION_JSON) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + ) + .andDo(print()) + .andDo(document("members/delete", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ))) + .andExpect(status().isNoContent()); + } +} diff --git a/backend/src/test/java/com/allog/dallog/presentation/ScheduleControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/ScheduleControllerTest.java new file mode 100644 index 00000000..07e224ef --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/presentation/ScheduleControllerTest.java @@ -0,0 +1,368 @@ +package com.allog.dallog.presentation; + +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_시작일시; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_제목; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.레벨_인터뷰_종료일시; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_메모; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_시작일시; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_응답; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_제목; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.알록달록_회의_종료일시; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.willDoNothing; +import static org.mockito.BDDMockito.willThrow; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.allog.dallog.domain.auth.application.AuthService; +import com.allog.dallog.domain.auth.exception.NoPermissionException; +import com.allog.dallog.domain.category.exception.NoSuchCategoryException; +import com.allog.dallog.domain.composition.application.CalendarService; +import com.allog.dallog.domain.composition.application.SchedulerService; +import com.allog.dallog.domain.externalcalendar.application.ExternalCalendarClient; +import com.allog.dallog.domain.integrationschedule.dao.IntegrationScheduleDao; +import com.allog.dallog.domain.schedule.application.ScheduleService; +import com.allog.dallog.domain.schedule.dto.request.ScheduleCreateRequest; +import com.allog.dallog.domain.schedule.dto.request.ScheduleUpdateRequest; +import com.allog.dallog.domain.schedule.dto.response.MemberScheduleResponse; +import com.allog.dallog.domain.schedule.dto.response.MemberScheduleResponses; +import com.allog.dallog.domain.schedule.exception.NoSuchScheduleException; +import com.allog.dallog.domain.subscription.domain.Color; +import java.time.LocalDateTime; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; + +@WebMvcTest(ScheduleController.class) +class ScheduleControllerTest extends ControllerTest { + + private static final String AUTHORIZATION_HEADER_NAME = "Authorization"; + private static final String AUTHORIZATION_HEADER_VALUE = "Bearer aaaaaaaa.bbbbbbbb.cccccccc"; + + @MockBean + private AuthService authService; + + @MockBean + private ScheduleService scheduleService; + + @MockBean + private SchedulerService schedulerService; + + @MockBean + private CalendarService calendarService; + + @MockBean + private IntegrationScheduleDao integrationScheduleDao; + + @MockBean + private ExternalCalendarClient externalCalendarClient; + + @DisplayName("일정 정보를 등록하면 상태코드 201을 반환한다.") + @Test + void 일정_정보를_등록하면_상태코드_201을_반환한다() throws Exception { + // given + Long categoryId = 1L; + ScheduleCreateRequest request = new ScheduleCreateRequest(알록달록_회의_제목, 알록달록_회의_시작일시, 알록달록_회의_종료일시, 알록달록_회의_메모); + + given(scheduleService.save(any(), any(), any())).willReturn(알록달록_회의_응답); + + // when & then + mockMvc.perform(post("/api/categories/{categoryId}/schedules", categoryId) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andDo(document("schedules/save", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()) + )) + .andExpect(status().isCreated()); + } + + @DisplayName("일정 정보를 등록할때 해당 카테고리에 권한이 없으면 403을 반환한다.") + @Test + void 일정_정보를_등록할때_해당_카테고리에_권한이_없으면_403을_반환한다() throws Exception { + // given + Long categoryId = 1L; + ScheduleCreateRequest request = new ScheduleCreateRequest(알록달록_회의_제목, 알록달록_회의_시작일시, 알록달록_회의_종료일시, 알록달록_회의_메모); + + given(scheduleService.save(any(), any(), any())).willThrow(new NoPermissionException()); + + // when & then + mockMvc.perform(post("/api/categories/{categoryId}/schedules", categoryId) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andDo(document("schedules/save/forbidden", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()) + )) + .andExpect(status().isForbidden()); + } + + @DisplayName("일정 생성시 전달한 카테고리가 존재하지 않는다면 404를 반환한다.") + @Test + void 일정_생성시_전달한_카테고리가_존재하지_않는다면_404를_반환한다() throws Exception { + // given + Long categoryId = 0L; + ScheduleCreateRequest request = new ScheduleCreateRequest(알록달록_회의_제목, 알록달록_회의_시작일시, 알록달록_회의_종료일시, 알록달록_회의_메모); + + given(scheduleService.save(any(), any(), any())).willThrow(new NoSuchCategoryException()); + + // when & then + mockMvc.perform(post("/api/categories/{categoryId}/schedules", categoryId) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andDo(document("schedules/save/notfound", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()) + )) + .andExpect(status().isNotFound()); + } + + @DisplayName("일정을 단건 조회 하면 상태코드 200을 반환한다") + @Test + void 일정을_단건_조회_하면_상태코드_200을_반환한다() throws Exception { + // given + Long scheduleId = 1L; + + given(scheduleService.findById(scheduleId)).willReturn(알록달록_회의_응답); + + // when & then + mockMvc.perform(get("/api/schedules/{scheduleId}", scheduleId) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andDo(document("schedules/findone", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()) + )) + .andExpect(status().isOk()); + } + + @DisplayName("일정을 단건 조회 할 때 일정이 존재하지 않으면 상태코드 404를 반환한다.") + @Test + void 일정을_단건_조회_할_때_일정이_존재하지_않으면_상태코드_404를_반환한다() throws Exception { + // given + Long scheduleId = 1L; + + given(scheduleService.findById(scheduleId)).willThrow(new NoSuchScheduleException()); + + // when & then + mockMvc.perform(get("/api/schedules/{scheduleId}", scheduleId) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andDo(document("schedules/findone/notfound", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()) + )) + .andExpect(status().isNotFound()); + } + + + @DisplayName("일정을 수정하는데 성공하면 204를 반환한다.") + @Test + void 일정을_수정하는데_성공하면_204를_반환한다() throws Exception { + // given + Long scheduleId = 1L; + ScheduleUpdateRequest 수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + willDoNothing() + .given(scheduleService) + .update(any(), any(), any()); + + // when & then + mockMvc.perform(RestDocumentationRequestBuilders.patch("/api/schedules/{scheduleId}", scheduleId) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(수정_요청))) + .andDo(print()) + .andDo(document("schedules/update", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("scheduleId").description("일정 ID") + ) + )) + .andExpect(status().isNoContent()); + } + + @DisplayName("일정을 수정하는데 해당 일정의 카테고리에 대한 권한이 없다면 403을 반환한다.") + @Test + void 일정을_수정하는데_해당_일정의_카테고리에_대한_권한이_없다면_403을_반환한다() throws Exception { + // given + Long scheduleId = 1L; + ScheduleUpdateRequest 수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + willThrow(new NoPermissionException()) + .given(scheduleService) + .update(any(), any(), any()); + + // when & then + mockMvc.perform(patch("/api/schedules/{scheduleId}", scheduleId) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(수정_요청))) + .andDo(print()) + .andDo(document("schedules/update/forbidden", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()) + )) + .andExpect(status().isForbidden()); + } + + @DisplayName("일정을 수정하는데 일정이 존재하지 않는 경우 404를 반환한다") + @Test + void 일정을_수정하는데_일정이_존재하지_않는_경우_404를_반환한다() throws Exception { + // given + Long scheduleId = 1L; + ScheduleUpdateRequest 수정_요청 = new ScheduleUpdateRequest(레벨_인터뷰_제목, 레벨_인터뷰_시작일시, 레벨_인터뷰_종료일시, 레벨_인터뷰_메모); + willThrow(new NoSuchScheduleException()) + .given(scheduleService) + .update(any(), any(), any()); + + // when & then + mockMvc.perform(patch("/api/schedules/{scheduleId}", scheduleId) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(수정_요청))) + .andDo(print()) + .andDo(document("schedules/update/notfound", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()) + )) + .andExpect(status().isNotFound()); + } + + @DisplayName("일정을 제거하는데 성공하면 204를 반환한다.") + @Test + void 일정을_제거하는데_성공하면_204를_반환한다() throws Exception { + // given + Long scheduleId = 1L; + willDoNothing() + .given(scheduleService) + .deleteById(any(), any()); + + // when & then + mockMvc.perform(RestDocumentationRequestBuilders.delete("/api/schedules/{scheduleId}", scheduleId) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE)) + .andDo(print()) + .andDo(document("schedules/delete", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("scheduleId").description("일정 ID") + ) + )) + .andExpect(status().isNoContent()); + } + + @DisplayName("일정을 제거하는데 해당 일정의 카테고리에 대한 권한이 없다면 403을 반환한다.") + @Test + void 일정을_제거하는데_해당_일정의_카테고리에_대한_권한이_없다면_403을_반환한다() throws Exception { + // given + Long scheduleId = 1L; + willThrow(new NoPermissionException()) + .given(scheduleService) + .deleteById(any(), any()); + + // when & then + mockMvc.perform(delete("/api/schedules/{scheduleId}", scheduleId) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE)) + .andDo(print()) + .andDo(document("schedules/delete/forbidden", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()) + )) + .andExpect(status().isForbidden()); + } + + @DisplayName("일정을 제거하는데 일정이 존재하지 않는 경우 404를 반환한다") + @Test + void 일정을_제거하는데_일정이_존재하지_않는_경우_404를_반환한다() throws Exception { + // given + Long scheduleId = 1L; + willThrow(new NoSuchScheduleException()) + .given(scheduleService) + .deleteById(any(), any()); + + // when & then + mockMvc.perform(delete("/api/schedules/{scheduleId}", scheduleId) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE)) + .andDo(print()) + .andDo(document("schedules/delete/notfound", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()) + )) + .andExpect(status().isNotFound()); + } + + @DisplayName("회원의 일정 목록을 정상적으로 조회하면 200을 반환한다.") + @Test + void 회원의_일정_목록을_정상적으로_조회하면_200을_반환한다() throws Exception { + // given + String startDate = "2022-07-31T00:00"; + String endDate = "2022-09-03T00:00"; + + MemberScheduleResponse 장기간_일정_1 = new MemberScheduleResponse("1L", "장기간 일정 1", + LocalDateTime.of(2022, 8, 1, 0, 0), + LocalDateTime.of(2022, 8, 3, 0, 0), "장기간 일정 1의 메모", 1L, Color.COLOR_1.getColorCode(), "NORMAL"); + MemberScheduleResponse 장기간_일정_2 = new MemberScheduleResponse("1L", "장기간 일정 2", + LocalDateTime.of(2022, 8, 3, 0, 0), + LocalDateTime.of(2022, 8, 10, 0, 0), "장기간 일정 2의 메모", 3L, Color.COLOR_2.getColorCode(), "NORMAL"); + + MemberScheduleResponse 종일_일정_1 = new MemberScheduleResponse("1L", "종일 일정 1", LocalDateTime.of(2022, 8, 1, 0, 0), + LocalDateTime.of(2022, 8, 1, 23, 59), "종일 일정 1의 메모", 1L, Color.COLOR_3.getColorCode(), "NORMAL"); + MemberScheduleResponse 종일_일정_2 = new MemberScheduleResponse("1L", "종일 일정 2", LocalDateTime.of(2022, 8, 5, 0, 0), + LocalDateTime.of(2022, 8, 5, 23, 59), "종일 일정 2의 메모", 3L, Color.COLOR_4.getColorCode(), "NORMAL"); + + MemberScheduleResponse 짧은_일정_1 = new MemberScheduleResponse("1L", "짧은 일정 1", LocalDateTime.of(2022, 8, 1, 0, 0), + LocalDateTime.of(2022, 8, 1, 1, 0), "짧은 일정 1의 메모", 1L, Color.COLOR_5.getColorCode(), "NORMAL"); + MemberScheduleResponse 짧은_일정_2 = new MemberScheduleResponse("1L", "짧은 일정 2", + LocalDateTime.of(2022, 8, 5, 17, 0), + LocalDateTime.of(2022, 8, 5, 19, 0), "짧은 일정 2의 메모", 3L, Color.COLOR_6.getColorCode(), "NORMAL"); + + MemberScheduleResponses memberScheduleResponses = new MemberScheduleResponses(List.of(장기간_일정_1, 장기간_일정_2), + List.of(종일_일정_1, 종일_일정_2), List.of(짧은_일정_1, 짧은_일정_2)); + + given(calendarService.findSchedulesByMemberId(any(), any())) + .willReturn(memberScheduleResponses); + + // when & then + mockMvc.perform( + get("/api/members/me/schedules?startDateTime={startDate}&endDateTime={endDate}", startDate, endDate) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE)) + .andDo(print()) + .andDo(document("schedules/findAllByMember", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestParameters( + parameterWithName("startDateTime").description("일정 조회 시작 범위 (yyyy-mm-dd'T'HH:mm)"), + parameterWithName("endDateTime").description("일정 조회 마지막 범위 (yyyy-mm-dd'T'HH:mm)") + ) + )) + .andExpect(status().isOk()); + } +} diff --git a/backend/src/test/java/com/allog/dallog/presentation/SchedulerControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/SchedulerControllerTest.java new file mode 100644 index 00000000..85c174a9 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/presentation/SchedulerControllerTest.java @@ -0,0 +1,81 @@ +package com.allog.dallog.presentation; + +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_10일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_15일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_16시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_18시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_16일_20시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_20일_11시_59분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_27일_0시_0분; +import static com.allog.dallog.common.fixtures.ScheduleFixtures.날짜_2022년_7월_7일_16시_0분; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.restdocs.request.RequestDocumentation.requestParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.allog.dallog.domain.auth.application.AuthService; +import com.allog.dallog.domain.composition.application.SchedulerService; +import com.allog.dallog.domain.integrationschedule.domain.Period; +import com.allog.dallog.domain.schedule.dto.response.PeriodResponse; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; + +@WebMvcTest(SchedulerController.class) +class SchedulerControllerTest extends ControllerTest { + + private static final String AUTHORIZATION_HEADER_NAME = "Authorization"; + private static final String AUTHORIZATION_HEADER_VALUE = "Bearer aaaaaaaa.bbbbbbbb.cccccccc"; + + @MockBean + private AuthService authService; + + @MockBean + private SchedulerService schedulerService; + + @DisplayName("일정 조율 결과를 반환한다.") + @Test + void 일정_조율_결과를_반환한다() throws Exception { + // given + String startDateTime = "2022-07-01T00:00"; + String endDateTime = "2022-07-31T00:00"; + + given(schedulerService.getAvailablePeriods(any(), any())) + .willReturn(List.of( + new PeriodResponse(new Period(날짜_2022년_7월_7일_16시_0분, 날짜_2022년_7월_10일_0시_0분)), + new PeriodResponse(new Period(날짜_2022년_7월_15일_16시_0분, 날짜_2022년_7월_16일_16시_0분)), + new PeriodResponse(new Period(날짜_2022년_7월_16일_18시_0분, 날짜_2022년_7월_16일_20시_0분)), + new PeriodResponse(new Period(날짜_2022년_7월_20일_11시_59분, 날짜_2022년_7월_27일_0시_0분)) + )); + + // when & then + mockMvc.perform(RestDocumentationRequestBuilders.get( + "/api/scheduler/categories/{categoryId}/available-periods?startDateTime={startDateTime}&endDateTime={endDateTime}", + 1L, startDateTime, endDateTime) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE)) + .andDo(print()) + .andDo(document("scheduler/category/available-periods", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("categoryId").description("카테고리 ID") + ), + requestParameters( + parameterWithName("startDateTime").description("일정 조회 시작 범위 (yyyy-mm-dd'T'HH:mm)"), + parameterWithName("endDateTime").description("일정 조회 마지막 범위 (yyyy-mm-dd'T'HH:mm)") + ) + )) + .andExpect(status().isOk()); + } + +} diff --git a/backend/src/test/java/com/allog/dallog/presentation/SubscriptionControllerTest.java b/backend/src/test/java/com/allog/dallog/presentation/SubscriptionControllerTest.java new file mode 100644 index 00000000..06bb1753 --- /dev/null +++ b/backend/src/test/java/com/allog/dallog/presentation/SubscriptionControllerTest.java @@ -0,0 +1,282 @@ +package com.allog.dallog.presentation; + +import static com.allog.dallog.common.fixtures.CategoryFixtures.BE_일정_응답; +import static com.allog.dallog.common.fixtures.CategoryFixtures.FE_일정_응답; +import static com.allog.dallog.common.fixtures.CategoryFixtures.공통_일정_응답; +import static com.allog.dallog.common.fixtures.MemberFixtures.관리자_응답; +import static com.allog.dallog.common.fixtures.MemberFixtures.매트_응답; +import static com.allog.dallog.common.fixtures.SubscriptionFixtures.색상1_구독_응답; +import static com.allog.dallog.common.fixtures.SubscriptionFixtures.색상2_구독_응답; +import static com.allog.dallog.common.fixtures.SubscriptionFixtures.색상3_구독_응답; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.willDoNothing; +import static org.mockito.BDDMockito.willThrow; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse; +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import com.allog.dallog.domain.auth.application.AuthService; +import com.allog.dallog.domain.auth.exception.NoPermissionException; +import com.allog.dallog.domain.category.dto.response.CategoryResponse; +import com.allog.dallog.domain.subscription.application.SubscriptionService; +import com.allog.dallog.domain.subscription.domain.Color; +import com.allog.dallog.domain.subscription.dto.request.SubscriptionUpdateRequest; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionResponse; +import com.allog.dallog.domain.subscription.dto.response.SubscriptionsResponse; +import com.allog.dallog.domain.subscription.exception.ExistSubscriptionException; +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; + +@WebMvcTest(SubscriptionController.class) +class SubscriptionControllerTest extends ControllerTest { + + private static final String AUTHORIZATION_HEADER_NAME = "Authorization"; + private static final String AUTHORIZATION_HEADER_VALUE = "Bearer aaaaa.bbbbb.ccccc"; + + @MockBean + private AuthService authService; + + @MockBean + private SubscriptionService subscriptionService; + + @DisplayName("회원과 카테고리 정보를 기반으로 구독한다.") + @Test + void 회원과_카테고리_정보를_기반으로_구독한다() throws Exception { + // given + CategoryResponse 공통_일정_응답 = 공통_일정_응답(관리자_응답); + SubscriptionResponse 색상1_구독_응답 = 색상1_구독_응답(공통_일정_응답); + + given(authService.extractMemberId(any())).willReturn(매트_응답.getId()); + given(subscriptionService.save(any(), any())).willReturn(색상1_구독_응답); + + // when & then + mockMvc.perform(post("/api/members/me/categories/{categoryId}/subscriptions", 공통_일정_응답.getId()) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andDo(document("subscription/save", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("categoryId").description("카테고리 id") + ), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ))) + .andExpect(status().isCreated()); + } + + @DisplayName("자신의 구독 목록을 가져온다.") + @Test + void 자신의_구독_목록을_가져온다() throws Exception { + // given + CategoryResponse 공통_일정_응답 = 공통_일정_응답(관리자_응답); + + SubscriptionsResponse subscriptionsResponse = new SubscriptionsResponse( + List.of(색상1_구독_응답(공통_일정_응답), 색상2_구독_응답(공통_일정_응답), 색상3_구독_응답(공통_일정_응답))); + + given(subscriptionService.findByMemberId(any())) + .willReturn(subscriptionsResponse); + + // when & then + mockMvc.perform(get("/api/members/me/subscriptions", 공통_일정_응답.getId()) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andDo(document("subscription/findMine", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()) + )) + .andExpect(status().isOk()); + } + + @DisplayName("회원이 이미 카테고리를 구독한 경우 예외를 던진다.") + @Test + void 회원이_이미_카테고리를_구독한_경우_예외를_던진다() throws Exception { + // given + given(authService.extractMemberId(any())).willReturn(매트_응답.getId()); + given(subscriptionService.save(any(), any())).willThrow(new ExistSubscriptionException()); + + // when & then + mockMvc.perform( + post("/api/members/me/categories/{categoryId}/subscriptions", 1L) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andDo(document("subscription/exist", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("categoryId").description("카테고리 id") + ), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ))) + .andExpect(status().isBadRequest()); + } + + @DisplayName("타인의 개인 카테고리 구독 요청시 403 Forbidden을 반환한다.") + @Test + void 타인의_개인_카테고리_구독_요청시_403_Forbidden을_반환한다() throws Exception { + // given + given(subscriptionService.save(any(), any())) + .willThrow(new NoPermissionException("구독 권한이 없는 카테고리입니다.")); + + // when & then + mockMvc.perform( + post("/api/members/me/categories/{categoryId}/subscriptions", 1L) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andDo(document("subscription/private-category", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("categoryId").description("카테고리 id") + ), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ))) + .andExpect(status().isForbidden()); + } + + @DisplayName("자신의 구독 정보를 조회한다.") + @Test + void 자신의_구독_정보를_조회한다() throws Exception { + // given + CategoryResponse 공통_일정_응답 = 공통_일정_응답(관리자_응답); + CategoryResponse BE_일정_응답 = BE_일정_응답(관리자_응답); + CategoryResponse FE_일정_응답 = FE_일정_응답(관리자_응답); + + SubscriptionResponse 색상1_구독_응답 = 색상1_구독_응답(공통_일정_응답); + SubscriptionResponse 색상2_구독_응답 = 색상2_구독_응답(BE_일정_응답); + SubscriptionResponse 색상3_구독_응답 = 색상3_구독_응답(FE_일정_응답); + + given(authService.extractMemberId(any())).willReturn(매트_응답.getId()); + + List subscriptionResponses = List.of(색상1_구독_응답, 색상2_구독_응답, 색상3_구독_응답); + given(subscriptionService.findByMemberId(any())).willReturn(new SubscriptionsResponse(subscriptionResponses)); + + // when & then + mockMvc.perform(get("/api/members/me/subscriptions") + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andDo(document("subscription/me", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ))) + .andExpect(status().isOk()); + } + + @DisplayName("자신의 구독 정보를 수정한다.") + @Test + void 자신의_구독_정보를_수정한다() throws Exception { + // given + CategoryResponse 공통_일정_응답 = 공통_일정_응답(관리자_응답); + SubscriptionResponse 색상1_구독_응답 = 색상1_구독_응답(공통_일정_응답); + SubscriptionUpdateRequest request = new SubscriptionUpdateRequest(Color.COLOR_2, true); + + given(authService.extractMemberId(any())).willReturn(매트_응답.getId()); + willDoNothing().given(subscriptionService) + .update(색상1_구독_응답.getId(), 매트_응답.getId(), request); + + // when & then + mockMvc.perform(patch("/api/members/me/subscriptions/{subscriptionId}", 색상1_구독_응답.getId()) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request))) + .andDo(print()) + .andDo(document("subscription/update", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("subscriptionId").description("구독 id") + ), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ), + requestFields( + fieldWithPath("colorCode").type(JsonFieldType.STRING).description("구독 색 정보"), + fieldWithPath("checked").type(JsonFieldType.BOOLEAN).description("체크 유무") + ))) + .andExpect(status().isNoContent()); + } + + @DisplayName("구독 id를 기반으로 자신의 구독 정보를 삭제한다.") + @Test + void 구독_id를_기반으로_자신의_구독_정보를_삭제한다() throws Exception { + // given + CategoryResponse 공통_일정_응답 = 공통_일정_응답(관리자_응답); + SubscriptionResponse 색상1_구독_응답 = 색상1_구독_응답(공통_일정_응답); + + given(authService.extractMemberId(any())).willReturn(매트_응답.getId()); + willDoNothing().given(subscriptionService) + .deleteById(색상1_구독_응답.getId(), 매트_응답.getId()); + + // when & then + mockMvc.perform(delete("/api/members/me/subscriptions/{subscriptionId}", 색상1_구독_응답.getId()) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andDo(document("subscription/delete", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("subscriptionId").description("구독 id") + ), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ))) + .andExpect(status().isNoContent()); + } + + @DisplayName("자신이 가지고 있지 않은 구독 정보인 경우 예외를 던진다.") + @Test + void 자신이_가지고_있지_않은_구독_정보인_경우_예외를_던진다() throws Exception { + // given + given(authService.extractMemberId(any())).willReturn(매트_응답.getId()); + willThrow(new NoPermissionException()) + .willDoNothing() + .given(subscriptionService) + .deleteById(any(), any()); + + // when & then + mockMvc.perform(delete("/api/members/me/subscriptions/{subscriptionId}", 1L) + .header(AUTHORIZATION_HEADER_NAME, AUTHORIZATION_HEADER_VALUE) + .contentType(MediaType.APPLICATION_JSON)) + .andDo(print()) + .andDo(document("subscription/permission", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + pathParameters( + parameterWithName("subscriptionId").description("구독 id") + ), + requestHeaders( + headerWithName("Authorization").description("JWT 토큰") + ))) + .andExpect(status().isForbidden()); + } +} diff --git a/backend/src/test/resources/application.yml b/backend/src/test/resources/application.yml new file mode 100644 index 00000000..bbb8ec55 --- /dev/null +++ b/backend/src/test/resources/application.yml @@ -0,0 +1,51 @@ +spring: + main: + allow-bean-definition-overriding: true + + datasource: + url: jdbc:h2:~/dallog;MODE=MYSQL;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE + username: sa + + sql: + init: + mode: NEVER + + jpa: + properties: + hibernate: + format_sql: true + show-sql: true + + hibernate: + ddl-auto: create + + data: + web: + pageable: + max-page-size: 100 + +logging: + level: + org.hibernate.type.descriptor.sql.BasicBinder: TRACE + +oauth: + google: + client-id: hyeonic + client-secret: 123 + redirect-uri: http://localhost:3000 + oauth-end-point: https://accounts.google.com/o/oauth2/v2/auth + response-type: code + scopes: + - https://www.googleapis.com/auth/userinfo.profile + - https://www.googleapis.com/auth/userinfo.email + token-uri: https://oauth2.googleapis.com/token + +cors: + allow-origin: + urls: http://localhost:3000 + +security: + jwt: + token: + secret-key: fsmjgbdafmjgbasmfgadbsgmadfhgbfamjghbvmssdgsdfgdf + expire-length: 3600000 diff --git a/backend/~/Desktop/jacoco/index.xml b/backend/~/Desktop/jacoco/index.xml new file mode 100644 index 00000000..bde0fd01 --- /dev/null +++ b/backend/~/Desktop/jacoco/index.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json new file mode 100644 index 00000000..c1e6f3c9 --- /dev/null +++ b/frontend/.eslintrc.json @@ -0,0 +1,20 @@ +{ + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint", "react-hooks", "prettier"], + "extends": [ + "plugin:react/recommended", + "plugin:import/errors", + "plugin:import/warnings", + "plugin:import/recommended", + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended" + ], + "rules": { + "react/react-in-jsx-scope": "off", + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn", + "import/named": "off", + "import/no-unresolved": "off", + "@typescript-eslint/no-var-requires": "off" + } +} diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 00000000..9c97bbd4 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,3 @@ +node_modules +dist +.env diff --git a/frontend/.prettierrc.json b/frontend/.prettierrc.json new file mode 100644 index 00000000..ca2ae640 --- /dev/null +++ b/frontend/.prettierrc.json @@ -0,0 +1,27 @@ +{ + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "bracketSpacing": true, + "bracketSameLine": false, + "arrowParens": "always", + "endOfLine": "auto", + "importOrder": [ + "@/hooks/(.*)$", + "@/@types", + "@/recoil/(atoms|selectors)", + "@/styles/(.*)$", + "@/(components/@common/|components/|pages/)(.*)$", + "@/constants", + "@/utils", + "@/(api|mocks)/(.*)$", + "react-icons", + "^[./]" + ], + "importOrderSeparation": true, + "importOrderSortSpecifiers": true, + "importOrderCaseInsensitive": true +} diff --git a/frontend/.storybook/main.js b/frontend/.storybook/main.js new file mode 100644 index 00000000..b640a76b --- /dev/null +++ b/frontend/.storybook/main.js @@ -0,0 +1,23 @@ +const path = require('path'); + +module.exports = { + stories: ['../src/**/*.stories.@(tsx)'], + addons: [ + '@storybook/addon-links', + '@storybook/addon-essentials', + '@storybook/addon-interactions', + ], + framework: '@storybook/react', + core: { + builder: '@storybook/builder-webpack5', + }, + webpackFinal: async (config) => { + config.resolve.alias = { + ...config.resolve.alias, + '@': path.resolve(__dirname, '../src/'), + }; + config.resolve.extensions.push('.ts', '.tsx'); + + return config; + }, +}; diff --git a/frontend/.storybook/preview-body.html b/frontend/.storybook/preview-body.html new file mode 100644 index 00000000..508dda40 --- /dev/null +++ b/frontend/.storybook/preview-body.html @@ -0,0 +1,5 @@ + diff --git a/frontend/.storybook/preview.js b/frontend/.storybook/preview.js new file mode 100644 index 00000000..6c3fa7db --- /dev/null +++ b/frontend/.storybook/preview.js @@ -0,0 +1,35 @@ +import { ThemeProvider } from '@emotion/react'; +import { QueryClient, QueryClientProvider } from 'react-query'; +import { BrowserRouter as Router } from 'react-router-dom'; +import { RecoilRoot } from 'recoil'; + +import GlobalStyle from '../src/styles/GlobalStyle'; +import theme from '../src/styles/theme'; + +const queryClient = new QueryClient(); + +export const parameters = { + actions: { argTypesRegex: '^on[A-Z].*' }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/, + }, + }, +}; + +export const decorators = [ + (Story) => ( + + + + + + + + + + + + ), +]; diff --git a/frontend/babel.config.js b/frontend/babel.config.js new file mode 100644 index 00000000..32315f68 --- /dev/null +++ b/frontend/babel.config.js @@ -0,0 +1,9 @@ +module.exports = { + presets: [ + '@babel/preset-react', + '@babel/preset-env', + '@babel/preset-typescript', + '@emotion/babel-preset-css-prop', + ], + plugins: ['@emotion'], +}; diff --git a/frontend/jest.config.js b/frontend/jest.config.js new file mode 100644 index 00000000..5cea0750 --- /dev/null +++ b/frontend/jest.config.js @@ -0,0 +1,6 @@ +/** @type {import('@jest/types').Config.InitialOptions} */ +const config = { + verbose: true, +}; + +module.exports = config; diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 00000000..4d793cf9 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,81 @@ +{ + "name": "dallog", + "version": "2.0.0", + "description": "share calendar dallog", + "main": "index.js", + "scripts": { + "dev": "webpack serve --mode development --open --hot", + "prod-build": "webpack --mode production", + "dev-build": "webpack --mode development", + "storybook": "start-storybook -p 6006", + "build-storybook": "build-storybook", + "pretty": "prettier . --write", + "check-lint": "eslint . --ext .js,.jsx,.ts,.tsx", + "check-prettier": "prettier -c ./src", + "test": "jest --setupFiles ./setupFile.js" + }, + "dependencies": { + "@emotion/react": "^11.9.3", + "@emotion/styled": "^11.9.3", + "axios": "^0.27.2", + "dotenv-webpack": "^8.0.0", + "emotion-reset": "^3.0.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-query": "^3.39.1", + "react-router-dom": "^6.3.0", + "recoil": "^0.7.4" + }, + "devDependencies": { + "@babel/core": "^7.18.6", + "@babel/preset-env": "^7.18.6", + "@babel/preset-react": "^7.18.6", + "@babel/preset-typescript": "^7.18.6", + "@emotion/babel-plugin": "^11.9.2", + "@emotion/babel-preset-css-prop": "^11.2.0", + "@storybook/addon-actions": "^6.5.9", + "@storybook/addon-essentials": "^6.5.9", + "@storybook/addon-interactions": "^6.5.9", + "@storybook/addon-links": "^6.5.9", + "@storybook/builder-webpack5": "^6.5.9", + "@storybook/manager-webpack5": "^6.5.9", + "@storybook/react": "^6.5.9", + "@storybook/testing-library": "^0.0.13", + "@storybook/testing-react": "^1.3.0", + "@testing-library/react": "^13.3.0", + "@trivago/prettier-plugin-sort-imports": "^3.2.0", + "@types/body-parser": "^1.19.2", + "@types/graceful-fs": "^4.1.5", + "@types/jest": "^28.1.6", + "@types/node": "^18.0.1", + "@types/react": "^18.0.14", + "@types/react-dom": "^18.0.5", + "@typescript-eslint/eslint-plugin": "^5.30.4", + "@typescript-eslint/parser": "^5.30.4", + "babel-loader": "^8.2.5", + "clean-webpack-plugin": "^4.0.0", + "css-loader": "^6.7.1", + "eslint": "^8.19.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-plugin-react": "^7.30.1", + "eslint-plugin-react-hooks": "^4.6.0", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.5.0", + "jest": "^28.1.3", + "jest-environment-jsdom": "^28.1.3", + "msw": "^0.43.0", + "prettier": "^2.7.1", + "react-icons": "^4.4.0", + "style-loader": "^3.3.1", + "ts-loader": "^9.3.1", + "typescript": "^4.7.4", + "webpack": "^5.73.0", + "webpack-cli": "^4.10.0", + "webpack-dev-server": "^4.9.3" + }, + "msw": { + "workerDirectory": "public" + } +} diff --git a/frontend/public/mockServiceWorker.js b/frontend/public/mockServiceWorker.js new file mode 100644 index 00000000..a5fbb289 --- /dev/null +++ b/frontend/public/mockServiceWorker.js @@ -0,0 +1,364 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker (0.43.0). + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + * - Please do NOT serve this file on production. + */ + +const INTEGRITY_CHECKSUM = 'c9450df6e4dc5e45740c3b0b640727a2'; +const activeClientIds = new Set(); + +self.addEventListener('install', function () { + self.skipWaiting(); +}); + +self.addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()); +}); + +self.addEventListener('message', async function (event) { + const clientId = event.source.id; + + if (!clientId || !self.clients) { + return; + } + + const client = await self.clients.get(clientId); + + if (!client) { + return; + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }); + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }); + break; + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: INTEGRITY_CHECKSUM, + }); + break; + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId); + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: true, + }); + break; + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId); + break; + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId); + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId; + }); + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister(); + } + + break; + } + } +}); + +self.addEventListener('fetch', function (event) { + const { request } = event; + const accept = request.headers.get('accept') || ''; + + // Bypass server-sent events. + if (accept.includes('text/event-stream')) { + return; + } + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return; + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return; + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return; + } + + // Generate unique request ID. + const requestId = Math.random().toString(16).slice(2); + + event.respondWith( + handleRequest(event, requestId).catch((error) => { + if (error.name === 'NetworkError') { + console.warn( + '[MSW] Successfully emulated a network error for the "%s %s" request.', + request.method, + request.url + ); + return; + } + + // At this point, any exception indicates an issue with the original request/response. + console.error( + `\ +[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, + request.method, + request.url, + `${error.name}: ${error.message}` + ); + }) + ); +}); + +async function handleRequest(event, requestId) { + const client = await resolveMainClient(event); + const response = await getResponse(event, client, requestId); + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + (async function () { + const clonedResponse = response.clone(); + sendToClient(client, { + type: 'RESPONSE', + payload: { + requestId, + type: clonedResponse.type, + ok: clonedResponse.ok, + status: clonedResponse.status, + statusText: clonedResponse.statusText, + body: clonedResponse.body === null ? null : await clonedResponse.text(), + headers: Object.fromEntries(clonedResponse.headers.entries()), + redirected: clonedResponse.redirected, + }, + }); + })(); + } + + return response; +} + +// Resolve the main client for the given event. +// Client that issues a request doesn't necessarily equal the client +// that registered the worker. It's with the latter the worker should +// communicate with during the response resolving phase. +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId); + + if (client.frameType === 'top-level') { + return client; + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }); + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible'; + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id); + }); +} + +async function getResponse(event, client, requestId) { + const { request } = event; + const clonedRequest = request.clone(); + + function passthrough() { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the cilent). + const headers = Object.fromEntries(clonedRequest.headers.entries()); + + // Remove MSW-specific request headers so the bypassed requests + // comply with the server's CORS preflight check. + // Operate with the headers as an object because request "Headers" + // are immutable. + delete headers['x-msw-bypass']; + + return fetch(clonedRequest, { headers }); + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough(); + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough(); + } + + // Bypass requests with the explicit bypass header. + // Such requests can be issued by "ctx.fetch()". + if (request.headers.get('x-msw-bypass') === 'true') { + return passthrough(); + } + + // Create a communication channel scoped to the current request. + // This way events can be exchanged outside of the worker's global + // "message" event listener (i.e. abstracted into functions). + const operationChannel = new BroadcastChannel(`msw-response-stream-${requestId}`); + + // Notify the client that a request has been intercepted. + const clientMessage = await sendToClient(client, { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + mode: request.mode, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.text(), + bodyUsed: request.bodyUsed, + keepalive: request.keepalive, + }, + }); + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.payload); + } + + case 'MOCK_RESPONSE_START': { + return respondWithMockStream(operationChannel, clientMessage.payload); + } + + case 'MOCK_NOT_FOUND': { + return passthrough(); + } + + case 'NETWORK_ERROR': { + const { name, message } = clientMessage.payload; + const networkError = new Error(message); + networkError.name = name; + + // Rejecting a "respondWith" promise emulates a network error. + throw networkError; + } + + case 'INTERNAL_ERROR': { + const parsedBody = JSON.parse(clientMessage.payload.body); + + console.error( + `\ +[MSW] Uncaught exception in the request handler for "%s %s": + +${parsedBody.location} + +This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error, as it indicates a mistake in your code. If you wish to mock an error response, please see this guide: https://mswjs.io/docs/recipes/mocking-error-responses\ +`, + request.method, + request.url + ); + + return respondWithMock(clientMessage.payload); + } + } + + return passthrough(); +} + +function sendToClient(client, message) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel(); + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error); + } + + resolve(event.data); + }; + + client.postMessage(JSON.stringify(message), [channel.port2]); + }); +} + +function sleep(timeMs) { + return new Promise((resolve) => { + setTimeout(resolve, timeMs); + }); +} + +async function respondWithMock(response) { + await sleep(response.delay); + return new Response(response.body, response); +} + +function respondWithMockStream(operationChannel, mockResponse) { + let streamCtrl; + const stream = new ReadableStream({ + start: (controller) => (streamCtrl = controller), + }); + + return new Promise(async (resolve, reject) => { + operationChannel.onmessageerror = (event) => { + operationChannel.close(); + return reject(event.data.error); + }; + + operationChannel.onmessage = (event) => { + if (!event.data) { + return; + } + + switch (event.data.type) { + case 'MOCK_RESPONSE_CHUNK': { + streamCtrl.enqueue(event.data.payload); + break; + } + + case 'MOCK_RESPONSE_END': { + streamCtrl.close(); + operationChannel.close(); + } + } + }; + + await sleep(mockResponse.delay); + return resolve(new Response(stream, mockResponse)); + }); +} diff --git a/frontend/setupFile.js b/frontend/setupFile.js new file mode 100644 index 00000000..a0e5a9fa --- /dev/null +++ b/frontend/setupFile.js @@ -0,0 +1,5 @@ +import { setGlobalConfig } from '@storybook/testing-react'; + +import * as globalStorybookConfig from './.storybook/preview'; + +setGlobalConfig(globalStorybookConfig); diff --git a/frontend/src/@types/calendar.ts b/frontend/src/@types/calendar.ts new file mode 100644 index 00000000..c0aa76dc --- /dev/null +++ b/frontend/src/@types/calendar.ts @@ -0,0 +1,8 @@ +interface CalendarType { + year: number; + month: number; + date: number; + day: number; +} + +export { CalendarType }; diff --git a/frontend/src/@types/category.ts b/frontend/src/@types/category.ts new file mode 100644 index 00000000..6218dec1 --- /dev/null +++ b/frontend/src/@types/category.ts @@ -0,0 +1,18 @@ +import { CATEGORY_TYPE } from '@/constants/category'; + +import { ProfileType } from './profile'; + +interface CategoryType { + id: number; + name: string; + creator: ProfileType; + createdAt: string; + categoryType: typeof CATEGORY_TYPE[keyof typeof CATEGORY_TYPE]; +} + +interface CategoriesGetResponseType { + page: number; + categories: CategoryType[]; +} + +export { CategoryType, CategoriesGetResponseType }; diff --git a/frontend/src/@types/custom.d.ts b/frontend/src/@types/custom.d.ts new file mode 100644 index 00000000..3e348645 --- /dev/null +++ b/frontend/src/@types/custom.d.ts @@ -0,0 +1,4 @@ +declare module '*.png'; +declare module '*.gif'; +declare module '*.jpg'; +declare module '*.jpeg'; diff --git a/frontend/src/@types/emotion.d.ts b/frontend/src/@types/emotion.d.ts new file mode 100644 index 00000000..98b9b120 --- /dev/null +++ b/frontend/src/@types/emotion.d.ts @@ -0,0 +1,16 @@ +import { SerializedStyles } from '@emotion/react'; + +interface Colors { + [key: string]: string; +} + +interface Flex { + [key: string]: SerializedStyles; +} + +declare module '@emotion/react' { + export interface Theme { + colors: Colors; + flex: Flex; + } +} diff --git a/frontend/src/@types/googleCalendar.ts b/frontend/src/@types/googleCalendar.ts new file mode 100644 index 00000000..c286820a --- /dev/null +++ b/frontend/src/@types/googleCalendar.ts @@ -0,0 +1,13 @@ +interface GoogleCalendarGetResponseType { + externalCalendars: Array<{ + calendarId: string; + summary: string; + }>; +} + +interface GoogleCalendarPostBodyType { + externalId: string; + name: string; +} + +export { GoogleCalendarGetResponseType, GoogleCalendarPostBodyType }; diff --git a/frontend/src/@types/index.ts b/frontend/src/@types/index.ts new file mode 100644 index 00000000..0cb0d39d --- /dev/null +++ b/frontend/src/@types/index.ts @@ -0,0 +1,20 @@ +import { SerializedStyles } from '@emotion/react'; + +interface FieldsetCssPropType { + div?: SerializedStyles; + input?: SerializedStyles; + label?: SerializedStyles; +} + +interface InputRefType { + [index: string]: React.RefObject; +} + +interface ModalPosType { + top?: number; + right?: number; + bottom?: number; + left?: number; +} + +export { FieldsetCssPropType, InputRefType, ModalPosType }; diff --git a/frontend/src/@types/profile.ts b/frontend/src/@types/profile.ts new file mode 100644 index 00000000..f067662b --- /dev/null +++ b/frontend/src/@types/profile.ts @@ -0,0 +1,13 @@ +interface ProfileType { + id: number; + email: string; + displayName: string; + profileImageUrl: string; + socialType: string; +} + +interface ProfileGetResponseType { + data: ProfileType; +} + +export { ProfileType, ProfileGetResponseType }; diff --git a/frontend/src/@types/schedule.ts b/frontend/src/@types/schedule.ts new file mode 100644 index 00000000..1ff6c161 --- /dev/null +++ b/frontend/src/@types/schedule.ts @@ -0,0 +1,20 @@ +import { CATEGORY_TYPE } from '@/constants/category'; + +interface ScheduleType { + id: string; + categoryId: number; + title: string; + startDateTime: string; + endDateTime: string; + memo: string; + colorCode: string; + categoryType: typeof CATEGORY_TYPE[keyof typeof CATEGORY_TYPE]; +} + +interface ScheduleResponseType { + longTerms: Array; + allDays: Array; + fewHours: Array; +} + +export { ScheduleResponseType, ScheduleType }; diff --git a/frontend/src/@types/scheduler.ts b/frontend/src/@types/scheduler.ts new file mode 100644 index 00000000..dee1743a --- /dev/null +++ b/frontend/src/@types/scheduler.ts @@ -0,0 +1,6 @@ +interface SchedulingResponseType { + startDateTime: string; + endDateTime: string; +} + +export { SchedulingResponseType }; diff --git a/frontend/src/@types/subscription.ts b/frontend/src/@types/subscription.ts new file mode 100644 index 00000000..f0094aef --- /dev/null +++ b/frontend/src/@types/subscription.ts @@ -0,0 +1,10 @@ +import { CategoryType } from './category'; + +interface SubscriptionType { + id: number; + category: CategoryType; + colorCode: string; + checked: boolean; +} + +export { SubscriptionType }; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 00000000..65c5257c --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,60 @@ +import { QueryClient, QueryClientProvider } from 'react-query'; +import { Route, BrowserRouter as Router, Routes } from 'react-router-dom'; + +import ErrorBoundary from '@/components/@common/ErrorBoundary/ErrorBoundary'; +import NavBar from '@/components/NavBar/NavBar'; +import ProtectRoute from '@/components/ProtectRoute/ProtectRoute'; +import PublicRoute from '@/components/PublicRoute/PublicRoute'; +import SideBar from '@/components/SideBar/SideBar'; +import SnackBar from '@/components/SnackBar/SnackBar'; +import AuthPage from '@/pages/AuthPage/AuthPage'; +import CategoryPage from '@/pages/CategoryPage/CategoryPage'; +import MainPage from '@/pages/MainPage/MainPage'; +import NotFoundPage from '@/pages/NotFoundPage/NotFoundPage'; +import PrivacyPolicyPage from '@/pages/PrivacyPolicyPage/PrivacyPolicyPage'; +import SchedulingPage from '@/pages/SchedulingPage/SchedulingPage'; + +import { PATH } from '@/constants'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + useErrorBoundary: true, + retry: 1, + retryDelay: 0, + }, + mutations: { + useErrorBoundary: true, + retry: 1, + retryDelay: 0, + }, + }, +}); + +function App() { + return ( + + + + + + + }> + } /> + } /> + } /> + } /> + + }> + } /> + } /> + + + + + + + ); +} + +export default App; diff --git a/frontend/src/api/category.ts b/frontend/src/api/category.ts new file mode 100644 index 00000000..063b3b60 --- /dev/null +++ b/frontend/src/api/category.ts @@ -0,0 +1,74 @@ +import { CategoriesGetResponseType, CategoryType } from '@/@types/category'; + +import dallogApi from './'; + +const categoryApi = { + endpoint: { + entire: '/api/categories', + my: '/api/categories/me', + }, + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + + getEntire: async (name: string, page: number, size: number) => { + const response = await dallogApi.get( + categoryApi.endpoint.entire, + name === '' + ? { + params: { page, size }, + headers: categoryApi.headers, + } + : { + params: { name, page, size }, + headers: categoryApi.headers, + } + ); + + return response; + }, + + getSingle: async (categoryId: number | undefined) => { + const response = await dallogApi.get(`${categoryApi.endpoint.entire}/${categoryId}`, { + headers: { ...categoryApi.headers }, + }); + + return response; + }, + + getMy: async (accessToken: string) => { + const response = await dallogApi.get(categoryApi.endpoint.my, { + headers: { ...categoryApi.headers, Authorization: `Bearer ${accessToken}` }, + transformResponse: (res) => JSON.parse(res).categories, + }); + + return response; + }, + + post: async (accessToken: string, body: Pick) => { + const response = await dallogApi.post(categoryApi.endpoint.entire, body, { + headers: { ...categoryApi.headers, Authorization: `Bearer ${accessToken}` }, + }); + + return response; + }, + + patch: async (accessToken: string, categoryId: number, body: Pick) => { + const response = await dallogApi.patch(`${categoryApi.endpoint.entire}/${categoryId}`, body, { + headers: { ...categoryApi.headers, Authorization: `Bearer ${accessToken}` }, + }); + + return response; + }, + + delete: async (accessToken: string, categoryId: number) => { + const response = await dallogApi.delete(`${categoryApi.endpoint.entire}/${categoryId}`, { + headers: { ...categoryApi.headers, Authorization: `Bearer ${accessToken}` }, + }); + + return response; + }, +}; + +export default categoryApi; diff --git a/frontend/src/api/googleCalendar.ts b/frontend/src/api/googleCalendar.ts new file mode 100644 index 00000000..2aa0e32a --- /dev/null +++ b/frontend/src/api/googleCalendar.ts @@ -0,0 +1,33 @@ +import { GoogleCalendarGetResponseType, GoogleCalendarPostBodyType } from '@/@types/googleCalendar'; + +import dallogApi from './'; + +const googleCalendarApi = { + endpoint: '/api/external-calendars/me', + + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + + get: async (accessToken: string) => { + const response = await dallogApi.get( + googleCalendarApi.endpoint, + { + headers: { ...googleCalendarApi.headers, Authorization: `Bearer ${accessToken}` }, + } + ); + + return response; + }, + + post: async (accessToken: string, body: GoogleCalendarPostBodyType) => { + const response = await dallogApi.post(googleCalendarApi.endpoint, body, { + headers: { ...googleCalendarApi.headers, Authorization: `Bearer ${accessToken}` }, + }); + + return response; + }, +}; + +export default googleCalendarApi; diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts new file mode 100644 index 00000000..267f77f2 --- /dev/null +++ b/frontend/src/api/index.ts @@ -0,0 +1,7 @@ +import axios from 'axios'; + +const dallogApi = axios.create({ + baseURL: process.env.API_URL, +}); + +export default dallogApi; diff --git a/frontend/src/api/login.ts b/frontend/src/api/login.ts new file mode 100644 index 00000000..d36c1082 --- /dev/null +++ b/frontend/src/api/login.ts @@ -0,0 +1,40 @@ +import dallogApi from './'; + +const loginApi = { + endPoint: { + googleEntry: '/api/auth/google/oauth-uri', + googleToken: '/api/auth/google/token', + validate: '/api/auth/validate/token', + }, + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + + getUrl: async () => { + const { data } = await dallogApi.get( + `${loginApi.endPoint.googleEntry}?redirectUri=${location.href}oauth` + ); + + return data.oAuthUri; + }, + + auth: async (code: string | null) => { + const { data } = await dallogApi.post(loginApi.endPoint.googleToken, { + code, + redirectUri: location.href.split('?')[0], + }); + + return data.accessToken; + }, + + validate: async (accessToken: string) => { + const response = await dallogApi.get(loginApi.endPoint.validate, { + headers: { ...loginApi.headers, Authorization: `Bearer ${accessToken}` }, + }); + + return response; + }, +}; + +export default loginApi; diff --git a/frontend/src/api/profile.ts b/frontend/src/api/profile.ts new file mode 100644 index 00000000..223f41b9 --- /dev/null +++ b/frontend/src/api/profile.ts @@ -0,0 +1,32 @@ +import { ProfileType } from '@/@types/profile'; + +import dallogApi from './'; + +const profileApi = { + endpoint: { + get: '/api/members/me', + patch: '/api/members/me', + }, + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + + get: async (accessToken: string) => { + const response = await dallogApi.get(profileApi.endpoint.get, { + headers: { ...profileApi.headers, Authorization: `Bearer ${accessToken}` }, + }); + + return response; + }, + + patch: async (accessToken: string, body: Pick) => { + const response = await dallogApi.patch(profileApi.endpoint.patch, body, { + headers: { ...profileApi.headers, Authorization: `Bearer ${accessToken}` }, + }); + + return response; + }, +}; + +export default profileApi; diff --git a/frontend/src/api/schedule.ts b/frontend/src/api/schedule.ts new file mode 100644 index 00000000..a124936d --- /dev/null +++ b/frontend/src/api/schedule.ts @@ -0,0 +1,63 @@ +import { ScheduleResponseType, ScheduleType } from '@/@types/schedule'; + +import { DATE_TIME } from '@/constants/date'; + +import dallogApi from './'; + +const scheduleApi = { + endpoint: { + get: '/api/members/me/schedules', + post: (categoryId: number) => `/api/categories/${categoryId}/schedules`, + patch: (scheduleId: string) => `/api/schedules/${scheduleId}`, + delete: (scheduleId: string) => `/api/schedules/${scheduleId}`, + }, + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + + get: async (accessToken: string, startDate: string, endDate: string) => { + const response = await dallogApi.get( + `${scheduleApi.endpoint.get}?startDateTime=${startDate}T${DATE_TIME.START}&endDateTime=${endDate}T${DATE_TIME.END}`, + { + headers: { ...scheduleApi.headers, Authorization: `Bearer ${accessToken}` }, + } + ); + + return response; + }, + + post: async ( + accessToken: string, + categoryId: number, + body: Omit + ) => { + const response = await dallogApi.post(scheduleApi.endpoint.post(categoryId), body, { + headers: { ...scheduleApi.headers, Authorization: `Bearer ${accessToken}` }, + }); + + return response; + }, + + patch: async ( + accessToken: string, + scheduleId: string, + body: Omit + ) => { + const response = await dallogApi.patch(scheduleApi.endpoint.patch(scheduleId), body, { + headers: { ...scheduleApi.headers, Authorization: `Bearer ${accessToken}` }, + }); + + return response; + }, + + delete: async (accessToken: string, scheduleId: string) => { + const response = await dallogApi.delete(scheduleApi.endpoint.delete(scheduleId), { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + + return response; + }, +}; + +export default scheduleApi; diff --git a/frontend/src/api/scheduler.ts b/frontend/src/api/scheduler.ts new file mode 100644 index 00000000..f846ff22 --- /dev/null +++ b/frontend/src/api/scheduler.ts @@ -0,0 +1,33 @@ +import { SchedulingResponseType } from '@/@types/scheduler'; + +import { DATE_TIME } from '@/constants/date'; + +import dallogApi from '.'; + +const schedulerApi = { + endpoint: (categoryId: number) => `/api/scheduler/categories/${categoryId}/available-periods`, + + headers: { + 'Content-Type': 'application/json', + }, + + get: async ( + accessToken: string, + categoryId: number, + startDateTime: string, + endDateTime: string + ) => { + const response = await dallogApi.get( + `${schedulerApi.endpoint(categoryId)}?startDateTime=${startDateTime}T${ + DATE_TIME.START + }&endDateTime=${endDateTime}T${DATE_TIME.END}`, + { + headers: { ...schedulerApi.headers, Authorization: `Bearer ${accessToken}` }, + } + ); + + return response; + }, +}; + +export default schedulerApi; diff --git a/frontend/src/api/subscription.ts b/frontend/src/api/subscription.ts new file mode 100644 index 00000000..baa59ac7 --- /dev/null +++ b/frontend/src/api/subscription.ts @@ -0,0 +1,64 @@ +import { AxiosResponse } from 'axios'; + +import { SubscriptionType } from '@/@types/subscription'; + +import dallogApi from './'; + +const subscriptionApi = { + endpoint: { + get: '/api/members/me/subscriptions', + post: (categoryId: number) => `/api/members/me/categories/${categoryId}/subscriptions`, + patch: (subscriptionId: number) => `/api/members/me/subscriptions/${subscriptionId}`, + delete: (subscriptionId: number) => `/api/members/me/subscriptions/${subscriptionId}`, + }, + + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + + get: async (accessToken: string) => { + const response = await dallogApi.get(subscriptionApi.endpoint.get, { + headers: { ...subscriptionApi.headers, Authorization: `Bearer ${accessToken}` }, + transformResponse: (res) => { + return JSON.parse(res).subscriptions; + }, + }); + + return response; + }, + + post: async ( + accessToken: string, + categoryId: number, + body: Pick + ) => { + const response = await dallogApi.post(subscriptionApi.endpoint.post(categoryId), body, { + headers: { ...subscriptionApi.headers, Authorization: `Bearer ${accessToken}` }, + }); + + return response; + }, + + patch: async ( + accessToken: string, + subscriptionId: number, + body: Pick | Pick + ) => { + const response = await dallogApi.patch(subscriptionApi.endpoint.patch(subscriptionId), body, { + headers: { ...subscriptionApi.headers, Authorization: `Bearer ${accessToken}` }, + }); + + return response; + }, + + delete: async (accessToken: string, subscriptionId: number): Promise> => { + const response = await dallogApi.delete(subscriptionApi.endpoint.delete(subscriptionId), { + headers: { ...subscriptionApi.headers, Authorization: `Bearer ${accessToken}` }, + }); + + return response; + }, +}; + +export default subscriptionApi; diff --git a/frontend/src/assets/dallog_black.png b/frontend/src/assets/dallog_black.png new file mode 100644 index 0000000000000000000000000000000000000000..a6fff7453eff0166af2abe5a1a8bc13be2e19d33 GIT binary patch literal 7116 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�UPgkuv)7!QD zE5Av(8pR}!2dst?w;9VB*rYu;$p;9o{11SME3M^HXNd$Y7oTZuMRT1_PoE~6LX*vN zF@jD3Z(5yRTq70?J>RoTH=Ea%%(Pmc$g~>MkET^OEuu6B?D7t`O`ea%XYch?DwiTR zOQgT6nxjtMSMa~!fw`W7-wX%cDlbFkZXMXGeFzXH&A%!;gubzvf`|)!(gflPQkfo` z$^K-*(D!RV`Xdx-3i@83S8RI_;rbii~x|YgZnEVz^2R7)I+uXm8Q(YN%Lr~A;rM<%lGZAhXxD+sAEq!yOb_XtGOANRxb>s{$h=Z-$MHgX4OHgw~;VF$k{w! z=_MK!n@NA#zJi0t4a9X0!wnk|L63AkOjlF%+>g0d0cZNHNYj}l0&8a{WUUgJR*MIW zX%*Ep?BVMQ*bU2B7#OlBfAm-B&$FQ0Nulyw|7dS+JS3A!14^?y%l)np zplzA(NL)a$J_&2JbIm~W??l@w4}#)lu$ZPW#K8h8B(ho9OBUnMmg$|o7C6}T( z+yfpPmQ+LiIc&35Y`G3K3Z&v4^nCTb(mE#Qvr8jEv%UA#jPe2;wgBDN(bXkU6ZF1r zSsxO1ZOGcSH_dx4p0)b>y{a-Q0c-W*Kr*eO8U!gWO3-bQwhAW8Sv)piE%+%{qoQTS zl#>yR9ENT38AvM;DJI~94J;@@N9;DRSMQU-O4J-K7fU!Nn#~tWPm2e-dHK}5X)Im` zgOIt@45`lD>WlAJ)mLD|9!HQc;W5?kr}{py4yM(EU2S``H1-Xn;{$?=|8nCY|6Z!= z>Ff8XOK+pag!K#ye3kOujZWjwJtpIcw5%8-96^fLVktV?Q!QQNtJXgWpJOrTT?c+} zZzRP%aeJS9HD_2>{Hz$ASnUIMOkZS-Yu(I-B2X!0hYH)Ys#34Gs-tblCfvpx87&1JmlINBR!LL!)rh z{oQ{tDK|p(%HvQ2b!A(_!c*C|msIZvtXTUEdhULU^e2x)nhcMA1>zqrIH>5}F8?Q4 z4pzPxET*kf*qExGO#q}i*Geq<(^nzy>v1jK%B(o!8VFKt+gm-j$ISAu0%dcCglQG? zj6MJ{#g3s1P#E~Q&usWDmq%dhT^3J`@a>R~WjpwwU4J9gOf&`pV4y{c@=+pykWoq{ zVA=X!>)Kw9f~ahrGoAC~y9q>~rZg*=xv4KZ8H zPQ?EZ3z5b*n3dnxRnCpZLPn6%ie&N1qd|2|1(MeL?6SAF9$3!u{H+=_57mOCTOD~^ zsfqxq-Y_fGz-IM(_POdQy!rT_PJ6lui%o^kr_Wlw{AgCkCv6aC3Odw=2$uro9sX)o z7tS9^mqW|y9T7Ay`WBa^a#?k^B8&G`fMgS#7C$cNon%?O6rOZu;cV?ga1bOcbjsE_ zdppnE84s&0!pdhHq@%ri)3Pmd2FA>|dYQwAwiO=D0+}#kUSz)+zzem}9sL zL2;y4$&;FGhEYDOsMgY1emjD7=mF>a3w?qER%SBI9kgx$-u=7KCJBRt6^Ie^CSckA zj{XO4Y|Y{q^76a+cF@^~K@-;g+J@>^CkF{~(^{T+G%g^x>+U<%IWg@**`Z|awb!US z%LavM6}?{&Q;m?XRcr&qcDs#O)qrbRM@HVUM+L4$;FK+^RG z^Bt350wHH}Mz{0P+Egp$B`EXp;!02b1q|(X+4Viyy*3ran^r9d2pay7ARze9L1J3P zJX_SH`=AWgz4%$#)eYHB!aeNO28oHnOduV$Y|g)h82{Tzt+>iJGAH=QdGS6aR+S6S7vD5}H;w zB?Vb7#w}at!~&l|u()fPs2j8^Hc_jeiuAFlMYCtXyn2mRW@_QW=Zw0ntWH}}U zNUnu6R$#UFOgW)nz#=Uf@3qgfxuuVzRV-#}@Y6X@sBT~YByQmxnpP7Nm{#{BGp+JV zY@{=`>FuM0{T6v0qUrziTXi=Fozf3|PE;0BgCG|R04cuMXEEHxaC^X}R3Hzl8TMGx zO2Yo?qNr8;kwqiBl}&IIU>Rxa=Gpx+0|slB4hRj>)>PZ={zJMK;#jMQX*Dv=F)yF; zShr)-6U_XWu&J&E{&ov`^@@Z=G(o&2=q!l<1f?JmfYe0*0_{$9&V61gzEA!D(7) z_X=gO8w_qQ0D+0c6?ZfZlFGzlbUPEF&gQ;~e*hx(H$@cv5R0L*0jiEY&0;J<`WMqTt)>oM z(@J{=FtK*W`5^3W0w3i60{{U3|HZToe*gd~TS-JgRP9{}cpK%J4z$~DciTd_x-Bhj zq0oy$yKO1kyOb7ap%j)QJpwJq7D}N&IKvSGAp}AaayUMunUU?-NlfkpC(b33*p^0; z?KqBXNu$FTIcX?eda%1?_I-bO933MciLA@=^UO24EY0z~|98FL_f6{ZRD8tmuDU4X zvi(ML%L_G^^s1)HJponvZ_OqDFSd8E>9O0(wkP#PFa9Cx+v=L9^sW|*I5ux{rM`L9 zEWNt?kztR^R`#&IX~lE;-lhuie>l&$bBjIC%sjT~2ls{bKfeBY*0)3ZB6_fKkv?$G z@ATJxe`nmbV3$C~)nnh zPM*$(C5?prcaFn27%(e z{T6*;0yq}`gucFGzT?_{rLyddL8{vtKPb8QMT?+Z_ur* z@q6T_g00duLAUk%Znf%EO>21Xq)unuhc!>dzjroUrv#nSi$Kw4O_f3+kMaft?0>K& znOJL3P45dsl0jrtjSqHGvZsx$$@or4Ru?R)$>ilbYLdwdRyR@AcN7 zqBSgd@1&0A>N8+weJP~MKi53+te{J72zulI{^1BrEmMH3UpiyxWQ>!_r+uCEu_R#~ zj|)HSce<d`N7MEj!`=s&5^b$8Y%Wc;ULSDFGvJ`F*W5}s_D2FPT0&0AM{v%=66daL62=U(B#EUeI4815!w1j z;tk%q`uV&ENZH)`vTADe`*-9NKn`u)5~n@3{9{x5Or8{ zDSyKOB>{xdp?uO7sg89?6fZGw4!Wcq|7!%H8n8zrU}o_7j?O$p<|Ih#smxsjFQ%H|VOl9Dc`x5STiI zdRt*)9RxuBY6cJ|key2C^6m9TgM#DRj(O~vksEO?QMG3?YEjI1joU&%GC88>35(2 z{^C=uTf?d}$M06A>}$1sg}FhPSO22d10X~S5!2l!{f9u1yJ{g!EIaJ72);aJHk}!4 zV-+rhJ=p;xUCi-(KeA`7exR=liNnKL1IxlpV4Z z2au)XY+8-xdKjOfPZE2tnDFgPM>0{B4j^*73Ouaoh}d4;)m%21MPKklKt(< zCmFb6h*#J36DE1$`j%(Q{Ik6FQW4@60MYICcRL#vd~lzq`m6vWSuKcUAtoI1*mh~E z(g)(AQ5O<-(MTpsC18c{Iw=sw&B?|-*jlF_+TW8g&0$d955D%A-np$jtLL7<>-{$U z?T~{Jtm-L!ZPHg+{$WJ0bbZnZlNjsmXsrI%-P_8}^m^p42iuq29C6v^`9OO|h&P0T zeG{80X|WRKNFjhB)j80N%;3!$`YmdXY=FZ63A6m!IPWa{=AXhIWlf+>dK!F0)PYo? z4rINk1A+E9and9JgrTgKmbvfK+8RzDK!}T>F62UH3P;@&b<}a-f5(>Ohb)&<^Jr2D zUB11(>y^0~6_%i<8WF5e#x;il4({pmWI13_n2HAk!#=?^rGS>W;vQ13(& z;%yH{j2iyjLOK0dU+H|}2NnedNz>_~+F6&dvPevfsg zzu7ujxEIZ<&gh4`LAOIV=uv=7I)N`RqaMTu22fpyn%v16{xRsZqSFKX5V!RRE|94o zvQ&C(LI8w-vMzi;-?MXkB0T$h_vDQBFu)Sovt~p=%U9q4AP4+zd0EJ5d$ikCepM&( zfOIqHZioyz!D0Nx`XT&acWdR9Avi96m%L0+-(AuHm|K6vPZXj;e@=5NT|Noz{lwKfSDv#pco;#;%lEk0hul>w{ccN1$YHrQyz^-OL--%e zf5iUjc!7x#5*PjU^`~{WC|``I)}JAX@`T@|tOV6$w_mlg1eXEQzd&?2_H1VTz+t8#D9VYR`(zK`@O(ya6&pj*Y}KVkvG$7C*g z>+9ch1SPnlXx&|Psn2Eo1LMT-KO6u@Un(}=paR@EqDiAHAN`DKr!o2cW zpYpX-T;JbXcL7->N#WgOfJDQ+tfV3z#kbRP0nIFSn?rvr(%;(wghPyG;c=3gwP9I) zHTkT=%P98;GRMz2x{Wv;#6=i7cv3qaz6knQIPH1&m?l46)ws=DaBHzLsPL%W@CeFhq2FTeI>xx2*Y9i-kG9AUKEQ9@1K3^h%W1 zCIZ6jIX@_ph9EB<39*RE_ML#c;UyC7@=R)(m~Xjj^a z@VR>B$+)>B#x>*0n<#+!@H`3(^QPMzA8-#g!S|TubJ>2$;wWF! z!jIZxs|U>hk@y+3KCj)zzUC74P;ATAqKV)UVZS5G&SzlK-F>@lrft@Xq zIJYELNZ7d~9{@4dV`PbN?0M$Urm#A!cOKtVPR221e|;vl~a_idp)>{R0K5K?tIpODw9=UjlArDZ2GM z5pi0sHk@hJb+ zSUz^4r5*XAX!WVHXy#NG8Mkgl@i}aIZ-(xI*S|ZKVcWz&!M?${5 z0Ly`XU(zCJESAT*XWs+*v`j*Cx^h)n@~|DgMlE9U8$r!eRqAiC{+`W9ysf35W@I*| ztakLF-BdtI)FOKkA`5z|ZpKXY7vYO+U_=WWYDLVo2xpg%MJS4&H3yZgqC{`pP^0hm z?9vDNGv9G>q`&|h-32{M%!G8*uQ3G9mZB`H4}A6?&6>oxXREOCRXV9&MhZ8rTieWCc%p4N*^_5oi)^ z!c?i1)zfuy_>T2D(Tq3y-Bx>PAAID6K8NK_bWHrd;d^kj^OzHZ1{hN94K1QMqYf1~nEED0nUe#jERHOk(pgV3CR35dD{> zRWIlpR#|oXmKD0&u~qj1JmFxszW=rUw3}&SwqeLEDh@eyj2&NS4z+B2N$}7(R*h!S zGWZF~;`A>Tti#!j#B`#}`JC3b5lE_I-UCw$t_`;>yO1+*IvQu2*PN#k{L#KX6wyLS zZa?xO9JC&3l`n(eagX0&vA`FxvyU&Tw7~RD?gUReQo(9kXn=@4A5N&Eknm=4Ix9_u zriOWXJLWkWH;`>iv)MVgB?YdDTk_mGw zpReg?j-6p%6B$E0QQH=@tC~p{KxN&Lt+XL-G7P8Y> z_hFyYazVJ&dKI+CLtsiOh-T4Y55xZBY{Mii!b&@~%NflD$d}L1$>fdNv3|~o=f$~> z{Om+-a*>Yvx(35MO~9dp`qYCiJd2nVL@$L)Ll~z9((Nhi))Os=!o;X?{sR?0%Jc;? zMe>j+#lijx2lp&$*H;1@t=R7aXiOb;2SRe`xJbG39dychvpC~>G{?jl^EAgMT4ZlD z*Jd=I5hJ^iC`<;3^mjy3>-?%RjgEQ5Rr{qbBn4R=J8ZX-bOXi+`;vmvtyZ1tb4#B= zYoJS^)_%u^*8oZ*K+*+Mi@A}cqAH$PA=QjHHbrCbqt%SY_~MD!Y;QCG80j;qMp1~K z2`GjNx7lvuD7A<~`K0KrFxYFCE<(Dokn;Nshn?soN;z8^NyDGP#Cjy?Q4~zbMa_r= zQ%f>6wHVbh%ZQG2adPo}hBOKK(MIWv9$KRXR4$%#wvLHm<7CYNUsnvmQA#0mcZWT; zA8{b9-&1$mxI5;l5Mns&OuB?H0JPgt_DMG2g5Xaadvgl;U)IyseoWva=w{0G%C=G~ebpjv7W>dc!7qf z7Q(*wBT-{PW}SmT18~?`eV9Z;p1lf$S%6LN(AL z%{YND@+6#6l@$7=+RXPMJme9zZ7&_tBx@0dv@L#EP*0ddGzNh(kvLqgWIP2eU%xwL z>m*e^qLIa+D)%EmbXB;u>H>~%=3<>y(<&JxQ8FWA&Aodtp{Kdx9H#d{JGq$&n?ZlX zjVMDe0A%Wqa8(_xlbfqInu)+2!}RB+307;FmiO8+lh>ctMUvKlCV|QRHe_iz_K*7< z(hVV(@-?wIW0B@!%~V8IO)E2yFoDQ2Ms25jdc>oAmZ4u!1FXt(5%OIRGi!IW>703q zE(M6Zo=o0=@%Sy0@)&I)4~u*()-e<_=VPanu%8&Mi$s7Rh42rU?pr~Wok70?TKQXu znx4(YnTvF^3B&j&1QRhZ((@D8_?N29>n(jhRUL=2bM`biG4}u*i{Y$q0?PLyPjUb! zZJHk;6SNQ_TjK$|iS(UIDyNz9tOQuxavLen%?s2r3HNsj!aS>$RTJ2dAB>f34N_OSWDCnQ;j-?Epeh zP?J-DgpnUs<`tR{6wk-}p30@eP7nSQWJRVW5^yNj_O{x-VmRfjFV7SaF_p}Z`E`QC zaM~G{M(Fq4u*>!xKO$SOAD_X=E`_tz41Xj9jba-9r6v&|8AVNUQRvKtE?=$-HODZc zwcmow)IuH}MK_%rIFXjjjYzw4axOdNBZ1}wBZHe0A&-Qqv?<10Ri^zPs zlx0{2T3CsQljl⋘+k*8R~oygyL}~1DW}vVazJfNF))?jX0Ib-7fhXy^L7()_#Z< zN=`fFBO%0a+C^jw$s5*>c%9{+54G8TfJEYL=&AJ_K(QQ}BnlF@WLk-c&xj$J%rg`P zELp(v`I@jkhULk;$H|+7UTe|{CY;d|`WH0QS)@l=6G@194Gsf521wWgmE*7VELTV~~WHyAdk`1S9Q-K!OY>Se74Pjs(3@zkqJrKLj{#LtA=k zl4aPM;g6tAq?mzdYHqQ#wNPmC6B#V)mp)^f1Qb9~F)_d`zl<=h07n3<4EnIlbr2l1 zNSJeDgY}VQFK^zDMz(Y@b0iidqR>jU-HN#Ovq&b^!Q9#f$DJYIKjC7Tf`n}+x%y0G z0n68OXXu{H0s#b5vr2vo;8;cq3C${M4g(w=jnQ0(07oW^pE8BAM39jEi*DPW54o&A z2(-$-!DzQXePG~!k! z`{8p4vkIDH2pqY_u}U|15<$W&D678^vilyoQQQ<#ktcB}iXRzwXp>HWslpN0&k)+8Eo4jYHn<^iK)lkViM;L8F8Lgs2ZulI+tSWSH7+{NnB-;1^ z8PN9!o2$;YNkHZ~IDMAUsyLsc5X~wB6p4ESB zZFgaj{Hafsz8XOika-SKPmX9+!TKDDph$dfM7O9u*)x?Z23HQRqvGp9Rrwh%9NL<) zeWX$k4p$SLcRwy@$}li5ek<6(Og!bo%38?BxVq-4r2fRQ;K03o8^dfi!K9kOa6h&y z$gT1=Rh-2Pxse29D7T=ZRf!-N>A3(2FM@7=H&+aD`oYlrgysMX*sc6H=(PM7FCs{4 z)qWS4z@W<`415@xC8b}&jwu_V){BhbqdA96fstCn0lzR742h4S?f)2XS(c#L`4fJ( zbQ3A-UCq_!v(W;pQb`|)Ndgv5D0d_Od;fCGg1BfbhC#(x*eze@hZBbj zj9LIBk=S=D8VXKHbToj%mY~H9{P+Qd?Ra84*c;b9ZqMYOQ)+}2Ff*hocc4u6r2mZA)N7v6vj;{{iA$?&b#54usV??ykrBjTQ@YvfA~e4Q~6Fh@VF5&wg$ZJ24FZ) z)O2n;ULSHEnLAlq_w0EH7r`Q-;P#4y7N*rgyQ|&h#;RRk@_Kv|Gk zDZ{o3K-q~Ki0Qkjs}*B!FlpD@*St{7p~NH_*>)$zkeXzm&U(bW^tq31{jG7^R>{Q1SP|A7Pz|j2pHdd(@gp8usk$I4Yn}tL#|+qL|QH$Q@S5vSq8( zRC70Qy%Irz&W0jt=tW0$E0_+`0g^l5kYD3hE#GFXPY4SaB9W)i*C=X@d^pbaG=T$ka-GTL=NG|MbJrX#*bKw#V709J3hHu9#<7xQ^p-lu$C7L&(+2w7qVnlV-ne6h72+G&V@j=vO3L;!+ za4aep(Y#JtjaAPM4YLP5@m!Q=D0XO>@2m@N7isr|g#f`^IMLZud zJZvUJG{xx5a*z%y9T^VIrnX>?r>g7`eKjTuvwE-1UOHASVE~Ax=zjQcTXD_iV|$#Y z6oMhw`Bcj#G->-fE6(v?!WjJwQq!5><*4PT6JGQ+j!mM+=f4K2Dj+@rD0W1<7&S3~ zBIZWG^a?mp2k(+@z>U8X(U!;Ycpd;!h39Lry$T^}itGrf2JZ za+KJ-y1M+4v^D1@2xumeYsP@WCV-PY#K=FRhD$u2F+C?^+AZ&a=~N3qynsEEvbRM? z?8)CnLED%5pfLyxnzpR=3m{V=`y(55ov zRLueimN_1JErF{|IpMhSLbA44w2c74*042*2e!%;_r^|{I7T)y#VC()E2lN zCD`AC=f4VhY(D|$zSX(YazRgr+77P4GlPr8t)Qa+Clj%pyTOv^kf-`A7PT=4#h?gHsUk4O zU5v;72MnMvlENFx8_OHa8_gRIq4;=6tvXM0TffYbU#^yLM1KLnzEgk&>{B@NivXG` ze0C*X--O2=IQgWsya~ZdSqhwk;9!Wm7r<};7_~T`mjH;Tqo9y)^U?G~^bfCp!^cs2 z9@{z8x-?VSiH5T%+24#lMoeU?s4>Q0jK}{MjL>lO#^MIU{9^41%`Y-a#e79{qUYcr z6lr~hV8MVEm=@N_B1aVI z4MN%sZDWHr6hNPa$LO*z--F}-4IOR)3XlltTacZA_dXtME<2;$4*vpLms~iStK>}; zo$v9N)vW&lok-qj5r$-(9$JN=NfzwTSD^{Uu?_4cOwHnh@Sv9ANAMl4fw2CHksN?# zDjq*6%rQ0`aLE=3c~}+PA5$;niR~bU~2D&h48dkKh0q+Y$_nK#Q+SzaWw@_b6RhK zAl?P7^8f@Bp-_|vyX4tuqq+dAbva&Rk`fw6S_&a$vDX@yXpPWNI{`LmAJMpAHyqcRrn4o{=W?Gzl84urCfR%AfATz?hAMn zWVSgY67%~&{b27geC`{9ZO~f%IWiBJFVfmDE)S6?n4Sn^a9jn#v{*E_c*JOnq8XK0 z(agPHivtU6Yy&LREX=Dg0p|BqoHkg;a8?k2VbeQm81jxFyx*mk!K!@~1oS!xCUP!l zh6l$VLVPy_n`5ybhR{BS$J4sORyS7x4MnLmR`ZQ3k+FdLr~&-ZYK~3R5E927LslQ3dpzpZtFz=%!M=?2?*we2>5=?Y+?BT%_nBN zV?hz^Ai~PdVsbC{q%=p4D4KYrkVMTe;oQSH9kj(6!2w_@PNMS~gjwQmDLa!U89@_` z_$Xi0FnUfTDBZSiGNjBZItcUS5acUa%R*r$MFYMJqG!rv^2Zn)Fg8AifKR3dAmGsC z;98%P8q4|o3hGvPH8%Tn|^q>hsC!c~wTb&tXnq&lxpt^wC&6S#6Iy&L-5SH(9I)eH55m-<`wO%}m#5+* z0;u+i(|X}U5DfGqXet^Sf*NrM5QE$(u@Dqe1L%AdutGcLM8HJ(Mp4jE2x+cR(-1fm zP_Ehp4}})OdNLzsG;#b8=6wJXS9nH@DBy7$x=r$RzE9JR+KS+#6D>m2_&zcPXeJ3D zAR;GdHqkJ)0K;Il-ib#wMA39AG!_J1{D`81o%2#O z0S_<=oWUY50%tI^JY=c~Gl#q-V-tl$ literal 0 HcmV?d00001 diff --git a/frontend/src/assets/dallog_white.png b/frontend/src/assets/dallog_white.png new file mode 100644 index 0000000000000000000000000000000000000000..074312148c7026e95c4bc0d6f6d020b2a83984db GIT binary patch literal 7447 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 literal 0 HcmV?d00001 diff --git a/frontend/src/assets/how_to_use_1.png b/frontend/src/assets/how_to_use_1.png new file mode 100644 index 0000000000000000000000000000000000000000..19330c39631c84a4978a42e2e7c6f8df95f1b664 GIT binary patch literal 175449 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�)I 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%0Drj8v15zx@xG&-7-Pv1;~Qgte8>nW$BVrJ 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�&}%=IM3WJ;-PiG*Z6u&RF%YDNk^N4=He=Wi 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

29Q?sivk1K^nLXDe5ZH9Z#? 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 literal 0 HcmV?d00001 diff --git a/frontend/src/assets/how_to_use_3.png b/frontend/src/assets/how_to_use_3.png new file mode 100644 index 0000000000000000000000000000000000000000..7dcb9867dfba6121086a923377d1af5e735843c0 GIT binary patch literal 215547 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

B6~<(o<=W`;eKsoE#cZf&H0^9g`U7 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.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 + +const Template: ComponentStory = (args) => + ); +} + +export default Button; diff --git a/frontend/src/components/@common/ErrorBoundary/ErrorBoundary.tsx b/frontend/src/components/@common/ErrorBoundary/ErrorBoundary.tsx new file mode 100644 index 00000000..4691ca06 --- /dev/null +++ b/frontend/src/components/@common/ErrorBoundary/ErrorBoundary.tsx @@ -0,0 +1,49 @@ +/* eslint-disable react/display-name */ +import { AxiosError } from 'axios'; +import { Component } from 'react'; + +import useSnackBar from '@/hooks/useSnackBar'; + +import { ERROR_MESSAGE } from '@/constants/message'; + +interface Props { + children: JSX.Element; + openSnackBar: (text: string) => void; +} + +interface State { + hasError: boolean; +} + +export const withHooksHOC = (Component: any) => { + return (props: any) => { + const { openSnackBar } = useSnackBar(); + + return ; + }; +}; + +class ErrorBoundary extends Component { + public state: State = { + hasError: false, + }; + + public static getDerivedStateFromError(): State { + return { hasError: true }; + } + + public componentDidCatch(error: unknown) { + if (error instanceof AxiosError) { + this.props.openSnackBar(error.response?.data.message ?? ERROR_MESSAGE.DEFAULT); + return; + } + + this.props.openSnackBar(ERROR_MESSAGE.DEFAULT); + } + + public render() { + return this.props.children; + } +} + +export default withHooksHOC(ErrorBoundary); diff --git a/frontend/src/components/@common/Fieldset/Fieldset.stories.tsx b/frontend/src/components/@common/Fieldset/Fieldset.stories.tsx new file mode 100644 index 00000000..901a0526 --- /dev/null +++ b/frontend/src/components/@common/Fieldset/Fieldset.stories.tsx @@ -0,0 +1,27 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; + +import Fieldset from './Fieldset'; + +export default { + title: 'Components/@Common/Fieldset', + component: Fieldset, +} as ComponentMeta; + +const Template: ComponentStory = (args) =>
; + +const Primary = Template.bind({}); +Primary.args = { + id: 'primary', + labelText: 'primary', + placeholder: '입력해주세요.', +}; + +const DatePicker = Template.bind({}); +DatePicker.args = { + type: 'datetime-local', + id: 'date-time-picker', + labelText: '일정 시작', + placeholder: '입력해주세요.', +}; + +export { Primary, DatePicker }; diff --git a/frontend/src/components/@common/Fieldset/Fieldset.styles.ts b/frontend/src/components/@common/Fieldset/Fieldset.styles.ts new file mode 100644 index 00000000..8c7ea4c3 --- /dev/null +++ b/frontend/src/components/@common/Fieldset/Fieldset.styles.ts @@ -0,0 +1,45 @@ +import { css, Theme } from '@emotion/react'; + +const fieldsetStyle = ({ flex }: Theme) => css` + ${flex.column} + + align-items: flex-start; + gap: 2.5rem; + + width: 100%; + height: auto; + + font-size: 4rem; +`; + +const labelStyle = ({ colors }: Theme) => css` + padding: 0 1rem; + + color: ${colors.GRAY_800}; +`; + +const inputStyle = ({ colors }: Theme, isValid?: boolean) => css` + padding: 3rem; + + width: 100%; + border-radius: 8px; + border: 1px solid ${isValid === false ? colors.RED_400 : colors.GRAY_400}; + + font-family: inherit; + font-size: inherit; + + &:focus { + outline: none; + border-color: ${isValid === false ? colors.RED_400 : colors.YELLOW_500}; + box-shadow: 0 0 2px ${isValid === false ? colors.RED_400 : colors.YELLOW_500}; + } +`; + +const errorMessageStyle = ({ colors }: Theme, isValid?: boolean) => css` + display: ${isValid ? 'none' : 'block'}; + + font-size: 3rem; + color: ${colors.RED_400}; +`; + +export { errorMessageStyle, fieldsetStyle, labelStyle, inputStyle }; diff --git a/frontend/src/components/@common/Fieldset/Fieldset.test.tsx b/frontend/src/components/@common/Fieldset/Fieldset.test.tsx new file mode 100644 index 00000000..1f3dc6ef --- /dev/null +++ b/frontend/src/components/@common/Fieldset/Fieldset.test.tsx @@ -0,0 +1,25 @@ +/** + * @jest-environment jsdom + */ +import { composeStories } from '@storybook/testing-react'; +import { render, screen } from '@testing-library/react'; + +import * as stories from './Fieldset.stories'; + +const { Primary, DatePicker } = composeStories(stories); + +test('기본 입력 필드가 출력된다.', () => { + render(); + + const buttonElement = screen.getByText(/primary/i); + + expect(buttonElement).not.toBeNull(); +}); + +test('날짜 선택을 위한 입력 필드가 출력된다.', () => { + render(); + + const buttonElement = screen.getByText(/일정 시작/i); + + expect(buttonElement).not.toBeNull(); +}); diff --git a/frontend/src/components/@common/Fieldset/Fieldset.tsx b/frontend/src/components/@common/Fieldset/Fieldset.tsx new file mode 100644 index 00000000..db0eb11d --- /dev/null +++ b/frontend/src/components/@common/Fieldset/Fieldset.tsx @@ -0,0 +1,62 @@ +import { useTheme } from '@emotion/react'; + +import { FieldsetCssPropType } from '@/@types'; + +import { errorMessageStyle, fieldsetStyle, inputStyle, labelStyle } from './Fieldset.styles'; + +interface FieldsetProps extends React.HTMLAttributes { + type?: string; + value?: string; + defaultValue?: string; + cssProp?: FieldsetCssPropType; + labelText?: string; + autoFocus?: boolean; + refProp?: React.MutableRefObject; + disabled?: boolean; + onChange?: (e: React.ChangeEvent) => void; + isValid?: boolean; + errorMessage?: string; +} + +function Fieldset({ + type = 'text', + id, + cssProp, + placeholder, + value, + defaultValue, + autoFocus, + refProp, + disabled, + onChange, + labelText, + isValid, + errorMessage, +}: FieldsetProps) { + const theme = useTheme(); + + return ( +
+ {labelText && ( + + )} + + {errorMessage && {errorMessage}} +
+ ); +} + +export default Fieldset; diff --git a/frontend/src/components/@common/ModalPortal/ModalPortal.styles.ts b/frontend/src/components/@common/ModalPortal/ModalPortal.styles.ts new file mode 100644 index 00000000..0c3bb43a --- /dev/null +++ b/frontend/src/components/@common/ModalPortal/ModalPortal.styles.ts @@ -0,0 +1,27 @@ +import { css, Theme } from '@emotion/react'; + +import { TRANSPARENT } from '@/constants/style'; + +const dimmer = ( + { colors, flex }: Theme, + isOpen: boolean, + dimmerBackground?: typeof TRANSPARENT +) => css` + ${flex.row}; + + position: fixed; + top: 0; + left: 0; + z-index: 30; + + width: 100%; + height: 100%; + + background: ${dimmerBackground !== undefined + ? dimmerBackground + : isOpen + ? `${colors.BLACK}bb` + : 'transparent'}; +`; + +export { dimmer }; diff --git a/frontend/src/components/@common/ModalPortal/ModalPortal.tsx b/frontend/src/components/@common/ModalPortal/ModalPortal.tsx new file mode 100644 index 00000000..45f0e7b1 --- /dev/null +++ b/frontend/src/components/@common/ModalPortal/ModalPortal.tsx @@ -0,0 +1,41 @@ +import { useTheme } from '@emotion/react'; +import ReactDOM from 'react-dom'; + +import { TRANSPARENT } from '@/constants/style'; + +import { dimmer } from './ModalPortal.styles'; + +interface ModalPortalProps { + isOpen: boolean; + closeModal: () => void; + children: JSX.Element | JSX.Element[]; + dimmerBackground?: typeof TRANSPARENT; +} + +function ModalPortal({ isOpen, closeModal, children, dimmerBackground }: ModalPortalProps) { + const modalElement = document.getElementById('modal'); + + const theme = useTheme(); + + if (!(modalElement instanceof HTMLElement)) { + return <>; + } + + const handleClickDimmer = (e: React.MouseEvent) => { + if (e.target !== e.currentTarget) { + return; + } + + closeModal(); + }; + + const element = isOpen && ( +
+ {children} +
+ ); + + return ReactDOM.createPortal(element, modalElement); +} + +export default ModalPortal; diff --git a/frontend/src/components/@common/PageLayout/PageLayout.styles.ts b/frontend/src/components/@common/PageLayout/PageLayout.styles.ts new file mode 100644 index 00000000..4e368bad --- /dev/null +++ b/frontend/src/components/@common/PageLayout/PageLayout.styles.ts @@ -0,0 +1,14 @@ +import { css } from '@emotion/react'; + +const pageLayout = (isSideBarOpen: boolean) => css` + overflow-y: auto; + position: relative; + + height: calc(100vh - 16rem); + margin-top: 16rem; + margin-left: ${isSideBarOpen ? '64rem' : '0'}; + + transition: margin-left 0.3s; +`; + +export { pageLayout }; diff --git a/frontend/src/components/@common/PageLayout/PageLayout.tsx b/frontend/src/components/@common/PageLayout/PageLayout.tsx new file mode 100644 index 00000000..15f2f439 --- /dev/null +++ b/frontend/src/components/@common/PageLayout/PageLayout.tsx @@ -0,0 +1,17 @@ +import { useRecoilValue } from 'recoil'; + +import { sideBarState } from '@/recoil/atoms'; + +import { pageLayout } from './PageLayout.styles'; + +interface PageLayoutProps { + children: JSX.Element | JSX.Element[]; +} + +function PageLayout({ children }: PageLayoutProps) { + const isSideBarOpen = useRecoilValue(sideBarState); + + return
{children}
; +} + +export default PageLayout; diff --git a/frontend/src/components/@common/Skeleton/Skeleton.stories.tsx b/frontend/src/components/@common/Skeleton/Skeleton.stories.tsx new file mode 100644 index 00000000..8ca8c19b --- /dev/null +++ b/frontend/src/components/@common/Skeleton/Skeleton.stories.tsx @@ -0,0 +1,20 @@ +import { css } from '@emotion/react'; +import { ComponentMeta, ComponentStory } from '@storybook/react'; + +import Skeleton from './Skeleton'; + +export default { + title: 'Components/@Common/Skeleton', + component: Skeleton, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ; + +const Primary = Template.bind({}); +Primary.args = { + cssProp: css` + width: 100rem; + height: 20rem; + `, +}; +export { Primary }; diff --git a/frontend/src/components/@common/Skeleton/Skeleton.styles.ts b/frontend/src/components/@common/Skeleton/Skeleton.styles.ts new file mode 100644 index 00000000..d8e6b39d --- /dev/null +++ b/frontend/src/components/@common/Skeleton/Skeleton.styles.ts @@ -0,0 +1,31 @@ +import { css, Theme } from '@emotion/react'; + +const skeletonStyle = ({ colors }: Theme, width: string, height: string) => css` + @keyframes skeleton { + 0% { + background-color: transparent; + } + 25% { + background-color: ${colors.GRAY_100}; + } + 50% { + background-color: ${colors.GRAY_200}; + } + 75% { + background-color: ${colors.GRAY_300}; + } + 100% { + background-color: transparent; + } + } + + display: inline-block; + + width: ${width}; + height: ${height}; + border-radius: 4px; + + animation: skeleton 2s infinite ease-out; +`; + +export { skeletonStyle }; diff --git a/frontend/src/components/@common/Skeleton/Skeleton.tsx b/frontend/src/components/@common/Skeleton/Skeleton.tsx new file mode 100644 index 00000000..e9c0bd8a --- /dev/null +++ b/frontend/src/components/@common/Skeleton/Skeleton.tsx @@ -0,0 +1,21 @@ +import { SerializedStyles, useTheme } from '@emotion/react'; + +import { skeletonStyle } from './Skeleton.styles'; + +interface SkeletonProps { + cssProp?: SerializedStyles; + width?: string; + height?: string; +} + +function Skeleton({ cssProp, width = '100%', height = '100%' }: SkeletonProps) { + const theme = useTheme(); + + return ( +
+
+
+ ); +} + +export default Skeleton; diff --git a/frontend/src/components/@common/Spinner/Spinner.stories.tsx b/frontend/src/components/@common/Spinner/Spinner.stories.tsx new file mode 100644 index 00000000..990d23b5 --- /dev/null +++ b/frontend/src/components/@common/Spinner/Spinner.stories.tsx @@ -0,0 +1,13 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; + +import Spinner from './Spinner'; + +export default { + title: 'Components/@Common/Spinner', + component: Spinner, +} as ComponentMeta; + +const Template: ComponentStory = () => ; + +const Primary = Template.bind({}); +Primary.args = {}; diff --git a/frontend/src/components/@common/Spinner/Spinner.styles.ts b/frontend/src/components/@common/Spinner/Spinner.styles.ts new file mode 100644 index 00000000..3c9e2ae9 --- /dev/null +++ b/frontend/src/components/@common/Spinner/Spinner.styles.ts @@ -0,0 +1,45 @@ +import { css, Theme } from '@emotion/react'; + +const spinnerStyle = ({ colors }: Theme, size: number) => css` + position: relative; + display: inline-block; + + width: ${size}rem; + height: ${size}rem; + + & div { + position: absolute; + display: block; + + width: ${size * 0.8}rem; + height: ${size * 0.8}rem; + margin: ${size * 0.1}rem; + border: ${size * 0.1}rem solid ${colors.GRAY_700}; + box-sizing: border-box; + border-radius: 50%; + border-color: ${colors.YELLOW_500} transparent transparent transparent; + + animation: loading 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; + } + + & div:nth-of-type(1) { + animation-delay: -0.45s; + } + & div:nth-of-type(2) { + animation-delay: -0.3s; + } + & div:nth-of-type(3) { + animation-delay: -0.15s; + } + + @keyframes loading { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } + } +`; + +export { spinnerStyle }; diff --git a/frontend/src/components/@common/Spinner/Spinner.tsx b/frontend/src/components/@common/Spinner/Spinner.tsx new file mode 100644 index 00000000..769b695c --- /dev/null +++ b/frontend/src/components/@common/Spinner/Spinner.tsx @@ -0,0 +1,22 @@ +import { useTheme } from '@emotion/react'; + +import { spinnerStyle } from './Spinner.styles'; + +interface SpinnerProps { + size?: number; +} + +function Spinner({ size = 5 }: SpinnerProps) { + const theme = useTheme(); + + return ( +
+
+
+
+
+
+ ); +} + +export default Spinner; diff --git a/frontend/src/components/CategoryAddModal/CategoryAddModal.stories.tsx b/frontend/src/components/CategoryAddModal/CategoryAddModal.stories.tsx new file mode 100644 index 00000000..91a4003d --- /dev/null +++ b/frontend/src/components/CategoryAddModal/CategoryAddModal.stories.tsx @@ -0,0 +1,12 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; + +import CategoryAddModal from './CategoryAddModal'; + +export default { + title: 'Components/CategoryAddModal', + component: CategoryAddModal, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ; + +export const Primary = Template.bind({}); diff --git a/frontend/src/components/CategoryAddModal/CategoryAddModal.styles.ts b/frontend/src/components/CategoryAddModal/CategoryAddModal.styles.ts new file mode 100644 index 00000000..e5546388 --- /dev/null +++ b/frontend/src/components/CategoryAddModal/CategoryAddModal.styles.ts @@ -0,0 +1,78 @@ +import { css, Theme } from '@emotion/react'; + +const categoryAddModal = ({ colors, flex }: Theme) => css` + ${flex.column} + + width: 120rem; + height: 90rem; + padding: 12.5rem; + border-radius: 12px; + justify-content: space-between; + + background: ${colors.WHITE}; +`; + +const title = ({ colors }: Theme) => css` + font-size: 8rem; + font-weight: bold; + color: ${colors.GRAY_700}; +`; + +const form = ({ flex }: Theme) => css` + ${flex.column}; + + width: 100%; + height: 100%; + justify-content: space-between; +`; + +const content = ({ flex }: Theme) => css` + ${flex.column}; + + width: 100%; + height: 100%; + + justify-content: center; +`; + +const controlButtons = ({ flex }: Theme) => css` + ${flex.row} + + align-self: flex-end; + gap: 5rem; +`; + +const cancelButtonStyle = ({ colors }: Theme) => css` + padding: 2rem 3rem; + box-sizing: border-box; + border: 1px solid ${colors.GRAY_500}; + border-radius: 8px; + filter: drop-shadow(0 2px 2px ${colors.GRAY_400}); + + background: ${colors.WHITE}; + + font-size: 4rem; + color: ${colors.GRAY_600}; +`; + +const saveButtonStyle = ({ colors }: Theme) => css` + padding: 2rem 3rem; + box-sizing: border-box; + border-radius: 8px; + filter: drop-shadow(0px 2px 2px ${colors.GRAY_400}); + + background: ${colors.YELLOW_500}; + + font-size: 4rem; + color: ${colors.WHITE}; +`; + +export { + cancelButtonStyle, + categoryAddModal, + content, + controlButtons, + form, + saveButtonStyle, + title, +}; diff --git a/frontend/src/components/CategoryAddModal/CategoryAddModal.tsx b/frontend/src/components/CategoryAddModal/CategoryAddModal.tsx new file mode 100644 index 00000000..808f1147 --- /dev/null +++ b/frontend/src/components/CategoryAddModal/CategoryAddModal.tsx @@ -0,0 +1,93 @@ +import { useTheme } from '@emotion/react'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useMutation, useQueryClient } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import useValidateCategory from '@/hooks/useValidateCategory'; + +import { CategoryType } from '@/@types/category'; + +import { userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; +import Fieldset from '@/components/@common/Fieldset/Fieldset'; + +import { CACHE_KEY } from '@/constants/api'; +import { CATEGORY_TYPE } from '@/constants/category'; + +import categoryApi from '@/api/category'; + +import { + cancelButtonStyle, + categoryAddModal, + content, + controlButtons, + form, + saveButtonStyle, + title, +} from './CategoryAddModal.styles'; + +interface CategoryAddModalProps { + closeModal: () => void; +} + +function CategoryAddModal({ closeModal }: CategoryAddModalProps) { + const { accessToken } = useRecoilValue(userState); + + const theme = useTheme(); + + const { categoryValue, getCategoryErrorMessage, isValidCategory } = useValidateCategory(); + + const queryClient = useQueryClient(); + const { mutate } = useMutation< + AxiosResponse, + AxiosError, + Pick, + unknown + >((body) => categoryApi.post(accessToken, body), { + onSuccess: () => onSuccessPostCategory(), + }); + + const handleSubmitCategoryAddForm = (e: React.FormEvent) => { + e.preventDefault(); + + mutate({ name: categoryValue.inputValue, categoryType: CATEGORY_TYPE.NORMAL }); + }; + + const onSuccessPostCategory = () => { + queryClient.invalidateQueries(CACHE_KEY.CATEGORIES); + queryClient.invalidateQueries(CACHE_KEY.MY_CATEGORIES); + queryClient.invalidateQueries(CACHE_KEY.SUBSCRIPTIONS); + + closeModal(); + }; + + return ( +
+

새 카테고리 만들기

+
+
+
+
+
+ + +
+
+
+ ); +} + +export default CategoryAddModal; diff --git a/frontend/src/components/CategoryList/CategoryList.fallback.stories.tsx b/frontend/src/components/CategoryList/CategoryList.fallback.stories.tsx new file mode 100644 index 00000000..e1699cc9 --- /dev/null +++ b/frontend/src/components/CategoryList/CategoryList.fallback.stories.tsx @@ -0,0 +1,14 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; + +import CategoryListFallback from './CategoryList.fallback'; + +export default { + title: 'Components/CategoryListFallback', + component: CategoryListFallback, +} as ComponentMeta; + +const Template: ComponentStory = () => ; + +const Primary = Template.bind({}); + +export { Primary }; diff --git a/frontend/src/components/CategoryList/CategoryList.fallback.tsx b/frontend/src/components/CategoryList/CategoryList.fallback.tsx new file mode 100644 index 00000000..9ad39c95 --- /dev/null +++ b/frontend/src/components/CategoryList/CategoryList.fallback.tsx @@ -0,0 +1,32 @@ +import Skeleton from '@/components/@common/Skeleton/Skeleton'; +import { + categoryItem, + item, +} from '@/components/SubscribedCategoryItem/SubscribedCategoryItem.styles'; + +import { categoryTableHeaderStyle, categoryTableStyle, itemStyle } from './CategoryList.styles'; + +function CategoryListFallback() { + return ( +
+
+ 생성 날짜 + 카테고리 이름 + 생성자 + 구독 상태 +
+
+ {new Array(6).fill(0).map((el, index) => ( +
+ + + + +
+ ))} +
+
+ ); +} + +export default CategoryListFallback; diff --git a/frontend/src/components/CategoryList/CategoryList.stories.tsx b/frontend/src/components/CategoryList/CategoryList.stories.tsx new file mode 100644 index 00000000..c293c3d9 --- /dev/null +++ b/frontend/src/components/CategoryList/CategoryList.stories.tsx @@ -0,0 +1,15 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; + +import CategoryList from './CategoryList'; + +export default { + title: 'Components/CategoryList', + component: CategoryList, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ; + +export const Primary = Template.bind({}); +Primary.args = { + keyword: '', +}; diff --git a/frontend/src/components/CategoryList/CategoryList.styles.ts b/frontend/src/components/CategoryList/CategoryList.styles.ts new file mode 100644 index 00000000..c6e9f117 --- /dev/null +++ b/frontend/src/components/CategoryList/CategoryList.styles.ts @@ -0,0 +1,34 @@ +import { css, Theme } from '@emotion/react'; + +const categoryTableStyle = css` + overflow-y: overlay; + + width: 100%; + height: 100%; +`; + +const categoryTableHeaderStyle = ({ flex, colors }: Theme) => css` + ${flex.row} + + justify-content: space-around; + + width: 100%; + height: 12rem; + border-bottom: 2px solid ${colors.GRAY_400}; + + background: ${colors.GRAY_100}; + + font-size: 4rem; + font-weight: 700; +`; + +const intersectTargetStyle = css` + height: 1rem; +`; + +const itemStyle = css` + flex: 1 1 0; + text-align: center; +`; + +export { categoryTableHeaderStyle, categoryTableStyle, intersectTargetStyle, itemStyle }; diff --git a/frontend/src/components/CategoryList/CategoryList.tsx b/frontend/src/components/CategoryList/CategoryList.tsx new file mode 100644 index 00000000..5ba8d339 --- /dev/null +++ b/frontend/src/components/CategoryList/CategoryList.tsx @@ -0,0 +1,104 @@ +import { AxiosError, AxiosResponse } from 'axios'; +import { useInfiniteQuery, useQuery } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import useIntersect from '@/hooks/useIntersect'; + +import { CategoriesGetResponseType } from '@/@types/category'; +import { SubscriptionType } from '@/@types/subscription'; + +import { userState } from '@/recoil/atoms'; + +import SubscribedCategoryItem from '@/components/SubscribedCategoryItem/SubscribedCategoryItem'; +import UnsubscribedCategoryItem from '@/components/UnsubscribedCategoryItem/UnsubscribedCategoryItem'; + +import { API, CACHE_KEY } from '@/constants/api'; + +import categoryApi from '@/api/category'; +import subscriptionApi from '@/api/subscription'; + +import { + categoryTableHeaderStyle, + categoryTableStyle, + intersectTargetStyle, + itemStyle, +} from './CategoryList.styles'; + +interface CategoryListProps { + keyword: string; +} + +function CategoryList({ keyword }: CategoryListProps) { + const { accessToken } = useRecoilValue(userState); + + const { + error: categoriesGetError, + data: categoriesGetResponse, + fetchNextPage, + hasNextPage, + } = useInfiniteQuery, AxiosError>( + [CACHE_KEY.CATEGORIES, keyword], + ({ pageParam = 0 }) => categoryApi.getEntire(keyword, pageParam, API.CATEGORY_GET_SIZE), + { + getNextPageParam: ({ data }) => { + if (data.categories.length > 0) { + return data.page + 1; + } + }, + } + ); + + const { error: subscriptionsGetError, data: subscriptionsGetResponse } = useQuery< + AxiosResponse, + AxiosError + >(CACHE_KEY.SUBSCRIPTIONS, () => subscriptionApi.get(accessToken)); + + const ref = useIntersect(() => { + hasNextPage && fetchNextPage(); + }); + + if (categoriesGetError || subscriptionsGetError) { + return <>Error; + } + + const categoryList = categoriesGetResponse?.pages.flatMap(({ data }) => data.categories); + const subscriptionList = subscriptionsGetResponse?.data.map((el) => { + return { + subscriptionId: el.id, + categoryId: el.category.id, + }; + }); + + return ( + <> +
+ 생성 날짜 + 카테고리 이름 + 생성자 + 구독 상태 +
+
+ {categoryList?.map((category) => { + const subscribedCategoryInfo = subscriptionList?.find( + (el) => el.categoryId === category.id + ); + + if (subscribedCategoryInfo === undefined) { + return ; + } + + return ( + + ); + })} +
+
+ + ); +} + +export default CategoryList; diff --git a/frontend/src/components/CategoryModifyModal/CategoryModifyModal.styles.ts b/frontend/src/components/CategoryModifyModal/CategoryModifyModal.styles.ts new file mode 100644 index 00000000..71c2daf6 --- /dev/null +++ b/frontend/src/components/CategoryModifyModal/CategoryModifyModal.styles.ts @@ -0,0 +1,70 @@ +import { css, Theme } from '@emotion/react'; + +const modal = ({ colors, flex }: Theme) => css` + ${flex.column} + + width: 120rem; + height: 90rem; + padding: 12.5rem; + border-radius: 12px; + justify-content: space-between; + + background: ${colors.WHITE}; +`; + +const title = ({ colors }: Theme) => css` + font-size: 8rem; + font-weight: bold; + color: ${colors.GRAY_700}; +`; + +const form = ({ flex }: Theme) => css` + ${flex.column}; + + width: 100%; + height: 100%; + justify-content: space-between; +`; + +const content = ({ flex }: Theme) => css` + ${flex.column}; + + width: 100%; + height: 100%; + + justify-content: center; +`; + +const controlButtons = ({ flex }: Theme) => css` + ${flex.row} + + align-self: flex-end; + gap: 5rem; +`; + +const cancelButton = ({ colors }: Theme) => css` + width: 22.5rem; + height: 10rem; + border: 2px solid ${colors.GRAY_500}; + border-radius: 8px; + filter: drop-shadow(0 2px 2px ${colors.GRAY_400}); + + background: ${colors.WHITE}; + + font-size: 5rem; + color: ${colors.GRAY_600}; +`; + +const saveButton = ({ colors }: Theme) => css` + width: 22.5rem; + height: 10rem; + border-radius: 8px; + filter: drop-shadow(0px 2px 2px ${colors.GRAY_400}); + + background: ${colors.YELLOW_500}; + + font-size: 5rem; + color: ${colors.WHITE}; +`; + +export { cancelButton, content, controlButtons, form, modal, saveButton, title }; diff --git a/frontend/src/components/CategoryModifyModal/CategoryModifyModal.tsx b/frontend/src/components/CategoryModifyModal/CategoryModifyModal.tsx new file mode 100644 index 00000000..1317711e --- /dev/null +++ b/frontend/src/components/CategoryModifyModal/CategoryModifyModal.tsx @@ -0,0 +1,94 @@ +import { useTheme } from '@emotion/react'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useMutation, useQueryClient } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import useValidateCategory from '@/hooks/useValidateCategory'; + +import { CategoryType } from '@/@types/category'; + +import { userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; +import Fieldset from '@/components/@common/Fieldset/Fieldset'; + +import { CACHE_KEY } from '@/constants/api'; + +import categoryApi from '@/api/category'; + +import { + cancelButton, + content, + controlButtons, + form, + modal, + saveButton, + title, +} from './CategoryModifyModal.styles'; + +interface CategoryModifyModalProps { + category: CategoryType; + closeModal: () => void; +} + +function CategoryModifyModal({ category, closeModal }: CategoryModifyModalProps) { + const { accessToken } = useRecoilValue(userState); + + const theme = useTheme(); + + const { categoryValue, getCategoryErrorMessage, isValidCategory } = useValidateCategory( + category.name + ); + + const queryClient = useQueryClient(); + const { mutate } = useMutation< + AxiosResponse>, + AxiosError, + Pick, + unknown + >((body) => categoryApi.patch(accessToken, category.id, body), { + onSuccess: () => onSuccessPatchCategory(), + }); + + const handleSubmitCategoryModifyForm = (e: React.FormEvent) => { + e.preventDefault(); + + mutate({ name: categoryValue.inputValue }); + }; + + const onSuccessPatchCategory = () => { + queryClient.invalidateQueries(CACHE_KEY.CATEGORIES); + queryClient.invalidateQueries(CACHE_KEY.MY_CATEGORIES); + queryClient.invalidateQueries(CACHE_KEY.SUBSCRIPTIONS); + closeModal(); + }; + + return ( +
+

카테고리 이름 수정

+
+
+
+
+
+ + +
+
+
+ ); +} + +export default CategoryModifyModal; diff --git a/frontend/src/components/DateModal/DateModal.styles.ts b/frontend/src/components/DateModal/DateModal.styles.ts new file mode 100644 index 00000000..3d524b99 --- /dev/null +++ b/frontend/src/components/DateModal/DateModal.styles.ts @@ -0,0 +1,86 @@ +import { css, Theme } from '@emotion/react'; + +import { ModalPosType } from '@/@types'; + +const dateModalStyle = ({ colors, flex }: Theme, dateModalPos: ModalPosType) => css` + ${flex.column} + + position: absolute; + top: ${dateModalPos.top ? `${dateModalPos.top + 20}px` : 'none'}; + right: ${dateModalPos.right ? `${dateModalPos.right + 20}px` : 'none'}; + bottom: ${dateModalPos.bottom ? `${dateModalPos.bottom + 20}px` : 'none'}; + left: ${dateModalPos.left ? `${dateModalPos.left + 20}px` : 'none'}; + gap: 1rem; + + width: 50rem; + padding: 4rem; + border-radius: 4px; + box-shadow: 0 0 5px ${colors.GRAY_500}; + + background: ${colors.WHITE}; +`; + +const headerStyle = ({ flex }: Theme) => css` + ${flex.column} + + justify-content: flex-end; + gap: 1rem; + + width: 100%; + margin-bottom: 2rem; +`; + +const dayTextStyle = ({ colors }: Theme, day: number) => css` + font-size: 3rem; + color: ${day === 0 && colors.RED_400}; + text-align: right; +`; + +const dateTextStyle = ({ colors }: Theme, day: number, isToday: boolean) => css` + width: 5rem; + height: 5rem; + padding: 1rem; + border-radius: 50%; + + background: ${isToday && colors.YELLOW_500}; + + font-size: 2.5rem; + font-weight: 500; + color: ${isToday ? colors.WHITE : day === 0 ? colors.RED_400 : colors.GRAY_700}; + text-align: center; + line-height: 3rem; +`; + +const itemWithBackgroundStyle = (colorCode: string) => css` + width: 100%; + height: 5rem; + padding: 1rem; + + background: ${colorCode}; + + font-size: 2.75rem; + color: white; + white-space: nowrap; + line-height: 2.75rem; +`; + +const itemWithoutBackgroundStyle = ({ colors }: Theme, colorCode: string) => css` + ${itemWithBackgroundStyle(colorCode)}; + + overflow: hidden; + + border-left: 3px solid ${colorCode}; + + background: ${colors.WHITE}; + + color: black; +`; + +export { + dateModalStyle, + dateTextStyle, + dayTextStyle, + headerStyle, + itemWithBackgroundStyle, + itemWithoutBackgroundStyle, +}; diff --git a/frontend/src/components/DateModal/DateModal.tsx b/frontend/src/components/DateModal/DateModal.tsx new file mode 100644 index 00000000..59d17c90 --- /dev/null +++ b/frontend/src/components/DateModal/DateModal.tsx @@ -0,0 +1,106 @@ +import { useTheme } from '@emotion/react'; + +import { ModalPosType } from '@/@types'; +import { CalendarType } from '@/@types/calendar'; +import { ScheduleType } from '@/@types/schedule'; + +import { CALENDAR } from '@/constants'; +import { DAYS } from '@/constants/date'; + +import { getFormattedDate, getISODateString, getThisDate, getThisMonth } from '@/utils/date'; + +import { + dateModalStyle, + dateTextStyle, + dayTextStyle, + headerStyle, + itemWithBackgroundStyle, + itemWithoutBackgroundStyle, +} from './DateModal.styles'; + +interface DateModalProps { + dateModalPos: ModalPosType; + moreDateInfo: CalendarType; + longTermsWithPriority: { schedule: ScheduleType; priority: number }[]; + allDaysWithPriority: { schedule: ScheduleType; priority: number }[]; + fewHoursWithPriority: { schedule: ScheduleType; priority: number }[]; +} + +function DateModal({ + dateModalPos, + moreDateInfo, + longTermsWithPriority, + allDaysWithPriority, + fewHoursWithPriority, +}: DateModalProps) { + const theme = useTheme(); + + return ( +
+
+ {DAYS[moreDateInfo.day]} + + {moreDateInfo.date} + +
+ + {longTermsWithPriority.map((el) => { + const startDate = getISODateString(el.schedule.startDateTime); + const endDate = getISODateString(el.schedule.endDateTime); + const nowDate = getFormattedDate(moreDateInfo.year, moreDateInfo.month, moreDateInfo.date); + + return ( + startDate <= nowDate && + nowDate <= endDate && ( +
+ {el.schedule.title || CALENDAR.EMPTY_TITLE} +
+ ) + ); + })} + + {allDaysWithPriority.map((el) => { + const startDate = getISODateString(el.schedule.startDateTime); + const nowDate = getFormattedDate(moreDateInfo.year, moreDateInfo.month, moreDateInfo.date); + + return ( + startDate === nowDate && ( +
+ {el.schedule.title || CALENDAR.EMPTY_TITLE} +
+ ) + ); + })} + + {fewHoursWithPriority.map((el) => { + const startDate = getISODateString(el.schedule.startDateTime); + const nowDate = getFormattedDate(moreDateInfo.year, moreDateInfo.month, moreDateInfo.date); + + return ( + startDate === nowDate && ( +
+ {el.schedule.title || CALENDAR.EMPTY_TITLE} +
+ ) + ); + })} +
+ ); +} + +export default DateModal; diff --git a/frontend/src/components/FilterCategoryItem/FilterCategoryItem.styles.ts b/frontend/src/components/FilterCategoryItem/FilterCategoryItem.styles.ts new file mode 100644 index 00000000..dcdb8d3c --- /dev/null +++ b/frontend/src/components/FilterCategoryItem/FilterCategoryItem.styles.ts @@ -0,0 +1,112 @@ +import { css, Theme } from '@emotion/react'; + +const itemStyle = ({ colors, flex }: Theme) => css` + ${flex.row} + + justify-content: space-between; + + width: 100%; + height: 8rem; + + &:hover { + background-color: ${colors.GRAY_100}; + + button { + visibility: visible; + } + } +`; + +const checkBoxNameStyle = ({ flex }: Theme) => css` + ${flex.row} + + gap: 1rem; + + &:hover { + cursor: pointer; + } +`; + +const nameStyle = css` + overflow: hidden; + position: relative; + + width: 32rem; + + white-space: nowrap; + text-overflow: ellipsis; +`; + +const colorStyle = (color: string) => css` + width: 5rem; + height: 5rem; + border-radius: 50%; + + background: ${color}; + + &:hover { + filter: none; + transform: scale(1.2); + } +`; + +const headerStyle = css` + padding: 2rem; + + font-size: 5rem; +`; + +const iconStyle = css` + visibility: hidden; +`; + +const paletteStyle = ({ colors }: Theme) => css` + position: absolute; + right: 0; + z-index: 30; + + display: grid; + grid-template-columns: repeat(4, 1fr); + place-items: center; + gap: 2rem; + + width: 35rem; + padding: 2rem; + border: 1px solid ${colors.GRAY_300}; + border-radius: 4px; + + background: ${colors.WHITE}; +`; + +const paletteLayoutStyle = css` + position: relative; +`; + +const outerStyle = css` + position: fixed; + left: 0; + top: 16rem; + z-index: 20; + + width: 100%; + height: 100%; + + background-color: transparent; +`; + +const grayTextStyle = ({ colors }: Theme) => css` + color: ${colors.GRAY_600}; +`; + +export { + itemStyle, + colorStyle, + checkBoxNameStyle, + grayTextStyle, + headerStyle, + iconStyle, + nameStyle, + outerStyle, + paletteStyle, + paletteLayoutStyle, +}; diff --git a/frontend/src/components/FilterCategoryItem/FilterCategoryItem.tsx b/frontend/src/components/FilterCategoryItem/FilterCategoryItem.tsx new file mode 100644 index 00000000..d438af96 --- /dev/null +++ b/frontend/src/components/FilterCategoryItem/FilterCategoryItem.tsx @@ -0,0 +1,128 @@ +import { useMutation, useQueryClient } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import useToggle from '@/hooks/useToggle'; + +import { SubscriptionType } from '@/@types/subscription'; + +import { userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; +import Spinner from '@/components/@common/Spinner/Spinner'; + +import { CATEGORY_TYPE } from '@/constants/category'; +import { PALETTE } from '@/constants/style'; + +import subscriptionApi from '@/api/subscription'; + +import { BiPalette } from 'react-icons/bi'; +import { RiCheckboxBlankLine, RiCheckboxFill } from 'react-icons/ri'; + +import { + checkBoxNameStyle, + colorStyle, + grayTextStyle, + iconStyle, + itemStyle, + nameStyle, + outerStyle, + paletteLayoutStyle, + paletteStyle, +} from './FilterCategoryItem.styles'; + +interface FilterItemProps { + subscription: SubscriptionType; +} + +function FilterCategoryItem({ subscription }: FilterItemProps) { + const { accessToken } = useRecoilValue(userState); + + const { state: isPaletteOpen, toggleState: togglePaletteOpen } = useToggle(); + + const queryClient = useQueryClient(); + + const { isLoading, mutate } = useMutation( + (body: Pick | Pick) => + subscriptionApi.patch(accessToken, subscription.id, body), + { + onSuccess: () => queryClient.invalidateQueries(), + } + ); + + const handleClickFilledCheckBox = (colorCode: string) => { + mutate({ + checked: false, + colorCode, + }); + }; + + const handleClickBlankCheckBox = (colorCode: string) => { + mutate({ + checked: true, + colorCode, + }); + }; + + const handleClickPalette = (checked: boolean, colorCode: string) => { + mutate({ checked, colorCode }); + togglePaletteOpen(); + }; + + return ( +
+
+ {subscription.checked ? ( + + ) : ( + + )} + + {subscription.category.name} + + {subscription.category.categoryType === CATEGORY_TYPE.GOOGLE && ' (구글)'} + {subscription.category.categoryType === CATEGORY_TYPE.PERSONAL && ' (기본)'} + + +
+
+ {isLoading && } + + + {isPaletteOpen && ( + <> +
+
+ {PALETTE.map((color) => { + return ( + + ); + })} +
+ + )} +
+
+ ); +} + +export default FilterCategoryItem; diff --git a/frontend/src/components/FilterCategoryList/FilterCategoryList.fallback.stories.tsx b/frontend/src/components/FilterCategoryList/FilterCategoryList.fallback.stories.tsx new file mode 100644 index 00000000..7d761a95 --- /dev/null +++ b/frontend/src/components/FilterCategoryList/FilterCategoryList.fallback.stories.tsx @@ -0,0 +1,16 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; + +import FilterCategoryListFallback from './FilterCategoryList.fallback'; + +export default { + title: 'Components/FilterCategoryListFallback', + component: FilterCategoryListFallback, +} as ComponentMeta; + +const Template: ComponentStory = () => ( + +); + +const Primary = Template.bind({}); + +export { Primary }; diff --git a/frontend/src/components/FilterCategoryList/FilterCategoryList.fallback.tsx b/frontend/src/components/FilterCategoryList/FilterCategoryList.fallback.tsx new file mode 100644 index 00000000..ff5a7e8d --- /dev/null +++ b/frontend/src/components/FilterCategoryList/FilterCategoryList.fallback.tsx @@ -0,0 +1,22 @@ +import { useTheme } from '@emotion/react'; + +import Skeleton from '@/components/@common/Skeleton/Skeleton'; + +import { headerStyle, skeletonListStyle, skeletonStyle } from './FilterCategoryList.styles'; + +function FilterCategoryFallback() { + const theme = useTheme(); + + return ( +
+ 구독 카테고리 +
+ {new Array(10).fill(0).map((el, index) => ( + + ))} +
+
+ ); +} + +export default FilterCategoryFallback; diff --git a/frontend/src/components/FilterCategoryList/FilterCategoryList.styles.ts b/frontend/src/components/FilterCategoryList/FilterCategoryList.styles.ts new file mode 100644 index 00000000..d297352e --- /dev/null +++ b/frontend/src/components/FilterCategoryList/FilterCategoryList.styles.ts @@ -0,0 +1,85 @@ +import { css, Theme } from '@emotion/react'; + +const listStyle = ({ flex }: Theme, isSideBarOpen: boolean) => css` + ${flex.column} + + display: ${isSideBarOpen ? 'flex' : 'none'}; + justify-content: flex-start; + + width: 54rem; + + font-size: 4rem; +`; + +const headerStyle = ({ flex }: Theme) => css` + ${flex.row} + + justify-content: space-between; + + width: 100%; + height: 8rem; + + font-weight: bold; +`; + +const googleImportButtonStyle = ({ colors, flex }: Theme) => css` + ${flex.row} + + position: relative; + + width: 100%; + height: 11rem; + padding: 4rem; + margin: 2rem 0 3rem; + border-radius: 4px; + border: 1px solid ${colors.GRAY_600}; + + background: ${colors.WHITE}; + + font-size: 4rem; + color: ${colors.GRAY_600}; + + &:hover { + filter: none; + } +`; + +const googleImportTextStyle = css` + width: 100%; +`; + +const contentStyle = css` + display: flex; + flex-direction: column; + gap: 2rem; + + width: 100%; +`; + +const skeletonStyle = ({ flex }: Theme) => css` + ${flex.column}; + + gap: 5rem; +`; + +const skeletonListStyle = ({ flex }: Theme, isSideBarOpen: boolean) => css` + ${flex.column}; + + display: ${isSideBarOpen ? 'flex' : 'none'}; + justify-content: flex-start; + + width: 54rem; + margin-top: 16rem; + + font-size: 4rem; +`; + +export { + contentStyle, + googleImportButtonStyle, + googleImportTextStyle, + headerStyle, + listStyle, + skeletonStyle, + skeletonListStyle, +}; diff --git a/frontend/src/components/FilterCategoryList/FilterCategoryList.tsx b/frontend/src/components/FilterCategoryList/FilterCategoryList.tsx new file mode 100644 index 00000000..81b0ffd7 --- /dev/null +++ b/frontend/src/components/FilterCategoryList/FilterCategoryList.tsx @@ -0,0 +1,72 @@ +import { useTheme } from '@emotion/react'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useQuery } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import useToggle from '@/hooks/useToggle'; + +import { SubscriptionType } from '@/@types/subscription'; + +import { sideBarState, userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; +import ModalPortal from '@/components/@common/ModalPortal/ModalPortal'; +import FilterCategoryItem from '@/components/FilterCategoryItem/FilterCategoryItem'; +import GoogleImportModal from '@/components/GoogleImportModal/GoogleImportModal'; + +import { CACHE_KEY } from '@/constants/api'; + +import subscriptionApi from '@/api/subscription'; + +import { FcGoogle } from 'react-icons/fc'; + +import FilterCategoryFallback from './FilterCategoryList.fallback'; +import { + contentStyle, + googleImportButtonStyle, + googleImportTextStyle, + headerStyle, + listStyle, +} from './FilterCategoryList.styles'; + +function FilterCategoryList() { + const { accessToken } = useRecoilValue(userState); + const isSideBarOpen = useRecoilValue(sideBarState); + + const theme = useTheme(); + + const { state: isGoogleImportModalOpen, toggleState: toggleGoogleImportModalOpen } = useToggle(); + + const { isLoading, data } = useQuery, AxiosError>( + CACHE_KEY.SUBSCRIPTIONS, + () => subscriptionApi.get(accessToken) + ); + + if (isLoading || data === undefined) { + return ; + } + + const handleClickGoogleImportButton = () => { + toggleGoogleImportModalOpen(); + }; + + return ( +
+ 구독 카테고리 + +
+ {data?.data.map((el) => { + return ; + })} +
+ + + +
+ ); +} + +export default FilterCategoryList; diff --git a/frontend/src/components/Footer/Footer.styles.ts b/frontend/src/components/Footer/Footer.styles.ts new file mode 100644 index 00000000..a8c407e0 --- /dev/null +++ b/frontend/src/components/Footer/Footer.styles.ts @@ -0,0 +1,20 @@ +import { css, Theme } from '@emotion/react'; + +const footerStyle = ({ colors, flex }: Theme) => css` + ${flex.column} + + width: 100%; + height: 40rem; + + color: ${colors.GRAY_600}; + line-height: 150%; +`; + +const privacyPolicyButtonStyle = css` + margin: 1rem; + + font-size: inherit; + color: inherit; +`; + +export { footerStyle, privacyPolicyButtonStyle }; diff --git a/frontend/src/components/Footer/Footer.tsx b/frontend/src/components/Footer/Footer.tsx new file mode 100644 index 00000000..38cbd633 --- /dev/null +++ b/frontend/src/components/Footer/Footer.tsx @@ -0,0 +1,28 @@ +import { useNavigate } from 'react-router-dom'; + +import Button from '@/components/@common/Button/Button'; + +import { PATH } from '@/constants'; + +import { footerStyle, privacyPolicyButtonStyle } from './Footer.styles'; + +function Footer() { + const navigate = useNavigate(); + + const handleClickPrivacyPolicyButton = () => { + navigate(PATH.POLICY); + }; + + return ( +
+

우아한테크코스 4기 달록

+

서울특별시 송파구 올림픽로35다길 42, 14층 (한국루터회관)

+

Copyright © 2022 달록 - All rights reserved.

+ +
+ ); +} + +export default Footer; diff --git a/frontend/src/components/GoogleImportModal/GoogleImportModal.styles.ts b/frontend/src/components/GoogleImportModal/GoogleImportModal.styles.ts new file mode 100644 index 00000000..d594c053 --- /dev/null +++ b/frontend/src/components/GoogleImportModal/GoogleImportModal.styles.ts @@ -0,0 +1,58 @@ +import { css, Theme } from '@emotion/react'; + +const layoutStyle = ({ colors, flex }: Theme) => css` + ${flex.column}; + + align-items: flex-start; + justify-content: center; + gap: 10rem; + + width: 120rem; + height: 120rem; + padding: 12.5rem; + border-radius: 12px; + + background: ${colors.WHITE}; + + color: ${colors.GRAY_700}; +`; + +const headerStyle = css` + font-size: 8rem; + font-weight: bold; + text-align: center; +`; + +const titleStyle = css` + padding: 0 1rem; + font-size: 4rem; +`; + +const googleSelectStyle = ({ colors }: Theme) => css` + padding: 3rem; + border: 1px solid ${colors.GRAY_400}; + border-radius: 8px; + + font-size: 4rem; +`; + +const googleSelectBoxStyle = ({ flex }: Theme) => css` + ${flex.column}; + + align-items: flex-start; + + gap: 2rem; + + font-size: 4rem; +`; + +const formStyle = ({ flex }: Theme) => css` + ${flex.column}; + + align-items: flex-start; + + width: 100%; + height: 100%; +`; + +export { formStyle, googleSelectBoxStyle, googleSelectStyle, headerStyle, layoutStyle, titleStyle }; diff --git a/frontend/src/components/GoogleImportModal/GoogleImportModal.tsx b/frontend/src/components/GoogleImportModal/GoogleImportModal.tsx new file mode 100644 index 00000000..c5440136 --- /dev/null +++ b/frontend/src/components/GoogleImportModal/GoogleImportModal.tsx @@ -0,0 +1,132 @@ +import { validateNotEmpty } from '@/validation'; +import { useTheme } from '@emotion/react'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useMutation, useQuery, useQueryClient } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import useControlledInput from '@/hooks/useControlledInput'; +import useValidateCategory from '@/hooks/useValidateCategory'; + +import { GoogleCalendarGetResponseType, GoogleCalendarPostBodyType } from '@/@types/googleCalendar'; + +import { userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; +import Fieldset from '@/components/@common/Fieldset/Fieldset'; +import Spinner from '@/components/@common/Spinner/Spinner'; +import { + cancelButtonStyle, + content, + controlButtons, + saveButtonStyle, +} from '@/components/CategoryAddModal/CategoryAddModal.styles'; + +import { CACHE_KEY } from '@/constants/api'; + +import googleCalendarApi from '@/api/googleCalendar'; + +import { + formStyle, + googleSelectBoxStyle, + googleSelectStyle, + headerStyle, + layoutStyle, + titleStyle, +} from './GoogleImportModal.styles'; + +interface GoogleImportModal { + closeModal: () => void; +} + +function GoogleImportModal({ closeModal }: GoogleImportModal) { + const theme = useTheme(); + + const { accessToken } = useRecoilValue(userState); + + const { categoryValue, getCategoryErrorMessage, isValidCategory } = useValidateCategory(); + + const { inputValue: googleCalendarInputValue, onChangeValue: onChangeGoogleCalendarInputValue } = + useControlledInput(); + + const queryClient = useQueryClient(); + + const { isLoading, data } = useQuery, AxiosError>( + CACHE_KEY.GOOGLE_CALENDAR, + () => googleCalendarApi.get(accessToken) + ); + + const { mutate } = useMutation( + (body: GoogleCalendarPostBodyType) => googleCalendarApi.post(accessToken, body), + { + onSuccess: () => onSuccessPostCategory(), + } + ); + + const handleSubmitCategoryAddForm = (e: React.FormEvent) => { + e.preventDefault(); + + mutate({ externalId: googleCalendarInputValue, name: categoryValue.inputValue }); + }; + + const onSuccessPostCategory = () => { + queryClient.invalidateQueries(CACHE_KEY.CATEGORIES); + queryClient.invalidateQueries(CACHE_KEY.MY_CATEGORIES); + queryClient.invalidateQueries(CACHE_KEY.SUBSCRIPTIONS); + + closeModal(); + }; + + if (isLoading || data === undefined) { + return ; + } + + return ( +
+
구글 캘린더 가져오기
+
+
+
구글 캘린더 목록
+ +
+ +
+
+
+
+ + +
+
+
+ ); +} + +export default GoogleImportModal; diff --git a/frontend/src/components/MyCategoryItem/MyCategoryItem.style.ts b/frontend/src/components/MyCategoryItem/MyCategoryItem.style.ts new file mode 100644 index 00000000..587c27cd --- /dev/null +++ b/frontend/src/components/MyCategoryItem/MyCategoryItem.style.ts @@ -0,0 +1,63 @@ +import { css, Theme } from '@emotion/react'; + +const buttonStyle = ({ colors }: Theme) => css` + position: relative; + + width: 8rem; + height: 8rem; + + background: transparent; + + color: ${colors.GRAY_700}; + + &:hover { + border-radius: 50%; + + background: ${colors.GRAY_100}; + + filter: none; + } + + &:hover span { + visibility: visible; + } +`; + +const menuTitle = ({ colors }: Theme) => css` + visibility: hidden; + position: absolute; + top: 120%; + left: 50%; + transform: translateX(-50%); + + padding: 2rem 3rem; + + background: ${colors.GRAY_700}ee; + + font-size: 3rem; + font-weight: normal; + color: ${colors.WHITE}; + white-space: nowrap; +`; + +const categoryItemStyle = ({ colors, flex }: Theme) => css` + ${flex.row} + + justify-content: space-around; + + height: 20rem; + border-bottom: 1px solid ${colors.GRAY_400}; + + font-size: 4rem; +`; + +const itemStyle = css` + flex: 1 1 0; + text-align: center; +`; + +const grayTextStyle = ({ colors }: Theme) => css` + color: ${colors.GRAY_600}; +`; + +export { buttonStyle, categoryItemStyle, grayTextStyle, itemStyle, menuTitle }; diff --git a/frontend/src/components/MyCategoryItem/MyCategoryItem.tsx b/frontend/src/components/MyCategoryItem/MyCategoryItem.tsx new file mode 100644 index 00000000..e041a8dd --- /dev/null +++ b/frontend/src/components/MyCategoryItem/MyCategoryItem.tsx @@ -0,0 +1,101 @@ +import { useMutation, useQueryClient } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import useToggle from '@/hooks/useToggle'; + +import { CategoryType } from '@/@types/category'; + +import { userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; +import ModalPortal from '@/components/@common/ModalPortal/ModalPortal'; +import CategoryModifyModal from '@/components/CategoryModifyModal/CategoryModifyModal'; + +import { CACHE_KEY } from '@/constants/api'; +import { CATEGORY_TYPE } from '@/constants/category'; +import { CONFIRM_MESSAGE, TOOLTIP_MESSAGE } from '@/constants/message'; + +import { getISODateString } from '@/utils/date'; + +import categoryApi from '@/api/category'; + +import { FiEdit3 } from 'react-icons/fi'; +import { RiDeleteBin6Line } from 'react-icons/ri'; + +import { + buttonStyle, + categoryItemStyle, + grayTextStyle, + itemStyle, + menuTitle, +} from './MyCategoryItem.style'; + +interface MyCategoryItemProps { + category: CategoryType; +} + +function MyCategoryItem({ category }: MyCategoryItemProps) { + const { accessToken } = useRecoilValue(userState); + + const { state: isCategoryModifyModalOpen, toggleState: toggleCategoryModifyModalOpen } = + useToggle(); + + const queryClient = useQueryClient(); + const { mutate } = useMutation(() => categoryApi.delete(accessToken, category.id), { + onSuccess: () => onSuccessDeleteCategory(), + }); + + const handleClickDeleteButton = () => { + if (confirm(CONFIRM_MESSAGE.DELETE)) { + mutate(); + } + }; + + const onSuccessDeleteCategory = () => { + queryClient.invalidateQueries(CACHE_KEY.CATEGORIES); + queryClient.invalidateQueries(CACHE_KEY.MY_CATEGORIES); + queryClient.invalidateQueries(CACHE_KEY.SUBSCRIPTIONS); + }; + + const canEditCategory = category.categoryType !== CATEGORY_TYPE.PERSONAL; + + return ( +
+ {getISODateString(category.createdAt)} + + {category.name} + + {category.categoryType === CATEGORY_TYPE.GOOGLE && ' (구글)'} + {category.categoryType === CATEGORY_TYPE.PERSONAL && ' (기본)'} + + +
+ + + + + +
+
+ ); +} + +export default MyCategoryItem; diff --git a/frontend/src/components/MyCategoryList/MyCategoryList.fallback.tsx b/frontend/src/components/MyCategoryList/MyCategoryList.fallback.tsx new file mode 100644 index 00000000..8f7c2f26 --- /dev/null +++ b/frontend/src/components/MyCategoryList/MyCategoryList.fallback.tsx @@ -0,0 +1,33 @@ +import Skeleton from '@/components/@common/Skeleton/Skeleton'; +import { + categoryTableHeaderStyle, + categoryTableStyle, + itemStyle, +} from '@/components/CategoryList/CategoryList.styles'; +import { + categoryItem, + item, +} from '@/components/SubscribedCategoryItem/SubscribedCategoryItem.styles'; + +function CategoryListFallback() { + return ( +
+
+ 생성 날짜 + 카테고리 이름 + 수정 / 삭제 +
+
+ {new Array(6).fill(0).map((el, index) => ( +
+ + + +
+ ))} +
+
+ ); +} + +export default CategoryListFallback; diff --git a/frontend/src/components/MyCategoryList/MyCategoryList.styles.ts b/frontend/src/components/MyCategoryList/MyCategoryList.styles.ts new file mode 100644 index 00000000..17a620cf --- /dev/null +++ b/frontend/src/components/MyCategoryList/MyCategoryList.styles.ts @@ -0,0 +1,47 @@ +import { css, Theme } from '@emotion/react'; + +const itemStyle = css` + flex: 1 1 0; + text-align: center; +`; + +const headerStyle = ({ flex, colors }: Theme) => css` + ${flex.row} + + justify-content: space-around; + + width: 100%; + height: 12rem; + border-bottom: 2px solid ${colors.GRAY_400}; + + background: ${colors.GRAY_100}; + + font-size: 4rem; + font-weight: 700; +`; + +const buttonStyle = ({ colors }: Theme) => css` + width: 8rem; + height: 8rem; + + background: transparent; + + color: ${colors.GRAY_700}; + + &:hover { + border-radius: 50%; + + background: ${colors.GRAY_100}; + + filter: none; + } +`; + +const listStyle = css` + width: 100%; + height: 100%; + + overflow-y: overlay; +`; + +export { buttonStyle, listStyle, headerStyle, itemStyle }; diff --git a/frontend/src/components/MyCategoryList/MyCategoryList.tsx b/frontend/src/components/MyCategoryList/MyCategoryList.tsx new file mode 100644 index 00000000..d724cff5 --- /dev/null +++ b/frontend/src/components/MyCategoryList/MyCategoryList.tsx @@ -0,0 +1,41 @@ +import { AxiosError, AxiosResponse } from 'axios'; +import { useQuery } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import { CategoryType } from '@/@types/category'; + +import { userState } from '@/recoil/atoms'; + +import MyCategoryItem from '@/components/MyCategoryItem/MyCategoryItem'; + +import { CACHE_KEY } from '@/constants/api'; + +import categoryApi from '@/api/category'; + +import { headerStyle, itemStyle, listStyle } from './MyCategoryList.styles'; + +function MyCategoryList() { + const { accessToken } = useRecoilValue(userState); + + const { data } = useQuery, AxiosError>( + CACHE_KEY.MY_CATEGORIES, + () => categoryApi.getMy(accessToken) + ); + + return ( + <> +
+ 생성 날짜 + 카테고리 이름 + 수정 / 삭제 +
+
+ {data?.data.map((category) => ( + + ))} +
+ + ); +} + +export default MyCategoryList; diff --git a/frontend/src/components/NavBar/NavBar.stories.tsx b/frontend/src/components/NavBar/NavBar.stories.tsx new file mode 100644 index 00000000..9c4dd234 --- /dev/null +++ b/frontend/src/components/NavBar/NavBar.stories.tsx @@ -0,0 +1,12 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; + +import NavBar from './NavBar'; + +export default { + title: 'Components/NavBar', + component: NavBar, +} as ComponentMeta; + +const Template: ComponentStory = () => ; + +export const Primary = Template.bind({}); diff --git a/frontend/src/components/NavBar/NavBar.styles.ts b/frontend/src/components/NavBar/NavBar.styles.ts new file mode 100644 index 00000000..b6f24759 --- /dev/null +++ b/frontend/src/components/NavBar/NavBar.styles.ts @@ -0,0 +1,86 @@ +import { css, Theme } from '@emotion/react'; + +const navBar = ({ colors, flex }: Theme) => css` + ${flex.row} + + justify-content: space-between; + position: fixed; + top: 0; + left: 0; + z-index: 20; + + width: 100%; + height: 16rem; + padding: 2rem 5rem 2rem 2rem; + + background: ${colors.WHITE}; + + box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25); +`; + +const menus = ({ flex }: Theme) => css` + ${flex.row} + + gap: 3rem; +`; + +const logo = ({ colors, flex }: Theme) => css` + ${flex.row} + + position: relative; + + background: transparent; + + font-size: 5rem; + font-weight: bold; + color: ${colors.GRAY_700}; +`; + +const logoImg = css` + width: 6rem; + height: 6rem; +`; + +const logoText = css` + margin-left: 2rem; +`; + +const menu = ({ colors, flex }: Theme) => css` + ${logo({ colors, flex })} + + width: 11rem; + height: 11rem; + + font-size: 7rem; + + &:hover { + border-radius: 50%; + + background: ${colors.GRAY_100}; + + filter: none; + } + + &:hover span { + visibility: visible; + } +`; + +const menuTitle = ({ colors }: Theme) => css` + visibility: hidden; + position: absolute; + top: 120%; + left: 50%; + transform: translateX(-50%); + + padding: 2rem 3rem; + + background: ${colors.GRAY_700}ee; + + font-size: 3rem; + font-weight: normal; + color: ${colors.WHITE}; + white-space: nowrap; +`; + +export { logo, logoImg, logoText, menu, menus, menuTitle, navBar }; diff --git a/frontend/src/components/NavBar/NavBar.tsx b/frontend/src/components/NavBar/NavBar.tsx new file mode 100644 index 00000000..c996ef20 --- /dev/null +++ b/frontend/src/components/NavBar/NavBar.tsx @@ -0,0 +1,107 @@ +import { useTheme } from '@emotion/react'; +import { lazy, Suspense } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useRecoilState, useRecoilValue } from 'recoil'; + +import useToggle from '@/hooks/useToggle'; + +import { userState } from '@/recoil/atoms'; +import { sideBarSelector } from '@/recoil/selectors'; + +import Button from '@/components/@common/Button/Button'; +import ModalPortal from '@/components/@common/ModalPortal/ModalPortal'; +import ProfileFallback from '@/components/Profile/Profile.fallback'; + +import { PATH } from '@/constants'; +import { TRANSPARENT } from '@/constants/style'; + +import { BiCategory } from 'react-icons/bi'; +import { FaUserCircle } from 'react-icons/fa'; +import { FiCalendar } from 'react-icons/fi'; +import { HiChevronDoubleLeft, HiMenu } from 'react-icons/hi'; +import { IoPeopleOutline } from 'react-icons/io5'; + +import BlackLogo from '../../assets/dallog_black.png'; +import { logo, logoImg, logoText, menu, menus, menuTitle, navBar } from './NavBar.styles'; + +const Profile = lazy(() => import('@/components/Profile/Profile')); + +function NavBar() { + const { accessToken } = useRecoilValue(userState); + const [isSideBarOpen, toggleSideBarOpen] = useRecoilState(sideBarSelector); + + const theme = useTheme(); + const navigate = useNavigate(); + + const { state: isProfileModalOpen, toggleState: toggleProfileModalOpen } = useToggle(); + + const handleClickSideBarButton = () => { + toggleSideBarOpen(isSideBarOpen); + }; + + const handleClickMainButton = () => { + navigate(PATH.MAIN); + }; + + const handleClickCategoryMenuButton = () => { + navigate(PATH.CATEGORY); + }; + + const handleClickSchedulingMenuButton = () => { + navigate(PATH.SCHEDULING); + }; + + const handleClickProfileMenuButton = () => { + toggleProfileModalOpen(); + }; + + return ( +
+
+ {accessToken && ( + + )} + +
+
+ {accessToken && ( + <> + + + + + + }> + + + + + )} +
+
+ ); +} + +export default NavBar; diff --git a/frontend/src/components/Profile/Profile.fallback.stories.tsx b/frontend/src/components/Profile/Profile.fallback.stories.tsx new file mode 100644 index 00000000..3648f2f7 --- /dev/null +++ b/frontend/src/components/Profile/Profile.fallback.stories.tsx @@ -0,0 +1,15 @@ +import { css } from '@emotion/react'; +import { ComponentMeta, ComponentStory } from '@storybook/react'; + +import ProfileFallback from './Profile.fallback'; + +export default { + title: 'Components/ProfileFallback', + component: ProfileFallback, +} as ComponentMeta; + +const Template: ComponentStory = () => ; + +const Primary = Template.bind({}); + +export { Primary }; diff --git a/frontend/src/components/Profile/Profile.fallback.tsx b/frontend/src/components/Profile/Profile.fallback.tsx new file mode 100644 index 00000000..da8f50d2 --- /dev/null +++ b/frontend/src/components/Profile/Profile.fallback.tsx @@ -0,0 +1,18 @@ +import Skeleton from '@/components/@common/Skeleton/Skeleton'; + +import { imageStyle, layoutStyle, skeletonStyle } from './Profile.styles'; + +function ProfileFallback() { + return ( +
+ +
+ + + +
+
+ ); +} + +export default ProfileFallback; diff --git a/frontend/src/components/Profile/Profile.styles.ts b/frontend/src/components/Profile/Profile.styles.ts new file mode 100644 index 00000000..f6913f5c --- /dev/null +++ b/frontend/src/components/Profile/Profile.styles.ts @@ -0,0 +1,129 @@ +import { css, Theme } from '@emotion/react'; + +const contentStyle = css` + width: 100%; + + text-align: center; +`; + +const emailStyle = ({ colors }: Theme) => css` + font-size: 3rem; + color: ${colors.GRAY_500}; +`; + +const imageStyle = css` + width: 35rem; + height: 35rem; + border-radius: 50%; +`; + +const inputStyle = { + div: css` + height: 3rem; + + font-size: 3rem; + `, + input: css` + height: 3rem; + + font-size: 3rem; + `, +}; + +const layoutStyle = ({ flex, colors }: Theme) => css` + ${flex.column}; + + justify-content: space-around; + gap: 5rem; + position: absolute; + top: 15rem; + right: 2rem; + + width: 60rem; + padding: 5rem; + box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.25); + border-radius: 10px; + + background: ${colors.WHITE}; + + font-size: 4rem; +`; + +const logoutButtonStyle = ({ colors }: Theme) => css` + padding: 2rem 3rem; + border: 1px solid ${colors.GRAY_400}; + border-radius: 3px; + + font-size: 3rem; +`; + +const menu = ({ colors }: Theme) => css` + position: relative; + + width: 9rem; + height: 9rem; + + &:hover { + border-radius: 50%; + + background: ${colors.GRAY_100}; + + filter: none; + } + + &:hover span { + visibility: visible; + } +`; + +const menuTitle = ({ colors }: Theme) => css` + visibility: hidden; + position: absolute; + top: 120%; + left: 50%; + transform: translateX(-50%); + + padding: 2rem 3rem; + + background: ${colors.GRAY_700}ee; + + font-size: 3rem; + font-weight: normal; + color: ${colors.WHITE}; + white-space: nowrap; +`; + +const nameButtonStyle = ({ flex }: Theme) => css` + ${flex.row}; + + justify-content: flex-end; + gap: 2rem; + + font-size: 3rem; +`; + +const nameStyle = css` + margin-left: 7rem; + + font-size: 3.5rem; +`; + +const skeletonStyle = ({ flex }: Theme) => css` + ${flex.column}; + + gap: 3rem; +`; + +export { + contentStyle, + emailStyle, + imageStyle, + inputStyle, + layoutStyle, + logoutButtonStyle, + menu, + menuTitle, + nameStyle, + nameButtonStyle, + skeletonStyle, +}; diff --git a/frontend/src/components/Profile/Profile.tsx b/frontend/src/components/Profile/Profile.tsx new file mode 100644 index 00000000..da150d39 --- /dev/null +++ b/frontend/src/components/Profile/Profile.tsx @@ -0,0 +1,133 @@ +import { AxiosError, AxiosResponse } from 'axios'; +import { useRef, useState } from 'react'; +import { useMutation, useQuery, useQueryClient } from 'react-query'; +import { useNavigate } from 'react-router-dom'; +import { useRecoilValue } from 'recoil'; + +import { ProfileType } from '@/@types/profile'; + +import { userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; +import Fieldset from '@/components/@common/Fieldset/Fieldset'; + +import { PATH } from '@/constants'; +import { CACHE_KEY } from '@/constants/api'; +import { CONFIRM_MESSAGE } from '@/constants/message'; + +import { createPostBody } from '@/utils'; +import { removeAccessToken } from '@/utils/storage'; + +import profileApi from '@/api/profile'; + +import { AiOutlineCheck } from 'react-icons/ai'; +import { FiEdit3 } from 'react-icons/fi'; + +import { + contentStyle, + emailStyle, + imageStyle, + inputStyle, + layoutStyle, + logoutButtonStyle, + menu, + menuTitle, + nameButtonStyle, + nameStyle, +} from './Profile.styles'; + +function Profile() { + const [isEditingName, setEditingName] = useState(false); + + const inputRef = { + displayName: useRef(null), + }; + + const navigate = useNavigate(); + + const { accessToken } = useRecoilValue(userState); + + const queryClient = useQueryClient(); + const { data } = useQuery, AxiosError>(CACHE_KEY.PROFILE, () => + profileApi.get(accessToken) + ); + + const { mutate } = useMutation( + (body: { displayName: string }) => profileApi.patch(accessToken, body), + { + onSuccess: () => { + queryClient.invalidateQueries(CACHE_KEY.PROFILE); + queryClient.invalidateQueries(CACHE_KEY.CATEGORIES); + }, + } + ); + + const handleClickModifyButton = () => { + setEditingName(true); + }; + + const handleClickCompleteButton = (defaultName: string | undefined) => { + if (defaultName === undefined) { + return; + } + + const body = createPostBody(inputRef); + + if (body.displayName === '') { + body.displayName = defaultName; + } + + mutate(body); + setEditingName(false); + }; + + const handleClickLogoutButton = () => { + if (window.confirm(CONFIRM_MESSAGE.LOGOUT)) { + removeAccessToken(); + navigate(PATH.MAIN); + location.reload(); + } + }; + + return ( +
+ 프로필 이미지 +
+ {isEditingName ? ( +
+
+ + + ) : ( +
+ {data?.data.displayName} + +
+ )} + {data?.data.email} +
+ +
+ ); +} + +export default Profile; diff --git a/frontend/src/components/ProtectRoute/ProtectRoute.tsx b/frontend/src/components/ProtectRoute/ProtectRoute.tsx new file mode 100644 index 00000000..23176867 --- /dev/null +++ b/frontend/src/components/ProtectRoute/ProtectRoute.tsx @@ -0,0 +1,21 @@ +import { Navigate, Outlet } from 'react-router-dom'; + +import useUserValue from '@/hooks/useUserValue'; + +import { PATH } from '@/constants'; + +function ProtectRoute() { + const { isAuthenticating, user } = useUserValue(); + + if (isAuthenticating) { + return <>; + } + + if (!user.accessToken) { + return ; + } + + return ; +} + +export default ProtectRoute; diff --git a/frontend/src/components/PublicRoute/PublicRoute.tsx b/frontend/src/components/PublicRoute/PublicRoute.tsx new file mode 100644 index 00000000..b6b15b61 --- /dev/null +++ b/frontend/src/components/PublicRoute/PublicRoute.tsx @@ -0,0 +1,15 @@ +import { Outlet } from 'react-router-dom'; + +import useUserValue from '@/hooks/useUserValue'; + +function PublicRoute() { + const { isAuthenticating } = useUserValue(); + + if (isAuthenticating) { + return <>; + } + + return ; +} + +export default PublicRoute; diff --git a/frontend/src/components/ScheduleAddButton/ScheduleAddButton.stories.tsx b/frontend/src/components/ScheduleAddButton/ScheduleAddButton.stories.tsx new file mode 100644 index 00000000..39160033 --- /dev/null +++ b/frontend/src/components/ScheduleAddButton/ScheduleAddButton.stories.tsx @@ -0,0 +1,17 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; + +import ScheduleAddButton from './ScheduleAddButton'; + +export default { + title: 'Components/ScheduleAddButton', + component: ScheduleAddButton, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ( + +); + +export const Primary = Template.bind({}); +Primary.args = { + onClick: () => void 0, +}; diff --git a/frontend/src/components/ScheduleAddButton/ScheduleAddButton.styles.ts b/frontend/src/components/ScheduleAddButton/ScheduleAddButton.styles.ts new file mode 100644 index 00000000..c9f21a55 --- /dev/null +++ b/frontend/src/components/ScheduleAddButton/ScheduleAddButton.styles.ts @@ -0,0 +1,20 @@ +import { css, Theme } from '@emotion/react'; + +const scheduleAddButton = ({ colors }: Theme) => css` + position: fixed; + right: 7rem; + bottom: 7rem; + + width: 13rem; + height: 13rem; + border-radius: 50%; + box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25); + + background: ${colors.WHITE}; + + font-size: 7rem; + line-height: 7rem; + color: ${colors.YELLOW_500}; +`; + +export { scheduleAddButton }; diff --git a/frontend/src/components/ScheduleAddButton/ScheduleAddButton.tsx b/frontend/src/components/ScheduleAddButton/ScheduleAddButton.tsx new file mode 100644 index 00000000..e835f153 --- /dev/null +++ b/frontend/src/components/ScheduleAddButton/ScheduleAddButton.tsx @@ -0,0 +1,23 @@ +import { useTheme } from '@emotion/react'; + +import Button from '@/components/@common/Button/Button'; + +import { BsCalendarPlusFill } from 'react-icons/bs'; + +import { scheduleAddButton } from './ScheduleAddButton.styles'; + +interface ScheduleAddButtonProps { + onClick: () => void; +} + +function ScheduleAddButton({ onClick }: ScheduleAddButtonProps) { + const theme = useTheme(); + + return ( + + ); +} + +export default ScheduleAddButton; diff --git a/frontend/src/components/ScheduleAddModal/ScheduleAddModal.stories.tsx b/frontend/src/components/ScheduleAddModal/ScheduleAddModal.stories.tsx new file mode 100644 index 00000000..dc2129d8 --- /dev/null +++ b/frontend/src/components/ScheduleAddModal/ScheduleAddModal.stories.tsx @@ -0,0 +1,20 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; + +import ScheduleAddModal from './ScheduleAddModal'; + +export default { + title: 'Components/ScheduleAddModal', + component: ScheduleAddModal, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ; + +export const Primary = Template.bind({}); +Primary.args = { + dateInfo: { + year: 2022, + month: 8, + date: 5, + day: 5, + }, +}; diff --git a/frontend/src/components/ScheduleAddModal/ScheduleAddModal.styles.ts b/frontend/src/components/ScheduleAddModal/ScheduleAddModal.styles.ts new file mode 100644 index 00000000..f56f1364 --- /dev/null +++ b/frontend/src/components/ScheduleAddModal/ScheduleAddModal.styles.ts @@ -0,0 +1,116 @@ +import { css, Theme } from '@emotion/react'; + +const scheduleAddModal = ({ colors }: Theme) => css` + width: 120rem; + padding: 12.5rem; + border-radius: 12px; + + background: ${colors.WHITE}; +`; + +const form = ({ flex }: Theme) => css` + ${flex.column}; + + gap: 6rem; + + height: 100%; +`; + +const categorySelect = ({ colors }: Theme) => css` + width: 100%; + padding: 3rem; + border: 1px solid ${colors.GRAY_400}; + border-radius: 8px; + + font-size: 4rem; +`; + +const allDayButton = ({ colors }: Theme, isAllDay: boolean) => css` + width: 100%; + height: 9rem; + border: 1px solid ${colors.GRAY_500}; + border-radius: 8px; + filter: drop-shadow(0 2px 2px ${colors.GRAY_400}); + + background: ${isAllDay ? colors.YELLOW_500 : colors.WHITE}; + + font-size: 5rem; + color: ${isAllDay ? colors.WHITE : colors.GRAY_600}; +`; + +const dateTime = ({ flex }: Theme) => css` + ${flex.column} + + gap: 2.5rem; + + width: 100%; +`; + +const arrow = ({ colors }: Theme) => css` + font-size: 6rem; + font-weight: bold; + color: ${colors.GRAY_500}; +`; + +const controlButtons = ({ flex }: Theme) => css` + ${flex.row} + + align-self: flex-end; + gap: 5rem; +`; + +const cancelButton = ({ colors }: Theme) => css` + padding: 2rem 3rem; + box-sizing: border-box; + border: 1px solid ${colors.GRAY_500}; + border-radius: 8px; + filter: drop-shadow(0 2px 2px ${colors.GRAY_400}); + + background: ${colors.WHITE}; + + font-size: 4rem; + color: ${colors.GRAY_600}; +`; + +const saveButton = ({ colors }: Theme) => css` + padding: 2rem 3rem; + box-sizing: border-box; + border-radius: 8px; + + filter: drop-shadow(0px 2px 2px ${colors.GRAY_400}); + + background: ${colors.YELLOW_500}; + + font-size: 4rem; + color: ${colors.WHITE}; +`; + +const labelStyle = ({ colors }: Theme) => css` + padding: 0 1rem; + + font-size: 4rem; + color: ${colors.GRAY_800}; +`; + +const selectBoxStyle = ({ flex }: Theme) => css` + ${flex.column}; + + align-items: flex-start; + gap: 2.5rem; + + width: 100%; +`; + +export { + allDayButton, + arrow, + categorySelect, + cancelButton, + controlButtons, + dateTime, + form, + labelStyle, + saveButton, + scheduleAddModal, + selectBoxStyle, +}; diff --git a/frontend/src/components/ScheduleAddModal/ScheduleAddModal.tsx b/frontend/src/components/ScheduleAddModal/ScheduleAddModal.tsx new file mode 100644 index 00000000..d1113c9a --- /dev/null +++ b/frontend/src/components/ScheduleAddModal/ScheduleAddModal.tsx @@ -0,0 +1,222 @@ +import { validateLength } from '@/validation'; +import { useTheme } from '@emotion/react'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useState } from 'react'; +import { useMutation, useQuery, useQueryClient } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import useControlledInput from '@/hooks/useControlledInput'; +import useValidateSchedule from '@/hooks/useValidateSchedule'; + +import { CalendarType } from '@/@types/calendar'; +import { CategoryType } from '@/@types/category'; +import { ScheduleType } from '@/@types/schedule'; + +import { userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; +import Fieldset from '@/components/@common/Fieldset/Fieldset'; + +import { CACHE_KEY } from '@/constants/api'; +import { DATE_TIME } from '@/constants/date'; +import { VALIDATION_MESSAGE, VALIDATION_SIZE } from '@/constants/validate'; + +import { getDate, getDateTime } from '@/utils/date'; + +import categoryApi from '@/api/category'; +import scheduleApi from '@/api/schedule'; + +import { + allDayButton, + arrow, + cancelButton, + categorySelect, + controlButtons, + dateTime, + form, + labelStyle, + saveButton, + scheduleAddModal, + selectBoxStyle, +} from './ScheduleAddModal.styles'; + +interface ScheduleAddModalProps { + dateInfo: CalendarType | null; + closeModal: () => void; +} + +function ScheduleAddModal({ dateInfo, closeModal }: ScheduleAddModalProps) { + const { accessToken } = useRecoilValue(userState); + + const theme = useTheme(); + + const [isAllDay, setAllDay] = useState(true); + + const queryClient = useQueryClient(); + + const { data } = useQuery, AxiosError>( + CACHE_KEY.MY_CATEGORIES, + () => categoryApi.getMy(accessToken), + { + onSuccess: (data) => onSuccessGetCategories(data), + } + ); + + const categoryId = useControlledInput(); + + const { + isLoading, + error, + mutate: postSchedule, + } = useMutation< + AxiosResponse<{ schedules: ScheduleType[] }>, + AxiosError, + Omit, + unknown + >((body) => scheduleApi.post(accessToken, Number(categoryId.inputValue), body), { + onSuccess: () => { + onSuccessPostSchedule(); + }, + }); + + const dateFieldset = isAllDay + ? { + type: 'date', + initialValue: getDate(dateInfo), + } + : { + type: 'datetime-local', + initialValue: getDateTime(dateInfo), + }; + + const validationSchedule = useValidateSchedule({ + initialStartDateTime: dateFieldset.initialValue, + initialEndDateTime: dateFieldset.initialValue, + }); + + const handleClickAllDayButton = () => { + setAllDay((prev) => !prev); + }; + + const handleSubmitScheduleAddForm = (e: React.FormEvent) => { + e.preventDefault(); + + const body = { + title: validationSchedule.title.inputValue, + startDateTime: validationSchedule.startDateTime.inputValue, + endDateTime: validationSchedule.endDateTime.inputValue, + memo: validationSchedule.memo.inputValue, + }; + + if (!isAllDay) { + postSchedule(body); + + return; + } + + const allDayBody = { + ...body, + startDateTime: `${body.startDateTime}T${DATE_TIME.START}`, + endDateTime: `${body.endDateTime}T${DATE_TIME.END}`, + }; + + postSchedule(allDayBody); + }; + + const onSuccessGetCategories = (data: AxiosResponse) => { + categoryId.setInputValue(`${data.data[0].id}`); + }; + + const onSuccessPostSchedule = () => { + queryClient.invalidateQueries(CACHE_KEY.SCHEDULES); + + closeModal(); + }; + + if (isLoading) return <>Loading; + + if (error) return <>Error; + + return ( +
+
+
+ 카테고리 + +
+
+ +
+
+

+
+
+
+
+ + +
+ +
+ ); +} + +export default ScheduleAddModal; diff --git a/frontend/src/components/ScheduleModal/ScheduleModal.styles.ts b/frontend/src/components/ScheduleModal/ScheduleModal.styles.ts new file mode 100644 index 00000000..06913855 --- /dev/null +++ b/frontend/src/components/ScheduleModal/ScheduleModal.styles.ts @@ -0,0 +1,119 @@ +import { css, Theme } from '@emotion/react'; + +import { ModalPosType } from '@/@types'; + +const scheduleModalStyle = ({ colors }: Theme, scheduleModalPos: ModalPosType) => css` + position: absolute; + top: ${scheduleModalPos.top ? `${scheduleModalPos.top + 20}px` : 'none'}; + right: ${scheduleModalPos.right ? `${scheduleModalPos.right + 20}px` : 'none'}; + bottom: ${scheduleModalPos.bottom ? `${scheduleModalPos.bottom + 20}px` : 'none'}; + left: ${scheduleModalPos.left ? `${scheduleModalPos.left + 20}px` : 'none'}; + + padding: 5rem 5rem 10rem 10rem; + border-radius: 8px; + box-shadow: 0 0 30px ${colors.GRAY_500}; + + background: ${colors.WHITE}; +`; + +const headerStyle = ({ flex }: Theme) => css` + ${flex.row}; + + justify-content: flex-end; +`; + +const buttonStyle = ({ colors }: Theme) => css` + position: relative; + + width: 11rem; + height: 11rem; + + font-size: 5rem; + color: ${colors.GRAY_700}; + + &:hover { + border-radius: 50%; + + background: ${colors.GRAY_100}; + + filter: none; + } + + &:hover span { + visibility: visible; + } +`; + +const buttonTitleStyle = ({ colors }: Theme) => css` + visibility: hidden; + position: absolute; + top: 120%; + left: 50%; + transform: translateX(-50%); + + padding: 2rem 3rem; + + background: ${colors.GRAY_700}ee; + + font-size: 3rem; + font-weight: normal; + color: ${colors.WHITE}; + white-space: nowrap; +`; + +const contentStyle = ({ flex }: Theme) => css` + ${flex.column} + + gap: 10rem; +`; + +const contentBlockStyle = ({ colors }: Theme) => css` + display: flex; + gap: 3rem; + + width: 90rem; + + font-size: 4rem; + color: ${colors.GRAY_700}; +`; + +const scheduleIconStyle = css` + margin-top: 1rem; +`; + +const scheduleInfoStyle = ({ flex }: Theme) => css` + ${flex.column} + + align-items: flex-start; + gap: 3rem; +`; + +const scheduleTitleStyle = css` + font-size: 6rem; +`; + +const colorStyle = (colorCode: string | undefined) => css` + width: 4rem; + height: 4rem; + border-radius: 25%; + + background: ${colorCode}; +`; + +const grayTextStyle = ({ colors }: Theme) => css` + color: ${colors.GRAY_600}; +`; + +export { + buttonStyle, + buttonTitleStyle, + colorStyle, + contentBlockStyle, + contentStyle, + grayTextStyle, + headerStyle, + scheduleIconStyle, + scheduleInfoStyle, + scheduleModalStyle, + scheduleTitleStyle, +}; diff --git a/frontend/src/components/ScheduleModal/ScheduleModal.tsx b/frontend/src/components/ScheduleModal/ScheduleModal.tsx new file mode 100644 index 00000000..04516b60 --- /dev/null +++ b/frontend/src/components/ScheduleModal/ScheduleModal.tsx @@ -0,0 +1,155 @@ +import { useTheme } from '@emotion/react'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useMutation, useQuery, useQueryClient } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import { ModalPosType } from '@/@types'; +import { CategoryType } from '@/@types/category'; +import { ProfileType } from '@/@types/profile'; +import { ScheduleType } from '@/@types/schedule'; + +import { userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; + +import { CACHE_KEY } from '@/constants/api'; +import { CATEGORY_TYPE } from '@/constants/category'; +import { CONFIRM_MESSAGE } from '@/constants/message'; + +import categoryApi from '@/api/category'; +import profileApi from '@/api/profile'; +import scheduleApi from '@/api/schedule'; + +import { FiCalendar, FiEdit3 } from 'react-icons/fi'; +import { GrClose } from 'react-icons/gr'; +import { RiDeleteBin6Line } from 'react-icons/ri'; + +import { + buttonStyle, + buttonTitleStyle, + colorStyle, + contentBlockStyle, + contentStyle, + grayTextStyle, + headerStyle, + scheduleIconStyle, + scheduleInfoStyle, + scheduleModalStyle, + scheduleTitleStyle, +} from './ScheduleModal.styles'; + +interface ScheduleModalProps { + scheduleModalPos: ModalPosType; + scheduleInfo: ScheduleType; + toggleScheduleModifyModalOpen: () => void; + closeModal: () => void; +} + +function ScheduleModal({ + scheduleModalPos, + scheduleInfo, + toggleScheduleModifyModalOpen, + closeModal, +}: ScheduleModalProps) { + const { accessToken } = useRecoilValue(userState); + + const theme = useTheme(); + + const queryClient = useQueryClient(); + + const { data: profileGetResponse } = useQuery, AxiosError>( + CACHE_KEY.PROFILE, + () => profileApi.get(accessToken) + ); + + const { data: categoryGetResponse } = useQuery, AxiosError>( + CACHE_KEY.CATEGORY, + () => categoryApi.getSingle(scheduleInfo.categoryId) + ); + + const { mutate } = useMutation( + () => scheduleApi.delete(accessToken, scheduleInfo.id), + { + onSuccess: () => onSuccessDeleteSchedule(), + } + ); + + const onSuccessDeleteSchedule = () => { + queryClient.invalidateQueries(CACHE_KEY.SCHEDULES); + + closeModal(); + }; + + const handleClickModifyButton = () => { + closeModal(); + toggleScheduleModifyModalOpen(); + }; + + const handleClickDeleteButton = () => { + if (confirm(CONFIRM_MESSAGE.DELETE)) { + mutate(); + } + }; + + const formatDateTime = (dateTime: string | undefined) => { + if (dateTime === undefined) { + return; + } + + return dateTime.replace('T', ' '); + }; + + const canEditSchedule = + (scheduleInfo.categoryType === CATEGORY_TYPE.NORMAL || + scheduleInfo.categoryType === CATEGORY_TYPE.PERSONAL) && + profileGetResponse?.data.id === categoryGetResponse?.data.creator.id; + + return ( +
+
+ {canEditSchedule && ( + <> + + + + )} + +
+
+
+ +
+

{scheduleInfo.title}

+

+ {formatDateTime(scheduleInfo.startDateTime)} +   →   + {formatDateTime(scheduleInfo.endDateTime)} +

+ {scheduleInfo.memo &&

{scheduleInfo.memo}

} +
+
+
+
+ + {categoryGetResponse?.data.name} + + {scheduleInfo.categoryType === CATEGORY_TYPE.GOOGLE && ' (구글)'} + {scheduleInfo.categoryType === CATEGORY_TYPE.PERSONAL && ' (기본)'} + + +
+
+
+ ); +} + +export default ScheduleModal; diff --git a/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.styles.ts b/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.styles.ts new file mode 100644 index 00000000..0b2753de --- /dev/null +++ b/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.styles.ts @@ -0,0 +1,123 @@ +import { css, Theme } from '@emotion/react'; + +const modalStyle = ({ colors }: Theme) => css` + width: 120rem; + padding: 12.5rem; + border-radius: 12px; + + background: ${colors.WHITE}; +`; + +const formStyle = ({ flex }: Theme) => css` + ${flex.column}; + + gap: 6rem; +`; + +const categoryStyle = ({ colors }: Theme, colorCode: string) => css` + padding: 0 3rem; + + width: 100%; + height: 12rem; + border: 1px solid ${colors.GRAY_500}; + border-radius: 8px; + + background: ${colorCode}; + + font-size: 5rem; + color: ${colors.WHITE}; + line-height: 12rem; + + &:hover { + cursor: default; + } +`; + +const allDayButtonStyle = ({ colors }: Theme, isAllDay: boolean) => css` + width: 100%; + height: 9rem; + border: 1px solid ${colors.GRAY_500}; + border-radius: 8px; + filter: drop-shadow(0 2px 2px ${colors.GRAY_400}); + + background: ${isAllDay ? colors.YELLOW_500 : colors.WHITE}; + + font-size: 5rem; + color: ${isAllDay ? colors.WHITE : colors.GRAY_600}; +`; + +const dateTimeStyle = ({ flex }: Theme) => css` + ${flex.column} + + gap: 2.5rem; + + width: 100%; +`; + +const arrowStyle = ({ colors }: Theme) => css` + font-size: 6rem; + font-weight: bold; + color: ${colors.GRAY_500}; +`; + +const controlButtonsStyle = ({ flex }: Theme) => css` + ${flex.row} + + align-self: flex-end; + gap: 5rem; +`; + +const cancelButtonStyle = ({ colors }: Theme) => css` + padding: 2rem 3rem; + box-sizing: border-box; + border: 1px solid ${colors.GRAY_500}; + border-radius: 8px; + filter: drop-shadow(0 2px 2px ${colors.GRAY_400}); + + background: ${colors.WHITE}; + + font-size: 4rem; + color: ${colors.GRAY_600}; +`; + +const saveButtonStyle = ({ colors }: Theme) => css` + padding: 2rem 3rem; + box-sizing: border-box; + border-radius: 8px; + filter: drop-shadow(0px 2px 2px ${colors.GRAY_400}); + + background: ${colors.YELLOW_500}; + + font-size: 4rem; + color: ${colors.WHITE}; +`; + +const labelStyle = ({ colors }: Theme) => css` + padding: 0 1rem; + + font-size: 4rem; + color: ${colors.GRAY_800}; +`; + +const categoryBoxStyle = ({ flex }: Theme) => css` + ${flex.column}; + + align-items: flex-start; + gap: 2.5rem; + + width: 100%; +`; + +export { + allDayButtonStyle, + arrowStyle, + cancelButtonStyle, + categoryStyle, + controlButtonsStyle, + dateTimeStyle, + formStyle, + labelStyle, + modalStyle, + saveButtonStyle, + categoryBoxStyle, +}; diff --git a/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.tsx b/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.tsx new file mode 100644 index 00000000..88448b37 --- /dev/null +++ b/frontend/src/components/ScheduleModifyModal/ScheduleModifyModal.tsx @@ -0,0 +1,196 @@ +import { validateLength } from '@/validation'; +import { useTheme } from '@emotion/react'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useState } from 'react'; +import { useMutation, useQuery, useQueryClient } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import useValidateSchedule from '@/hooks/useValidateSchedule'; + +import { CategoryType } from '@/@types/category'; +import { ScheduleType } from '@/@types/schedule'; + +import { userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; +import Fieldset from '@/components/@common/Fieldset/Fieldset'; + +import { CACHE_KEY } from '@/constants/api'; +import { DATE_TIME } from '@/constants/date'; +import { VALIDATION_MESSAGE, VALIDATION_SIZE } from '@/constants/validate'; + +import { checkAllDay, getISODateString } from '@/utils/date'; + +import categoryApi from '@/api/category'; +import scheduleApi from '@/api/schedule'; + +import { + allDayButtonStyle, + arrowStyle, + cancelButtonStyle, + categoryBoxStyle, + categoryStyle, + controlButtonsStyle, + dateTimeStyle, + formStyle, + labelStyle, + modalStyle, + saveButtonStyle, +} from './ScheduleModifyModal.styles'; + +interface ScheduleModifyModalProps { + scheduleInfo: ScheduleType; + closeModal: () => void; +} + +function ScheduleModifyModal({ scheduleInfo, closeModal }: ScheduleModifyModalProps) { + const { accessToken } = useRecoilValue(userState); + + const theme = useTheme(); + + const [isAllDay, setAllDay] = useState( + !!checkAllDay(scheduleInfo.startDateTime, scheduleInfo.endDateTime) + ); + + const queryClient = useQueryClient(); + + const { data } = useQuery, AxiosError>(CACHE_KEY.CATEGORY, () => + categoryApi.getSingle(scheduleInfo.categoryId) + ); + + const { mutate } = useMutation< + AxiosResponse, + AxiosError, + Omit, + unknown + >(CACHE_KEY.SCHEDULE, (body) => scheduleApi.patch(accessToken, scheduleInfo.id, body), { + onSuccess: () => onSuccessPatchSchedule(), + }); + + const onSuccessPatchSchedule = () => { + queryClient.invalidateQueries(CACHE_KEY.SCHEDULES); + closeModal(); + }; + + const getDateFieldsetProps = (dateTime: string) => + isAllDay + ? { + type: 'date', + defaultValue: getISODateString(dateTime), + } + : { + type: 'datetime-local', + defaultValue: dateTime, + }; + + const startDateFieldsetProps = getDateFieldsetProps(scheduleInfo.startDateTime); + const endDateFieldsetProps = getDateFieldsetProps(scheduleInfo.endDateTime); + + const validationSchedule = useValidateSchedule({ + initialTitle: scheduleInfo.title, + initialStartDateTime: startDateFieldsetProps.defaultValue, + initialEndDateTime: endDateFieldsetProps.defaultValue, + initialMemo: scheduleInfo.memo, + }); + + const handleSubmitScheduleModifyForm = (e: React.FormEvent) => { + e.preventDefault(); + + const body = { + title: validationSchedule.title.inputValue, + startDateTime: validationSchedule.startDateTime.inputValue, + endDateTime: validationSchedule.endDateTime.inputValue, + memo: validationSchedule.memo.inputValue, + }; + + if (!isAllDay) { + mutate(body); + + return; + } + + const allDayBody = { + ...body, + startDateTime: `${body.startDateTime}T${DATE_TIME.START}`, + endDateTime: `${body.endDateTime}T${DATE_TIME.END}`, + }; + + mutate(allDayBody); + }; + + const handleClickAllDayButton = () => { + setAllDay((prev) => !prev); + }; + + return ( +
+
+
+
카테고리
+
{data?.data.name}
+
+
+ +
+
+

+
+
+
+
+ + +
+ +
+ ); +} + +export default ScheduleModifyModal; diff --git a/frontend/src/components/SideBar/SideBar.styles.ts b/frontend/src/components/SideBar/SideBar.styles.ts new file mode 100644 index 00000000..18c8371c --- /dev/null +++ b/frontend/src/components/SideBar/SideBar.styles.ts @@ -0,0 +1,19 @@ +import { css, Theme } from '@emotion/react'; + +const sideBar = ({ colors }: Theme, isSideBarOpen: boolean) => css` + overflow-y: overlay; + overflow-x: hidden; + position: fixed; + z-index: 10; + + width: ${isSideBarOpen ? '64rem' : '0'}; + height: calc(100vh - 16rem); + padding: ${isSideBarOpen ? '4rem' : '0'}; + border: 1px solid ${colors.GRAY_400}; + + background: ${colors.WHITE}; + + transition: width 0.3s; +`; + +export { sideBar }; diff --git a/frontend/src/components/SideBar/SideBar.tsx b/frontend/src/components/SideBar/SideBar.tsx new file mode 100644 index 00000000..5f90af8d --- /dev/null +++ b/frontend/src/components/SideBar/SideBar.tsx @@ -0,0 +1,26 @@ +import { useTheme } from '@emotion/react'; +import { useRecoilValue } from 'recoil'; + +import { sideBarState, userState } from '@/recoil/atoms'; + +import FilterCategoryList from '../FilterCategoryList/FilterCategoryList'; +import { sideBar } from './SideBar.styles'; + +function SideBar() { + const { accessToken } = useRecoilValue(userState); + const isSideBarOpen = useRecoilValue(sideBarState); + + const theme = useTheme(); + + if (!accessToken) { + return <>; + } + + return ( +
+ +
+ ); +} + +export default SideBar; diff --git a/frontend/src/components/SnackBar/SnackBar.styles.ts b/frontend/src/components/SnackBar/SnackBar.styles.ts new file mode 100644 index 00000000..7b38678c --- /dev/null +++ b/frontend/src/components/SnackBar/SnackBar.styles.ts @@ -0,0 +1,45 @@ +import { css, Theme } from '@emotion/react'; + +const snackBarStyle = ({ colors }: Theme, isOpen: boolean) => css` + ${isOpen && + css` + z-index: 30; + position: fixed; + bottom: 5rem; + left: 50%; + transform: translateX(-50%); + + padding: 4rem; + border-radius: 3px; + + background: ${colors.YELLOW_500}; + opacity: 0; + + color: ${colors.WHITE}; + font-size: 3.5rem; + line-height: 3.5rem; + text-align: center; + + @keyframes show { + 0% { + opacity: 0; + } + + 50% { + opacity: 1; + } + + 75% { + opacity: 1; + } + + 100% { + opacity: 0; + } + } + + animation: show 2.5s; + `} +`; + +export { snackBarStyle }; diff --git a/frontend/src/components/SnackBar/SnackBar.tsx b/frontend/src/components/SnackBar/SnackBar.tsx new file mode 100644 index 00000000..14096a8d --- /dev/null +++ b/frontend/src/components/SnackBar/SnackBar.tsx @@ -0,0 +1,33 @@ +import { useTheme } from '@emotion/react'; +import { useEffect, useState } from 'react'; +import { useRecoilValue } from 'recoil'; + +import { snackBarState } from '@/recoil/atoms'; + +import { snackBarStyle } from './SnackBar.styles'; + +function SnackBar() { + const theme = useTheme(); + + const snackBarInfo = useRecoilValue(snackBarState); + + const [timer, setTimer] = useState(null); + + useEffect(() => { + if (snackBarInfo.text === '' || timer) { + return; + } + + const newTimer = setTimeout(() => { + setTimer(null); + }, 4000); + + setTimer(newTimer); + }, [snackBarInfo]); + + const isOpen = timer !== null; + + return
{snackBarInfo.text}
; +} + +export default SnackBar; diff --git a/frontend/src/components/SubscribedCategoryItem/SubscribedCategoryItem.styles.ts b/frontend/src/components/SubscribedCategoryItem/SubscribedCategoryItem.styles.ts new file mode 100644 index 00000000..780a9f59 --- /dev/null +++ b/frontend/src/components/SubscribedCategoryItem/SubscribedCategoryItem.styles.ts @@ -0,0 +1,59 @@ +import { css, Theme } from '@emotion/react'; + +const categoryItem = ({ colors, flex }: Theme) => css` + ${flex.row} + + justify-content:space-around; + + height: 20rem; + border-bottom: 1px solid ${colors.GRAY_400}; + + font-size: 4rem; +`; + +const item = css` + flex: 1 1 0; + text-align: center; +`; + +const unsubscribeButton = ({ colors }: Theme) => css` + position: relative; + + width: 15rem; + height: 8rem; + border-radius: 3px; + + background-color: ${colors.GRAY_500}; + + font-size: 3.5rem; + font-weight: 700; + line-height: 3.5rem; + color: ${colors.WHITE}; + + &:hover { + filter: none; + } + + &:hover span { + visibility: visible; + } +`; + +const menuTitle = ({ colors }: Theme) => css` + visibility: hidden; + position: absolute; + top: 120%; + left: 50%; + transform: translateX(-50%); + + padding: 2rem 3rem; + + background: ${colors.GRAY_700}ee; + + font-size: 3rem; + font-weight: normal; + color: ${colors.WHITE}; + white-space: nowrap; +`; + +export { categoryItem, item, menuTitle, unsubscribeButton }; diff --git a/frontend/src/components/SubscribedCategoryItem/SubscribedCategoryItem.tsx b/frontend/src/components/SubscribedCategoryItem/SubscribedCategoryItem.tsx new file mode 100644 index 00000000..1b678bab --- /dev/null +++ b/frontend/src/components/SubscribedCategoryItem/SubscribedCategoryItem.tsx @@ -0,0 +1,75 @@ +import { useTheme } from '@emotion/react'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useMutation, useQuery, useQueryClient } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import { CategoryType } from '@/@types/category'; +import { ProfileType } from '@/@types/profile'; + +import { userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; + +import { CACHE_KEY } from '@/constants/api'; +import { CONFIRM_MESSAGE, TOOLTIP_MESSAGE } from '@/constants/message'; + +import { getISODateString } from '@/utils/date'; + +import profileApi from '@/api/profile'; +import subscriptionApi from '@/api/subscription'; + +import { categoryItem, item, menuTitle, unsubscribeButton } from './SubscribedCategoryItem.styles'; + +interface SubscribedCategoryItemProps { + category: CategoryType; + subscriptionId: number; +} + +function SubscribedCategoryItem({ category, subscriptionId }: SubscribedCategoryItemProps) { + const { accessToken } = useRecoilValue(userState); + const theme = useTheme(); + + const queryClient = useQueryClient(); + + const { data } = useQuery, AxiosError>(CACHE_KEY.PROFILE, () => + profileApi.get(accessToken) + ); + + const { mutate } = useMutation(() => subscriptionApi.delete(accessToken, subscriptionId), { + onSuccess: () => { + queryClient.invalidateQueries(CACHE_KEY.SUBSCRIPTIONS); + }, + }); + + const handleClickUnsubscribeButton = () => { + if (window.confirm(CONFIRM_MESSAGE.UNSUBSCRIBE)) { + mutate(); + } + }; + + const canUnsubscribeCategory = category.creator.id !== data?.data.id; + + return ( +
+ {getISODateString(category.createdAt)} + {category.name} + {category.creator.displayName} +
+ +
+
+ ); +} + +export default SubscribedCategoryItem; diff --git a/frontend/src/components/UnsubscribedCategoryItem/UnsubscribedCategoryItem.styles.ts b/frontend/src/components/UnsubscribedCategoryItem/UnsubscribedCategoryItem.styles.ts new file mode 100644 index 00000000..3a4f8d13 --- /dev/null +++ b/frontend/src/components/UnsubscribedCategoryItem/UnsubscribedCategoryItem.styles.ts @@ -0,0 +1,32 @@ +import { css, Theme } from '@emotion/react'; + +const categoryItem = ({ colors, flex }: Theme) => css` + ${flex.row} + + justify-content:space-around; + + height: 20rem; + border-bottom: 1px solid ${colors.GRAY_400}; + + font-size: 4rem; +`; + +const item = css` + flex: 1 1 0; + text-align: center; +`; + +const subscribeButton = ({ colors }: Theme) => css` + width: 15rem; + height: 8rem; + border-radius: 3px; + + background-color: ${colors.YELLOW_500}; + + font-size: 3.5rem; + font-weight: 700; + line-height: 3.5rem; + color: ${colors.WHITE}; +`; + +export { categoryItem, item, subscribeButton }; diff --git a/frontend/src/components/UnsubscribedCategoryItem/UnsubscribedCategoryItem.tsx b/frontend/src/components/UnsubscribedCategoryItem/UnsubscribedCategoryItem.tsx new file mode 100644 index 00000000..4ea5bff1 --- /dev/null +++ b/frontend/src/components/UnsubscribedCategoryItem/UnsubscribedCategoryItem.tsx @@ -0,0 +1,66 @@ +import { useTheme } from '@emotion/react'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useMutation, useQueryClient } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import { CategoryType } from '@/@types/category'; +import { SubscriptionType } from '@/@types/subscription'; + +import { userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; + +import { CACHE_KEY } from '@/constants/api'; +import { PALETTE } from '@/constants/style'; + +import { getRandomNumber } from '@/utils'; +import { getISODateString } from '@/utils/date'; + +import subscriptionApi from '@/api/subscription'; + +import { categoryItem, item, subscribeButton } from './UnsubscribedCategoryItem.styles'; + +interface UnsubscribedCategoryItemProps { + category: CategoryType; +} + +function UnsubscribedCategoryItem({ category }: UnsubscribedCategoryItemProps) { + const { accessToken } = useRecoilValue(userState); + + const theme = useTheme(); + + const body = { + colorCode: PALETTE[getRandomNumber(0, PALETTE.length)], + }; + + const queryClient = useQueryClient(); + const { mutate } = useMutation< + AxiosResponse>, + AxiosError, + Pick, + unknown + >(() => subscriptionApi.post(accessToken, category.id, body), { + onSuccess: () => { + queryClient.invalidateQueries(CACHE_KEY.SUBSCRIPTIONS); + }, + }); + + const handleClickSubscribeButton = () => { + mutate(body); + }; + + return ( +
+ {getISODateString(category.createdAt)} + {category.name} + {category.creator.displayName} +
+ +
+
+ ); +} + +export default UnsubscribedCategoryItem; diff --git a/frontend/src/constants/api.ts b/frontend/src/constants/api.ts new file mode 100644 index 00000000..08de8588 --- /dev/null +++ b/frontend/src/constants/api.ts @@ -0,0 +1,29 @@ +const API = { + AUTH_CODE_KEY: 'code', + CATEGORY_GET_SIZE: 4, +}; + +const API_URL = process.env.API_URL; + +const CACHE_KEY = { + AUTH: 'auth', + CATEGORIES: 'categories', + CATEGORY: 'category', + ENTER: 'enter', + GOOGLE_CALENDAR: 'googleCalendar', + MY_CATEGORIES: 'myCategories', + PROFILE: 'profile', + SCHEDULE: 'schedule', + SCHEDULER: 'scheduler', + SCHEDULES: 'schedules', + SUBSCRIPTIONS: 'subscriptions', + VALIDATE: 'validate', +}; + +const RESPONSE = { + STATUS: { + UNAUTHORIZED: 401, + }, +}; + +export { API, API_URL, CACHE_KEY, RESPONSE }; diff --git a/frontend/src/constants/category.ts b/frontend/src/constants/category.ts new file mode 100644 index 00000000..b82002b4 --- /dev/null +++ b/frontend/src/constants/category.ts @@ -0,0 +1,7 @@ +const CATEGORY_TYPE = { + GOOGLE: 'GOOGLE', + NORMAL: 'NORMAL', + PERSONAL: 'PERSONAL', +}; + +export { CATEGORY_TYPE }; diff --git a/frontend/src/constants/date.ts b/frontend/src/constants/date.ts new file mode 100644 index 00000000..877d4d00 --- /dev/null +++ b/frontend/src/constants/date.ts @@ -0,0 +1,8 @@ +const DATE_TIME = { + START: '00:00', + END: '23:59', +}; + +const DAYS = ['일', '월', '화', '수', '목', '금', '토']; + +export { DATE_TIME, DAYS }; diff --git a/frontend/src/constants/index.ts b/frontend/src/constants/index.ts new file mode 100644 index 00000000..f95f97c4 --- /dev/null +++ b/frontend/src/constants/index.ts @@ -0,0 +1,28 @@ +const ATOM_KEY = { + SNACK_BAR: 'snackBarState', + SIDE_BAR: 'sideBarState', + USER: 'userState', +}; + +const CALENDAR = { + MAX_VIEW: 10, + EMPTY_TITLE: '제목 없음', +}; + +const SELECTOR_KEY = { + SIDE_BAR: 'sideBarSelector', +}; + +const STORAGE_KEY = { + ACCESS_TOKEN: 'accessToken', +}; + +const PATH = { + MAIN: '/', + AUTH: '/oauth', + CATEGORY: '/category', + POLICY: '/policy', + SCHEDULING: '/scheduling', +}; + +export { ATOM_KEY, CALENDAR, SELECTOR_KEY, STORAGE_KEY, PATH }; diff --git a/frontend/src/constants/message.ts b/frontend/src/constants/message.ts new file mode 100644 index 00000000..e4c4bbfb --- /dev/null +++ b/frontend/src/constants/message.ts @@ -0,0 +1,16 @@ +const CONFIRM_MESSAGE = { + DELETE: '정말 삭제하시겠습니까?', + UNSUBSCRIBE: '구독을 해제하시겠습니까?', + LOGOUT: '로그아웃하시겠습니까?', +}; + +const ERROR_MESSAGE = { + DEFAULT: '에러가 발생했습니다. 잠시 후에 다시 시도해주세요.', +}; + +const TOOLTIP_MESSAGE = { + CANNOT_UNSUBSCRIBE_MINE: '나의 카테고리는 구독 취소할 수 없습니다.', + CANNOT_EDIT_DELETE_DEFAULT_CATEGORY: '기본 카테고리는 수정/삭제가 불가능합니다.', +}; + +export { CONFIRM_MESSAGE, ERROR_MESSAGE, TOOLTIP_MESSAGE }; diff --git a/frontend/src/constants/style.ts b/frontend/src/constants/style.ts new file mode 100644 index 00000000..4cce3499 --- /dev/null +++ b/frontend/src/constants/style.ts @@ -0,0 +1,30 @@ +const PALETTE = [ + '#AD1457', + '#D81B60', + '#D50000', + '#E67C73', + '#F4511E', + '#EF6C00', + '#F09300', + '#F6BF26', + '#E4C441', + '#C0CA33', + '#7CB342', + '#33B679', + '#0B8043', + '#009688', + '#039BE5', + '#4285F4', + '#3F51B5', + '#7986CB', + '#B39DDB', + '#9E69AF', + '#8E24AA', + '#795548', + '#616161', + '#A79B8E', +]; + +const TRANSPARENT = 'transparent'; + +export { PALETTE, TRANSPARENT }; diff --git a/frontend/src/constants/validate.ts b/frontend/src/constants/validate.ts new file mode 100644 index 00000000..3a2c98f4 --- /dev/null +++ b/frontend/src/constants/validate.ts @@ -0,0 +1,17 @@ +const VALIDATION_SIZE = { + MIN_LENGTH: 1, + SCHEDULE_MEMO_MAX_LENGTH: 255, + SCHEDULE_TITLE_MAX_LENGTH: 50, + CATEGORY_NAME_MAX_LENGTH: 20, +}; + +const VALIDATION_STRING = { + CATEGORY: '내 일정', +}; + +const VALIDATION_MESSAGE = { + STRING_LENGTH: (min: number, max: number) => `${min}자 ~ ${max}자로 입력해주세요.`, + INVALID_CATEGORY_NAME: `"${VALIDATION_STRING.CATEGORY}"을 카테고리 이름으로 지정할 수 없습니다.`, +}; + +export { VALIDATION_MESSAGE, VALIDATION_STRING, VALIDATION_SIZE }; diff --git a/frontend/src/hooks/useCalendar.ts b/frontend/src/hooks/useCalendar.ts new file mode 100644 index 00000000..b8273f79 --- /dev/null +++ b/frontend/src/hooks/useCalendar.ts @@ -0,0 +1,67 @@ +import { useState } from 'react'; + +import { + getBeforeYearMonth, + getCalendarMonth, + getFormattedDate, + getNextYearMonth, + getThisMonth, + getThisYear, +} from '@/utils/date'; + +function useCalendar() { + const [current, setCurrent] = useState({ + year: getThisYear(), + month: getThisMonth(), + }); + + const [calendarMonth, setCalendarMonth] = useState( + getCalendarMonth(getThisYear(), getThisMonth()) + ); + + const moveToBeforeMonth = () => { + const { year, month } = getBeforeYearMonth(current.year, current.month); + + setCurrent({ year, month }); + setCalendarMonth(getCalendarMonth(year, month)); + }; + + const moveToToday = () => { + const year = getThisYear(); + const month = getThisMonth(); + + setCurrent({ year, month }); + setCalendarMonth(getCalendarMonth(year, month)); + }; + + const moveToNextMonth = () => { + const { year, month } = getNextYearMonth(current.year, current.month); + + setCurrent({ year, month }); + setCalendarMonth(getCalendarMonth(year, month)); + }; + + const startDate = getFormattedDate( + calendarMonth[0].year, + calendarMonth[0].month, + calendarMonth[0].date + ); + + const endDate = getFormattedDate( + calendarMonth[calendarMonth.length - 1].year, + calendarMonth[calendarMonth.length - 1].month, + calendarMonth[calendarMonth.length - 1].date + ); + + return { + calendarMonth, + current, + endDate, + moveToBeforeMonth, + moveToNextMonth, + moveToToday, + startDate, + }; +} + +export default useCalendar; diff --git a/frontend/src/hooks/useControlledInput.ts b/frontend/src/hooks/useControlledInput.ts new file mode 100644 index 00000000..96f763aa --- /dev/null +++ b/frontend/src/hooks/useControlledInput.ts @@ -0,0 +1,21 @@ +import { useEffect, useState } from 'react'; + +function useControlledInput(initialInputValue?: string) { + const [inputValue, setInputValue] = useState(initialInputValue ?? ''); + + const onChangeValue = ({ + target, + }: React.ChangeEvent | React.ChangeEvent) => { + if (target instanceof HTMLInputElement || target instanceof HTMLSelectElement) { + setInputValue(target.value); + } + }; + + useEffect(() => { + setInputValue(initialInputValue ?? ''); + }, [initialInputValue]); + + return { inputValue, setInputValue, onChangeValue }; +} + +export default useControlledInput; diff --git a/frontend/src/hooks/useIntersect.ts b/frontend/src/hooks/useIntersect.ts new file mode 100644 index 00000000..9458cf94 --- /dev/null +++ b/frontend/src/hooks/useIntersect.ts @@ -0,0 +1,35 @@ +import { useCallback, useEffect, useRef } from 'react'; + +type IntersectHandler = (entry: IntersectionObserverEntry, observer: IntersectionObserver) => void; + +function useIntersect(onIntersect: IntersectHandler, options?: IntersectionObserverInit) { + const ref = useRef(null); + const callback = useCallback( + (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + onIntersect(entry, observer); + } + }); + }, + [onIntersect] + ); + + useEffect(() => { + if (!ref.current) { + return; + } + + const observer = new IntersectionObserver(callback, options); + + observer.observe(ref.current); + + return () => { + observer.disconnect(); + }; + }, [ref, options, callback]); + + return ref; +} + +export default useIntersect; diff --git a/frontend/src/hooks/useSchedulePriority.ts b/frontend/src/hooks/useSchedulePriority.ts new file mode 100644 index 00000000..3e73d101 --- /dev/null +++ b/frontend/src/hooks/useSchedulePriority.ts @@ -0,0 +1,121 @@ +import { ScheduleType } from '@/@types/schedule'; + +import { CALENDAR } from '@/constants'; + +import { getFormattedDate, getISODateString } from '@/utils/date'; + +interface DateType { + year: number; + month: number; + date: number; + day: number; +} + +function useSchedulePriority(calendarMonth: DateType[]) { + const calendarInfoWithPriority = calendarMonth.reduce( + ( + acc: { + [key: string]: Array; + }, + cur: DateType + ) => { + const { year, month, date } = cur; + + acc[getFormattedDate(year, month, date)] = new Array(CALENDAR.MAX_VIEW).fill(false); + + return acc; + }, + {} + ); + + const getLongTermsPriority = (longTerms: Array) => + longTerms.map((el) => { + const startDate = getISODateString(el.startDateTime); + const endDate = getISODateString(el.endDateTime); + + const scheduleRange = calendarMonth + .filter((el) => { + const date = getFormattedDate(el.year, el.month, el.date); + + return startDate <= date && date <= endDate; + }) + .map((el) => getFormattedDate(el.year, el.month, el.date)); + + if (!calendarInfoWithPriority.hasOwnProperty(scheduleRange[0])) { + return { + schedule: el, + priority: CALENDAR.MAX_VIEW + 1, + }; + } + + const priorityPosition = calendarInfoWithPriority[scheduleRange[0]].findIndex( + (el) => el === false + ); + + if (priorityPosition === -1) { + return { + schedule: el, + priority: CALENDAR.MAX_VIEW + 1, + }; + } + + scheduleRange.forEach((el) => { + if (calendarInfoWithPriority.hasOwnProperty(el)) { + calendarInfoWithPriority[el][priorityPosition] = true; + } + }); + + return { + schedule: el, + priority: priorityPosition + 1, + }; + }); + + const getAllDaysPriority = (allDays: Array) => + allDays.map((el) => { + const startDate = getISODateString(el.startDateTime); + const priorityPosition = calendarInfoWithPriority[startDate].findIndex((el) => el === false); + + if (priorityPosition === -1) { + return { + schedule: el, + priority: CALENDAR.MAX_VIEW + 1, + }; + } + + calendarInfoWithPriority[startDate][priorityPosition] = true; + + return { + schedule: el, + priority: priorityPosition + 1, + }; + }); + + const getFewHoursPriority = (fewHours: Array) => + fewHours.map((el) => { + const startDate = getISODateString(el.startDateTime); + const priorityPosition = calendarInfoWithPriority[startDate].findIndex((el) => el === false); + + if (priorityPosition === -1) { + return { + schedule: el, + priority: CALENDAR.MAX_VIEW + 1, + }; + } + + calendarInfoWithPriority[startDate][priorityPosition] = true; + + return { + schedule: el, + priority: priorityPosition + 1, + }; + }); + + return { + getLongTermsPriority, + getAllDaysPriority, + getFewHoursPriority, + }; +} + +export default useSchedulePriority; diff --git a/frontend/src/hooks/useSnackBar.ts b/frontend/src/hooks/useSnackBar.ts new file mode 100644 index 00000000..57de3351 --- /dev/null +++ b/frontend/src/hooks/useSnackBar.ts @@ -0,0 +1,15 @@ +import { useSetRecoilState } from 'recoil'; + +import { snackBarState } from '@/recoil/atoms'; + +function useSnackBar() { + const setText = useSetRecoilState(snackBarState); + + const openSnackBar = (text: string) => { + setText({ text }); + }; + + return { openSnackBar }; +} + +export default useSnackBar; diff --git a/frontend/src/hooks/useToggle.ts b/frontend/src/hooks/useToggle.ts new file mode 100644 index 00000000..535ed11f --- /dev/null +++ b/frontend/src/hooks/useToggle.ts @@ -0,0 +1,13 @@ +import { useState } from 'react'; + +function useToggle(initialState = false) { + const [state, setState] = useState(initialState); + + const toggleState = () => { + setState((prev) => !prev); + }; + + return { state, toggleState }; +} + +export default useToggle; diff --git a/frontend/src/hooks/useUserValue.ts b/frontend/src/hooks/useUserValue.ts new file mode 100644 index 00000000..d6345438 --- /dev/null +++ b/frontend/src/hooks/useUserValue.ts @@ -0,0 +1,38 @@ +import { AxiosError, AxiosResponse } from 'axios'; +import { useQuery } from 'react-query'; +import { useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'; + +import { sideBarState, userState } from '@/recoil/atoms'; + +import { CACHE_KEY } from '@/constants/api'; + +import { removeAccessToken } from '@/utils/storage'; + +import loginApi from '@/api/login'; + +function useUserValue() { + const user = useRecoilValue(userState); + const resetUser = useResetRecoilState(userState); + + const setSideBarOpen = useSetRecoilState(sideBarState); + + const { isLoading } = useQuery( + CACHE_KEY.VALIDATE, + () => loginApi.validate(user.accessToken), + { + onError: () => onErrorValidate(), + retry: false, + useErrorBoundary: false, + } + ); + + const onErrorValidate = () => { + setSideBarOpen(false); + removeAccessToken(); + resetUser(); + }; + + return { isAuthenticating: isLoading, user }; +} + +export default useUserValue; diff --git a/frontend/src/hooks/useValidateCategory.ts b/frontend/src/hooks/useValidateCategory.ts new file mode 100644 index 00000000..37cd1d4d --- /dev/null +++ b/frontend/src/hooks/useValidateCategory.ts @@ -0,0 +1,41 @@ +import { validateLength, validateNotEqualString } from '@/validation'; + +import { VALIDATION_MESSAGE, VALIDATION_SIZE, VALIDATION_STRING } from '@/constants/validate'; + +import useControlledInput from './useControlledInput'; + +function useValidateCategory(initialCategory?: string) { + const categoryValue = useControlledInput(initialCategory); + + const getCategoryErrorMessage = () => { + if ( + !validateLength( + categoryValue.inputValue, + VALIDATION_SIZE.MIN_LENGTH, + VALIDATION_SIZE.CATEGORY_NAME_MAX_LENGTH + ) + ) { + return VALIDATION_MESSAGE.STRING_LENGTH( + VALIDATION_SIZE.MIN_LENGTH, + VALIDATION_SIZE.CATEGORY_NAME_MAX_LENGTH + ); + } + + if (!validateNotEqualString(categoryValue.inputValue, VALIDATION_STRING.CATEGORY)) { + return VALIDATION_MESSAGE.INVALID_CATEGORY_NAME; + } + + return undefined; + }; + + const isValidCategory = + validateLength( + categoryValue.inputValue, + VALIDATION_SIZE.MIN_LENGTH, + VALIDATION_SIZE.CATEGORY_NAME_MAX_LENGTH + ) && validateNotEqualString(categoryValue.inputValue, VALIDATION_STRING.CATEGORY); + + return { categoryValue, getCategoryErrorMessage, isValidCategory }; +} + +export default useValidateCategory; diff --git a/frontend/src/hooks/useValidateSchedule.ts b/frontend/src/hooks/useValidateSchedule.ts new file mode 100644 index 00000000..998c78f3 --- /dev/null +++ b/frontend/src/hooks/useValidateSchedule.ts @@ -0,0 +1,84 @@ +import { validateLength, validateNotEmpty, validateStartEndDateTime } from '@/validation'; +import { useEffect } from 'react'; + +import { VALIDATION_SIZE } from '@/constants/validate'; + +import { getOneHourEarlierISOString, getOneHourLaterISOString } from '@/utils/date'; + +import useControlledInput from './useControlledInput'; + +interface useValidateScheduleParametersType { + initialTitle?: string; + initialStartDateTime?: string; + initialEndDateTime?: string; + initialMemo?: string; +} + +function useValidateSchedule({ + initialTitle, + initialStartDateTime, + initialEndDateTime, + initialMemo, +}: useValidateScheduleParametersType) { + const title = useControlledInput(initialTitle); + const startDateTime = useControlledInput(initialStartDateTime); + const endDateTime = useControlledInput(initialEndDateTime); + const memo = useControlledInput(initialMemo); + + useEffect(() => { + const resetEndDateTimeValue = () => { + if (startDateTime.inputValue <= endDateTime.inputValue) { + return; + } + + if (!startDateTime.inputValue.includes('T')) { + endDateTime.setInputValue(startDateTime.inputValue); + + return; + } + + endDateTime.setInputValue(getOneHourLaterISOString(startDateTime.inputValue)); + }; + + resetEndDateTimeValue(); + }, [startDateTime.inputValue]); + + useEffect(() => { + const resetStartDateTimeValue = () => { + if (endDateTime.inputValue >= startDateTime.inputValue) { + return; + } + + if (!endDateTime.inputValue.includes('T')) { + startDateTime.setInputValue(endDateTime.inputValue); + + return; + } + + startDateTime.setInputValue(getOneHourEarlierISOString(endDateTime.inputValue)); + }; + + resetStartDateTimeValue(); + }, [endDateTime.inputValue]); + + const isValidSchedule = + validateLength( + title.inputValue, + VALIDATION_SIZE.MIN_LENGTH, + VALIDATION_SIZE.SCHEDULE_TITLE_MAX_LENGTH + ) && + validateStartEndDateTime(startDateTime.inputValue, endDateTime.inputValue) && + validateLength(memo.inputValue, 0, VALIDATION_SIZE.SCHEDULE_MEMO_MAX_LENGTH) && + validateNotEmpty(startDateTime.inputValue) && + validateNotEmpty(endDateTime.inputValue); + + return { + title, + startDateTime, + endDateTime, + memo, + isValidSchedule, + }; +} + +export default useValidateSchedule; diff --git a/frontend/src/index.html b/frontend/src/index.html new file mode 100644 index 00000000..43007932 --- /dev/null +++ b/frontend/src/index.html @@ -0,0 +1,12 @@ + + + + + + 달록 + + +
+ + + diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx new file mode 100644 index 00000000..e5c38264 --- /dev/null +++ b/frontend/src/index.tsx @@ -0,0 +1,27 @@ +import { ThemeProvider } from '@emotion/react'; +import { createRoot } from 'react-dom/client'; +import { RecoilRoot } from 'recoil'; + +import GlobalStyle from '@/styles/GlobalStyle'; +import theme from '@/styles/theme'; + +import { worker } from '@/mocks/browser'; + +import App from './App'; + +if (process.env.NODE_ENV === 'development') { + worker.start(); +} + +const root = document.getElementById('root') as HTMLElement; + +const rootElement = createRoot(root); + +rootElement.render( + + + + + + +); diff --git a/frontend/src/mocks/browser.ts b/frontend/src/mocks/browser.ts new file mode 100644 index 00000000..9c10cad9 --- /dev/null +++ b/frontend/src/mocks/browser.ts @@ -0,0 +1,5 @@ +import { setupWorker } from 'msw'; + +import { handlers } from './handlers'; + +export const worker = setupWorker(...handlers); diff --git a/frontend/src/mocks/data.ts b/frontend/src/mocks/data.ts new file mode 100644 index 00000000..b6d36a0e --- /dev/null +++ b/frontend/src/mocks/data.ts @@ -0,0 +1,445 @@ +import { ScheduleType } from '@/@types/schedule'; + +const matProfileDB = { + id: 1, + email: 'example@email.com', + displayName: '매트', + profileImageUrl: 'https://img.insight.co.kr/static/2020/05/04/700/51wlu5y2281iku1o0hms.jpg', + socialType: 'GOOGLE', +}; + +const tigerProfileDB = { + id: 2, + email: 'tiger@dallog.com', + displayName: '티거', + profileImageUrl: + 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRBo4PPBaz3xqgKPEs8W2djKBXYb2D8PFDkZg&usqp=CAU', + socialType: 'GOOGLE', +}; + +const categoryDB = { + categories: [ + { + id: 1, + name: 'BE 공식 일정', + createdAt: '2022-07-04T13:00:00', + creator: matProfileDB, + }, + { + id: 2, + name: '알록달록', + createdAt: '2022-07-08T15:00:00', + creator: matProfileDB, + }, + { + id: 3, + name: '레벨 1 공원조', + createdAt: '2022-07-05T13:00:00', + creator: tigerProfileDB, + }, + { + id: 4, + name: '자바 스터디', + createdAt: '2022-07-03T15:00:00', + creator: matProfileDB, + }, + { + id: 5, + name: '코틀린 스터디', + createdAt: '2022-07-02T13:00:00', + creator: matProfileDB, + }, + { + id: 6, + name: '지원 플랫폼 근로', + createdAt: '2022-07-01T15:00:00', + creator: matProfileDB, + }, + { + id: 7, + name: '레벨 2 준조', + createdAt: '2022-07-08T15:00:00', + creator: tigerProfileDB, + }, + { + id: 8, + name: '레벨 1 네오조', + createdAt: '2022-07-12T15:00:00', + creator: matProfileDB, + }, + { + id: 9, + name: '매트의 아고라', + createdAt: '2022-07-13T13:00:00', + creator: matProfileDB, + }, + { + id: 10, + name: '자바스크립트 스터디', + createdAt: '2022-07-05T13:00:00', + creator: tigerProfileDB, + }, + { + id: 11, + name: '리액트 스터디', + createdAt: '2022-07-03T15:00:00', + creator: tigerProfileDB, + }, + { + id: 12, + name: 'FE 공식 일정', + createdAt: '2022-07-16T15:00:00', + creator: tigerProfileDB, + }, + { + id: 13, + name: '레벨 2 브리조', + createdAt: '2022-07-18T13:00:00', + creator: matProfileDB, + }, + { + id: 14, + name: '세미나', + createdAt: '2022-07-20T15:00:00', + creator: matProfileDB, + }, + { + id: 15, + name: '네오 면담', + createdAt: '2022-07-01T15:00:00', + creator: tigerProfileDB, + }, + { + id: 16, + name: '공원 면담', + createdAt: '2022-07-11T13:00:00', + creator: tigerProfileDB, + }, + { + id: 17, + name: '포코 면담', + createdAt: '2022-07-12T15:00:00', + creator: tigerProfileDB, + }, + { + id: 18, + name: '포비의 수다 타임', + createdAt: '2022-07-13T13:00:00', + creator: matProfileDB, + }, + { + id: 19, + name: '튼튼이 클럽', + createdAt: '2022-07-13T13:00:00', + creator: tigerProfileDB, + }, + { + id: 20, + name: '한강팟', + createdAt: '2022-07-14T15:00:00', + creator: tigerProfileDB, + }, + ], +}; + +const myCategoryDB = { + categories: [ + { + id: 3, + name: '레벨 1 공원조', + createdAt: '2022-07-05T13:00:00', + creator: tigerProfileDB, + }, + { + id: 7, + name: '레벨 2 준조', + createdAt: '2022-07-08T15:00:00', + creator: tigerProfileDB, + }, + { + id: 10, + name: '자바스크립트 스터디', + createdAt: '2022-07-05T13:00:00', + creator: tigerProfileDB, + }, + { + id: 11, + name: '리액트 스터디', + createdAt: '2022-07-03T15:00:00', + creator: tigerProfileDB, + }, + { + id: 12, + name: 'FE 공식 일정', + createdAt: '2022-07-16T15:00:00', + creator: tigerProfileDB, + }, + { + id: 15, + name: '네오 면담', + createdAt: '2022-07-01T15:00:00', + creator: tigerProfileDB, + }, + { + id: 16, + name: '공원 면담', + createdAt: '2022-07-11T13:00:00', + creator: tigerProfileDB, + }, + { + id: 17, + name: '포코 면담', + createdAt: '2022-07-12T15:00:00', + creator: tigerProfileDB, + }, + { + id: 19, + name: '튼튼이 클럽', + createdAt: '2022-07-13T13:00:00', + creator: tigerProfileDB, + }, + { + id: 20, + name: '한강팟', + createdAt: '2022-07-14T15:00:00', + creator: tigerProfileDB, + }, + ], +}; + +const scheduleDB = [ + { + id: '1', + title: '공원조 회식', + startDateTime: '2022-07-07T18:00', + endDateTime: '2022-07-07T22:00', + memo: '잠실역', + categoryId: 3, + }, + { + id: '2', + title: 'Typescript 강의', + startDateTime: '2022-07-09T10:00', + endDateTime: '2022-07-10T12:30', + memo: '포코', + categoryId: 12, + }, + { + id: '3', + title: '어깨', + startDateTime: '2022-07-17T00:00', + endDateTime: '2022-07-17T23:59', + memo: '', + categoryId: 19, + }, + { + id: '4', + title: '준조 MT', + startDateTime: '2022-07-17T17:00', + endDateTime: '2022-07-21T07:00', + memo: '', + categoryId: 7, + }, + { + id: '5', + title: '제주 여행', + startDateTime: '2022-07-04T13:00', + endDateTime: '2022-07-06T07:00', + memo: '✈️', + categoryId: 20, + }, + { + id: '6', + title: '공원조 MT', + startDateTime: '2022-07-23T13:00', + endDateTime: '2022-07-23T15:00', + memo: '리액트 MT', + categoryId: 13, + }, +]; + +const getScheduleDB: { [key: string]: ScheduleType[] } = { + longTerms: [ + { + id: '1', + title: '공원조 회식', + startDateTime: '2022-08-04T13:00', + endDateTime: '2022-08-06T07:00', + memo: '잠실역', + categoryId: 3, + colorCode: '#F37970', + categoryType: 'NORMAL', + }, + { + id: '2', + title: '준조 MT', + startDateTime: '2022-08-17T13:00', + endDateTime: '2022-08-21T07:00', + memo: '', + categoryId: 7, + colorCode: '#FEDA15', + categoryType: 'NORMAL', + }, + { + id: '3', + title: '제주 여행', + startDateTime: '2022-08-05T13:00', + endDateTime: '2022-08-22T07:00', + memo: '✈️', + categoryId: 20, + colorCode: '#0C2D48', + categoryType: 'NORMAL', + }, + ], + + allDays: [ + { + id: '4', + title: '어깨', + startDateTime: '2022-08-17T00:00', + endDateTime: '2022-08-17T23:59', + memo: '', + categoryId: 19, + colorCode: '#695E93', + categoryType: 'NORMAL', + }, + ], + + fewHours: [ + { + id: '5', + title: '모의 면접', + startDateTime: '2022-08-23T13:00', + endDateTime: '2022-08-23T15:00', + memo: '실행 컨텍스트', + categoryId: 10, + colorCode: '#81B622', + categoryType: 'NORMAL', + }, + ], +}; + +const subscriptionDB = { + subscriptions: [ + { + id: 1, + category: { + id: 3, + name: '레벨 1 공원조', + createdAt: '2022-07-05T13:00:00', + creator: tigerProfileDB, + }, + colorCode: '#F37970', + checked: true, + }, + { + id: 2, + category: { + id: 7, + name: '레벨 2 준조', + createdAt: '2022-07-08T15:00:00', + creator: tigerProfileDB, + }, + colorCode: '#FEDA15', + checked: true, + }, + { + id: 3, + category: { + id: 10, + name: '자바스크립트 스터디', + createdAt: '2022-07-05T13:00:00', + creator: tigerProfileDB, + }, + colorCode: '#81B622', + checked: true, + }, + { + id: 4, + category: { + id: 11, + name: '리액트 스터디', + createdAt: '2022-07-03T15:00:00', + creator: tigerProfileDB, + }, + colorCode: '#145DA0', + checked: true, + }, + { + id: 5, + category: { + id: 12, + name: 'FE 공식 일정', + createdAt: '2022-07-16T15:00:00', + creator: tigerProfileDB, + }, + colorCode: '#464033', + checked: true, + }, + { + id: 6, + category: { + id: 15, + name: '네오 면담', + createdAt: '2022-07-01T15:00:00', + creator: tigerProfileDB, + }, + colorCode: '#7E7C73', + checked: false, + }, + { + id: 7, + category: { + id: 16, + name: '공원 면담', + createdAt: '2022-07-11T13:00:00', + creator: tigerProfileDB, + }, + colorCode: '#7E7C73', + checked: false, + }, + { + id: 8, + category: { + id: 17, + name: '포코 면담', + createdAt: '2022-07-12T15:00:00', + creator: tigerProfileDB, + }, + colorCode: '#7E7C73', + checked: false, + }, + { + id: 9, + category: { + id: 19, + name: '튼튼이 클럽', + createdAt: '2022-07-13T13:00:00', + creator: tigerProfileDB, + }, + colorCode: '#695E93', + checked: true, + }, + { + id: 10, + category: { + id: 20, + name: '한강팟', + createdAt: '2022-07-14T15:00:00', + creator: tigerProfileDB, + }, + colorCode: '#0C2D48', + checked: true, + }, + ], +}; + +export { + categoryDB, + getScheduleDB, + matProfileDB, + myCategoryDB, + scheduleDB, + subscriptionDB, + tigerProfileDB, +}; diff --git a/frontend/src/mocks/handlers.ts b/frontend/src/mocks/handlers.ts new file mode 100644 index 00000000..957668c4 --- /dev/null +++ b/frontend/src/mocks/handlers.ts @@ -0,0 +1,263 @@ +import { rest } from 'msw'; + +import { CategoryType } from '@/@types/category'; +import { ProfileType } from '@/@types/profile'; +import { ScheduleType } from '@/@types/schedule'; +import { SubscriptionType } from '@/@types/subscription'; + +import { API, API_URL } from '@/constants/api'; +import { CATEGORY_TYPE } from '@/constants/category'; + +import categoryApi from '@/api/category'; +import profileApi from '@/api/profile'; +import scheduleApi from '@/api/schedule'; +import subscriptionApi from '@/api/subscription'; + +import { + categoryDB, + getScheduleDB, + myCategoryDB, + scheduleDB, + subscriptionDB, + tigerProfileDB, +} from './data'; + +const handlers = [ + rest.get(API_URL + categoryApi.endpoint.entire, (req, res, ctx) => { + const page = parseInt(req.url.searchParams.get('page') as string); + const size = API.CATEGORY_GET_SIZE; + const name = req.url.searchParams.get('name'); + + if (name === null || name === '') { + const entireCategories = categoryDB.categories.slice(page * size, page * size + size); + + return res(ctx.status(200), ctx.json({ page, categories: entireCategories })); + } + + const filteredCategories = categoryDB.categories + .filter((category) => category.name.includes(name)) + .slice(page * size, page * size + size); + + return res(ctx.status(200), ctx.json({ page, categories: filteredCategories })); + }), + + rest.post>(API_URL + categoryApi.endpoint.entire, (req, res, ctx) => { + const newCategory: CategoryType = { + ...req.body, + id: categoryDB.categories.length + 1, + createdAt: new Date().toISOString().slice(0, -5), + creator: tigerProfileDB, + categoryType: CATEGORY_TYPE.NORMAL, + }; + + categoryDB.categories.push(newCategory); + myCategoryDB.categories.push(newCategory); + + return res(ctx.status(201), ctx.json(newCategory)); + }), + + rest.patch>( + `${API_URL}${categoryApi.endpoint.entire}/:id`, + (req, res, ctx) => { + const { id } = req.params; + const categoryId = parseInt(id as string); + const categoryIndex = categoryDB.categories.findIndex((el) => el.id === categoryId); + const myCategoryIndex = myCategoryDB.categories.findIndex((el) => el.id === categoryId); + const subscriptionIndex = subscriptionDB.subscriptions.findIndex( + (el) => el.category.id === categoryId + ); + + if (categoryIndex < 0 || myCategoryIndex < 0 || subscriptionIndex < 0) { + return res(ctx.status(404)); + } + + categoryDB.categories[categoryIndex] = { + ...categoryDB.categories[categoryIndex], + ...req.body, + }; + myCategoryDB.categories[myCategoryIndex] = { + ...myCategoryDB.categories[myCategoryIndex], + ...req.body, + }; + subscriptionDB.subscriptions[subscriptionIndex].category = { + ...subscriptionDB.subscriptions[subscriptionIndex].category, + ...req.body, + }; + + return res(ctx.status(201)); + } + ), + + rest.delete>( + `${API_URL}${categoryApi.endpoint.entire}/:id`, + (req, res, ctx) => { + const { id } = req.params; + const categoryId = parseInt(id as string); + const categoryIndex = categoryDB.categories.findIndex((el) => el.id === categoryId); + const myCategoryIndex = myCategoryDB.categories.findIndex((el) => el.id === categoryId); + const subscriptionIndex = subscriptionDB.subscriptions.findIndex( + (el) => el.category.id === categoryId + ); + + if (categoryIndex < 0 || myCategoryIndex < 0 || subscriptionIndex < 0) { + return res(ctx.status(400)); + } + + categoryDB.categories.splice(categoryIndex, 1); + myCategoryDB.categories.splice(myCategoryIndex, 1); + subscriptionDB.subscriptions.splice(subscriptionIndex, 1); + + return res(ctx.status(204)); + } + ), + + rest.get(`${API_URL}${categoryApi.endpoint.entire}/:id`, (req, res, ctx) => { + const { id } = req.params; + const categoryId = parseInt(id as string); + const category = categoryDB.categories.find((el) => el.id === categoryId); + + if (!category) { + return res(ctx.status(404)); + } + + return res(ctx.status(201), ctx.json(category)); + }), + + rest.get(API_URL + categoryApi.endpoint.my, (req, res, ctx) => { + return res(ctx.status(200), ctx.json(myCategoryDB)); + }), + + rest.get(API_URL + profileApi.endpoint.get, (req, res, ctx) => { + return res(ctx.status(200), ctx.json(tigerProfileDB)); + }), + + rest.patch(API_URL + profileApi.endpoint.patch, (req, res, ctx) => { + if (!req.body) { + return res(ctx.status(404)); + } + + tigerProfileDB.displayName = (req.body as Pick).displayName; + + return res(ctx.status(204)); + }), + + rest.get(API_URL + scheduleApi.endpoint.get, (req, res, ctx) => { + return res(ctx.status(200), ctx.json(getScheduleDB)); + }), + + rest.post>( + `${API_URL}/api/categories/:id/schedules`, + (req, res, ctx) => { + const { id } = req.params; + const categoryId = parseInt(id as string); + const newSchedule = { id: `${scheduleDB.length + 1}`, categoryId, ...req.body }; + + scheduleDB.push(newSchedule); + + return res(ctx.status(201)); + } + ), + + rest.patch>( + `${API_URL}/api/schedules/:id`, + (req, res, ctx) => { + const { id: scheduleId } = req.params; + + const scheduleIndex = scheduleDB.findIndex((el) => el.id === scheduleId); + + if (scheduleIndex < 0) { + return res(ctx.status(404)); + } + + const scheduleGetIndex: { [key: string]: number } = { + longTerms: getScheduleDB.longTerms.findIndex((el) => el.id === scheduleId), + allDays: getScheduleDB.allDays.findIndex((el) => el.id === scheduleId), + fewHours: getScheduleDB.fewHours.findIndex((el) => el.id === scheduleId), + }; + const scheduleType = Object.keys(scheduleGetIndex).find( + (key) => scheduleGetIndex[key] !== -1 + ); + + if (scheduleType === undefined) { + return res(ctx.status(404)); + } + + scheduleDB[scheduleIndex] = { ...scheduleDB[scheduleIndex], ...req.body }; + getScheduleDB[scheduleType][scheduleGetIndex[scheduleType]] = { + ...getScheduleDB[scheduleType][scheduleGetIndex[scheduleType]], + ...req.body, + }; + + return res(ctx.status(204)); + } + ), + + rest.delete(`${API_URL}/api/schedules/:id`, (req, res, ctx) => { + const { id: scheduleId } = req.params; + + const scheduleIndex = scheduleDB.findIndex((el) => el.id === scheduleId); + + if (scheduleIndex < 0) { + return res(ctx.status(404)); + } + + const scheduleGetIndex: { [key: string]: number } = { + longTerms: getScheduleDB.longTerms.findIndex((el) => el.id === scheduleId), + allDays: getScheduleDB.allDays.findIndex((el) => el.id === scheduleId), + fewHours: getScheduleDB.fewHours.findIndex((el) => el.id === scheduleId), + }; + const scheduleType = Object.keys(scheduleGetIndex).find((key) => scheduleGetIndex[key] !== -1); + + if (scheduleType === undefined) { + return res(ctx.status(404)); + } + + scheduleDB.splice(scheduleIndex, 1); + getScheduleDB[scheduleType].splice(scheduleGetIndex[scheduleType], 1); + + return res(ctx.status(204)); + }), + + rest.get(API_URL + subscriptionApi.endpoint.get, (req, res, ctx) => { + return res(ctx.status(200), ctx.json(subscriptionDB)); + }), + + rest.post>( + `${API_URL}/api/members/me/categories/:id/subscriptions`, + (req, res, ctx) => { + const { id } = req.params; + const categoryId = parseInt(id as string); + const newSubscription = { + id: subscriptionDB.subscriptions.length + 1, + category: { + id: categoryDB.categories[categoryId - 1].id, + name: categoryDB.categories[categoryId - 1].name, + creator: tigerProfileDB, + createdAt: categoryDB.categories[categoryId - 1].createdAt, + }, + colorCode: req.body.colorCode, + checked: true, + }; + + subscriptionDB.subscriptions.push(newSubscription); + + return res(ctx.status(201), ctx.json(newSubscription)); + } + ), + + rest.delete(`${API_URL}/api/members/me/subscriptions/:id`, (req, res, ctx) => { + const { id } = req.params; + const subscriptionId = parseInt(id as string); + const subscriptionIndex = subscriptionDB.subscriptions.findIndex( + (el) => el.id === subscriptionId + ); + + if (subscriptionIndex > -1) { + subscriptionDB.subscriptions.splice(subscriptionIndex, 1); + } + + return res(ctx.status(204)); + }), +]; + +export { handlers }; diff --git a/frontend/src/pages/AuthPage/AuthPage.tsx b/frontend/src/pages/AuthPage/AuthPage.tsx new file mode 100644 index 00000000..2e7fdfc2 --- /dev/null +++ b/frontend/src/pages/AuthPage/AuthPage.tsx @@ -0,0 +1,47 @@ +import { AxiosError } from 'axios'; +import { useEffect } from 'react'; +import { useMutation } from 'react-query'; +import { useNavigate } from 'react-router-dom'; +import { useRecoilState } from 'recoil'; + +import { userState } from '@/recoil/atoms'; + +import { PATH } from '@/constants'; +import { API, CACHE_KEY } from '@/constants/api'; + +import { getSearchParam } from '@/utils'; +import { setAccessToken } from '@/utils/storage'; + +import loginApi from '@/api/login'; + +function AuthPage() { + const [user, setUser] = useRecoilState(userState); + const navigate = useNavigate(); + + const code = getSearchParam(API.AUTH_CODE_KEY); + + const { mutate } = useMutation(CACHE_KEY.AUTH, () => loginApi.auth(code), { + onError: () => onErrorAuth(), + onSuccess: (data) => onSuccessAuth(data), + }); + + const onErrorAuth = () => { + navigate(PATH.MAIN); + }; + + const onSuccessAuth = (accessToken: string) => { + setUser({ ...user, accessToken }); + setAccessToken(accessToken); + + navigate(PATH.MAIN); + }; + + useEffect(() => { + code && mutate(); + !code && navigate(PATH.MAIN); + }, []); + + return
; +} + +export default AuthPage; diff --git a/frontend/src/pages/CalendarPage/CalendarPage.styles.ts b/frontend/src/pages/CalendarPage/CalendarPage.styles.ts new file mode 100644 index 00000000..cf63dc19 --- /dev/null +++ b/frontend/src/pages/CalendarPage/CalendarPage.styles.ts @@ -0,0 +1,245 @@ +import { css, Theme } from '@emotion/react'; + +import { DAYS } from '@/constants/date'; + +const calendarPage = css` + padding: 0 5rem; +`; + +const calendarHeader = ({ colors, flex }: Theme) => css` + ${flex.row} + + justify-content: space-between; + + width: 100%; + padding: 3rem 2rem; + + font-size: 5rem; + font-weight: 500; + color: ${colors.GRAY_700}; +`; + +const monthPicker = ({ flex }: Theme) => css` + ${flex.row} + + justify-content: space-around; +`; + +const todayButton = ({ colors }: Theme) => css` + width: 12rem; + height: 8rem; + + padding: auto 0; + + font-size: 4rem; + line-height: 4rem; + font-weight: 500; + color: ${colors.GRAY_700}; +`; + +const navButton = ({ colors }: Theme) => css` + position: relative; + + width: 8rem; + height: 8rem; + padding: 0; + + font-size: 4rem; + line-height: 4rem; + color: ${colors.GRAY_600}; + + &:hover { + border-radius: 50%; + + background: ${colors.GRAY_100}; + + filter: none; + } + + &:hover span { + visibility: visible; + } +`; + +const navButtonTitle = ({ colors }: Theme) => css` + visibility: hidden; + position: absolute; + + top: 120%; + left: 50%; + transform: translateX(-50%); + + padding: 2rem 3rem; + + background: ${colors.GRAY_700}ee; + + font-size: 3rem; + font-weight: normal; + color: ${colors.WHITE}; + white-space: nowrap; +`; + +const navBarGrid = css` + display: grid; + grid-template-columns: repeat(7, calc(100% / 7)); +`; + +const calendarGrid = (rowNum: number) => css` + display: grid; + grid-template-columns: repeat(7, calc(100% / 7)); + grid-auto-rows: calc(75vh / ${rowNum}); +`; + +const dayBar = ({ colors }: Theme, day: string) => css` + padding: 2rem 3rem; + border-top: 1px solid ${colors.GRAY_300}; + border-right: 1px solid ${colors.GRAY_300}; + border-left: ${day === DAYS[0] && `1px solid ${colors.GRAY_300}`}; + + font-size: 3rem; + text-align: right; + color: ${day === DAYS[0] && colors.RED_400}; +`; + +const dateBorder = ({ colors }: Theme, day: number) => css` + position: relative; + + height: 100%; + border-bottom: 1px solid ${colors.GRAY_300}; + border-right: 1px solid ${colors.GRAY_300}; + border-left: ${day === 0 && `1px solid ${colors.GRAY_300}`}; + + &:hover { + background: ${colors.GRAY_000}; + } +`; + +const dateText = ({ colors }: Theme, day: number, isThisMonth: boolean, isToday: boolean) => css` + position: absolute; + top: 1rem; + right: 1rem; + + width: 5rem; + height: 5rem; + padding: 1rem; + border-radius: 50%; + + background: ${isToday && colors.YELLOW_500}; + + font-size: 2.5rem; + text-align: center; + line-height: 3rem; + font-weight: 500; + color: ${isToday + ? colors.WHITE + : day === 0 + ? isThisMonth + ? colors.RED_400 + : `${colors.RED_400}80` + : isThisMonth + ? colors.GRAY_700 + : `${colors.GRAY_700}80`}; +`; + +const itemWithBackgroundStyle = ( + priority: number, + color: string, + isHovering: boolean, + maxView: number +) => css` + position: absolute; + top: ${priority * 5.5 + 1}rem; + + display: ${priority >= maxView ? 'none' : 'block'}; + width: 100%; + height: 5rem; + padding: 1rem; + + background: ${color}; + + font-size: 2.75rem; + line-height: 2.75rem; + white-space: nowrap; + color: white; + + cursor: pointer; + filter: ${isHovering && 'brightness(0.95)'}; +`; + +const itemWithoutBackgroundStyle = ( + { colors }: Theme, + priority: number, + color: string, + isHovering: boolean, + maxView: number +) => css` + ${itemWithBackgroundStyle(priority, color, isHovering, maxView)}; + + overflow: hidden; + + border-left: 3px solid ${color}; + + background: ${isHovering ? colors.GRAY_000 : colors.WHITE}; + + color: black; + + cursor: pointer; + filter: none; +`; + +const moreStyle = ({ colors }: Theme) => css` + position: absolute; + bottom: 0; + + width: 100%; + height: 5rem; + padding: 1rem; + + font-size: 2.75rem; + line-height: 2.75rem; + white-space: nowrap; + font-weight: 200; + color: ${colors.GRAY_500}; + + cursor: pointer; + + &:hover { + color: ${colors.BLACK}; + } +`; + +const spinnerStyle = ({ flex }: Theme) => css` + ${flex.row} + + gap: 2rem; + + width: 100%; + height: 100%; + + font-size: 2rem; +`; + +const waitingNavStyle = ({ flex }: Theme) => css` + ${flex.row} + + gap:4rem; +`; + +export { + calendarGrid, + calendarHeader, + calendarPage, + dayBar, + dateBorder, + dateText, + itemWithoutBackgroundStyle, + itemWithBackgroundStyle, + monthPicker, + moreStyle, + navBarGrid, + navButton, + navButtonTitle, + spinnerStyle, + todayButton, + waitingNavStyle, +}; diff --git a/frontend/src/pages/CalendarPage/CalendarPage.tsx b/frontend/src/pages/CalendarPage/CalendarPage.tsx new file mode 100644 index 00000000..02cbf894 --- /dev/null +++ b/frontend/src/pages/CalendarPage/CalendarPage.tsx @@ -0,0 +1,443 @@ +import { useTheme } from '@emotion/react'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useRef, useState } from 'react'; +import { useQuery } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import useCalendar from '@/hooks/useCalendar'; +import useSchedulePriority from '@/hooks/useSchedulePriority'; +import useToggle from '@/hooks/useToggle'; + +import { ModalPosType } from '@/@types'; +import { CalendarType } from '@/@types/calendar'; +import { ScheduleResponseType, ScheduleType } from '@/@types/schedule'; + +import { userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; +import ModalPortal from '@/components/@common/ModalPortal/ModalPortal'; +import PageLayout from '@/components/@common/PageLayout/PageLayout'; +import Spinner from '@/components/@common/Spinner/Spinner'; +import DateModal from '@/components/DateModal/DateModal'; +import ScheduleAddButton from '@/components/ScheduleAddButton/ScheduleAddButton'; +import ScheduleAddModal from '@/components/ScheduleAddModal/ScheduleAddModal'; +import ScheduleModal from '@/components/ScheduleModal/ScheduleModal'; +import ScheduleModifyModal from '@/components/ScheduleModifyModal/ScheduleModifyModal'; + +import { CALENDAR } from '@/constants'; +import { CACHE_KEY } from '@/constants/api'; +import { DAYS } from '@/constants/date'; +import { TRANSPARENT } from '@/constants/style'; + +import { + getDayFromFormattedDate, + getFormattedDate, + getISODateString, + getThisDate, + getThisMonth, +} from '@/utils/date'; + +import scheduleApi from '@/api/schedule'; + +import { AiOutlineLeft, AiOutlineRight } from 'react-icons/ai'; + +import { + calendarGrid, + calendarHeader, + calendarPage, + dateBorder, + dateText, + dayBar, + itemWithBackgroundStyle, + itemWithoutBackgroundStyle, + monthPicker, + moreStyle, + navBarGrid, + navButton, + navButtonTitle, + spinnerStyle, + todayButton, + waitingNavStyle, +} from './CalendarPage.styles'; + +function CalendarPage() { + const { accessToken } = useRecoilValue(userState); + + const theme = useTheme(); + + const dateRef = useRef(null); + + const [hoveringId, setHoveringId] = useState('0'); + const [dateInfo, setDateInfo] = useState(null); + const [modalPos, setModalPos] = useState({}); + const [scheduleInfo, setScheduleInfo] = useState(null); + const [moreDateInfo, setMoreDateInfo] = useState(null); + + const { + calendarMonth, + current, + moveToBeforeMonth, + moveToToday, + moveToNextMonth, + startDate, + endDate, + } = useCalendar(); + + const { getLongTermsPriority, getAllDaysPriority, getFewHoursPriority } = + useSchedulePriority(calendarMonth); + + const { state: isScheduleAddModalOpen, toggleState: toggleScheduleAddModalOpen } = useToggle(); + const { state: isScheduleModalOpen, toggleState: toggleScheduleModalOpen } = useToggle(); + const { state: isScheduleModifyModalOpen, toggleState: toggleScheduleModifyModalOpen } = + useToggle(); + const { state: isDateModalOpen, toggleState: toggleDateModalOpen } = useToggle(); + + const { isLoading, data } = useQuery, AxiosError>( + [CACHE_KEY.SCHEDULES, current], + () => scheduleApi.get(accessToken, startDate, endDate) + ); + + const rowNum = Math.ceil(calendarMonth.length / 7); + + const handleClickDate = (e: React.MouseEvent, info: CalendarType) => { + if (e.target !== e.currentTarget) { + return; + } + + setDateInfo(info); + toggleScheduleAddModalOpen(); + }; + + const handleClickSchedule = (e: React.MouseEvent, info: ScheduleType) => { + if (e.target !== e.currentTarget) { + return; + } + + setModalPos(calculateModalPos(e.clientX, e.clientY)); + setScheduleInfo(info); + toggleScheduleModalOpen(); + }; + + const calculateModalPos = (clickX: number, clickY: number) => { + const position = { top: clickY, right: 0, bottom: 0, left: clickX }; + + if (clickX > innerWidth / 2) { + position.right = innerWidth - clickX; + position.left = 0; + } + + if (clickY > innerHeight / 2) { + position.bottom = innerHeight - clickY; + position.top = 0; + } + + return position; + }; + + if (isLoading || data === undefined) { + return ( + +
+
+ + {current.year}년 {current.month}월 + +
+
+ + 일정을 가져오고 있습니다. +
+
+ + + +
+
+
+
+ {DAYS.map((day) => ( + + {day} + + ))} +
+
+ {calendarMonth.map((info) => { + const key = getFormattedDate(info.year, info.month, info.date); + + return ( +
handleClickDate(e, info)} + ref={dateRef} + > + + {info.date} + +
+ ); + })} +
+ + + + +
+
+ ); + } + + const longTermsWithPriority = getLongTermsPriority(data.data.longTerms); + const allDaysWithPriority = getAllDaysPriority(data.data.allDays); + const fewHoursWithPriority = getFewHoursPriority(data.data.fewHours); + + const handleClickMoreButton = (e: React.MouseEvent, info: CalendarType) => { + if (e.target !== e.currentTarget) { + return; + } + + setModalPos(calculateModalPos(e.clientX, e.clientY)); + setMoreDateInfo(info); + toggleDateModalOpen(); + }; + + const onMouseEnter = (scheduleId: string) => { + setHoveringId(scheduleId); + }; + + const onMouseLeave = () => { + setHoveringId('0'); + }; + + const maxView = + dateRef.current !== null + ? Math.floor((dateRef.current.clientHeight - 20) / 22) + : CALENDAR.MAX_VIEW; + + return ( + +
+
+ + {current.year}년 {current.month}월 + +
+ + + +
+
+
+ {DAYS.map((day) => ( + + {day} + + ))} +
+
+ {calendarMonth.map((info) => { + const key = getFormattedDate(info.year, info.month, info.date); + + return ( + <> +
handleClickDate(e, info)} + ref={dateRef} + > + + {info.date} + + + {longTermsWithPriority.map((el) => { + const startDate = getISODateString(el.schedule.startDateTime); + const endDate = getISODateString(el.schedule.endDateTime); + const nowDate = getFormattedDate(info.year, info.month, info.date); + const nowDay = getDayFromFormattedDate(nowDate); + + if (startDate <= nowDate && nowDate <= endDate && el.priority >= maxView) { + return ( + handleClickMoreButton(e, info)}> + 일정 더보기 + + ); + } + + return ( + startDate <= nowDate && + nowDate <= endDate && ( +
onMouseEnter(el.schedule.id)} + onClick={(e) => handleClickSchedule(e, el.schedule)} + onMouseLeave={onMouseLeave} + > + {(startDate === nowDate || nowDay === 0) && + (el.schedule.title || CALENDAR.EMPTY_TITLE)} +
+ ) + ); + })} + + {allDaysWithPriority.map((el) => { + const startDate = getISODateString(el.schedule.startDateTime); + const nowDate = getFormattedDate(info.year, info.month, info.date); + + if (startDate === nowDate && el.priority >= maxView) { + return ( + handleClickMoreButton(e, info)}> + 일정 더보기 + + ); + } + + return ( + startDate === nowDate && ( +
onMouseEnter(el.schedule.id)} + onClick={(e) => handleClickSchedule(e, el.schedule)} + onMouseLeave={onMouseLeave} + > + {el.schedule.title || CALENDAR.EMPTY_TITLE} +
+ ) + ); + })} + + {fewHoursWithPriority.map((el) => { + const startDate = getISODateString(el.schedule.startDateTime); + const nowDate = getFormattedDate(info.year, info.month, info.date); + + if (startDate === nowDate && el.priority >= maxView) { + return ( + handleClickMoreButton(e, info)}> + 일정 더보기 + + ); + } + + return ( + startDate === nowDate && ( +
onMouseEnter(el.schedule.id)} + onClick={(e) => handleClickSchedule(e, el.schedule)} + onMouseLeave={onMouseLeave} + > + {el.schedule.title || CALENDAR.EMPTY_TITLE} +
+ ) + ); + })} +
+ + {info === moreDateInfo && ( + + + + )} + + ); + })} +
+
+ + + + + + + {scheduleInfo ? ( + <> + + + + + + + + ) : ( + <> + )} +
+ ); +} + +export default CalendarPage; diff --git a/frontend/src/pages/CategoryPage/CategoryPage.styles.ts b/frontend/src/pages/CategoryPage/CategoryPage.styles.ts new file mode 100644 index 00000000..2376d6e6 --- /dev/null +++ b/frontend/src/pages/CategoryPage/CategoryPage.styles.ts @@ -0,0 +1,108 @@ +import { css, Theme } from '@emotion/react'; + +const categoryPageStyle = css` + height: 80%; + padding: 9rem; +`; + +const searchFormStyle = css` + position: relative; + + width: 100%; + height: 12rem; + margin-bottom: 8rem; +`; + +const searchButtonStyle = css` + position: absolute; + + top: 50%; + transform: translateY(-50%); + + width: 12rem; +`; + +const searchFieldsetStyle = css` + height: 100%; +`; + +const searchInputStyle = css` + height: 100%; + padding-left: 12rem; + + font-size: 4rem; +`; + +const buttonStyle = ({ colors }: Theme) => css` + width: 40rem; + height: 12rem; + border-radius: 8px; + border: 1px solid ${colors.GRAY_500}; + + background: ${colors.YELLOW_500}; + + font-size: 3.5rem; + font-weight: 700; + line-height: 3.5rem; + color: ${colors.WHITE}; +`; + +const controlStyle = ({ flex }: Theme) => css` + ${flex.row}; + + align-items: flex-start; + justify-content: center; + gap: 4rem; + + width: 100%; +`; + +const outLineButtonStyle = ({ colors }: Theme) => css` + width: 40rem; + height: 12rem; + border-radius: 8px; + border: 1px solid ${colors.GRAY_500}; + + font-size: 3.5rem; + font-weight: 700; + line-height: 3.5rem; + color: ${colors.YELLOW_500}; +`; + +const toggleModeStyle = ({ colors, flex }: Theme, mode: 'ALL' | 'MY') => css` + ${flex.row}; + + justify-content: space-around; + + width: 35rem; + height: 12rem; + padding: 0 1rem; + border-radius: 8px; + border: 1px solid ${colors.GRAY_500}; + + background: linear-gradient( + 90deg, + ${mode === 'ALL' ? colors.YELLOW_500 : colors.WHITE} 50%, + ${mode === 'MY' ? colors.YELLOW_500 : colors.WHITE} 50% + ); +`; + +const modeTextStyle = ({ colors }: Theme, isSelected: boolean) => css` + font-size: 3.5rem; + font-weight: 700; + line-height: 3.5rem; + color: ${isSelected ? colors.WHITE : colors.YELLOW_500}; +`; + +export { + buttonStyle, + categoryPageStyle, + controlStyle, + modeTextStyle, + outLineButtonStyle, + searchButtonStyle, + searchFieldsetStyle, + searchFormStyle, + searchInputStyle, + toggleModeStyle, +}; diff --git a/frontend/src/pages/CategoryPage/CategoryPage.tsx b/frontend/src/pages/CategoryPage/CategoryPage.tsx new file mode 100644 index 00000000..ec8cdd3d --- /dev/null +++ b/frontend/src/pages/CategoryPage/CategoryPage.tsx @@ -0,0 +1,100 @@ +import { useTheme } from '@emotion/react'; +import { lazy, Suspense, useRef, useState } from 'react'; + +import useToggle from '@/hooks/useToggle'; + +import Button from '@/components/@common/Button/Button'; +import Fieldset from '@/components/@common/Fieldset/Fieldset'; +import ModalPortal from '@/components/@common/ModalPortal/ModalPortal'; +import PageLayout from '@/components/@common/PageLayout/PageLayout'; +import CategoryAddModal from '@/components/CategoryAddModal/CategoryAddModal'; +import CategoryListFallback from '@/components/CategoryList/CategoryList.fallback'; +import MyCategoryListFallback from '@/components/MyCategoryList/MyCategoryList.fallback'; + +import { GoSearch } from 'react-icons/go'; + +import { + buttonStyle, + categoryPageStyle, + controlStyle, + modeTextStyle, + searchButtonStyle, + searchFieldsetStyle, + searchFormStyle, + searchInputStyle, + toggleModeStyle, +} from './CategoryPage.styles'; + +const CategoryList = lazy(() => import('@/components/CategoryList/CategoryList')); +const MyCategoryList = lazy(() => import('@/components/MyCategoryList/MyCategoryList')); + +function CategoryPage() { + const theme = useTheme(); + const [mode, setMode] = useState<'ALL' | 'MY'>('ALL'); + const { state: isCategoryAddModalOpen, toggleState: toggleCategoryAddModalOpen } = useToggle(); + + const keywordRef = useRef(null); + + const [keyword, setKeyword] = useState(''); + + const handleSubmitCategorySearchForm = (e: React.FormEvent) => { + e.preventDefault(); + + if (!(keywordRef.current instanceof HTMLInputElement)) { + return; + } + + setKeyword((keywordRef.current as HTMLInputElement).value); + }; + + const handleClickFilteringButton = () => { + mode === 'ALL' && setMode('MY'); + mode === 'MY' && setMode('ALL'); + }; + + const handleClickCategoryAddButton = () => { + toggleCategoryAddModalOpen(); + }; + + return ( + +
+ + + +
+
+ +
+ + + +
+ {mode === 'ALL' && ( + }> + + + )} + {mode === 'MY' && ( + }> + + + )} +
+
+ ); +} + +export default CategoryPage; diff --git a/frontend/src/pages/MainPage/MainPage.tsx b/frontend/src/pages/MainPage/MainPage.tsx new file mode 100644 index 00000000..adaac4fd --- /dev/null +++ b/frontend/src/pages/MainPage/MainPage.tsx @@ -0,0 +1,18 @@ +import { useRecoilValue } from 'recoil'; + +import { userState } from '@/recoil/atoms'; + +import CalendarPage from '@/pages/CalendarPage/CalendarPage'; +import StartPage from '@/pages/StartPage/StartPage'; + +function MainPage() { + const { accessToken } = useRecoilValue(userState); + + if (!accessToken) { + return ; + } + + return ; +} + +export default MainPage; diff --git a/frontend/src/pages/NotFoundPage/NotFoundPage.styles.ts b/frontend/src/pages/NotFoundPage/NotFoundPage.styles.ts new file mode 100644 index 00000000..b9439857 --- /dev/null +++ b/frontend/src/pages/NotFoundPage/NotFoundPage.styles.ts @@ -0,0 +1,33 @@ +import { css, Theme } from '@emotion/react'; + +const layoutStyle = ({ flex }: Theme) => css` + ${flex.column}; + + gap: 15rem; + + height: calc(100% - 16rem); +`; + +const buttonStyle = ({ colors }: Theme) => css` + padding: 3rem 6rem; + border: 1px solid ${colors.GRAY_800}; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.25); + + font-size: 5rem; + color: ${colors.GRAY_800}; + + &:hover { + box-shadow: none; + } +`; + +const textStyle = ({ colors }: Theme, fontSize: string) => css` + font-size: ${fontSize}; + font-weight: 400; + text-align: center; + line-height: 150%; + color: ${colors.GRAY_800}; +`; + +export { buttonStyle, layoutStyle, textStyle }; diff --git a/frontend/src/pages/NotFoundPage/NotFoundPage.tsx b/frontend/src/pages/NotFoundPage/NotFoundPage.tsx new file mode 100644 index 00000000..2137b1c7 --- /dev/null +++ b/frontend/src/pages/NotFoundPage/NotFoundPage.tsx @@ -0,0 +1,36 @@ +import { useTheme } from '@emotion/react'; +import { useNavigate } from 'react-router-dom'; + +import Button from '@/components/@common/Button/Button'; +import PageLayout from '@/components/@common/PageLayout/PageLayout'; + +import { PATH } from '@/constants'; + +import { buttonStyle, layoutStyle, textStyle } from './NotFoundPage.styles'; + +function NotFoundPage() { + const theme = useTheme(); + + const navigation = useNavigate(); + + const handleClickReturnButton = () => { + navigation(PATH.MAIN); + }; + + return ( + +
+ (⊙_⊙;) + + 죄송합니다.
요청하신 페이지를 찾을 수가 없어요. +
+ + +
+
+ ); +} + +export default NotFoundPage; diff --git a/frontend/src/pages/PrivacyPolicyPage/PrivacyPolicyPage.styles.ts b/frontend/src/pages/PrivacyPolicyPage/PrivacyPolicyPage.styles.ts new file mode 100644 index 00000000..ebc9740d --- /dev/null +++ b/frontend/src/pages/PrivacyPolicyPage/PrivacyPolicyPage.styles.ts @@ -0,0 +1,22 @@ +import { css, Theme } from '@emotion/react'; + +const privacyPolicyStyle = ({ flex }: Theme) => css` + ${flex.column} + + align-items: flex-start; + + margin: 5% 10%; +`; + +const headerStyle = css` + margin-bottom: 10rem; + + font-size: 5rem; + font-weight: bold; +`; + +const contentStyle = css` + white-space: pre-wrap; +`; + +export { contentStyle, headerStyle, privacyPolicyStyle }; diff --git a/frontend/src/pages/PrivacyPolicyPage/PrivacyPolicyPage.tsx b/frontend/src/pages/PrivacyPolicyPage/PrivacyPolicyPage.tsx new file mode 100644 index 00000000..7f4b813a --- /dev/null +++ b/frontend/src/pages/PrivacyPolicyPage/PrivacyPolicyPage.tsx @@ -0,0 +1,158 @@ +import PageLayout from '@/components/@common/PageLayout/PageLayout'; + +import { contentStyle, headerStyle, privacyPolicyStyle } from './PrivacyPolicyPage.styles'; + +function PrivacyPolicyPage() { + return ( + +
+

개인정보처리방침

+

+ {` +< dallog >('https://dallog.me'이하 'dallog')은(는) 「개인정보 보호법」 제30조에 따라 정보주체의 개인정보를 보호하고 이와 관련한 고충을 신속하고 원활하게 처리할 수 있도록 하기 위하여 다음과 같이 개인정보 처리방침을 수립·공개합니다. + +○ 이 개인정보처리방침은 2022년 8월 17부터 적용됩니다. + + +제1조(개인정보의 처리 목적) + +< dallog >('https://dallog.me'이하 'dallog')은(는) 다음의 목적을 위하여 개인정보를 처리합니다. 처리하고 있는 개인정보는 다음의 목적 이외의 용도로는 이용되지 않으며 이용 목적이 변경되는 경우에는 「개인정보 보호법」 제18조에 따라 별도의 동의를 받는 등 필요한 조치를 이행할 예정입니다. + +1. 홈페이지 회원가입 및 관리 + +회원자격 유지·관리 목적으로 개인정보를 처리합니다. + + +2. 재화 또는 서비스 제공 + +서비스 제공을 목적으로 개인정보를 처리합니다. + + + + +제2조(개인정보의 처리 및 보유 기간) + +① < dallog >은(는) 법령에 따른 개인정보 보유·이용기간 또는 정보주체로부터 개인정보를 수집 시에 동의받은 개인정보 보유·이용기간 내에서 개인정보를 처리·보유합니다. + +② 각각의 개인정보 처리 및 보유 기간은 다음과 같습니다. + +1.<홈페이지 회원가입 및 관리> +<홈페이지 회원가입 및 관리>와 관련한 개인정보는 수집.이용에 관한 동의일로부터<회원탈퇴 시 까지>까지 위 이용목적을 위하여 보유.이용됩니다. +보유근거 : 회원 가입의사 확인, 회원자격 유지․관리, 서비스 부정이용 방지 목적으로 개인정보를 처리합니다. +관련법령 : +예외사유 : + + +제3조(처리하는 개인정보의 항목) + +① < dallog >은(는) 다음의 개인정보 항목을 처리하고 있습니다. + +1< 홈페이지 회원가입 및 관리 > +필수항목 : 이메일, 유저네임, 프로필이미지URL +선택항목 : + + +제4조(개인정보의 파기절차 및 파기방법) + + +① < dallog > 은(는) 개인정보 보유기간의 경과, 처리목적 달성 등 개인정보가 불필요하게 되었을 때에는 지체없이 해당 개인정보를 파기합니다. + +② 정보주체로부터 동의받은 개인정보 보유기간이 경과하거나 처리목적이 달성되었음에도 불구하고 다른 법령에 따라 개인정보를 계속 보존하여야 하는 경우에는, 해당 개인정보를 별도의 데이터베이스(DB)로 옮기거나 보관장소를 달리하여 보존합니다. + +③ 개인정보 파기의 절차 및 방법은 다음과 같습니다. +1. 파기절차 +< dallog > 은(는) 파기 사유가 발생한 개인정보를 선정하고, < dallog > 의 개인정보 보호책임자의 승인을 받아 개인정보를 파기합니다. + + + +제5조(정보주체와 법정대리인의 권리·의무 및 그 행사방법에 관한 사항) + + + +① 정보주체는 dallog에 대해 언제든지 개인정보 열람·정정·삭제·처리정지 요구 등의 권리를 행사할 수 있습니다. + +② 제1항에 따른 권리 행사는 dallog에 대해 「개인정보 보호법」 시행령 제41조제1항에 따라 서면, 전자우편, 모사전송(FAX) 등을 통하여 하실 수 있으며 dallog은(는) 이에 대해 지체 없이 조치하겠습니다. + +③ 제1항에 따른 권리 행사는 정보주체의 법정대리인이나 위임을 받은 자 등 대리인을 통하여 하실 수 있습니다.이 경우 “개인정보 처리 방법에 관한 고시(제2020-7호)” 별지 제11호 서식에 따른 위임장을 제출하셔야 합니다. + +④ 개인정보 열람 및 처리정지 요구는 「개인정보 보호법」 제35조 제4항, 제37조 제2항에 의하여 정보주체의 권리가 제한 될 수 있습니다. + +⑤ 개인정보의 정정 및 삭제 요구는 다른 법령에서 그 개인정보가 수집 대상으로 명시되어 있는 경우에는 그 삭제를 요구할 수 없습니다. + +⑥ dallog은(는) 정보주체 권리에 따른 열람의 요구, 정정·삭제의 요구, 처리정지의 요구 시 열람 등 요구를 한 자가 본인이거나 정당한 대리인인지를 확인합니다. + + + +제6조(개인정보의 안전성 확보조치에 관한 사항) + +< dallog >은(는) 개인정보의 안전성 확보를 위해 다음과 같은 조치를 취하고 있습니다. + +1. 해킹 등에 대비한 기술적 대책 +('dallog')은 해킹이나 컴퓨터 바이러스 등에 의한 개인정보 유출 및 훼손을 막기 위하여 보안프로그램을 설치하고 주기적인 갱신·점검을 하며 외부로부터 접근이 통제된 구역에 시스템을 설치하고 기술적/물리적으로 감시 및 차단하고 있습니다. + +2. 개인정보에 대한 접근 제한 +개인정보를 처리하는 데이터베이스시스템에 대한 접근권한의 부여,변경,말소를 통하여 개인정보에 대한 접근통제를 위하여 필요한 조치를 하고 있으며 침입차단시스템을 이용하여 외부로부터의 무단 접근을 통제하고 있습니다. + + + + +제7조(개인정보를 자동으로 수집하는 장치의 설치·운영 및 그 거부에 관한 사항) + + + +dallog 은(는) 정보주체의 이용정보를 저장하고 수시로 불러오는 ‘쿠키(cookie)’를 사용하지 않습니다. + +제8조 (개인정보 보호책임자에 관한 사항) + +① dallog 은(는) 개인정보 처리에 관한 업무를 총괄해서 책임지고, 개인정보 처리와 관련한 정보주체의 불만처리 및 피해구제 등을 위하여 아래와 같이 개인정보 보호책임자를 지정하고 있습니다. + +▶ 개인정보 보호책임자 +성명 :구동희 +직책 :팀원 +직급 :팀원 +연락처 :010-3160-2953, gudonghee2000@gmail.com, +※ 개인정보 보호 담당부서로 연결됩니다. + +▶ 개인정보 보호 담당부서 +부서명 : +담당자 : +연락처 :, , +② 정보주체께서는 dallog 의 서비스(또는 사업)을 이용하시면서 발생한 모든 개인정보 보호 관련 문의, 불만처리, 피해구제 등에 관한 사항을 개인정보 보호책임자 및 담당부서로 문의하실 수 있습니다. dallog 은(는) 정보주체의 문의에 대해 지체 없이 답변 및 처리해드릴 것입니다. + +제9조(개인정보의 열람청구를 접수·처리하는 부서) +정보주체는 「개인정보 보호법」 제35조에 따른 개인정보의 열람 청구를 아래의 부서에 할 수 있습니다. +< dallog >은(는) 정보주체의 개인정보 열람청구가 신속하게 처리되도록 노력하겠습니다. + +▶ 개인정보 열람청구 접수·처리 부서 +부서명 : +담당자 : +연락처 : , , + + +제10조(정보주체의 권익침해에 대한 구제방법) + + + +정보주체는 개인정보침해로 인한 구제를 받기 위하여 개인정보분쟁조정위원회, 한국인터넷진흥원 개인정보침해신고센터 등에 분쟁해결이나 상담 등을 신청할 수 있습니다. 이 밖에 기타 개인정보침해의 신고, 상담에 대하여는 아래의 기관에 문의하시기 바랍니다. + +1. 개인정보분쟁조정위원회 : (국번없이) 1833-6972 (www.kopico.go.kr) +2. 개인정보침해신고센터 : (국번없이) 118 (privacy.kisa.or.kr) +3. 대검찰청 : (국번없이) 1301 (www.spo.go.kr) +4. 경찰청 : (국번없이) 182 (ecrm.cyber.go.kr) + +「개인정보보호법」제35조(개인정보의 열람), 제36조(개인정보의 정정·삭제), 제37조(개인정보의 처리정지 등)의 규정에 의한 요구에 대 하여 공공기관의 장이 행한 처분 또는 부작위로 인하여 권리 또는 이익의 침해를 받은 자는 행정심판법이 정하는 바에 따라 행정심판을 청구할 수 있습니다. + +※ 행정심판에 대해 자세한 사항은 중앙행정심판위원회(www.simpan.go.kr) 홈페이지를 참고하시기 바랍니다. + +제11조(개인정보 처리방침 변경) + + +① 이 개인정보처리방침은 2022년 8월 17부터 적용됩니다. + `} +

+
+
+ ); +} + +export default PrivacyPolicyPage; diff --git a/frontend/src/pages/SchedulingPage/SchedulingPage.styles.ts b/frontend/src/pages/SchedulingPage/SchedulingPage.styles.ts new file mode 100644 index 00000000..d6348639 --- /dev/null +++ b/frontend/src/pages/SchedulingPage/SchedulingPage.styles.ts @@ -0,0 +1,124 @@ +import { css, Theme } from '@emotion/react'; + +const pageStyle = ({ flex }: Theme) => css` + ${flex.column} + + padding: 9rem 10%; +`; + +const formStyle = ({ flex }: Theme) => css` + ${flex.row} + + gap: 6rem; + + max-width: 100%; + + font-size: 4rem; +`; + +const subscriptionStyle = ({ flex }: Theme) => css` + ${flex.column} + + align-items: flex-start; + gap: 2.5rem; +`; + +const labelStyle = ({ colors }: Theme) => css` + padding: 0 1rem; + + font-size: 4rem; + color: ${colors.GRAY_800}; +`; + +const subscriptionSelectStyle = ({ colors }: Theme) => css` + width: 80rem; + height: 12rem; + padding: 3rem; + border: 1px solid ${colors.GRAY_400}; + border-radius: 8px; + + font-size: inherit; +`; + +const dateTimeStyle = ({ flex }: Theme) => css` + ${flex.row} + + gap: 3rem; + + width: 120rem; +`; + +const dateTimeFieldsetStyle = css` + height: auto; + + font-size: 4rem; +`; + +const resultStyle = ({ flex }: Theme) => css` + ${flex.column} + + overflow-y: overlay; + justify-content: flex-start; + gap: 5rem; + + width: 214rem; + height: 60vh; + padding: 0 4rem; +`; + +const searchButtonStyle = ({ colors, flex }: Theme) => css` + ${flex.row} + + gap: 5rem; + + width: 206rem; + padding: 5rem; + margin: 8rem 0 5rem; + border-radius: 8px; + box-shadow: 2px 2px 4px ${colors.GRAY_500}; + + background: ${colors.ORANGE_500}; + + font-size: 4rem; + font-weight: 500; + color: ${colors.WHITE}; + + &:hover { + box-shadow: none; + } +`; + +const resultTimeStyle = ({ flex }: Theme) => css` + ${flex.row} + + gap: 3rem; + + width: 100%; +`; + +const resultDateTimeStyle = ({ colors }: Theme) => css` + width: 100%; + padding: 6rem; + border-radius: 8px; + + background: ${colors.BLUE_500}; + + font-size: 4rem; + font-weight: 500; + color: white; + text-align: center; +`; + +export { + dateTimeFieldsetStyle, + dateTimeStyle, + formStyle, + labelStyle, + pageStyle, + resultDateTimeStyle, + resultStyle, + resultTimeStyle, + searchButtonStyle, + subscriptionSelectStyle, + subscriptionStyle, +}; diff --git a/frontend/src/pages/SchedulingPage/SchedulingPage.tsx b/frontend/src/pages/SchedulingPage/SchedulingPage.tsx new file mode 100644 index 00000000..9b37254d --- /dev/null +++ b/frontend/src/pages/SchedulingPage/SchedulingPage.tsx @@ -0,0 +1,153 @@ +import { useTheme } from '@emotion/react'; +import { AxiosError, AxiosResponse } from 'axios'; +import { useQuery } from 'react-query'; +import { useRecoilValue } from 'recoil'; + +import useControlledInput from '@/hooks/useControlledInput'; + +import { SchedulingResponseType } from '@/@types/scheduler'; +import { SubscriptionType } from '@/@types/subscription'; + +import { userState } from '@/recoil/atoms'; + +import Button from '@/components/@common/Button/Button'; +import Fieldset from '@/components/@common/Fieldset/Fieldset'; +import PageLayout from '@/components/@common/PageLayout/PageLayout'; + +import { CACHE_KEY } from '@/constants/api'; +import { CATEGORY_TYPE } from '@/constants/category'; + +import { getDate } from '@/utils/date'; + +import schedulerApi from '@/api/scheduler'; +import subscriptionApi from '@/api/subscription'; + +import { BsArrowRight } from 'react-icons/bs'; +import { GoSearch } from 'react-icons/go'; + +import { + dateTimeFieldsetStyle, + dateTimeStyle, + formStyle, + labelStyle, + pageStyle, + resultDateTimeStyle, + resultStyle, + resultTimeStyle, + searchButtonStyle, + subscriptionSelectStyle, + subscriptionStyle, +} from './SchedulingPage.styles'; + +function SchedulingPage() { + const { accessToken } = useRecoilValue(userState); + + const theme = useTheme(); + + const { isLoading: isGetSubscriptionsLoading, data: subscriptionsGetResponse } = useQuery< + AxiosResponse, + AxiosError + >(CACHE_KEY.SUBSCRIPTIONS, () => subscriptionApi.get(accessToken), { + onSuccess: (data) => onSuccessGetSubscriptions(data), + }); + + const category = useControlledInput(); + const startDateTime = useControlledInput(getDate(null)); + const endDateTime = useControlledInput(getDate(null)); + + const onSuccessGetSubscriptions = (data: AxiosResponse) => { + category.setInputValue(`${data.data[0].category.id}`); + refetch(); + }; + + const { data: schedulingGetResponse, refetch } = useQuery< + AxiosResponse, + AxiosError + >( + CACHE_KEY.SCHEDULER, + () => + schedulerApi.get( + accessToken, + Number(category.inputValue), + startDateTime.inputValue, + endDateTime.inputValue + ), + { + enabled: false, + } + ); + + if (isGetSubscriptionsLoading || subscriptionsGetResponse === undefined) { + return <>; + } + + const handleClickSearchButton = () => { + refetch(); + }; + + const formatDateTime = (dateTime: string) => { + return `${dateTime.replace('T', ' ').replace(':', '시 ').slice(0, -3)}분`; + }; + + const subscriptions = subscriptionsGetResponse.data.filter( + (subscription) => subscription.category.categoryType === CATEGORY_TYPE.NORMAL + ); + + return ( + +
+
+
+ 조회할 카테고리 + +
+
+
+ +
+
+
+ +
+ {schedulingGetResponse && + schedulingGetResponse.data.map((schedule) => ( +
+
{formatDateTime(schedule.startDateTime)}
+ +
{formatDateTime(schedule.endDateTime)}
+
+ ))} +
+
+
+ ); +} + +export default SchedulingPage; diff --git a/frontend/src/pages/StartPage/StartPage.styles.ts b/frontend/src/pages/StartPage/StartPage.styles.ts new file mode 100644 index 00000000..bd0ab98d --- /dev/null +++ b/frontend/src/pages/StartPage/StartPage.styles.ts @@ -0,0 +1,286 @@ +import { css, Theme } from '@emotion/react'; + +const calendarStyle = ({ flex, colors }: Theme) => css` + ${flex.column}; + + gap: 2rem; + position: relative; + left: -20%; + top: -10rem; + + width: 200rem; + border: 1px solid ${colors.GRAY_400}; + box-sizing: content-box; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.25); + + opacity: 0; + + transform: rotate(10deg); + + @keyframes fadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } + } + + animation-name: fadeIn; + animation-duration: 1s; + animation-timing-function: ease-in-out; + animation-fill-mode: forwards; +`; + +const dateItemStyle = ({ colors }: Theme) => css` + width: 200rem; + height: 30rem; + padding: 6rem 6rem 4rem; + + background: ${colors.WHITE}; + + font-size: 20rem; + text-align: right; +`; + +const itemStyle = css` + position: relative; + left: -100%; + + width: 200rem; + height: 50rem; + padding: 4rem; + + font-size: 10rem; + font-weight: 600; + text-align: right; + + opacity: 0; + + @keyframes slideIn { + from { + opacity: 0; + left: -100%; + } + + to { + left: 0; + opacity: 1; + } + } + + animation-name: slideIn; + animation-duration: 2s; + animation-timing-function: ease-in-out; + animation-fill-mode: forwards; +`; + +const firstItemStyle = ({ colors }: Theme) => css` + ${itemStyle}; + + background: ${colors.YELLOW_500}; + + animation-delay: 0.5s; +`; + +const secondItemStyle = ({ colors }: Theme) => css` + ${itemStyle}; + + background: ${colors.ORANGE_500}; + + animation-delay: 1s; +`; + +const thirdItemStyle = ({ colors }: Theme) => css` + ${itemStyle}; + + background: ${colors.BLUE_500}; + + animation-delay: 1.5s; +`; + +const blackTextStyle = ({ colors }: Theme) => css` + width: 100%; + + font-size: 20rem; + font-weight: 700; + line-height: 25rem; + color: ${colors.BLACK}; + text-shadow: 5px 5px 10px rgba(0, 0, 0, 0.25); +`; + +const whiteTextStyle = ({ colors }: Theme) => css` + width: 100%; + + font-size: 20rem; + font-weight: 700; + line-height: 25rem; + color: ${colors.WHITE}; + text-shadow: 5px 5px 10px rgba(0, 0, 0, 0.25); +`; + +const introductionStyle = css` + width: 100%; + margin: 20rem 20rem 0 0; + + text-align: right; +`; + +const detailTextStyle = ({ colors }: Theme) => css` + font-size: 3.5rem; + color: ${colors.GRAY_500}; +`; + +const loginText = css` + width: 100%; +`; + +const googleLoginButton = ({ colors, flex }: Theme) => css` + ${flex.row} + + position: relative; + justify-content: flex-start; + + width: 75rem; + height: 15rem; + padding: 4rem; + border-radius: 8px; + border: 1px solid ${colors.GRAY_400}; + box-shadow: 2px 2px 2px ${colors.GRAY_400}; + + background: ${colors.WHITE}; + + font-size: 4rem; + font-weight: 500; + color: ${colors.BLACK}8a; + + &:hover { + box-shadow: 3px 3px 3px ${colors.GRAY_500}; + } +`; + +const mainContentStyle = ({ flex }: Theme) => css` + ${flex.column}; + + align-items: flex-end; + gap: 6rem; +`; + +const firstSectionStyle = ({ flex }: Theme) => css` + ${flex.row}; + + align-items: flex-start; +`; + +const secondSectionStyle = ({ flex, colors }: Theme) => css` + ${flex.column}; + + align-items: center; + justify-content: flex-start; + + width: 100%; + height: 100%; + margin-top: 20rem; + padding: 20rem auto; + gap: 10rem; + + background: ${colors.GRAY_200}; +`; + +const pageStyle = ({ flex }: Theme) => css` + ${flex.column}; + + overflow-x: hidden; +`; + +const methodHeaderStyle = ({ colors }: Theme) => css` + margin-top: 10rem; + + font-size: 5rem; + color: ${colors.GRAY_500}; +`; + +const methodTextStyle = ({ colors }: Theme) => css` + font-size: 6rem; + line-height: 10rem; + color: ${colors.GRAY_500}; +`; + +const methodItemStyle = ({ flex }: Theme, isShowing: boolean, direction: 'left' | 'right') => css` + ${flex.row}; + + position: relative; + left: ${direction === 'left' ? '-100%' : '100%'}; + gap: 20rem; + + width: 70%; + height: 100%; + padding: 20rem; + + opacity: 0; + + @keyframes slideInFromLeft { + from { + opacity: 0; + left: -100%; + } + + to { + left: 0; + opacity: 1; + } + } + + @keyframes slideInFromRight { + from { + opacity: 0; + left: 100%; + } + + to { + left: 0; + opacity: 1; + } + } + + ${isShowing && + css` + animation-name: ${direction === 'left' ? 'slideInFromLeft' : 'slideInFromRight'}; + animation-duration: 0.7s; + animation-timing-function: ease-in-out; + animation-fill-mode: forwards; + `} +`; + +const imageStyle = css` + width: 120rem; + height: auto; +`; + +const refStyle = css` + height: 1rem; +`; + +export { + blackTextStyle, + calendarStyle, + detailTextStyle, + dateItemStyle, + firstItemStyle, + firstSectionStyle, + googleLoginButton, + loginText, + imageStyle, + introductionStyle, + mainContentStyle, + methodHeaderStyle, + methodItemStyle, + methodTextStyle, + pageStyle, + refStyle, + secondItemStyle, + secondSectionStyle, + thirdItemStyle, + whiteTextStyle, +}; diff --git a/frontend/src/pages/StartPage/StartPage.tsx b/frontend/src/pages/StartPage/StartPage.tsx new file mode 100644 index 00000000..49a41e18 --- /dev/null +++ b/frontend/src/pages/StartPage/StartPage.tsx @@ -0,0 +1,153 @@ +import { useTheme } from '@emotion/react'; +import { useState } from 'react'; +import { useQuery } from 'react-query'; + +import useIntersect from '@/hooks/useIntersect'; + +import Button from '@/components/@common/Button/Button'; +import PageLayout from '@/components/@common/PageLayout/PageLayout'; +import Footer from '@/components/Footer/Footer'; + +import { CACHE_KEY } from '@/constants/api'; + +import { getThisDate } from '@/utils/date'; + +import loginApi from '@/api/login'; + +import { FcGoogle } from 'react-icons/fc'; + +import howToUse1 from '../../assets/how_to_use_1.png'; +import howToUse2 from '../../assets/how_to_use_2.png'; +import howToUse3 from '../../assets/how_to_use_3.png'; +import { + blackTextStyle, + calendarStyle, + dateItemStyle, + detailTextStyle, + firstItemStyle, + firstSectionStyle, + googleLoginButton, + imageStyle, + introductionStyle, + loginText, + mainContentStyle, + methodHeaderStyle, + methodItemStyle, + methodTextStyle, + pageStyle, + refStyle, + secondItemStyle, + secondSectionStyle, + thirdItemStyle, + whiteTextStyle, +} from './StartPage.styles'; + +function StartPage() { + const theme = useTheme(); + + const [methodAnimation, setMethodAnimation] = useState([false, false, false]); + + const baseMethod = useIntersect(() => { + setMethodAnimation([false, false, false]); + }); + + const firstMethod = useIntersect(() => { + setMethodAnimation([true, false, false]); + }); + + const secondMethod = useIntersect(() => { + setMethodAnimation([true, true, false]); + }); + + const thirdMethod = useIntersect(() => { + setMethodAnimation([true, true, true]); + }); + + const { error, refetch } = useQuery(CACHE_KEY.ENTER, loginApi.getUrl, { + enabled: false, + onSuccess: (data) => onSuccessGetLoginUrl(data), + }); + + const onSuccessGetLoginUrl = (loginUrl: string) => { + location.href = loginUrl; + }; + + const handleClickGoogleLoginButton = () => { + refetch(); + }; + + if (error) { + return <>Error; + } + + return ( + +
+
+
+
{getThisDate()}
+
운동 일정
+
스터디 일정
+
동아리 일정
+
+
+ +
+
+
+ 달력 + +
+ 기록 + +
+ 공유할때 +
+ 달록 +
+
+ + 공유 캘린더 플랫폼 달록은 달력과 카테고리를
+ 이용하여 누구나 자신의 일정을 공유할 수 있습니다. +
+ +
+
+
+
+ 달록은 이렇습니다 +
+
+ 달록 사용법 1 + + 공유 받고 싶은 카테고리를 구독하여
내 달력에서 일정을 볼 수 있습니다. +
+
+
+
+ + 구글 캘린더에서 원하는 캘린더를 +
가져올 수 있습니다. +
+ 달록 사용법 2 +
+
+
+ 달록 사용법 3 + + 카테고리 내에서 팀원들과
+ 겹치지 않는 시간을
+ 확인할 수 있습니다. +
+
+
+
+
+
+ ); +} + +export default StartPage; diff --git a/frontend/src/recoil/atoms/index.ts b/frontend/src/recoil/atoms/index.ts new file mode 100644 index 00000000..2dc19022 --- /dev/null +++ b/frontend/src/recoil/atoms/index.ts @@ -0,0 +1,26 @@ +import { atom } from 'recoil'; + +import { ATOM_KEY } from '@/constants'; + +import { getAccessToken } from '@/utils/storage'; + +const sideBarState = atom({ + key: ATOM_KEY.SIDE_BAR, + default: false, +}); + +const userState = atom({ + key: ATOM_KEY.USER, + default: { + accessToken: getAccessToken() ?? '', + }, +}); + +const snackBarState = atom({ + key: ATOM_KEY.SNACK_BAR, + default: { + text: '', + }, +}); + +export { snackBarState, sideBarState, userState }; diff --git a/frontend/src/recoil/selectors/index.ts b/frontend/src/recoil/selectors/index.ts new file mode 100644 index 00000000..4372137a --- /dev/null +++ b/frontend/src/recoil/selectors/index.ts @@ -0,0 +1,13 @@ +import { selector } from 'recoil'; + +import { sideBarState } from '@/recoil/atoms'; + +import { SELECTOR_KEY } from '@/constants'; + +const sideBarSelector = selector({ + key: SELECTOR_KEY.SIDE_BAR, + get: ({ get }) => get(sideBarState), + set: ({ set }) => set(sideBarState, (prev) => !prev), +}); + +export { sideBarSelector }; diff --git a/frontend/src/styles/GlobalStyle.tsx b/frontend/src/styles/GlobalStyle.tsx new file mode 100644 index 00000000..efdf9a00 --- /dev/null +++ b/frontend/src/styles/GlobalStyle.tsx @@ -0,0 +1,48 @@ +import { css, Global, Theme } from '@emotion/react'; +import emotionReset from 'emotion-reset'; + +const global = ({ colors }: Theme) => css` + @import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.4/dist/web/static/pretendard.css'); + + ${emotionReset} + + *, + *::after, + *::before { + margin: 0; + + box-sizing: border-box; + } + + body { + overflow: overlay; + + font-family: 'Pretendard', sans-serif; + font-size: 3rem; + + *::-webkit-scrollbar { + width: 2rem; + } + + *::-webkit-scrollbar-thumb { + border-radius: 10px; + background-clip: padding-box; + border: 2px solid transparent; + + background: ${colors.YELLOW_500}; + } + + *::-webkit-scrollbar-track { + border-radius: 10px; + box-shadow: inset 0px 0px 5px white; + + background: ${colors.GRAY_200}; + } + } +`; + +function GlobalStyle() { + return ; +} + +export default GlobalStyle; diff --git a/frontend/src/styles/theme.ts b/frontend/src/styles/theme.ts new file mode 100644 index 00000000..9a38c3e9 --- /dev/null +++ b/frontend/src/styles/theme.ts @@ -0,0 +1,61 @@ +import { css, Theme } from '@emotion/react'; + +const colors = { + YELLOW_000: '#fff9db', + YELLOW_100: '#fff3bf', + YELLOW_200: '#ffec99', + YELLOW_300: '#ffe066', + YELLOW_400: '#fee500', + YELLOW_500: '#F4BD68', + YELLOW_600: '#fab005', + YELLOW_700: '#f59f00', + YELLOW_800: '#f08c00', + YELLOW_900: '#e67700', + GREEN_500: '#03c75a', + WHITE: '#ffffff', + GRAY_000: '#f8f9fa', + GRAY_100: '#f1f3f5', + GRAY_200: '#e9ecef', + GRAY_300: '#dee2e6', + GRAY_400: '#ced4da', + GRAY_500: '#adb5bd', + GRAY_600: '#868e96', + GRAY_700: '#495057', + GRAY_800: '#343a40', + GRAY_900: '#212529', + BLACK: '#000000', + RED_000: '#fff5f5', + RED_100: '#ffe3e3', + RED_200: '#ffc9c9', + RED_300: '#ffa8a8', + RED_400: '#ff8787', + RED_500: '#ff6b6b', + RED_600: '#fa5252', + RED_700: '#f03e3e', + RED_800: '#e03131', + RED_900: '#c92a2a', + ORANGE_500: '#EE6C4C', + BLUE_500: '#88B6B9', +}; + +const flex = { + row: css` + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + `, + column: css` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + `, +}; + +const theme: Theme = { + colors, + flex, +}; + +export default theme; diff --git a/frontend/src/utils/date.ts b/frontend/src/utils/date.ts new file mode 100644 index 00000000..4b071421 --- /dev/null +++ b/frontend/src/utils/date.ts @@ -0,0 +1,165 @@ +import { CalendarType } from '@/@types/calendar'; + +import { DATE_TIME } from '@/constants/date'; + +import { zeroFill } from '.'; + +const checkAllDay = (startDateTime: string | undefined, endDateTime: string | undefined) => { + if (startDateTime === undefined || endDateTime === undefined) { + return null; + } + + return startDateTime.includes(DATE_TIME.START) && endDateTime.includes(DATE_TIME.END); +}; + +const getBeforeDate = (targetDay: Date, offset: number) => + new Date(targetDay.setDate(targetDay.getDate() - offset)); + +const getBeforeYearMonth = (targetYear: number, targetMonth: number) => { + if (targetMonth === 1) { + return { year: targetYear - 1, month: 12 }; + } + + return { year: targetYear, month: targetMonth - 1 }; +}; + +const getCalendarMonth = (year: number, month: number) => { + const firstDate = new Date(year, month - 1, 1); + + const calendarInfo: Date[] = []; + + while (firstDate.getMonth() === month - 1) { + calendarInfo.push(new Date(firstDate)); + firstDate.setDate(firstDate.getDate() + 1); + } + + const firstDay = calendarInfo[0].getDay(); + const lastDay = calendarInfo[calendarInfo.length - 1].getDay(); + + if (firstDay !== 0) { + Array(firstDay) + .fill(0) + .forEach((_, idx) => { + calendarInfo.unshift(getBeforeDate(new Date(year, month - 1, 1), idx + 1)); + }); + } + + if (lastDay !== 6) { + Array(6 - lastDay) + .fill(0) + .forEach((_, idx) => { + calendarInfo.push(getNextDate(new Date(year, month, 0), idx + 1)); + }); + } + + return calendarInfo.map((el) => { + return { + year: el.getFullYear(), + month: el.getMonth() + 1, + date: el.getDate(), + day: el.getDay(), + }; + }); +}; + +const getDate = (dateInfo: Omit | null) => { + if (dateInfo === null) { + return getISODateString(new Date(+new Date() + 3240 * 10000).toISOString()); + } + + const { year, month, date } = dateInfo; + + return getISODateString(new Date(+new Date(year, month - 1, date) + 3240 * 10000).toISOString()); +}; + +const getDateTime = (dateInfo: Omit | null) => { + if (dateInfo === null) { + return new Date(+new Date() + 3240 * 10000).toISOString().replace(/\..*/, '').slice(0, -3); + } + + const { year, month, date } = dateInfo; + + return new Date(+new Date(year, month - 1, date) + 3240 * 10000) + .toISOString() + .replace(/\..*/, '') + .slice(0, -3); +}; + +const getDayFromFormattedDate = (date: string) => { + return new Date(date).getDay(); +}; + +const getFormattedDate = (year: number | string, month: number | string, date: number | string) => { + return `${year}-${zeroFill(month.toString())}-${zeroFill(date.toString())}`; +}; + +const getISODateString = (ISOString: string) => { + return ISOString.split('T')[0]; +}; + +const getKoreaISOString = (time: number) => { + return new Date(time - new Date().getTimezoneOffset() * 60000).toISOString(); +}; + +const getNextDate = (targetDay: Date, offset: number) => + new Date(targetDay.setDate(targetDay.getDate() + offset)); + +const getNextYearMonth = (targetYear: number, targetMonth: number) => { + if (targetMonth === 12) { + return { year: targetYear + 1, month: 1 }; + } + + return { year: targetYear, month: targetMonth + 1 }; +}; + +const getOneHourEarlierISOString = (ISOString: string) => { + const hour = ISOString.split('T')[1].split(':')[0]; + + const oneHourEarlierISOString = getKoreaISOString(new Date(ISOString).setHours(Number(hour) - 1)) + .replace(/\..*/, '') + .slice(0, -3); + + return oneHourEarlierISOString; +}; + +const getOneHourLaterISOString = (ISOString: string) => { + const hour = ISOString.split('T')[1].split(':')[0]; + + const oneHourEarlierISOString = getKoreaISOString(new Date(ISOString).setHours(Number(hour) + 1)) + .replace(/\..*/, '') + .slice(0, -3); + + return oneHourEarlierISOString; +}; + +const getThisDate = () => { + return new Date().getDate(); +}; + +const getThisMonth = () => { + return new Date().getMonth() + 1; +}; + +const getThisYear = () => { + return new Date().getFullYear(); +}; + +export { + checkAllDay, + getBeforeDate, + getBeforeYearMonth, + getCalendarMonth, + getDate, + getDateTime, + getDayFromFormattedDate, + getFormattedDate, + getISODateString, + getKoreaISOString, + getNextDate, + getNextYearMonth, + getOneHourEarlierISOString, + getOneHourLaterISOString, + getThisDate, + getThisMonth, + getThisYear, +}; diff --git a/frontend/src/utils/index.ts b/frontend/src/utils/index.ts new file mode 100644 index 00000000..cd5e7f3e --- /dev/null +++ b/frontend/src/utils/index.ts @@ -0,0 +1,34 @@ +import { InputRefType } from '@/@types'; + +const createPostBody = (inputRef: InputRefType) => { + const inputElements = Object.values(inputRef).map((el) => el.current); + const isValidInputRefs = inputElements.every((el) => el instanceof HTMLInputElement); + + if (!isValidInputRefs) { + return; + } + + const inputRefKeys = Object.keys(inputRef); + + const body = inputRefKeys.reduce((acc: any, cur: number | string) => { + acc[cur] = (inputRef[cur].current as HTMLInputElement).value; + + return acc; + }, {}); + + return body; +}; + +const getRandomNumber = (min: number, max: number) => { + return Math.floor(Math.random() * (max - min)) + min; +}; + +const getSearchParam = (key: string) => { + return new URLSearchParams(location.search).get(key); +}; + +const zeroFill = (str: string | number) => { + return str.toString().padStart(2, '0'); +}; + +export { createPostBody, getRandomNumber, getSearchParam, zeroFill }; diff --git a/frontend/src/utils/storage.ts b/frontend/src/utils/storage.ts new file mode 100644 index 00000000..52135302 --- /dev/null +++ b/frontend/src/utils/storage.ts @@ -0,0 +1,15 @@ +import { STORAGE_KEY } from '@/constants'; + +const getAccessToken = () => { + return localStorage.getItem(STORAGE_KEY.ACCESS_TOKEN); +}; + +const setAccessToken = (accessToken: string) => { + localStorage.setItem(STORAGE_KEY.ACCESS_TOKEN, accessToken); +}; + +const removeAccessToken = () => { + localStorage.removeItem(STORAGE_KEY.ACCESS_TOKEN); +}; + +export { getAccessToken, removeAccessToken, setAccessToken }; diff --git a/frontend/src/validation/index.ts b/frontend/src/validation/index.ts new file mode 100644 index 00000000..a4fe9a0f --- /dev/null +++ b/frontend/src/validation/index.ts @@ -0,0 +1,17 @@ +const validateLength = (target: string, min: number, max: number) => { + return min <= target.length && target.length <= max; +}; + +const validateNotEmpty = (target: string) => { + return target.length > 0; +}; + +const validateNotEqualString = (target: string, comparisonTarget: string) => { + return target.trim() !== comparisonTarget; +}; + +const validateStartEndDateTime = (startDate: string, endDate: string) => { + return startDate <= endDate; +}; + +export { validateLength, validateNotEmpty, validateNotEqualString, validateStartEndDateTime }; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 00000000..eaf372ba --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "esnext", + "module": "ESNext", + "jsx": "react-jsx", + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "esModuleInterop": true, + "jsxImportSource": "@emotion/react", + "moduleResolution": "node", + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "exclude": ["node_modules"], + "include": ["src"] +} diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js new file mode 100644 index 00000000..bf706698 --- /dev/null +++ b/frontend/webpack.config.js @@ -0,0 +1,62 @@ +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const path = require('path'); +const webpack = require('webpack'); +const { CleanWebpackPlugin } = require('clean-webpack-plugin'); +const Dotenv = require('dotenv-webpack'); + +const prod = process.env.NODE_ENV === 'production'; + +module.exports = { + mode: prod ? 'production' : 'development', + devtool: prod ? 'hidden-source-map' : 'eval', + entry: './src/index.tsx', + resolve: { + alias: { + '@': path.resolve(__dirname, './src/'), + }, + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, + module: { + rules: [ + { + test: /\.tsx?$/, + use: ['babel-loader', 'ts-loader'], + }, + { + test: /\\.css$/, + use: ['style-loader', 'css-loader'], + }, + { + test: /\.(png|jp(e*)g|gif)$/, + use: [ + { + loader: 'file-loader', + options: { + name: 'images/[hash]-[name].[ext]', + }, + }, + ], + }, + ], + }, + output: { + path: path.join(__dirname, '/dist'), + filename: 'bundle.js', + }, + plugins: [ + new webpack.ProvidePlugin({ + React: 'react', + }), + new HtmlWebpackPlugin({ + template: './src/index.html', + favicon: './src/assets/dallog_color.png', + }), + new CleanWebpackPlugin(), + new Dotenv(), + ], + devServer: { + historyApiFallback: true, + port: 3000, + hot: true, + }, +}; diff --git a/frontend/yarn.lock b/frontend/yarn.lock new file mode 100644 index 00000000..7589f939 --- /dev/null +++ b/frontend/yarn.lock @@ -0,0 +1,13329 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@ampproject/remapping@^2.1.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + dependencies: + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.6.tgz#8b37d24e88e8e21c499d4328db80577d8882fa53" + integrity sha512-tzulrgDT0QD6U7BJ4TKVk2SDDg7wlP39P9yAx1RfLy7vP/7rsDRlWVfbWxElslu56+r7QOhB2NSDsabYYruoZQ== + +"@babel/compat-data@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" + integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== + +"@babel/core@7.12.9": + version "7.12.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.9.tgz#fd450c4ec10cdbb980e2928b7aa7a28484593fc8" + integrity sha512-gTXYh3M5wb7FRXQy+FErKFAv90BnlOuNn1QkCK2lREoPAjrQCO49+HVSrFoe5uakFAF5eenS75KbO2vQiLrTMQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-module-transforms" "^7.12.1" + "@babel/helpers" "^7.12.5" + "@babel/parser" "^7.12.7" + "@babel/template" "^7.12.7" + "@babel/traverse" "^7.12.9" + "@babel/types" "^7.12.7" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.19" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + +"@babel/core@7.13.10": + version "7.13.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.10.tgz#07de050bbd8193fcd8a3c27918c0890613a94559" + integrity sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.9" + "@babel/helper-compilation-targets" "^7.13.10" + "@babel/helper-module-transforms" "^7.13.0" + "@babel/helpers" "^7.13.10" + "@babel/parser" "^7.13.10" + "@babel/template" "^7.12.13" + "@babel/traverse" "^7.13.0" + "@babel/types" "^7.13.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.1.2" + lodash "^4.17.19" + semver "^6.3.0" + source-map "^0.5.0" + +"@babel/core@^7.1.0", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.18.6", "@babel/core@^7.7.5": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.6.tgz#54a107a3c298aee3fe5e1947a6464b9b6faca03d" + integrity sha512-cQbWBpxcbbs/IUredIPkHiAGULLV8iwgNRMFzvbhEXISp4f3rUUXE5+TIw6KwUWUR3DwyI6gmBRnmAtYaWehwQ== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.6" + "@babel/helper-compilation-targets" "^7.18.6" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helpers" "^7.18.6" + "@babel/parser" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + +"@babel/core@^7.11.6": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8" + integrity sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw== + dependencies: + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.10" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-module-transforms" "^7.18.9" + "@babel/helpers" "^7.18.9" + "@babel/parser" "^7.18.10" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.18.10" + "@babel/types" "^7.18.10" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + +"@babel/generator@7.13.9": + version "7.13.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" + integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== + dependencies: + "@babel/types" "^7.13.0" + jsesc "^2.5.1" + source-map "^0.5.0" + +"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.13.0", "@babel/generator@^7.13.9", "@babel/generator@^7.18.6", "@babel/generator@^7.18.7": + version "7.18.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.7.tgz#2aa78da3c05aadfc82dbac16c99552fc802284bd" + integrity sha512-shck+7VLlY72a2w9c3zYWuE1pwOKEiQHV7GTUbSnhyl5eu3i04t30tBY82ZRWrDfo3gkakCFtevExnxbkf2a3A== + dependencies: + "@babel/types" "^7.18.7" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + +"@babel/generator@^7.18.10", "@babel/generator@^7.7.2": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.10.tgz#794f328bfabdcbaf0ebf9bf91b5b57b61fa77a2a" + integrity sha512-0+sW7e3HjQbiHbj1NeU/vN8ornohYlacAfZIaXhdoGweQqgcNy69COVciYYqEXJ/v+9OBA7Frxm4CVAuNqKeNA== + dependencies: + "@babel/types" "^7.18.10" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.6.tgz#f14d640ed1ee9246fb33b8255f08353acfe70e6a" + integrity sha512-KT10c1oWEpmrIRYnthbzHgoOf6B+Xd6a5yhdbNtdhtG7aO1or5HViuf1TQR36xY/QprXA5nvxO6nAjhJ4y38jw== + dependencies: + "@babel/helper-explode-assignable-expression" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.10", "@babel/helper-compilation-targets@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.6.tgz#18d35bfb9f83b1293c22c55b3d576c1315b6ed96" + integrity sha512-vFjbfhNCzqdeAtZflUFrG5YIFqGTqsctrtkZ1D/NB0mDW9TwW3GmmUepYY4G9wCET5rY5ugz4OGTcLd614IzQg== + dependencies: + "@babel/compat-data" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.20.2" + semver "^6.3.0" + +"@babel/helper-compilation-targets@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" + integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== + dependencies: + "@babel/compat-data" "^7.18.8" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.20.2" + semver "^6.3.0" + +"@babel/helper-create-class-features-plugin@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.6.tgz#6f15f8459f3b523b39e00a99982e2c040871ed72" + integrity sha512-YfDzdnoxHGV8CzqHGyCbFvXg5QESPFkXlHtvdCkesLjjVMT2Adxe4FGUR5ChIb3DxSaXO12iIOCWoXdsUVwnqw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-function-name" "^7.18.6" + "@babel/helper-member-expression-to-functions" "^7.18.6" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + +"@babel/helper-create-regexp-features-plugin@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz#3e35f4e04acbbf25f1b3534a657610a000543d3c" + integrity sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.1.0" + +"@babel/helper-define-polyfill-provider@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.1.5.tgz#3c2f91b7971b9fc11fe779c945c014065dea340e" + integrity sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-define-polyfill-provider@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" + integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA== + dependencies: + "@babel/helper-compilation-targets" "^7.13.0" + "@babel/helper-module-imports" "^7.12.13" + "@babel/helper-plugin-utils" "^7.13.0" + "@babel/traverse" "^7.13.0" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + +"@babel/helper-environment-visitor@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.6.tgz#b7eee2b5b9d70602e59d1a6cad7dd24de7ca6cd7" + integrity sha512-8n6gSfn2baOY+qlp+VSzsosjCVGFqWKmDF0cCWOybh52Dw3SEyoWR1KrhMJASjLwIEkkAufZ0xvr+SxLHSpy2Q== + +"@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== + +"@babel/helper-explode-assignable-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz#41f8228ef0a6f1a036b8dfdfec7ce94f9a6bc096" + integrity sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-function-name@^7.12.13", "@babel/helper-function-name@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.6.tgz#8334fecb0afba66e6d87a7e8c6bb7fed79926b83" + integrity sha512-0mWMxV1aC97dhjCah5U5Ua7668r5ZmSC2DLfH2EZnf9c3/dHZKiFa5pRLMH5tjSl471tY6496ZWk/kjNONBxhw== + dependencies: + "@babel/template" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/helper-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" + integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== + dependencies: + "@babel/template" "^7.18.6" + "@babel/types" "^7.18.9" + +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-member-expression-to-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.6.tgz#44802d7d602c285e1692db0bad9396d007be2afc" + integrity sha512-CeHxqwwipekotzPDUuJOfIMtcIHBuc7WAzLmTYWctVigqS5RktNMQ5bEwQSuGewzYnCtTWa3BARXeiLxDTv+Ng== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.6.tgz#57e3ca669e273d55c3cda55e6ebf552f37f483c8" + integrity sha512-L//phhB4al5uucwzlimruukHB3jRd5JGClwRMD/ROrVjXfLqovYnvQrK/JK36WYyVwGGO7OD3kMyVTjx+WVPhw== + dependencies: + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/helper-module-transforms@^7.13.0": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.8.tgz#4f8408afead0188cfa48672f9d0e5787b61778c8" + integrity sha512-che3jvZwIcZxrwh63VfnFTUzcAM9v/lznYkkRxIBGMPt1SudOKHAEec0SIRCfiuIzTcF7VGj/CaTT6gY4eWxvA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.8" + "@babel/types" "^7.18.8" + +"@babel/helper-module-transforms@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" + integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-plugin-utils@7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375" + integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg== + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.6.tgz#9448974dd4fb1d80fefe72e8a0af37809cd30d6d" + integrity sha512-gvZnm1YAAxh13eJdkb9EWHBnF3eAub3XTLCZEehHT2kWxiKVRL64+ae5Y6Ivne0mVHmMYKT+xWgZO+gQhuLUBg== + +"@babel/helper-remap-async-to-generator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.6.tgz#fa1f81acd19daee9d73de297c0308783cd3cfc23" + integrity sha512-z5wbmV55TveUPZlCLZvxWHtrjuJd+8inFhk7DG0WW87/oJuGDcjDiu7HIvGcpf5464L6xKCg3vNkmlVVz9hwyQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-wrap-function" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/helper-replace-supers@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.6.tgz#efedf51cfccea7b7b8c0f00002ab317e7abfe420" + integrity sha512-fTf7zoXnUGl9gF25fXCWE26t7Tvtyn6H4hkLSYhATwJvw2uYxd3aoXplMSe0g9XbwK7bmxNes7+FGO0rB/xC0g== + dependencies: + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-member-expression-to-functions" "^7.18.6" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/helper-simple-access@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" + integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-skip-transparent-expression-wrappers@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.6.tgz#7dff00a5320ca4cf63270e5a0eca4b268b7380d9" + integrity sha512-4KoLhwGS9vGethZpAhYnMejWkX64wsnHPDwvOsKWU6Fg4+AlK2Jz3TyjQLMEPvz+1zemi/WBdkYxCD0bAfIkiw== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-split-export-declaration@^7.12.13", "@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" + integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== + +"@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" + integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== + +"@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== + +"@babel/helper-wrap-function@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.6.tgz#ec44ea4ad9d8988b90c3e465ba2382f4de81a073" + integrity sha512-I5/LZfozwMNbwr/b1vhhuYD+J/mU+gfGAj5td7l5Rv9WYmH6i3Om69WGKNmlIpsVW/mF6O5bvTKbvDQZVgjqOw== + dependencies: + "@babel/helper-function-name" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/helpers@^7.12.5", "@babel/helpers@^7.13.10", "@babel/helpers@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.6.tgz#4c966140eaa1fcaa3d5a8c09d7db61077d4debfd" + integrity sha512-vzSiiqbQOghPngUYt/zWGvK3LAsPhz55vc9XNN0xAl2gV4ieShI2OQli5duxWHD+72PZPTKAcfcZDE1Cwc5zsQ== + dependencies: + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/helpers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" + integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== + dependencies: + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" + +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@7.14.6": + version "7.14.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.6.tgz#d85cc68ca3cac84eae384c06f032921f5227f4b2" + integrity sha512-oG0ej7efjEXxb4UgE+klVx+3j4MVo+A2vCzm7OUN4CLo6WhQ+vSOD2yJ8m7B+DghObxtLxt3EfgMWpq+AsWehQ== + +"@babel/parser@^7.1.0", "@babel/parser@^7.18.10", "@babel/parser@^7.18.11": + version "7.18.11" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" + integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== + +"@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.14.7", "@babel/parser@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.6.tgz#845338edecad65ebffef058d3be851f1d28a63bc" + integrity sha512-uQVSa9jJUe/G/304lXspfWVpKpK4euFLgGiMQFOCpM/bgcAdeoHwi/OQz23O9GK2osz26ZiXRRV9aV+Yl1O8tw== + +"@babel/parser@^7.13.0", "@babel/parser@^7.13.10", "@babel/parser@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.8.tgz#822146080ac9c62dac0823bb3489622e0bc1cbdf" + integrity sha512-RSKRfYX20dyH+elbJK2uqAkVyucL+xXzhqlMD5/ZXx+dAAwpyB7HsvnHe/ZUGOF+xLr5Wx9/JoXVTj6BQE2/oA== + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" + integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.6.tgz#b4e4dbc2cd1acd0133479918f7c6412961c9adb8" + integrity sha512-Udgu8ZRgrBrttVz6A0EVL0SJ1z+RLbIeqsu632SA1hf0awEppD6TvdznoH+orIF8wtFFAV/Enmw9Y+9oV8TQcw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.18.6" + +"@babel/plugin-proposal-async-generator-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.6.tgz#aedac81e6fc12bb643374656dd5f2605bf743d17" + integrity sha512-WAz4R9bvozx4qwf74M+sfqPMKfSqwM0phxPTR6iJIi8robgzXwkEgmeJG1gEKhm6sDqT/U9aV3lfcqybIpev8w== + dependencies: + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-remap-async-to-generator" "^7.18.6" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-proposal-class-properties@^7.12.1", "@babel/plugin-proposal-class-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-class-static-block@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz#8aa81d403ab72d3962fc06c26e222dacfc9b9020" + integrity sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-proposal-decorators@^7.12.12": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.6.tgz#68e9fd0f022b944f84a8824bb28bfaee724d2595" + integrity sha512-gAdhsjaYmiZVxx5vTMiRfj31nB7LhwBJFMSLzeDxc7X4tKLixup0+k9ughn0RcpBrv9E3PBaXJW7jF5TCihAOg== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/plugin-syntax-decorators" "^7.18.6" + +"@babel/plugin-proposal-dynamic-import@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" + integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-proposal-export-default-from@^7.12.1": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-default-from/-/plugin-proposal-export-default-from-7.18.6.tgz#121b3ba0152d0020865bc86271c8150e5115abc7" + integrity sha512-oTvzWB16T9cB4j5kX8c8DuUHo/4QtR2P9vnUNKed9xqFP8Jos/IRniz1FiIryn6luDYoltDJSYF7RCpbm2doMg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-export-default-from" "^7.18.6" + +"@babel/plugin-proposal-export-namespace-from@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.6.tgz#1016f0aa5ab383bbf8b3a85a2dcaedf6c8ee7491" + integrity sha512-zr/QcUlUo7GPo6+X1wC98NJADqmy5QTFWWhqeQWiki4XHafJtLl/YMGkmRB2szDD2IYJCCdBTd4ElwhId9T7Xw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-proposal-json-strings@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" + integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-proposal-logical-assignment-operators@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.6.tgz#3b9cac6f1ffc2aa459d111df80c12020dfc6b665" + integrity sha512-zMo66azZth/0tVd7gmkxOkOjs2rpHyhpcFo565PUP37hSp6hSd9uUKIfTDFMz58BwqgQKhJ9YxtM5XddjXVn+Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-object-rest-spread@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz#def9bd03cea0f9b72283dac0ec22d289c7691069" + integrity sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.0" + "@babel/plugin-transform-parameters" "^7.12.1" + +"@babel/plugin-proposal-object-rest-spread@^7.12.1", "@babel/plugin-proposal-object-rest-spread@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.6.tgz#ec93bba06bfb3e15ebd7da73e953d84b094d5daf" + integrity sha512-9yuM6wr4rIsKa1wlUAbZEazkCrgw2sMPEXCr4Rnwetu7cEW1NydkCWytLuYletbf8vFxdJxFhwEZqMpOx2eZyw== + dependencies: + "@babel/compat-data" "^7.18.6" + "@babel/helper-compilation-targets" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.18.6" + +"@babel/plugin-proposal-optional-catch-binding@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-proposal-optional-chaining@^7.12.7", "@babel/plugin-proposal-optional-chaining@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.6.tgz#46d4f2ffc20e87fad1d98bc4fa5d466366f6aa0b" + integrity sha512-PatI6elL5eMzoypFAiYDpYQyMtXTn+iMhuxxQt5mAXD4fEmKorpSI3PHd+i3JXBJN3xyA6MvJv7at23HffFHwA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.6" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.12.1", "@babel/plugin-proposal-private-methods@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-private-property-in-object@^7.12.1", "@babel/plugin-proposal-private-property-in-object@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz#a64137b232f0aca3733a67eb1a144c192389c503" + integrity sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-proposal-unicode-property-regex@^7.18.6", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" + integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" + integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" + integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" + integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-decorators@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.18.6.tgz#2e45af22835d0b0f8665da2bfd4463649ce5dbc1" + integrity sha512-fqyLgjcxf/1yhyZ6A+yo1u9gJ7eleFQod2lkaUsF9DQ7sbbY3Ligym3L0+I2c0WmqNKDpoD9UTb1AKP3qRMOAQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" + integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-default-from@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-default-from/-/plugin-syntax-export-default-from-7.18.6.tgz#8df076711a4818c4ce4f23e61d622b0ba2ff84bc" + integrity sha512-Kr//z3ujSVNx6E9z9ih5xXXMqK07VVTuqPmqGe6Mss/zW5XPeLZeSDZoP9ab/hT4wPKqAgjl2PnhPrcpk8Seew== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" + integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-flow@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz#774d825256f2379d06139be0c723c4dd444f3ca1" + integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-import-assertions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz#cd6190500a4fa2fe31990a963ffab4b63e4505e4" + integrity sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" + integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" + integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@7.12.1": + version "7.12.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.1.tgz#9d9d357cc818aa7ae7935917c1257f67677a0926" + integrity sha512-1yRi7yAtB0ETgxdY9ti/p2TivUxJkTdhu/ZbF9MshVGqOx1TdB3b7xCXs49Fupgg50N45KcAsRP/ZqWjs9SRjg== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.18.6", "@babel/plugin-syntax-jsx@^7.2.0": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" + integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" + integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" + integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" + integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@7.8.3", "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" + integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" + integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" + integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" + integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" + integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.18.6", "@babel/plugin-syntax-typescript@^7.7.2": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285" + integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-arrow-functions@^7.12.1", "@babel/plugin-transform-arrow-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz#19063fcf8771ec7b31d742339dac62433d0611fe" + integrity sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-async-to-generator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz#ccda3d1ab9d5ced5265fdb13f1882d5476c71615" + integrity sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-remap-async-to-generator" "^7.18.6" + +"@babel/plugin-transform-block-scoped-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-block-scoping@^7.12.12", "@babel/plugin-transform-block-scoping@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.6.tgz#b5f78318914615397d86a731ef2cc668796a726c" + integrity sha512-pRqwb91C42vs1ahSAWJkxOxU1RHWDn16XAa6ggQ72wjLlWyYeAcLvTtE0aM8ph3KNydy9CQF2nLYcjq1WysgxQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-classes@^7.12.1", "@babel/plugin-transform-classes@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.6.tgz#3501a8f3f4c7d5697c27a3eedbee71d68312669f" + integrity sha512-XTg8XW/mKpzAF3actL554Jl/dOYoJtv3l8fxaEczpgz84IeeVf+T1u2CSvPHuZbt0w3JkIx4rdn/MRQI7mo0HQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-function-name" "^7.18.6" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.6.tgz#5d15eb90e22e69604f3348344c91165c5395d032" + integrity sha512-9repI4BhNrR0KenoR9vm3/cIc1tSBIo+u1WVjKCAynahj25O8zfbiE6JtAtHPGQSs4yZ+bA8mRasRP+qc+2R5A== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-destructuring@^7.12.1", "@babel/plugin-transform-destructuring@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.6.tgz#a98b0e42c7ffbf5eefcbcf33280430f230895c6f" + integrity sha512-tgy3u6lRp17ilY8r1kP4i2+HDUwxlVqq3RTc943eAWSzGgpU1qhiKpqZ5CMyHReIYPHdo3Kg8v8edKtDqSVEyQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-dotall-regex@^7.18.6", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" + integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-duplicate-keys@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.6.tgz#e6c94e8cd3c9dd8a88144f7b78ae22975a7ff473" + integrity sha512-NJU26U/208+sxYszf82nmGYqVF9QN8py2HFTblPT9hbawi8+1C5a9JubODLTGFuT0qlkqVinmkwOD13s0sZktg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-exponentiation-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" + integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-flow-strip-types@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.18.6.tgz#6d3dd9f9c0fe13349428569fef00b31310bb3f9f" + integrity sha512-wE0xtA7csz+hw4fKPwxmu5jnzAsXPIO57XnRwzXP3T19jWh1BODnPGoG9xKYwvAwusP7iUktHayRFbMPGtODaQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-flow" "^7.18.6" + +"@babel/plugin-transform-for-of@^7.12.1", "@babel/plugin-transform-for-of@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.6.tgz#e0fdb813be908e91ccc9ec87b30cc2eabf046f7c" + integrity sha512-WAjoMf4wIiSsy88KmG7tgj2nFdEK7E46tArVtcgED7Bkj6Fg/tG5SbvNIOKxbFS2VFgNh6+iaPswBeQZm4ox8w== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-function-name@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.6.tgz#6a7e4ae2893d336fd1b8f64c9f92276391d0f1b4" + integrity sha512-kJha/Gbs5RjzIu0CxZwf5e3aTTSlhZnHMT8zPWnJMjNpLOUgqevg+PN5oMH68nMCXnfiMo4Bhgxqj59KHTlAnA== + dependencies: + "@babel/helper-compilation-targets" "^7.18.6" + "@babel/helper-function-name" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.6.tgz#9d6af353b5209df72960baf4492722d56f39a205" + integrity sha512-x3HEw0cJZVDoENXOp20HlypIHfl0zMIhMVZEBVTfmqbObIpsMxMbmU5nOEO8R7LYT+z5RORKPlTI5Hj4OsO9/Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-member-expression-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-modules-amd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz#8c91f8c5115d2202f277549848874027d7172d21" + integrity sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg== + dependencies: + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-commonjs@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz#afd243afba166cca69892e24a8fd8c9f2ca87883" + integrity sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q== + dependencies: + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-systemjs@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.6.tgz#026511b7657d63bf5d4cf2fd4aeb963139914a54" + integrity sha512-UbPYpXxLjTw6w6yXX2BYNxF3p6QY225wcTkfQCy3OMnSlS/C3xGtwUjEzGkldb/sy6PWLiCQ3NbYfjWUTI3t4g== + dependencies: + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + babel-plugin-dynamic-import-node "^2.3.3" + +"@babel/plugin-transform-modules-umd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" + integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== + dependencies: + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz#c89bfbc7cc6805d692f3a49bc5fc1b630007246d" + integrity sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-new-target@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" + integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-object-super@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" + +"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.6.tgz#cbe03d5a4c6385dd756034ac1baa63c04beab8dc" + integrity sha512-FjdqgMv37yVl/gwvzkcB+wfjRI8HQmc5EgOG9iGNvUY1ok+TjsoaMP7IqCDZBhkFcM5f3OPVMs6Dmp03C5k4/A== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-property-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-display-name@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.18.6.tgz#8b1125f919ef36ebdfff061d664e266c666b9415" + integrity sha512-TV4sQ+T013n61uMoygyMRm+xf04Bd5oqFpv2jAEQwSZ8NwQA7zeRPg1LMVg2PWi3zWBz+CLKD+v5bcpZ/BS0aA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-react-jsx-development@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5" + integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.18.6" + +"@babel/plugin-transform-react-jsx@^7.12.1", "@babel/plugin-transform-react-jsx@^7.12.12", "@babel/plugin-transform-react-jsx@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.6.tgz#2721e96d31df96e3b7ad48ff446995d26bc028ff" + integrity sha512-Mz7xMPxoy9kPS/JScj6fJs03TZ/fZ1dJPlMjRAgTaxaS0fUBk8FV/A2rRgfPsVCZqALNwMexD+0Uaf5zlcKPpw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-jsx" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/plugin-transform-react-pure-annotations@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.6.tgz#561af267f19f3e5d59291f9950fd7b9663d0d844" + integrity sha512-I8VfEPg9r2TRDdvnHgPepTKvuRomzA8+u+nhY7qSI1fR2hRNebasZEETLyM5mAUr0Ku56OkXJ0I7NHJnO6cJiQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-regenerator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz#585c66cb84d4b4bf72519a34cfce761b8676ca73" + integrity sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + regenerator-transform "^0.15.0" + +"@babel/plugin-transform-reserved-words@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" + integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-shorthand-properties@^7.12.1", "@babel/plugin-transform-shorthand-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-spread@^7.12.1", "@babel/plugin-transform-spread@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.6.tgz#82b080241965f1689f0a60ecc6f1f6575dbdb9d6" + integrity sha512-ayT53rT/ENF8WWexIRg9AiV9h0aIteyWn5ptfZTZQrjk/+f3WdrJGCY4c9wcgl2+MKkKPhzbYp97FTsquZpDCw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-skip-transparent-expression-wrappers" "^7.18.6" + +"@babel/plugin-transform-sticky-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" + integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-template-literals@^7.12.1", "@babel/plugin-transform-template-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.6.tgz#b763f4dc9d11a7cce58cf9a490d82e80547db9c2" + integrity sha512-UuqlRrQmT2SWRvahW46cGSany0uTlcj8NYOS5sRGYi8FxPYPoLd5DDmMd32ZXEj2Jq+06uGVQKHxa/hJx2EzKw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-typeof-symbol@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.6.tgz#486bb39d5a18047358e0d04dc0d2f322f0b92e92" + integrity sha512-7m71iS/QhsPk85xSjFPovHPcH3H9qeyzsujhTc+vcdnsXavoWYJ74zx0lP5RhpC5+iDnVLO+PPMHzC11qels1g== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-typescript@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.6.tgz#8f4ade1a9cf253e5cf7c7c20173082c2c08a50a7" + integrity sha512-ijHNhzIrLj5lQCnI6aaNVRtGVuUZhOXFLRVFs7lLrkXTHip4FKty5oAuQdk4tywG0/WjXmjTfQCWmuzrvFer1w== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-typescript" "^7.18.6" + +"@babel/plugin-transform-unicode-escapes@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.6.tgz#0d01fb7fb2243ae1c033f65f6e3b4be78db75f27" + integrity sha512-XNRwQUXYMP7VLuy54cr/KS/WeL3AZeORhrmeZ7iewgu+X2eBqmpaLI/hzqr9ZxCeUoq0ASK4GUzSM0BDhZkLFw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-unicode-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" + integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/preset-env@^7.12.11", "@babel/preset-env@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.6.tgz#953422e98a5f66bc56cd0b9074eaea127ec86ace" + integrity sha512-WrthhuIIYKrEFAwttYzgRNQ5hULGmwTj+D6l7Zdfsv5M7IWV/OZbUfbeL++Qrzx1nVJwWROIFhCHRYQV4xbPNw== + dependencies: + "@babel/compat-data" "^7.18.6" + "@babel/helper-compilation-targets" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.6" + "@babel/plugin-proposal-async-generator-functions" "^7.18.6" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.18.6" + "@babel/plugin-proposal-dynamic-import" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.6" + "@babel/plugin-proposal-json-strings" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.18.6" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.18.6" + "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.18.6" + "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.18.6" + "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.18.6" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.18.6" + "@babel/plugin-transform-async-to-generator" "^7.18.6" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.18.6" + "@babel/plugin-transform-classes" "^7.18.6" + "@babel/plugin-transform-computed-properties" "^7.18.6" + "@babel/plugin-transform-destructuring" "^7.18.6" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.6" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.18.6" + "@babel/plugin-transform-function-name" "^7.18.6" + "@babel/plugin-transform-literals" "^7.18.6" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.18.6" + "@babel/plugin-transform-modules-commonjs" "^7.18.6" + "@babel/plugin-transform-modules-systemjs" "^7.18.6" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.18.6" + "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.18.6" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.18.6" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.18.6" + "@babel/plugin-transform-sticky-regex" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.6" + "@babel/plugin-transform-typeof-symbol" "^7.18.6" + "@babel/plugin-transform-unicode-escapes" "^7.18.6" + "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.18.6" + babel-plugin-polyfill-corejs2 "^0.3.1" + babel-plugin-polyfill-corejs3 "^0.5.2" + babel-plugin-polyfill-regenerator "^0.3.1" + core-js-compat "^3.22.1" + semver "^6.3.0" + +"@babel/preset-flow@^7.12.1": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-flow/-/preset-flow-7.18.6.tgz#83f7602ba566e72a9918beefafef8ef16d2810cb" + integrity sha512-E7BDhL64W6OUqpuyHnSroLnqyRTcG6ZdOBl1OKI/QK/HJfplqK/S3sq1Cckx7oTodJ5yOXyfw7rEADJ6UjoQDQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-flow-strip-types" "^7.18.6" + +"@babel/preset-modules@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9" + integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.12.10", "@babel/preset-react@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.18.6.tgz#979f76d6277048dc19094c217b507f3ad517dd2d" + integrity sha512-zXr6atUmyYdiWRVLOZahakYmOBHtWc2WGCkP8PYTgZi0iJXDY2CN180TdrIW4OGOAdLc7TifzDIvtx6izaRIzg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-react-display-name" "^7.18.6" + "@babel/plugin-transform-react-jsx" "^7.18.6" + "@babel/plugin-transform-react-jsx-development" "^7.18.6" + "@babel/plugin-transform-react-pure-annotations" "^7.18.6" + +"@babel/preset-typescript@^7.12.7", "@babel/preset-typescript@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.18.6.tgz#ce64be3e63eddc44240c6358daefac17b3186399" + integrity sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-validator-option" "^7.18.6" + "@babel/plugin-transform-typescript" "^7.18.6" + +"@babel/register@^7.12.1": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.18.6.tgz#48a4520f1b2a7d7ac861e8148caeb0cefe6c59db" + integrity sha512-tkYtONzaO8rQubZzpBnvZPFcHgh8D9F55IjOsYton4X2IBoyRn2ZSWQqySTZnUn2guZbxbQiAB27hJEbvXamhQ== + dependencies: + clone-deep "^4.0.1" + find-cache-dir "^2.0.0" + make-dir "^2.1.0" + pirates "^4.0.5" + source-map-support "^0.5.16" + +"@babel/runtime@7.7.2": + version "7.7.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.2.tgz#111a78002a5c25fc8e3361bedc9529c696b85a6a" + integrity sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw== + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/runtime@^7.0.0", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.17.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.6.tgz#6a1ef59f838debd670421f8c7f2cbb8da9751580" + integrity sha512-t9wi7/AW6XtKahAe20Yw0/mMljKq0B1r2fPdvaAdV/KPDZewFXdaaa6K7lxmZBZ8FBNpCiAT6iHPmd6QO9bKfQ== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/runtime@~7.5.4": + version "7.5.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" + integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ== + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/template@^7.12.13", "@babel/template@^7.12.7", "@babel/template@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" + integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.6" + "@babel/types" "^7.18.6" + +"@babel/template@^7.18.10", "@babel/template@^7.3.3": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" + integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.10" + "@babel/types" "^7.18.10" + +"@babel/traverse@7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.0.tgz#6d95752475f86ee7ded06536de309a65fc8966cc" + integrity sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ== + dependencies: + "@babel/code-frame" "^7.12.13" + "@babel/generator" "^7.13.0" + "@babel/helper-function-name" "^7.12.13" + "@babel/helper-split-export-declaration" "^7.12.13" + "@babel/parser" "^7.13.0" + "@babel/types" "^7.13.0" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.19" + +"@babel/traverse@^7.1.6", "@babel/traverse@^7.12.11", "@babel/traverse@^7.12.9", "@babel/traverse@^7.13.0", "@babel/traverse@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.6.tgz#a228562d2f46e89258efa4ddd0416942e2fd671d" + integrity sha512-zS/OKyqmD7lslOtFqbscH6gMLFYOfG1YPqCKfAW5KrTeolKqvB8UelR49Fpr6y93kYkW2Ik00mT1LOGiAGvizw== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-function-name" "^7.18.6" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.18.6" + "@babel/types" "^7.18.6" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/traverse@^7.18.10", "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": + version "7.18.11" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f" + integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.10" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.18.11" + "@babel/types" "^7.18.10" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/traverse@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.8.tgz#f095e62ab46abf1da35e5a2011f43aee72d8d5b0" + integrity sha512-UNg/AcSySJYR/+mIcJQDCv00T+AqRO7j/ZEJLzpaYtgM48rMg5MnkJgyNqkzo88+p4tfRvZJCEiwwfG6h4jkRg== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.7" + "@babel/helper-environment-visitor" "^7.18.6" + "@babel/helper-function-name" "^7.18.6" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.18.8" + "@babel/types" "^7.18.8" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@7.13.0": + version "7.13.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80" + integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + lodash "^4.17.19" + to-fast-properties "^2.0.0" + +"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" + integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== + dependencies: + "@babel/helper-string-parser" "^7.18.10" + "@babel/helper-validator-identifier" "^7.18.6" + to-fast-properties "^2.0.0" + +"@babel/types@^7.12.11", "@babel/types@^7.12.7", "@babel/types@^7.18.6", "@babel/types@^7.18.7", "@babel/types@^7.2.0", "@babel/types@^7.4.4": + version "7.18.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.7.tgz#a4a2c910c15040ea52cdd1ddb1614a65c8041726" + integrity sha512-QG3yxTcTIBoAcQmkCs+wAPYZhu7Dk9rXKacINfNbdJDNERTbLQbHGyVG8q/YGMPeCJRIhSY0+fTc5+xuh6WPSQ== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + to-fast-properties "^2.0.0" + +"@babel/types@^7.13.0", "@babel/types@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.8.tgz#c5af199951bf41ba4a6a9a6d0d8ad722b30cd42f" + integrity sha512-qwpdsmraq0aJ3osLJRApsc2ouSJCdnMeZwB0DhbtHAtRpZNZCdlbRnHIgcRKzdE1g0iOGg644fzjOBcdOz9cPw== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + to-fast-properties "^2.0.0" + +"@base2/pretty-print-object@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz#371ba8be66d556812dc7fb169ebc3c08378f69d4" + integrity sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA== + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + +"@cnakazawa/watch@^1.0.3": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" + integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ== + dependencies: + exec-sh "^0.3.2" + minimist "^1.2.0" + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@design-systems/utils@2.12.0": + version "2.12.0" + resolved "https://registry.yarnpkg.com/@design-systems/utils/-/utils-2.12.0.tgz#955c108be07cb8f01532207cbfea8f848fa760c9" + integrity sha512-Y/d2Zzr+JJfN6u1gbuBUb1ufBuLMJJRZQk+dRmw8GaTpqKx5uf7cGUYGTwN02dIb3I+Tf+cW8jcGBTRiFxdYFg== + dependencies: + "@babel/runtime" "^7.11.2" + clsx "^1.0.4" + focus-lock "^0.8.0" + react-merge-refs "^1.0.0" + +"@devtools-ds/object-inspector@^1.1.2": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@devtools-ds/object-inspector/-/object-inspector-1.2.0.tgz#64a132fbd4159affa5a87c8cf6cf8540c337aed2" + integrity sha512-VztcwqVwScSvYdvJVZBJYsVO/2Pew3JPpFV3T9fuCHQLlHcLYOV3aU/kBS2ScuE2O1JN0ZbobLqFLa3vQF54Fw== + dependencies: + "@babel/runtime" "7.7.2" + "@devtools-ds/object-parser" "^1.2.0" + "@devtools-ds/themes" "^1.2.0" + "@devtools-ds/tree" "^1.2.0" + clsx "1.1.0" + +"@devtools-ds/object-parser@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@devtools-ds/object-parser/-/object-parser-1.2.0.tgz#8da39bf481687afdf113c78dbac5ced6fd8e30d1" + integrity sha512-SjGGyiFFY8dtUpiWXAvRSzRT+hE11EAAysrq2PsC/GVLf2ZLyT2nHlQO5kDStywyTz+fjw7S7pyDRj1HG9YTTA== + dependencies: + "@babel/runtime" "~7.5.4" + +"@devtools-ds/themes@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@devtools-ds/themes/-/themes-1.2.0.tgz#2fda60af9741e97bc09257b512e49a7aecf6f4bc" + integrity sha512-LimEITorE6yWZWWuMc6OiBfLQgPrQqWbyMEmfRUDPa3PHXoAY4SpDxczfg31fgyRDUNWnZhjaJH5bBbu8VEbIw== + dependencies: + "@babel/runtime" "~7.5.4" + "@design-systems/utils" "2.12.0" + clsx "1.1.0" + +"@devtools-ds/tree@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@devtools-ds/tree/-/tree-1.2.0.tgz#e882d10ae13a30f2aa02e75c3eeb6c44a47a80c3" + integrity sha512-hC4g4ocuo2eg7jsnzKdauxH0sDQiPW3KSM2+uK3kRgcmr9PzpBD5Kob+Y/WFSVKswFleftOGKL4BQLuRv0sPxA== + dependencies: + "@babel/runtime" "7.7.2" + "@devtools-ds/themes" "^1.2.0" + clsx "1.1.0" + +"@discoveryjs/json-ext@^0.5.0", "@discoveryjs/json-ext@^0.5.3": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@emotion/babel-plugin-jsx-pragmatic@^0.1.5": + version "0.1.5" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin-jsx-pragmatic/-/babel-plugin-jsx-pragmatic-0.1.5.tgz#27debfe9c27c4d83574d509787ae553bf8a34d7e" + integrity sha512-y+3AJ0SItMDaAgGPVkQBC/S/BaqaPACkQ6MyCI2CUlrjTxKttTVfD3TMtcs7vLEcLxqzZ1xiG0vzwCXjhopawQ== + dependencies: + "@babel/plugin-syntax-jsx" "^7.2.0" + +"@emotion/babel-plugin@^11.2.0", "@emotion/babel-plugin@^11.7.1", "@emotion/babel-plugin@^11.9.2": + version "11.9.2" + resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.9.2.tgz#723b6d394c89fb2ef782229d92ba95a740576e95" + integrity sha512-Pr/7HGH6H6yKgnVFNEj2MVlreu3ADqftqjqwUvDy/OJzKFgxKeTQ+eeUf20FOTuHVkDON2iNa25rAXVYtWJCjw== + dependencies: + "@babel/helper-module-imports" "^7.12.13" + "@babel/plugin-syntax-jsx" "^7.12.13" + "@babel/runtime" "^7.13.10" + "@emotion/hash" "^0.8.0" + "@emotion/memoize" "^0.7.5" + "@emotion/serialize" "^1.0.2" + babel-plugin-macros "^2.6.1" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.0.13" + +"@emotion/babel-preset-css-prop@^11.2.0": + version "11.2.0" + resolved "https://registry.yarnpkg.com/@emotion/babel-preset-css-prop/-/babel-preset-css-prop-11.2.0.tgz#c7e945f56b2610b438f0dc8ae5253fc55488de0e" + integrity sha512-9XLQm2eLPYTho+Cx1LQTDA1rATjoAaB4O+ds55XDvoAa+Z16Hhg8y5Vihj3C8E6+ilDM8SV5A9Z6z+yj0YIRBg== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.12.1" + "@babel/runtime" "^7.7.2" + "@emotion/babel-plugin" "^11.2.0" + "@emotion/babel-plugin-jsx-pragmatic" "^0.1.5" + +"@emotion/cache@^11.9.3": + version "11.9.3" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.9.3.tgz#96638449f6929fd18062cfe04d79b29b44c0d6cb" + integrity sha512-0dgkI/JKlCXa+lEXviaMtGBL0ynpx4osh7rjOXE71q9bIF8G+XhJgvi+wDu0B0IdCVx37BffiwXlN9I3UuzFvg== + dependencies: + "@emotion/memoize" "^0.7.4" + "@emotion/sheet" "^1.1.1" + "@emotion/utils" "^1.0.0" + "@emotion/weak-memoize" "^0.2.5" + stylis "4.0.13" + +"@emotion/hash@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" + integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== + +"@emotion/is-prop-valid@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.1.3.tgz#f0907a416368cf8df9e410117068e20fe87c0a3a" + integrity sha512-RFg04p6C+1uO19uG8N+vqanzKqiM9eeV1LDOG3bmkYmuOj7NbKNlFC/4EZq5gnwAIlcC/jOT24f8Td0iax2SXA== + dependencies: + "@emotion/memoize" "^0.7.4" + +"@emotion/memoize@^0.7.4", "@emotion/memoize@^0.7.5": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50" + integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ== + +"@emotion/react@^11.9.3": + version "11.9.3" + resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.9.3.tgz#f4f4f34444f6654a2e550f5dab4f2d360c101df9" + integrity sha512-g9Q1GcTOlzOEjqwuLF/Zd9LC+4FljjPjDfxSM7KmEakm+hsHXk+bYZ2q+/hTJzr0OUNkujo72pXLQvXj6H+GJQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@emotion/babel-plugin" "^11.7.1" + "@emotion/cache" "^11.9.3" + "@emotion/serialize" "^1.0.4" + "@emotion/utils" "^1.1.0" + "@emotion/weak-memoize" "^0.2.5" + hoist-non-react-statics "^3.3.1" + +"@emotion/serialize@^1.0.2", "@emotion/serialize@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.4.tgz#ff31fd11bb07999611199c2229e152faadc21a3c" + integrity sha512-1JHamSpH8PIfFwAMryO2bNka+y8+KA5yga5Ocf2d7ZEiJjb7xlLW7aknBGZqJLajuLOvJ+72vN+IBSwPlXD1Pg== + dependencies: + "@emotion/hash" "^0.8.0" + "@emotion/memoize" "^0.7.4" + "@emotion/unitless" "^0.7.5" + "@emotion/utils" "^1.0.0" + csstype "^3.0.2" + +"@emotion/sheet@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.1.1.tgz#015756e2a9a3c7c5f11d8ec22966a8dbfbfac787" + integrity sha512-J3YPccVRMiTZxYAY0IOq3kd+hUP8idY8Kz6B/Cyo+JuXq52Ek+zbPbSQUrVQp95aJ+lsAW7DPL1P2Z+U1jGkKA== + +"@emotion/styled@^11.9.3": + version "11.9.3" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.9.3.tgz#47f0c71137fec7c57035bf3659b52fb536792340" + integrity sha512-o3sBNwbtoVz9v7WB1/Y/AmXl69YHmei2mrVnK7JgyBJ//Rst5yqPZCecEJlMlJrFeWHp+ki/54uN265V2pEcXA== + dependencies: + "@babel/runtime" "^7.13.10" + "@emotion/babel-plugin" "^11.7.1" + "@emotion/is-prop-valid" "^1.1.3" + "@emotion/serialize" "^1.0.4" + "@emotion/utils" "^1.1.0" + +"@emotion/unitless@^0.7.5": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" + integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== + +"@emotion/utils@^1.0.0", "@emotion/utils@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.1.0.tgz#86b0b297f3f1a0f2bdb08eeac9a2f49afd40d0cf" + integrity sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ== + +"@emotion/weak-memoize@^0.2.5": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" + integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== + +"@eslint/eslintrc@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" + integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.3.2" + globals "^13.15.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@gar/promisify@^1.0.1": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" + integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== + +"@humanwhocodes/config-array@^0.9.2": + version "0.9.5" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" + integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" + integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2", "@istanbuljs/schema@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" + integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== + +"@jest/console@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df" + integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + slash "^3.0.0" + +"@jest/core@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-28.1.3.tgz#0ebf2bd39840f1233cd5f2d1e6fc8b71bd5a1ac7" + integrity sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA== + dependencies: + "@jest/console" "^28.1.3" + "@jest/reporters" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + ci-info "^3.2.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^28.1.3" + jest-config "^28.1.3" + jest-haste-map "^28.1.3" + jest-message-util "^28.1.3" + jest-regex-util "^28.0.2" + jest-resolve "^28.1.3" + jest-resolve-dependencies "^28.1.3" + jest-runner "^28.1.3" + jest-runtime "^28.1.3" + jest-snapshot "^28.1.3" + jest-util "^28.1.3" + jest-validate "^28.1.3" + jest-watcher "^28.1.3" + micromatch "^4.0.4" + pretty-format "^28.1.3" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-28.1.3.tgz#abed43a6b040a4c24fdcb69eab1f97589b2d663e" + integrity sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA== + dependencies: + "@jest/fake-timers" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + jest-mock "^28.1.3" + +"@jest/expect-utils@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525" + integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA== + dependencies: + jest-get-type "^28.0.2" + +"@jest/expect@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-28.1.3.tgz#9ac57e1d4491baca550f6bdbd232487177ad6a72" + integrity sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw== + dependencies: + expect "^28.1.3" + jest-snapshot "^28.1.3" + +"@jest/fake-timers@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-28.1.3.tgz#230255b3ad0a3d4978f1d06f70685baea91c640e" + integrity sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw== + dependencies: + "@jest/types" "^28.1.3" + "@sinonjs/fake-timers" "^9.1.2" + "@types/node" "*" + jest-message-util "^28.1.3" + jest-mock "^28.1.3" + jest-util "^28.1.3" + +"@jest/globals@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-28.1.3.tgz#a601d78ddc5fdef542728309894895b4a42dc333" + integrity sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/expect" "^28.1.3" + "@jest/types" "^28.1.3" + +"@jest/reporters@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-28.1.3.tgz#9adf6d265edafc5fc4a434cfb31e2df5a67a369a" + integrity sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + "@jridgewell/trace-mapping" "^0.3.13" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + jest-worker "^28.1.3" + slash "^3.0.0" + string-length "^4.0.1" + strip-ansi "^6.0.0" + terminal-link "^2.0.0" + v8-to-istanbul "^9.0.1" + +"@jest/schemas@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" + integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== + dependencies: + "@sinclair/typebox" "^0.24.1" + +"@jest/source-map@^28.1.2": + version "28.1.2" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-28.1.2.tgz#7fe832b172b497d6663cdff6c13b0a920e139e24" + integrity sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww== + dependencies: + "@jridgewell/trace-mapping" "^0.3.13" + callsites "^3.0.0" + graceful-fs "^4.2.9" + +"@jest/test-result@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5" + integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg== + dependencies: + "@jest/console" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz#9d0c283d906ac599c74bde464bc0d7e6a82886c3" + integrity sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw== + dependencies: + "@jest/test-result" "^28.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^28.1.3" + slash "^3.0.0" + +"@jest/transform@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.6.2.tgz#5ac57c5fa1ad17b2aae83e73e45813894dcf2e4b" + integrity sha512-E9JjhUgNzvuQ+vVAL21vlyfy12gP0GhazGgJC4h6qUt1jSdUXGWJ1wfu/X7Sd8etSgxV4ovT1pb9v5D6QW4XgA== + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^26.6.2" + babel-plugin-istanbul "^6.0.0" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.4" + jest-haste-map "^26.6.2" + jest-regex-util "^26.0.0" + jest-util "^26.6.2" + micromatch "^4.0.2" + pirates "^4.0.1" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/transform@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0" + integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA== + dependencies: + "@babel/core" "^7.11.6" + "@jest/types" "^28.1.3" + "@jridgewell/trace-mapping" "^0.3.13" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^28.1.3" + jest-regex-util "^28.0.2" + jest-util "^28.1.3" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + write-file-atomic "^4.0.1" + +"@jest/types@^26.6.2": + version "26.6.2" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" + integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + +"@jest/types@^27.5.1": + version "27.5.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80" + integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + +"@jest/types@^28.1.3": + version "28.1.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b" + integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ== + dependencies: + "@jest/schemas" "^28.1.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.8.tgz#687cc2bbf243f4e9a868ecf2262318e2658873a1" + integrity sha512-YK5G9LaddzGbcucK4c8h5tWFmMPBvRZ/uyWmN1/SbBdIvqGUdWGkJ5BAaccgs6XbzVLsqbPJrBSFwKv3kT9i7w== + +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.14" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" + integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" + integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== + +"@mdx-js/mdx@^1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" + integrity sha512-AMxuLxPz2j5/6TpF/XSdKpQP1NlG0z11dFOlq+2IP/lSgl11GY8ji6S/rgsViN/L0BDvHvUMruRb7ub+24LUYA== + dependencies: + "@babel/core" "7.12.9" + "@babel/plugin-syntax-jsx" "7.12.1" + "@babel/plugin-syntax-object-rest-spread" "7.8.3" + "@mdx-js/util" "1.6.22" + babel-plugin-apply-mdx-type-prop "1.6.22" + babel-plugin-extract-import-names "1.6.22" + camelcase-css "2.0.1" + detab "2.0.4" + hast-util-raw "6.0.1" + lodash.uniq "4.5.0" + mdast-util-to-hast "10.0.1" + remark-footnotes "2.0.0" + remark-mdx "1.6.22" + remark-parse "8.0.3" + remark-squeeze-paragraphs "4.0.0" + style-to-object "0.3.0" + unified "9.2.0" + unist-builder "2.0.3" + unist-util-visit "2.0.3" + +"@mdx-js/react@^1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-1.6.22.tgz#ae09b4744fddc74714ee9f9d6f17a66e77c43573" + integrity sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg== + +"@mdx-js/util@1.6.22": + version "1.6.22" + resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" + integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@mswjs/cookies@^0.2.0": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@mswjs/cookies/-/cookies-0.2.1.tgz#66a283b45970ffc5350d22657983a1377ea6bf97" + integrity sha512-0tDfcPw5/s7QsNQqS3knAvAD5w5PF1nNPagRhKO/yECY+sMbJxoC2sLWnH7Lzmh52mTSVLKDhd1r92Q3kfljnQ== + dependencies: + "@types/set-cookie-parser" "^2.4.0" + set-cookie-parser "^2.4.6" + +"@mswjs/interceptors@^0.16.3": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.16.6.tgz#c1a777ed3f69b55bbbc725b2deb827f160c0107c" + integrity sha512-7ax1sRx5s4ZWl0KvVhhcPOUoPbCCkVh8M8hYaqOyvoAQOiqLVzy+Z6Mh2ywPhYw4zudr5Mo/E8UT/zJBO/Wxrw== + dependencies: + "@open-draft/until" "^1.0.3" + "@xmldom/xmldom" "^0.7.5" + debug "^4.3.3" + headers-polyfill "^3.0.4" + outvariant "^1.2.1" + strict-event-emitter "^0.2.4" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@npmcli/fs@^1.0.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" + integrity sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ== + dependencies: + "@gar/promisify" "^1.0.1" + semver "^7.3.5" + +"@npmcli/move-file@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + +"@open-draft/until@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" + integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q== + +"@pmmmwh/react-refresh-webpack-plugin@^0.5.3": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz#58f8217ba70069cc6a73f5d7e05e85b458c150e2" + integrity sha512-bcKCAzF0DV2IIROp9ZHkRJa6O4jy7NlnHdWL3GmcUxYWNjLXkK5kfELELwEfSP5hXPfVL/qOGMAROuMQb9GG8Q== + dependencies: + ansi-html-community "^0.0.8" + common-path-prefix "^3.0.0" + core-js-pure "^3.8.1" + error-stack-parser "^2.0.6" + find-up "^5.0.0" + html-entities "^2.1.0" + loader-utils "^2.0.0" + schema-utils "^3.0.0" + source-map "^0.7.3" + +"@sinclair/typebox@^0.24.1": + version "0.24.27" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.27.tgz#d55643516a1546174e10da681a8aaa81e757452d" + integrity sha512-K7C7IlQ3zLePEZleUN21ceBA2aLcMnLHTLph8QWk1JK37L90obdpY+QGY8bXMKxf1ht1Z0MNewvXxWv0oGDYFg== + +"@sinonjs/commons@^1.7.0": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" + integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^9.1.2": + version "9.1.2" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" + integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@storybook/addon-actions@6.5.9", "@storybook/addon-actions@^6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-6.5.9.tgz#d50d65631403e1a5b680961429d9c0d7bd383e68" + integrity sha512-wDYm3M1bN+zcYZV3Q24M03b/P8DDpvj1oSoY6VLlxDAi56h8qZB/voeIS2I6vWXOB79C5tbwljYNQO0GsufS0g== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/theming" "6.5.9" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.21" + polished "^4.2.2" + prop-types "^15.7.2" + react-inspector "^5.1.0" + regenerator-runtime "^0.13.7" + telejson "^6.0.8" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + uuid-browser "^3.1.0" + +"@storybook/addon-backgrounds@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-backgrounds/-/addon-backgrounds-6.5.9.tgz#a9579fc9d73f783a768c6c6ceb97193c5a1ee708" + integrity sha512-9k+GiY5aiANLOct34ar29jqgdi5ZpCqpZ86zPH0GsEC6ifH6nzP4trLU0vFUe260XDCvB4g8YaI7JZKPhozERg== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/theming" "6.5.9" + core-js "^3.8.2" + global "^4.4.0" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/addon-controls@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-controls/-/addon-controls-6.5.9.tgz#8f6ef939c87b3dbad98f8bda7e124f0b34f668d2" + integrity sha512-VvjkgK32bGURKyWU2No6Q2B0RQZjLZk8D3neVNCnrWxwrl1G82StegxjRPn/UZm9+MZVPvTvI46nj1VdgOktnw== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/node-logger" "6.5.9" + "@storybook/store" "6.5.9" + "@storybook/theming" "6.5.9" + core-js "^3.8.2" + lodash "^4.17.21" + ts-dedent "^2.0.0" + +"@storybook/addon-docs@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-docs/-/addon-docs-6.5.9.tgz#32b27fb298624afd738c1371a764d7ff4831fe6d" + integrity sha512-9lwOZyiOJFUgGd9ADVfcgpels5o0XOXqGMeVLuzT1160nopbZjNjo/3+YLJ0pyHRPpMJ4rmq2+vxRQR6PVRgPg== + dependencies: + "@babel/plugin-transform-react-jsx" "^7.12.12" + "@babel/preset-env" "^7.12.11" + "@jest/transform" "^26.6.2" + "@mdx-js/react" "^1.6.22" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/docs-tools" "6.5.9" + "@storybook/mdx1-csf" "^0.0.1" + "@storybook/node-logger" "6.5.9" + "@storybook/postinstall" "6.5.9" + "@storybook/preview-web" "6.5.9" + "@storybook/source-loader" "6.5.9" + "@storybook/store" "6.5.9" + "@storybook/theming" "6.5.9" + babel-loader "^8.0.0" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.21" + regenerator-runtime "^0.13.7" + remark-external-links "^8.0.0" + remark-slug "^6.0.0" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/addon-essentials@^6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-essentials/-/addon-essentials-6.5.9.tgz#32ba63acba4d153f4cf6ac33cbbf14b87d260788" + integrity sha512-V9ThjKQsde4A2Es20pLFBsn0MWx2KCJuoTcTsANP4JDcbvEmj8UjbDWbs8jAU+yzJT5r+CI6NoWmQudv12ZOgw== + dependencies: + "@storybook/addon-actions" "6.5.9" + "@storybook/addon-backgrounds" "6.5.9" + "@storybook/addon-controls" "6.5.9" + "@storybook/addon-docs" "6.5.9" + "@storybook/addon-measure" "6.5.9" + "@storybook/addon-outline" "6.5.9" + "@storybook/addon-toolbars" "6.5.9" + "@storybook/addon-viewport" "6.5.9" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/node-logger" "6.5.9" + core-js "^3.8.2" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + +"@storybook/addon-interactions@^6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-interactions/-/addon-interactions-6.5.9.tgz#4356e96beae8f44000955d8870c3acc6c8d1fb3a" + integrity sha512-p3xBbrhmYTHvRO8MqAIr2DucgrXt38nJE71rogLNLsJ01rUN4JsLI8OkQAMQbqfIpwC27irMjQxJTp4HSzkFJA== + dependencies: + "@devtools-ds/object-inspector" "^1.1.2" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/instrumenter" "6.5.9" + "@storybook/theming" "6.5.9" + core-js "^3.8.2" + global "^4.4.0" + jest-mock "^27.0.6" + polished "^4.2.2" + ts-dedent "^2.2.0" + +"@storybook/addon-links@^6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-links/-/addon-links-6.5.9.tgz#91cbca0c044796badf2498723fdd10dacea5748b" + integrity sha512-4BYC7pkxL3NLRnEgTA9jpIkObQKril+XFj1WtmY/lngF90vvK0Kc/TtvTA2/5tSgrHfxEuPevIdxMIyLJ4ejWQ== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/router" "6.5.9" + "@types/qs" "^6.9.5" + core-js "^3.8.2" + global "^4.4.0" + prop-types "^15.7.2" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + +"@storybook/addon-measure@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-measure/-/addon-measure-6.5.9.tgz#f949d4f5f4025c839634114365f1399ea04bd0ae" + integrity sha512-0aA22wD0CIEUccsEbWkckCOXOwr4VffofMH1ToVCOeqBoyLOMB0gxFubESeprqM54CWsYh2DN1uujgD6508cwA== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + core-js "^3.8.2" + global "^4.4.0" + +"@storybook/addon-outline@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-outline/-/addon-outline-6.5.9.tgz#6ce9b3fb77e6a1a59607d7657c359c69f26cf6dd" + integrity sha512-oJ1DK3BDJr6aTlZc9axfOxV1oDkZO7hOohgUQDaKO1RZrSpyQsx2ViK2X6p/W7JhFJHKh7rv+nGCaVlLz3YIZA== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + core-js "^3.8.2" + global "^4.4.0" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + +"@storybook/addon-toolbars@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-toolbars/-/addon-toolbars-6.5.9.tgz#feedfdac08482d43bb1f3cc00840d80322c5eace" + integrity sha512-6JFQNHYVZUwp17p5rppc+iQJ2QOIWPTF+ni1GMMThjc84mzXs2+899Sf1aPFTvrFJTklmT+bPX6x4aUTouVa1w== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/theming" "6.5.9" + core-js "^3.8.2" + regenerator-runtime "^0.13.7" + +"@storybook/addon-viewport@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addon-viewport/-/addon-viewport-6.5.9.tgz#fc390ccebea56d2e874ed2fda085c09fe04dd240" + integrity sha512-thKS+iw6M7ueDQQ7M66STZ5rgtJKliAcIX6UCopo0Ffh4RaRYmX6MCjqtvBKk8joyXUvm9SpWQemJD9uBQrjgw== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/theming" "6.5.9" + core-js "^3.8.2" + global "^4.4.0" + memoizerific "^1.11.3" + prop-types "^15.7.2" + regenerator-runtime "^0.13.7" + +"@storybook/addons@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-6.5.9.tgz#5a9d7395c579a9cbc44dfc122362fb3c95dfb9d5" + integrity sha512-adwdiXg+mntfPocLc1KXjZXyLgGk7Aac699Fwe+OUYPEC5tW347Rm/kFatcE556d42o5czcRiq3ZSIGWnm9ieQ== + dependencies: + "@storybook/api" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/router" "6.5.9" + "@storybook/theming" "6.5.9" + "@types/webpack-env" "^1.16.0" + core-js "^3.8.2" + global "^4.4.0" + regenerator-runtime "^0.13.7" + +"@storybook/api@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/api/-/api-6.5.9.tgz#303733214c9de0422d162f7c54ae05d088b89bf9" + integrity sha512-9ylztnty4Y+ALU/ehW3BML9czjCAFsWvrwuCi6UgcwNjswwjSX3VRLhfD1KT3pl16ho//95LgZ0LnSwROCcPOA== + dependencies: + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/router" "6.5.9" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.5.9" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.21" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + store2 "^2.12.0" + telejson "^6.0.8" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/builder-webpack4@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/builder-webpack4/-/builder-webpack4-6.5.9.tgz#4b37e1fa23a25aa4bfeaba640e5d318fcd511f95" + integrity sha512-YOeA4++9uRZ8Hog1wC60yjaxBOiI1FRQNtax7b9E7g+kP8UlSCPCGcv4gls9hFmzbzTOPfQTWnToA9Oa6jzRVw== + dependencies: + "@babel/core" "^7.12.10" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/node-logger" "6.5.9" + "@storybook/preview-web" "6.5.9" + "@storybook/router" "6.5.9" + "@storybook/semver" "^7.3.2" + "@storybook/store" "6.5.9" + "@storybook/theming" "6.5.9" + "@storybook/ui" "6.5.9" + "@types/node" "^14.0.10 || ^16.0.0" + "@types/webpack" "^4.41.26" + autoprefixer "^9.8.6" + babel-loader "^8.0.0" + case-sensitive-paths-webpack-plugin "^2.3.0" + core-js "^3.8.2" + css-loader "^3.6.0" + file-loader "^6.2.0" + find-up "^5.0.0" + fork-ts-checker-webpack-plugin "^4.1.6" + glob "^7.1.6" + glob-promise "^3.4.0" + global "^4.4.0" + html-webpack-plugin "^4.0.0" + pnp-webpack-plugin "1.6.4" + postcss "^7.0.36" + postcss-flexbugs-fixes "^4.2.1" + postcss-loader "^4.2.0" + raw-loader "^4.0.2" + stable "^0.1.8" + style-loader "^1.3.0" + terser-webpack-plugin "^4.2.3" + ts-dedent "^2.0.0" + url-loader "^4.1.1" + util-deprecate "^1.0.2" + webpack "4" + webpack-dev-middleware "^3.7.3" + webpack-filter-warnings-plugin "^1.2.1" + webpack-hot-middleware "^2.25.1" + webpack-virtual-modules "^0.2.2" + +"@storybook/builder-webpack5@^6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/builder-webpack5/-/builder-webpack5-6.5.9.tgz#30b4e08622daff104bcccd015d3ee7902f99dd99" + integrity sha512-NUVZ4Qci6HWPuoH8U/zQkdBO5soGgu7QYrGC/LWU0tRfmmZxkjr7IUU14ppDpGPYgx3r7jkaQI1J/E1YEmSCWQ== + dependencies: + "@babel/core" "^7.12.10" + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/node-logger" "6.5.9" + "@storybook/preview-web" "6.5.9" + "@storybook/router" "6.5.9" + "@storybook/semver" "^7.3.2" + "@storybook/store" "6.5.9" + "@storybook/theming" "6.5.9" + "@types/node" "^14.0.10 || ^16.0.0" + babel-loader "^8.0.0" + babel-plugin-named-exports-order "^0.0.2" + browser-assert "^1.2.1" + case-sensitive-paths-webpack-plugin "^2.3.0" + core-js "^3.8.2" + css-loader "^5.0.1" + fork-ts-checker-webpack-plugin "^6.0.4" + glob "^7.1.6" + glob-promise "^3.4.0" + html-webpack-plugin "^5.0.0" + path-browserify "^1.0.1" + process "^0.11.10" + stable "^0.1.8" + style-loader "^2.0.0" + terser-webpack-plugin "^5.0.3" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + webpack "^5.9.0" + webpack-dev-middleware "^4.1.0" + webpack-hot-middleware "^2.25.1" + webpack-virtual-modules "^0.4.1" + +"@storybook/channel-postmessage@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/channel-postmessage/-/channel-postmessage-6.5.9.tgz#9cf4530f0364cee0d5e58f92d6fb5ce98e10257b" + integrity sha512-pX/0R8UW7ezBhCrafRaL20OvMRcmESYvQQCDgjqSzJyHkcG51GOhsd6Ge93eJ6QvRMm9+w0Zs93N2VKjVtz0Qw== + dependencies: + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + core-js "^3.8.2" + global "^4.4.0" + qs "^6.10.0" + telejson "^6.0.8" + +"@storybook/channel-websocket@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/channel-websocket/-/channel-websocket-6.5.9.tgz#6b7a0127fec58ee5be4f6aebcf460adc564f2f34" + integrity sha512-xtHvSNwuOhkgALwVshKWsoFhDmuvcosdYfxcfFGEiYKXIu46tRS5ZXmpmgEC/0JAVkVoFj5nL8bV7IY5np6oaA== + dependencies: + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + core-js "^3.8.2" + global "^4.4.0" + telejson "^6.0.8" + +"@storybook/channels@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/channels/-/channels-6.5.9.tgz#abfab89a6587a2688e9926d4aafeb11c9d8b2e79" + integrity sha512-FvGA35nV38UPXWOl9ERapFTJaxwSTamQ339s2Ev7E9riyRG+GRkgTWzf5kECJgS1PAYKd/7m/RqKJT9BVv6A5g== + dependencies: + core-js "^3.8.2" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/client-api@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/client-api/-/client-api-6.5.9.tgz#3e4a8ec1d277fd81325c5d959c553161a85fa182" + integrity sha512-pc7JKJoWLesixUKvG2nV36HukUuYoGRyAgD3PpIV7qSBS4JixqZ3VAHFUtqV1UzfOSQTovLSl4a0rIRnpie6gA== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/store" "6.5.9" + "@types/qs" "^6.9.5" + "@types/webpack-env" "^1.16.0" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.21" + memoizerific "^1.11.3" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + store2 "^2.12.0" + synchronous-promise "^2.0.15" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/client-logger@6.5.9", "@storybook/client-logger@^6.4.0": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/client-logger/-/client-logger-6.5.9.tgz#dc1669abe8c45af1cc38f74c6f4b15ff33e63014" + integrity sha512-DOHL6p0uiDd3gV/Sb2FR+Vh6OiPrrf8BrA06uvXWsMRIIvEEvnparxv9EvPg7FlmUX0T3nq7d3juwjx4F8Wbcg== + dependencies: + core-js "^3.8.2" + global "^4.4.0" + +"@storybook/components@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/components/-/components-6.5.9.tgz#97e07ffe11ab76c01ccee380888991bd161f75b2" + integrity sha512-BhfX980O9zn/1J4FNMeDo8ZvL1m5Ml3T4HRpfYmEBnf8oW5b5BeF6S2K2cwFStZRjWqm1feUcwNpZxCBVMkQnQ== + dependencies: + "@storybook/client-logger" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/theming" "6.5.9" + "@types/react-syntax-highlighter" "11.0.5" + core-js "^3.8.2" + memoizerific "^1.11.3" + qs "^6.10.0" + react-syntax-highlighter "^15.4.5" + regenerator-runtime "^0.13.7" + util-deprecate "^1.0.2" + +"@storybook/core-client@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/core-client/-/core-client-6.5.9.tgz#ea6035d1c90d2c68e860e3cf629979491856cd88" + integrity sha512-LY0QbhShowO+PQx3gao3wdVjpKMH1AaSLmuI95FrcjoMmSXGf96jVLKQp9mJRGeHIsAa93EQBYuCihZycM3Kbg== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/channel-websocket" "6.5.9" + "@storybook/client-api" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/preview-web" "6.5.9" + "@storybook/store" "6.5.9" + "@storybook/ui" "6.5.9" + airbnb-js-shims "^2.2.1" + ansi-to-html "^0.6.11" + core-js "^3.8.2" + global "^4.4.0" + lodash "^4.17.21" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + unfetch "^4.2.0" + util-deprecate "^1.0.2" + +"@storybook/core-common@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/core-common/-/core-common-6.5.9.tgz#7ca8258ea2634b1d64695c1e4262f71cc7457989" + integrity sha512-NxOK0mrOCo0TWZ7Npc5HU66EKoRHlrtg18/ZixblLDWQMIqY9XCck8K1kJ8QYpYCHla+aHIsYUArFe2vhlEfZA== + dependencies: + "@babel/core" "^7.12.10" + "@babel/plugin-proposal-class-properties" "^7.12.1" + "@babel/plugin-proposal-decorators" "^7.12.12" + "@babel/plugin-proposal-export-default-from" "^7.12.1" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1" + "@babel/plugin-proposal-object-rest-spread" "^7.12.1" + "@babel/plugin-proposal-optional-chaining" "^7.12.7" + "@babel/plugin-proposal-private-methods" "^7.12.1" + "@babel/plugin-proposal-private-property-in-object" "^7.12.1" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-transform-arrow-functions" "^7.12.1" + "@babel/plugin-transform-block-scoping" "^7.12.12" + "@babel/plugin-transform-classes" "^7.12.1" + "@babel/plugin-transform-destructuring" "^7.12.1" + "@babel/plugin-transform-for-of" "^7.12.1" + "@babel/plugin-transform-parameters" "^7.12.1" + "@babel/plugin-transform-shorthand-properties" "^7.12.1" + "@babel/plugin-transform-spread" "^7.12.1" + "@babel/preset-env" "^7.12.11" + "@babel/preset-react" "^7.12.10" + "@babel/preset-typescript" "^7.12.7" + "@babel/register" "^7.12.1" + "@storybook/node-logger" "6.5.9" + "@storybook/semver" "^7.3.2" + "@types/node" "^14.0.10 || ^16.0.0" + "@types/pretty-hrtime" "^1.0.0" + babel-loader "^8.0.0" + babel-plugin-macros "^3.0.1" + babel-plugin-polyfill-corejs3 "^0.1.0" + chalk "^4.1.0" + core-js "^3.8.2" + express "^4.17.1" + file-system-cache "^1.0.5" + find-up "^5.0.0" + fork-ts-checker-webpack-plugin "^6.0.4" + fs-extra "^9.0.1" + glob "^7.1.6" + handlebars "^4.7.7" + interpret "^2.2.0" + json5 "^2.1.3" + lazy-universal-dotenv "^3.0.1" + picomatch "^2.3.0" + pkg-dir "^5.0.0" + pretty-hrtime "^1.0.3" + resolve-from "^5.0.0" + slash "^3.0.0" + telejson "^6.0.8" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + webpack "4" + +"@storybook/core-events@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/core-events/-/core-events-6.5.9.tgz#5b0783c7d22a586c0f5e927a61fe1b1223e19637" + integrity sha512-tXt7a3ZvJOCeEKpNa/B5rQM5VI7UJLlOh3IHOImWn4HqoBRrZvbourmac+PRZAtXpos0h3c6554Hjapj/Sny5Q== + dependencies: + core-js "^3.8.2" + +"@storybook/core-server@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/core-server/-/core-server-6.5.9.tgz#749a881c1a81d7cf1a69f3782c06a7f0c39a505c" + integrity sha512-YeePGUrd5fQPvGzMhowh124KrcZURFpFXg1VB0Op3ESqCIsInoMZeObci4Gc+binMXC7vcv7aw3EwSLU37qJzQ== + dependencies: + "@discoveryjs/json-ext" "^0.5.3" + "@storybook/builder-webpack4" "6.5.9" + "@storybook/core-client" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/csf-tools" "6.5.9" + "@storybook/manager-webpack4" "6.5.9" + "@storybook/node-logger" "6.5.9" + "@storybook/semver" "^7.3.2" + "@storybook/store" "6.5.9" + "@storybook/telemetry" "6.5.9" + "@types/node" "^14.0.10 || ^16.0.0" + "@types/node-fetch" "^2.5.7" + "@types/pretty-hrtime" "^1.0.0" + "@types/webpack" "^4.41.26" + better-opn "^2.1.1" + boxen "^5.1.2" + chalk "^4.1.0" + cli-table3 "^0.6.1" + commander "^6.2.1" + compression "^1.7.4" + core-js "^3.8.2" + cpy "^8.1.2" + detect-port "^1.3.0" + express "^4.17.1" + fs-extra "^9.0.1" + global "^4.4.0" + globby "^11.0.2" + ip "^2.0.0" + lodash "^4.17.21" + node-fetch "^2.6.7" + open "^8.4.0" + pretty-hrtime "^1.0.3" + prompts "^2.4.0" + regenerator-runtime "^0.13.7" + serve-favicon "^2.5.0" + slash "^3.0.0" + telejson "^6.0.8" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + watchpack "^2.2.0" + webpack "4" + ws "^8.2.3" + x-default-browser "^0.4.0" + +"@storybook/core@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/core/-/core-6.5.9.tgz#da4f237391d99aed1228323f24b335cafbdf3499" + integrity sha512-Mt3TTQnjQt2/pa60A+bqDsAOrYpohapdtt4DDZEbS8h0V6u11KyYYh3w7FCySlL+sPEyogj63l5Ec76Jah3l2w== + dependencies: + "@storybook/core-client" "6.5.9" + "@storybook/core-server" "6.5.9" + +"@storybook/csf-tools@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/csf-tools/-/csf-tools-6.5.9.tgz#8e01df2305b53e228229f0b45ada3720e6e42a1c" + integrity sha512-RAdhsO2XmEDyWy0qNQvdKMLeIZAuyfD+tYlUwBHRU6DbByDucvwgMOGy5dF97YNJFmyo93EUYJzXjUrJs3U1LQ== + dependencies: + "@babel/core" "^7.12.10" + "@babel/generator" "^7.12.11" + "@babel/parser" "^7.12.11" + "@babel/plugin-transform-react-jsx" "^7.12.12" + "@babel/preset-env" "^7.12.11" + "@babel/traverse" "^7.12.11" + "@babel/types" "^7.12.11" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/mdx1-csf" "^0.0.1" + core-js "^3.8.2" + fs-extra "^9.0.1" + global "^4.4.0" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + +"@storybook/csf@0.0.2--canary.4566f4d.1": + version "0.0.2--canary.4566f4d.1" + resolved "https://registry.yarnpkg.com/@storybook/csf/-/csf-0.0.2--canary.4566f4d.1.tgz#dac52a21c40ef198554e71fe4d20d61e17f65327" + integrity sha512-9OVvMVh3t9znYZwb0Svf/YQoxX2gVOeQTGe2bses2yj+a3+OJnCrUF3/hGv6Em7KujtOdL2LL+JnG49oMVGFgQ== + dependencies: + lodash "^4.17.15" + +"@storybook/csf@0.0.2--canary.87bc651.0": + version "0.0.2--canary.87bc651.0" + resolved "https://registry.yarnpkg.com/@storybook/csf/-/csf-0.0.2--canary.87bc651.0.tgz#c7b99b3a344117ef67b10137b6477a3d2750cf44" + integrity sha512-ajk1Uxa+rBpFQHKrCcTmJyQBXZ5slfwHVEaKlkuFaW77it8RgbPJp/ccna3sgoi8oZ7FkkOyvv1Ve4SmwFqRqw== + dependencies: + lodash "^4.17.15" + +"@storybook/docs-tools@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/docs-tools/-/docs-tools-6.5.9.tgz#5ff304f881e972ce14923a5ffcfed3f052094889" + integrity sha512-UoTaXLvec8x+q+4oYIk/t8DBju9C3ZTGklqOxDIt+0kS3TFAqEgI3JhKXqQOXgN5zDcvLVSxi8dbVAeSxk2ktA== + dependencies: + "@babel/core" "^7.12.10" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/store" "6.5.9" + core-js "^3.8.2" + doctrine "^3.0.0" + lodash "^4.17.21" + regenerator-runtime "^0.13.7" + +"@storybook/instrumenter@6.5.9", "@storybook/instrumenter@^6.4.0": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/instrumenter/-/instrumenter-6.5.9.tgz#885d9dec31b7b7fa6ea29b446105480450e527b8" + integrity sha512-I2nu/6H0MAy8d+d3LY/G6oYEFyWlc8f2Qs2DhpYh5FiCgIpzvY0DMN05Lf8oaXdKHL3lPF/YLJH17FttekXs1w== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + core-js "^3.8.2" + global "^4.4.0" + +"@storybook/manager-webpack4@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/manager-webpack4/-/manager-webpack4-6.5.9.tgz#c75d2cced4550c8a786f00b0e57b203d613e706c" + integrity sha512-49LZlHqWc7zj9tQfOOANixPYmLxqWTTZceA6DSXnKd9xDiO2Gl23Y+l/CSPXNZGDB8QFAwpimwqyKJj/NLH45A== + dependencies: + "@babel/core" "^7.12.10" + "@babel/plugin-transform-template-literals" "^7.12.1" + "@babel/preset-react" "^7.12.10" + "@storybook/addons" "6.5.9" + "@storybook/core-client" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/node-logger" "6.5.9" + "@storybook/theming" "6.5.9" + "@storybook/ui" "6.5.9" + "@types/node" "^14.0.10 || ^16.0.0" + "@types/webpack" "^4.41.26" + babel-loader "^8.0.0" + case-sensitive-paths-webpack-plugin "^2.3.0" + chalk "^4.1.0" + core-js "^3.8.2" + css-loader "^3.6.0" + express "^4.17.1" + file-loader "^6.2.0" + find-up "^5.0.0" + fs-extra "^9.0.1" + html-webpack-plugin "^4.0.0" + node-fetch "^2.6.7" + pnp-webpack-plugin "1.6.4" + read-pkg-up "^7.0.1" + regenerator-runtime "^0.13.7" + resolve-from "^5.0.0" + style-loader "^1.3.0" + telejson "^6.0.8" + terser-webpack-plugin "^4.2.3" + ts-dedent "^2.0.0" + url-loader "^4.1.1" + util-deprecate "^1.0.2" + webpack "4" + webpack-dev-middleware "^3.7.3" + webpack-virtual-modules "^0.2.2" + +"@storybook/manager-webpack5@^6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/manager-webpack5/-/manager-webpack5-6.5.9.tgz#ce9dd6ea6298ab426b111f170c23deea7085ba08" + integrity sha512-J1GamphSsaZLNBEhn1awgxzOS8KfvzrHtVlAm2VHwW7j1E1DItROFJhGCgduYYuBiN9eqm+KIYrxcr6cRuoolQ== + dependencies: + "@babel/core" "^7.12.10" + "@babel/plugin-transform-template-literals" "^7.12.1" + "@babel/preset-react" "^7.12.10" + "@storybook/addons" "6.5.9" + "@storybook/core-client" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/node-logger" "6.5.9" + "@storybook/theming" "6.5.9" + "@storybook/ui" "6.5.9" + "@types/node" "^14.0.10 || ^16.0.0" + babel-loader "^8.0.0" + case-sensitive-paths-webpack-plugin "^2.3.0" + chalk "^4.1.0" + core-js "^3.8.2" + css-loader "^5.0.1" + express "^4.17.1" + find-up "^5.0.0" + fs-extra "^9.0.1" + html-webpack-plugin "^5.0.0" + node-fetch "^2.6.7" + process "^0.11.10" + read-pkg-up "^7.0.1" + regenerator-runtime "^0.13.7" + resolve-from "^5.0.0" + style-loader "^2.0.0" + telejson "^6.0.8" + terser-webpack-plugin "^5.0.3" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + webpack "^5.9.0" + webpack-dev-middleware "^4.1.0" + webpack-virtual-modules "^0.4.1" + +"@storybook/mdx1-csf@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@storybook/mdx1-csf/-/mdx1-csf-0.0.1.tgz#d4184e3f6486fade9f7a6bfaf934d9bc07718d5b" + integrity sha512-4biZIWWzoWlCarMZmTpqcJNgo/RBesYZwGFbQeXiGYsswuvfWARZnW9RE9aUEMZ4XPn7B1N3EKkWcdcWe/K2tg== + dependencies: + "@babel/generator" "^7.12.11" + "@babel/parser" "^7.12.11" + "@babel/preset-env" "^7.12.11" + "@babel/types" "^7.12.11" + "@mdx-js/mdx" "^1.6.22" + "@types/lodash" "^4.14.167" + js-string-escape "^1.0.1" + loader-utils "^2.0.0" + lodash "^4.17.21" + prettier ">=2.2.1 <=2.3.0" + ts-dedent "^2.0.0" + +"@storybook/node-logger@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.5.9.tgz#129cfe0d0f79cab4f6a2ba194d39516680b1626f" + integrity sha512-nZZNZG2Wtwv6Trxi3FrnIqUmB55xO+X/WQGPT5iKlqNjdRIu/T72mE7addcp4rbuWCQfZUhcDDGpBOwKtBxaGg== + dependencies: + "@types/npmlog" "^4.1.2" + chalk "^4.1.0" + core-js "^3.8.2" + npmlog "^5.0.1" + pretty-hrtime "^1.0.3" + +"@storybook/postinstall@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/postinstall/-/postinstall-6.5.9.tgz#a5a2565808e9d7bc310e78c279b09ce337fe3457" + integrity sha512-KQBupK+FMRrtSt8IL0MzCZ/w9qbd25Yxxp/+ajfWgZTRgsWgVFOqcDyMhS16eNbBp5qKIBCBDXfEF+/mK8HwQQ== + dependencies: + core-js "^3.8.2" + +"@storybook/preview-web@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/preview-web/-/preview-web-6.5.9.tgz#557d919e6df50d66259521aa36ebf4055bbd236e" + integrity sha512-4eMrO2HJyZUYyL/j+gUaDvry6iGedshwT5MQqe7J9FaA+Q2pNARQRB1X53f410w7S4sObRmYIAIluWPYdWym9w== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/channel-postmessage" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/store" "6.5.9" + ansi-to-html "^0.6.11" + core-js "^3.8.2" + global "^4.4.0" + lodash "^4.17.21" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + synchronous-promise "^2.0.15" + ts-dedent "^2.0.0" + unfetch "^4.2.0" + util-deprecate "^1.0.2" + +"@storybook/react-docgen-typescript-plugin@1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0": + version "1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0" + resolved "https://registry.yarnpkg.com/@storybook/react-docgen-typescript-plugin/-/react-docgen-typescript-plugin-1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0.tgz#3103532ff494fb7dc3cf835f10740ecf6a26c0f9" + integrity sha512-eVg3BxlOm2P+chijHBTByr90IZVUtgRW56qEOLX7xlww2NBuKrcavBlcmn+HH7GIUktquWkMPtvy6e0W0NgA5w== + dependencies: + debug "^4.1.1" + endent "^2.0.1" + find-cache-dir "^3.3.1" + flat-cache "^3.0.4" + micromatch "^4.0.2" + react-docgen-typescript "^2.1.1" + tslib "^2.0.0" + +"@storybook/react@^6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/react/-/react-6.5.9.tgz#687ec1f6b785822a392b7ac115b61800f69fb7cd" + integrity sha512-Rp+QaTQAzxJhwuzJXVd49mnIBLQRlF8llTxPT2YoGHdrGkku/zl/HblQ6H2yzEf15367VyzaAv/BpLsO9Jlfxg== + dependencies: + "@babel/preset-flow" "^7.12.1" + "@babel/preset-react" "^7.12.10" + "@pmmmwh/react-refresh-webpack-plugin" "^0.5.3" + "@storybook/addons" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core" "6.5.9" + "@storybook/core-common" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + "@storybook/docs-tools" "6.5.9" + "@storybook/node-logger" "6.5.9" + "@storybook/react-docgen-typescript-plugin" "1.0.2-canary.6.9d540b91e815f8fc2f8829189deb00553559ff63.0" + "@storybook/semver" "^7.3.2" + "@storybook/store" "6.5.9" + "@types/estree" "^0.0.51" + "@types/node" "^14.14.20 || ^16.0.0" + "@types/webpack-env" "^1.16.0" + acorn "^7.4.1" + acorn-jsx "^5.3.1" + acorn-walk "^7.2.0" + babel-plugin-add-react-displayname "^0.0.5" + babel-plugin-react-docgen "^4.2.1" + core-js "^3.8.2" + escodegen "^2.0.0" + fs-extra "^9.0.1" + global "^4.4.0" + html-tags "^3.1.0" + lodash "^4.17.21" + prop-types "^15.7.2" + react-element-to-jsx-string "^14.3.4" + react-refresh "^0.11.0" + read-pkg-up "^7.0.1" + regenerator-runtime "^0.13.7" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + webpack ">=4.43.0 <6.0.0" + +"@storybook/router@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/router/-/router-6.5.9.tgz#4740248f8517425b2056273fb366ace8a17c65e8" + integrity sha512-G2Xp/2r8vU2O34eelE+G5VbEEVFDeHcCURrVJEROh6dq2asFJAPbzslVXSeCqgOTNLSpRDJ2NcN5BckkNqmqJg== + dependencies: + "@storybook/client-logger" "6.5.9" + core-js "^3.8.2" + memoizerific "^1.11.3" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + +"@storybook/semver@^7.3.2": + version "7.3.2" + resolved "https://registry.yarnpkg.com/@storybook/semver/-/semver-7.3.2.tgz#f3b9c44a1c9a0b933c04e66d0048fcf2fa10dac0" + integrity sha512-SWeszlsiPsMI0Ps0jVNtH64cI5c0UF3f7KgjVKJoNP30crQ6wUSddY2hsdeczZXEKVJGEn50Q60flcGsQGIcrg== + dependencies: + core-js "^3.6.5" + find-up "^4.1.0" + +"@storybook/source-loader@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/source-loader/-/source-loader-6.5.9.tgz#7b6f065c6a6108c4b4ca7e45bfd78707373d84ac" + integrity sha512-H03nFKaP6borfWMTTa9igBA+Jm2ph+FoVJImWC/X+LAmLSJYYSXuqSgmiZ/DZvbjxS4k8vccE2HXogne1IvaRA== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + core-js "^3.8.2" + estraverse "^5.2.0" + global "^4.4.0" + loader-utils "^2.0.0" + lodash "^4.17.21" + prettier ">=2.2.1 <=2.3.0" + regenerator-runtime "^0.13.7" + +"@storybook/store@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/store/-/store-6.5.9.tgz#dc9963fc013636569082bd8f7200804866373735" + integrity sha512-80pcDTcCwK6wUA63aWOp13urI77jfipIVee9mpVvbNyfrNN8kGv1BS0z/JHDxuV6rC4g7LG1fb+BurR0yki7BA== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/csf" "0.0.2--canary.4566f4d.1" + core-js "^3.8.2" + fast-deep-equal "^3.1.3" + global "^4.4.0" + lodash "^4.17.21" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + slash "^3.0.0" + stable "^0.1.8" + synchronous-promise "^2.0.15" + ts-dedent "^2.0.0" + util-deprecate "^1.0.2" + +"@storybook/telemetry@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/telemetry/-/telemetry-6.5.9.tgz#8e1e0d4a89fc2387620045e5ea96c109d16a7247" + integrity sha512-JluoHCRhHAr4X0eUNVBSBi1JIBA92404Tu1TPdbN7x6gCZxHXXPTSUTAnspXp/21cTdMhY2x+kfZQ8fmlGK4MQ== + dependencies: + "@storybook/client-logger" "6.5.9" + "@storybook/core-common" "6.5.9" + chalk "^4.1.0" + core-js "^3.8.2" + detect-package-manager "^2.0.1" + fetch-retry "^5.0.2" + fs-extra "^9.0.1" + global "^4.4.0" + isomorphic-unfetch "^3.1.0" + nanoid "^3.3.1" + read-pkg-up "^7.0.1" + regenerator-runtime "^0.13.7" + +"@storybook/testing-library@^0.0.13": + version "0.0.13" + resolved "https://registry.yarnpkg.com/@storybook/testing-library/-/testing-library-0.0.13.tgz#417c87d4ea62895092ec5fdf67027ae201254f45" + integrity sha512-vRMeIGer4EjJkTgI8sQyK9W431ekPWYCWL//OmSDJ64IT3h7FnW7Xg6p+eqM3oII98/O5pcya5049GxnjaPtxw== + dependencies: + "@storybook/client-logger" "^6.4.0" + "@storybook/instrumenter" "^6.4.0" + "@testing-library/dom" "^8.3.0" + "@testing-library/user-event" "^13.2.1" + ts-dedent "^2.2.0" + +"@storybook/testing-react@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@storybook/testing-react/-/testing-react-1.3.0.tgz#cd69cae6f48b337ee992520e142c7904cb0bbea7" + integrity sha512-TfxzflxwBHSPhetWKuYt239t+1iN8gnnUN8OKo5UGtwwirghKQlApjH23QXW6j8YBqFhmq+yP29Oqf8HgKCFLw== + dependencies: + "@storybook/csf" "0.0.2--canary.87bc651.0" + +"@storybook/theming@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/theming/-/theming-6.5.9.tgz#13f60a3a3cd73ceb5caf9f188e1627e79f1891aa" + integrity sha512-KM0AMP5jMQPAdaO8tlbFCYqx9uYM/hZXGSVUhznhLYu7bhNAIK7ZVmXxyE/z/khM++8eUHzRoZGiO/cwCkg9Xw== + dependencies: + "@storybook/client-logger" "6.5.9" + core-js "^3.8.2" + memoizerific "^1.11.3" + regenerator-runtime "^0.13.7" + +"@storybook/ui@6.5.9": + version "6.5.9" + resolved "https://registry.yarnpkg.com/@storybook/ui/-/ui-6.5.9.tgz#41e59279323cccc0d613974ec9782d797220c8a7" + integrity sha512-ryuPxJgtbb0gPXKGgGAUC+Z185xGAd1IvQ0jM5fJ0SisHXI8jteG3RaWhntOehi9qCg+64Vv6eH/cj9QYNHt1Q== + dependencies: + "@storybook/addons" "6.5.9" + "@storybook/api" "6.5.9" + "@storybook/channels" "6.5.9" + "@storybook/client-logger" "6.5.9" + "@storybook/components" "6.5.9" + "@storybook/core-events" "6.5.9" + "@storybook/router" "6.5.9" + "@storybook/semver" "^7.3.2" + "@storybook/theming" "6.5.9" + core-js "^3.8.2" + memoizerific "^1.11.3" + qs "^6.10.0" + regenerator-runtime "^0.13.7" + resolve-from "^5.0.0" + +"@testing-library/dom@^8.3.0": + version "8.14.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.14.0.tgz#c9830a21006d87b9ef6e1aae306cf49b0283e28e" + integrity sha512-m8FOdUo77iMTwVRCyzWcqxlEIk+GnopbrRI15a0EaLbpZSCinIVI4kSQzWhkShK83GogvEFJSsHF3Ws0z1vrqA== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^4.2.0" + aria-query "^5.0.0" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.4.4" + pretty-format "^27.0.2" + +"@testing-library/dom@^8.5.0": + version "8.16.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.16.1.tgz#96e528c9752d60061128f043e2b43566c0aba2d9" + integrity sha512-XEV2mBxgv6DKjL3+U3WEUzBgT2CjYksoXGlLrrJXYP8OvRfGkBonvelkorazpFlp8tkEecO06r43vN4DIEyegQ== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^4.2.0" + aria-query "^5.0.0" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.4.4" + pretty-format "^27.0.2" + +"@testing-library/react@^13.3.0": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.3.0.tgz#bf298bfbc5589326bbcc8052b211f3bb097a97c5" + integrity sha512-DB79aA426+deFgGSjnf5grczDPiL4taK3hFaa+M5q7q20Kcve9eQottOG5kZ74KEr55v0tU2CQormSSDK87zYQ== + dependencies: + "@babel/runtime" "^7.12.5" + "@testing-library/dom" "^8.5.0" + "@types/react-dom" "^18.0.0" + +"@testing-library/user-event@^13.2.1": + version "13.5.0" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.5.0.tgz#69d77007f1e124d55314a2b73fd204b333b13295" + integrity sha512-5Kwtbo3Y/NowpkbRuSepbyMFkZmHgD+vPzYB/RJ4oxt5Gj/avFFBYjhw27cqSVPVw/3a67NK1PbiIr9k4Gwmdg== + dependencies: + "@babel/runtime" "^7.12.5" + +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + +"@trivago/prettier-plugin-sort-imports@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-3.2.0.tgz#7a32b6b3085c436eda0143b2710c440a4a56db90" + integrity sha512-DnwLe+z8t/dZX5xBbYZV1+C5STkyK/P6SSq3Nk6NXlJZsgvDZX2eN4ND7bMFgGV/NL/YChWzcNf6ziGba1ktQQ== + dependencies: + "@babel/core" "7.13.10" + "@babel/generator" "7.13.9" + "@babel/parser" "7.14.6" + "@babel/traverse" "7.13.0" + "@babel/types" "7.13.0" + javascript-natural-sort "0.7.1" + lodash "4.17.21" + +"@types/aria-query@^4.2.0": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" + integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== + +"@types/babel__core@^7.1.14": + version "7.1.19" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" + integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" + integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.1" + resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" + integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": + version "7.18.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.0.tgz#8134fd78cb39567465be65b9fdc16d378095f41f" + integrity sha512-v4Vwdko+pgymgS+A2UIaJru93zQd85vIGWObM5ekZNdXCKtDYqATlEYnWgfo86Q6I1Lh0oXnksDnMU1cwmlPDw== + dependencies: + "@babel/types" "^7.3.0" + +"@types/body-parser@*", "@types/body-parser@^1.19.2": + version "1.19.2" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" + integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.9": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275" + integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.3.5": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae" + integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/cookie@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" + integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== + +"@types/eslint-scope@^3.7.3": + version "3.7.4" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" + integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.4.5" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.5.tgz#acdfb7dd36b91cc5d812d7c093811a8f3d9b31e4" + integrity sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "0.0.52" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.52.tgz#7f1f57ad5b741f3d5b210d3b1f145640d89bf8fe" + integrity sha512-BZWrtCU0bMVAIliIV+HJO1f1PR41M7NKjfxrFJwwhKI1KwhwOxYw1SXg9ao+CIMt774nFuGiG6eU+udtbEI9oQ== + +"@types/estree@^0.0.51": + version "0.0.51" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" + integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18": + version "4.17.29" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.29.tgz#2a1795ea8e9e9c91b4a4bbe475034b20c1ec711c" + integrity sha512-uMd++6dMKS32EOuw1Uli3e3BPgdLIXmezcfHv7N4c1s3gkhikBplORPpMq3fuWkxncZN1reb16d5n8yhQ80x7Q== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + +"@types/express@*", "@types/express@^4.17.13": + version "4.17.13" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034" + integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.18" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/glob@*", "@types/glob@^7.1.1": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" + integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== + dependencies: + "@types/minimatch" "*" + "@types/node" "*" + +"@types/graceful-fs@^4.1.2", "@types/graceful-fs@^4.1.3", "@types/graceful-fs@^4.1.5": + version "4.1.5" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" + integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== + dependencies: + "@types/node" "*" + +"@types/hast@^2.0.0": + version "2.3.4" + resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" + integrity sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g== + dependencies: + "@types/unist" "*" + +"@types/html-minifier-terser@^5.0.0": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz#693b316ad323ea97eed6b38ed1a3cc02b1672b57" + integrity sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w== + +"@types/html-minifier-terser@^6.0.0": + version "6.1.0" + resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35" + integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg== + +"@types/http-proxy@^1.17.8": + version "1.17.9" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.9.tgz#7f0e7931343761efde1e2bf48c40f02f3f75705a" + integrity sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw== + dependencies: + "@types/node" "*" + +"@types/is-function@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/is-function/-/is-function-1.0.1.tgz#2d024eace950c836d9e3335a66b97960ae41d022" + integrity sha512-A79HEEiwXTFtfY+Bcbo58M2GRYzCr9itHWzbzHVFNEYCcoU/MMGwYYf721gBrnhpj1s6RGVVha/IgNFnR0Iw/Q== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@^28.1.6": + version "28.1.6" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-28.1.6.tgz#d6a9cdd38967d2d746861fb5be6b120e38284dd4" + integrity sha512-0RbGAFMfcBJKOmqRazM8L98uokwuwD5F8rHrv/ZMbrZBwVOWZUyPG6VFNscjYr/vjM3Vu4fRrCPbOs42AfemaQ== + dependencies: + jest-matcher-utils "^28.0.0" + pretty-format "^28.0.0" + +"@types/js-levenshtein@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/js-levenshtein/-/js-levenshtein-1.1.1.tgz#ba05426a43f9e4e30b631941e0aa17bf0c890ed5" + integrity sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g== + +"@types/jsdom@^16.2.4": + version "16.2.15" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.15.tgz#6c09990ec43b054e49636cba4d11d54367fc90d6" + integrity sha512-nwF87yjBKuX/roqGYerZZM0Nv1pZDMAT5YhOHYeM/72Fic+VEqJh4nyoqoapzJnW3pUlfxPY5FhgsJtM+dRnQQ== + dependencies: + "@types/node" "*" + "@types/parse5" "^6.0.3" + "@types/tough-cookie" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/lodash@^4.14.167": + version "4.14.182" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" + integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== + +"@types/mdast@^3.0.0": + version "3.0.10" + resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" + integrity sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA== + dependencies: + "@types/unist" "*" + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== + +"@types/minimatch@*": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== + +"@types/node-fetch@^2.5.7": + version "2.6.2" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" + integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== + dependencies: + "@types/node" "*" + form-data "^3.0.0" + +"@types/node@*", "@types/node@^18.0.1": + version "18.0.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.1.tgz#e91bd73239b338557a84d1f67f7b9e0f25643870" + integrity sha512-CmR8+Tsy95hhwtZBKJBs0/FFq4XX7sDZHlGGf+0q+BRZfMbOTkzkj0AFAuTyXbObDIoanaBBW0+KEW+m3N16Wg== + +"@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0": + version "16.11.43" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.43.tgz#555e5a743f76b6b897d47f945305b618525ddbe6" + integrity sha512-GqWykok+3uocgfAJM8imbozrqLnPyTrpFlrryURQlw1EesPUCx5XxTiucWDSFF9/NUEXDuD4bnvHm8xfVGWTpQ== + +"@types/normalize-package-data@^2.4.0": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" + integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== + +"@types/npmlog@^4.1.2": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@types/npmlog/-/npmlog-4.1.4.tgz#30eb872153c7ead3e8688c476054ddca004115f6" + integrity sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ== + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" + integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== + +"@types/parse5@^5.0.0": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" + integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== + +"@types/parse5@^6.0.3": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb" + integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g== + +"@types/prettier@^2.1.5": + version "2.7.0" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.0.tgz#ea03e9f0376a4446f44797ca19d9c46c36e352dc" + integrity sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A== + +"@types/pretty-hrtime@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/pretty-hrtime/-/pretty-hrtime-1.0.1.tgz#72a26101dc567b0d68fd956cf42314556e42d601" + integrity sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ== + +"@types/prop-types@*": + version "15.7.5" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" + integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== + +"@types/qs@*", "@types/qs@^6.9.5": + version "6.9.7" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" + integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" + integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== + +"@types/react-dom@^18.0.0": + version "18.0.6" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.6.tgz#36652900024842b74607a17786b6662dd1e103a1" + integrity sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA== + dependencies: + "@types/react" "*" + +"@types/react-dom@^18.0.5": + version "18.0.5" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.5.tgz#330b2d472c22f796e5531446939eacef8378444a" + integrity sha512-OWPWTUrY/NIrjsAPkAk1wW9LZeIjSvkXRhclsFO8CZcZGCOg2G0YZy4ft+rOyYxy8B7ui5iZzi9OkDebZ7/QSA== + dependencies: + "@types/react" "*" + +"@types/react-syntax-highlighter@11.0.5": + version "11.0.5" + resolved "https://registry.yarnpkg.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz#0d546261b4021e1f9d85b50401c0a42acb106087" + integrity sha512-VIOi9i2Oj5XsmWWoB72p3KlZoEbdRAcechJa8Ztebw7bDl2YmR+odxIqhtJGp1q2EozHs02US+gzxJ9nuf56qg== + dependencies: + "@types/react" "*" + +"@types/react@*", "@types/react@^18.0.14": + version "18.0.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.14.tgz#e016616ffff51dba01b04945610fe3671fdbe06d" + integrity sha512-x4gGuASSiWmo0xjDLpm5mPb52syZHJx02VKbqUKdLmKtAwIh63XClGsiTI1K6DO5q7ox4xAsQrU+Gl3+gGXF9Q== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + +"@types/scheduler@*": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" + integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + +"@types/serve-index@^1.9.1": + version "1.9.1" + resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278" + integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg== + dependencies: + "@types/express" "*" + +"@types/serve-static@*", "@types/serve-static@^1.13.10": + version "1.13.10" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9" + integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/set-cookie-parser@^2.4.0": + version "2.4.2" + resolved "https://registry.yarnpkg.com/@types/set-cookie-parser/-/set-cookie-parser-2.4.2.tgz#b6a955219b54151bfebd4521170723df5e13caad" + integrity sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w== + dependencies: + "@types/node" "*" + +"@types/sockjs@^0.3.33": + version "0.3.33" + resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f" + integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== + dependencies: + "@types/node" "*" + +"@types/source-list-map@*": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" + integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== + +"@types/stack-utils@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" + integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== + +"@types/tapable@^1", "@types/tapable@^1.0.5": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" + integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== + +"@types/tough-cookie@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" + integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== + +"@types/uglify-js@*": + version "3.16.0" + resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.16.0.tgz#2cf74a0e6ebb6cd54c0d48e509d5bd91160a9602" + integrity sha512-0yeUr92L3r0GLRnBOvtYK1v2SjqMIqQDHMl7GLb+l2L8+6LSFWEEWEIgVsPdMn5ImLM8qzWT8xFPtQYpp8co0g== + dependencies: + source-map "^0.6.1" + +"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.2", "@types/unist@^2.0.3": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" + integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== + +"@types/webpack-env@^1.16.0": + version "1.17.0" + resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.17.0.tgz#f99ce359f1bfd87da90cc4a57cab0a18f34a48d0" + integrity sha512-eHSaNYEyxRA5IAG0Ym/yCyf86niZUIF/TpWKofQI/CVfh5HsMEUyfE2kwFxha4ow0s5g0LfISQxpDKjbRDrizw== + +"@types/webpack-sources@*": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-3.2.0.tgz#16d759ba096c289034b26553d2df1bf45248d38b" + integrity sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg== + dependencies: + "@types/node" "*" + "@types/source-list-map" "*" + source-map "^0.7.3" + +"@types/webpack@^4.41.26", "@types/webpack@^4.41.8": + version "4.41.32" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.32.tgz#a7bab03b72904070162b2f169415492209e94212" + integrity sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg== + dependencies: + "@types/node" "*" + "@types/tapable" "^1" + "@types/uglify-js" "*" + "@types/webpack-sources" "*" + anymatch "^3.0.0" + source-map "^0.6.0" + +"@types/ws@^8.5.1": + version "8.5.3" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" + integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== + dependencies: + "@types/node" "*" + +"@types/yargs-parser@*": + version "21.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" + integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + +"@types/yargs@^15.0.0": + version "15.0.14" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.14.tgz#26d821ddb89e70492160b66d10a0eb6df8f6fb06" + integrity sha512-yEJzHoxf6SyQGhBhIYGXQDSCkJjB6HohDShto7m8vaKg9Yp0Yn8+71J9eakh2bnPg6BfsH9PRMhiRTZnd4eXGQ== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^16.0.0": + version "16.0.4" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977" + integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^17.0.8": + version "17.0.10" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.10.tgz#591522fce85d8739bca7b8bb90d048e4478d186a" + integrity sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA== + dependencies: + "@types/yargs-parser" "*" + +"@typescript-eslint/eslint-plugin@^5.30.4": + version "5.30.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.4.tgz#a46c8c0ab755a936cb63786a6222876ce51675e4" + integrity sha512-xjujQISAIa4HAaos8fcMZXmqkuZqMx6icdxkI88jMM/eNe4J8AuTLYnLK+zdm0mBYLyctdFf//UE4/xFCcQzYQ== + dependencies: + "@typescript-eslint/scope-manager" "5.30.4" + "@typescript-eslint/type-utils" "5.30.4" + "@typescript-eslint/utils" "5.30.4" + debug "^4.3.4" + functional-red-black-tree "^1.0.1" + ignore "^5.2.0" + regexpp "^3.2.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.30.4": + version "5.30.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.30.4.tgz#659411e8700b22c8d5400798ef24838425bf4567" + integrity sha512-/ge1HtU63wVoED4VnlU2o+FPFmi017bPYpeSrCmd8Ycsti4VSxXrmcpXXm7JpI4GT0Aa7qviabv1PEp6L5bboQ== + dependencies: + "@typescript-eslint/scope-manager" "5.30.4" + "@typescript-eslint/types" "5.30.4" + "@typescript-eslint/typescript-estree" "5.30.4" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.30.4": + version "5.30.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.30.4.tgz#8140efd2bc12d41d74e8af23872a89f3edbe552e" + integrity sha512-DNzlQwGSiGefz71JwaHrpcaAX3zYkEcy8uVuan3YMKOa6qeW/y+7SaD8KIsIAruASwq6P+U4BjWBWtM2O+mwBQ== + dependencies: + "@typescript-eslint/types" "5.30.4" + "@typescript-eslint/visitor-keys" "5.30.4" + +"@typescript-eslint/type-utils@5.30.4": + version "5.30.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.30.4.tgz#00ff19073cd01f7d27e9af49ce08d6a69f1e4f01" + integrity sha512-55cf1dZviwwv+unDB+mF8vZkfta5muTK6bppPvenWWCD7slZZ0DEsXUjZerqy7Rq8s3J4SXdg4rMIY8ngCtTmA== + dependencies: + "@typescript-eslint/utils" "5.30.4" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.30.4": + version "5.30.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.30.4.tgz#3bc99eca8ba3fcfd6a21480e216b09dab81c3999" + integrity sha512-NTEvqc+Vvu8Q6JeAKryHk2eqLKqsr2St3xhIjhOjQv5wQUBhaTuix4WOSacqj0ONWfKVU12Eug3LEAB95GBkMA== + +"@typescript-eslint/typescript-estree@5.30.4": + version "5.30.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.4.tgz#ac4be8a2f8fb1f1c3b346d5992a36163121ddb3f" + integrity sha512-V4VnEs6/J9/nNizaA12IeU4SAeEYaiKr7XndLNfV5+3zZSB4hIu6EhHJixTKhvIqA+EEHgBl6re8pivBMLLO1w== + dependencies: + "@typescript-eslint/types" "5.30.4" + "@typescript-eslint/visitor-keys" "5.30.4" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.30.4": + version "5.30.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.30.4.tgz#07a2b7ce80b2527ea506829f190591b76c70ba9f" + integrity sha512-a+GQrJzOUhn4WT1mUumXDyam+22Oo4c5K/jnZ+6r/4WTQF3q8e4CsC9PLHb4SnOClzOqo/5GLZWvkE1aa5UGKQ== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.30.4" + "@typescript-eslint/types" "5.30.4" + "@typescript-eslint/typescript-estree" "5.30.4" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/visitor-keys@5.30.4": + version "5.30.4" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.4.tgz#b4969df1a440cc999d4bb7f7b7932dce05537089" + integrity sha512-ulKGse3mruSc8x6l8ORSc6+1ORyJzKmZeIaRTu/WpaF/jx3vHvEn5XZUKF9XaVg2710mFmTAUlLcLYLPp/Zf/Q== + dependencies: + "@typescript-eslint/types" "5.30.4" + eslint-visitor-keys "^3.3.0" + +"@webassemblyjs/ast@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" + integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + +"@webassemblyjs/ast@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" + integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== + dependencies: + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + +"@webassemblyjs/floating-point-hex-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f" + integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ== + +"@webassemblyjs/floating-point-hex-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" + integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== + +"@webassemblyjs/helper-api-error@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16" + integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg== + +"@webassemblyjs/helper-api-error@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" + integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== + +"@webassemblyjs/helper-buffer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5" + integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA== + +"@webassemblyjs/helper-buffer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" + integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== + +"@webassemblyjs/helper-code-frame@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" + integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== + dependencies: + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/helper-fsm@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" + integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== + +"@webassemblyjs/helper-module-context@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" + integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== + dependencies: + "@webassemblyjs/ast" "1.9.0" + +"@webassemblyjs/helper-numbers@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae" + integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1" + integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q== + +"@webassemblyjs/helper-wasm-bytecode@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" + integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== + +"@webassemblyjs/helper-wasm-section@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a" + integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + +"@webassemblyjs/helper-wasm-section@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" + integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + +"@webassemblyjs/ieee754@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614" + integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/ieee754@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" + integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5" + integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/leb128@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" + integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff" + integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ== + +"@webassemblyjs/utf8@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" + integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== + +"@webassemblyjs/wasm-edit@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6" + integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/helper-wasm-section" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-opt" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + "@webassemblyjs/wast-printer" "1.11.1" + +"@webassemblyjs/wasm-edit@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" + integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/helper-wasm-section" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-opt" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/wasm-gen@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76" + integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-gen@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" + integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wasm-opt@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2" + integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-buffer" "1.11.1" + "@webassemblyjs/wasm-gen" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + +"@webassemblyjs/wasm-opt@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" + integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + +"@webassemblyjs/wasm-parser@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199" + integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/helper-api-error" "1.11.1" + "@webassemblyjs/helper-wasm-bytecode" "1.11.1" + "@webassemblyjs/ieee754" "1.11.1" + "@webassemblyjs/leb128" "1.11.1" + "@webassemblyjs/utf8" "1.11.1" + +"@webassemblyjs/wasm-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" + integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wast-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" + integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/floating-point-hex-parser" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-code-frame" "1.9.0" + "@webassemblyjs/helper-fsm" "1.9.0" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.11.1": + version "1.11.1" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0" + integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg== + dependencies: + "@webassemblyjs/ast" "1.11.1" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" + integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" + integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== + +"@webpack-cli/info@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" + integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== + dependencies: + envinfo "^7.7.3" + +"@webpack-cli/serve@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" + integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== + +"@xmldom/xmldom@^0.7.5": + version "0.7.5" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.7.5.tgz#09fa51e356d07d0be200642b0e4f91d8e6dd408d" + integrity sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abab@^2.0.5, abab@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-import-assertions@^1.7.6: + version "1.8.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9" + integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw== + +acorn-jsx@^5.3.1, acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^7.1.1, acorn-walk@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn@^6.4.1: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + +acorn@^7.1.1, acorn@^7.4.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1: + version "8.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30" + integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A== + +address@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/address/-/address-1.2.0.tgz#d352a62c92fee90f89a693eccd2a8b2139ab02d9" + integrity sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig== + +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +airbnb-js-shims@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/airbnb-js-shims/-/airbnb-js-shims-2.2.1.tgz#db481102d682b98ed1daa4c5baa697a05ce5c040" + integrity sha512-wJNXPH66U2xjgo1Zwyjf9EydvJ2Si94+vSdk6EERcBfB2VZkeltpqIats0cqIZMLCXP3zcyaUKGYQeIBT6XjsQ== + dependencies: + array-includes "^3.0.3" + array.prototype.flat "^1.2.1" + array.prototype.flatmap "^1.2.1" + es5-shim "^4.5.13" + es6-shim "^0.35.5" + function.prototype.name "^1.1.0" + globalthis "^1.0.0" + object.entries "^1.1.0" + object.fromentries "^2.0.0 || ^1.0.0" + object.getownpropertydescriptors "^2.0.3" + object.values "^1.1.0" + promise.allsettled "^1.0.0" + promise.prototype.finally "^3.1.0" + string.prototype.matchall "^4.0.0 || ^3.0.1" + string.prototype.padend "^3.0.0" + string.prototype.padstart "^3.0.0" + symbol.prototype.description "^1.0.0" + +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv-keywords@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.0, ajv@^8.8.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" + integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + +ansi-colors@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== + +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + +ansi-html-community@0.0.8, ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +ansi-to-html@^0.6.11: + version "0.6.15" + resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.6.15.tgz#ac6ad4798a00f6aa045535d7f6a9cb9294eebea7" + integrity sha512-28ijx2aHJGdzbs+O5SNQF65r6rrKYnkuwTYm8lZlChuoJ9P1vVzIpWO20sQTqTPDXYp6NFwk326vApTtLVFXpQ== + dependencies: + entities "^2.0.0" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +app-root-dir@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118" + integrity sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g== + +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +aria-query@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c" + integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg== + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== + +arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + +array-flatten@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + +array-includes@^3.0.3, array-includes@^3.1.4, array-includes@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" + integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + get-intrinsic "^1.1.1" + is-string "^1.0.7" + +array-union@^1.0.1, array-union@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== + dependencies: + array-uniq "^1.0.1" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== + +array.prototype.flat@^1.2.1, array.prototype.flat@^1.2.5: + version "1.3.0" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" + integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.2" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.2.1, array.prototype.flatmap@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f" + integrity sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.2" + es-shim-unscopables "^1.0.0" + +array.prototype.map@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/array.prototype.map/-/array.prototype.map-1.0.4.tgz#0d97b640cfdd036c1b41cfe706a5e699aa0711f2" + integrity sha512-Qds9QnX7A0qISY7JT5WuJO0NJPE9CMlC6JzHQfhpqAAQQzufVRoeH7EzUY5GcPTx72voG8LV/5eo+b8Qi8hmhA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + +array.prototype.reduce@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f" + integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.2" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + +arrify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +assert@^1.1.1: + version "1.5.0" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" + integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + dependencies: + object-assign "^4.1.1" + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== + +ast-types@^0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.14.2.tgz#600b882df8583e3cd4f2df5fa20fa83759d4bdfd" + integrity sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA== + dependencies: + tslib "^2.0.1" + +async-each@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" + integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" + integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== + +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + +autoprefixer@^9.8.6: + version "9.8.8" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.8.tgz#fd4bd4595385fa6f06599de749a4d5f7a474957a" + integrity sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA== + dependencies: + browserslist "^4.12.0" + caniuse-lite "^1.0.30001109" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + picocolors "^0.2.1" + postcss "^7.0.32" + postcss-value-parser "^4.1.0" + +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + +babel-jest@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-28.1.3.tgz#c1187258197c099072156a0a121c11ee1e3917d5" + integrity sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q== + dependencies: + "@jest/transform" "^28.1.3" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^28.1.3" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-loader@^8.0.0, babel-loader@^8.2.5: + version "8.2.5" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.5.tgz#d45f585e654d5a5d90f5350a779d7647c5ed512e" + integrity sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ== + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^2.0.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" + +babel-plugin-add-react-displayname@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/babel-plugin-add-react-displayname/-/babel-plugin-add-react-displayname-0.0.5.tgz#339d4cddb7b65fd62d1df9db9fe04de134122bd5" + integrity sha512-LY3+Y0XVDYcShHHorshrDbt4KFWL4bSeniCtl4SYZbask+Syngk1uMPCeN9+nSiZo6zX5s0RTq/J9Pnaaf/KHw== + +babel-plugin-apply-mdx-type-prop@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-apply-mdx-type-prop/-/babel-plugin-apply-mdx-type-prop-1.6.22.tgz#d216e8fd0de91de3f1478ef3231e05446bc8705b" + integrity sha512-VefL+8o+F/DfK24lPZMtJctrCVOfgbqLAGZSkxwhazQv4VxPg3Za/i40fu22KR2m8eEda+IfSOlPLUSIiLcnCQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + "@mdx-js/util" "1.6.22" + +babel-plugin-dynamic-import-node@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" + integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== + dependencies: + object.assign "^4.1.0" + +babel-plugin-extract-import-names@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/babel-plugin-extract-import-names/-/babel-plugin-extract-import-names-1.6.22.tgz#de5f9a28eb12f3eb2578bf74472204e66d1a13dc" + integrity sha512-yJ9BsJaISua7d8zNT7oRG1ZLBJCIdZ4PZqmH8qa9N5AK01ifk3fnkc98AXhtzE7UkfCsEumvoQWgoYLhOnJ7jQ== + dependencies: + "@babel/helper-plugin-utils" "7.10.4" + +babel-plugin-istanbul@^6.0.0, babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" + integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz#1952c4d0ea50f2d6d794353762278d1d8cca3fbe" + integrity sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q== + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.1.14" + "@types/babel__traverse" "^7.0.6" + +babel-plugin-macros@^2.6.1: + version "2.8.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" + integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== + dependencies: + "@babel/runtime" "^7.7.2" + cosmiconfig "^6.0.0" + resolve "^1.12.0" + +babel-plugin-macros@^3.0.1: + version "3.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1" + integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg== + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + +babel-plugin-named-exports-order@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/babel-plugin-named-exports-order/-/babel-plugin-named-exports-order-0.0.2.tgz#ae14909521cf9606094a2048239d69847540cb09" + integrity sha512-OgOYHOLoRK+/mvXU9imKHlG6GkPLYrUCvFXG/CM93R/aNNO8pOOF4aS+S8CCHMDQoNSeiOYEZb/G6RwL95Jktw== + +babel-plugin-polyfill-corejs2@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5" + integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w== + dependencies: + "@babel/compat-data" "^7.13.11" + "@babel/helper-define-polyfill-provider" "^0.3.1" + semver "^6.1.1" + +babel-plugin-polyfill-corejs3@^0.1.0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.1.7.tgz#80449d9d6f2274912e05d9e182b54816904befd0" + integrity sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.1.5" + core-js-compat "^3.8.1" + +babel-plugin-polyfill-corejs3@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" + integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.1" + core-js-compat "^3.21.0" + +babel-plugin-polyfill-regenerator@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" + integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.1" + +babel-plugin-react-docgen@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/babel-plugin-react-docgen/-/babel-plugin-react-docgen-4.2.1.tgz#7cc8e2f94e8dc057a06e953162f0810e4e72257b" + integrity sha512-UQ0NmGHj/HAqi5Bew8WvNfCk8wSsmdgNd8ZdMjBCICtyCJCq9LiqgqvjCYe570/Wg7AQArSq1VQ60Dd/CHN7mQ== + dependencies: + ast-types "^0.14.2" + lodash "^4.17.15" + react-docgen "^5.0.0" + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" + integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz#5dfc20b99abed5db994406c2b9ab94c73aaa419d" + integrity sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A== + dependencies: + babel-plugin-jest-hoist "^28.1.3" + babel-preset-current-node-syntax "^1.0.0" + +bail@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.5.tgz#b6fa133404a392cbc1f8c4bf63f5953351e7a776" + integrity sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-js@^1.0.2, base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + +better-opn@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/better-opn/-/better-opn-2.1.1.tgz#94a55b4695dc79288f31d7d0e5f658320759f7c6" + integrity sha512-kIPXZS5qwyKiX/HcRvDYfmBQUa8XP17I0mYZZ0y4UhpYOSvtsLHDYqmomS+Mj20aDvD3knEiQ0ecQy2nhio3yA== + dependencies: + open "^7.0.3" + +big-integer@^1.6.16, big-integer@^1.6.7: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + +bluebird@^3.5.5: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + +bn.js@^5.0.0, bn.js@^5.1.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + +body-parser@1.20.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" + integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.10.3" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.0.11: + version "1.0.13" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.13.tgz#4ac003dc1626023252d58adf2946f57e5da450c1" + integrity sha512-LWKRU/7EqDUC9CTAQtuZl5HzBALoCYwtLhffW3et7vZMwv3bWLpJf8bRYlMD5OCcDpTfnPgNCV4yo9ZIaJGMiA== + dependencies: + array-flatten "^2.1.2" + dns-equal "^1.0.0" + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +boxen@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + +bplist-parser@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/bplist-parser/-/bplist-parser-0.1.1.tgz#d60d5dcc20cba6dc7e1f299b35d3e1f95dafbae6" + integrity sha512-2AEM0FXy8ZxVLBuqX0hqt1gDwcnz2zygEkQ6zaD5Wko/sB9paUNwlpawrFtKeHUAQUOzjVy9AO4oeonqIHKA9Q== + dependencies: + big-integer "^1.6.7" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^2.3.1, braces@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +broadcast-channel@^3.4.1: + version "3.7.0" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937" + integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg== + dependencies: + "@babel/runtime" "^7.7.2" + detect-node "^2.1.0" + js-sha3 "0.8.0" + microseconds "0.2.0" + nano-time "1.0.0" + oblivious-set "1.0.0" + rimraf "3.0.2" + unload "2.2.0" + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + +browser-assert@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/browser-assert/-/browser-assert-1.2.1.tgz#9aaa5a2a8c74685c2ae05bfe46efd606f068c200" + integrity sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ== + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.20.2, browserslist@^4.21.0: + version "4.21.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.1.tgz#c9b9b0a54c7607e8dc3e01a0d311727188011a00" + integrity sha512-Nq8MFCSrnJXSc88yliwlzQe3qNe3VntIjhsArW9IJOEPSHNx23FalwApUVbzAWABLhYJJ7y8AynWI/XM8OdfjQ== + dependencies: + caniuse-lite "^1.0.30001359" + electron-to-chromium "^1.4.172" + node-releases "^2.0.5" + update-browserslist-db "^1.0.4" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" + integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== + +buffer@^4.3.0: + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +c8@^7.6.0: + version "7.11.3" + resolved "https://registry.yarnpkg.com/c8/-/c8-7.11.3.tgz#88c8459c1952ed4f701b619493c9ae732b057163" + integrity sha512-6YBmsaNmqRm9OS3ZbIiL2EZgi1+Xc4O24jL3vMYGE6idixYuGdy76rIfIdltSKDj9DpLNrcXSonUTR1miBD0wA== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@istanbuljs/schema" "^0.1.3" + find-up "^5.0.0" + foreground-child "^2.0.0" + istanbul-lib-coverage "^3.2.0" + istanbul-lib-report "^3.0.0" + istanbul-reports "^3.1.4" + rimraf "^3.0.2" + test-exclude "^6.0.0" + v8-to-istanbul "^9.0.0" + yargs "^16.2.0" + yargs-parser "^20.2.9" + +cacache@^12.0.2: + version "12.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" + integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== + dependencies: + bluebird "^3.5.5" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.4" + graceful-fs "^4.1.15" + infer-owner "^1.0.3" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.3" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" + +cacache@^15.0.5: + version "15.3.0" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" + integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== + dependencies: + "@npmcli/fs" "^1.0.0" + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.0.2" + unique-filename "^1.1.1" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw== + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@^4.1.1, camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camelcase-css@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" + integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== + +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ== + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw== + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001359: + version "1.0.30001363" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001363.tgz#26bec2d606924ba318235944e1193304ea7c4f15" + integrity sha512-HpQhpzTGGPVMnCjIomjt+jvyUu8vNFo3TaDiZ/RcoTrlOq/5+tC8zHdsbgFB6MxmaY+jCpsH09aD80Bb4Ow3Sg== + +capture-exit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" + integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== + dependencies: + rsvp "^4.8.4" + +case-sensitive-paths-webpack-plugin@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" + integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw== + +ccount@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.1.0.tgz#246687debb6014735131be8abab2d93898f8d043" + integrity sha512-vlNK021QdI7PNeiUh/lKkC/mNHHfV0m/Ad5JoI0TYtlBnJAslM/JIkm/tGC88bkLIwO6OQ5uV6ztS6kVAtCDlg== + +chalk@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^2.0.0, chalk@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + +character-entities-legacy@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz#94bc1845dce70a5bb9d2ecc748725661293d8fc1" + integrity sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA== + +character-entities@^1.0.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.4.tgz#e12c3939b7eaf4e5b15e7ad4c5e28e1d48c5b16b" + integrity sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw== + +character-reference-invalid@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz#083329cda0eae272ab3dbbf37e9a382c13af1560" + integrity sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg== + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + +chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chokidar@^3.4.1, chokidar@^3.4.2, chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chownr@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" + integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== + +ci-info@^3.2.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128" + integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +cjs-module-lexer@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" + integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +clean-css@^4.2.3: + version "4.2.4" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" + integrity sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A== + dependencies: + source-map "~0.6.0" + +clean-css@^5.2.2: + version "5.3.0" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.0.tgz#ad3d8238d5f3549e83d5f87205189494bc7cbb59" + integrity sha512-YYuuxv4H/iNb1Z/5IbMRoxgrzjWGhOEFfd+groZ5dMCVkpENiMZmwspdrzBo9286JjM1gZJPAyL7ZIdzuvu2AQ== + dependencies: + source-map "~0.6.0" + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +clean-webpack-plugin@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-4.0.0.tgz#72947d4403d452f38ed61a9ff0ada8122aacd729" + integrity sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w== + dependencies: + del "^4.1.1" + +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" + integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== + +cli-table3@^0.6.1: + version "0.6.2" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.2.tgz#aaf5df9d8b5bf12634dc8b3040806a0c07120d2a" + integrity sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + +clsx@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.0.tgz#62937c6adfea771247c34b54d320fb99624f5702" + integrity sha512-3avwM37fSK5oP6M5rQ9CNe99lwxhXDOeSWVPAOYF6OazUTgZCMb0yWlJpmdD74REy1gkEaFiub2ULv4fq9GUhA== + +clsx@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.0.tgz#b0e415ea7537dbac01b169c5cec1caeb11d86566" + integrity sha512-EPRP7XJsM1y0iCU3Z7C7jFKdQboXSeHgEfzQUTlz7m5NP3hDrlz48aUsmNGp4pC+JOW9WA3vIRqlYuo/bl4Drw== + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + +collapse-white-space@^1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" + integrity sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ== + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +colorette@^1.2.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" + integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== + +colorette@^2.0.10, colorette@^2.0.14: + version "2.0.19" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" + integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +comma-separated-tokens@^1.0.0: + version "1.0.8" + resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" + integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== + +commander@^2.19.0, commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +commander@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" + integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== + +commander@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + +commander@^7.0.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + +common-path-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0" + integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + +component-emitter@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +concat-stream@^1.5.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + +console-browserify@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" + integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== + +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ== + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + +convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" + integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== + dependencies: + safe-buffer "~5.1.1" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + +cookie@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== + +core-js-compat@^3.21.0, core-js-compat@^3.22.1, core-js-compat@^3.8.1: + version "3.23.3" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.23.3.tgz#7d8503185be76bb6d8d592c291a4457a8e440aa9" + integrity sha512-WSzUs2h2vvmKsacLHNTdpyOC9k43AEhcGoFlVgCY4L7aw98oSBKtPL6vD0/TqZjRWRQYdDSLkzZIni4Crbbiqw== + dependencies: + browserslist "^4.21.0" + semver "7.0.0" + +core-js-pure@^3.8.1: + version "3.23.3" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.3.tgz#bcd02d3d8ec68ad871ef50d5ccbb248ddb54f401" + integrity sha512-XpoouuqIj4P+GWtdyV8ZO3/u4KftkeDVMfvp+308eGMhCrA3lVDSmAxO0c6GGOcmgVlaKDrgWVMo49h2ab/TDA== + +core-js@^3.0.4, core-js@^3.6.5, core-js@^3.8.2: + version "3.23.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.23.3.tgz#3b977612b15da6da0c9cc4aec487e8d24f371112" + integrity sha512-oAKwkj9xcWNBAvGbT//WiCdOMpb9XQG92/Fe3ABFM/R16BsHgePG00mFOgKf7IsCtfj8tA1kHtf/VwErhriz5Q== + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cosmiconfig@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" + integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.7.2" + +cosmiconfig@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cp-file@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/cp-file/-/cp-file-7.0.0.tgz#b9454cfd07fe3b974ab9ea0e5f29655791a9b8cd" + integrity sha512-0Cbj7gyvFVApzpK/uhCtQ/9kE9UnYpxMzaq5nQQC/Dh4iaj5fxp7iEFIullrYwzj8nf0qnsI1Qsx34hAeAebvw== + dependencies: + graceful-fs "^4.1.2" + make-dir "^3.0.0" + nested-error-stacks "^2.0.0" + p-event "^4.1.0" + +cpy@^8.1.2: + version "8.1.2" + resolved "https://registry.yarnpkg.com/cpy/-/cpy-8.1.2.tgz#e339ea54797ad23f8e3919a5cffd37bfc3f25935" + integrity sha512-dmC4mUesv0OYH2kNFEidtf/skUwv4zePmGeepjyyJ0qTo5+8KhA1o99oIAwVVLzQMAeDJml74d6wPPKb6EZUTg== + dependencies: + arrify "^2.0.1" + cp-file "^7.0.0" + globby "^9.2.0" + has-glob "^1.0.0" + junk "^3.1.0" + nested-error-stacks "^2.1.0" + p-all "^2.1.0" + p-filter "^2.1.0" + p-map "^3.0.0" + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-spawn@^6.0.0: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +css-loader@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-3.6.0.tgz#2e4b2c7e6e2d27f8c8f28f61bffcd2e6c91ef645" + integrity sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ== + dependencies: + camelcase "^5.3.1" + cssesc "^3.0.0" + icss-utils "^4.1.1" + loader-utils "^1.2.3" + normalize-path "^3.0.0" + postcss "^7.0.32" + postcss-modules-extract-imports "^2.0.0" + postcss-modules-local-by-default "^3.0.2" + postcss-modules-scope "^2.2.0" + postcss-modules-values "^3.0.0" + postcss-value-parser "^4.1.0" + schema-utils "^2.7.0" + semver "^6.3.0" + +css-loader@^5.0.1: + version "5.2.7" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae" + integrity sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg== + dependencies: + icss-utils "^5.1.0" + loader-utils "^2.0.0" + postcss "^8.2.15" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.1.0" + schema-utils "^3.0.0" + semver "^7.3.5" + +css-loader@^6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.1.tgz#e98106f154f6e1baf3fc3bc455cb9981c1d5fd2e" + integrity sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw== + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.7" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.0" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.3.5" + +css-select@^4.1.3: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-what@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +csstype@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" + integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng== + dependencies: + array-find-index "^1.0.1" + +cyclist@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" + integrity sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A== + +data-urls@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" + integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +debug@^3.0.0, debug@^3.2.7: + version "3.2.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decimal.js@^10.3.1: + version "10.3.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" + integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og== + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" + integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== + +deep-is@^0.1.3, deep-is@~0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + +default-browser-id@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/default-browser-id/-/default-browser-id-1.0.4.tgz#e59d09a5d157b828b876c26816e61c3d2a2c203a" + integrity sha512-qPy925qewwul9Hifs+3sx1ZYn14obHxpkX+mPD369w4Rzg+YkJBgi3SOvwUq81nWSjqGUegIgEPwD8u+HUnxlw== + dependencies: + bplist-parser "^0.1.0" + meow "^3.1.0" + untildify "^2.0.0" + +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA== + dependencies: + clone "^1.0.2" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + +define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" + integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +del@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" + integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== + dependencies: + "@types/glob" "^7.1.1" + globby "^6.1.0" + is-path-cwd "^2.0.0" + is-path-in-cwd "^2.0.0" + p-map "^2.0.0" + pify "^4.0.1" + rimraf "^2.6.3" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +detab@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detab/-/detab-2.0.4.tgz#b927892069aff405fbb9a186fe97a44a92a94b43" + integrity sha512-8zdsQA5bIkoRECvCrNKPla84lyoR7DSAyf7p0YgXzBO9PDJx8KntPUay7NS6yp+KdxdVtiE5SpHKtbp2ZQyA9g== + dependencies: + repeat-string "^1.5.4" + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== + +detect-node@^2.0.4, detect-node@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + +detect-package-manager@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/detect-package-manager/-/detect-package-manager-2.0.1.tgz#6b182e3ae5e1826752bfef1de9a7b828cffa50d8" + integrity sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A== + dependencies: + execa "^5.1.1" + +detect-port@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/detect-port/-/detect-port-1.3.0.tgz#d9c40e9accadd4df5cac6a782aefd014d573d1f1" + integrity sha512-E+B1gzkl2gqxt1IhUzwjrxBKRqx1UzC3WLONHinn8S3T6lwV/agVCyitiFOsGJ/eYuEUBvD71MZHy3Pv1G9doQ== + dependencies: + address "^1.0.1" + debug "^2.6.0" + +diff-sequences@^28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" + integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== + +dns-packet@^5.2.2: + version "5.4.0" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.4.0.tgz#1f88477cf9f27e78a213fb6d118ae38e759a879b" + integrity sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dom-accessibility-api@^0.5.9: + version "0.5.14" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56" + integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg== + +dom-converter@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" + integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== + dependencies: + utila "~0.4" + +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-walk@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" + integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + +domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dotenv-defaults@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz#6b3ec2e4319aafb70940abda72d3856770ee77ac" + integrity sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg== + dependencies: + dotenv "^8.2.0" + +dotenv-expand@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" + integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== + +dotenv-webpack@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/dotenv-webpack/-/dotenv-webpack-8.0.0.tgz#bde8750eebda8fd1c6d7134f02bf56ad1f865772" + integrity sha512-vsWj11yWbIxLUPcQDbifCGW1+Mp03XfApFHJTC+/Ag9g3D/AnxoaVZcp76LpuBmReRwIJ+YO1fVdhmpzh+LL1A== + dependencies: + dotenv-defaults "^2.0.2" + +dotenv@^8.0.0, dotenv@^8.2.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" + integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== + +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.172: + version "1.4.177" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.177.tgz#b6a4436eb788ca732556cd69f384b8a3c82118c5" + integrity sha512-FYPir3NSBEGexSZUEeht81oVhHfLFl6mhUKSkjHN/iB/TwEIt/WHQrqVGfTLN5gQxwJCQkIJBe05eOXjI7omgg== + +elliptic@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +emittery@^0.10.2: + version "0.10.2" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933" + integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + +emotion-reset@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/emotion-reset/-/emotion-reset-3.0.1.tgz#1445e66bab7e8fd9975ce0d8cd3324589981f0a6" + integrity sha512-v6scW83qSu+wtxg7lX1s0+/2U4EAAGFxDQMkvXE10jhKtyuXCzy3/su5/MU9ZjXeNv6ZjxZH51WktrKosKUy9g== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.4" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" + integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== + dependencies: + once "^1.4.0" + +endent@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/endent/-/endent-2.1.0.tgz#5aaba698fb569e5e18e69e1ff7a28ff35373cd88" + integrity sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w== + dependencies: + dedent "^0.7.0" + fast-json-parse "^1.0.3" + objectorarray "^1.0.5" + +enhanced-resolve@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" + integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.5.0" + tapable "^1.0.0" + +enhanced-resolve@^5.0.0, enhanced-resolve@^5.9.3: + version "5.10.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6" + integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +envinfo@^7.7.3: + version "7.8.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" + integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== + +errno@^0.1.3, errno@~0.1.7: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +error-ex@^1.2.0, error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +error-stack-parser@^2.0.6: + version "2.1.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" + integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== + dependencies: + stackframe "^1.3.4" + +es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" + integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + regexp.prototype.flags "^1.4.3" + string.prototype.trimend "^1.0.5" + string.prototype.trimstart "^1.0.5" + unbox-primitive "^1.0.2" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" + integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + +es-get-iterator@^1.0.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.2.tgz#9234c54aba713486d7ebde0220864af5e2b283f7" + integrity sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.0" + has-symbols "^1.0.1" + is-arguments "^1.1.0" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.5" + isarray "^2.0.5" + +es-module-lexer@^0.9.0: + version "0.9.3" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" + integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es5-shim@^4.5.13: + version "4.6.7" + resolved "https://registry.yarnpkg.com/es5-shim/-/es5-shim-4.6.7.tgz#bc67ae0fc3dd520636e0a1601cc73b450ad3e955" + integrity sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ== + +es6-shim@^0.35.5: + version "0.35.6" + resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.6.tgz#d10578301a83af2de58b9eadb7c2c9945f7388a0" + integrity sha512-EmTr31wppcaIAgblChZiuN/l9Y7DPyw8Xtbg7fIVngn6zMW+IEBJDJngeKC3x6wr0V/vcA2wqeFnaw1bFJbDdA== + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-prettier@^8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" + integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== + +eslint-import-resolver-node@^0.3.6: + version "0.3.6" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" + integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== + dependencies: + debug "^3.2.7" + resolve "^1.20.0" + +eslint-module-utils@^2.7.3: + version "2.7.3" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" + integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== + dependencies: + debug "^3.2.7" + find-up "^2.1.0" + +eslint-plugin-import@^2.26.0: + version "2.26.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" + integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== + dependencies: + array-includes "^3.1.4" + array.prototype.flat "^1.2.5" + debug "^2.6.9" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.6" + eslint-module-utils "^2.7.3" + has "^1.0.3" + is-core-module "^2.8.1" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.values "^1.1.5" + resolve "^1.22.0" + tsconfig-paths "^3.14.1" + +eslint-plugin-prettier@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-plugin-react-hooks@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" + integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== + +eslint-plugin-react@^7.30.1: + version "7.30.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz#2be4ab23ce09b5949c6631413ba64b2810fd3e22" + integrity sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg== + dependencies: + array-includes "^3.1.5" + array.prototype.flatmap "^1.3.0" + doctrine "^2.1.0" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.5" + object.fromentries "^2.0.5" + object.hasown "^1.1.1" + object.values "^1.1.5" + prop-types "^15.8.1" + resolve "^2.0.0-next.3" + semver "^6.3.0" + string.prototype.matchall "^4.0.7" + +eslint-scope@5.1.1, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint@^8.19.0: + version "8.19.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.19.0.tgz#7342a3cbc4fbc5c106a1eefe0fd0b50b6b1a7d28" + integrity sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw== + dependencies: + "@eslint/eslintrc" "^1.3.0" + "@humanwhocodes/config-array" "^0.9.2" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.3.2" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^6.0.1" + globals "^13.15.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^9.3.2: + version "9.3.2" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596" + integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA== + dependencies: + acorn "^8.7.1" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.1.0, esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-to-babel@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/estree-to-babel/-/estree-to-babel-3.2.1.tgz#82e78315275c3ca74475fdc8ac1a5103c8a75bf5" + integrity sha512-YNF+mZ/Wu2FU/gvmzuWtYc8rloubL7wfXCTgouFrnjGVXPA/EeYYA7pupXWrb3Iv1cTBeSSxxJIbK23l4MRNqg== + dependencies: + "@babel/traverse" "^7.1.6" + "@babel/types" "^7.2.0" + c8 "^7.6.0" + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.0.0, events@^3.2.0, events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +exec-sh@^0.3.2: + version "0.3.6" + resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.6.tgz#ff264f9e325519a60cb5e273692943483cca63bc" + integrity sha512-nQn+hI3yp+oD0huYhKwvYI32+JFeq+XkNcD1GAo3Y/MjxsfVGmrrzrnzjWiNY6f+pUCP440fThsFh5gZrRAU/w== + +execa@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" + integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== + dependencies: + cross-spawn "^6.0.0" + get-stream "^4.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execa@^5.0.0, execa@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expect@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" + integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== + dependencies: + "@jest/expect-utils" "^28.1.3" + jest-get-type "^28.0.2" + jest-matcher-utils "^28.1.3" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + +express@^4.17.1, express@^4.17.3: + version "4.18.1" + resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" + integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.0" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.10.3" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-glob@^2.2.6: + version "2.2.7" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" + integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + +fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-parse@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fast-json-parse/-/fast-json-parse-1.0.3.tgz#43e5c61ee4efa9265633046b770fb682a7577c4d" + integrity sha512-FRWsaZRWEJ1ESVNbDWmsAlqDk96gPQezzLghafp5J4GUKjbCz3OkAHuZs5TuPEtkbVQERysLp9xv6c24fBm8Aw== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastest-levenshtein@^1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2" + integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow== + +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + +fault@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.4.tgz#eafcfc0a6d214fc94601e170df29954a4f842f13" + integrity sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA== + dependencies: + format "^0.2.0" + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + +fb-watchman@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" + integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== + dependencies: + bser "2.1.1" + +fetch-retry@^5.0.2: + version "5.0.3" + resolved "https://registry.yarnpkg.com/fetch-retry/-/fetch-retry-5.0.3.tgz#edfa3641892995f9afee94f25b168827aa97fe3d" + integrity sha512-uJQyMrX5IJZkhoEUBQ3EjxkeiZkppBd5jS/fMTJmfZxLSiaQjv2zD0kTvuvkSH89uFvgSlB6ueGpjD3HWN7Bxw== + +figgy-pudding@^3.5.1: + version "3.5.2" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" + integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== + +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +file-loader@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" + integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +file-system-cache@^1.0.5: + version "1.1.0" + resolved "https://registry.yarnpkg.com/file-system-cache/-/file-system-cache-1.1.0.tgz#984de17b976b75a77a27e08d6828137c1aa80fa1" + integrity sha512-IzF5MBq+5CR0jXx5RxPe4BICl/oEhBSXKaL9fLhAXrIfIUS77Hr4vzrYyqYMHN6uTt+BOqi3fDCTjjEBCjERKw== + dependencies: + fs-extra "^10.1.0" + ramda "^0.28.0" + +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-cache-dir@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-root@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" + integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + integrity sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA== + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== + dependencies: + locate-path "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== + dependencies: + locate-path "^3.0.0" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" + integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== + +flush-write-stream@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + +focus-lock@^0.8.0: + version "0.8.1" + resolved "https://registry.yarnpkg.com/focus-lock/-/focus-lock-0.8.1.tgz#bb36968abf77a2063fa173cb6c47b12ac8599d33" + integrity sha512-/LFZOIo82WDsyyv7h7oc0MJF9ACOvDRdx9rWPZ2pgMfNWu/z8hQDBtOchuB/0BVLmuFOZjV02YwUVzNsWx/EzA== + dependencies: + tslib "^1.9.3" + +follow-redirects@^1.0.0, follow-redirects@^1.14.9: + version "1.15.1" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" + integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== + +for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== + +foreground-child@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-2.0.0.tgz#71b32800c9f15aa8f2f83f4a6bd9bff35d861a53" + integrity sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^3.0.2" + +fork-ts-checker-webpack-plugin@^4.1.6: + version "4.1.6" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz#5055c703febcf37fa06405d400c122b905167fc5" + integrity sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw== + dependencies: + "@babel/code-frame" "^7.5.5" + chalk "^2.4.1" + micromatch "^3.1.10" + minimatch "^3.0.4" + semver "^5.6.0" + tapable "^1.0.0" + worker-rpc "^0.1.0" + +fork-ts-checker-webpack-plugin@^6.0.4: + version "6.5.2" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz#4f67183f2f9eb8ba7df7177ce3cf3e75cdafb340" + integrity sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA== + dependencies: + "@babel/code-frame" "^7.8.3" + "@types/json-schema" "^7.0.5" + chalk "^4.1.0" + chokidar "^3.4.2" + cosmiconfig "^6.0.0" + deepmerge "^4.2.2" + fs-extra "^9.0.0" + glob "^7.1.6" + memfs "^3.1.2" + minimatch "^3.0.4" + schema-utils "2.7.0" + semver "^7.3.2" + tapable "^1.0.0" + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +format@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b" + integrity sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww== + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== + dependencies: + map-cache "^0.2.2" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g== + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-extra@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" + integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^9.0.0, fs-extra@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs-monkey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3" + integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q== + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + integrity sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA== + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@^1.2.7: + version "1.2.13" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" + integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== + dependencies: + bindings "^1.5.0" + nan "^2.12.1" + +fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +function.prototype.name@^1.1.0, function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" + integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g== + +functions-have-names@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + 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" + +gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" + integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" + integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.3" + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" + integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== + +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw== + +get-stream@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" + integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== + dependencies: + pump "^3.0.0" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== + +github-slugger@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.4.0.tgz#206eb96cdb22ee56fdc53a28d5a302338463444e" + integrity sha512-w0dzqw/nt51xMVmlaV1+JRzN+oCa1KfcgGEWhxUG16wbdA+Xnt/yoFO8Z8x/V82ZcZ0wy6ln9QDup5avbhiDhQ== + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA== + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob-promise@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/glob-promise/-/glob-promise-3.4.0.tgz#b6b8f084504216f702dc2ce8c9bc9ac8866fdb20" + integrity sha512-q08RJ6O+eJn+dVanerAndJwIcumgbDdYiUT7zFQl3Wm1xD6fBKtah7H8ZJChj4wP+8C+QfeVy8xautR7rdmKEw== + dependencies: + "@types/glob" "*" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig== + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@^7.0.3, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" + integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== + dependencies: + min-document "^2.19.0" + process "^0.11.10" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^13.15.0: + version "13.15.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac" + integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.0.2, globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + integrity sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw== + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globby@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" + integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== + dependencies: + "@types/glob" "^7.1.1" + array-union "^1.0.2" + dir-glob "^2.2.2" + fast-glob "^2.2.6" + glob "^7.1.3" + ignore "^4.0.3" + pify "^4.0.1" + slash "^2.0.0" + +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.10" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" + integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + +graphql@^16.3.0: + version "16.5.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.5.0.tgz#41b5c1182eaac7f3d47164fb247f61e4dfb69c85" + integrity sha512-qbHgh8Ix+j/qY+a/ZcJnFQ+j8ezakqPiHwPiZhV/3PgGlgf96QMBB5/f2rkiC9sgLoy/xvT6TSiaf2nTHJh5iA== + +hamt_plus@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/hamt_plus/-/hamt_plus-1.0.2.tgz#e21c252968c7e33b20f6a1b094cd85787a265601" + integrity sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA== + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + +handlebars@^4.7.7: + version "4.7.7" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" + integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== + dependencies: + minimist "^1.2.5" + neo-async "^2.6.0" + source-map "^0.6.1" + wordwrap "^1.0.0" + optionalDependencies: + uglify-js "^3.1.4" + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-glob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-glob/-/has-glob-1.0.0.tgz#9aaa9eedbffb1ba3990a7b0010fb678ee0081207" + integrity sha512-D+8A457fBShSEI3tFCj65PAbT++5sKiFtdCdOam0gnfBgw9D277OERk+HM9qYJXmdVLZ/znez10SqHN0BBQ50g== + dependencies: + is-glob "^3.0.0" + +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hast-to-hyperscript@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz#9b67fd188e4c81e8ad66f803855334173920218d" + integrity sha512-zQgLKqF+O2F72S1aa4y2ivxzSlko3MAvxkwG8ehGmNiqd98BIN3JM1rAJPmplEyLmGLO2QZYJtIneOSZ2YbJuA== + dependencies: + "@types/unist" "^2.0.3" + comma-separated-tokens "^1.0.0" + property-information "^5.3.0" + space-separated-tokens "^1.0.0" + style-to-object "^0.3.0" + unist-util-is "^4.0.0" + web-namespaces "^1.0.0" + +hast-util-from-parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-from-parse5/-/hast-util-from-parse5-6.0.1.tgz#554e34abdeea25ac76f5bd950a1f0180e0b3bc2a" + integrity sha512-jeJUWiN5pSxW12Rh01smtVkZgZr33wBokLzKLwinYOUfSzm1Nl/c3GUGebDyOKjdsRgMvoVbV0VpAcpjF4NrJA== + dependencies: + "@types/parse5" "^5.0.0" + hastscript "^6.0.0" + property-information "^5.0.0" + vfile "^4.0.0" + vfile-location "^3.2.0" + web-namespaces "^1.0.0" + +hast-util-parse-selector@^2.0.0: + version "2.2.5" + resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz#d57c23f4da16ae3c63b3b6ca4616683313499c3a" + integrity sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ== + +hast-util-raw@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/hast-util-raw/-/hast-util-raw-6.0.1.tgz#973b15930b7529a7b66984c98148b46526885977" + integrity sha512-ZMuiYA+UF7BXBtsTBNcLBF5HzXzkyE6MLzJnL605LKE8GJylNjGc4jjxazAHUtcwT5/CEt6afRKViYB4X66dig== + dependencies: + "@types/hast" "^2.0.0" + hast-util-from-parse5 "^6.0.0" + hast-util-to-parse5 "^6.0.0" + html-void-elements "^1.0.0" + parse5 "^6.0.0" + unist-util-position "^3.0.0" + vfile "^4.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hast-util-to-parse5@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hast-util-to-parse5/-/hast-util-to-parse5-6.0.0.tgz#1ec44650b631d72952066cea9b1445df699f8479" + integrity sha512-Lu5m6Lgm/fWuz8eWnrKezHtVY83JeRGaNQ2kn9aJgqaxvVkFCZQBEhgodZUDUvoodgyROHDb3r5IxAEdl6suJQ== + dependencies: + hast-to-hyperscript "^9.0.0" + property-information "^5.0.0" + web-namespaces "^1.0.0" + xtend "^4.0.0" + zwitch "^1.0.0" + +hastscript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-6.0.0.tgz#e8768d7eac56c3fdeac8a92830d58e811e5bf640" + integrity sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w== + dependencies: + "@types/hast" "^2.0.0" + comma-separated-tokens "^1.0.0" + hast-util-parse-selector "^2.0.0" + property-information "^5.0.0" + space-separated-tokens "^1.0.0" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +headers-polyfill@^3.0.4: + version "3.0.7" + resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-3.0.7.tgz#725c4f591e6748f46b036197eae102c92b959ff4" + integrity sha512-JoLCAdCEab58+2/yEmSnOlficyHFpIl0XJqwu3l+Unkm1gXpFUYsThz6Yha3D6tNhocWkCPfyW0YVIGWFqTi7w== + +highlight.js@^10.4.1, highlight.js@~10.7.0: + version "10.7.3" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== + +history@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b" + integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ== + dependencies: + "@babel/runtime" "^7.7.6" + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +hoist-non-react-statics@^3.3.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== + dependencies: + whatwg-encoding "^2.0.0" + +html-entities@^2.1.0, html-entities@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46" + integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA== + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +html-minifier-terser@^5.0.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz#922e96f1f3bb60832c2634b79884096389b1f054" + integrity sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg== + dependencies: + camel-case "^4.1.1" + clean-css "^4.2.3" + commander "^4.1.1" + he "^1.2.0" + param-case "^3.0.3" + relateurl "^0.2.7" + terser "^4.6.3" + +html-minifier-terser@^6.0.2: + version "6.1.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab" + integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw== + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" + +html-tags@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.2.0.tgz#dbb3518d20b726524e4dd43de397eb0a95726961" + integrity sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg== + +html-void-elements@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483" + integrity sha512-uE/TxKuyNIcx44cIWnjr/rfIATDH7ZaOMmstu0CwhFG1Dunhlp4OC6/NMbhiwoq5BpW0ubi303qnEk/PZj614w== + +html-webpack-plugin@^4.0.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-4.5.2.tgz#76fc83fa1a0f12dd5f7da0404a54e2699666bc12" + integrity sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A== + dependencies: + "@types/html-minifier-terser" "^5.0.0" + "@types/tapable" "^1.0.5" + "@types/webpack" "^4.41.8" + html-minifier-terser "^5.0.1" + loader-utils "^1.2.3" + lodash "^4.17.20" + pretty-error "^2.1.1" + tapable "^1.1.3" + util.promisify "1.0.0" + +html-webpack-plugin@^5.0.0, html-webpack-plugin@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz#c3911936f57681c1f9f4d8b68c158cd9dfe52f50" + integrity sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw== + dependencies: + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +iconv-lite@0.4.24, iconv-lite@^0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +icss-utils@^4.0.0, icss-utils@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" + integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== + dependencies: + postcss "^7.0.14" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" + integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== + +ieee754@^1.1.13, ieee754@^1.1.4: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + integrity sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA== + +ignore@^4.0.3: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== + +import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg== + dependencies: + repeating "^2.0.0" + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +infer-owner@^1.0.3, infer-owner@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA== + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + +inline-style-parser@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" + integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== + +inquirer@^8.2.0: + version "8.2.4" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4" + integrity sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.5.5" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + wrap-ansi "^7.0.0" + +internal-slot@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" + integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== + dependencies: + get-intrinsic "^1.1.0" + has "^1.0.3" + side-channel "^1.0.4" + +interpret@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9" + integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw== + +ip@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" + integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipaddr.js@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0" + integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng== + +is-absolute-url@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" + integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + integrity sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A== + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== + dependencies: + kind-of "^6.0.0" + +is-alphabetical@1.0.4, is-alphabetical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz#9e7d6b94916be22153745d184c298cbf986a686d" + integrity sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg== + +is-alphanumerical@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz#7eb9a2431f855f6b1ef1a78e326df515696c4dbf" + integrity sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + +is-arguments@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q== + dependencies: + binary-extensions "^1.0.0" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== + +is-buffer@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-callable@^1.1.4, is-callable@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" + integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== + +is-ci@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" + integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== + dependencies: + ci-info "^2.0.0" + +is-core-module@^2.8.1, is-core-module@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== + dependencies: + has "^1.0.3" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + integrity sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg== + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== + dependencies: + kind-of "^6.0.0" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-decimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.4.tgz#65a3a5958a1c5b63a706e1b333d7cd9f630d3fa5" + integrity sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw== + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-dom@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-dom/-/is-dom-1.1.0.tgz#af1fced292742443bb59ca3f76ab5e80907b4e8a" + integrity sha512-u82f6mvhYxRPKpw8V1N0W8ce1xXwOrQtgGcxl6UCL5zBmZu3is/18K0rR7uFCnMDuAsS/3W54mGL4vsaFUQlEQ== + dependencies: + is-object "^1.0.1" + is-window "^1.0.2" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-finite@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" + integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-function@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" + integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" + integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== + +is-glob@^3.0.0, is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw== + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-hexadecimal@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz#cc35c97588da4bd49a8eedd6bc4082d44dcb23a7" + integrity sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw== + +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + +is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-node-process@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-node-process/-/is-node-process-1.0.1.tgz#4fc7ac3a91e8aac58175fe0578abbc56f2831b23" + integrity sha512-5IcdXuf++TTNt3oGl9EBdkvndXA8gmc4bz/Y+mdEpWh3Mcn/+kOw6hI7LD5CocqJWMzeb0I0ClndRVNdEPuJXQ== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== + dependencies: + kind-of "^3.0.2" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== + +is-path-cwd@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" + integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== + +is-path-in-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" + integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== + dependencies: + is-path-inside "^2.1.0" + +is-path-inside@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" + integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== + dependencies: + path-is-inside "^1.0.2" + +is-plain-obj@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + +is-plain-object@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + +is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + +is-regex@^1.1.2, is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +is-utf8@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" + integrity sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q== + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-whitespace-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" + integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== + +is-window@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-window/-/is-window-1.0.2.tgz#2c896ca53db97de45d3c33133a65d8c9f563480d" + integrity sha512-uj00kdXyZb9t9RcAUAwMZAnkBUwdYGhYlt7djMXhfyhUCzwNba50tIiBKR7q0l7tdoBtFVw/3JmLY6fI3rmZmg== + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== + +is-word-character@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.4.tgz#ce0e73216f98599060592f62ff31354ddbeb0230" + integrity sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== + +is-wsl@^2.1.1, is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +isobject@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" + integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== + +isomorphic-unfetch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f" + integrity sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q== + dependencies: + node-fetch "^2.6.1" + unfetch "^4.2.0" + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" + integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== + +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz#31d18bdd127f825dd02ea7bfdfd906f8ab840e9f" + integrity sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A== + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" + integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.5" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" + integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +istanbul-reports@^3.1.4: + version "3.1.4" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c" + integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +iterate-iterator@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.2.tgz#551b804c9eaa15b847ea6a7cdc2f5bf1ec150f91" + integrity sha512-t91HubM4ZDQ70M9wqp+pcNpu8OyJ9UAtXntT/Bcsvp5tZMnz9vRa+IunKXeI8AnfZMTv0jNuVEmGeLSMjVvfPw== + +iterate-value@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/iterate-value/-/iterate-value-1.0.2.tgz#935115bd37d006a52046535ebc8d07e9c9337f57" + integrity sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ== + dependencies: + es-get-iterator "^1.0.2" + iterate-iterator "^1.0.1" + +javascript-natural-sort@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59" + integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== + +jest-changed-files@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.1.3.tgz#d9aeee6792be3686c47cb988a8eaf82ff4238831" + integrity sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA== + dependencies: + execa "^5.0.0" + p-limit "^3.1.0" + +jest-circus@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-28.1.3.tgz#d14bd11cf8ee1a03d69902dc47b6bd4634ee00e4" + integrity sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/expect" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + is-generator-fn "^2.0.0" + jest-each "^28.1.3" + jest-matcher-utils "^28.1.3" + jest-message-util "^28.1.3" + jest-runtime "^28.1.3" + jest-snapshot "^28.1.3" + jest-util "^28.1.3" + p-limit "^3.1.0" + pretty-format "^28.1.3" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-cli@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-28.1.3.tgz#558b33c577d06de55087b8448d373b9f654e46b2" + integrity sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ== + dependencies: + "@jest/core" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/types" "^28.1.3" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^28.1.3" + jest-util "^28.1.3" + jest-validate "^28.1.3" + prompts "^2.0.1" + yargs "^17.3.1" + +jest-config@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-28.1.3.tgz#e315e1f73df3cac31447eed8b8740a477392ec60" + integrity sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ== + dependencies: + "@babel/core" "^7.11.6" + "@jest/test-sequencer" "^28.1.3" + "@jest/types" "^28.1.3" + babel-jest "^28.1.3" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-circus "^28.1.3" + jest-environment-node "^28.1.3" + jest-get-type "^28.0.2" + jest-regex-util "^28.0.2" + jest-resolve "^28.1.3" + jest-runner "^28.1.3" + jest-util "^28.1.3" + jest-validate "^28.1.3" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^28.1.3" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" + integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== + dependencies: + chalk "^4.0.0" + diff-sequences "^28.1.1" + jest-get-type "^28.0.2" + pretty-format "^28.1.3" + +jest-docblock@^28.1.1: + version "28.1.1" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-28.1.1.tgz#6f515c3bf841516d82ecd57a62eed9204c2f42a8" + integrity sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA== + dependencies: + detect-newline "^3.0.0" + +jest-each@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-28.1.3.tgz#bdd1516edbe2b1f3569cfdad9acd543040028f81" + integrity sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g== + dependencies: + "@jest/types" "^28.1.3" + chalk "^4.0.0" + jest-get-type "^28.0.2" + jest-util "^28.1.3" + pretty-format "^28.1.3" + +jest-environment-jsdom@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-28.1.3.tgz#2d4e5d61b7f1d94c3bddfbb21f0308ee506c09fb" + integrity sha512-HnlGUmZRdxfCByd3GM2F100DgQOajUBzEitjGqIREcb45kGjZvRrKUdlaF6escXBdcXNl0OBh+1ZrfeZT3GnAg== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/fake-timers" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/jsdom" "^16.2.4" + "@types/node" "*" + jest-mock "^28.1.3" + jest-util "^28.1.3" + jsdom "^19.0.0" + +jest-environment-node@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5" + integrity sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/fake-timers" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + jest-mock "^28.1.3" + jest-util "^28.1.3" + +jest-get-type@^28.0.2: + version "28.0.2" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" + integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== + +jest-haste-map@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" + integrity sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w== + dependencies: + "@jest/types" "^26.6.2" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.4" + jest-regex-util "^26.0.0" + jest-serializer "^26.6.2" + jest-util "^26.6.2" + jest-worker "^26.6.2" + micromatch "^4.0.2" + sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.1.2" + +jest-haste-map@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b" + integrity sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA== + dependencies: + "@jest/types" "^28.1.3" + "@types/graceful-fs" "^4.1.3" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^28.0.2" + jest-util "^28.1.3" + jest-worker "^28.1.3" + micromatch "^4.0.4" + walker "^1.0.8" + optionalDependencies: + fsevents "^2.3.2" + +jest-leak-detector@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz#a6685d9b074be99e3adee816ce84fd30795e654d" + integrity sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA== + dependencies: + jest-get-type "^28.0.2" + pretty-format "^28.1.3" + +jest-matcher-utils@^28.0.0, jest-matcher-utils@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" + integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== + dependencies: + chalk "^4.0.0" + jest-diff "^28.1.3" + jest-get-type "^28.0.2" + pretty-format "^28.1.3" + +jest-message-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d" + integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^28.1.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^28.1.3" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^27.0.6: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6" + integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og== + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + +jest-mock@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-28.1.3.tgz#d4e9b1fc838bea595c77ab73672ebf513ab249da" + integrity sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" + integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== + +jest-regex-util@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== + +jest-regex-util@^28.0.2: + version "28.0.2" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead" + integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw== + +jest-resolve-dependencies@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz#8c65d7583460df7275c6ea2791901fa975c1fe66" + integrity sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA== + dependencies: + jest-regex-util "^28.0.2" + jest-snapshot "^28.1.3" + +jest-resolve@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-28.1.3.tgz#cfb36100341ddbb061ec781426b3c31eb51aa0a8" + integrity sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ== + dependencies: + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^28.1.3" + jest-pnp-resolver "^1.2.2" + jest-util "^28.1.3" + jest-validate "^28.1.3" + resolve "^1.20.0" + resolve.exports "^1.1.0" + slash "^3.0.0" + +jest-runner@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-28.1.3.tgz#5eee25febd730b4713a2cdfd76bdd5557840f9a1" + integrity sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA== + dependencies: + "@jest/console" "^28.1.3" + "@jest/environment" "^28.1.3" + "@jest/test-result" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.10.2" + graceful-fs "^4.2.9" + jest-docblock "^28.1.1" + jest-environment-node "^28.1.3" + jest-haste-map "^28.1.3" + jest-leak-detector "^28.1.3" + jest-message-util "^28.1.3" + jest-resolve "^28.1.3" + jest-runtime "^28.1.3" + jest-util "^28.1.3" + jest-watcher "^28.1.3" + jest-worker "^28.1.3" + p-limit "^3.1.0" + source-map-support "0.5.13" + +jest-runtime@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-28.1.3.tgz#a57643458235aa53e8ec7821949e728960d0605f" + integrity sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/fake-timers" "^28.1.3" + "@jest/globals" "^28.1.3" + "@jest/source-map" "^28.1.2" + "@jest/test-result" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + execa "^5.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^28.1.3" + jest-message-util "^28.1.3" + jest-mock "^28.1.3" + jest-regex-util "^28.0.2" + jest-resolve "^28.1.3" + jest-snapshot "^28.1.3" + jest-util "^28.1.3" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-serializer@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" + integrity sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g== + dependencies: + "@types/node" "*" + graceful-fs "^4.2.4" + +jest-snapshot@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668" + integrity sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg== + dependencies: + "@babel/core" "^7.11.6" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.3.3" + "@jest/expect-utils" "^28.1.3" + "@jest/transform" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/babel__traverse" "^7.0.6" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^28.1.3" + graceful-fs "^4.2.9" + jest-diff "^28.1.3" + jest-get-type "^28.0.2" + jest-haste-map "^28.1.3" + jest-matcher-utils "^28.1.3" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + natural-compare "^1.4.0" + pretty-format "^28.1.3" + semver "^7.3.5" + +jest-util@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1" + integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q== + dependencies: + "@jest/types" "^26.6.2" + "@types/node" "*" + chalk "^4.0.0" + graceful-fs "^4.2.4" + is-ci "^2.0.0" + micromatch "^4.0.2" + +jest-util@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0" + integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ== + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-28.1.3.tgz#e322267fd5e7c64cea4629612c357bbda96229df" + integrity sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA== + dependencies: + "@jest/types" "^28.1.3" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^28.0.2" + leven "^3.1.0" + pretty-format "^28.1.3" + +jest-watcher@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.3.tgz#c6023a59ba2255e3b4c57179fc94164b3e73abd4" + integrity sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g== + dependencies: + "@jest/test-result" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.10.2" + jest-util "^28.1.3" + string-length "^4.0.1" + +jest-worker@^26.5.0, jest-worker@^26.6.2: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98" + integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest/-/jest-28.1.3.tgz#e9c6a7eecdebe3548ca2b18894a50f45b36dfc6b" + integrity sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA== + dependencies: + "@jest/core" "^28.1.3" + "@jest/types" "^28.1.3" + import-local "^3.0.2" + jest-cli "^28.1.3" + +js-levenshtein@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" + integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== + +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +js-string-escape@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" + integrity sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg== + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +jsdom@^19.0.0: + version "19.0.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-19.0.0.tgz#93e67c149fe26816d38a849ea30ac93677e16b6a" + integrity sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A== + dependencies: + abab "^2.0.5" + acorn "^8.5.0" + acorn-globals "^6.0.0" + cssom "^0.5.0" + cssstyle "^2.3.0" + data-urls "^3.0.1" + decimal.js "^10.3.1" + domexception "^4.0.0" + escodegen "^2.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^3.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^10.0.0" + ws "^8.2.3" + xml-name-validator "^4.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" + integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== + +json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + +json5@^2.1.2, json5@^2.1.3, json5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.3.1" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.1.tgz#a3e0f1cb7e230954eab4dcbce9f6288a78f8ba44" + integrity sha512-pxrjmNpeRw5wwVeWyEAk7QJu2GnBO3uzPFmHCKJJFPKK2Cy0cWL23krGtLdnMmbIi6/FjlrQpPyfQI19ByPOhQ== + dependencies: + array-includes "^3.1.5" + object.assign "^4.1.2" + +junk@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" + integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" + integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== + +klona@^2.0.4: + version "2.0.5" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" + integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== + +lazy-universal-dotenv@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lazy-universal-dotenv/-/lazy-universal-dotenv-3.0.1.tgz#a6c8938414bca426ab8c9463940da451a911db38" + integrity sha512-prXSYk799h3GY3iOWnC6ZigYzMPjxN2svgjJ9shk7oMadSNX3wXy0B6F32PMJv7qtMnrIbUxoEHzbutvxR2LBQ== + dependencies: + "@babel/runtime" "^7.5.0" + app-root-dir "^1.0.2" + core-js "^3.0.4" + dotenv "^8.0.0" + dotenv-expand "^5.1.0" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" + integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +load-json-file@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" + integrity sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A== + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + pinkie-promise "^2.0.0" + strip-bom "^2.0.0" + +loader-runner@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +loader-utils@^1.2.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +loader-utils@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129" + integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.uniq@4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash@4.17.21, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ== + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lowlight@^1.17.0: + version "1.20.0" + resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.20.0.tgz#ddb197d33462ad0d93bf19d17b6c301aa3941888" + integrity sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw== + dependencies: + fault "^1.0.0" + highlight.js "~10.7.0" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +lz-string@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" + integrity sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ== + +make-dir@^2.0.0, make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + +make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" + integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== + dependencies: + tmpl "1.0.5" + +map-age-cleaner@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" + integrity sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w== + dependencies: + p-defer "^1.0.0" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== + +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== + +map-or-similar@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08" + integrity sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg== + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== + dependencies: + object-visit "^1.0.0" + +markdown-escapes@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" + integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== + +match-sorter@^6.0.2: + version "6.3.1" + resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda" + integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw== + dependencies: + "@babel/runtime" "^7.12.5" + remove-accents "0.4.2" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mdast-squeeze-paragraphs@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" + integrity sha512-zxdPn69hkQ1rm4J+2Cs2j6wDEv7O17TfXTJ33tl/+JPIoEmtV9t2ZzBM5LPHE8QlHsmVD8t3vPKCyY3oH+H8MQ== + dependencies: + unist-util-remove "^2.0.0" + +mdast-util-definitions@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz#c5c1a84db799173b4dcf7643cda999e440c24db2" + integrity sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ== + dependencies: + unist-util-visit "^2.0.0" + +mdast-util-to-hast@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-10.0.1.tgz#0cfc82089494c52d46eb0e3edb7a4eb2aea021eb" + integrity sha512-BW3LM9SEMnjf4HXXVApZMt8gLQWVNXc3jryK0nJu/rOXPOnlkUjmdkDlmxMirpbU9ILncGFIwLH/ubnWBbcdgA== + dependencies: + "@types/mdast" "^3.0.0" + "@types/unist" "^2.0.0" + mdast-util-definitions "^4.0.0" + mdurl "^1.0.0" + unist-builder "^2.0.0" + unist-util-generated "^1.0.0" + unist-util-position "^3.0.0" + unist-util-visit "^2.0.0" + +mdast-util-to-string@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz#27055500103f51637bd07d01da01eb1967a43527" + integrity sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A== + +mdurl@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" + integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +mem@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/mem/-/mem-8.1.1.tgz#cf118b357c65ab7b7e0817bdf00c8062297c0122" + integrity sha512-qFCFUDs7U3b8mBDPyz5EToEKoAkgCzqquIgi9nkkR9bixxOVOre+09lbuH7+9Kn2NFpm56M3GUWVbU2hQgdACA== + dependencies: + map-age-cleaner "^0.1.3" + mimic-fn "^3.1.0" + +memfs@^3.1.2, memfs@^3.2.2, memfs@^3.4.3: + version "3.4.7" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.7.tgz#e5252ad2242a724f938cb937e3c4f7ceb1f70e5a" + integrity sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw== + dependencies: + fs-monkey "^1.0.3" + +memoizerific@^1.11.3: + version "1.11.3" + resolved "https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" + integrity sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog== + dependencies: + map-or-similar "^1.5.0" + +memory-fs@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + integrity sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +memory-fs@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" + integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^3.1.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA== + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +microevent.ts@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" + integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== + +micromatch@^3.1.10, micromatch@^3.1.4: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +microseconds@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39" + integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.30, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + +mime@^2.4.4: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-fn@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74" + integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ== + +min-document@^2.19.0: + version "2.19.0" + resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" + integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ== + dependencies: + dom-walk "^0.1.0" + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + +minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== + +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass@^3.0.0, minipass@^3.1.1: + version "3.3.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.4.tgz#ca99f95dd77c43c7a76bf51e6d200025eee0ffae" + integrity sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw== + dependencies: + yallist "^4.0.0" + +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mississippi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^3.0.0" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mixin-deep@^1.2.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@^0.5.1, mkdirp@^0.5.3: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mkdirp@^1.0.3, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + integrity sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ== + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +msw@^0.43.0: + version "0.43.0" + resolved "https://registry.yarnpkg.com/msw/-/msw-0.43.0.tgz#10c6fc3fb1752c0a144179e5ab04c6a512cc9959" + integrity sha512-XJylZP0qW3D5WUGWh9FFefJEl3MGG4y1I+/8a833d0eedm6B+GaPm6wPVZNcnlS2YVTagvEgShVJ7ZtY66tTRQ== + dependencies: + "@mswjs/cookies" "^0.2.0" + "@mswjs/interceptors" "^0.16.3" + "@open-draft/until" "^1.0.3" + "@types/cookie" "^0.4.1" + "@types/js-levenshtein" "^1.1.1" + chalk "4.1.1" + chokidar "^3.4.2" + cookie "^0.4.2" + graphql "^16.3.0" + headers-polyfill "^3.0.4" + inquirer "^8.2.0" + is-node-process "^1.0.1" + js-levenshtein "^1.1.6" + node-fetch "^2.6.7" + outvariant "^1.3.0" + path-to-regexp "^6.2.0" + statuses "^2.0.0" + strict-event-emitter "^0.2.0" + type-fest "^1.2.2" + yargs "^17.3.1" + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +nan@^2.12.1: + version "2.16.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.16.0.tgz#664f43e45460fb98faf00edca0bb0d7b8dce7916" + integrity sha512-UdAqHyFngu7TfQKsCBgAA6pWDkT8MAO7d0jyOecVhN5354xbLqdn8mV9Tat9gepAupm0bt2DbeaSC8vS52MuFA== + +nano-time@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef" + integrity sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA== + dependencies: + big-integer "^1.6.16" + +nanoid@^3.3.1, nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz#26c8a3cee6cc05fbcf1e333cd2fc3e003326c0b5" + integrity sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw== + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-dir@^0.1.10: + version "0.1.17" + resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + integrity sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg== + dependencies: + minimatch "^3.0.2" + +node-fetch@^2.6.1, node-fetch@^2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" + +node-forge@^1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + +node-libs-browser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^3.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.1" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.11.0" + vm-browserify "^1.0.1" + +node-releases@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666" + integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw== + dependencies: + path-key "^2.0.0" + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + 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" + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + integrity sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg== + +nwsapi@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.1.tgz#10a9f268fbf4c461249ebcfe38e359aa36e2577c" + integrity sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg== + +object-assign@^4.0.1, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-inspect@^1.12.0, object-inspect@^1.9.0: + version "1.12.2" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" + integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== + dependencies: + isobject "^3.0.0" + +object.assign@^4.1.0, object.assign@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" + integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + has-symbols "^1.0.1" + object-keys "^1.1.1" + +object.entries@^1.1.0, object.entries@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" + integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +"object.fromentries@^2.0.0 || ^1.0.0", object.fromentries@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" + integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.2: + version "2.1.4" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37" + integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ== + dependencies: + array.prototype.reduce "^1.0.4" + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.1" + +object.hasown@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3" + integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A== + dependencies: + define-properties "^1.1.4" + es-abstract "^1.19.5" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== + dependencies: + isobject "^3.0.1" + +object.values@^1.1.0, object.values@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" + integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +objectorarray@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/objectorarray/-/objectorarray-1.0.5.tgz#2c05248bbefabd8f43ad13b41085951aac5e68a5" + integrity sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg== + +oblivious-set@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566" + integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw== + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0, onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open@^7.0.3: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== + dependencies: + is-docker "^2.0.0" + is-wsl "^2.1.1" + +open@^8.0.9, open@^8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" + integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +outvariant@^1.2.1, outvariant@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.3.0.tgz#c39723b1d2cba729c930b74bf962317a81b9b1c9" + integrity sha512-yeWM9k6UPfG/nzxdaPlJkB2p08hCg4xP6Lx99F+vP8YF7xyZVfTmJjrrNalkmzudD4WFvNLVudQikqUmF8zhVQ== + +p-all@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-all/-/p-all-2.1.0.tgz#91419be56b7dee8fe4c5db875d55e0da084244a0" + integrity sha512-HbZxz5FONzz/z2gJfk6bFca0BCiSRF8jU3yCsWOen/vR6lZjfPOu/e7L3uFzTW1i0H8TlC3vqQstEJPQL4/uLA== + dependencies: + p-map "^2.0.0" + +p-defer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" + integrity sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw== + +p-event@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" + integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== + dependencies: + p-timeout "^3.1.0" + +p-filter@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-filter/-/p-filter-2.1.0.tgz#1b1472562ae7a0f742f0f3d3d3718ea66ff9c09c" + integrity sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw== + dependencies: + p-map "^2.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2, p-limit@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + +p-map@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-3.0.0.tgz#d704d9af8a2ba684e2600d9a215983d4141a979d" + integrity sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ== + dependencies: + aggregate-error "^3.0.0" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +p-retry@^4.5.0: + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +p-timeout@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== + dependencies: + p-finally "^1.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +pako@~1.0.5: + version "1.0.11" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parallel-transform@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" + integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== + dependencies: + cyclist "^1.0.1" + inherits "^2.0.3" + readable-stream "^2.1.5" + +param-case@^3.0.3, param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8" + integrity sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ== + dependencies: + error-ex "^1.2.0" + +parse-json@^5.0.0, parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse5@6.0.1, parse5@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== + +path-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== + +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q== + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + integrity sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ== + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + +path-to-regexp@^6.2.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5" + integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== + +path-type@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" + integrity sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg== + dependencies: + graceful-fs "^4.1.2" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pbkdf2@^3.0.3: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" + integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +picocolors@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" + integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.0, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== + +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== + +pirates@^4.0.1, pirates@^4.0.4, pirates@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" + integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== + +pkg-dir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" + integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== + dependencies: + find-up "^3.0.0" + +pkg-dir@^4.1.0, pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +pkg-dir@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-5.0.0.tgz#a02d6aebe6ba133a928f74aec20bafdfe6b8e760" + integrity sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA== + dependencies: + find-up "^5.0.0" + +pnp-webpack-plugin@1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" + integrity sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg== + dependencies: + ts-pnp "^1.1.6" + +polished@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/polished/-/polished-4.2.2.tgz#2529bb7c3198945373c52e34618c8fe7b1aa84d1" + integrity sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ== + dependencies: + "@babel/runtime" "^7.17.8" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== + +postcss-flexbugs-fixes@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.2.1.tgz#9218a65249f30897deab1033aced8578562a6690" + integrity sha512-9SiofaZ9CWpQWxOwRh1b/r85KD5y7GgvsNt1056k6OYLvWUun0czCvogfJgylC22uJTwW1KzY3Gz65NZRlvoiQ== + dependencies: + postcss "^7.0.26" + +postcss-loader@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.3.0.tgz#2c4de9657cd4f07af5ab42bd60a673004da1b8cc" + integrity sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q== + dependencies: + cosmiconfig "^7.0.0" + klona "^2.0.4" + loader-utils "^2.0.0" + schema-utils "^3.0.0" + semver "^7.3.4" + +postcss-modules-extract-imports@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" + integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== + dependencies: + postcss "^7.0.5" + +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" + integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== + +postcss-modules-local-by-default@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" + integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== + dependencies: + icss-utils "^4.1.1" + postcss "^7.0.32" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-local-by-default@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c" + integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ== + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" + integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== + dependencies: + postcss "^7.0.6" + postcss-selector-parser "^6.0.0" + +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" + integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" + integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== + dependencies: + icss-utils "^4.0.0" + postcss "^7.0.6" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c" + integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ== + dependencies: + icss-utils "^5.0.0" + +postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: + version "6.0.10" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" + integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== + +postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.36, postcss@^7.0.5, postcss@^7.0.6: + version "7.0.39" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + +postcss@^8.2.15, postcss@^8.4.7: + version "8.4.14" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" + integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +"prettier@>=2.2.1 <=2.3.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" + integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== + +prettier@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + +pretty-error@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6" + integrity sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw== + dependencies: + lodash "^4.17.20" + renderkid "^2.0.4" + +pretty-error@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6" + integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw== + dependencies: + lodash "^4.17.20" + renderkid "^3.0.0" + +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +pretty-format@^28.0.0, pretty-format@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" + integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== + dependencies: + "@jest/schemas" "^28.1.3" + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +pretty-hrtime@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" + integrity sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A== + +prismjs@^1.27.0: + version "1.28.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.28.0.tgz#0d8f561fa0f7cf6ebca901747828b149147044b6" + integrity sha512-8aaXdYvl1F7iC7Xm1spqSaY/OJBpYW3v+KJ+F17iYxvdc8sfjW194COK5wVhMZX45tGteiBQgdvD/nhxcRwylw== + +prismjs@~1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057" + integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== + +promise.allsettled@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.5.tgz#2443f3d4b2aa8dfa560f6ac2aa6c4ea999d75f53" + integrity sha512-tVDqeZPoBC0SlzJHzWGZ2NKAguVq2oiYj7gbggbiTvH2itHohijTp7njOUA0aQ/nl+0lr/r6egmhoYu63UZ/pQ== + dependencies: + array.prototype.map "^1.0.4" + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + iterate-value "^1.0.2" + +promise.prototype.finally@^3.1.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/promise.prototype.finally/-/promise.prototype.finally-3.1.3.tgz#d3186e58fcf4df1682a150f934ccc27b7893389c" + integrity sha512-EXRF3fC9/0gz4qkt/f5EP5iW4kj9oFpBICNpCNOb/52+8nlHIX07FPLbi/q4qYBQ1xZqivMzTpNQSnArVASolQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +prompts@^2.0.1, prompts@^2.4.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" + integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prop-types@^15.0.0, prop-types@^15.7.2, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +property-information@^5.0.0, property-information@^5.3.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" + integrity sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA== + dependencies: + xtend "^4.0.0" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== + +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" + integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== + +punycode@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== + +punycode@^2.1.0, punycode@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@6.10.3: + version "6.10.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" + integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== + dependencies: + side-channel "^1.0.4" + +qs@^6.10.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== + +querystring@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" + integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +ramda@^0.28.0: + version "0.28.0" + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.28.0.tgz#acd785690100337e8b063cab3470019be427cc97" + integrity sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +raw-loader@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" + integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +react-docgen-typescript@^2.1.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz#4611055e569edc071204aadb20e1c93e1ab1659c" + integrity sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg== + +react-docgen@^5.0.0: + version "5.4.3" + resolved "https://registry.yarnpkg.com/react-docgen/-/react-docgen-5.4.3.tgz#7d297f73b977d0c7611402e5fc2a168acf332b26" + integrity sha512-xlLJyOlnfr8lLEEeaDZ+X2J/KJoe6Nr9AzxnkdQWush5hz2ZSu66w6iLMOScMmxoSHWpWMn+k3v5ZiyCfcWsOA== + dependencies: + "@babel/core" "^7.7.5" + "@babel/generator" "^7.12.11" + "@babel/runtime" "^7.7.6" + ast-types "^0.14.2" + commander "^2.19.0" + doctrine "^3.0.0" + estree-to-babel "^3.1.0" + neo-async "^2.6.1" + node-dir "^0.1.10" + strip-indent "^3.0.0" + +react-dom@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" + integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== + dependencies: + loose-envify "^1.1.0" + scheduler "^0.23.0" + +react-element-to-jsx-string@^14.3.4: + version "14.3.4" + resolved "https://registry.yarnpkg.com/react-element-to-jsx-string/-/react-element-to-jsx-string-14.3.4.tgz#709125bc72f06800b68f9f4db485f2c7d31218a8" + integrity sha512-t4ZwvV6vwNxzujDQ+37bspnLwA4JlgUPWhLjBJWsNIDceAf6ZKUTCjdm08cN6WeZ5pTMKiCJkmAYnpmR4Bm+dg== + dependencies: + "@base2/pretty-print-object" "1.0.1" + is-plain-object "5.0.0" + react-is "17.0.2" + +react-icons@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.4.0.tgz#a13a8a20c254854e1ec9aecef28a95cdf24ef703" + integrity sha512-fSbvHeVYo/B5/L4VhB7sBA1i2tS8MkT0Hb9t2H1AVPkwGfVHLJCqyr2Py9dKMxsyM63Eng1GkdZfbWj+Fmv8Rg== + +react-inspector@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-5.1.1.tgz#58476c78fde05d5055646ed8ec02030af42953c8" + integrity sha512-GURDaYzoLbW8pMGXwYPDBIv6nqei4kK7LPRZ9q9HCZF54wqXz/dnylBp/kfE9XmekBhHvLDdcYeyIwSrvtOiWg== + dependencies: + "@babel/runtime" "^7.0.0" + is-dom "^1.0.0" + prop-types "^15.0.0" + +react-is@17.0.2, react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +react-is@^16.13.1, react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + +react-merge-refs@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/react-merge-refs/-/react-merge-refs-1.1.0.tgz#73d88b892c6c68cbb7a66e0800faa374f4c38b06" + integrity sha512-alTKsjEL0dKH/ru1Iyn7vliS2QRcBp9zZPGoWxUOvRGWPUYgjo+V01is7p04It6KhgrzhJGnIj9GgX8W4bZoCQ== + +react-query@^3.39.1: + version "3.39.1" + resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.1.tgz#3876c0fdac7a3b5a84e195534e5fa8fbdd628847" + integrity sha512-qYKT1bavdDiQZbngWZyPotlBVzcBjDYEJg5RQLBa++5Ix5jjfbEYJmHSZRZD+USVHUSvl/ey9Hu+QfF1QAK80A== + dependencies: + "@babel/runtime" "^7.5.5" + broadcast-channel "^3.4.1" + match-sorter "^6.0.2" + +react-refresh@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046" + integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== + +react-router-dom@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d" + integrity sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw== + dependencies: + history "^5.2.0" + react-router "6.3.0" + +react-router@6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557" + integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ== + dependencies: + history "^5.2.0" + +react-syntax-highlighter@^15.4.5: + version "15.5.0" + resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-15.5.0.tgz#4b3eccc2325fa2ec8eff1e2d6c18fa4a9e07ab20" + integrity sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg== + dependencies: + "@babel/runtime" "^7.3.1" + highlight.js "^10.4.1" + lowlight "^1.17.0" + prismjs "^1.27.0" + refractor "^3.6.0" + +react@^18.2.0: + version "18.2.0" + resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" + integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== + dependencies: + loose-envify "^1.1.0" + +read-pkg-up@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" + integrity sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A== + dependencies: + find-up "^1.0.0" + read-pkg "^1.0.0" + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +read-pkg@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" + integrity sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ== + dependencies: + load-json-file "^1.0.0" + normalize-package-data "^2.3.2" + path-type "^1.0.0" + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6, readable-stream@^3.4.0, readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686" + integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== + dependencies: + resolve "^1.9.0" + +recoil@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/recoil/-/recoil-0.7.4.tgz#d6508fa656d9c93e66fdf334e1f723a9e98801cf" + integrity sha512-sCXvQGMfSprkNU4ZRkJV4B0qFQSURJMgsICqY1952WRlg66NMwYqi6n67vhnhn0qw4zHU1gHXJuMvRDaiRNFZw== + dependencies: + hamt_plus "1.0.2" + +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g== + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + +refractor@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/refractor/-/refractor-3.6.0.tgz#ac318f5a0715ead790fcfb0c71f4dd83d977935a" + integrity sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA== + dependencies: + hastscript "^6.0.0" + parse-entities "^2.0.0" + prismjs "~1.27.0" + +regenerate-unicode-properties@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56" + integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw== + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" + integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== + +regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.7: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + +regenerator-transform@^0.15.0: + version "0.15.0" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" + integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg== + dependencies: + "@babel/runtime" "^7.8.4" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" + integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + functions-have-names "^1.2.2" + +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +regexpu-core@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.1.0.tgz#2f8504c3fd0ebe11215783a41541e21c79942c6d" + integrity sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA== + dependencies: + regenerate "^1.4.2" + regenerate-unicode-properties "^10.0.1" + regjsgen "^0.6.0" + regjsparser "^0.8.2" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.0.0" + +regjsgen@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" + integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA== + +regjsparser@^0.8.2: + version "0.8.4" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f" + integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA== + dependencies: + jsesc "~0.5.0" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + +remark-external-links@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/remark-external-links/-/remark-external-links-8.0.0.tgz#308de69482958b5d1cd3692bc9b725ce0240f345" + integrity sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA== + dependencies: + extend "^3.0.0" + is-absolute-url "^3.0.0" + mdast-util-definitions "^4.0.0" + space-separated-tokens "^1.0.0" + unist-util-visit "^2.0.0" + +remark-footnotes@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/remark-footnotes/-/remark-footnotes-2.0.0.tgz#9001c4c2ffebba55695d2dd80ffb8b82f7e6303f" + integrity sha512-3Clt8ZMH75Ayjp9q4CorNeyjwIxHFcTkaektplKGl2A1jNGEUey8cKL0ZC5vJwfcD5GFGsNLImLG/NGzWIzoMQ== + +remark-mdx@1.6.22: + version "1.6.22" + resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-1.6.22.tgz#06a8dab07dcfdd57f3373af7f86bd0e992108bbd" + integrity sha512-phMHBJgeV76uyFkH4rvzCftLfKCr2RZuF+/gmVcaKrpsihyzmhXjA0BEMDaPTXG5y8qZOKPVo83NAOX01LPnOQ== + dependencies: + "@babel/core" "7.12.9" + "@babel/helper-plugin-utils" "7.10.4" + "@babel/plugin-proposal-object-rest-spread" "7.12.1" + "@babel/plugin-syntax-jsx" "7.12.1" + "@mdx-js/util" "1.6.22" + is-alphabetical "1.0.4" + remark-parse "8.0.3" + unified "9.2.0" + +remark-parse@8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-8.0.3.tgz#9c62aa3b35b79a486454c690472906075f40c7e1" + integrity sha512-E1K9+QLGgggHxCQtLt++uXltxEprmWzNfg+MxpfHsZlrddKzZ/hZyWHDbK3/Ap8HJQqYJRXP+jHczdL6q6i85Q== + dependencies: + ccount "^1.0.0" + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^2.0.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^2.0.0" + vfile-location "^3.0.0" + xtend "^4.0.1" + +remark-slug@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/remark-slug/-/remark-slug-6.1.0.tgz#0503268d5f0c4ecb1f33315c00465ccdd97923ce" + integrity sha512-oGCxDF9deA8phWvxFuyr3oSJsdyUAxMFbA0mZ7Y1Sas+emILtO+e5WutF9564gDsEN4IXaQXm5pFo6MLH+YmwQ== + dependencies: + github-slugger "^1.0.0" + mdast-util-to-string "^1.0.0" + unist-util-visit "^2.0.0" + +remark-squeeze-paragraphs@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/remark-squeeze-paragraphs/-/remark-squeeze-paragraphs-4.0.0.tgz#76eb0e085295131c84748c8e43810159c5653ead" + integrity sha512-8qRqmL9F4nuLPIgl92XUuxI3pFxize+F1H0e/W3llTk0UsjJaj01+RrirkMw7P21RKe4X6goQhYRSvNWX+70Rw== + dependencies: + mdast-squeeze-paragraphs "^4.0.0" + +remove-accents@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" + integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA== + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== + +renderkid@^2.0.4: + version "2.0.7" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.7.tgz#464f276a6bdcee606f4a15993f9b29fc74ca8609" + integrity sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^3.0.1" + +renderkid@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a" + integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg== + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^6.0.1" + +repeat-element@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" + integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== + +repeat-string@^1.5.4, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A== + dependencies: + is-finite "^1.0.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== + +resolve.exports@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" + integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== + +resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.3.2, resolve@^1.9.0: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.3: + version "2.0.0-next.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" + integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +rimraf@^2.5.4, rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rsvp@^4.8.4: + version "4.8.5" + resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" + integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== + +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + integrity sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg== + dependencies: + aproba "^1.1.1" + +rxjs@^7.5.5: + version "7.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" + integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== + dependencies: + tslib "^2.1.0" + +safe-buffer@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg== + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sane@^4.0.3: + version "4.1.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" + integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA== + dependencies: + "@cnakazawa/watch" "^1.0.3" + anymatch "^2.0.0" + capture-exit "^2.0.0" + exec-sh "^0.3.2" + execa "^1.0.0" + fb-watchman "^2.0.0" + micromatch "^3.1.4" + minimist "^1.1.1" + walker "~1.0.5" + +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + +scheduler@^0.23.0: + version "0.23.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" + integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== + dependencies: + loose-envify "^1.1.0" + +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== + dependencies: + ajv "^6.1.0" + ajv-errors "^1.0.0" + ajv-keywords "^3.1.0" + +schema-utils@^2.6.5, schema-utils@^2.7.0: + version "2.7.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" + integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + +schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" + integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7" + integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.8.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.0.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.0.1.tgz#8b2df7fa56bf014d19b6007655fff209c0ef0a56" + integrity sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ== + dependencies: + node-forge "^1" + +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +semver@7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" + integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== + +semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== + +semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: + version "7.3.7" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" + integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== + dependencies: + lru-cache "^6.0.0" + +send@0.18.0: + version "0.18.0" + resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +serve-favicon@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/serve-favicon/-/serve-favicon-2.5.0.tgz#935d240cdfe0f5805307fdfe967d88942a2cbcf0" + integrity sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA== + dependencies: + etag "~1.8.1" + fresh "0.5.2" + ms "2.1.1" + parseurl "~1.3.2" + safe-buffer "5.1.1" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + +set-cookie-parser@^2.4.6: + version "2.5.0" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.5.0.tgz#96b59525e1362c94335c3c761100bb6e8f2da4b0" + integrity sha512-cHMAtSXilfyBePduZEBVPTCftTQWz6ehWJD5YNUg4mqvRosrrjKbo4WS8JkB0/RxonMoohHm7cOGH60mDkRQ9w== + +set-value@^2.0.0, set-value@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" + integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" + integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== + +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-resolve@^0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" + integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@0.5.13: + version "0.5.13" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" + integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-support@^0.5.16, source-map-support@~0.5.12, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map-url@^0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" + integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== + +source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== + +source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +source-map@^0.7.3: + version "0.7.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" + integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== + +space-separated-tokens@^1.0.0: + version "1.1.5" + resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" + integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== + +spdx-correct@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" + integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.11" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" + integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +ssri@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5" + integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q== + dependencies: + figgy-pudding "^3.5.1" + +ssri@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== + dependencies: + minipass "^3.1.1" + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" + integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== + +stack-utils@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" + integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== + dependencies: + escape-string-regexp "^2.0.0" + +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" + integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== + +state-toggle@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" + integrity sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ== + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +statuses@2.0.1, statuses@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +store2@^2.12.0: + version "2.13.2" + resolved "https://registry.yarnpkg.com/store2/-/store2-2.13.2.tgz#01ad8802ca5b445b9c316b55e72645c13a3cd7e3" + integrity sha512-CMtO2Uneg3SAz/d6fZ/6qbqqQHi2ynq6/KzMD/26gTkiEShCcpqFfTHgOxsE0egAq6SX3FmN4CeSqn8BzXQkJg== + +stream-browserify@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-each@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" + integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== + +strict-event-emitter@^0.2.0, strict-event-emitter@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.2.4.tgz#365714f0c95f059db31064ca745d5b33e5b30f6e" + integrity sha512-xIqTLS5azUH1djSUsLH9DbP6UnM/nI18vu8d43JigCQEoVsnY+mrlE+qv6kYqs6/1OkMnMIiL6ffedQSZStuoQ== + dependencies: + events "^3.3.0" + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" + integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string.prototype.matchall@^4.0.0 || ^3.0.1", string.prototype.matchall@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" + integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + get-intrinsic "^1.1.1" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + regexp.prototype.flags "^1.4.1" + side-channel "^1.0.4" + +string.prototype.padend@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz#997a6de12c92c7cb34dc8a201a6c53d9bd88a5f1" + integrity sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +string.prototype.padstart@^3.0.0: + version "3.1.3" + resolved "https://registry.yarnpkg.com/string.prototype.padstart/-/string.prototype.padstart-3.1.3.tgz#4551d0117d9501692ec6000b15056ac3f816cfa5" + integrity sha512-NZydyOMtYxpTjGqp0VN5PYUF/tsU15yDMZnUdj16qRUIUiMJkHHSDElYyQFrMu+/WloTpA7MQSiADhBicDfaoA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.1" + +string.prototype.trimend@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" + integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + +string.prototype.trimstart@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" + integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.19.5" + +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" + integrity sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g== + dependencies: + is-utf8 "^0.2.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA== + dependencies: + get-stdin "^4.0.1" + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +style-loader@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.3.0.tgz#828b4a3b3b7e7aa5847ce7bae9e874512114249e" + integrity sha512-V7TCORko8rs9rIqkSrlMfkqA63DfoGBBJmK1kKGCcSi+BWb4cqz0SRsnp4l6rU5iwOEd0/2ePv68SV22VXon4Q== + dependencies: + loader-utils "^2.0.0" + schema-utils "^2.7.0" + +style-loader@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c" + integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ== + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +style-loader@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575" + integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ== + +style-to-object@0.3.0, style-to-object@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" + integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA== + dependencies: + inline-style-parser "0.1.1" + +stylis@4.0.13: + version "4.0.13" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91" + integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb" + integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +symbol.prototype.description@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/symbol.prototype.description/-/symbol.prototype.description-1.0.5.tgz#d30e01263b6020fbbd2d2884a6276ce4d49ab568" + integrity sha512-x738iXRYsrAt9WBhRCVG5BtIC3B7CUkFwbHW2zOvGtwM33s7JjrCDyq8V0zgMYVb5ymsL8+qkzzpANH63CPQaQ== + dependencies: + call-bind "^1.0.2" + get-symbol-description "^1.0.0" + has-symbols "^1.0.2" + object.getownpropertydescriptors "^2.1.2" + +synchronous-promise@^2.0.15: + version "2.0.15" + resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.15.tgz#07ca1822b9de0001f5ff73595f3d08c4f720eb8e" + integrity sha512-k8uzYIkIVwmT+TcglpdN50pS2y1BDcUnBPK9iJeGu0Pl1lOI8pD6wtzgw91Pjpe+RxtTncw32tLxs/R0yNL2Mg== + +tapable@^1.0.0, tapable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + +tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +tar@^6.0.2: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +telejson@^6.0.8: + version "6.0.8" + resolved "https://registry.yarnpkg.com/telejson/-/telejson-6.0.8.tgz#1c432db7e7a9212c1fbd941c3e5174ec385148f7" + integrity sha512-nerNXi+j8NK1QEfBHtZUN/aLdDcyupA//9kAboYLrtzZlPLpUfqbVGWb9zz91f/mIjRbAYhbgtnJHY8I1b5MBg== + dependencies: + "@types/is-function" "^1.0.0" + global "^4.4.0" + is-function "^1.0.2" + is-regex "^1.1.2" + is-symbol "^1.0.3" + isobject "^4.0.0" + lodash "^4.17.21" + memoizerific "^1.11.3" + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +terser-webpack-plugin@^1.4.3: + version "1.4.5" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" + integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== + dependencies: + cacache "^12.0.2" + find-cache-dir "^2.1.0" + is-wsl "^1.1.0" + schema-utils "^1.0.0" + serialize-javascript "^4.0.0" + source-map "^0.6.1" + terser "^4.1.2" + webpack-sources "^1.4.0" + worker-farm "^1.7.0" + +terser-webpack-plugin@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz#28daef4a83bd17c1db0297070adc07fc8cfc6a9a" + integrity sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ== + dependencies: + cacache "^15.0.5" + find-cache-dir "^3.3.1" + jest-worker "^26.5.0" + p-limit "^3.0.2" + schema-utils "^3.0.0" + serialize-javascript "^5.0.1" + source-map "^0.6.1" + terser "^5.3.4" + webpack-sources "^1.4.3" + +terser-webpack-plugin@^5.0.3, terser-webpack-plugin@^5.1.3: + version "5.3.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz#8033db876dd5875487213e87c627bca323e5ed90" + integrity sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ== + dependencies: + "@jridgewell/trace-mapping" "^0.3.7" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.0" + terser "^5.7.2" + +terser@^4.1.2, terser@^4.6.3: + version "4.8.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" + integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + +terser@^5.10.0, terser@^5.3.4, terser@^5.7.2: + version "5.14.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.1.tgz#7c95eec36436cb11cf1902cc79ac564741d19eca" + integrity sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ== + dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +through2@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + +timers-browserify@^2.0.4: + version "2.0.12" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" + integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== + dependencies: + setimmediate "^1.0.4" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA== + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +tough-cookie@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.1.2" + +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== + dependencies: + punycode "^2.1.1" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw== + +trim-trailing-lines@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" + integrity sha512-rjUWSqnfTNrjbB9NQWfPMH/xRK1deHeGsHoVfpxJ++XeYXE0d6B1En37AHfw3jtfTU7dzMzZL2jjpe8Qb5gLIQ== + +trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + integrity sha512-YzQV+TZg4AxpKxaTHK3c3D+kRDCGVEE7LemdlQZoQXn0iennk10RsIoY6ikzAqJTc9Xjl9C1/waHom/J86ziAQ== + +trough@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" + integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== + +ts-dedent@^2.0.0, ts-dedent@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" + integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== + +ts-loader@^9.3.1: + version "9.3.1" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.3.1.tgz#fe25cca56e3e71c1087fe48dc67f4df8c59b22d4" + integrity sha512-OkyShkcZTsTwyS3Kt7a4rsT/t2qvEVQuKCTg4LJmpj9fhFR7ukGdZwV6Qq3tRUkqcXtfGpPR7+hFKHCG/0d3Lw== + dependencies: + chalk "^4.1.0" + enhanced-resolve "^5.0.0" + micromatch "^4.0.0" + semver "^7.3.4" + +ts-pnp@^1.1.6: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" + integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== + +tsconfig-paths@^3.14.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" + integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.1" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^1.8.1, tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-fest@^1.2.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== + +typescript@^4.7.4: + version "4.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== + +uglify-js@^3.1.4: + version "3.16.2" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.16.2.tgz#0481e1dbeed343ad1c2ddf3c6d42e89b7a6d4def" + integrity sha512-AaQNokTNgExWrkEYA24BTNMSjyqEXPSfhqoS0AxmHkCJ4U+Dyy5AvbGV/sqxuxficEfGGoX3zWw9R7QpLFfEsg== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +unfetch@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" + integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== + +unherit@^1.0.4: + version "1.1.3" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.3.tgz#6c9b503f2b41b262330c80e91c8614abdaa69c22" + integrity sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ== + dependencies: + inherits "^2.0.0" + xtend "^4.0.0" + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" + integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" + integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" + integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" + integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ== + +unified@9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-9.2.0.tgz#67a62c627c40589edebbf60f53edfd4d822027f8" + integrity sha512-vx2Z0vY+a3YoTj8+pttM3tiJHCwY5UFbYdiWrwBEbHmK8pvsPj2rtAX2BFfgXen8T39CJWblWRDT4L5WGXtDdg== + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-buffer "^2.0.0" + is-plain-obj "^2.0.0" + trough "^1.0.0" + vfile "^4.0.0" + +union-value@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" + integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^2.0.1" + +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" + +unist-builder@2.0.3, unist-builder@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-2.0.3.tgz#77648711b5d86af0942f334397a33c5e91516436" + integrity sha512-f98yt5pnlMWlzP539tPc4grGMsFaQQlP/vM396b00jngsiINumNmsY8rkXjfoi1c6QaM8nQ3vaGDuoKWbe/1Uw== + +unist-util-generated@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-1.1.6.tgz#5ab51f689e2992a472beb1b35f2ce7ff2f324d4b" + integrity sha512-cln2Mm1/CZzN5ttGK7vkoGw+RZ8VcUH6BtGbq98DDtRGquAAOXig1mrBQYelOwMXYS8rK+vZDyyojSjp7JX+Lg== + +unist-util-is@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-4.1.0.tgz#976e5f462a7a5de73d94b706bac1b90671b57797" + integrity sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg== + +unist-util-position@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-3.1.0.tgz#1c42ee6301f8d52f47d14f62bbdb796571fa2d47" + integrity sha512-w+PkwCbYSFw8vpgWD0v7zRCl1FpY3fjDSQ3/N/wNd9Ffa4gPi8+4keqt99N3XW6F99t/mUzp2xAhNmfKWp95QA== + +unist-util-remove-position@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz#5d19ca79fdba712301999b2b73553ca8f3b352cc" + integrity sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA== + dependencies: + unist-util-visit "^2.0.0" + +unist-util-remove@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unist-util-remove/-/unist-util-remove-2.1.0.tgz#b0b4738aa7ee445c402fda9328d604a02d010588" + integrity sha512-J8NYPyBm4baYLdCbjmf1bhPu45Cr1MWTm77qd9istEkzWpnN6O9tMsEbB2JhNnBCqGENRqEWomQ+He6au0B27Q== + dependencies: + unist-util-is "^4.0.0" + +unist-util-stringify-position@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz#cce3bfa1cdf85ba7375d1d5b17bdc4cada9bd9da" + integrity sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g== + dependencies: + "@types/unist" "^2.0.2" + +unist-util-visit-parents@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz#65a6ce698f78a6b0f56aa0e88f13801886cdaef6" + integrity sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + +unist-util-visit@2.0.3, unist-util-visit@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-2.0.3.tgz#c3703893146df47203bb8a9795af47d7b971208c" + integrity sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q== + dependencies: + "@types/unist" "^2.0.0" + unist-util-is "^4.0.0" + unist-util-visit-parents "^3.0.0" + +universalify@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +unload@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7" + integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA== + dependencies: + "@babel/runtime" "^7.6.2" + detect-node "^2.0.4" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +untildify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-2.1.0.tgz#17eb2807987f76952e9c0485fc311d06a826a2e0" + integrity sha512-sJjbDp2GodvkB0FZZcn7k6afVisqX5BZD7Yq3xp4nN2O15BBK0cLm3Vwn2vQaF7UDS0UUsrQMkkplmDI5fskig== + dependencies: + os-homedir "^1.0.0" + +upath@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" + integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== + +update-browserslist-db@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.4.tgz#dbfc5a789caa26b1db8990796c2c8ebbce304824" + integrity sha512-jnmO2BEGUjsMOe/Fg9u0oczOe/ppIDZPebzccl1yDWGLFP16Pa1/RM5wEoKYPG2zstNcDuAStejyxsOuKINdGA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== + +url-loader@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-4.1.1.tgz#28505e905cae158cf07c92ca622d7f237e70a4e2" + integrity sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== + dependencies: + loader-utils "^2.0.0" + mime-types "^2.1.27" + schema-utils "^3.0.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ== + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util.promisify@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ== + dependencies: + inherits "2.0.1" + +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== + dependencies: + inherits "2.0.3" + +utila@~0.4: + version "0.4.0" + resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" + integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +uuid-browser@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid-browser/-/uuid-browser-3.1.0.tgz#0f05a40aef74f9e5951e20efbf44b11871e56410" + integrity sha512-dsNgbLaTrd6l3MMxTtouOCFw4CBFc/3a+GgYA2YyrJvyQ1u6q4pcu3ktLoUZ/VN/Aw9WsauazbgsgdfVWgAKQg== + +uuid@^3.3.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +v8-compile-cache@^2.0.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" + integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== + +v8-to-istanbul@^9.0.0, v8-to-istanbul@^9.0.1: + version "9.0.1" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" + integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.12" + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +vfile-location@^3.0.0, vfile-location@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-3.2.0.tgz#d8e41fbcbd406063669ebf6c33d56ae8721d0f3c" + integrity sha512-aLEIZKv/oxuCDZ8lkJGhuhztf/BW4M+iHdCwglA/eWc+vtuRFJj8EtgceYFX4LRjOhCAAiNHsKGssC6onJ+jbA== + +vfile-message@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-2.0.4.tgz#5b43b88171d409eae58477d13f23dd41d52c371a" + integrity sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ== + dependencies: + "@types/unist" "^2.0.0" + unist-util-stringify-position "^2.0.0" + +vfile@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-4.2.1.tgz#03f1dce28fc625c625bc6514350fbdb00fa9e624" + integrity sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA== + dependencies: + "@types/unist" "^2.0.0" + is-buffer "^2.0.0" + unist-util-stringify-position "^2.0.0" + vfile-message "^2.0.0" + +vm-browserify@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923" + integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg== + dependencies: + xml-name-validator "^4.0.0" + +walker@^1.0.7, walker@^1.0.8, walker@~1.0.5: + version "1.0.8" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" + integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== + dependencies: + makeerror "1.0.12" + +watchpack-chokidar2@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" + integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== + dependencies: + chokidar "^2.1.8" + +watchpack@^1.7.4: + version "1.7.5" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" + integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== + dependencies: + graceful-fs "^4.1.2" + neo-async "^2.5.0" + optionalDependencies: + chokidar "^3.4.1" + watchpack-chokidar2 "^2.0.1" + +watchpack@^2.2.0, watchpack@^2.3.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + +web-namespaces@^1.0.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" + integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + +webpack-cli@^4.10.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" + integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^1.2.0" + "@webpack-cli/info" "^1.5.0" + "@webpack-cli/serve" "^1.7.0" + colorette "^2.0.14" + commander "^7.0.0" + cross-spawn "^7.0.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^2.2.0" + rechoir "^0.7.0" + webpack-merge "^5.7.3" + +webpack-dev-middleware@^3.7.3: + version "3.7.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" + integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ== + dependencies: + memory-fs "^0.4.1" + mime "^2.4.4" + mkdirp "^0.5.1" + range-parser "^1.2.1" + webpack-log "^2.0.0" + +webpack-dev-middleware@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-4.3.0.tgz#179cc40795882cae510b1aa7f3710cbe93c9333e" + integrity sha512-PjwyVY95/bhBh6VUqt6z4THplYcsvQ8YNNBTBM873xLVmw8FLeALn0qurHbs9EmcfhzQis/eoqypSnZeuUz26w== + dependencies: + colorette "^1.2.2" + mem "^8.1.1" + memfs "^3.2.2" + mime-types "^2.1.30" + range-parser "^1.2.1" + schema-utils "^3.0.0" + +webpack-dev-middleware@^5.3.1: + version "5.3.3" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" + integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== + dependencies: + colorette "^2.0.10" + memfs "^3.4.3" + mime-types "^2.1.31" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@^4.9.3: + version "4.9.3" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.9.3.tgz#2360a5d6d532acb5410a668417ad549ee3b8a3c9" + integrity sha512-3qp/eoboZG5/6QgiZ3llN8TUzkSpYg1Ko9khWX1h40MIEUNS2mDoIa8aXsPfskER+GbTvs/IJZ1QTBBhhuetSw== + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.1" + ansi-html-community "^0.0.8" + bonjour-service "^1.0.11" + chokidar "^3.5.3" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.3.2" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.0.1" + open "^8.0.9" + p-retry "^4.5.0" + rimraf "^3.0.2" + schema-utils "^4.0.0" + selfsigned "^2.0.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^5.3.1" + ws "^8.4.2" + +webpack-filter-warnings-plugin@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/webpack-filter-warnings-plugin/-/webpack-filter-warnings-plugin-1.2.1.tgz#dc61521cf4f9b4a336fbc89108a75ae1da951cdb" + integrity sha512-Ez6ytc9IseDMLPo0qCuNNYzgtUl8NovOqjIq4uAU8LTD4uoa1w1KpZyyzFtLTEMZpkkOkLfL9eN+KGYdk1Qtwg== + +webpack-hot-middleware@^2.25.1: + version "2.25.1" + resolved "https://registry.yarnpkg.com/webpack-hot-middleware/-/webpack-hot-middleware-2.25.1.tgz#581f59edf0781743f4ca4c200fd32c9266c6cf7c" + integrity sha512-Koh0KyU/RPYwel/khxbsDz9ibDivmUbrRuKSSQvW42KSDdO4w23WI3SkHpSUKHE76LrFnnM/L7JCrpBwu8AXYw== + dependencies: + ansi-html-community "0.0.8" + html-entities "^2.1.0" + querystring "^0.2.0" + strip-ansi "^6.0.0" + +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== + dependencies: + ansi-colors "^3.0.0" + uuid "^3.3.2" + +webpack-merge@^5.7.3: + version "5.8.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61" + integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q== + dependencies: + clone-deep "^4.0.1" + wildcard "^2.0.0" + +webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack-virtual-modules@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.2.2.tgz#20863dc3cb6bb2104729fff951fbe14b18bd0299" + integrity sha512-kDUmfm3BZrei0y+1NTHJInejzxfhtU8eDj2M7OKb2IWrPFAeO1SOH2KuQ68MSZu9IGEHcxbkKKR1v18FrUSOmA== + dependencies: + debug "^3.0.0" + +webpack-virtual-modules@^0.4.1: + version "0.4.4" + resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.4.4.tgz#a19fcf371923c59c4712d63d7d194b1e4d8262cc" + integrity sha512-h9atBP/bsZohWpHnr+2sic8Iecb60GxftXsWNLLLSqewgIsGzByd2gcIID4nXcG+3tNe4GQG3dLcff3kXupdRA== + +webpack@4: + version "4.46.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" + integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/wasm-edit" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + acorn "^6.4.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.5.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.3" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.7.4" + webpack-sources "^1.4.1" + +"webpack@>=4.43.0 <6.0.0", webpack@^5.73.0, webpack@^5.9.0: + version "5.73.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38" + integrity sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^0.0.51" + "@webassemblyjs/ast" "1.11.1" + "@webassemblyjs/wasm-edit" "1.11.1" + "@webassemblyjs/wasm-parser" "1.11.1" + acorn "^8.4.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.9.3" + es-module-lexer "^0.9.0" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.1.3" + watchpack "^2.3.1" + webpack-sources "^3.2.3" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da" + integrity sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + +whatwg-url@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" + integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + +wildcard@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" + integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== + +word-wrap@^1.2.3, word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wordwrap@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== + +worker-farm@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" + integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== + dependencies: + errno "~0.1.7" + +worker-rpc@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/worker-rpc/-/worker-rpc-0.1.1.tgz#cb565bd6d7071a8f16660686051e969ad32f54d5" + integrity sha512-P1WjMrUB3qgJNI9jfmpZ/htmBEjFh//6l/5y8SD9hg1Ef5zTTVVoRjTrTEzPrNBQvmhMxkoTsjOXN10GWU7aCg== + dependencies: + microevent.ts "~0.1.1" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +write-file-atomic@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" + integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== + dependencies: + imurmurhash "^0.1.4" + signal-exit "^3.0.7" + +ws@^8.2.3, ws@^8.4.2: + version "8.8.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.0.tgz#8e71c75e2f6348dbf8d78005107297056cb77769" + integrity sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ== + +x-default-browser@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/x-default-browser/-/x-default-browser-0.4.0.tgz#70cf0da85da7c0ab5cb0f15a897f2322a6bdd481" + integrity sha512-7LKo7RtWfoFN/rHx1UELv/2zHGMx8MkZKDq1xENmOCTkfIqZJ0zZ26NEJX8czhnPXVcqS0ARjjfJB+eJ0/5Cvw== + optionalDependencies: + default-browser-id "^1.0.4" + +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +y18n@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" + integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" + integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@^1.10.0, yaml@^1.7.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + +yargs-parser@^20.2.2, yargs-parser@^20.2.9: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@^21.0.0: + version "21.0.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" + integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yargs@^17.3.1: + version "17.5.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e" + integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.0.0" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zwitch@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" + integrity sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw== diff --git a/jenkins/backend-dev.jenkinsfile b/jenkins/backend-dev.jenkinsfile new file mode 100644 index 00000000..19967532 --- /dev/null +++ b/jenkins/backend-dev.jenkinsfile @@ -0,0 +1,64 @@ +pipeline { + agent any + stages { + stage('Github') { + steps { + git branch: 'develop', url: 'https://github.com/woowacourse-teams/2022-dallog.git' + withCredentials([GitUsernamePassword(credentialsId: 'github-access-token', gitToolName: 'Default')]) { + sh 'git submodule update --init --recursive' + } + } + } + stage('SonarQube analysis') { + steps { + dir('backend') { + withSonarQubeEnv('SonarServer') { + sh './gradlew test sonarqube' + } + } + } + } + stage("Quality Gate") { + steps { + timeout(time: 1, unit: 'HOURS') { + waitForQualityGate abortPipeline: true + } + } + } + stage('Build') { + steps { + dir('backend') { + sh "./gradlew bootJar" + } + } + } + stage('Deploy') { + steps { + dir('backend/build/libs') { + sshagent(credentials: ['key-dallog']) { + sh "scp -o StrictHostKeyChecking=no backend-0.0.1-SNAPSHOT.jar ubuntu@${env.BACKEND_DEV_IP}:/home/ubuntu" + sh "ssh -o StrictHostKeyChecking=no ubuntu@${env.BACKEND_DEV_IP} 'sh run.sh' &" + } + } + } + } + } + post { + success { + discordSend title: "백엔드 개발 서버 배포에 성공하였습니다 ✨", + description: "빌드 번호 #${env.BUILD_NUMBER}", + link: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}", + result: currentBuild.currentResult, + webhookURL: env.DISCORD_BACKEND_DEV_WEBHOOK, + footer: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}" + } + failure { + discordSend title: "백엔드 개발 서버 배포에 실패하였습니다 ❌", + description: "빌드 번호 #${env.BUILD_NUMBER}", + link: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}", + result: currentBuild.currentResult, + webhookURL: env.DISCORD_BACKEND_DEV_WEBHOOK, + footer: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}" + } + } +} diff --git a/jenkins/backend-prod.jenkinsfile b/jenkins/backend-prod.jenkinsfile new file mode 100644 index 00000000..dc36e0b7 --- /dev/null +++ b/jenkins/backend-prod.jenkinsfile @@ -0,0 +1,48 @@ +pipeline { + agent any + stages { + stage('Github') { + steps { + git branch: 'main', url: 'https://github.com/woowacourse-teams/2022-dallog.git' + withCredentials([GitUsernamePassword(credentialsId: 'github-access-token', gitToolName: 'Default')]) { + sh 'git submodule update --init --recursive' + } + } + } + stage('Build') { + steps { + dir('backend') { + sh "./gradlew bootJar" + } + } + } + stage('Deploy') { + steps { + dir('backend/build/libs') { + sshagent(credentials: ['key-dallog']) { + sh "scp -o StrictHostKeyChecking=no backend-0.0.1-SNAPSHOT.jar ubuntu@${env.BACKEND_PROD_IP}:/home/ubuntu" + sh "ssh -o StrictHostKeyChecking=no ubuntu@${env.BACKEND_PROD_IP} 'sh run.sh' &" + } + } + } + } + } + post { + success { + discordSend title: "백엔드 프로덕션 서버 배포에 성공하였습니다 ✨", + description: "빌드 번호 #${env.BUILD_NUMBER}", + link: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}", + result: currentBuild.currentResult, + webhookURL: env.DISCORD_BACKEND_PROD_WEBHOOK, + footer: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}" + } + failure { + discordSend title: "백엔드 프로덕션 서버 배포에 실패하였습니다 ❌", + description: "빌드 번호 #${env.BUILD_NUMBER}", + link: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}", + result: currentBuild.currentResult, + webhookURL: env.DISCORD_BACKEND_PROD_WEBHOOK, + footer: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}" + } + } +} diff --git a/jenkins/frontend-dev.jenkinsfile b/jenkins/frontend-dev.jenkinsfile new file mode 100644 index 00000000..aae120ed --- /dev/null +++ b/jenkins/frontend-dev.jenkinsfile @@ -0,0 +1,53 @@ +API_URL = "https://dev-api.dallog.me" + +pipeline { + agent any + + stages { + stage('Github') { + steps { + git branch: 'develop', url: 'https://github.com/woowacourse-teams/2022-dallog.git' + } + } + stage('Build') { + steps { + dir('frontend') { + sh "echo 'API_URL = ${API_URL}' > .env" + nodejs(nodeJSInstallationName: 'NodeJS 16.14.0') { + sh "npm install -g yarn" + sh "yarn" + sh "yarn prod-build" + } + } + } + } + stage('Deploy') { + steps { + dir('frontend/dist') { + sshagent(credentials: ['key-dallog']) { + sh "scp -o StrictHostKeyChecking=no -r ./* ubuntu@${env.FRONTEND_DEV_IP}:/home/ubuntu/" + sh "ssh -o StrictHostKeyChecking=no ubuntu@${env.FRONTEND_DEV_IP} 'sudo cp -r ./* /usr/share/nginx/html/ && sudo rm -rf ./*'" + } + } + } + } + } + post { + success { + discordSend title: "프론트엔드 개발 서버 배포에 성공하였습니다 ✨", + description: "빌드 번호 #${env.BUILD_NUMBER}", + link: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}", + result: currentBuild.currentResult, + webhookURL: env.DISCORD_FRONTEND_DEV_WEBHOOK, + footer: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}" + } + failure { + discordSend title: "프론트엔드 개발 서버 배포에 실패하였습니다 ❌", + description: "빌드 번호 #${env.BUILD_NUMBER}", + link: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}", + result: currentBuild.currentResult, + webhookURL: env.DISCORD_FRONTEND_DEV_WEBHOOK, + footer: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}" + } + } +} diff --git a/jenkins/frontend-prod.jenkinsfile b/jenkins/frontend-prod.jenkinsfile new file mode 100644 index 00000000..5504ac05 --- /dev/null +++ b/jenkins/frontend-prod.jenkinsfile @@ -0,0 +1,53 @@ +API_URL = "https://api.dallog.me" + +pipeline { + agent any + + stages { + stage('Github') { + steps { + git branch: 'main', url: 'https://github.com/woowacourse-teams/2022-dallog.git' + } + } + stage('Build') { + steps { + dir('frontend') { + sh "echo 'API_URL = ${API_URL}' > .env" + nodejs(nodeJSInstallationName: 'NodeJS 16.14.0') { + sh "npm install -g yarn" + sh "yarn" + sh "yarn prod-build" + } + } + } + } + stage('Deploy') { + steps { + dir('frontend/dist') { + sshagent(credentials: ['key-dallog']) { + sh "scp -o StrictHostKeyChecking=no -r ./* ubuntu@${env.FRONTEND_PROD_IP}:/home/ubuntu/" + sh "ssh -o StrictHostKeyChecking=no ubuntu@${env.FRONTEND_PROD_IP} 'sudo cp -r ./* /usr/share/nginx/html/ && sudo rm -rf ./*'" + } + } + } + } + } + post { + success { + discordSend title: "프론트엔드 프로덕션 서버 배포에 성공하였습니다 ✨", + description: "빌드 번호 #${env.BUILD_NUMBER}", + link: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}", + result: currentBuild.currentResult, + webhookURL: env.DISCORD_FRONTEND_PROD_WEBHOOK, + footer: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}" + } + failure { + discordSend title: "프론트엔드 프로덕션 서버 배포에 실패하였습니다 ❌", + description: "빌드 번호 #${env.BUILD_NUMBER}", + link: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}", + result: currentBuild.currentResult, + webhookURL: env.DISCORD_FRONTEND_PROD_WEBHOOK, + footer: "http://jenkins.dallog.me:8080/job/${env.JOB_NAME}/${env.BUILD_NUMBER}" + } + } +}