Compare commits

...
318 Commits
Author SHA1 Message Date
GitHub Actions a0706d9b51 chore: release v4.8.2
Post release / release (push) Canceled after 0s
2024-12-17 17:05:26 +00:00
Omar LópezandKeith Williams 2eb385af48 feat: allows forcing/skipping calendar cache serving (#18224)
* feat: allows forcing/skipping calendar cache serving

Signed-off-by: Omar López <zomars@me.com>

* Update features.repository.ts

* Added to HNB

* type fixes

* Update packages/prisma/migrations/20241216000000_add_calendar_cache_serve/migration.sql

Co-authored-by: Keith Williams <keithwillcode@gmail.com>

* Update packages/prisma/zod-utils.ts

Co-authored-by: Keith Williams <keithwillcode@gmail.com>

* Update selectedCalendar.ts

---------

Signed-off-by: Omar López <zomars@me.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2024-12-17 16:54:55 +00:00
Lauris Skraucis a0f986f949 feat: v2 teams CRUD (#18019)
* feat: teams endpoints

* refactor: organization teams reuse team types

* refactor: move team specific functions out of organizations-teams.repository

* supporting files

* regenerate docs

* feat: stripe service to generate team creation checkout

* refactor: request payment when creating a team

* fix: tests

* fix env

* driveby: event type name and email booking fields

* finish merge main
2024-12-17 14:19:57 +00:00
Keith Williams b0094bad2d perf: More tracing for handleNewBooking (#18233) 2024-12-17 12:29:41 +00:00
Rajiv Sahal c732c06835 fix: bookerEmbed atom (#18168)
* update translations

* add custom hook for platform embed

* update event type ouput DTO response

* fixup

* display terms and conditions for embed

* update v2 docs

* PR feedback

* update docs

* PR feedback

* update docs

* update output DTO

* update docs

* update output typing

* docs update

* resolve merge conflicts

* merge conflicts resolving

* resolve merge conflicts

* fixup

* update docs
2024-12-17 12:07:47 +00:00
Anik Dhabal Babu c1334475c5 fix: private team does not hide team members from manged event type (#18166) 2024-12-17 17:05:15 +05:30
Lauris Skraucis b35dc43011 feat: v2 wildcard redirectURIs (#18165)
* feat: isOriginAllowed function

* feat: use isOriginAllowed in v2

* refactor: shorten isOriginAllowed
2024-12-17 11:21:59 +00:00
Morgan c7dc3ecc49 chore: add TLD in username of managed users (#18229)
* chore: add TLD in username of managed users

* fixup! chore: add TLD in username of managed users

* bump platform libraries
2024-12-17 12:45:48 +02:00
Somay Chauhan a613c975d3 refactor: added ApiPropertyOptional decorator for optional fields in api for better documentation (#18208)
* update decorators 1

* update decorators 2

* update decorators 3

* update decorators 4

* update decorators 4

* update decorators 6

* update decorators 7

* added documentation,json and openapi.json

* implemented feedback

* remove default values from update dto's

* update

* removed default from redirectUris
2024-12-17 10:29:07 +00:00
Morgan 5fad7c9478 fix: redirect on calendar oauth denied auth (#18228) 2024-12-17 09:05:10 +00:00
Udit Takkar 318470d866 fix: webhook improvement (#18220) 2024-12-17 14:32:38 +05:30
Morgan a0ce42e375 chore: improve prisma exception filter messages (#18227)
* chore: improve prisma exception filter messages

* fixup! chore: improve prisma exception filter messages
2024-12-17 08:40:19 +00:00
Anik Dhabal Babu 5293a5cc54 fix: column f.userid does not exist (#18215) 2024-12-17 08:37:48 +00:00
Benny Joo 42aefc9032 perf: limit # of host members for OG image (#18219)
* fetch only 5 hosts and only first user

* refactor

* limit to 3
2024-12-17 08:15:11 +00:00
Keith Williams 14acf4e9bc chore: New booking perf tracing (#18212)
* chore: Add perf tracing for handleNewBooking

* Added a few more to the event manager

* Added the sentry wrapper that enables perf tracing

* Revert "Added a few more to the event manager"

This reverts commit ccf3ee5200466b8b1d51f5663363fbb9dfd9a06f.

* Fixed where we wrap sentry

* Reverse sentry wrapper
2024-12-17 08:51:00 +01:00
Lauris Skraucis 80815bd8a5 feat: Booker atom slots callbacks (#18203) 2024-12-17 12:58:18 +05:30
Benny Joo 9ed8face03 Revert "feat: remove setup-availability step in onboarding flow (#17799)" (#18205)
This reverts commit a466e8eef7.
2024-12-16 22:03:41 +00:00
Udit Takkar 18a107d081 fix: trigger guest no show bug (#18164)
* fix: trigger guest no show

* test: add tests for trigger guest no show
2024-12-16 11:43:03 -05:00
Alex van Andel 04d7537e2f perf: improve insights where query and type fix (#18211)
* perf: insights perf improvement

* removed additional const
2024-12-16 15:50:20 +00:00
Udit Takkar 423b17a819 refactor: getContacts (#18137) 2024-12-16 10:17:59 -05:00
GitHub Actions 0466b7ebd6 chore: release v4.8.1
Post release / release (push) Canceled after 0s
2024-12-16 14:35:27 +00:00
Lauris Skraucis 09329707c1 fix: bookings parsing errors (#18200) 2024-12-16 16:31:04 +02:00
Anik Dhabal Babu 1773aba378 fix: fix data expose issue in routing form insights (#18207) 2024-12-16 15:30:25 +01:00
Somay ChauhanandMorgan f8be2b3833 feat: added office 365 video to conferencing atoms (#18067)
* feat: added office 365 video to conferencing atoms

* added documentation for conferencing atoms

* added props table to the documentation

---------

Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2024-12-16 16:20:59 +02:00
Eunjae Leeandsean-brydon 79d488faca feat: insights filter refactoring (#18167)
* Reapply "feat: add text & numeric filters to insights routing responses (#18016)"

This reverts commit 49070b96ae.

* support SINGLE_SELECT

* fix type error

* put back ts-expect-error

* remove playground

---------

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2024-12-16 13:47:28 +00:00
Venkata Rohan KambhampatiandUdit Takkar da2c57c302 fix: Booking questions order correction in confirmation email (#18192)
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2024-12-16 09:33:04 +00:00
Keith Williams abaf03adde fix: Next.js bundle analysis build error (#18193) 2024-12-15 17:40:59 +05:30
GitHub Actions 5b353c3d0c chore: release v4.8.0
Post release / release (push) Canceled after 0s
2024-12-15 11:29:06 +00:00
Benny Joo 1f4cda31ac update app layout (#17126) 2024-12-14 18:42:43 -05:00
Calcom BotandReplexica 73d1a6f468 feat: update translations via @replexica (#18158)
Co-authored-by: Replexica <support@replexica.com>
2024-12-14 19:05:59 +00:00
Benny JooandAlex van Andel f72748b6f2 fix: trpc/react-query — only one query client should exist (#18179)
* use prefetch function in ScheduleListItem and refactor

* modify app router trpc provider to match the original configs

* revert unneeded change

* move trpc provider up and do refactors

* remove log

* add comment

* fix: Some typing style fixes

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2024-12-13 20:26:51 -05:00
Benny Joo d12fc78fea chore: add viewport / favicon metadata (#18177)
* fix favicon name

* add viewport metadata for PageWrapperAppDir

* fix

* fix

* refactor
2024-12-13 20:25:03 +00:00
Carina WollendorferandCarinaWolli 3d26332443 fix: change trigger time for form submitted not booked webhook (#18176)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2024-12-13 20:16:11 +00:00
Omar López bdb490c894 Revert "fix: correct line-breaks in calendar event description" (#18175) 2024-12-13 20:03:56 +00:00
Morgan 01dbc686b9 chore: bump platform libraries version for api-v2 (#18174) 2024-12-13 19:35:18 +00:00
Carina WollendorferandCarinaWolli 376961f30d fix: don't send reassingment email to attendee for past events (#18173)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2024-12-13 19:02:54 +00:00
Morgan 18a0eb9736 fix: improve getAllUserBooking filter by teamIds (#18171)
* fix: improve getAllUserBooking filted by teamIds

* fixup! fix: improve getAllUserBooking filted by teamIds
2024-12-13 20:18:58 +02:00
Carina WollendorferandCarinaWolli 84fb4f68b2 fix: wrong workflow notifications when enabling workflows (#18169)
* only schedule workflow emails not send

* fix if statement

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2024-12-13 11:58:30 -05:00
Udit Takkar b5ab6fe3fa fix: add no show guest (#18075)
* fix: add booker no show

* chore: update name
2024-12-13 16:00:17 +00:00
Anik Dhabal BabuandUdit Takkar 645985c783 fix: Invalid time zone specified: Etc/Unknown (#18121)
* fix: Invalid time zone specified: Etc/Unknown

* update

* Update PaymentPage.tsx

* update

* update one more file

* address review

* update test

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2024-12-13 15:29:02 +00:00
GitHub Actions a84513eda5 chore: release v4.7.25
Post release / release (push) Canceled after 0s
2024-12-13 14:19:52 +00:00
Benny Joo b97331aeb3 chore: /availability - Remove Pages Router / Render App Router (#16517)
* /availability: Remove Pages Router / Render App Router

* comment out server side fetching

* fix

* refactor for safer code

* add to config.matcher
2024-12-13 12:44:57 +00:00
Morgan 851d96423b chore: small changes for platform booker embed (#18159) 2024-12-13 13:24:19 +01:00
Anik Dhabal BabuandPeer Richelsen ef830f11ec fix: Hyperlinks not highlighted in eventmeta (#18120)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2024-12-13 13:23:25 +01:00
Keith Williams 1ab4ec9c22 chore: Upgrade actions/upload-artifact to v4 (#18160) 2024-12-13 11:32:50 +00:00
Benny Joo 656a5bf423 chore: remove hardcoded strings (#18156) 2024-12-13 09:54:28 +01:00
Benny Joo 8a6450e854 chore: use getTranslate instead (#18148) 2024-12-13 13:06:47 +05:30
Benny Joo 51841e5e9c fix: ensure migrated pages are added to config.matcher in middleware (#18153) 2024-12-13 12:43:08 +05:30
Benny Joo 59dfcaa2fa chore: fix misleading test names (#18154) 2024-12-13 04:32:52 +00:00
Omar López 0d24706380 refactor: improved observability in google calendar service (#18147) 2024-12-13 03:47:43 +00:00
Alex van Andel 87b6e0b49c fix: Prisma validation error in generateMetadata (workflows) (#18149) 2024-12-13 03:33:19 +00:00
Calcom BotandReplexica 4244ca3117 feat: update translations via @replexica (#18139)
* chore: sync with main

* feat: update translations via @replexica

---------

Co-authored-by: Replexica <support@replexica.com>
2024-12-12 20:48:07 -05:00
Carina WollendorferandCarinaWolli 8d0f0d4899 fix: default to 100 when attribute weight undefined (#18145)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2024-12-12 22:57:43 +00:00
Anik Dhabal Babu 6463983d55 fix: Searching users in admin page not working (#18140) 2024-12-12 19:13:00 +00:00
Rajiv Sahal c8661563c8 fix: update get booking output for v2 (#18082)
* also include email field for attendees

* update tests

* update tests

* updating more tests

* fixup

* fixup

* fixup fixup
2024-12-12 18:55:28 +00:00
Sanchit Tewari 2262d56491 fix: show tooltip below the delete button (#18135) 2024-12-12 18:53:25 +00:00
GitHub Actions 6b1c747c0e chore: release v4.7.24
Post release / release (push) Canceled after 0s
2024-12-12 17:22:03 +00:00
Benny Joo 04036c32a7 fix: filling users logic (#18128)
* fix filling users logic

* fix type check

* refactor
2024-12-12 17:12:56 +00:00
Alex van Andel 49070b96ae Revert "feat: add text & numeric filters to insights routing responses (#18016)"
This reverts commit a7e7561efa.
2024-12-12 17:10:49 +00:00
GitHub Actions 237fea0a41 chore: release v4.7.23
Post release / release (push) Canceled after 0s
2024-12-12 16:40:04 +00:00
a7e7561efa feat: add text & numeric filters to insights routing responses (#18016)
* feat: improve text filters (WIP)

* move function to bottom

* apply some styles

* fix selection of TextFilterOptions

* rename value to operand

* remove unused file

* merge filters/filters into filters/utils

* fix regression of not putting url params correctly

* move makeWhereClause to filters/utils

* fix negative, empty, and not empty operators

* fix initial filtering from search state (url)

* fix type errors

* do not send an empty array to query

* update yarn.lock

* i18n for text filter operators

* extract logic as useColumnFilters()

* add missing import

* fix type error

* revert yarn.lock

* use i18n

* insensitive text match

* move data-table to @calcom/features

* fix type errors

* fix type errors

* fix type errors

* feat: support DataTable filters for Insights Routing WIP

* remove unused filters

* remove additionalFilters and fix types

* clean up filter components

* support icons for ActiveFilters

* support filters on json

* fix filter ui

* fix type error and clean up

* revert changes

* revert change

* clean up

* revert change

* fix compatibility with insights booking page

* remove unused params

* fix type errors

* update yarn.lock

* fix field filter and adjust ui

* chore: update yarn.lock

* fix text filter

* add Clear Filters button

* add more test data

* feat: support numeric filter WIP

* feat: support numeric filter

* feat: move People to an external filter

* fixing query states

* rename state to activeFilters

* add useDataTable

* fix ResponseCellValue bug and enable resizing for RoutingFormResponsesTable

* fix response values

* truncate attribute text

* seed booking attendees

* fix accessor (no actual change though)

* add a gap

* rename

* use pathname as default identifier for DataTable

* fix type error

* support text filter for assignment reason

* Apply suggestions from code review

Co-authored-by: Alex van Andel <me@alexvanandel.com>

* rename fields to headers

* use safeParse

* fix type error

* push yarn.lock with faker

* fix MemberList

---------

Co-authored-by: Udit Takkar <udit222001@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: sean-brydon <sean@cal.com>
2024-12-12 16:06:30 +00:00
Alex van Andel e366c10521 Convert update statement into unique select in getServerSession 2024-12-12 15:50:44 +00:00
Morgan 2ace1d1017 chore: add successRedirectUrl to event-types api v2 (#18136)
* chore: add successRedirectUrl to event-types api v2

* chore: add successRedirectUrl to event-types api v2 patch

* chore: add successRedirectUrl to event-types api v2 patch
2024-12-12 16:32:35 +02:00
sean-brydon fbe31ad82d early return if no users are found to prevent prisma error (#18133) 2024-12-12 13:37:39 +01:00
sean-brydonandUdit Takkar ee3c08918a feat: weights per attribute UI (#18109)
* wip attribut weights

* edit sheet and display in table

* add shrink so badges dont split

* push changes to support weights on csv export

* fix typing issue

* type error

* fall back to 100 weight

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2024-12-12 11:54:10 +00:00
Lauris Skraucis b6d6b7cdb4 fix: store default booking fields when creating event (#18108)
* refactor: store default booking fields when creating event

* Revert "refactor: store default booking fields when creating event"

This reverts commit 78b7c4eda2be61420b4410bc80a3d77b5557a967.

* refactor: v2 return default booking fields if they are null

* test: v2 returning default booking fields if they are null

* chore: platform-libraries export getBookingFieldsWithSystemFields
2024-12-12 10:58:45 +01:00
Hariom BalharaandUdit Takkar 1b82552e76 Fix crm error log (#18105)
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2024-12-12 13:15:14 +05:30
Joe Au-Yeung 13a90aba30 Fix logic (#18131) 2024-12-11 21:03:51 -07:00
Alex van Andel de0de64a16 build: Node alpine libssl symlink fix (#18127) 2024-12-12 00:39:36 +00:00
GitHub Actions 8e772d9046 chore: release v4.7.22
Post release / release (push) Canceled after 0s
2024-12-11 23:04:54 +00:00
Omar López 7ac7a14beb refactor: improved observability for Salesforce (#18125)
* refactor: better observability for Salesforce

* Update crmManager.ts

* Update CrmService.d.ts
2024-12-11 22:57:43 +00:00
Alex van Andel 9c127cbfc2 Revert "feat: migrate /apps/routing-forms/forms to /routing (#18001)"
This reverts commit 1de007ad21.
2024-12-11 22:16:50 +00:00
GitHub Actions 187a2c576e chore: release v4.7.22 2024-12-11 21:54:50 +00:00
5e8d994ac1 feat: Change 'add override' to 'save override' (#17434)
* feat: Change 'add override' to 'save override'

* fix: type check

* fix: type check

* autosave in availability edit sheet

* remove unused translation key

* added requested changes

* added requested changes

---------

Co-authored-by: Prashant Varma <prashantvarma5083@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2024-12-11 21:20:42 +00:00
VishnuandPrashant Varma 1de007ad21 feat: migrate /apps/routing-forms/forms to /routing (#18001)
* feat: add getServerSideProps and getStaticProps

* feat: add routing pages view

* feat: add routing and related pages

* feat: setup redirect

* feat: remove routing configs in getStaticProps and getServerSideProps

* feat: add redirects in next.config and middleware

* feat: add split icon

* feat: rename href to /routing/forms

* feat: fix redirects

* feat: finish routing page

* feat: remove /apps/routing-forms/forms redirect in next-config

* feat: revert back redirects in serverSideProps

* fix: minor issues

* fix: routing-form url cloning

* chore: remove comments

* fix: routing page link

* fix: routing hover link

---------

Co-authored-by: Prashant Varma <prashantvarma5083@gmail.com>
2024-12-11 21:18:46 +00:00
michaelvaletteandYounes Henni 06494a6999 fix: correct line-breaks in calendar event description (#18077)
* fix: correct line-breaks in calendar event description

* Update packages/lib/CalEventParser.ts

Co-authored-by: Younes Henni <youneshenniwrites@gmail.com>

* fix: review comments

* fix: better add spacing on who

---------

Co-authored-by: Younes Henni <youneshenniwrites@gmail.com>
2024-12-11 21:18:29 +00:00
Benny Joo 016c47d535 fix: do not export client components directly in app routes (#18122) 2024-12-11 21:17:53 +00:00
Alex van Andel 2aeb290bda fix: Error on webhooks page exporting client only comp directly (#18118)
* fix: Error on webhooks page exporting client only comp directly

* Wrap correctly, WebhooksView contains dynamic logic in the SettingsHeader
2024-12-11 19:42:04 +00:00
Keith Williams 95b4f67842 chore: Add CODEOWNERS (#18116)
* chore: Add CODEOWNERS

* Added the calcom org

* Removed non-critical calendar apps
2024-12-11 18:29:41 +00:00
Benny JooandUdit Takkar 1a4afe06c8 chore: Remove Pages Router / Render App Router - /video (#16518)
* /video: Remove Pages Router / Render App Router

* fix

* add meeting_unavailable i18n

* remove <Head> from videos view

* add correct metadata to video/[uid]

* add safety to videos-single-view logic

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2024-12-11 18:51:30 +01:00
Keith Williams 0eb72d66a7 revert: "fix: Request permissions to allow events to be created on shared Office365/Outlook calendars (#17760)" (#18111)
This reverts commit 1680cba5a5.
2024-12-11 14:57:57 +00:00
Eunjae Lee 7d2523e823 fix: prevent infinite rerendering of DataTable (#18087) 2024-12-11 14:03:17 +00:00
Benny JooandKeith Williams 6739ed3a87 feat: Title - AI translation for booking pages (#17794)
* feat: AI description - DB model + frontend + backend (fetch only)

* fix types and add validation to backend

* improve log

* improve

* import type

* add EventTypeTranslationRepository

* fix replexica error

* fix replexica error

* fix

* fix test

* wip

* wip

* add createManyDescriptionTranslations method

* improve logic

* refactor

* update replexica type

* improve typing

* Renamed descriptionTranslations to fieldTranslations

* log errors in replexica service

* handle translations in background

* don't create, upsert instead

* make sure that description is updated

* simplify

* fix

* Update packages/trpc/server/routers/viewer/eventTypes/update.handler.ts

* improve filter logic in frontend

* implement background job using Tasker

* update schema

* update schema again

* rename

* rename id to uid

* add migration

* fix type

* update

* update schema and migration sql

* fix migration

* fix type check

* fix type check 2

* improve find logic in EventMeta

* add more validation

* add logger

* rename

* upgrade replexica/sdk to 0.7.0

* upgrade replexica to 0.7.0

* use batchLocalizeText

* add comments

* override browser locale if user is a signed in calcom user

* fix type in replexica

* fix migration sql

* fix type

* use dynamic import

* do not use batchLocalizeText

* chore: drop sourceLang, targetLang and id columns

* fix

* wip

* add more validations

* fix type error

* address comment

* refactor

* update booking page to use translated title

* fix type error

* fix

* revert yarn.lock changes

* remove unused import

* remove duplication migrate.sql file

* fix test

* remove test tags

* fix function name

* refactor

* fix type check

* use Promise.all

* remove unneeded code

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2024-12-11 14:18:32 +01:00
Anik Dhabal Babu dd3505ad8b fix: Insights team filter behavior (#18084)
* fix: Insights team filter behavior

* fix type error
2024-12-11 14:43:25 +05:30
Benny Joo ce9df65b5d chore: Remove Pages Router / Use App Router - /workflows (#16519)
* /workflows: Remove Pages Router / Render App Router

* fix

* unneeded changes

* disable server side fetching for now

* revert unneeded changes
2024-12-11 09:51:22 +01:00
sean-brydon fc6e39c78a feat: assigned bookings per period (#17940)
* wip

* bring in users median status for selected range

* merge

* i18n

* routed to talbe period scroll x-y infinite

* insights i18n + hover card on user

* add search by name or email

* wip download button

* fix type error

* type fix

* type errors + status

* type error
2024-12-11 08:12:06 +00:00
Calcom BotandReplexica 931f3d3396 feat: update translations via @replexica (#18100)
* chore: sync with main

* feat: update translations via @replexica

---------

Co-authored-by: Replexica <support@replexica.com>
2024-12-11 00:12:17 +00:00
GitHub Actions 8a6fac6748 chore: release v4.7.21
Post release / release (push) Canceled after 0s
2024-12-11 00:07:34 +00:00
Benny Joo af43ede38e remove pages router and use app router for insights pages (#18088) 2024-12-10 16:56:56 -07:00
Benny Joo a66eb9dfac chore: remove extra fetching and get user locale from headers in settings pages (#18048)
* add settings pages to matcher

* fetch user locale from headers in settings pages
2024-12-10 16:56:12 -07:00
Benny Joo 5ba676b014 chore: app router migration - /enterprise and d/[link]/[slug] (#18101)
* use app router for /enterprise page

* use app router for /d page
2024-12-10 23:48:24 +00:00
Benny Joo c2c082c105 chore: Remove Pages Router / Use App Router - more, maintenance, upgrade, connect-and-join (#16531)
* Remove Pages Router / Render App Router

* remove head component from maintenance view

* use i18n metadata
2024-12-10 14:37:25 -07:00
Joe Au-Yeung 44d6c1f3fb feat: Show routing reason on booking page [CAL-4814] (#18053) 2024-12-10 14:31:48 -07:00
Benny Joo a466e8eef7 feat: remove setup-availability step in onboarding flow (#17799)
* feat: make the onboarding flow 3-step for google signups

* fix

* refactor

* rename

* improve tests

* remove test tag

* fix test and type error

* fix

* feat: remove setup-availability step in onboarding flow

* update test

* revert unneeded change

* add safety

* update create schedule options
2024-12-10 14:30:23 -07:00
VijayandPrashant Varma 370345da22 fix: Not able to create consecutive ooo (#17388)
* fix: able to create consecutive ooo

* added e2e test

* chore

* update to allow overlapping ooo and test for same

* refactor to reduce repeating code in tests

* nit

* added testcases for consecutive and reverse redirects

* fix:duplicate OOO creation

* prevent duplicate entries, also added test

* chore

* update after merges

* correct datetype in prisma query

* update to changes in rdp after merge

---------

Co-authored-by: Prashant Varma <prashantvarma5083@gmail.com>
2024-12-10 19:46:28 +05:30
Kartik SainiandKeith Williams a1fc6668d5 feat: ability to require an email (#18057)
* feat: ability to require an email

* fix: requireEmails empty bug

* Update apps/web/public/static/locales/en/common.json

* Update apps/web/public/static/locales/en/common.json

* add tests

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2024-12-10 17:27:32 +05:30
Calcom BotandReplexica 351ab0790f feat: update translations via @replexica (#18078)
Co-authored-by: Replexica <support@replexica.com>
2024-12-10 08:49:46 +00:00
Alex van Andel 504600b151 Change alpine image to one compatible with Prisma (#18080) 2024-12-10 02:50:00 +00:00
GitHub Actions 8b222490c2 chore: release v4.7.20
Post release / release (push) Canceled after 0s
2024-12-10 01:14:21 +00:00
Anwar SadathandAnik Dhabal Babu 0657a96795 feat: Add OAuth support for closecom app linking (#17936)
* feat: Add OAuth support for closecom app linking

* Fix types

---------

Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2024-12-10 00:18:30 +00:00
Kartik Saini 9c9d7fe0c3 fix: can't edit ooo (#18020)
* fix: can't edit ooo

* fix: unauthorized users can edit ooo
2024-12-09 23:39:56 +00:00
sean-brydon 950879b9c7 fix: sorting insights table for booking status (#17982)
* Revert "merge columns (#17938)"

This reverts commit a3ceefc00e.

* sort order by status
2024-12-09 23:39:10 +00:00
Peer Richelsen 55dc4bddb6 fixed days of trial (#18074) 2024-12-09 19:04:45 +00:00
Anik Dhabal Babu cbb1b285f5 fix: not able to mark no show (#17980)
* fix: not able to mark no show

* add test

* Update bookings-list.e2e.ts
2024-12-09 14:02:45 -05:00
Anik Dhabal Babu 45f8ecbab7 chore: Add location to workflow reminder template (#17449)
* chore: Add location to workflow reminder template

* update

* remove log
2024-12-09 18:58:17 +00:00
Toon ToetenelandToon Toetenel 1680cba5a5 fix: Request permissions to allow events to be created on shared Office365/Outlook calendars (#17760)
Co-authored-by: Toon Toetenel <toon@breakingbits.nl>
2024-12-09 16:17:48 +00:00
Benny Joo 046042a877 feat: remove 2 steps from the onboarding flow for google signups (#17787) 2024-12-09 15:44:42 +00:00
Morgan a926e0b9d6 fix: public booking page serialization error (#18070)
* fix: public booking page serialization error

* fixup! fix: public booking page serialization error
2024-12-09 14:48:06 +00:00
9334b67f2c feat: banner and avatars for Booker atom (#17821)
* init

* added bannerUrl to output

* cleanup

* fixup

* fix response DTOs

* remove logs

* send back avatar url for user

* enable passing avatar urls for managed user

* dont include urls for platform

* remove logs

* add team banner

* update docs

* nit

* PR feedback

* fix type error

* add prop to pass in bannerUrl

* update DTOs

* update tests

* fix tests

---------

Co-authored-by: Somay Chauhan <somaychauhan98@gmail.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2024-12-09 14:35:57 +00:00
Hariom Balhara ae1b844bea chore: Remove pages/settings (#18064)
* Remove unused org mkigration pages and delete pages/settings

* Remove more unused code causing ts errors

* Remove SettingsLayout
2024-12-09 15:19:12 +01:00
Morgan 15e93af2ac chore: enable markdown for platform atoms (#18069)
* chore: enable markdown for platform atoms

* fixup! chore: enable markdown for platform atoms
2024-12-09 16:11:02 +02:00
Rajiv Sahal 49c97c6380 rearrange order of platform tabs (#18068) 2024-12-09 18:50:59 +05:30
Keith Williams 8679d21d78 docs: Fix OAuth casing (#18066) 2024-12-09 13:18:44 +01:00
Vincent Lam 382f00e178 - Set stripe payment element to accordion layout (#18056)
- Removed hardcoded payment_method_types for checkouts and immediate payments
2024-12-09 11:37:46 +01:00
Anik Dhabal Babu e8eab10d17 fix: team event leading to a 404 page due slug conflict (#18055) 2024-12-09 11:36:15 +01:00
Eunjae Lee 7a7282a7ab fix: add members page back to settings (#18065) 2024-12-09 10:23:27 +00:00
Calcom BotandReplexica 19b3d6a633 feat: update translations via @replexica (#18063)
* chore: sync with main

* feat: update translations via @replexica

---------

Co-authored-by: Replexica <support@replexica.com>
2024-12-09 10:07:43 +00:00
Lauris Skraucis 046b7a0d08 feat: v2 teams memberships endpoints (#18041) 2024-12-09 10:52:01 +01:00
Benny Joo 30acf6b196 chore: use app router - /apps main page (#16980)
* chore: apps main page remove pages router

* revert unneeded changes
2024-12-09 09:48:22 +00:00
Eunjae Lee 5e95018870 feat: resizing in DataTable (#17973)
* feat: support resizing in data table

* wip

* clean up colors

* feat: resizable DataTable

* update implementation

* fix

* remove unused code

* improve dependency array

* enable resizing at DataTable level

* hide header label

* fix type error
2024-12-09 08:26:02 +00:00
Morgan e559e7a46f fix: add onTouchEnd for mobile apps (#18040) 2024-12-09 10:21:52 +02:00
Morgan 3b1123a29a chore: add user profile to org user endpoints apiv2 (#18039) 2024-12-09 10:21:28 +02:00
Udit TakkarandHariom be3e580107 feat: domain wide delegation UI (#18013)
Co-authored-by: Hariom <hariombalhara@gmail.com>
2024-12-09 08:27:16 +05:30
Ignite_99 b325cf8d00 Fixes #17863: When crating a new event from a team event list, the cancel button redirects to /event-types (#18058) 2024-12-08 17:19:04 +00:00
Hariom Balhara 6ce9defecf fix: Do not consider salesforce ownership when making rerouting decision (#18034) 2024-12-07 02:36:41 +00:00
Anik Dhabal Babu 8d05656b7b fix: Schedule a timezone change only allows you to select one day not a range (#18045) 2024-12-06 16:53:55 +00:00
Anik Dhabal Babu 091400b7d5 chore: Add host default language to workflow reminder weekdays (#18029)
* chore: Add host default language to workflow reminder weekdays

* update
2024-12-06 15:56:03 +00:00
Hariom BalharaandJoe Au-Yeung 6de26b3c43 feat: Enable SCIM logging that can be configured through constant (#18043)
* Enable SCIM logging that can be configured through constants

* Remove example

---------

Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
2024-12-06 15:48:16 +00:00
Somay ChauhanandMorgan 063400fcbc feat: conferencing atoms (#17988)
* added api/v2 for zoom connect

* Update conferencing.controller.ts

* fix: added api for ConferencingAppsViewPlatformWrapper

* type fixes

* added getBulkEventTypes api for bulk-update default location

* added bulk-update-to-default-location

* fix:bulk-update-to-default-location

* invalidated

* fixed returnTo and onErrorReturnTo

* make logos work for conferencing atoms

* smalll improvements

* fixup

* share common types

* type fix, and fixed a typo

* fix: type-error

* fix: json.parse(json.parse())

* fix: invalidate query

* fix: removing app throws error in the main app

* undo platform-libraries-1.2.3

* update platform-libraries version

---------

Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2024-12-06 19:03:04 +05:30
Syed Ali Shahbaz 44115ac7fa feat: v1 booking "created at" field in response (#18038)
* Update booking.ts

* Update booking.ts
2024-12-06 10:22:47 +00:00
Hariom Balhara 7e76770ba6 feat: Okta(SCIM2.0) -> Attributes Sync and Group Option support (#17805) 2024-12-06 15:17:50 +05:30
GitHub Actions d5ef3e1d72 chore: release v4.7.19
Post release / release (push) Canceled after 0s
2024-12-06 09:31:35 +00:00
Benny Joo 0c19606e19 chore: technical debt - remove instances of testing /future routes (#18032) 2024-12-06 02:53:00 +00:00
Benny Joo b8219c273d feat: Reduce bundle size via better imports + install gcal + gvideo by default for google signups (#17810)
* wip

* use light imports

* fix

* perf: Reduce bundle size via better imports with `googleapis` (#17811)

* fix test

* refactor

* update vite.config.js

* fix

* fix unit test failing
2024-12-06 02:51:48 +00:00
Calcom BotandReplexica f75415e414 feat: update translations via @replexica (#18033)
Co-authored-by: Replexica <support@replexica.com>
2024-12-06 01:14:40 +00:00
Syed Ali ShahbazandCarinaWolli 4bf5e3cdf5 feat: Webhook support for Managed Events (#17986)
* managed event webhooks

* more fixes

* fix types --WIP

* fix types

* fix test

* add test

* remove remnant comments

* address feedback

* fix test

* fix test

* fix tests

* change of plans \(00)/

* remove console log

* remove unnecessary comments

* revert tests

* list active webhooks in child event types

* add translations

* allow editing own event types

* add locked info for children

* secure create handler

* fix for isLocked check

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2024-12-05 23:20:34 +00:00
Benny Joo 4181b5988a chore: remove unused files (#18025) 2024-12-05 22:01:24 +00:00
Keith Williams c41ba9d984 revert: "feat: refactor filters on insights response table (#17796)" (#18024)
* revert: "feat: refactor filters on insights response table (#17796)"

This reverts commit 5d88858995.

* Put back the search columns and options english terms
2024-12-05 17:29:09 +00:00
Benny Joo 2a578ea9d5 chore: drop sourceLang, targetLang and id columns (#17792)
* chore: drop sourceLang, targetLang and id columns

* remove copying for sourceLocale and targetLocale
2024-12-05 17:23:07 +01:00
GitHub Actions c96827e1c0 chore: release v4.7.18
Post release / release (push) Canceled after 0s
2024-12-05 16:18:37 +00:00
Calcom BotandReplexica 3f5363038c feat: update translations via @replexica (#18022)
Co-authored-by: Replexica <support@replexica.com>
2024-12-05 16:12:27 +00:00
Kartik SainiandPrashant Varma e2de28b933 feat: prevent rescheduling past bookings (#18003)
* feat: prevent rescheduling past bookings

* hide reschedule option on frontend

* fix

* addd null check for booking.endTime

---------

Co-authored-by: Prashant Varma <prashantvarma5083@gmail.com>
2024-12-05 16:36:00 +01:00
Eunjae LeeandUdit Takkar 5d88858995 feat: refactor filters on insights response table (#17796)
* feat: improve text filters (WIP)

* move function to bottom

* apply some styles

* fix selection of TextFilterOptions

* rename value to operand

* remove unused file

* merge filters/filters into filters/utils

* fix regression of not putting url params correctly

* move makeWhereClause to filters/utils

* fix negative, empty, and not empty operators

* fix initial filtering from search state (url)

* fix type errors

* do not send an empty array to query

* update yarn.lock

* i18n for text filter operators

* extract logic as useColumnFilters()

* add missing import

* fix type error

* revert yarn.lock

* use i18n

* insensitive text match

* move data-table to @calcom/features

* fix type errors

* fix type errors

* fix type errors

* feat: support DataTable filters for Insights Routing WIP

* remove unused filters

* remove additionalFilters and fix types

* clean up filter components

* support icons for ActiveFilters

* support filters on json

* fix filter ui

* fix type error and clean up

* revert changes

* revert change

* clean up

* revert change

* fix compatibility with insights booking page

* remove unused params

* fix type errors

* update yarn.lock

* fix field filter and adjust ui

* chore: update yarn.lock

* fix text filter

* add Clear Filters button

* add more test data

---------

Co-authored-by: Udit Takkar <udit222001@gmail.com>
2024-12-05 16:32:52 +01:00
Anik Dhabal Babu 82a500aeaa fix: days of the week in different language in the workflow (#17850)
* fix: days of the week in different language in the workflow

* Update
2024-12-05 15:19:45 +00:00
Hariom Balhara 2408337c78 fix: Don't force reschedule with same RR host if reschedule is actually rerouting (#17511)
* Dont force reschedule with same RR host if routed there

* Add unit tests
2024-12-05 09:30:22 -05:00
Rajiv Sahal 119edb5389 fix: localStorage not found for Booker atom (#18014) 2024-12-05 19:50:09 +05:30
Anwar SadathandPraash ae4474cd42 feat: Add slotFormat query parameter to slots API V2 endpoint to return start and end time of a slot (#17873)
* feat: Add formatAsStartAndEndTime query parameter to slots API V2 endpoint to return start and end time of a slot

* Update apps/api/v2/src/modules/slots/controllers/slots.controller.ts

Co-authored-by: Praash <99237795+Praashh@users.noreply.github.com>

* Fix duration was allowed to be any number

* Refactor code

* Show slotFormat property as optional in swagger doc

---------

Co-authored-by: Praash <99237795+Praashh@users.noreply.github.com>
2024-12-05 19:12:17 +05:30
Morgan 3290ae526a fix: custom implem markdownToSafeHTML for platform (#18018) 2024-12-05 12:18:45 +00:00
Morgan 16a7cf6d04 fix: fetching event-types of org users apiv2 (#18004) 2024-12-05 06:02:19 +00:00
Benny Joo ab2ab31eef chore: named export / default export same time (#18012) 2024-12-04 23:21:29 +01:00
GitHub Actions a08ae43533 chore: release v4.7.17
Post release / release (push) Canceled after 0s
2024-12-04 18:14:31 +00:00
Benny Joo d60b6c6a6d chore: settings/teams and settings/billing - remove pages router and use app router (#16751)
* remove pages router and use app router

* revert yarn.lock

* remove line break from yarn lock

* Add booking limits page to app dir

* revert yarn.lock

* do not render bookingLimits from pages router

* clean up code

* remove isAppDir

* revert unneeded change

* add layout to billing page

* remove files not needed

* rename imports

* remove APP_ROUTER_SETTINGS_DEVELOPER

* remove layout
2024-12-04 12:31:45 -05:00
Keith Williams 9085287fc6 revert: "chore: Move pre-commit commands to pre-push (#17996)" (#18009)
This reverts commit f63c06d68a.
2024-12-04 17:17:58 +00:00
Benny Joo 7e2b5b12b4 chore: settings/developer - remove pages router and use app router (#16794)
* remove pages router and use app router

* remove Meta / improve code quality

* remove isAppDir

* refactor code for better code

* fix test

* remove meta

* improve tests
2024-12-04 11:00:37 -05:00
sean-brydon 3211c5bcff fix data loading state (#18002) 2024-12-04 15:15:01 +00:00
Christoffer Bjelke a75a5b72b2 chore: enable eslint rule react/no-danger (#17983)
* enable react/no-danger and disable rule all places

* remove wild rule
2024-12-04 15:07:30 +00:00
Rajdeep Ghosh 509fc4b289 style: rename [...nextauth].tsx to use .ts file extention (#17992) 2024-12-04 14:52:31 +00:00
Keith Williams f63c06d68a chore: Move pre-commit commands to pre-push (#17996)
* chore: Move pre-commit commands to pre-push

* Is this fast

* Removed test commit

* Adding logging
2024-12-04 14:51:26 +00:00
Anik Dhabal Babu 3dff77fe59 chore: don't show parent managed eventType as event redirectionoption (#17993) 2024-12-04 20:08:52 +05:30
Calcom BotandReplexica a6f9952d75 feat: update translations via @replexica (#18000)
Co-authored-by: Replexica <support@replexica.com>
2024-12-04 13:13:51 +00:00
Benny JooandKeith Williams 03704b2e73 feat: fetch SEO data for booking pages in server (#17987)
* feat: fetch public event data for booking pages server-side

* fix types

* refactor

* Replace the getPublicEvent with other smaller calls

* Change username to teamSlug

* Fixing types

* Type checks

* Pushing latest for Benny - this still has type errors

* fix type checks

* fill data for team pages too

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2024-12-04 14:08:27 +01:00
sean-brydon 982a715c4b feat: add assignment reason to routing insights (#17984)
* add assignment reason

* add routing reason to csv export
2024-12-04 12:39:20 +00:00
Morgan eaff9e8c89 fix: disable mandatory reminder for platform (#17998)
* fix: disable mandatory reminder for platform

* bump libraries version

* fixup! bump libraries version
2024-12-04 12:33:53 +00:00
e2938ac39a refactor: v2 OAuth webhooks & workflows (#17959)
* refactor: handleMarkNoShow OAuth webhook handling

* refactor: confirm / decline booking OAuth webhook handling

* refactor: pass oauth client to calendar event

* refactor: pass oauth params

* fix import

* fix tests

* chore: bump libraries

* chore: bump libraries

* chore: republish platform libraries

* chore: republish platform libraries

* fix: use replexica key in v2 and unit test CI

* lock file

* fixup! Merge branch 'main' into lauris/cal-4807-platform-refactor-oauth-webhooks

* Revert "fix: use replexica key in v2 and unit test CI"

This reverts commit 0bd6c364535f9e11a4d1497a6bd8909885562080.

---------

Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
Co-authored-by: Morgan Vernay <morgan@cal.com>
2024-12-04 11:25:20 +01:00
Morgan b92db55380 chore: add REPLEXICA env to ci tests (#17970)
* chore: add REPLEXICA env to ci tests

* fixup! chore: add REPLEXICA env to ci tests
2024-12-04 09:37:25 +00:00
Somay Chauhan 3015c5a15e fix: trying to update the timeFormat throws error (#17991)
* timeFormat should be an intiger
2024-12-04 09:29:51 +01:00
Joe Au-YeungandUdit Takkar 2444598e38 Fuzzy search account website, fallback to contacts (#17989)
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2024-12-04 07:46:51 +00:00
Benny Joo 6eadadec73 fix: remove duplicate header in attributes page (#17985) 2024-12-03 20:32:56 +00:00
sean-brydon 145449e7b4 fix:z index hover card on routing form responses (#17981)
* Revert "merge columns (#17938)"

This reverts commit a3ceefc00e.

* fix hovercard on responses
2024-12-03 19:00:56 +00:00
1d8326ed83 feat: watchlist (#17947)
* lock users on signup if their email is in blacklist

* add to turbo env list

* prevent locked user or blocked email domain from using API

* Refactored to only run 1 query to find the user

* Fixing tests

* WIP

* WIP

* WIP

* Discard changes to turbo.json

* Fixed tests

* Update isLockedOrBlocked.test.ts

* Update isAdmin.integration-test.ts

* Update tsconfig.json

* chore: rename to watchlist

Signed-off-by: Omar López <zomars@me.com>

---------

Signed-off-by: Omar López <zomars@me.com>
Co-authored-by: sean-brydon <sean@cal.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
2024-12-03 18:43:01 +01:00
GitHub Actions 59993891b8 chore: release v4.7.16
Post release / release (push) Canceled after 0s
2024-12-03 17:36:59 +00:00
Alex van Andel a31d140761 fix: handling of defaultLabel (#17976) 2024-12-03 17:28:02 +00:00
Calcom BotandReplexica b055fe182a feat: update translations via @replexica (#17965)
Co-authored-by: Replexica <support@replexica.com>
2024-12-03 17:19:32 +00:00
392757fb55 fix: Remove user from managed event type when removing from org (#17369)
* Remove user from managed event type when removing from org

* added test for admin check, correct changes for membership, user info, profiles

* remove host assignment for removed members

* expand test to cover  event types removal

* chore: use var

* chore

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Udit Takkar <udit222001@gmail.com>
2024-12-03 17:54:04 +01:00
3222be9171 docs: v2 rolling window and authentication (#17825)
* docs: booking window rolling description

* docs: v2 auth and navigating endpoints

* Update apps/api/v2/swagger/documentation.json

* Update docs/api-reference/v2/introduction.mdx

* Update docs/api-reference/v2/introduction.mdx

* Update docs/api-reference/v2/introduction.mdx

* docs: refresh and force-refresh endpoints

* docs: booking fields and toasts guides

* fix docs

* fix: bookings docs

* docs: booking-redirects usebooking

* Update booking-fields.mdx

* Update booking-fields.mdx

* Update booking-fields.mdx

* Update booking-window.input.ts

* Update replacing-toasts.mdx

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2024-12-03 17:53:14 +01:00
Benny Joo 6378729962 chore: use markdownToSafeHTML in EventMeta (#17975) 2024-12-03 16:45:48 +00:00
Udit Takkar 1c5786b66f fix: org bio (#17971)
* fix: org bio

* chore: use in bookings
2024-12-03 16:30:03 +00:00
Peer Richelsen e61a0d8081 chore: safeMarkdown (#17972) 2024-12-03 15:47:58 +00:00
Rajiv SahalandMorgan Vernay 0cb955cc0b fix: build failure for v4.7.15 (#17968)
* fix: type error causing broken build

* fix typings

* fixup! fix typings

* fixup! fixup! fix typings

---------

Co-authored-by: Morgan Vernay <morgan@cal.com>
2024-12-03 15:44:42 +00:00
Hariom BalharaandAlex van Andel 00689fda0a fix: remove platform and console submodule (#17939)
* Fix: remove platform submodule

* Update git-setup.sh

* Update git-setup.sh

* Update git-setup.sh

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2024-12-03 11:49:52 +00:00
Joe Au-Yeung bd83b36b75 feat: Salesforce - add option to ignore guests (#17956)
* Enable option to ignore guests

* Ignore guests in CRM Manager
2024-12-03 11:19:57 +00:00
Eunjae Lee 05e2029bc9 fix: change case for column size vars (#17961)
* fix: change case for column size vars

* fix wrong import
2024-12-03 09:50:00 +00:00
Udit Takkar 9e3d369ac3 feat: return participants (#17950)
* feat: return participants

* fix: update tests
2024-12-03 09:36:59 +00:00
sean-brydon 356c603e3f Revert "merge columns (#17938)" (#17960)
This reverts commit a3ceefc00e.
2024-12-03 09:23:28 +00:00
5afe3aa54d feat: update translations via @replexica (#17952)
* chore: sync with main

* feat: update translations via @replexica

---------

Co-authored-by: Replexica <support@replexica.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2024-12-03 06:56:08 +00:00
Alex van Andel 985f216a43 test: fix organisation settings e2e (#17957) 2024-12-03 09:41:20 +05:30
Anwar SadathandHariom 3a472997ff fix: brand colors set by user not respected in embed (#17399)
Co-authored-by: Hariom <hariombalhara@gmail.com>
2024-12-03 04:10:58 +00:00
GitHub Actions 6dcb6aece4 chore: release v4.7.15
Post release / release (push) Canceled after 0s
2024-12-02 22:51:37 +00:00
GitHub Actions dc7ee1aba2 chore: release vv4.7.15 2024-12-02 22:41:39 +00:00
Joe Au-Yeung 2f9f29511b Reduce banner height on mobile booker (#17954) 2024-12-02 22:30:16 +00:00
Anik Dhabal BabuandPeer Richelsen 0aa58c1095 fix: Date Range Picker behavior (#17914)
* fix: Date Range Picker behaviour

* remove console log

* update

* update

* update css

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2024-12-03 02:10:23 +05:30
Eunjae LeeandAlex van Andel 58b77b2a16 fix: fix date comparison for OOO (#17756)
* fix: fix date comparison for OOO

* fix date comparison with today, and add tests

* remove wrong code

* remove logic that is no longer in use

* fix type error

* mock more methods

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2024-12-02 19:39:44 +00:00
Peer Richelsen caeaf9053b chore: fixed colored event margin (#17932) 2024-12-02 19:31:07 +00:00
fe209df2be fix: ai translations description for atoms (#17875)
* remove ai translations description for atoms

* PR feedback

* fixup

---------

Co-authored-by: Benny Joo <sldisek783@gmail.com>
Co-authored-by: Somay Chauhan <somaychauhan98@gmail.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2024-12-02 19:27:54 +00:00
CodeWithAdnanandPrashant Varma 2e7a5a84a6 feat: Show add to calendar section for customers after booking an event (#17761)
* Show 'Add to calendar' section for customers after booking an event

* removed comment

---------

Co-authored-by: Prashant Varma <prashantvarma5083@gmail.com>
2024-12-02 19:25:56 +00:00
Anik Dhabal Babu 410558f822 fix: Display option label in the routing form response email (#17898)
* fix: display option label int the routing form response email

* Update

* fix type error

* type error
2024-12-02 19:24:11 +00:00
sean-brydon a3ceefc00e merge columns (#17938) 2024-12-02 19:21:42 +00:00
Kartik SainiandAlex van Andel c524a3c1ef fix: host entry not created when user added to team in org (#17927)
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2024-12-02 19:19:05 +00:00
a5ba2682f6 fix: /members improvements (#17911)
* remove heading for more space

* fix: mobile support for mass attribute assigning bar

* change the order of Members item on Navigation

* refactor NavigationItem to support moreOnMobile property

* fix logic for nav

* decrease column widths on member list

* improve mobile design for mass attribute assign

* adjust mobile ui for DataTableSelectionBar

* add key to EventTypesList

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2024-12-02 19:13:08 +00:00
Anik Dhabal Babu 51c91e41da fix: navigation items weird behavior (#17929) 2024-12-02 19:08:13 +00:00
Alex van Andel cf6654dd1f Extract some refactor work from util.ts to improve error handling (#17912) 2024-12-02 19:06:45 +00:00
Benny Joo d057d7d37f fix: refactor OG link builder (#17897)
* fix: manually encode special characters

* fix test

* fix

* fix
2024-12-02 19:01:09 +00:00
Anik Dhabal BabuandAlex van Andel 262738f288 fix: Meeting URL does not save when changing the location to Gmeet or any static app (#17840)
* fix: meetingurl does not save when we change location

* update

* fix type error

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2024-12-02 19:00:43 +00:00
Eunjae Lee 27cd2457e8 fix: fix UI regression related to DataTable (#17943)
* fix: fix UI regression regarding DataTable

* update comment
2024-12-02 18:54:58 +00:00
Vishnu b33ca2f937 feat: no show guests on insights (#17919)
* feat: return noShowGuest data from eventsByStatus & eventsTimeline procedures

* feat: add noShowGuest Data in BookingKPICards

* feat: add noShowGuest Data in BookingStatusLineChart

* feat: track noShowGuests in outer query

* chore: fix noShowGuest naming
2024-12-02 18:53:32 +00:00
Alex van Andel 82f64f7d6a fix: Broken integration test due to month change (#17949) 2024-12-02 18:26:45 +00:00
Amit Sharma 8052d27d0b feat: show attributes in profile (#17920) 2024-11-30 22:53:05 +05:30
Aditya Pandey 5720ec8038 fix : Issue on docs (#17895) 2024-11-29 19:50:57 +00:00
Vincent Lam 01b1f5bcc8 fix: Added proper updates/interactivity on the "Getting started" app integ… (#17903)
* Added proper updates/interactivity on the "Getting started" app integrations.

- InstallAppButtonWithoutPlanCheck properly vends the mutation data in the render's props
- Fixed the improper props wiring in the AppConnectionItem
- Added a loading state for reactivity

* -Added toaster for error/success confirmation
2024-11-29 19:14:01 +00:00
Jesse KlotzandHariom 953a8119db build: Reduce bundle size from unnecessary bundeling react/jsx-runtime in embed-react package (#17790)
* Update vite.config.js

* linting fix

---------

Co-authored-by: Hariom <hariombalhara@gmail.com>
2024-11-29 22:19:16 +05:30
Eunjae Lee e8daa4f92f fix: bulk attribute assignment (#17896)
remove comment
2024-11-29 10:00:20 +00:00
f631abfdc4 fix: #16028 Org Settings - SEOIndexing,Disable Org Url (#16075)
Co-authored-by: Amit Sharma <74371312+Amit91848@users.noreply.github.com>
Co-authored-by: Hariom <hariombalhara@gmail.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
2024-11-29 10:21:14 +05:30
GitHub Actions b5c6d7af91 chore: release v4.7.14
Post release / release (push) Canceled after 0s
2024-11-28 21:50:59 +00:00
Omar López 249bcaca68 fix: locked users admin list and bypass query (#17902) 2024-11-28 21:45:40 +00:00
Joe Au-Yeung f2efd6dc28 feat: Salesforce - send routing form responses to Salesforce (#17890) 2024-11-28 12:37:45 -07:00
Joe Au-Yeung be2f0f986e fix: getAttributeRoutingConfig find the route based on the chosenRouteId (#17867)
## What does this PR do?

<!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. -->

We were parsing routes based on `eventTypeId` but routes could be pointing to the same event type. This was causing misroutings.

- Fixes #XXXX (GitHub issue number)
- Fixes CAL-XXXX (Linear issue number - should be visible at the bottom of the GitHub issue description)

<!-- Please provide a loom video for visual changes to speed up reviews
 Loom Video: https://www.loom.com/
-->

## Mandatory Tasks (DO NOT REMOVE)

- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [ ] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). If N/A, write N/A here and check the checkbox.
- [ ] I confirm automated tests are in place that prove my fix is effective or that my feature works.
2024-11-28 12:31:06 -07:00
Udit TakkarandAlex van Andel 589c1dccb9 fix: remove from host when removed from org (#17893)
* fix: remove from host when removed from org

* test: add test for removing members

* fix: type errors, some unused params

* fix: test

* refactor: use prisma.count

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2024-11-28 15:56:17 +00:00
Eunjae Lee 4eac6c755d fix: revert bg color for pinned column at data table (#17892) 2024-11-28 10:04:53 +01:00
Lauris Skraucis 131b3d0984 refactor: v2 validate booking fields (#17878)
* refactor: v2 validate booking fields

* test
2024-11-28 10:37:42 +02:00
Omar López 693356d3c4 fix: Exclude locked users by default (#17889) 2024-11-27 17:03:58 -07:00
GitHub Actions 4b717108b0 chore: release v4.7.13
Post release / release (push) Canceled after 0s
2024-11-27 22:46:23 +00:00
Peer Richelsen ad8c888b77 chore: temporarily remove push notifications button until we have proper support (#17887) 2024-11-27 19:27:07 +00:00
Benny Joo d6fe955ea8 feat: hide the update button in case of no permission (#17883)
* feat: hide button if not owner of org

* fix
2024-11-27 11:43:46 -05:00
Benny Joo ab9b6024bb feat: add tooltip to button for better UX (#17881) 2024-11-27 15:47:08 +00:00
Benny Joo a7e99f3674 fix: Remove double encoding for OG image URLs (#17872)
* fix: do not use vercel image api for meeting link preview image

* fix

* fix

* remove double encoding

* fix

* fix test

* fix

* fix

* fix test

* fix

* fix

* remove test tag

* fix
2024-11-27 15:45:54 +00:00
Lauris Skraucis 999019c6f0 refactor: v2 return host username (#17880)
* fix: v2 return booking with host username

* auto gen docs

* auto gen docs
2024-11-27 15:15:12 +00:00
Lauris Skraucis 240ab60c5f fix: v2 return booking with split name attendee (#17877) 2024-11-27 16:22:58 +02:00
Benny Joo e8f84779f5 chore: make i18n.json the single source of truth (#17876) 2024-11-27 15:18:38 +01:00
Eunjae Lee 08a92b46f9 fix: load more rows if the existing ones do not fill the space (#17874) 2024-11-27 07:08:28 -07:00
Eunjae LeeandOmar López 877e4199e7 fix: move organization members list (#17531)
Co-authored-by: Omar López <zomars@me.com>
2024-11-27 13:53:36 +00:00
Hariom Balhara afd730e1c8 fix: (Hotfix)any group of rules in routing (#17869) 2024-11-27 10:44:45 +05:30
Morgan 471f5bd7eb chore: add Free to platform plan guard (#17862) 2024-11-27 01:40:39 +00:00
Joe Au-Yeung c676d47b7e fix: Routing form report filters (#17865) 2024-11-27 01:34:48 +00:00
GitHub Actions b3536728e0 chore: release v4.7.12
Post release / release (push) Canceled after 0s
2024-11-26 20:22:01 +00:00
Eunjae LeeandOmar López e22fec93b1 fix: provide fixed widths for DataTable columns (#17853)
Co-authored-by: Omar López <zomars@me.com>
2024-11-26 20:17:33 +00:00
Joe Au-Yeung f81716fbb2 feat: Salesforce - pass company booking response to the Lead field (#17860)
* Catch undefined app option properties

* Add `getCompanyNameFromBookingResponse` method

* Pass bookingResponse when Lead is being created

* Pass bookingResponse from CRMManager

* Handle empty company response

* Return the company value
2024-11-26 19:14:44 +00:00
Anik Dhabal Babu 0b66ddc6db fix: unable to remove banner (#17857) 2024-11-26 18:42:41 +00:00
Somay Chauhan 382a27f134 fix: round robin host reschedule error (#17855) 2024-11-26 18:08:58 +00:00
Calcom BotandReplexica ac5ee225d5 feat: update translations via @replexica (#17854)
* chore: sync with main

* feat: update translations via @replexica

---------

Co-authored-by: Replexica <support@replexica.com>
2024-11-26 16:46:54 +00:00
Anik Dhabal Babu d8fd6f5c25 chore: exclude emails form field refactor (#17824)
* chore: exclude emails refactor

* Update
2024-11-26 21:30:28 +05:30
Benny Joo 0e333aa71c fix: use tags icon for attr (#17841) 2024-11-26 12:52:21 -03:00
Carina WollendorferandCarinaWolli ae70786b2b fix: send attendee email with event updates of reassignment (#17836)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2024-11-26 10:33:00 -05:00
Benny Joo 2df553b58b chore: use app router for settings/platform/members page (#17849)
* use i18n strings

* migrate platform/members page
2024-11-26 15:33:01 +01:00
Morgan d4afa1436d chore: make throttler logs verbose (#17845) 2024-11-26 09:42:27 +00:00
Morgan 88efa0a2b2 fix: oauth client users controller permissions (#17822)
* fix: oauth client users controller permissions

* fixup! fix: oauth client users controller permissions

* fixup! fixup! fix: oauth client users controller permissions

* fixup! fixup! fixup! fix: oauth client users controller permissions
2024-11-26 09:41:03 +00:00
Calcom BotandReplexica b563e41623 feat: update translations via @replexica (#17842)
Co-authored-by: Replexica <support@replexica.com>
2024-11-25 22:17:54 +00:00
Benny Joo f097f852ad chore: settings/organizations - remove pages router and use app router (#16774)
* remove pages router and use app router

* remove future/settings/organizations files

* fix billing page

* sync SettingsLayoutAppDirClient and fix members page

* fix attribute pages

* remove isAppDir where not needed anymore

* add CTA button for members view
2024-11-25 16:49:00 -05:00
Joe Au-Yeung aea8588a21 fix: Salesforce owner check, handle no contact email (#17834) 2024-11-25 19:38:06 +00:00
Benny Joo 7e0c684348 fix: UI issue in Phone Input dropdown (#17837) 2024-11-25 19:35:09 +00:00
Keith Williams 21956f096d chore: Add check to skip DB migrations as part of build (#17827) 2024-11-25 16:33:51 -03:00
Benny Joo d1adfa3bf9 fix (#17812) 2024-11-25 14:07:55 -05:00
Benny JooandKeith Williams 62976a4c77 feat: AI description - Backend for adding translation objects to EventType model (#17657)
* feat: AI description - DB model + frontend + backend (fetch only)

* fix types and add validation to backend

* improve log

* improve

* import type

* add EventTypeTranslationRepository

* fix replexica error

* fix replexica error

* fix

* fix test

* wip

* wip

* add createManyDescriptionTranslations method

* improve logic

* refactor

* update replexica type

* improve typing

* Renamed descriptionTranslations to fieldTranslations

* log errors in replexica service

* handle translations in background

* don't create, upsert instead

* make sure that description is updated

* simplify

* fix

* Update packages/trpc/server/routers/viewer/eventTypes/update.handler.ts

* improve filter logic in frontend

* implement background job using Tasker

* update schema

* update schema again

* rename

* rename id to uid

* add migration

* fix type

* update

* update schema and migration sql

* fix migration

* fix type check

* fix type check 2

* improve find logic in EventMeta

* add more validation

* add logger

* rename

* upgrade replexica/sdk to 0.7.0

* upgrade replexica to 0.7.0

* use batchLocalizeText

* add comments

* override browser locale if user is a signed in calcom user

* fix type in replexica

* fix migration sql

* fix type

* use dynamic import

* do not use batchLocalizeText

* fix

* add more validations

* fix type error

* address comment

* update locales

* update locales

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2024-11-25 18:37:42 +00:00
GitHub Actions 93489709a8 chore: release v4.7.11
Post release / release (push) Canceled after 0s
2024-11-25 16:35:44 +00:00
Amit Sharma 050102fde2 fix: OOO redirect, email wrong formatting (#17829) 2024-11-25 16:11:44 +00:00
Morgan c1112ab2ab fix: extract clientId header manually apiv2 (Revert) (#17832)
This reverts commit b0bf87f3ed.
2024-11-25 15:59:39 +00:00
Morgan 34d4c3d775 fix: api v2 webhook issue (#17831) 2024-11-25 15:52:00 +00:00
Somay Chauhan e477e5892c feat: added customClassnames for event-setup tab (#17240)
* feat: added customClassnames for event-setup tab

* Update EventTypePlatformWrapper.tsx

* better classnames naming

* added classNames to common types

* updated classnames to use the common exported types

* udpate

* better classnames

* better classname names

* classnames -> classNames

* added default styling

* Update Locations.tsx

* Update event-types.tsx

* update

* locationSelectCustomClassNames -> LocationSelectCustomClassNames

* Update documentation.json

* fix: better classnames

* Update types.ts
2024-11-25 09:59:09 +00:00
ad44ca9f2a fix: change order of eventtypes and routingforms (#17615)
* change order of eventtypes and routingforms

* for workflows

* for filtered workflows

* update slug of first expected event-type

* updated due to latest merges

* test to ensure new eventtype added is first in the list

* test for workflows

---------

Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2024-11-25 06:25:05 -03:00
Somay ChauhanandMorgan 868cf91c35 refactor: conferencing apps atoms web wrapper (#17732)
* conferencing apps atoms web wrapper

* Update DisconnectIntegrationModal.tsx

* formating changes

* handleBulkUpdateDefaultLocation -> bulkUpdateFunction

* fix: type error for handleRemoveApp

* fix: type error for handleRemoveApp

* refactor: removed ConferencingView component and migrated its functionality to ConferencingAppsViewWebWrapper

* refactor: moved AppList, AppListCard to packages/features

* fix: type changes

---------

Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2024-11-25 09:53:38 +02:00
Lauris Skraucis 8b9693ea60 fix: v2 booking location (#17817)
* fix: create-booking.input dont require location to be url

* fix: return video call url for booking location

* try to debug test

* remove test file
2024-11-25 07:34:59 +00:00
Somay Chauhan 935115d7c1 feat: added custom classnames for event-limits tab (#17478)
* added custom classnames for event limits tab

* added classNames to common types

* updated classnames to use the common exported types

* undo

* Update EventTypePlatformWrapper.tsx

* better clasnames naming

* added example classnames for demo

* fix: better calssnames

bookingFrequencyLimitSection-> bookingFrequencyLimit
totalDurationLimitSection-> totalDurationLimit
futureBookingLimitSection-> futureBookingLimit
offsetStartTimesSection-> offsetStartTimes

* suffix->addOn

* Update types.ts
2024-11-25 07:13:58 +00:00
Somay Chauhan eae74c568c feat: added custom classnames for event-advanced tab (#17479)
* feat: added custom classnames to event-advanced tab

* added more calssnames

* added classNames to common types

* undo extra changes

* Update EventTypePlatformWrapper.tsx

* Update types.ts

* Update EventTypePlatformWrapper.tsx

* better classnames

* Update EventTypePlatformWrapper.tsx

* addeed CustomEventTypeModalClassNames

* added example classnames for demo and some changes

* fix: better classnames

* timezoneLockToggle -> timezoneLock

* Update types.ts

* Update CustomEventTypeModal.tsx
2024-11-25 12:11:27 +05:30
Somay Chauhan 9e9dd6070d feat: added custom classnames to event-assignment tab atom (#17527)
* added classNames to common types

* Update types.ts

* feat: added custom classnames to event-assignment tab atom

* Update EventTypePlatformWrapper.tsx

* update: better classnames

* added example clasnames for demo

* fix: classnames

* fix: type check

* fixed classnames

* fix: better calssnames

assingAllTeamMembersToggle -> assingAllTeamMembers
enableWeightsToggle -> enableWeights
assingAllTeamMembersToggle -> assingAllTeamMembers
2024-11-25 06:10:22 +01:00
Amit SharmaandPeer Richelsen 5af10a2f61 feat: past and overlapping ooo (#17373)
* feat: past and overlapping ooo

* use existing props

* added e2e test

* disallow duplicates added tests

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2024-11-24 18:58:22 +00:00
Peer Richelsen 3936a0ae94 target blank for settings link (#17815) 2024-11-23 21:12:44 +00:00
Morgan c2a3d8b8df fix: extract trpc and react-awesome-query-builder from atoms (#17801)
* wip

* fix: externalize react-awesome-query-builder

* fix: move useSession to webWrapper

* fixup! Merge branch 'main' into fix-platform-event-types-segment

* fixup! fixup! Merge branch 'main' into fix-platform-event-types-segment

* fixup! fixup! fixup! Merge branch 'main' into fix-platform-event-types-segment

* fixup! fixup! fixup! fixup! Merge branch 'main' into fix-platform-event-types-segment
2024-11-23 12:29:16 +00:00
GitHub Actions 9bb66035c8 chore: release v4.7.10
Post release / release (push) Canceled after 0s
2024-11-22 23:01:45 +00:00
Joe Au-Yeung 226325062e Conditionally get the first assignment reason (#17808) 2024-11-22 22:23:15 +00:00
Carina Wollendorfer b61661214a fix: improvements for canceling workflow reminders (#17807) 2024-11-22 13:55:30 -07:00
Omar López 9f2047f203 fix: Got back to access token for release action
Signed-off-by: Omar López <zomars@me.com>
2024-11-22 13:53:19 -07:00
Omar López 9b0db345b0 Revert "Add new release actions"
This reverts commit 05c8b53444.
2024-11-22 13:50:27 -07:00
GitHub Actions a84a2b3152 chore: release 4.7.9
Post release / release (push) Canceled after 0s
2024-11-22 20:43:40 +00:00
Omar López 087dbefec2 fix: add missing version plugin for release action 2024-11-22 13:35:52 -07:00
Omar López 46ba793521 fix: draft release setup 2024-11-22 13:32:29 -07:00
Omar López 21ab9bf109 fix: draft release setup 2024-11-22 13:30:15 -07:00
Omar López 05c8b53444 Add new release actions
Signed-off-by: Omar López <zomars@me.com>
2024-11-22 13:27:33 -07:00
Joe Au-Yeung e7495e311b feat: Render assignment reason on routing form report (#17791) 2024-11-22 10:57:16 -07:00
Omar López 9c10ecf8ea ci: Add new release actions (#17789) 2024-11-22 10:08:26 -07:00
Lauris Skraucis 9fa73aca86 fix: v2 booking confirmation emails not sent (#17764)
* fix: round robin recurring don't send email if noEmail

* fix: noEmail: false for non platform requests

* fix: booking e2e tests

* refactor: move bookings e2e tests in folder and uniform name

* test: emails

* test: team emails

* reschedule and cancel emails with api key

* test: round robin reassign emails

* test: confirm and decline emails

* test: confirm and decline emails

* fix tests

* test: recurring booking emails

* remove unused imports and spies

* fix test

* remove log

* chore: bump libraries

* try to fix ci tests
2024-11-22 18:19:56 +02:00
Alex van Andel 8e7ce1216c Revert "feat: install gcal + gvideo by default for google signups (#17773)" (#17803)
release.yml / release (push) Canceled after 0s
This reverts commit fcd06c80e3.
2024-11-22 15:55:11 +00:00
Alex van Andel 4ddbdde553 chore: Release v4.7.8 2024-11-22 15:54:42 +00:00
Keith Williams cd24067f17 chore: Add ability to disable Sentry source maps (#17699)
* chore: Remove Sentry source maps

* Using .env to control Sentry source maps

* Trying with sourcemaps property

* Turned silent off for testing

* Completely turned off devtool
2024-11-22 15:19:52 +00:00
Alex van Andel 040adb2ddf chore: bump lockfile (#17802) 2024-11-22 15:14:39 +00:00
Somay Chauhan 4d578d2cdf feat: added customClassnames to event-availability tab (#17268)
* feat: added customClassnames to event-availability tab

* added classNames to common types

* use common classnames type

* Update types.ts

* update name

* fix: better classnames

* better calssnames

* Update types.ts
2024-11-22 10:35:20 +01:00
Anwar Sadath 1cf84a270c feat: Expose booking created at in v2 API response (#17452)
* feat: Expose booking created at in booking detailed view and v2 API response

* Fix tests

* Fix spacing

* Add API v2 tests to verify createdAt is present while creating a booking and fetching a booking

* Add test to validate createdAt after rescheduling a booking
2024-11-22 13:50:11 +05:30
Somay Chauhan d03651312e feat: added customClassnames for event-recurring tab (#17551)
* added classNames to common types

* Update types.ts

* feat: added custom classnames to event-recurring tab

* Update EventTypePlatformWrapper.tsx

* customClassnames -> customClassNames

* added example classnames for demo

* fixed classnames
2024-11-22 12:30:02 +05:30
Alex van Andel 4b34aec13d chore: Release v4.7.7 (#17786)
release.yml / release (push) Canceled after 0s
2024-11-21 22:47:11 +00:00
Anik Dhabal Babu 7ab8fdbcb4 fix: short text booking questions does not save we we click add (#17778) 2024-11-21 22:46:44 +00:00
Morgan b0bf87f3ed fix: extract clientId header manually apiv2 (#17783) 2024-11-21 22:46:13 +00:00
Joe Au-Yeung 1490642260 feat: Record Salesforce ownership assignment (#17775) 2024-11-21 14:22:39 -07:00
Udit Takkar 04b49acbb0 chore: add rescheduled by property (#17631)
* refactor: add rescheduledBy

* chore

* fix: bug

* test: add test for rescheduled booking

* fix: test
2024-11-21 11:51:19 -05:00
Joe Au-Yeung ff79e5c70e feat: Record assignment reasons for routing form and reassignment (#17767)
* Add assignment reason table

* Increment id

* Update migration

* Record assignment when manual reassignment is used

* Add comment to `getAttributesQueryValue`

* Add `getUsersAttributes`

* Record routing form routing reason

* Type fixes

* Remove unique property from bookingId

* Handle nested values
2024-11-21 16:27:19 +00:00
Alex van Andel 5d51c79739 fix: temporarily revert stricter schema so we can handle invalid DB e… (#17774) 2024-11-20 21:35:43 -07:00
Carina WollendorferandCarinaWolli 8bc2c08288 fix: no availability on reschedule with load balancing (#17770)
* don't filter if reschedule with same host

* check for reschedule

* fix filtering in handleNewBooking

* fix type error

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2024-11-21 01:22:43 +00:00
Omar López ade144aa20 ci: Re-drafts PR when changes are requested (#17137) 2024-11-21 00:59:11 +00:00
Benny Joo fcd06c80e3 feat: install gcal + gvideo by default for google signups (#17773)
* feat: install gcal + gvideo by default for google signups

* add upsertSelectedCalendar

* fix type check
2024-11-20 23:54:11 +00:00
Omar López 57bbba3d95 chore: v4.7.6 (#17766)
release.yml / release (push) Canceled after 0s
2024-11-20 13:22:47 -07:00
Amit Sharma d952e77b78 feat: Don't remove hosts when disabling 'Add all team members' (#17747)
* feat: Don't remove hosts when disabling 'Add all team members'

* retain existing host weights and priorities

* fix: merge changes, revert onInactive removal

* lint fix

* fix: weight priority dialog value
2024-11-20 17:54:42 +00:00
Anik Dhabal Babu 73989da3b7 fix: common schedule toggle for team event in never uncheckable (#17759) 2024-11-20 11:25:58 -05:00
Hariom Balhara a534d39351 Log error correctly (#17755) 2024-11-20 12:03:07 -03:00
Calcom BotandReplexica c16ef798f5 feat: update translations via @replexica (#17757)
* chore: sync with main

* feat: update translations via @replexica

---------

Co-authored-by: Replexica <support@replexica.com>
2024-11-20 14:03:09 +00:00
Lauris Skraucis 58cac84f4a feat: v2 expose booking metadata (#17708)
* feat: accept metadata and store it in database

* feat: accept metadata and store it in database

* feat: add BookingSeat.metadata column

* feat: handleNewBooking: store booking seat metadata when first time seated booking

* feat: handleSeats, createNewSeat store metadata when 2nd+ seated booking

* feat: return booking metadata in output

* tests: metadata

* test: seated bookings

* swagger

* fix: reassign bookings test

* test: use different metadata in recurring

* fix: test

* test invalid metadata

* refactor: output service

* readd new line

* chore: bump platform libraries

* refactor: metadata doc
2024-11-20 15:15:49 +02:00
Eunjae LeeandUdit Takkar 1404581f78 feat: add text filter on org member list (#17632)
* feat: improve text filters (WIP)

* move function to bottom

* apply some styles

* fix selection of TextFilterOptions

* rename value to operand

* remove unused file

* merge filters/filters into filters/utils

* fix regression of not putting url params correctly

* move makeWhereClause to filters/utils

* fix negative, empty, and not empty operators

* fix initial filtering from search state (url)

* fix type errors

* do not send an empty array to query

* update yarn.lock

* i18n for text filter operators

* extract logic as useColumnFilters()

* add missing import

* fix type error

* revert yarn.lock

* use i18n

* insensitive text match

* move data-table to @calcom/features

* fix type errors

* fix type errors

* fix type errors

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2024-11-20 09:38:10 +00:00
Anwar Sadath ff18b9a733 chore: Add support for new docker compose command to run V2 API (#17744) 2024-11-20 08:47:37 +00:00
Hariom BalharaandCarinaWolli 82e52a1948 feat: Attribute filter/Segment for Round Robin hosts (#17361)
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2024-11-20 10:22:53 +05:30
Vijay e3abd35ba7 fix: upgrade prismock to 1.33.4 (#17735)
* upgrade prismock, removed 2 hacks, update 3 tests

* yarnlock changes
2024-11-19 23:20:27 +00:00
sean-brydon dfdb04acf4 chore: routing table nits (#17738)
* portal date preset selection with a high zindex

* increase height of data-table

* skeleton fixes
2024-11-19 23:04:45 +00:00
Omar López 352a6ba8cf fix: Better error handling for user session (#17753) 2024-11-19 22:57:37 +00:00
Eunjae Lee 997d6a75e8 fix: fix wrong dates on OOO edit dialog (#17736)
* fix: fix wrong dates on OOO edit dialog

* do not use utc() on the browser side
2024-11-19 19:54:12 -03:00
NizzyandKeith Williams 757e3a2faf fix: PostHog event tracking (#17653)
* posthog provider fix

* posthog fixes w/ trpc changes

* Update yarn.lock

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2024-11-19 22:52:18 +00:00
Omar López 1b457f82a1 chore: v4.7.5 (#17750)
release.yml / release (push) Canceled after 0s
2024-11-19 20:44:58 +00:00
Omar López 16573717b4 fix: identify org-wide features per user (#17010) 2024-11-19 15:21:38 -05:00
Omar LópezandAnik Dhabal Babu 97b920744e feat: adds last active timestamp (#17261)
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2024-11-20 00:38:20 +05:30
Joe Au-Yeung 6cfe9ff88d feat: Salesforce: Write booking question values to attendee record in Salesforce [CAL-4731] (#17650) 2024-11-19 10:30:34 -07:00
Anik Dhabal Babu 7c5292e13b fix (#17734) 2024-11-19 18:37:39 +05:30
Morgan 3cd4e34406 fix: platform libraries build vite.config (#17743) 2024-11-19 12:05:28 +00:00
sean-brydon a99eda21af chore: booking status reporting (#17733)
* move booking status into new column

* update i18n

* nowrap + badge for submitedAt to provide consistant feel to other dates
2024-11-19 11:24:45 +00:00
Omar López 03e2c6278d fix: cron api key checks (#17730) 2024-11-19 00:18:53 +00:00
1122 changed files with 43416 additions and 14297 deletions

No files matched your search

+9 -13
View File
@@ -119,7 +119,7 @@ NEXT_PUBLIC_FRESHCHAT_HOST=
# Google OAuth credentials # Google OAuth credentials
# To enable Login with Google you need to: # To enable Login with Google you need to:
# 1. Set `GOOGLE_API_CREDENTIALS` above # 1. Set `GOOGLE_API_CREDENTIALS` below
# 2. Set `GOOGLE_LOGIN_ENABLED` to `true` # 2. Set `GOOGLE_LOGIN_ENABLED` to `true`
# When self-hosting please ensure you configure the Google integration as an Internal app so no one else can login to your instance # When self-hosting please ensure you configure the Google integration as an Internal app so no one else can login to your instance
# @see https://support.google.com/cloud/answer/6158849#public-and-internal&zippy=%2Cpublic-and-internal-applications # @see https://support.google.com/cloud/answer/6158849#public-and-internal&zippy=%2Cpublic-and-internal-applications
@@ -147,6 +147,8 @@ NEXT_PUBLIC_SENDGRID_SENDER_NAME=
# Used for capturing exceptions and logging messages # Used for capturing exceptions and logging messages
NEXT_PUBLIC_SENTRY_DSN= NEXT_PUBLIC_SENTRY_DSN=
SENTRY_DEBUG= SENTRY_DEBUG=
SENTRY_DISABLE_CLIENT_SOURCE_MAPS=
SENTRY_DISABLE_SERVER_SOURCE_MAPS=
SENTRY_MAX_SPANS= SENTRY_MAX_SPANS=
SENTRY_TRACES_SAMPLE_RATE= SENTRY_TRACES_SAMPLE_RATE=
@@ -237,7 +239,8 @@ CLOUDFLARE_TURNSTILE_SECRET=
NEXT_PUBLIC_TEAM_IMPERSONATION=false NEXT_PUBLIC_TEAM_IMPERSONATION=false
# Close.com internal CRM # Close.com internal CRM
CLOSECOM_API_KEY= CLOSECOM_CLIENT_ID=
CLOSECOM_CLIENT_SECRET=
# Sendgrid internal sync service # Sendgrid internal sync service
SENDGRID_SYNC_API_KEY= SENDGRID_SYNC_API_KEY=
@@ -345,8 +348,6 @@ E2E_TEST_OIDC_USER_PASSWORD=
AB_TEST_BUCKET_PROBABILITY=50 AB_TEST_BUCKET_PROBABILITY=50
# whether we redirect to the future/event-types from event-types or not # whether we redirect to the future/event-types from event-types or not
APP_ROUTER_EVENT_TYPES_ENABLED=0 APP_ROUTER_EVENT_TYPES_ENABLED=0
APP_ROUTER_SETTINGS_DEVELOPER_ENABLED=0
APP_ROUTER_SETTINGS_ORG_ENABLED=0
APP_ROUTER_APPS_INSTALLED_CATEGORY_ENABLED=0 APP_ROUTER_APPS_INSTALLED_CATEGORY_ENABLED=0
APP_ROUTER_APPS_SLUG_ENABLED=0 APP_ROUTER_APPS_SLUG_ENABLED=0
APP_ROUTER_APPS_SLUG_SETUP_ENABLED=0 APP_ROUTER_APPS_SLUG_SETUP_ENABLED=0
@@ -356,14 +357,10 @@ APP_ROUTER_APPS_CATEGORIES_ENABLED=0
APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED=0 APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED=0
APP_ROUTER_BOOKING_ENABLED=0 APP_ROUTER_BOOKING_ENABLED=0
APP_ROUTER_BOOKINGS_STATUS_ENABLED=0 APP_ROUTER_BOOKINGS_STATUS_ENABLED=0
APP_ROUTER_WORKFLOWS_ENABLED=0
APP_ROUTER_SETTINGS_TEAMS_ENABLED=0
APP_ROUTER_GETTING_STARTED_STEP_ENABLED=0 APP_ROUTER_GETTING_STARTED_STEP_ENABLED=0
APP_ROUTER_APPS_ENABLED=0 APP_ROUTER_APPS_ENABLED=0
APP_ROUTER_VIDEO_ENABLED=0
APP_ROUTER_TEAM_ENABLED=0 APP_ROUTER_TEAM_ENABLED=0
APP_ROUTER_TEAMS_ENABLED=0 APP_ROUTER_TEAMS_ENABLED=0
APP_ROUTER_AVAILABILITY_ENABLED=0
APP_ROUTER_AUTH_FORGOT_PASSWORD_ENABLED=0 APP_ROUTER_AUTH_FORGOT_PASSWORD_ENABLED=0
APP_ROUTER_AUTH_LOGIN_ENABLED=0 APP_ROUTER_AUTH_LOGIN_ENABLED=0
APP_ROUTER_AUTH_LOGOUT_ENABLED=0 APP_ROUTER_AUTH_LOGOUT_ENABLED=0
@@ -372,10 +369,6 @@ APP_ROUTER_AUTH_SAML_ENABLED=0
APP_ROUTER_AUTH_ERROR_ENABLED=0 APP_ROUTER_AUTH_ERROR_ENABLED=0
APP_ROUTER_AUTH_PLATFORM_ENABLED=0 APP_ROUTER_AUTH_PLATFORM_ENABLED=0
APP_ROUTER_AUTH_OAUTH2_ENABLED=0 APP_ROUTER_AUTH_OAUTH2_ENABLED=0
APP_ROUTER_MORE_ENABLED=0
APP_ROUTER_MAINTENANCE_ENABLED=0
APP_ROUTER_UPGRADE_ENABLED=0
APP_ROUTER_CONNECT_AND_JOIN_ENABLED=0
# disable setry server source maps # disable setry server source maps
SENTRY_DISABLE_SERVER_WEBPACK_PLUGIN=1 SENTRY_DISABLE_SERVER_WEBPACK_PLUGIN=1
@@ -414,4 +407,7 @@ NEXT_PUBLIC_WEBSITE_TERMS_URL=
NEXT_PUBLIC_LOGGER_LEVEL= NEXT_PUBLIC_LOGGER_LEVEL=
# Used to use Replexica SDK, a tool for real-time AI-powered localization # Used to use Replexica SDK, a tool for real-time AI-powered localization
REPLEXICA_API_KEY= REPLEXICA_API_KEY=
# Comma-separated list of DSyncData.directoryId to log SCIM API requests for. It can be enabled temporarily for debugging the requests being sent to SCIM server.
DIRECTORY_IDS_TO_LOG=
+11
View File
@@ -0,0 +1,11 @@
/packages/app-store/applecalendar/**/* @calcom/Foundation
/packages/app-store/caldavcalendar/**/* @calcom/Foundation
/packages/app-store/exchange2013calendar/**/* @calcom/Foundation
/packages/app-store/exchange2016calendar/**/* @calcom/Foundation
/packages/app-store/exchangecalendar/**/* @calcom/Foundation
/packages/app-store/feishucalendar/**/* @calcom/Foundation
/packages/app-store/googlecalendar/**/* @calcom/Foundation
/packages/app-store/ics-feedcalendar/**/* @calcom/Foundation
/packages/app-store/larkcalendar/**/* @calcom/Foundation
/packages/app-store/office365calendar/**/* @calcom/Foundation
/packages/app-store/zohocalendar/**/* @calcom/Foundation
+50
View File
@@ -0,0 +1,50 @@
name: Draft release
run-name: Draft release ${{ inputs.next_version }}
on:
workflow_dispatch:
inputs:
next_version:
required: true
type: string
description: 'Version name'
permissions:
contents: write
jobs:
draft_release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: 'main'
token: ${{ secrets.GH_ACCESS_TOKEN }}
- name: Configure git
run: |
git config --local user.email "github-actions@github.com"
git config --local user.name "GitHub Actions"
- uses: ./.github/actions/yarn-install
- name: Bump version
run: |
cd apps/web
yarn version ${{ inputs.next_version }}
- name: Commit changes
run: |
git add .
git commit -m "chore: release v${{ inputs.next_version }}"
git push
- name: Draft release
run: gh release create v$VERSION --generate-notes --draft
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ inputs.next_version }}
+1
View File
@@ -17,6 +17,7 @@ env:
NEXTAUTH_URL: ${{ secrets.CI_NEXTAUTH_URL }} NEXTAUTH_URL: ${{ secrets.CI_NEXTAUTH_URL }}
NODE_OPTIONS: --max-old-space-size=29000 NODE_OPTIONS: --max-old-space-size=29000
REDIS_URL: "redis://localhost:6379" REDIS_URL: "redis://localhost:6379"
REPLEXICA_API_KEY: ${{ secrets.CI_REPLEXICA_API_KEY }}
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }} STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_API_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }} STRIPE_API_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }} STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
+3 -2
View File
@@ -17,7 +17,6 @@ env:
DAILY_API_KEY: ${{ secrets.CI_DAILY_API_KEY }} DAILY_API_KEY: ${{ secrets.CI_DAILY_API_KEY }}
DATABASE_URL: ${{ secrets.CI_DATABASE_URL }} DATABASE_URL: ${{ secrets.CI_DATABASE_URL }}
DATABASE_DIRECT_URL: ${{ secrets.CI_DATABASE_URL }} DATABASE_DIRECT_URL: ${{ secrets.CI_DATABASE_URL }}
DEPLOYSENTINEL_API_KEY: ${{ secrets.DEPLOYSENTINEL_API_KEY }}
E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }} E2E_TEST_APPLE_CALENDAR_EMAIL: ${{ secrets.E2E_TEST_APPLE_CALENDAR_EMAIL }}
E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }} E2E_TEST_APPLE_CALENDAR_PASSWORD: ${{ secrets.E2E_TEST_APPLE_CALENDAR_PASSWORD }}
E2E_TEST_MAILHOG_ENABLED: ${{ vars.E2E_TEST_MAILHOG_ENABLED }} E2E_TEST_MAILHOG_ENABLED: ${{ vars.E2E_TEST_MAILHOG_ENABLED }}
@@ -51,7 +50,8 @@ env:
jobs: jobs:
analyze: analyze:
if: always() if: always()
runs-on: buildjet-2vcpu-ubuntu-2204 runs-on: buildjet-4vcpu-ubuntu-2204
timeout-minutes: 30
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: ./.github/actions/dangerous-git-checkout - uses: ./.github/actions/dangerous-git-checkout
@@ -60,6 +60,7 @@ jobs:
- name: Analyze bundle - name: Analyze bundle
run: | run: |
cd apps/web cd apps/web
export NODE_OPTIONS="--max_old_space_size=8192"
npx -p nextjs-bundle-analysis@0.5.0 report npx -p nextjs-bundle-analysis@0.5.0 report
- name: Upload bundle - name: Upload bundle
@@ -0,0 +1,21 @@
name: Changes Requested
on:
pull_request_review:
types: [submitted]
jobs:
changes-requested:
if: github.event.review.state == 'changes_requested'
runs-on: ubuntu-latest
steps:
- name: Explanation
run: echo "This workflow triggers a workflow_run; it's necessary because otherwise the repo secrets aren't available for 'pull_request_review' events from externally forked pull requests"
- name: Save PR number to context.json
run: |
printf '{
"pr_number": ${{ github.event.pull_request.number }}
}' >> context.json
- uses: actions/upload-artifact@v4
with:
name: context.json
path: ./
+25
View File
@@ -0,0 +1,25 @@
name: Post release
on:
workflow_dispatch:
push:
# Pattern matched against refs/tags
tags:
- "*"
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: 'main'
token: ${{ secrets.GH_ACCESS_TOKEN }}
- name: Configure git
run: |
git config --local user.email "github-actions@github.com"
git config --local user.name "GitHub Actions"
- run: git push origin +main:production
+104
View File
@@ -0,0 +1,104 @@
name: Re-draft
on:
workflow_run:
workflows:
- Changes Requested
types:
- completed
permissions:
contents: write
issues: write
pull-requests: write
statuses: write
jobs:
download-context:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- name: 'Download artifact'
uses: actions/github-script@v7
with:
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == "context.json"
})[0];
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
let fs = require('fs');
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/context.zip`, Buffer.from(download.data));
- name: 'Unzip artifact'
run: unzip context.zip
- name: 'Return Parsed JSON'
uses: actions/github-script@v7
id: return-parsed-json
with:
script: |
let fs = require('fs');
let data = fs.readFileSync('./context.json');
return JSON.parse(data);
outputs:
pr_number: ${{fromJSON(steps.return-parsed-json.outputs.result).pr_number}}
re-draft:
needs:
- download-context
permissions:
contents: write
issues: write
pull-requests: write
statuses: write
runs-on: ubuntu-latest
steps:
- run: echo "This PR was rejected"
- name: Convert PR to draft when changes are requested
uses: actions/github-script@v7
with:
script: |
async function getPullRequestId() {
const pull_number = ${{ needs.download-context.outputs.pr_number }};
const pullRequest = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number,
});
if (!pullRequest.data.node_id) throw new Error(`pullRequestId no found for '${pull_number}'`);
return pullRequest.data.node_id;
}
const query = `
mutation($id: ID!) {
convertPullRequestToDraft(input: { pullRequestId: $id }) {
pullRequest {
id
number
isDraft
}
}
}
`;
const pullRequestId = await getPullRequestId();
const variables = {
id: pullRequestId,
}
const response = await github.graphql(query, variables)
if (!response.convertPullRequestToDraft) {
throw new Error("Failed to convert pull request to draft");
}
console.info("Pull request successfully converted to draft.");
console.info(`Draft conversion response: ${JSON.stringify(response, null, 2)}`);
-17
View File
@@ -1,17 +0,0 @@
on:
workflow_dispatch:
push:
# Pattern matched against refs/tags
tags:
- "*" # Push events to every tag not containing /
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: main # Always checkout main even for tagged releases
fetch-depth: 0
token: ${{ secrets.GH_ACCESS_TOKEN }}
- run: git push origin +main:production
+2
View File
@@ -1,6 +1,8 @@
name: Unit name: Unit
on: on:
workflow_call: workflow_call:
env:
REPLEXICA_API_KEY: ${{ secrets.CI_REPLEXICA_API_KEY }}
permissions: permissions:
contents: read contents: read
jobs: jobs:
+550
View File
@@ -0,0 +1,550 @@
/* eslint-disable */
//prettier-ignore
module.exports = {
name: "@yarnpkg/plugin-version",
factory: function (require) {
var plugin=(()=>{var ZB=Object.create,zy=Object.defineProperty,$B=Object.defineProperties,eU=Object.getOwnPropertyDescriptor,tU=Object.getOwnPropertyDescriptors,nU=Object.getOwnPropertyNames,uS=Object.getOwnPropertySymbols,rU=Object.getPrototypeOf,oS=Object.prototype.hasOwnProperty,iU=Object.prototype.propertyIsEnumerable;var lS=(i,o,f)=>o in i?zy(i,o,{enumerable:!0,configurable:!0,writable:!0,value:f}):i[o]=f,E0=(i,o)=>{for(var f in o||(o={}))oS.call(o,f)&&lS(i,f,o[f]);if(uS)for(var f of uS(o))iU.call(o,f)&&lS(i,f,o[f]);return i},Gf=(i,o)=>$B(i,tU(o)),uU=i=>zy(i,"__esModule",{value:!0});var ce=(i,o)=>()=>(o||i((o={exports:{}}).exports,o),o.exports),sS=(i,o)=>{for(var f in o)zy(i,f,{get:o[f],enumerable:!0})},oU=(i,o,f)=>{if(o&&typeof o=="object"||typeof o=="function")for(let p of nU(o))!oS.call(i,p)&&p!=="default"&&zy(i,p,{get:()=>o[p],enumerable:!(f=eU(o,p))||f.enumerable});return i},Mi=i=>oU(uU(zy(i!=null?ZB(rU(i)):{},"default",i&&i.__esModule&&"default"in i?{get:()=>i.default,enumerable:!0}:{value:i,enumerable:!0})),i);var eD=ce((F$,aS)=>{function lU(i,o){for(var f=-1,p=i==null?0:i.length,E=Array(p);++f<p;)E[f]=o(i[f],f,i);return E}aS.exports=lU});var cS=ce((b$,fS)=>{function sU(){this.__data__=[],this.size=0}fS.exports=sU});var tD=ce((P$,dS)=>{function aU(i,o){return i===o||i!==i&&o!==o}dS.exports=aU});var qy=ce((I$,pS)=>{var fU=tD();function cU(i,o){for(var f=i.length;f--;)if(fU(i[f][0],o))return f;return-1}pS.exports=cU});var vS=ce((B$,hS)=>{var dU=qy(),pU=Array.prototype,hU=pU.splice;function vU(i){var o=this.__data__,f=dU(o,i);if(f<0)return!1;var p=o.length-1;return f==p?o.pop():hU.call(o,f,1),--this.size,!0}hS.exports=vU});var yS=ce((U$,mS)=>{var mU=qy();function yU(i){var o=this.__data__,f=mU(o,i);return f<0?void 0:o[f][1]}mS.exports=yU});var _S=ce((j$,gS)=>{var gU=qy();function _U(i){return gU(this.__data__,i)>-1}gS.exports=_U});var DS=ce((z$,ES)=>{var EU=qy();function DU(i,o){var f=this.__data__,p=EU(f,i);return p<0?(++this.size,f.push([i,o])):f[p][1]=o,this}ES.exports=DU});var Hy=ce((q$,wS)=>{var wU=cS(),SU=vS(),TU=yS(),CU=_S(),xU=DS();function jv(i){var o=-1,f=i==null?0:i.length;for(this.clear();++o<f;){var p=i[o];this.set(p[0],p[1])}}jv.prototype.clear=wU;jv.prototype.delete=SU;jv.prototype.get=TU;jv.prototype.has=CU;jv.prototype.set=xU;wS.exports=jv});var TS=ce((H$,SS)=>{var AU=Hy();function RU(){this.__data__=new AU,this.size=0}SS.exports=RU});var xS=ce((W$,CS)=>{function OU(i){var o=this.__data__,f=o.delete(i);return this.size=o.size,f}CS.exports=OU});var RS=ce((V$,AS)=>{function kU(i){return this.__data__.get(i)}AS.exports=kU});var kS=ce((G$,OS)=>{function MU(i){return this.__data__.has(i)}OS.exports=MU});var nD=ce((Y$,MS)=>{var NU=typeof global=="object"&&global&&global.Object===Object&&global;MS.exports=NU});var Yf=ce((K$,NS)=>{var LU=nD(),FU=typeof self=="object"&&self&&self.Object===Object&&self,bU=LU||FU||Function("return this")();NS.exports=bU});var zv=ce((X$,LS)=>{var PU=Yf(),IU=PU.Symbol;LS.exports=IU});var BS=ce((Q$,bS)=>{var PS=zv(),IS=Object.prototype,BU=IS.hasOwnProperty,UU=IS.toString,Wy=PS?PS.toStringTag:void 0;function jU(i){var o=BU.call(i,Wy),f=i[Wy];try{i[Wy]=void 0;var p=!0}catch(t){}var E=UU.call(i);return p&&(o?i[Wy]=f:delete i[Wy]),E}bS.exports=jU});var jS=ce((J$,US)=>{var zU=Object.prototype,qU=zU.toString;function HU(i){return qU.call(i)}US.exports=HU});var Qp=ce((Z$,zS)=>{var qS=zv(),WU=BS(),VU=jS(),GU="[object Null]",YU="[object Undefined]",HS=qS?qS.toStringTag:void 0;function KU(i){return i==null?i===void 0?YU:GU:HS&&HS in Object(i)?WU(i):VU(i)}zS.exports=KU});var qv=ce(($$,WS)=>{function XU(i){var o=typeof i;return i!=null&&(o=="object"||o=="function")}WS.exports=XU});var rD=ce((eee,VS)=>{var QU=Qp(),JU=qv(),ZU="[object AsyncFunction]",$U="[object Function]",ej="[object GeneratorFunction]",tj="[object Proxy]";function nj(i){if(!JU(i))return!1;var o=QU(i);return o==$U||o==ej||o==ZU||o==tj}VS.exports=nj});var YS=ce((tee,GS)=>{var rj=Yf(),ij=rj["__core-js_shared__"];GS.exports=ij});var QS=ce((nee,KS)=>{var iD=YS(),XS=function(){var i=/[^.]+$/.exec(iD&&iD.keys&&iD.keys.IE_PROTO||"");return i?"Symbol(src)_1."+i:""}();function uj(i){return!!XS&&XS in i}KS.exports=uj});var uD=ce((ree,JS)=>{var oj=Function.prototype,lj=oj.toString;function sj(i){if(i!=null){try{return lj.call(i)}catch(o){}try{return i+""}catch(o){}}return""}JS.exports=sj});var $S=ce((iee,ZS)=>{var aj=rD(),fj=QS(),cj=qv(),dj=uD(),pj=/[\\^$.*+?()[\]{}|]/g,hj=/^\[object .+?Constructor\]$/,vj=Function.prototype,mj=Object.prototype,yj=vj.toString,gj=mj.hasOwnProperty,_j=RegExp("^"+yj.call(gj).replace(pj,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");function Ej(i){if(!cj(i)||fj(i))return!1;var o=aj(i)?_j:hj;return o.test(dj(i))}ZS.exports=Ej});var tT=ce((uee,eT)=>{function Dj(i,o){return i==null?void 0:i[o]}eT.exports=Dj});var sd=ce((oee,nT)=>{var wj=$S(),Sj=tT();function Tj(i,o){var f=Sj(i,o);return wj(f)?f:void 0}nT.exports=Tj});var L_=ce((lee,rT)=>{var Cj=sd(),xj=Line truncated
in `+(X||"Unknown")+Me}var ft=1;function Ke(X){return X._status===ft?X._result:null}function jt(X,_e,Ne){var Me=_e.displayName||_e.name||"";return X.displayName||(Me!==""?Ne+"("+Me+")":Ne)}function $t(X){if(X==null)return null;if(typeof X.tag=="number"&&ct("Received an unexpected object in getComponentName(). This is likely a bug in React. Please file an issue."),typeof X=="function")return X.displayName||X.name||null;if(typeof X=="string")return X;switch(X){case k:return"Fragment";case t:return"Portal";case N:return"Profiler";case L:return"StrictMode";case ne:return"Suspense";case m:return"SuspenseList"}if(typeof X=="object")switch(X.$$typeof){case U:return"Context.Consumer";case C:return"Context.Provider";case W:return jt(X,X.render,"ForwardRef");case we:return $t(X.type);case he:return $t(X.render);case Se:{var _e=X,Ne=Ke(_e);if(Ne)return $t(Ne);break}}return null}var at={},Q=null;function ae(X){Q=X}at.getCurrentStack=null,at.getStackAddendum=function(){var X="";if(Q){var _e=$t(Q.type),Ne=Q._owner;X+=$e(_e,Q._source,Ne&&$t(Ne.type))}var Me=at.getCurrentStack;return Me&&(X+=Me()||""),X};var Ce={current:!1},ue={ReactCurrentDispatcher:Ge,ReactCurrentBatchConfig:rt,ReactCurrentOwner:wt,IsSomeRendererActing:Ce,assign:i};i(ue,{ReactDebugCurrentFrame:at,ReactComponentTreeHook:{}});function je(X){{for(var _e=arguments.length,Ne=new Array(_e>1?_e-1:0),Me=1;Me<_e;Me++)Ne[Me-1]=arguments[Me];At("warn",X,Ne)}}function ct(X){{for(var _e=arguments.length,Ne=new Array(_e>1?_e-1:0),Me=1;Me<_e;Me++)Ne[Me-1]=arguments[Me];At("error",X,Ne)}}function At(X,_e,Ne){{var Me=Ne.length>0&&typeof Ne[Ne.length-1]=="string"&&Ne[Ne.length-1].indexOf(`
in`)===0;if(!Me){var dt=ue.ReactDebugCurrentFrame,Hn=dt.getStackAddendum();Hn!==""&&(_e+="%s",Ne=Ne.concat([Hn]))}var Dn=Ne.map(function(Su){return""+Su});Dn.unshift("Warning: "+_e),Function.prototype.apply.call(console[X],console,Dn);try{var or=0,mi="Warning: "+_e.replace(/%s/g,function(){return Ne[or++]});throw new Error(mi)}catch(Su){}}}var en={};function ln(X,_e){{var Ne=X.constructor,Me=Ne&&(Ne.displayName||Ne.name)||"ReactClass",dt=Me+"."+_e;if(en[dt])return;ct("Can't call %s on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the %s component.",_e,Me),en[dt]=!0}}var An={isMounted:function(X){return!1},enqueueForceUpdate:function(X,_e,Ne){ln(X,"forceUpdate")},enqueueReplaceState:function(X,_e,Ne,Me){ln(X,"replaceState")},enqueueSetState:function(X,_e,Ne,Me){ln(X,"setState")}},nr={};Object.freeze(nr);function un(X,_e,Ne){this.props=X,this.context=_e,this.refs=nr,this.updater=Ne||An}un.prototype.isReactComponent={},un.prototype.setState=function(X,_e){if(!(typeof X=="object"||typeof X=="function"||X==null))throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,X,_e,"setState")},un.prototype.forceUpdate=function(X){this.updater.enqueueForceUpdate(this,X,"forceUpdate")};{var Wt={isMounted:["isMounted","Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks."],replaceState:["replaceState","Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236)."]},vr=function(X,_e){Object.defineProperty(un.prototype,X,{get:function(){je("%s(...) is deprecated in plain JavaScript React classes. %s",_e[0],_e[1])}})};for(var w in Wt)Wt.hasOwnProperty(w)&&vr(w,Wt[w])}function Ut(){}Ut.prototype=un.prototype;function Vn(X,_e,Ne){this.props=X,this.context=_e,this.refs=nr,this.updater=Ne||An}var fr=Vn.prototype=new Ut;fr.constructor=Vn,i(fr,un.prototype),fr.isPureReactComponent=!0;function Fr(){var X={current:null};return Object.seal(X),X}var ur=Object.prototype.hasOwnProperty,br={key:!0,ref:!0,__self:!0,__source:!0},Kt,vu,a0;a0={};function So(X){if(ur.call(X,"ref")){var _e=Object.getOwnPropertyDescriptor(X,"ref").get;if(_e&&_e.isReactWarning)return!1}return X.ref!==void 0}function Go(X){if(ur.call(X,"key")){var _e=Object.getOwnPropertyDescriptor(X,"key").get;if(_e&&_e.isReactWarning)return!1}return X.key!==void 0}function Os(X,_e){var Ne=function(){Kt||(Kt=!0,ct("%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://fb.me/react-special-props)",_e))};Ne.isReactWarning=!0,Object.defineProperty(X,"key",{get:Ne,configurable:!0})}function Yo(X,_e){var Ne=function(){vu||(vu=!0,ct("%s: `ref` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://fb.me/react-special-props)",_e))};Ne.isReactWarning=!0,Object.defineProperty(X,"ref",{get:Ne,configurable:!0})}function Ko(X){if(typeof X.ref=="string"&&wt.current&&X.__self&&wt.current.stateNode!==X.__self){var _e=$t(wt.current.type);a0[_e]||(ct('Component "%s" contains the string ref "%s". Support for string refs will be removed in a future major release. This case cannot be automatically converted to an arrow function. We ask you to manually fix this case by using useRef() or createRef() instead. Learn more about using refs safely here: https://fb.me/react-strict-mode-string-ref',$t(wt.current.type),X.ref),a0[_e]=!0)}}var qt=function(X,_e,Ne,Me,dt,Hn,Dn){var or={$$typeof:E,type:X,key:_e,ref:Ne,props:Dn,_owner:Hn};return or._store={},Object.defineProperty(or._store,"validated",{configurable:!1,enumerable:!1,writable:!0,value:!1}),Object.defineProperty(or,"_self",{configurable:!1,enumerable:!1,writable:!1,value:Me}),Object.defineProperty(or,"_source",{configurable:!1,enumerable:!1,writable:!1,value:dt}),Object.freeze&&(Object.freeze(or.props),Object.freeze(or)),or};function _i(X,_e,Ne){var Me,dt={},Hn=null,Dn=null,or=null,mi=null;if(_e!=null){So(_e)&&(Dn=_e.ref,Ko(_e)),Go(_e)&&(Hn=""+_e.key),or=_e.__self===void 0?null:_e.__self,mi=_e.__source===void 0?null:_e.__source;for(Me in _e)ur.call(_e,Me)&&!br.hasOwnProperty(Me)&&(dt[Me]=_e[Me])}var Su=arguments.length-2;if(Su===1)dt.children=Ne;else if(Su>1){for(var bu=Array(Su),Pu=0;Pu<Su;Pu++)bu[Pu]=arguments[Pu+2];Object.freeze&&Object.freeze(bu),dt.children=bu}if(X&&X.defaultProps){var mu=X.defaultProps;for(Me in mu)dt[Me]===void 0&&(dt[Me]=mu[Me])}if(Hn||Dn){var yi=typeof X=="function"?X.displayName||X.name||"Unknown":X;Hn&&Os(dt,yi),Dn&&Yo(dt,yi)}return qt(X,Hn,Dn,or,mi,wt.current,dt)}function eu Line truncated
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.`);return X}function Wl(X,_e){var Ne=Ui();if(_e!==void 0&&ct("useContext() second argument is reserved for future use in React. Passing it is not supported. You passed: %s.%s",_e,typeof _e=="number"&&Array.isArray(arguments[2])?`
Did you call array.map(useContext)? Calling Hooks inside a loop is not supported. Learn more at https://fb.me/rules-of-hooks`:""),X._context!==void 0){var Me=X._context;Me.Consumer===X?ct("Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be removed in a future major release. Did you mean to call useContext(Context) instead?"):Me.Provider===X&&ct("Calling useContext(Context.Provider) is not supported. Did you mean to call useContext(Context) instead?")}return Ne.useContext(X,_e)}function xo(X){var _e=Ui();return _e.useState(X)}function ni(X,_e,Ne){var Me=Ui();return Me.useReducer(X,_e,Ne)}function oo(X){var _e=Ui();return _e.useRef(X)}function Vl(X,_e){var Ne=Ui();return Ne.useEffect(X,_e)}function Ao(X,_e){var Ne=Ui();return Ne.useLayoutEffect(X,_e)}function Ms(X,_e){var Ne=Ui();return Ne.useCallback(X,_e)}function Xn(X,_e){var Ne=Ui();return Ne.useMemo(X,_e)}function Qo(X,_e,Ne){var Me=Ui();return Me.useImperativeHandle(X,_e,Ne)}function lo(X,_e){{var Ne=Ui();return Ne.useDebugValue(X,_e)}}var b0;b0=!1;function yl(){if(wt.current){var X=$t(wt.current.type);if(X)return`
Check the render method of \``+X+"`."}return""}function Ro(X){if(X!==void 0){var _e=X.fileName.replace(/^.*[\\\/]/,""),Ne=X.lineNumber;return`
Check your code at `+_e+":"+Ne+"."}return""}function Et(X){return X!=null?Ro(X.__source):""}var Pt={};function Bn(X){var _e=yl();if(!_e){var Ne=typeof X=="string"?X:X.displayName||X.name;Ne&&(_e=`
Check the top-level render call using <`+Ne+">.")}return _e}function Ir(X,_e){if(!(!X._store||X._store.validated||X.key!=null)){X._store.validated=!0;var Ne=Bn(_e);if(!Pt[Ne]){Pt[Ne]=!0;var Me="";X&&X._owner&&X._owner!==wt.current&&(Me=" It was passed a child from "+$t(X._owner.type)+"."),ae(X),ct('Each child in a list should have a unique "key" prop.%s%s See https://fb.me/react-warning-keys for more information.',Ne,Me),ae(null)}}}function ji(X,_e){if(typeof X=="object"){if(Array.isArray(X))for(var Ne=0;Ne<X.length;Ne++){var Me=X[Ne];mr(Me)&&Ir(Me,_e)}else if(mr(X))X._store&&(X._store.validated=!0);else if(X){var dt=Ue(X);if(typeof dt=="function"&&dt!==X.entries)for(var Hn=dt.call(X),Dn;!(Dn=Hn.next()).done;)mr(Dn.value)&&Ir(Dn.value,_e)}}}function Wr(X){{var _e=X.type;if(_e==null||typeof _e=="string")return;var Ne=$t(_e),Me;if(typeof _e=="function")Me=_e.propTypes;else if(typeof _e=="object"&&(_e.$$typeof===W||_e.$$typeof===we))Me=_e.propTypes;else return;Me?(ae(X),o(Me,X.props,"prop",Ne,at.getStackAddendum),ae(null)):_e.PropTypes!==void 0&&!b0&&(b0=!0,ct("Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?",Ne||"Unknown")),typeof _e.getDefaultProps=="function"&&!_e.getDefaultProps.isReactClassApproved&&ct("getDefaultProps is only used on classic React.createClass definitions. Use a static property named `defaultProps` instead.")}}function wu(X){{ae(X);for(var _e=Object.keys(X.props),Ne=0;Ne<_e.length;Ne++){var Me=_e[Ne];if(Me!=="children"&&Me!=="key"){ct("Invalid prop `%s` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.",Me);break}}X.ref!==null&&ct("Invalid attribute `ref` supplied to `React.Fragment`."),ae(null)}}function c0(X,_e,Ne){var Me=Yi(X);if(!Me){var dt="";(X===void 0||typeof X=="object"&&X!==null&&Object.keys(X).length===0)&&(dt+=" You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.");var Hn=Et(_e);Hn?dt+=Hn:dt+=yl();var Dn;X===null?Dn="null":Array.isArray(X)?Dn="array":X!==void 0&&X.$$typeof===E?(Dn="<"+($t(X.type)||"Unknown")+" />",dt=" Did you accidentally export a JSX literal instead of a component?"):Dn=typeof X,ct("React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",Dn,dt)}var or=_i.apply(this,arguments);if(or==null)return or;if(Me)for(var mi=2;mi<arguments.length;mi++)ji(arguments[mi],X);return X===k?wu(or):Wr(or),or}var Ti=!1;function d0(X){var _e=c0.bind(null,X);return _e.type=X,Ti||(Ti=!0,je("React.createFactory() is deprecated and will be removed in a future major release. Consider using JSX or use React.createElement() directly instead.")),Object.defineProperty(_e,"type",{enumerable:!1,get:function(){return je("Factory.type is deprecated. Access the class directly before passing it to createFactory."),Object.defineProperty(this,"type",{value:X}),X}}),_e}function as(X,_e,Ne){for(var Me=ai.apply(this,arguments),dt=2;dt<arguments.length;dt++)ji(arguments[dt],Me.type);return Wr(Me),Me}try{var St=Object.freeze({}),so=new Map([[St,null]]),Jo=new Set([St]);so.set(0,0),Jo.add(0)}catch(X){}var Gl=c0,Fu=as,fs=d0,P0={map:fi,forEach:Pr,count:Zt,toArray:Ln,only:Di};pu.Children=P0,pu.Component=un,pu.Fragment=k,pu.Profiler=N,pu.PureComponent=Vn,pu.StrictMode=L,pu.Suspense=ne,pu.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED=ue,pu.cloneElement=Fu,pu.createContext=ci,pu.createElement=Gl,pu.createFactory=fs,pu.createRef=Fr,pu.forwardRef=Du,pu.isValidElement=mr,pu.lazy=Ht,pu.memo=Y0,pu.useCallback=Ms,pu.useContext=Wl,pu.useDebugValue=lo,pu.useEffect=Vl,pu.useImperativeHandle=Qo,pu.useLayoutEffect=Ao,pu.useMemo=Xn,pu.useReducer=ni,pu.useRef=oo,pu.useState=xo,pu.version=f}()});var su=ce((Ene,WD)=>{"use strict";process.env.NODE_ENV==="production"?WD.exports=k5():WD.exports=I5()});var B5=ce((nm,rg)=>{(function(){var i,o="4.17.21",f=200,p="Unsupported core-js use. Try https://npms.io/search?q=ponyfill.",E="Expected a function",t="Invalid `variable` option passed into `_.template`",k="__lodash_hash_undefined__",L=500,N="__lodash_placeholder__",C=1,U=2,q=4,W=1,ne=2,m=1,we=2,Se=4,he=8,ge=16,ze=32,pe=64,Oe=128,le=256,Ue=512,Ge=30,rt="...",wt=800,xt=16,$e=1,ft=2,Ke=3,jt=1/0,$t=9007199254740991,at=17976931348623157e292,Q=0/0,ae=4294967295,Ce=ae-1,ue=ae>>>1,je=[["ary",Oe],["bind",m],["bindKey",we],["curry",he],["curryRight",ge],["flip",Ue],["partial",ze],["partialRight",pe],["rearg",le]],ct="[object Arguments]",At="[object Array]",en="[object AsyncFunction]",ln="[object Boolean]",An="[object Date]",nr="[object DOMException]",un="[object Error]",Wt="[object Function]",vr="[object GeneratorFunction]",w="[object Map]",Ut="[object Number]",Vn="[object Null]",fr="[object Object]",Fr="[object Promise]",ur="[object Proxy]",br="[object RegExp]",Kt="[object Set]",vu="[object String]",a0="[object Symbol]",So="[object Undefined]",Go="[object WeakMap]",Os="[object We Line truncated
/* [wrapped with `+v+`] */
`)}function $l(d){return Jn(d)||sl(d)||!!(co&&d&&d[co])}function go(d,v){var x=typeof d;return v=v==null?$t:v,!!v&&(x=="number"||x!="symbol"&&Ms.test(d))&&d>-1&&d%1==0&&d<v}function io(d,v,x){if(!ku(x))return!1;var b=typeof v;return(b=="number"?al(x)&&go(v,x.length):b=="string"&&v in x)?Eo(x[v],d):!1}function Hi(d,v){if(Jn(d))return!1;var x=typeof d;return x=="number"||x=="symbol"||x=="boolean"||d==null||Nl(d)?!0:Ei.test(d)||!Pr.test(d)||v!=null&&d in wn(v)}function zr(d){var v=typeof d;return v=="string"||v=="number"||v=="symbol"||v=="boolean"?d!=="__proto__":d===null}function Cf(d){var v=Bo(d),x=Y[v];if(typeof x!="function"||!(v in lt.prototype))return!1;if(d===x)return!0;var b=Un(x);return!!b&&d===b[0]}function _m(d){return!!Ps&&Ps in d}var Al=C0?Ea:Ip;function xf(d){var v=d&&d.constructor,x=typeof v=="function"&&v.prototype||Sn;return d===x}function Af(d){return d===d&&!ku(d)}function v2(d,v){return function(x){return x==null?!1:x[d]===v&&(v!==i||d in wn(x))}}function m2(d){var v=T2(d,function(b){return x.size===L&&x.clear(),b}),x=v.cache;return v}function mc(d,v){var x=d[1],b=v[1],H=x|b,ee=H<(m|we|Oe),de=b==Oe&&x==he||b==Oe&&x==le&&d[7].length<=v[8]||b==(Oe|le)&&v[7].length<=v[8]&&x==he;if(!(ee||de))return d;b&m&&(d[2]=v[2],H|=x&m?0:Se);var ye=v[3];if(ye){var be=d[3];d[3]=be?js(be,ye,v[4]):ye,d[4]=be?T0(d[3],N):v[4]}return ye=v[5],ye&&(be=d[5],d[5]=be?fa(be,ye,v[6]):ye,d[6]=be?T0(d[5],N):v[6]),ye=v[7],ye&&(d[7]=ye),b&Oe&&(d[8]=d[8]==null?v[8]:Wn(d[8],v[8])),d[9]==null&&(d[9]=v[9]),d[0]=v[0],d[1]=H,d}function e1(d){var v=[];if(d!=null)for(var x in wn(d))v.push(x);return v}function Ha(d){return Uu.call(d)}function Nd(d,v,x){return v=Xr(v===i?d.length-1:v,0),function(){for(var b=arguments,H=-1,ee=Xr(b.length-v,0),de=Ve(ee);++H<ee;)de[H]=b[v+H];H=-1;for(var ye=Ve(v+1);++H<v;)ye[H]=b[H];return ye[v]=x(de),ie(d,this,ye)}}function y2(d,v){return v.length<2?d:J(d,rl(v,0,-1))}function t1(d,v){for(var x=d.length,b=Wn(v.length,x),H=Ji(d);b--;){var ee=v[b];d[b]=go(ee,x)?H[ee]:i}return d}function Rf(d,v){if(!(v==="constructor"&&typeof d[v]=="function")&&v!="__proto__")return d[v]}var n1=g2(Wc),Wa=df||function(d,v){return Ni.setTimeout(d,v)},r1=g2(Sd);function Ld(d,v,x){var b=v+"";return r1(d,an(b,wh(Tf(b),x)))}function g2(d){var v=0,x=0;return function(){var b=Xu(),H=xt-(b-x);if(x=b,H>0){if(++v>=wt)return arguments[0]}else v=0;return d.apply(i,arguments)}}function yc(d,v){var x=-1,b=d.length,H=b-1;for(v=v===i?b:v;++x<v;){var ee=o2(x,H),de=d[ee];d[ee]=d[x],d[x]=de}return d.length=v,d}var i1=m2(function(d){var v=[];return d.charCodeAt(0)===46&&v.push(""),d.replace(G0,function(x,b,H,ee){v.push(H?ee.replace(Ui,"$1"):b||x)}),v});function Rl(d){if(typeof d=="string"||Nl(d))return d;var v=d+"";return v=="0"&&1/d==-jt?"-0":v}function pa(d){if(d!=null){try{return Au.call(d)}catch(v){}try{return d+""}catch(v){}}return""}function wh(d,v){return tt(je,function(x){var b="_."+x[0];v&x[1]&&!on(d,b)&&d.push(b)}),d.sort()}function Fd(d){if(d instanceof lt)return d.clone();var v=new Ur(d.__wrapped__,d.__chain__);return v.__actions__=Ji(d.__actions__),v.__index__=d.__index__,v.__values__=d.__values__,v}function bd(d,v,x){(x?io(d,v,x):v===i)?v=1:v=Xr(Cr(v),0);var b=d==null?0:d.length;if(!b||v<1)return[];for(var H=0,ee=0,de=Ve(Ku(b/v));H<b;)de[ee++]=rl(d,H,H+=v);return de}function Sh(d){for(var v=-1,x=d==null?0:d.length,b=0,H=[];++v<x;){var ee=d[v];ee&&(H[b++]=ee)}return H}function _2(){var d=arguments.length;if(!d)return[];for(var v=Ve(d-1),x=arguments[0],b=d;b--;)v[b-1]=arguments[b];return gn(Jn(x)?Ji(x):[x],bi(v,1))}var Th=Mr(function(d,v){return n0(d)?Sl(d,bi(v,1,n0,!0)):[]}),Ol=Mr(function(d,v){var x=kl(v);return n0(x)&&(x=i),n0(d)?Sl(d,bi(v,1,n0,!0),zn(x,2)):[]}),es=Mr(function(d,v){var x=kl(v);return n0(x)&&(x=i),n0(d)?Sl(d,bi(v,1,n0,!0),i,x):[]});function Ds(d,v,x){var b=d==null?0:d.length;return b?(v=x||v===i?1:Cr(v),rl(d,v<0?0:v,b)):[]}function zs(d,v,x){var b=d==null?0:d.length;return b?(v=x||v===i?1:Cr(v),v=b-v,rl(d,0,v<0?0:v)):[]}function Pd(d,v){return d&&d.length?ac(d,zn(v,3),!0,!0):[]}function Ch(d,v){return d&&d.length?ac(d,zn(v,3),!0):[]}function Id(d,v,x,b){var H=d==null?0:d.length;return H?(x&&typeof x!="number"&&io(d,v,x)&&(x=0,b=H),sc(d,v,x,b)):[]}function Bd(d,v,x){var b=d==null?0:d.length;if(!b)return-1;var H=x==null?0:Cr(x);return H<0&&(H=Xr(b+H,0)),nt(d,zn(v,3),H)}function u1(d,v,x){var b=d==null?0:d.length;if(!b)return-1;var H=b-1;return x!==i&&(H=Cr(x),H=x<0?Xr(b+H,0):Wn(H,b-1)),nt(d,zn(v,3),H,!0)}function o1(d){var v=d==null?0:d.length;return v?bi(d,1):[]}function xh(d){var v=d==null?0:d.length;return v?bi(d,jt):[]}function Ud(d,v){var x=d==null?0:d.length;return x?(v=v===i?1:Cr(v),bi(d,v)):[]}function jd(d){for(var v=-1,x=d==null?0:d.length,b={};++v<x;){var H=d[v];b[H[0]]=H[1]}return b}function ws(d){return d&&d.length?d[0]:i}function Va(d,v,x){var b=d==null?0:d.length;if(!b)return-1;var H=x==null?0:Cr(x);return H<0&&(H=Xr(b+H,0)),_t(d,v,H)}function Ah(d){var v=d==null?0:d.length;return v?rl(d,0,-1):[]}va Line truncated
`;d.replace(rn,function(ir,Zr,ui,bl,Wi,uo){return ui||(ui=bl),Rt+=d.slice(gt,uo).replace(lo,bs),Zr&&(ye=!0,Rt+=`' +
__e(`+Zr+`) +
'`),Wi&&(be=!0,Rt+=`';
`+Wi+`;
__p += '`),ui&&(Rt+=`' +
((__t = (`+ui+`)) == null ? '' : __t) +
'`),gt=uo+ir.length,ir}),Rt+=`';
`;var $n=ei.call(v,"variable")&&v.variable;if(!$n)Rt=`with (obj) {
`+Rt+`
}
`;else if(Y0.test($n))throw new Yt(t);Rt=(be?Rt.replace(Hr,""):Rt).replace(To,"$1").replace(Co,"$1;"),Rt="function("+($n||"obj")+`) {
`+($n?"":`obj || (obj = {});
`)+"var __t, __p = ''"+(ye?", __e = _.escape":"")+(be?`, __j = Array.prototype.join;
function print() { __p += __j.call(arguments, '') }
`:`;
`)+Rt+`return __p
}`;var Nr=_v(function(){return sr(ee,Rn+"return "+Rt).apply(i,de)});if(Nr.source=Rt,mp(Nr))throw Nr;return Nr}function hv(d){return ki(d).toLowerCase()}function W2(d){return ki(d).toUpperCase()}function V2(d,v,x){if(d=ki(d),d&&(x||v===i))return xu(d);if(!d||!(v=il(v)))return d;var b=Yr(d),H=Yr(v),ee=sf(b,H),de=gl(b,H)+1;return aa(b,ee,de).join("")}function Ap(d,v,x){if(d=ki(d),d&&(x||v===i))return d.slice(0,fo(d)+1);if(!d||!(v=il(v)))return d;var b=Yr(d),H=gl(b,Yr(v))+1;return aa(b,0,H).join("")}function vv(d,v,x){if(d=ki(d),d&&(x||v===i))return d.replace(Ln,"");if(!d||!(v=il(v)))return d;var b=Yr(d),H=sf(b,Yr(v));return aa(b,H).join("")}function G2(d,v){var x=Ge,b=rt;if(ku(v)){var H="separator"in v?v.separator:H;x="length"in v?Cr(v.length):x,b="omission"in v?il(v.omission):b}d=ki(d);var ee=d.length;if(Lo(d)){var de=Yr(d);ee=de.length}if(x>=ee)return d;var ye=x-Ki(b);if(ye<1)return b;var be=de?aa(de,0,ye).join(""):d.slice(0,ye);if(H===i)return be+b;if(de&&(ye+=be.length-ye),g1(H)){if(d.slice(ye).search(H)){var gt,Dt=be;for(H.global||(H=fu(H.source,ki(xo.exec(H))+"g")),H.lastIndex=0;gt=H.exec(Dt);)var Rt=gt.index;be=be.slice(0,Rt===i?ye:Rt)}}else if(d.indexOf(il(H),ye)!=ye){var rn=be.lastIndexOf(H);rn>-1&&(be=be.slice(0,rn))}return be+b}function mv(d){return d=ki(d),d&&Si.test(d)?d.replace(L0,Oi):d}var yv=_f(function(d,v,x){return d+(x?" ":"")+v.toUpperCase()}),Rp=_h("toUpperCase");function gv(d,v,x){return d=ki(d),v=x?i:v,v===i?ps(d)?cf(d):v0(d):d.match(v)||[]}var _v=Mr(function(d,v){try{return ie(d,i,v)}catch(x){return mp(x)?x:new Yt(x)}}),uy=ol(function(d,v){return tt(v,function(x){x=Rl(x),ju(d,x,y1(d[x],d))}),d});function Ev(d){var v=d==null?0:d.length,x=zn();return d=v?Lt(d,function(b){if(typeof b[1]!="function")throw new Kr(E);return[x(b[0]),b[1]]}):[],Mr(function(b){for(var H=-1;++H<v;){var ee=d[H];if(ie(ee[0],this,b))return ie(ee[1],this,b)}})}function oy(d){return vf(U0(d,C))}function Y2(d){return function(){return d}}function Dv(d,v){return d==null||d!==d?v:d}var Op=Jc(),ly=Jc(!0);function r0(d){return d}function kp(d){return gu(typeof d=="function"?d:U0(d,C))}function sy(d){return Dd(U0(d,C))}function ay(d,v){return mf(d,U0(v,C))}var Mp=Mr(function(d,v){return function(x){return Tl(x,d,v)}}),Xa=Mr(function(d,v){return function(x){return Tl(d,x,v)}});function Np(d,v,x){var b=M0(v),H=I(v,b);x==null&&!(ku(v)&&(H.length||!b.length))&&(x=v,v=d,d=this,H=I(v,M0(v)));var ee=!(ku(x)&&"chain"in x)||!!x.chain,de=Ea(d);return tt(H,function(ye){var be=v[ye];d[ye]=be,de&&(d.prototype[ye]=function(){var gt=this.__chain__;if(ee||gt){var Dt=d(this.__wrapped__),Rt=Dt.__actions__=Ji(this.__actions__);return Rt.push({func:be,args:arguments,thisArg:d}),Dt.__chain__=gt,Dt}return be.apply(d,gn([this.value()],arguments))})}),d}function Lp(){return Ni._===this&&(Ni._=zi),this}function Fp(){}function fy(d){return d=Cr(d),Mr(function(v){return qc(v,d)})}var cy=Or(Lt),wv=Or(kt),Pf=Or(_r);function bp(d){return Hi(d)?sn(Rl(d)):mo(d)}function Sv(d){return function(v){return d==null?i:J(d,v)}}var dy=Zc(),py=Zc(!0);function Pp(){return[]}function Ip(){return!1}function hy(){return{}}function vy(){return""}function Tv(){return!0}function Bp(d,v){if(d=Cr(d),d<1||d>$t)return[];var x=ae,b=Wn(d,ae);v=zn(v),d-=ae;for(var H=S0(b,v);++x<d;)v(x);return H}function Up(d){return Jn(d)?Lt(d,Rl):Nl(d)?[d]:Ji(i1(ki(d)))}function K2(d){var v=++_l;return ki(d)+v}var my=d2(function(d,v){return d+v},0),R1=Md("ceil"),yy=d2(function(d,v){return d/v},1),Cv=Md("floor");function xv(d){return d&&d.length?la(d,r0,Ct):i}function gy(d,v){return d&&d.length?la(d,zn(v,2),Ct):i}function If(d){return nn(d,r0)}function jp(d,v){return nn(d,zn(v,2))}function Av(d){return d&&d.length?la(d,r0,r2):i}function _y(d,v){return d&&d.length?la(d,zn(v,2),r2):i}var Ey=d2(function(d,v){return d*v},1),zp=Md("round"),Dy=d2(function(d,v){return d-v},0);function Ys(d){return d&&d.length?Cu(d,r0):0}function wy(d,v){return d&&d.length?Cu(d,zn(v,2)):0}return Y.after=op,Y.ary=Yh,Y.assign=Dp,Y.assignIn=F2,Y.assignInWith=Do,Y.assignWith=Ss,Y.at=Mf,Y.before=S2,Y.bind=y1,Y.bindAll=uy,Y.bindKey=Kh,Y.castArray=$h,Y.chain=Bh,Y.chunk=bd,Y.compact=Sh,Y.concat=_2,Y.cond=Ev,Y.conforms=oy,Y.constant=Y2,Y.countBy=D2,Y.create=b2,Y.curry=lp,Y.curryRight=Xh,Y.debounce=sp,Y.defaults=wp,Y.defaultsDeep=D1,Y.defer=Qh,Y.delay=Jh,Y.difference=Th,Y.differenceBy=Ol,Y.differenceWith=es,Y.drop=Ds,Y.dropRight=zs,Y.dropRightWhile=Pd,Y.dropWhile=Ch,Y.fill=Id,Y.filter=Zd,Y.flatMap=qh,Y.flatMapDeep=ep,Y.flatMapDepth=Hh,Y.flatten=o1,Y.flattenDeep=xh,Y.flattenDepth=Ud,Y.flip=ap,Y.flow=Op,Y.flowRight=ly,Y.fromPairs=jd,Y.functions=k0,Y.functionsIn=P2,Y.groupBy=Rm,Y.initial=Ah,Y.intersection=uu,Y.intersectionBy=l1,Y.intersectionWith=Rh,Y.invert=wc,Y.invertBy=I2,Y.invokeMap=km,Y.iteratee=kp,Y.keyBy=Vh,Y.keys=M0,Y.keysIn=fn,Y.map=v1,Y.mapKeys=B2,Y.mapValues=U2,Y.matches=sy,Y.matchesProperty=ay,Y.memoize=T2,Y.merge=Sc,Y.mergeWith=Tc,Y.method=Mp,Y.methodOf=Xa,Y.mixin=Np,Y.negate=C2,Y.nthArg=fy,Y.omit=Cc,Y.omitBy=T1,Y.once Line truncated
`;L!==p&&(p=L,i.write(Q5.default.eraseLines(f)+L),f=L.split(`
`).length)};return t.clear=()=>{i.write(Q5.default.eraseLines(f)),p="",f=0},t.done=()=>{p="",f=0,o||(J5.default.show(),E=!1)},t};sg.default={create:YK}});var e9=ce((One,$5)=>{$5.exports=[{name:"AppVeyor",constant:"APPVEYOR",env:"APPVEYOR",pr:"APPVEYOR_PULL_REQUEST_NUMBER"},{name:"Azure Pipelines",constant:"AZURE_PIPELINES",env:"SYSTEM_TEAMFOUNDATIONCOLLECTIONURI",pr:"SYSTEM_PULLREQUEST_PULLREQUESTID"},{name:"Bamboo",constant:"BAMBOO",env:"bamboo_planKey"},{name:"Bitbucket Pipelines",constant:"BITBUCKET",env:"BITBUCKET_COMMIT",pr:"BITBUCKET_PR_ID"},{name:"Bitrise",constant:"BITRISE",env:"BITRISE_IO",pr:"BITRISE_PULL_REQUEST"},{name:"Buddy",constant:"BUDDY",env:"BUDDY_WORKSPACE_ID",pr:"BUDDY_EXECUTION_PULL_REQUEST_ID"},{name:"Buildkite",constant:"BUILDKITE",env:"BUILDKITE",pr:{env:"BUILDKITE_PULL_REQUEST",ne:"false"}},{name:"CircleCI",constant:"CIRCLE",env:"CIRCLECI",pr:"CIRCLE_PULL_REQUEST"},{name:"Cirrus CI",constant:"CIRRUS",env:"CIRRUS_CI",pr:"CIRRUS_PR"},{name:"AWS CodeBuild",constant:"CODEBUILD",env:"CODEBUILD_BUILD_ARN"},{name:"Codeship",constant:"CODESHIP",env:{CI_NAME:"codeship"}},{name:"Drone",constant:"DRONE",env:"DRONE",pr:{DRONE_BUILD_EVENT:"pull_request"}},{name:"dsari",constant:"DSARI",env:"DSARI"},{name:"GitLab CI",constant:"GITLAB",env:"GITLAB_CI"},{name:"GoCD",constant:"GOCD",env:"GO_PIPELINE_LABEL"},{name:"Hudson",constant:"HUDSON",env:"HUDSON_URL"},{name:"Jenkins",constant:"JENKINS",env:["JENKINS_URL","BUILD_ID"],pr:{any:["ghprbPullId","CHANGE_ID"]}},{name:"Magnum CI",constant:"MAGNUM",env:"MAGNUM"},{name:"Netlify CI",constant:"NETLIFY",env:"NETLIFY_BUILD_BASE",pr:{env:"PULL_REQUEST",ne:"false"}},{name:"Sail CI",constant:"SAIL",env:"SAILCI",pr:"SAIL_PULL_REQUEST_NUMBER"},{name:"Semaphore",constant:"SEMAPHORE",env:"SEMAPHORE",pr:"PULL_REQUEST_NUMBER"},{name:"Shippable",constant:"SHIPPABLE",env:"SHIPPABLE",pr:{IS_PULL_REQUEST:"true"}},{name:"Solano CI",constant:"SOLANO",env:"TDDIUM",pr:"TDDIUM_PR_ID"},{name:"Strider CD",constant:"STRIDER",env:"STRIDER"},{name:"TaskCluster",constant:"TASKCLUSTER",env:["TASK_ID","RUN_ID"]},{name:"TeamCity",constant:"TEAMCITY",env:"TEAMCITY_VERSION"},{name:"Travis CI",constant:"TRAVIS",env:"TRAVIS",pr:{env:"TRAVIS_PULL_REQUEST",ne:"false"}}]});var r9=ce(Ra=>{"use strict";var t9=e9(),bc=process.env;Object.defineProperty(Ra,"_vendors",{value:t9.map(function(i){return i.constant})});Ra.name=null;Ra.isPR=null;t9.forEach(function(i){var o=Array.isArray(i.env)?i.env:[i.env],f=o.every(function(p){return n9(p)});if(Ra[i.constant]=f,f)switch(Ra.name=i.name,typeof i.pr){case"string":Ra.isPR=!!bc[i.pr];break;case"object":"env"in i.pr?Ra.isPR=i.pr.env in bc&&bc[i.pr.env]!==i.pr.ne:"any"in i.pr?Ra.isPR=i.pr.any.some(function(p){return!!bc[p]}):Ra.isPR=n9(i.pr);break;default:Ra.isPR=null}});Ra.isCI=!!(bc.CI||bc.CONTINUOUS_INTEGRATION||bc.BUILD_NUMBER||bc.RUN_ID||Ra.name);function n9(i){return typeof i=="string"?!!bc[i]:Object.keys(i).every(function(o){return bc[o]===i[o]})}});var u9=ce((Mne,i9)=>{"use strict";i9.exports=r9().isCI});var l9=ce((Nne,o9)=>{"use strict";var KK=i=>{let o=new Set;do for(let f of Reflect.ownKeys(i))o.add([i,f]);while((i=Reflect.getPrototypeOf(i))&&i!==Object.prototype);return o};o9.exports=(i,{include:o,exclude:f}={})=>{let p=E=>{let t=k=>typeof k=="string"?E===k:k.test(E);return o?o.some(t):f?!f.some(t):!0};for(let[E,t]of KK(i.constructor.prototype)){if(t==="constructor"||!p(t))continue;let k=Reflect.getOwnPropertyDescriptor(E,t);k&&typeof k.value=="function"&&(i[t]=i[t].bind(i))}return i}});var h9=ce($i=>{"use strict";Object.defineProperty($i,"__esModule",{value:!0});var om,ag,u4,o4,$D;typeof window=="undefined"||typeof MessageChannel!="function"?(lm=null,ew=null,tw=function(){if(lm!==null)try{var i=$i.unstable_now();lm(!0,i),lm=null}catch(o){throw setTimeout(tw,0),o}},s9=Date.now(),$i.unstable_now=function(){return Date.now()-s9},om=function(i){lm!==null?setTimeout(om,0,i):(lm=i,setTimeout(tw,0))},ag=function(i,o){ew=setTimeout(i,o)},u4=function(){clearTimeout(ew)},o4=function(){return!1},$D=$i.unstable_forceFrameRate=function(){}):(l4=window.performance,nw=window.Date,a9=window.setTimeout,f9=window.clearTimeout,typeof console!="undefined"&&(c9=window.cancelAnimationFrame,typeof window.requestAnimationFrame!="function"&&console.error("This browser doesn't support requestAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills"),typeof c9!="function"&&console.error("This browser doesn't support cancelAnimationFrame. Make sure that you load a polyfill in older browsers. https://fb.me/react-polyfills")),typeof l4=="object"&&typeof l4.now=="function"?$i.unstable_now=function(){return l4.now()}:(d9=nw.now(),$i.unstable_now=function(){return nw.now()-d9}),fg=!1,cg=null,s4=-1,rw=5,iw=0,o4=function(){return $i.unstable_now()>=iw},$D=function(){},$i.unstable_forceFrameRate=function(i){0>i||125<i?console.error("forceFrameRate takes a positive int between 0 and 125, forcing framerates higher than 125 fps is not un Line truncated
in `+(F||"Unknown")+J}y+=A,g=g.return}while(g);return y}new Set;var Ei=[],G0=-1;function fi(g){0>G0||(g.current=Ei[G0],Ei[G0]=null,G0--)}function Zt(g,y){G0++,Ei[G0]=g.current,g.current=y}var Ln={},Di={current:Ln},ci={current:!1},Ht=Ln;function Du(g,y){var A=g.type.contextTypes;if(!A)return Ln;var F=g.stateNode;if(F&&F.__reactInternalMemoizedUnmaskedChildContext===y)return F.__reactInternalMemoizedMaskedChildContext;var I={},J;for(J in A)I[J]=y[J];return F&&(g=g.stateNode,g.__reactInternalMemoizedUnmaskedChildContext=y,g.__reactInternalMemoizedMaskedChildContext=I),I}function Yi(g){return g=g.childContextTypes,g!=null}function Y0(g){fi(ci,g),fi(Di,g)}function Ui(g){fi(ci,g),fi(Di,g)}function Wl(g,y,A){if(Di.current!==Ln)throw Error(t(168));Zt(Di,y,g),Zt(ci,A,g)}function xo(g,y,A){var F=g.stateNode;if(g=y.childContextTypes,typeof F.getChildContext!="function")return A;F=F.getChildContext();for(var I in F)if(!(I in g))throw Error(t(108,Ge(y)||"Unknown",I));return f({},A,{},F)}function ni(g){var y=g.stateNode;return y=y&&y.__reactInternalMemoizedMergedChildContext||Ln,Ht=Di.current,Zt(Di,y,g),Zt(ci,ci.current,g),!0}function oo(g,y,A){var F=g.stateNode;if(!F)throw Error(t(169));A?(y=xo(g,y,Ht),F.__reactInternalMemoizedMergedChildContext=y,fi(ci,g),fi(Di,g),Zt(Di,y,g)):fi(ci,g),Zt(ci,A,g)}var Vl=E.unstable_runWithPriority,Ao=E.unstable_scheduleCallback,Ms=E.unstable_cancelCallback,Xn=E.unstable_shouldYield,Qo=E.unstable_requestPaint,lo=E.unstable_now,b0=E.unstable_getCurrentPriorityLevel,yl=E.unstable_ImmediatePriority,Ro=E.unstable_UserBlockingPriority,Et=E.unstable_NormalPriority,Pt=E.unstable_LowPriority,Bn=E.unstable_IdlePriority,Ir={},ji=Qo!==void 0?Qo:function(){},Wr=null,wu=null,c0=!1,Ti=lo(),d0=1e4>Ti?lo:function(){return lo()-Ti};function as(){switch(b0()){case yl:return 99;case Ro:return 98;case Et:return 97;case Pt:return 96;case Bn:return 95;default:throw Error(t(332))}}function St(g){switch(g){case 99:return yl;case 98:return Ro;case 97:return Et;case 96:return Pt;case 95:return Bn;default:throw Error(t(332))}}function so(g,y){return g=St(g),Vl(g,y)}function Jo(g,y,A){return g=St(g),Ao(g,y,A)}function Gl(g){return Wr===null?(Wr=[g],wu=Ao(yl,fs)):Wr.push(g),Ir}function Fu(){if(wu!==null){var g=wu;wu=null,Ms(g)}fs()}function fs(){if(!c0&&Wr!==null){c0=!0;var g=0;try{var y=Wr;so(99,function(){for(;g<y.length;g++){var A=y[g];do A=A(!0);while(A!==null)}}),Wr=null}catch(A){throw Wr!==null&&(Wr=Wr.slice(g+1)),Ao(yl,Fu),A}finally{c0=!1}}}var P0=3;function X(g,y,A){return A/=10,1073741821-(((1073741821-g+y/10)/A|0)+1)*A}function _e(g,y){return g===y&&(g!==0||1/g==1/y)||g!==g&&y!==y}var Ne=typeof Object.is=="function"?Object.is:_e,Me=Object.prototype.hasOwnProperty;function dt(g,y){if(Ne(g,y))return!0;if(typeof g!="object"||g===null||typeof y!="object"||y===null)return!1;var A=Object.keys(g),F=Object.keys(y);if(A.length!==F.length)return!1;for(F=0;F<A.length;F++)if(!Me.call(y,A[F])||!Ne(g[A[F]],y[A[F]]))return!1;return!0}function Hn(g,y){if(g&&g.defaultProps){y=f({},y),g=g.defaultProps;for(var A in g)y[A]===void 0&&(y[A]=g[A])}return y}var Dn={current:null},or=null,mi=null,Su=null;function bu(){Su=mi=or=null}function Pu(g,y){var A=g.type._context;un?(Zt(Dn,A._currentValue,g),A._currentValue=y):(Zt(Dn,A._currentValue2,g),A._currentValue2=y)}function mu(g){var y=Dn.current;fi(Dn,g),g=g.type._context,un?g._currentValue=y:g._currentValue2=y}function yi(g,y){for(;g!==null;){var A=g.alternate;if(g.childExpirationTime<y)g.childExpirationTime=y,A!==null&&A.childExpirationTime<y&&(A.childExpirationTime=y);else if(A!==null&&A.childExpirationTime<y)A.childExpirationTime=y;else break;g=g.return}}function Oo(g,y){or=g,Su=mi=null,g=g.dependencies,g!==null&&g.firstContext!==null&&(g.expirationTime>=y&&(fo=!0),g.firstContext=null)}function Tu(g,y){if(Su!==g&&y!==!1&&y!==0)if((typeof y!="number"||y===1073741823)&&(Su=g,y=1073741823),y={context:g,observedBits:y,next:null},mi===null){if(or===null)throw Error(t(308));mi=y,or.dependencies={expirationTime:0,firstContext:y,responders:null}}else mi=mi.next=y;return un?g._currentValue:g._currentValue2}var ao=!1;function Iu(g){return{baseState:g,firstUpdate:null,lastUpdate:null,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function Oa(g){return{baseState:g.baseState,firstUpdate:g.firstUpdate,lastUpdate:g.lastUpdate,firstCapturedUpdate:null,lastCapturedUpdate:null,firstEffect:null,lastEffect:null,firstCapturedEffect:null,lastCapturedEffect:null}}function p0(g,y){return{expirationTime:g,suspenseConfig:y,tag:0,payload:null,callback:null,next:null,nextEffect:null}}function Zs(g,y){g.lastUpdate===null?g.firstUpdate=g.lastUpdate=y:(g.lastUpdate.next=y,g.lastUpdate=y)}function K0(g,y){var A=g.alternate;if(A===null){var F=g.updateQueue,I=null;F===null&&(F=g.updateQueue=Iu(g.memoizedState))}else F=g.updateQueue,I=A.updateQueue,F===null?I===null?(F=g.updateQueue=Iu(g.memoizedState),I=A.updateQueue=Iu(A.me Line truncated
Add a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.`+Pr(I))}Ae!==y0&&(Ae=Wn),J=_l(J,I),Ct=F;do{switch(Ct.tag){case 3:fe=J,Ct.effectTag|=4096,Ct.expirationTime=y;var Be=hs(Ct,fe,y);$s(Ct,Be);break e;case 1:fe=J;var ut=Ct.type,Jt=Ct.stateNode;if((Ct.effectTag&64)==0&&(typeof ut.getDerivedStateFromError=="function"||Jt!==null&&typeof Jt.componentDidCatch=="function"&&(cr===null||!cr.has(Jt)))){Ct.effectTag|=4096,Ct.expirationTime=y;var jn=ra(Ct,fe,y);$s(Ct,jn);break e}}Ct=Ct.return}while(Ct!==null)}re=ho(re)}catch(ti){y=ti;continue}break}while(1)}function B0(){var g=Ku.current;return Ku.current=No,g===null?No:g}function oc(g,y){g<vt&&2<g&&(vt=g),y!==null&&g<Xt&&2<g&&(Xt=g,xn=y)}function La(g){g>_n&&(_n=g)}function gd(){for(;re!==null;)re=e2(re)}function $1(){for(;re!==null&&!Xn();)re=e2(re)}function e2(g){var y=Pa(g.alternate,g,Le);return g.memoizedProps=g.pendingProps,y===null&&(y=ho(g)),vs.current=null,y}function ho(g){re=g;do{var y=re.alternate;if(g=re.return,(re.effectTag&2048)==0){e:{var A=y;y=re;var F=Le,I=y.pendingProps;switch(y.tag){case 2:break;case 16:break;case 15:case 0:break;case 1:Yi(y.type)&&Y0(y);break;case 3:Xe(y),Ui(y),I=y.stateNode,I.pendingContext&&(I.context=I.pendingContext,I.pendingContext=null),(A===null||A.child===null)&&Kl(y)&&Gu(y),Vr(y);break;case 5:ie(y);var J=Z(xe.current);if(F=y.type,A!==null&&y.stateNode!=null)Bu(A,y,F,I,J),A.ref!==y.ref&&(y.effectTag|=128);else if(I){if(A=Z(De.current),Kl(y)){if(I=y,!w)throw Error(t(175));A=tu(I.stateNode,I.type,I.memoizedProps,J,A,I),I.updateQueue=A,A=A!==null,A&&Gu(y)}else{var fe=ae(F,I,J,A,y);Kr(fe,y,!1,!1),y.stateNode=fe,ue(fe,F,I,J,A)&&Gu(y)}y.ref!==null&&(y.effectTag|=128)}else if(y.stateNode===null)throw Error(t(166));break;case 6:if(A&&y.stateNode!=null)Sn(A,y,A.memoizedProps,I);else{if(typeof I!="string"&&y.stateNode===null)throw Error(t(166));if(A=Z(xe.current),J=Z(De.current),Kl(y)){if(A=y,!w)throw Error(t(176));(A=Si(A.stateNode,A.memoizedProps,A))&&Gu(y)}else y.stateNode=en(I,A,J,y)}break;case 11:break;case 13:if(fi(qe,y),I=y.memoizedState,(y.effectTag&64)!=0){y.expirationTime=F;break e}I=I!==null,J=!1,A===null?y.memoizedProps.fallback!==void 0&&Kl(y):(F=A.memoizedState,J=F!==null,I||F===null||(F=A.child.sibling,F!==null&&(fe=y.firstEffect,fe!==null?(y.firstEffect=F,F.nextEffect=fe):(y.firstEffect=y.lastEffect=F,F.nextEffect=null),F.effectTag=8))),I&&!J&&(y.mode&2)!=0&&(A===null&&y.memoizedProps.unstable_avoidThisFallback!==!0||(qe.current&1)!=0?Ae===Ci&&(Ae=Xu):((Ae===Ci||Ae===Xu)&&(Ae=m0),_n!==0&&se!==null&&(Sl(se,Le),_s(se,_n)))),vr&&I&&(y.effectTag|=4),Wt&&(I||J)&&(y.effectTag|=4);break;case 7:break;case 8:break;case 12:break;case 4:Xe(y),Vr(y);break;case 10:mu(y);break;case 9:break;case 14:break;case 17:Yi(y.type)&&Y0(y);break;case 19:if(fi(qe,y),I=y.memoizedState,I===null)break;if(J=(y.effectTag&64)!=0,fe=I.rendering,fe===null){if(J)Au(I,!1);else if(Ae!==Ci||A!==null&&(A.effectTag&64)!=0)for(A=y.child;A!==null;){if(fe=tt(A),fe!==null){for(y.effectTag|=64,Au(I,!1),A=fe.updateQueue,A!==null&&(y.updateQueue=A,y.effectTag|=4),I.lastEffect===null&&(y.firstEffect=null),y.lastEffect=I.lastEffect,A=F,I=y.child;I!==null;)J=I,F=A,J.effectTag&=2,J.nextEffect=null,J.firstEffect=null,J.lastEffect=null,fe=J.alternate,fe===null?(J.childExpirationTime=0,J.expirationTime=F,J.child=null,J.memoizedProps=null,J.memoizedState=null,J.updateQueue=null,J.dependencies=null):(J.childExpirationTime=fe.childExpirationTime,J.expirationTime=fe.expirationTime,J.child=fe.child,J.memoizedProps=fe.memoizedProps,J.memoizedState=fe.memoizedState,J.updateQueue=fe.updateQueue,F=fe.dependencies,J.dependencies=F===null?null:{expirationTime:F.expirationTime,firstContext:F.firstContext,responders:F.responders}),I=I.sibling;Zt(qe,qe.current&1|2,y),y=y.child;break e}A=A.sibling}}else{if(!J)if(A=tt(fe),A!==null){if(y.effectTag|=64,J=!0,A=A.updateQueue,A!==null&&(y.updateQueue=A,y.effectTag|=4),Au(I,!0),I.tail===null&&I.tailMode==="hidden"&&!fe.alternate){y=y.lastEffect=I.lastEffect,y!==null&&(y.nextEffect=null);break}}else d0()>I.tailExpiration&&1<F&&(y.effectTag|=64,J=!0,Au(I,!1),y.expirationTime=y.childExpirationTime=F-1);I.isBackwards?(fe.sibling=y.child,y.child=fe):(A=I.last,A!==null?A.sibling=fe:y.child=fe,I.last=fe)}if(I.tail!==null){I.tailExpiration===0&&(I.tailExpiration=d0()+500),A=I.tail,I.rendering=A,I.tail=A.sibling,I.lastEffect=y.lastEffect,A.sibling=null,I=qe.current,I=J?I&1|2:I&1,Zt(qe,I,y),y=A;break e}break;case 20:break;case 21:break;default:throw Error(t(156,y.tag))}y=null}if(A=re,Le===1||A.childExpirationTime!==1){for(I=0,J=A.child;J!==null;)F=J.expirationTime,fe=J.childExpirationTime,F>I&&(I=F),fe>I&&(I=fe),J=J.sibling;A.childExpirationTime=I}if(y!==null)return y;g!==null&&(g.effectTag&2048)==0&&(g.firstEffect===null&&(g.firstEffect=re.firstEffect),re.lastEffect!==null&&(g.lastEffect!==null&&(g.lastEffect.nextEffect=re.firstEffect),g.lastEffect=re.lastEffect),1<re.effectTag&&(g.lastEff Line truncated
Your code should look like:
const MyComponent = lazy(() => import('./MyComponent'))`,T),a._status=So,a._result=R}},function(T){a._status===a0&&(a._status=Go,a._result=T)})}}function Ko(a,c,_){var T=c.displayName||c.name||"";return a.displayName||(T!==""?_+"("+T+")":_)}function qt(a){if(a==null)return null;if(typeof a.tag=="number"&&Ke(!1,"Received an unexpected object in getComponentName(). This is likely a bug in React. Please file an issue."),typeof a=="function")return a.displayName||a.name||null;if(typeof a=="string")return a;switch(a){case ue:return"Fragment";case Ce:return"Portal";case ct:return"Profiler";case je:return"StrictMode";case nr:return"Suspense";case un:return"SuspenseList"}if(typeof a=="object")switch(a.$$typeof){case en:return"Context.Consumer";case At:return"Context.Provider";case An:return Ko(a,a.render,"ForwardRef");case Wt:return qt(a.type);case vr:{var c=a,_=Os(c);if(_)return qt(_);break}}return null}var _i=0,eu=1,ai=2,mr=4,Xo=6,W0=8,Lu=16,V0=32,Hr=64,To=128,Co=256,L0=512,tu=1024,Si=1028,ks=932,Hl=2047,F0=2048,f0=4096,Pr=!0,Ei=!0,G0=!0,fi=!0,Zt=!0,Ln=!0,Di=!1,ci=!1,Ht=!1,Du=!1,Yi=!1,Y0=!0,Ui=!1,Wl=!1,xo=!1,ni=!1,oo=!1,Vl=at.ReactCurrentOwner;function Ao(a){var c=a,_=a;if(a.alternate)for(;c.return;)c=c.return;else{var T=c;do c=T,(c.effectTag&(ai|tu))!==_i&&(_=c.return),T=c.return;while(T)}return c.tag===U?_:null}function Ms(a){return Ao(a)===a}function Xn(a){{var c=Vl.current;if(c!==null&&c.tag===N){var _=c,T=_.stateNode;T._warnedAboutRefsInRender||Ke(!1,"%s is accessing isMounted inside its render() function. render() should be a pure function of props and state. It should never access something that requires stale data from the previous render, such as refs. Move this logic to componentDidMount and componentDidUpdate instead.",qt(_.type)||"A component"),T._warnedAboutRefsInRender=!0}}var R=jt(a);return R?Ao(R)===R:!1}function Qo(a){if(Ao(a)!==a)throw Error("Unable to find node on an unmounted component.")}function lo(a){var c=a.alternate;if(!c){var _=Ao(a);if(_===null)throw Error("Unable to find node on an unmounted component.");return _!==a?null:a}for(var T=a,R=c;;){var j=T.return;if(j===null)break;var V=j.alternate;if(V===null){var te=j.return;if(te!==null){T=R=te;continue}break}if(j.child===V.child){for(var oe=j.child;oe;){if(oe===T)return Qo(j),a;if(oe===R)return Qo(j),c;oe=oe.sibling}throw Error("Unable to find node on an unmounted component.")}if(T.return!==R.return)T=j,R=V;else{for(var Ie=!1,Ye=j.child;Ye;){if(Ye===T){Ie=!0,T=j,R=V;break}if(Ye===R){Ie=!0,R=j,T=V;break}Ye=Ye.sibling}if(!Ie){for(Ye=V.child;Ye;){if(Ye===T){Ie=!0,T=V,R=j;break}if(Ye===R){Ie=!0,R=V,T=j;break}Ye=Ye.sibling}if(!Ie)throw Error("Child was not found in either parent set. This indicates a bug in React related to the return pointer. Please file an issue.")}}if(T.alternate!==R)throw Error("Return fibers should always be each others' alternates. This error is likely caused by a bug in React. Please file an issue.")}if(T.tag!==U)throw Error("Unable to find node on an unmounted component.");return T.stateNode.current===T?a:c}function b0(a){var c=lo(a);if(!c)return null;for(var _=c;;){if(_.tag===W||_.tag===ne)return _;if(_.child){_.child.return=_,_=_.child;continue}if(_===c)return null;for(;!_.sibling;){if(!_.return||_.return===c)return null;_=_.return}_.sibling.return=_.return,_=_.sibling}return null}function yl(a){var c=lo(a);if(!c)return null;for(var _=c;;){if(_.tag===W||_.tag===ne||Ht&&_.tag===xt)return _;if(_.child&&_.tag!==q){_.child.return=_,_=_.child;continue}if(_===c)return null;for(;!_.sibling;){if(!_.return||_.return===c)return null;_=_.return}_.sibling.return=_.return,_=_.sibling}return null}var Ro=o.getPublicInstance,Et=o.getRootHostContext,Pt=o.getChildHostContext,Bn=o.prepareForCommit,Ir=o.resetAfterCommit,ji=o.createInstance,Wr=o.appendInitialChild,wu=o.finalizeInitialChildren,c0=o.prepareUpdate,Ti=o.shouldSetTextContent,d0=o.shouldDeprioritizeSubtree,as=o.createTextInstance,St=o.setTimeout,so=o.clearTimeout,Jo=o.noTimeout,Gl=o.now,Fu=o.isPrimaryRenderer,fs=o.warnsIfNotActing,P0=o.supportsMutation,X=o.supportsPersistence,_e=o.supportsHydration,Ne=o.mountResponderInstance,Me=o.unmountResponderInstance,dt=o.getFundamentalComponentInstance,Hn=o.mountFundamentalComponent,Dn=o.shouldUpdateFundamentalComponent,or=o.getInstanceFromNode,mi=o.appendChild,Su=o.appendChildToContainer,bu=o.commitTextUpdate,Pu=o.commitMount,mu=o.commitUpdate,yi=o.insertBefore,Oo=o.insertInContainerBefore,Tu=o.removeChild,ao=o.removeChildFromContainer,Iu=o.resetTextContent,Oa=o.hideInstance,p0=o.hideTextInstance,Zs=o.unhideInstance,K0=o.unhideTextInstance,$s=o.updateFundamentalComponent,ka=o.unmountFundamentalComponent,cs=o.cloneInstance,w0=o.createContainerChildSet,Gn=o.appendChildToContainerChildSet,ic=o.finalizeContainerChildren,ri=o.replaceContainerChildren,Gr=o.cloneHiddenInstance,Yl=o.cloneHiddenTextInstance,ea=o.cloneInstance,lf=o.canHydrateInstance,Ns=o.canHydrateTextInstance,Ma=o.canHydrateSuspenseInstance,Ls=o.isSuspenseInstancePending,h0 Line truncated
in `+(a||"Unknown")+T},lr=at.ReactDebugCurrentFrame;function Qn(a){switch(a.tag){case U:case q:case ne:case m:case he:case Se:return"";default:var c=a._debugOwner,_=a._debugSource,T=qt(a.type),R=null;return c&&(R=qt(c.type)),gn(T,_,R)}}function _r(a){var c="",_=a;do c+=Qn(_),_=_.return;while(_);return c}var Cn=null,Ar=null;function v0(){{if(Cn===null)return null;var a=Cn._debugOwner;if(a!==null&&typeof a!="undefined")return qt(a.type)}return null}function Rr(){return Cn===null?"":_r(Cn)}function nt(){lr.getCurrentStack=null,Cn=null,Ar=null}function _t(a){lr.getCurrentStack=Rr,Cn=a,Ar=null}function Ze(a){Ar=a}var Ft="\u269B",nn="\u26D4",sn=typeof performance!="undefined"&&typeof performance.mark=="function"&&typeof performance.clearMarks=="function"&&typeof performance.measure=="function"&&typeof performance.clearMeasures=="function",Yn=null,yr=null,nu=null,Cu=!1,S0=!1,X0=!1,xu=0,di=0,ko=new Set,Zo=function(a){return Ft+" "+a},sf=function(a,c){var _=c?nn+" ":Ft+" ",T=c?" Warning: "+c:"";return""+_+a+T},gl=function(a){performance.mark(Zo(a))},af=function(a){performance.clearMarks(Zo(a))},Mo=function(a,c,_){var T=Zo(c),R=sf(a,_);try{performance.measure(R,T)}catch(j){}performance.clearMarks(T),performance.clearMeasures(R)},ds=function(a,c){return a+" (#"+c+")"},bs=function(a,c,_){return _===null?a+" ["+(c?"update":"mount")+"]":a+"."+_},No=function(a,c){var _=qt(a.type)||"Unknown",T=a._debugID,R=a.alternate!==null,j=bs(_,R,c);if(Cu&&ko.has(j))return!1;ko.add(j);var V=ds(j,T);return gl(V),!0},Lo=function(a,c){var _=qt(a.type)||"Unknown",T=a._debugID,R=a.alternate!==null,j=bs(_,R,c),V=ds(j,T);af(V)},ps=function(a,c,_){var T=qt(a.type)||"Unknown",R=a._debugID,j=a.alternate!==null,V=bs(T,j,c),te=ds(V,R);Mo(V,te,_)},Vu=function(a){switch(a.tag){case U:case W:case ne:case q:case m:case he:case Se:case we:return!0;default:return!1}},yu=function(){yr!==null&&nu!==null&&Lo(nu,yr),nu=null,yr=null,X0=!1},pi=function(){for(var a=Yn;a;)a._debugIsCurrentlyTiming&&ps(a,null,null),a=a.return},T0=function(a){a.return!==null&&T0(a.return),a._debugIsCurrentlyTiming&&No(a,null)},Q0=function(){Yn!==null&&T0(Yn)};function Fo(){Pr&&di++}function ta(){Pr&&(Cu&&(S0=!0),yr!==null&&yr!=="componentWillMount"&&yr!=="componentWillReceiveProps"&&(X0=!0))}function Kl(a){if(Pr){if(!sn||Vu(a)||(Yn=a,!No(a,null)))return;a._debugIsCurrentlyTiming=!0}}function Ki(a){if(Pr){if(!sn||Vu(a))return;a._debugIsCurrentlyTiming=!1,Lo(a,null)}}function Yr(a){if(Pr){if(!sn||Vu(a)||(Yn=a.return,!a._debugIsCurrentlyTiming))return;a._debugIsCurrentlyTiming=!1,ps(a,null,null)}}function fo(a){if(Pr){if(!sn||Vu(a)||(Yn=a.return,!a._debugIsCurrentlyTiming))return;a._debugIsCurrentlyTiming=!1;var c=a.tag===pe?"Rendering was suspended":"An error was thrown inside this error boundary";ps(a,null,c)}}function Oi(a,c){if(Pr){if(!sn||(yu(),!No(a,c)))return;nu=a,yr=c}}function gi(){if(Pr){if(!sn)return;if(yr!==null&&nu!==null){var a=X0?"Scheduled a cascading update":null;ps(nu,yr,a)}yr=null,nu=null}}function ff(a){if(Pr){if(Yn=a,!sn)return;xu=0,gl("(React Tree Reconciliation)"),Q0()}}function cf(a,c){if(Pr){if(!sn)return;var _=null;if(a!==null)if(a.tag===U)_="A top-level update interrupted the previous render";else{var T=qt(a.type)||"Unknown";_="An update to "+T+" interrupted the previous render"}else xu>1&&(_="There were cascading updates");xu=0;var R=c?"(React Tree Reconciliation: Completed Root)":"(React Tree Reconciliation: Yielded)";pi(),Mo(R,"(React Tree Reconciliation)",_)}}function J0(){if(Pr){if(!sn)return;Cu=!0,S0=!1,ko.clear(),gl("(Committing Changes)")}}function Z0(){if(Pr){if(!sn)return;var a=null;S0?a="Lifecycle hook scheduled a cascading update":xu>0&&(a="Caused by a cascading update in earlier commit"),S0=!1,xu++,Cu=!1,ko.clear(),Mo("(Committing Changes)","(Committing Changes)",a)}}function Te(){if(Pr){if(!sn)return;di=0,gl("(Committing Snapshot Effects)")}}function et(){if(Pr){if(!sn)return;var a=di;di=0,Mo("(Committing Snapshot Effects: "+a+" Total)","(Committing Snapshot Effects)",null)}}function Ve(){if(Pr){if(!sn)return;di=0,gl("(Committing Host Effects)")}}function Gt(){if(Pr){if(!sn)return;var a=di;di=0,Mo("(Committing Host Effects: "+a+" Total)","(Committing Host Effects)",null)}}function Yt(){if(Pr){if(!sn)return;di=0,gl("(Calling Lifecycle Methods)")}}function sr(){if(Pr){if(!sn)return;var a=di;di=0,Mo("(Calling Lifecycle Methods: "+a+" Total)","(Calling Lifecycle Methods)",null)}}var Br=[],wn;wn=[];var fu=-1;function Gu(a){return{current:a}}function Kr(a,c){if(fu<0){Ke(!1,"Unexpected pop.");return}c!==wn[fu]&&Ke(!1,"Unexpected Fiber popped."),a.current=Br[fu],Br[fu]=null,wn[fu]=null,fu--}function Vr(a,c,_){fu++,Br[fu]=a.current,wn[fu]=_,a.current=c}var Bu;Bu={};var Sn={};Object.freeze(Sn);var C0=Gu(Sn),Au=Gu(!1),ei=Sn;function _l(a,c,_){return ni?Sn:_&&zi(c)?ei:C0.current}function Ps(a,c,_){if(!ni){var T=a.stateNode;T.__reactInternalMemoizedUnmaskedChildContext=c,T.__reactInternalMemoizedMaskedChildContext=_}}function Uu(a,c){if(ni)return Sn; Line truncated
* Move code with side effects to componentDidMount, and set initial state in the constructor.
Please update the following components: %s`,V)}if(T.size>0){var te=Po(T);Ke(!1,`Using UNSAFE_componentWillReceiveProps in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
* Move data fetching code or side effects to componentDidUpdate.
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
Please update the following components: %s`,te)}if(j.size>0){var oe=Po(j);Ke(!1,`Using UNSAFE_componentWillUpdate in strict mode is not recommended and may indicate bugs in your code. See https://fb.me/react-unsafe-component-lifecycles for details.
* Move data fetching code or side effects to componentDidUpdate.
Please update the following components: %s`,oe)}if(a.size>0){var Ie=Po(a);Bs(!1,`componentWillMount has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details.
* Move code with side effects to componentDidMount, and set initial state in the constructor.
* Rename componentWillMount to UNSAFE_componentWillMount to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder.
Please update the following components: %s`,Ie)}if(_.size>0){var Ye=Po(_);Bs(!1,`componentWillReceiveProps has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details.
* Move data fetching code or side effects to componentDidUpdate.
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps. Learn more at: https://fb.me/react-derived-state
* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder.
Please update the following components: %s`,Ye)}if(R.size>0){var pt=Po(R);Bs(!1,`componentWillUpdate has been renamed, and is not recommended for use. See https://fb.me/react-unsafe-component-lifecycles for details.
* Move data fetching code or side effects to componentDidUpdate.
* Rename componentWillUpdate to UNSAFE_componentWillUpdate to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run \`npx react-codemod rename-unsafe-lifecycles\` in your project source folder.
Please update the following components: %s`,pt)}};var Io=new Map,hf=new Set;wl.recordLegacyContextWarning=function(a,c){var _=t2(a);if(_===null){Ke(!1,"Expected to find a StrictMode component in a strict mode tree. This error is likely caused by a bug in React. Please file an issue.");return}if(!hf.has(a.type)){var T=Io.get(_);(a.type.contextTypes!=null||a.type.childContextTypes!=null||c!==null&&typeof c.getChildContext=="function")&&(T===void 0&&(T=[],Io.set(_,T)),T.push(a))}},wl.flushLegacyContextWarning=function(){Io.forEach(function(a,c){var _=new Set;a.forEach(function(j){_.add(qt(j.type)||"Component"),hf.add(j.type)});var T=Po(_),R=_r(c);Ke(!1,`Legacy context API has been detected within a strict-mode tree.
The old API will be supported in all 16.x releases, but applications using it should migrate to the new version.
Please update the following components: %s
Learn more about this warning here: https://fb.me/react-legacy-context%s`,T,R)})},wl.discardPendingWarnings=function(){Fa=[],ba=[],Pa=[],ua=[],ys=[],gs=[],Io=new Map}}var tl=null,ju=null,Ia=function(a){tl=a};function Zu(a){{if(tl===null)return a;var c=tl(a);return c===void 0?a:c.current}}function U0(a){return Zu(a)}function vf(a){{if(tl===null)return a;var c=tl(a);if(c===void 0){if(a!=null&&typeof a.render=="function"){var _=Zu(a.render);if(a.render!==_){var T={$$typeof:An,render:_};return a.displayName!==void 0&&(T.displayName=a.displayName),T}}return a}return c.current}}function jc(a,c){{if(tl===null)return!1;var _=a.elementType,T=c.type,R=!1,j=typeof T=="object"&&T!==null?T.$$typeof:null;switch(a.tag){case N:{typeof T=="function"&&(R=!0);break}case L:{(typeof T=="function"||j===vr)&&(R=!0);break}case ge:{(j===An||j===vr)&&(R=!0);break}case Oe:case le:{(j===Wt||j===vr)&&(R=!0);break}default:return!1}if(R){var V=tl(_);if(V!==void 0&&V===tl(T))return!0}return!1}}function lc(a){{if(tl===null||typeof WeakSet!="function")return;ju===null&&(ju=new WeakSet),ju.add(a)}}var Sl=function(a,c){{if(tl===null)return;var _=c.staleFamilies,T=c.updatedFamilies;Xa(),xp(function(){oa(a.current,T,_)})}},_s=function(a,c){{if(a.context!==Sn)return;Xa(),fv(function(){l_(c,a,null,null)})}};function oa(a,c,_){{var T=a.alternate,R=a.child,j=a.sibling,V=a.tag,te=a.type,oe=null;switch(V){case L:case le:case N:oe=te;break;case ge:oe=te.render;break;default:break}if(tl===null)throw new Error("Expected resolveFamily to be set during hot reload.");var Ie=!1,Ye=!1;if(oe!==null){var pt=tl(oe);pt!==void 0&&(_.has(pt)?Ye=!0:c.has(pt)&&(V===N?Ye=!0:Ie=!0))}ju!==null&&(ju.has(a)||T!==null&&ju.has(T))&&(Ye=!0),Ye&&(a._debugNeedsRemount=!0),(Ye||Ie)&&dl(a,bn),R!==null&&!Ye&&oa(R,c,_),j!==null&&oa(j,c,_)}}var n2=function(a,c){{var _=new Set,T=new Set(c.map(function(R){return R.current}));return la(a.current,T,_),_}};function la(a,c,_){{var T=a.child,R=a.sibling,j=a.tag,V=a.type,te=null;switch(j){case L:case le:case N:te=V;break;case ge:te=V.render;break;default:break}var oe=!1;te!==null&&c.has(te)&&(oe=!0),oe?sc(a,_):T!==null&&la(T,c,_),R!==null&&la(R,c,_)}}function sc(a,c){{var _=zc(a,c);if(_)return;for(var T=a;;){switch(T.tag){case W:c.add(T.stateNode);return;case q:c.add(T.stateNode.containerInfo);return;case U:c.add(T.stateNode.containerInfo);return}if(T.return===null)throw new Error("Expected to reach root first.");T=T.return}}}function zc(a,c){for(var _=a,T=!1;;){if(_.tag===W)T=!0,c.add(_.stateNode);else if(_.child!==null){_.child.return=_,_=_.child;continue}if(_===a)return T;for(;_.sibling===null;){if(_.return===null||_.return===a)return T;_=_.return}_.sibling.return=_.return,_=_.sibling}return!1}function bi(a,c){if(a&&a.defaultProps){var _=f({},c),T=a.defaultProps;for(var R in T)_[R]===void 0&&(_[R]=T[R]);return _}return c}function g(a){if(Yo(a),a._status!==So)throw a._result;return a._result}var y=Gu(null),A;A={};var F=null,I=null,J=null,fe=!1;function mt(){F=null,I=null,J=null,fe=!1}function Ct(){fe=!0}function Mt(){fe=!1}function Er(a,c){var _=a.type._context;Fu?(Vr(y,_._currentValue,a),_._currentValue=c,_._currentRenderer===void 0||_._currentRenderer===null||_._currentRenderer===A||Ke(!1,"Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported."),_._currentRenderer=A):(Vr(y,_._currentValue2,a),_._currentValue2=c,_._currentRenderer2===void 0||_._currentRenderer2===null||_._currentRenderer2===A||Ke(!1,"Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported."),_._currentRenderer2=A)}function $u(a){var c=y.current;Kr(y,a);var _=a.type._context;Fu?_._currentValue=c:_._currentValue2=c}function iu(a,c,_){if(ho(_,c))return 0;var T=typeof a._calculateChangedBits=="function"?a._calculateChangedBits(_,c):Ur;return(T&Ur)!==T&&Kt(!1,"calculateChangedBits: Expected the return value to be a 31-bit integer. Instead received: %s",T),T|0}function j0(a,c){for(var _=a;_!==null;){var T=_.alternate;if(_.childExpirationTime<c)_.childExpirationTime=c,T!==null&&T.childExpirationTime<c&&(T.childExpirationTime=c);else if(T!==null&&T.childExpirationTime<c)T.childExpirationTime=c;else break;_=_.return}}function Tl(a,c,_,T){var R=a.child;for(R!==null&&(R.return=a);R!==null;){var j=void 0,V=R.dependencies;if(V!==null){j=R.child;for(var te=V.firstContext;te!==null;){if(te.context===c&&(te.observedBits&_)!=0){if(R.tag===N){var oe=gu(T,null);oe.tag=Jt,Ua(R,oe)}R.expirationTime<T&&(R.expirationTime=T);var Ie=R.alternate;Ie!==null&&Ie.expirationTime<T&&(Ie.expirationTime=T),j0(R.return,T),V.expirationTime<T&&(V.expirationTime=T);break}te=te.next}}else if(R.tag===he)j=R.type===a.type?null:R.child;else if(Di&&R.tag===rt){var Ye=R.return;if(Ye===null)throw Error("We just came from a parent so we must have had a parent. This is a bug in React.");Ye.expirationTime<T&&(Ye.expirationTime=T);var pt=Ye.alternate;pt!==null&&pt.expirationTime<T&&(pt.expirationTime=T),j0(Ye,T) Line truncated
%s uses %s but also contains the following legacy lifecycles:%s%s%s
The above lifecycles should be removed. Learn more about this warning here:
https://fb.me/react-unsafe-component-lifecycles`,$r,wi,zt!==null?`
`+zt:"",vn!==null?`
`+vn:"",xr!==null?`
`+xr:""))}}}return R&&Ps(a,j,V),pt}function Cd(a,c){Oi(a,"componentWillMount");var _=c.state;typeof c.componentWillMount=="function"&&c.componentWillMount(),typeof c.UNSAFE_componentWillMount=="function"&&c.UNSAFE_componentWillMount(),gi(),_!==c.state&&(Ke(!1,"%s.componentWillMount(): Assigning directly to this.state is deprecated (except inside a component's constructor). Use setState instead.",qt(a.type)||"Component"),Vc.enqueueReplaceState(c,c.state,null))}function xd(a,c,_,T){var R=c.state;if(Oi(a,"componentWillReceiveProps"),typeof c.componentWillReceiveProps=="function"&&c.componentWillReceiveProps(_,T),typeof c.UNSAFE_componentWillReceiveProps=="function"&&c.UNSAFE_componentWillReceiveProps(_,T),gi(),c.state!==R){{var j=qt(a.type)||"Component";wd.has(j)||(wd.add(j),Ke(!1,"%s.componentWillReceiveProps(): Assigning directly to this.state is deprecated (except inside a component's constructor). Use setState instead.",j))}Vc.enqueueReplaceState(c,c.state,null)}}function ac(a,c,_,T){vh(a,c,_);var R=a.stateNode;R.props=_,R.state=a.memoizedState,R.refs=o2;var j=c.contextType;if(typeof j=="object"&&j!==null)R.context=He(j);else if(ni)R.context=Sn;else{var V=_l(a,c,!0);R.context=Uu(a,V)}{if(R.state===_){var te=qt(c)||"Component";Sd.has(te)||(Sd.add(te),Ke(!1,"%s: It is not recommended to assign props directly to state because updates to props won't be reflected in state. In most cases, it is better to use props directly.",te))}a.mode&cr&&wl.recordLegacyContextWarning(a,R),fi&&wl.recordUnsafeLifecycleWarnings(a,R)}var oe=a.updateQueue;oe!==null&&(mf(a,oe,_,R,T),R.state=a.memoizedState);var Ie=c.getDerivedStateFromProps;typeof Ie=="function"&&(yf(a,c,Ie,_),R.state=a.memoizedState),typeof c.getDerivedStateFromProps!="function"&&typeof R.getSnapshotBeforeUpdate!="function"&&(typeof R.UNSAFE_componentWillMount=="function"||typeof R.componentWillMount=="function")&&(Cd(a,R),oe=a.updateQueue,oe!==null&&(mf(a,oe,_,R,T),R.state=a.memoizedState)),typeof R.componentDidMount=="function"&&(a.effectTag|=mr)}function mh(a,c,_,T){var R=a.stateNode,j=a.memoizedProps;R.props=j;var V=R.context,te=c.contextType,oe=Sn;if(typeof te=="object"&&te!==null)oe=He(te);else if(!ni){var Ie=_l(a,c,!0);oe=Uu(a,Ie)}var Ye=c.getDerivedStateFromProps,pt=typeof Ye=="function"||typeof R.getSnapshotBeforeUpdate=="function";!pt&&(typeof R.UNSAFE_componentWillReceiveProps=="function"||typeof R.componentWillReceiveProps=="function")&&(j!==_||V!==oe)&&xd(a,R,_,oe),ch();var Nt=a.memoizedState,Vt=R.state=Nt,zt=a.updateQueue;if(zt!==null&&(mf(a,zt,_,R,T),Vt=a.memoizedState),j===_&&Nt===Vt&&!na()&&!qc())return typeof R.componentDidMount=="function"&&(a.effectTag|=mr),!1;typeof Ye=="function"&&(yf(a,c,Ye,_),Vt=a.memoizedState);var vn=qc()||Td(a,c,j,_,Nt,Vt,oe);return vn?(!pt&&(typeof R.UNSAFE_componentWillMount=="function"||typeof R.componentWillMount=="function")&&(Oi(a,"componentWillMount"),typeof R.componentWillMount=="function"&&R.componentWillMount(),typeof R.UNSAFE_componentWillMount=="function"&&R.UNSAFE_componentWillMount(),gi()),typeof R.componentDidMount=="function"&&(a.effectTag|=mr)):(typeof R.componentDidMount=="function"&&(a.effectTag|=mr),a.memoizedProps=_,a.memoizedState=Vt),R.props=_,R.state=Vt,R.context=oe,vn}function Ad(a,c,_,T,R){var j=c.stateNode,V=c.memoizedProps;j.props=c.type===c.elementType?V:bi(c.type,V);var te=j.context,oe=_.contextType,Ie=Sn;if(typeof oe=="object"&&oe!==null)Ie=He(oe);else if(!ni){var Ye=_l(c,_,!0);Ie=Uu(c,Ye)}var pt=_.getDerivedStateFromProps,Nt=typeof pt=="function"||typeof j.getSnapshotBeforeUpdate=="function";!Nt&&(typeof j.UNSAFE_componentWillReceiveProps=="function"||typeof j.componentWillReceiveProps=="function")&&(V!==T||te!==Ie)&&xd(c,j,T,Ie),ch();var Vt=c.memoizedState,zt=j.state=Vt,vn=c.updateQueue;if(vn!==null&&(mf(c,vn,T,j,R),zt=c.memoizedState),V===T&&Vt===zt&&!na()&&!qc())return typeof j.componentDidUpdate=="function"&&(V!==a.memoizedProps||Vt!==a.memoizedState)&&(c.effectTag|=mr),typeof j.getSnapshotBeforeUpdate=="function"&&(V!==a.memoizedProps||Vt!==a.memoizedState)&&(c.effectTag|=Co),!1;typeof pt=="function"&&(yf(c,_,pt,T),zt=c.memoizedState);var xr=qc()||Td(c,_,V,T,Vt,zt,Ie);return xr?(!Nt&&(typeof j.UNSAFE_componentWillUpdate=="function"||typeof j.componentWillUpdate=="function")&&(Oi(c,"componentWillUpdate"),typeof j.componentWillUpdate=="function"&&j.componentWillUpdate(T,zt,Ie),typeof j.UNSAFE_componentWillUpdate=="function"&&j.UNSAFE_componentWillUpdate(T,zt,Ie),gi()),typeof j.componentDidUpdate=="function"&&(c.effectTag|=mr),typeof j.getSnapshotBeforeUpdate=="function"&&(c.effectTag|=Co)):(typeof j.componentDidUpdate=="function"&&(V!==a.memoizedProps||Vt!==a.memoizedState)&&(c.effectTag|=mr),typeof j.getSnapshotBeforeUpdate=="function"&&(V!==a.memoizedProps||Vt!==a.memoizedState)&&(c.effectTag|=Co),c.memoizedProps=T,c.memoizedState=zt),j.props=T,j.state=zt,j.context=Ie,xr}var a2,Gc,Yc,Us,Rd,aa=function(a){};a2=!1,Gc=!1,Yc={},Us={},Rd={},aa=function(a){if(!(a===null||typeof a!="objec Line truncated
1. You may be adding a ref to a function component
2. You may be adding a ref to a component that was not created inside a component's render method
3. You have multiple copies of React loaded
See https://fb.me/react-refs-must-have-owner for more information.`)}}return T}function cc(a,c){if(a.type!=="textarea"){var _="";throw _=" If you meant to render a collection of children, use an array instead."+Rr(),Error("Objects are not valid as a React child (found: "+(Object.prototype.toString.call(c)==="[object Object]"?"object with keys {"+Object.keys(c).join(", ")+"}":c)+")."+_)}}function f2(){var a="Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it."+Rr();Rd[a]||(Rd[a]=!0,Kt(!1,"Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it."))}function yh(a){function c(it,Ot){if(!!a){var Je=it.lastEffect;Je!==null?(Je.nextEffect=Ot,it.lastEffect=Ot):it.firstEffect=it.lastEffect=Ot,Ot.nextEffect=null,Ot.effectTag=W0}}function _(it,Ot){if(!a)return null;for(var Je=Ot;Je!==null;)c(it,Je),Je=Je.sibling;return null}function T(it,Ot){for(var Je=new Map,Bt=Ot;Bt!==null;)Bt.key!==null?Je.set(Bt.key,Bt):Je.set(Bt.index,Bt),Bt=Bt.sibling;return Je}function R(it,Ot,Je){var Bt=wo(it,Ot,Je);return Bt.index=0,Bt.sibling=null,Bt}function j(it,Ot,Je){if(it.index=Je,!a)return Ot;var Bt=it.alternate;if(Bt!==null){var Mn=Bt.index;return Mn<Ot?(it.effectTag=ai,Ot):Mn}else return it.effectTag=ai,Ot}function V(it){return a&&it.alternate===null&&(it.effectTag=ai),it}function te(it,Ot,Je,Bt){if(Ot===null||Ot.tag!==ne){var Mn=Cy(Je,it.mode,Bt);return Mn.return=it,Mn}else{var pn=R(Ot,Je,Bt);return pn.return=it,pn}}function oe(it,Ot,Je,Bt){if(Ot!==null&&(Ot.elementType===Je.type||jc(Ot,Je))){var Mn=R(Ot,Je.props,Bt);return Mn.ref=fc(it,Ot,Je),Mn.return=it,Mn._debugSource=Je._source,Mn._debugOwner=Je._owner,Mn}else{var pn=Ty(Je,it.mode,Bt);return pn.ref=fc(it,Ot,Je),pn.return=it,pn}}function Ie(it,Ot,Je,Bt){if(Ot===null||Ot.tag!==q||Ot.stateNode.containerInfo!==Je.containerInfo||Ot.stateNode.implementation!==Je.implementation){var Mn=xy(Je,it.mode,Bt);return Mn.return=it,Mn}else{var pn=R(Ot,Je.children||[],Bt);return pn.return=it,pn}}function Ye(it,Ot,Je,Bt,Mn){if(Ot===null||Ot.tag!==m){var pn=Qa(Je,it.mode,Bt,Mn);return pn.return=it,pn}else{var Pi=R(Ot,Je,Bt);return Pi.return=it,Pi}}function pt(it,Ot,Je){if(typeof Ot=="string"||typeof Ot=="number"){var Bt=Cy(""+Ot,it.mode,Je);return Bt.return=it,Bt}if(typeof Ot=="object"&&Ot!==null){switch(Ot.$$typeof){case ae:{var Mn=Ty(Ot,it.mode,Je);return Mn.ref=fc(it,null,Ot),Mn.return=it,Mn}case Ce:{var pn=xy(Ot,it.mode,Je);return pn.return=it,pn}}if(Kc(Ot)||ur(Ot)){var Pi=Qa(Ot,it.mode,Je,null);return Pi.return=it,Pi}cc(it,Ot)}return typeof Ot=="function"&&f2(),null}function Nt(it,Ot,Je,Bt){var Mn=Ot!==null?Ot.key:null;if(typeof Je=="string"||typeof Je=="number")return Mn!==null?null:te(it,Ot,""+Je,Bt);if(typeof Je=="object"&&Je!==null){switch(Je.$$typeof){case ae:return Je.key===Mn?Je.type===ue?Ye(it,Ot,Je.props.children,Bt,Mn):oe(it,Ot,Je,Bt):null;case Ce:return Je.key===Mn?Ie(it,Ot,Je,Bt):null}if(Kc(Je)||ur(Je))return Mn!==null?null:Ye(it,Ot,Je,Bt,null);cc(it,Je)}return typeof Je=="function"&&f2(),null}function Vt(it,Ot,Je,Bt,Mn){if(typeof Bt=="string"||typeof Bt=="number"){var pn=it.get(Je)||null;return te(Ot,pn,""+Bt,Mn)}if(typeof Bt=="object"&&Bt!==null){switch(Bt.$$typeof){case ae:{var Pi=it.get(Bt.key===null?Je:Bt.key)||null;return Bt.type===ue?Ye(Ot,Pi,Bt.props.children,Mn,Bt.key):oe(Ot,Pi,Bt,Mn)}case Ce:{var oi=it.get(Bt.key===null?Je:Bt.key)||null;return Ie(Ot,oi,Bt,Mn)}}if(Kc(Bt)||ur(Bt)){var qu=it.get(Je)||null;return Ye(Ot,qu,Bt,Mn,null)}cc(Ot,Bt)}return typeof Bt=="function"&&f2(),null}function zt(it,Ot){{if(typeof it!="object"||it===null)return Ot;switch(it.$$typeof){case ae:case Ce:aa(it);var Je=it.key;if(typeof Je!="string")break;if(Ot===null){Ot=new Set,Ot.add(Je);break}if(!Ot.has(Je)){Ot.add(Je);break}Kt(!1,"Encountered two children with the same key, `%s`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted \u2014 the behavior is unsupported and could change in a future version.",Je);break;default:break}}return Ot}function vn(it,Ot,Je,Bt){for(var Mn=null,pn=0;pn<Je.length;pn++){var Pi=Je[pn];Mn=zt(Pi,Mn)}for(var oi=null,qu=null,ar=Ot,ou=0,qr=0,_u=null;ar!==null&&qr<Je.length;qr++){ar.index>qr?(_u=ar,ar=null):_u=ar.sibling;var _0=Nt(it,ar,Je[qr],Bt);if(_0===null){ar===null&&(ar=_u);break}a&&ar&&_0.alternate===null&&c(it,ar),ou=j(_0,ou,qr),qu===null?oi=_0:qu.sibling=_0,qu=_0,ar=_u}if(qr===Je.length)return _(it,ar),oi;if(ar===null){for(;qr<Je.length;qr++){var H0=pt(it,Je[qr],Bt);H0!==null&&(ou=j(H0,ou,qr),qu===null?oi=H0:qu.sibling=H0,qu=H0)}return oi}for(var Cs=T(it,ar);qr<Je.length;qr++){var Hu=Vt(Cs,it,qr,Je[qr],Bt);Hu!==null&&(a&&Hu.alternate!==null&&Cs.delete(Hu.key===null?qr:Hu.key),ou=j(Hu,ou,qr),qu===null?oi=Hu: Line truncated
`,_+=te}Kt(!1,`React has detected a change in the order of Hooks called by %s. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://fb.me/rules-of-hooks
Previous render Next render
------------------------------------------------------
%s ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
`,c,_)}}}function Al(){throw Error(`Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.`)}function xf(a,c){if(io)return!1;if(c===null)return Kt(!1,"%s received a final argument during this render, but not during the previous render. Even though the final argument is optional, its type cannot change between renders.",an),!1;a.length!==c.length&&Kt(!1,`The final argument passed to %s changed size between renders. The order and size of this array must remain constant.
Previous: %s
Incoming: %s`,an,"["+c.join(", ")+"]","["+a.join(", ")+"]");for(var _=0;_<c.length&&_<a.length;_++)if(!ho(a[_],c[_]))return!1;return!0}function Af(a,c,_,T,R,j){zn=j,ll=c,yo=a!==null?a.memoizedState:null,$l=a!==null?a._debugHookTypes:null,go=-1,io=a!==null&&a.type!==c.type,yo!==null?Un.current=jd:$l!==null?Un.current=Ud:Un.current=xh;var V=_(T,R);if(hc){do hc=!1,vc+=1,io=!1,yo=a!==null?a.memoizedState:null,qa=pc,Pn=null,ro=null,Zl=null,go=-1,Un.current=jd,V=_(T,R);while(hc);Es=null,vc=0}Un.current=o1;var te=ll;te.memoizedState=pc,te.expirationTime=Ou,te.updateQueue=Zl,te.effectTag|=Tf,te._debugHookTypes=$l;var oe=Pn!==null&&Pn.next!==null;if(zn=lt,ll=null,Pn=null,yo=null,pc=null,ro=null,qa=null,an=null,$l=null,go=-1,Ou=lt,Zl=null,Tf=0,oe)throw Error("Rendered fewer hooks than expected. This may be caused by an accidental early return statement.");return V}function v2(a,c,_){c.updateQueue=a.updateQueue,c.effectTag&=~(L0|mr),a.expirationTime<=_&&(a.expirationTime=lt)}function m2(){Un.current=o1,zn=lt,ll=null,Pn=null,yo=null,pc=null,ro=null,qa=null,$l=null,go=-1,an=null,Ou=lt,Zl=null,Tf=0,hc=!1,Es=null,vc=0}function mc(){var a={memoizedState:null,baseState:null,queue:null,baseUpdate:null,next:null};return ro===null?pc=ro=a:ro=ro.next=a,ro}function e1(){if(qa!==null)ro=qa,qa=ro.next,Pn=yo,yo=Pn!==null?Pn.next:null;else{if(yo===null)throw Error("Rendered more hooks than during the previous render.");Pn=yo;var a={memoizedState:Pn.memoizedState,baseState:Pn.baseState,queue:Pn.queue,baseUpdate:Pn.baseUpdate,next:null};ro===null?ro=pc=a:ro=ro.next=a,yo=Pn.next}return ro}function Ha(){return{lastEffect:null}}function Nd(a,c){return typeof c=="function"?c(a):c}function y2(a,c,_){var T=mc(),R;_!==void 0?R=_(c):R=c,T.memoizedState=T.baseState=R;var j=T.queue={last:null,dispatch:null,lastRenderedReducer:a,lastRenderedState:R},V=j.dispatch=u1.bind(null,ll,j);return[T.memoizedState,V]}function t1(a,c,_){var T=e1(),R=T.queue;if(R===null)throw Error("Should have a queue. This is likely a bug in React. Please file an issue.");if(R.lastRenderedReducer=a,vc>0){var j=R.dispatch;if(Es!==null){var V=Es.get(R);if(V!==void 0){Es.delete(R);var te=T.memoizedState,oe=V;do{var Ie=oe.action;te=a(te,Ie),oe=oe.next}while(oe!==null);return ho(te,T.memoizedState)||up(),T.memoizedState=te,T.baseUpdate===R.last&&(T.baseState=te),R.lastRenderedState=te,[te,j]}}return[T.memoizedState,j]}var Ye=R.last,pt=T.baseUpdate,Nt=T.baseState,Vt;if(pt!==null?(Ye!==null&&(Ye.next=null),Vt=pt.next):Vt=Ye!==null?Ye.next:null,Vt!==null){var zt=Nt,vn=null,xr=null,$r=pt,wi=Vt,N0=!1;do{var Vi=wi.expirationTime;if(Vi<zn)N0||(N0=!0,xr=$r,vn=zt),Vi>Ou&&(Ou=Vi,G2(Ou));else if(vv(Vi,wi.suspenseConfig),wi.eagerReducer===a)zt=wi.eagerState;else{var it=wi.action;zt=a(zt,it)}$r=wi,wi=wi.next}while(wi!==null&&wi!==Vt);N0||(xr=$r,vn=zt),ho(zt,T.memoizedState)||up(),T.memoizedState=zt,T.baseUpdate=xr,T.baseState=vn,R.lastRenderedState=zt}var Ot=R.dispatch;return[T.memoizedState,Ot]}function Rf(a){var c=mc();typeof a=="function"&&(a=a()),c.memoizedState=c.baseState=a;var _=c.queue={last:null,dispatch:null,lastRenderedReducer:Nd,lastRenderedState:a},T=_.dispatch=u1.bind(null,ll,_);return[c.memoizedState,T]}function n1(a){return t1(Nd,a)}function Wa(a,c,_,T){var R={tag:a,create:c,destroy:_,deps:T,next:null};if(Zl===null)Zl=Ha(),Zl.lastEffect=R.next=R;else{var j=Zl.lastEffect;if(j===null)Zl.lastEffect=R.next=R;else{var V=j.next;j.next=R,R.next=V,Zl.lastEffect=R}}return R}function r1(a){var c=mc(),_={current:a};return Object.seal(_),c.memoizedState=_,_}function Ld(a){var c=e1();return c.memoizedState}function g2(a,c,_,T){var R=mc(),j=T===void 0?null:T;Tf|=a,R.memoizedState=Wa(c,_,void 0,j)}function yc(a,c,_,T){var R=e1(),j=T===void 0?null:T,V=void 0;if(Pn!==null){var te=Pn.memoizedState;if(V=te.destroy,j!==null){var oe=te.deps;if(xf(j,oe)){Wa(wf,_,V,j);return}}}Tf|=a,R.memoizedState=Wa(c,_,V,j)}function i1(a,c){return typeof jest!="undefined"&&Av(ll),g2(mr|L0,rr|$c,a,c)}function Rl(a,c){return typeof jest!="undefined"&&Av(ll),yc(mr|L0,rr|$c,a,c)}function pa(a,c){return g2(mr,Sf|ol,a,c)}function wh(a,c){return yc(mr,Sf|ol,a,c)}function Fd(a,c){if(typeof c=="function"){var _=c,T=a();return _(T),function(){_(null)}}else if(c!=null){var R=c;R.hasOwnProperty("current")||Kt(!1,"Expected useImperativeHandle() first argument to either be a ref callback or React.createRef() object. Instead received: %s.","an object with keys {"+Object.keys(R).join(", ")+"}");var j=a();return R.current=j,function(){R.current=null}}}function bd(a,c,_){typeof c!="function"&&Kt(!1,"Expected useImperativeHandle() second argument to be a function that creates a handle. Instead received: %s.",c!==null?typeof c:"null");var T=_!=null?_.concat([a]):null;return g2(mr,Sf|ol,Fd.bind(null,c,a),T)}function Sh(a,c,_){typeof c!="function"&&Kt(!1,"Expected useImperativeHandle() second argument to be a function that creates a handle. Instead received: %s.",c!==null?typeof c:"null");var T=_!=null?_.concat([a]):null;return yc( Line truncated
Check the render method of \``+T+"`.");var R=T||a._debugID||"",j=a._debugSource;j&&(R=j.fileName+":"+j.lineNumber),Kd[R]||(Kd[R]=!0,Kt(!1,"Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?%s",_))}if(Wl&&c.defaultProps!==void 0){var V=qt(c)||"Unknown";Xd[V]||(Ke(!1,"%s: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.",V),Xd[V]=!0)}if(typeof c.getDerivedStateFromProps=="function"){var te=qt(c)||"Unknown";Ws[te]||(Ke(!1,"%s: Function components do not support getDerivedStateFromProps.",te),Ws[te]=!0)}if(typeof c.contextType=="object"&&c.contextType!==null){var oe=qt(c)||"Unknown";Hs[oe]||(Ke(!1,"%s: Function components do not support contextType.",oe),Hs[oe]=!0)}}var w2={dehydrated:null,retryTime:lt};function $d(a,c,_){return Jc(a,Qc)&&(c===null||c.memoizedState!==null)}function qh(a,c,_){var T=c.mode,R=c.pendingProps;a_(c)&&(c.effectTag|=Hr);var j=xl.current,V=!1,te=(c.effectTag&Hr)!==_i;if(te||$d(j,a,c)?(V=!0,c.effectTag&=~Hr):(a===null||a.memoizedState!==null)&&R.fallback!==void 0&&R.unstable_avoidThisFallback!==!0&&(j=d2(j,Ef)),j=ca(j),Or(c,j),"maxDuration"in R&&(p1||(p1=!0,Kt(!1,"maxDuration has been removed from React. Remove the maxDuration prop."))),a===null){if(R.fallback!==void 0&&(Vd(c),Di)){var oe=c.memoizedState;if(oe!==null){var Ie=oe.dehydrated;if(Ie!==null)return Hh(c,Ie,_)}}if(V){var Ye=R.fallback,pt=Qa(null,T,lt,null);if(pt.return=c,(c.mode&Y)===Sr){var Nt=c.memoizedState,Vt=Nt!==null?c.child.child:c.child;pt.child=Vt;for(var zt=Vt;zt!==null;)zt.return=pt,zt=zt.sibling}var vn=Qa(Ye,T,_,null);return vn.return=c,pt.sibling=vn,c.memoizedState=w2,c.child=pt,vn}else{var xr=R.children;return c.memoizedState=null,c.child=Xc(c,null,xr,_)}}else{var $r=a.memoizedState;if($r!==null){if(Di){var wi=$r.dehydrated;if(wi!==null)if(te){if(c.memoizedState!==null)return c.child=a.child,c.effectTag|=Hr,null;var N0=R.fallback,Vi=Qa(null,T,lt,null);if(Vi.return=c,Vi.child=null,(c.mode&Y)===Sr)for(var it=Vi.child=c.child;it!==null;)it.return=Vi,it=it.sibling;else gf(c,a.child,null,_);if(Zt&&c.mode&Jr){for(var Ot=0,Je=Vi.child;Je!==null;)Ot+=Je.treeBaseDuration,Je=Je.sibling;Vi.treeBaseDuration=Ot}var Bt=Qa(N0,T,_,null);return Bt.return=c,Vi.sibling=Bt,Bt.effectTag|=ai,Vi.childExpirationTime=lt,c.memoizedState=w2,c.child=Vi,Bt}else return Wh(a,c,wi,$r,_)}var Mn=a.child,pn=Mn.sibling;if(V){var Pi=R.fallback,oi=wo(Mn,Mn.pendingProps,lt);if(oi.return=c,(c.mode&Y)===Sr){var qu=c.memoizedState,ar=qu!==null?c.child.child:c.child;if(ar!==Mn.child){oi.child=ar;for(var ou=ar;ou!==null;)ou.return=oi,ou=ou.sibling}}if(Zt&&c.mode&Jr){for(var qr=0,_u=oi.child;_u!==null;)qr+=_u.treeBaseDuration,_u=_u.sibling;oi.treeBaseDuration=qr}var _0=wo(pn,Pi,pn.expirationTime);return _0.return=c,oi.sibling=_0,oi.childExpirationTime=lt,c.memoizedState=w2,c.child=oi,_0}else{var H0=R.children,Cs=Mn.child,Hu=gf(c,Cs,H0,_);return c.memoizedState=null,c.child=Hu}}else{var pl=a.child;if(V){var Ja=R.fallback,jo=Qa(null,T,lt,null);if(jo.return=c,jo.child=pl,pl!==null&&(pl.return=jo),(c.mode&Y)===Sr){var xs=c.memoizedState,X2=xs!==null?c.child.child:c.child;jo.child=X2;for(var Uf=X2;Uf!==null;)Uf.return=jo,Uf=Uf.sibling}if(Zt&&c.mode&Jr){for(var Rc=0,Pl=jo.child;Pl!==null;)Rc+=Pl.treeBaseDuration,Pl=Pl.sibling;jo.treeBaseDuration=Rc}var zo=Qa(Ja,T,_,null);return zo.return=c,jo.sibling=zo,zo.effectTag|=ai,jo.childExpirationTime=lt,c.memoizedState=w2,c.child=jo,zo}else{c.memoizedState=null;var O1=R.children;return c.child=gf(c,pl,O1,_)}}}}function ep(a,c,_){c.memoizedState=null;var T=c.pendingProps,R=T.children;return _o(a,c,R,_),c.child}function Hh(a,c,_){if((a.mode&Y)===Sr)Kt(!1,"Cannot hydrate Suspense in legacy mode. Switch from ReactDOM.hydrate(element, container) to ReactDOM.createBlockingRoot(container, { hydrate: true }).render(element) or remove the Suspense components from the server rendered components."),a.expirationTime=bn;else if(h0(c)){var T=Fl(),R=ms(T);Ln&&x(R),a.expirationTime=R}else a.expirationTime=hi,Ln&&x(hi);return null}function Wh(a,c,_,T,R){if(qd(),(c.mode&Y)===Sr||h0(_))return ep(a,c,R);var j=a.childExpirationTime>=R;if(va||j){if(R<bn&&T.retryTime<=R){var V=R+1;T.retryTime=V,dl(a,V)}return yv(),ep(a,c,R)}else{if(Ls(_))return c.effectTag|=Hr,c.child=a.child,Fs(_,dy.bind(null,a)),null;Em(c,_);for(var te=c.pendingProps,oe=te.children,Ie=Xc(c,null,oe,R),Ye=Ie;Ye;)Ye.effectTag|=tu,Ye=Ye.sibling;return c.child=Ie,c.child}}function tp(a,c){a.expirationTime<c&&(a.expirationTime=c);var _=a.alternate;_!==null&&_.expirationTime<c&&(_.expirationTime=c),j0(a.return,c)}function Rm(a,c,_){for(var T=c;T!==null;){if(T.tag===pe){var R=T.memoizedState;R!==null&&tp(T,_)}else if(T.tag===wt)tp(T,_);else if(T.child!==null){T.child.return=T,T=T.child;continue}if(T===a)return;for(;T.sibling===null;){if(T.return===null||T.return===a)return;T=T.return}T.sibling.return=T.return,T=T.sibling}}functioLine truncated
Recreating the tree from scratch failed so React will unmount the tree.`:Ie=`Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://fb.me/react-error-boundaries to learn more about error boundaries.`;var Ye=""+oe+R+`
`+(""+Ie);console.error(Ye)}}}var ev=null;ev=new Set;var Gs=typeof WeakSet=="function"?WeakSet:Set;function hp(a,c){var _=c.source,T=c.stack;T===null&&_!==null&&(T=_r(_));var R={componentName:_!==null?qt(_.type):null,componentStack:T!==null?T:"",error:c.value,errorBoundary:null,errorBoundaryName:null,errorBoundaryFound:!1,willRetry:!1};a!==null&&a.tag===N&&(R.errorBoundary=a.stateNode,R.errorBoundaryName=qt(a.type),R.errorBoundaryFound=!0,R.willRetry=!0);try{n0(R)}catch(j){setTimeout(function(){throw j})}}var jm=function(a,c){Oi(a,"componentWillUnmount"),c.props=a.memoizedProps,c.state=a.memoizedState,c.componentWillUnmount(),gi()};function tv(a,c){if(sl(null,jm,null,a,c),Jn()){var _=Vs();Pf(a,_)}}function vp(a){var c=a.ref;if(c!==null)if(typeof c=="function"){if(sl(null,c,null,null),Jn()){var _=Vs();Pf(a,_)}}else c.current=null}function zm(a,c){if(sl(null,c,null),Jn()){var _=Vs();Pf(a,_)}}function mp(a,c){switch(c.tag){case L:case ge:case le:{_c(ym,wf,c);return}case N:{if(c.effectTag&Co&&a!==null){var _=a.memoizedProps,T=a.memoizedState;Oi(c,"getSnapshotBeforeUpdate");var R=c.stateNode;c.type===c.elementType&&!ma&&(R.props!==c.memoizedProps&&Kt(!1,"Expected %s props to match memoized props before getSnapshotBeforeUpdate. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(c.type)||"instance"),R.state!==c.memoizedState&&Kt(!1,"Expected %s state to match memoized state before getSnapshotBeforeUpdate. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(c.type)||"instance"));var j=R.getSnapshotBeforeUpdate(c.elementType===c.type?_:bi(c.type,_),T);{var V=ev;j===void 0&&!V.has(c.type)&&(V.add(c.type),Ke(!1,"%s.getSnapshotBeforeUpdate(): A snapshot value (or null) must be returned. You have returned undefined.",qt(c.type)))}R.__reactInternalSnapshotBeforeUpdate=j,gi()}return}case U:case W:case ne:case q:case Ge:return;default:throw Error("This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.")}}function _c(a,c,_){var T=_.updateQueue,R=T!==null?T.lastEffect:null;if(R!==null){var j=R.next,V=j;do{if((V.tag&a)!==wf){var te=V.destroy;V.destroy=void 0,te!==void 0&&te()}if((V.tag&c)!==wf){var oe=V.create;V.destroy=oe();{var Ie=V.destroy;if(Ie!==void 0&&typeof Ie!="function"){var Ye=void 0;Ie===null?Ye=" You returned null. If your effect does not require clean up, return undefined (or nothing).":typeof Ie.then=="function"?Ye=`
It looks like you wrote useEffect(async () => ...) or returned a Promise. Instead, write the async function inside your effect and call it immediately:
useEffect(() => {
async function fetchData() {
// You can await here
const response = await MyAPI.getData(someId);
// ...
}
fetchData();
}, [someId]); // Or [] if effect doesn't need props or state
Learn more about data fetching with Hooks: https://fb.me/react-hooks-data-fetching`:Ye=" You returned: "+Ie,Ke(!1,"An effect function must not return anything besides a function, which is used for clean-up.%s%s",Ye,_r(_))}}}V=V.next}while(V!==j)}}function Ea(a){if((a.effectTag&L0)!==_i)switch(a.tag){case L:case ge:case le:{_c(rr,wf,a),_c(wf,$c,a);break}default:break}}function yp(a,c,_,T){switch(_.tag){case L:case ge:case le:{_c(gm,ol,_);break}case N:{var R=_.stateNode;if(_.effectTag&mr)if(c===null)Oi(_,"componentDidMount"),_.type===_.elementType&&!ma&&(R.props!==_.memoizedProps&&Kt(!1,"Expected %s props to match memoized props before componentDidMount. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(_.type)||"instance"),R.state!==_.memoizedState&&Kt(!1,"Expected %s state to match memoized state before componentDidMount. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(_.type)||"instance")),R.componentDidMount(),gi();else{var j=_.elementType===_.type?c.memoizedProps:bi(_.type,c.memoizedProps),V=c.memoizedState;Oi(_,"componentDidUpdate"),_.type===_.elementType&&!ma&&(R.props!==_.memoizedProps&&Kt(!1,"Expected %s props to match memoized props before componentDidUpdate. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(_.type)||"instance"),R.state!==_.memoizedState&&Kt(!1,"Expected %s state to match memoized state before componentDidUpdate. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(_.type)||"instance")),R.componentDidUpdate(j,V,R.__reactInternalSnapshotBeforeUpdate),gi()}var te=_.updateQueue;te!==null&&(_.type===_.elementType&&!ma&&(R.props!==_.memoizedProps&&Kt(!1,"Expected %s props to match memoized props before processing the update queue. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(_.type)||"instance"),R.state!==_.memoizedState&&Kt(!1,"Expected %s state to match memoized state before processing the update queue. This might either be because of a bug in React, or because a component reassigns its own `this.props`. Please file an issue.",qt(_.type)||"instance")),vo(_,te,R,T));return}case U:{var oe=_.updateQueue;if(oe!==null){var Ie=null;if(_.child!==null)switch(_.child.tag){case W:Ie=Ro(_.child.stateNode);break;case N:Ie=_.child.stateNode;break}vo(_,oe,Ie,T)}return}case W:{var Ye=_.stateNode;if(c===null&&_.effectTag&mr){var pt=_.type,Nt=_.memoizedProps;Pu(Ye,pt,Nt,_)}return}case ne:return;case q:return;case ze:{if(Zt){var Vt=_.memoizedProps.onRender;typeof Vt=="function"&&(Ln?Vt(_.memoizedProps.id,c===null?"mount":"update",_.actualDuration,_.treeBaseDuration,_.actualStartTime,kl(),a.memoizedInteractions):Vt(_.memoizedProps.id,c===null?"mount":"update",_.actualDuration,_.treeBaseDuration,_.actualStartTime,kl()))}return}case pe:{Nl(a,_);return}case wt:case Ge:case xt:case $e:return;default:throw Error("This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.")}}function M2(a,c){if(P0)for(var _=a;;){if(_.tag===W){var T=_.stateNode;c?Oa(T):Zs(_.stateNode,_.memoizedProps)}else if(_.tag===ne){var R=_.stateNode;c?p0(R):K0(R,_.memoizedProps)}else if(_.tag===pe&&_.memoizedState!==null&&_.memoizedState.dehydrated===null){var j=_.child.sibling;j.return=_,_=j;continue}else if(_.child!==null){_.child.return=_,_=_.child;continue}if(_===a)return;for(;_.sibling===null;){if(_.return===null||_.return===a)return;_=_.return}_.sibling.return=_.return,_=_.sibling}}function ku(a){var c=a.ref;if(c!==null){var _=a.stateNode,T;switch(a.tag){case W:T=Ro(_);break;default:T=_}Du&&a.tag===$e&&(T=_.methods),typeof c=="function"?c(T):(c.hasOwnProperty("current")||Ke(!1,"Unexpected ref object provided for %s. Use either a ref-setter function or React.createRef().%s",qt(a.type),_r(a)),c.current=T)}}function zu(a){var c=a.ref;c!==null&&(typeof c=="function"?c(null):c.current=null)}function gp(a,c,_){switch(Rn(c),c.tag){case L:case ge:case Oe:case le:{var T=c.updateQueue;if(T!==null){var R=T.lastEffect;if(R!==null){var j=R.next,V=_>Wn?Wn:_;_n(V,function(){var $r=j;do{var wi=$r.destroy;wi!==void 0&&zm(c,wi),$r=$r.next}while($r!==j)})}}break}case N:{vp(c);var te=c.stateNode;typeof te.componentWillUnmount=="function"&&tv(c,te);return}case W:{if(ci){var oe=c.dependencies;if(oe!==null){var Ie=oe.responders;if(Ie!==null){for(var Ye=Array.from(Ie.values()),pt=0,Nt=Ye.length;pt<Nt;pt++){var Vt=Ye[pt];Me(Vt)}oe.responders=null}}}vp(c);return}case q:{P0?g1(a,c,_):X&&qm(c);return}case xt:{if(Ht){var zt=c.stateNode;zt!==null&&(ka(zt),c.stateNode=null)}return}case rt:{if(Ui){var vn=a.hydrationCallbacks;if(vn!==null){var xr=vn.onDeleted;xr&&xr(c.stateNode)}}return}case $e:Du&&vp(c)}}functio Line truncated
Add a <Suspense fallback=...> component higher in the tree to provide a loading indicator or placeholder to display.`+_r(_))}Rp(),T=cp(T,_);var Nt=c;do{switch(Nt.tag){case U:{var Vt=T;Nt.effectTag|=f0,Nt.expirationTime=R;var zt=uv(Nt,Vt,R);r2(Nt,zt);return}case N:var vn=T,xr=Nt.type,$r=Nt.stateNode;if((Nt.effectTag&Hr)===_i&&(typeof xr.getDerivedStateFromError=="function"||$r!==null&&typeof $r.componentDidCatch=="function"&&!Lp($r))){Nt.effectTag|=f0,Nt.expirationTime=R;var wi=ov(Nt,vn,R);r2(Nt,wi);return}break;default:break}Nt=Nt.return}while(Nt!==null)}var wa=Math.ceil,Cr=at.ReactCurrentDispatcher,Ep=at.ReactCurrentOwner,fl=at.IsSomeRendererActing,cu=0,E1=1,ki=2,Dp=4,F2=8,Do=16,Ss=32,Mf=0,b2=1,wp=2,D1=3,w1=4,Sp=5,Zn=cu,cl=null,qn=null,q0=lt,k0=Mf,P2=null,Ll=bn,S1=bn,Dc=null,wc=lt,I2=!1,Tp=0,M0=500,fn=null,B2=!1,U2=null,Sc=null,Tc=!1,Cc=null,T1=y0,Cp=lt,Ka=null,Km=50,xc=0,j2=null,sv=50,C1=0,Nf=null,Lf=null,x1=lt;function Fl(){return(Zn&(Do|Ss))!==cu?Ju(vt()):(x1!==lt||(x1=Ju(vt())),x1)}function Ac(){return Ju(vt())}function Ff(a,c,_){var T=c.mode;if((T&Y)===Sr)return bn;var R=Xt();if((T&Qr)===Sr)return R===Ci?bn:Qu;if((Zn&Do)!==cu)return q0;var j;if(_!==null)j=ia(a,_.timeoutMs|0||pf);else switch(R){case Ci:j=bn;break;case Xr:j=La(a);break;case Wn:case Xu:j=ms(a);break;case m0:j=Qi;break;default:throw Error("Expected a valid priority level")}return cl!==null&&j===q0&&(j-=1),j}function Xm(a,c){hy(),gy(a);var _=z2(a,c);if(_===null){my(a);return}Up(a,c),ta();var T=Xt();if(c===bn?(Zn&F2)!==cu&&(Zn&(Do|Ss))===cu?(H(_,c),A1(_)):(Uo(_),H(_,c),Zn===cu&&It()):(Uo(_),H(_,c)),(Zn&Dp)!==cu&&(T===Xr||T===Ci))if(Ka===null)Ka=new Map([[_,c]]);else{var R=Ka.get(_);(R===void 0||R>c)&&Ka.set(_,c)}}var dl=Xm;function z2(a,c){a.expirationTime<c&&(a.expirationTime=c);var _=a.alternate;_!==null&&_.expirationTime<c&&(_.expirationTime=c);var T=a.return,R=null;if(T===null&&a.tag===U)R=a.stateNode;else for(;T!==null;){if(_=T.alternate,T.childExpirationTime<c&&(T.childExpirationTime=c),_!==null&&_.childExpirationTime<c&&(_.childExpirationTime=c),T.return===null&&T.tag===U){R=T.stateNode;break}T=T.return}return R!==null&&(cl===R&&(G2(c),k0===w1&&Bf(R,q0)),o_(R,c)),R}function q2(a){var c=a.lastExpiredTime;if(c!==lt)return c;var _=a.firstPendingTime;if(!u_(a,_))return _;var T=a.lastPingedTime,R=a.nextKnownPendingLevel;return T>R?T:R}function Uo(a){var c=a.lastExpiredTime;if(c!==lt){a.callbackExpirationTime=bn,a.callbackPriority=Ci,a.callbackNode=En(A1.bind(null,a));return}var _=q2(a),T=a.callbackNode;if(_===lt){T!==null&&(a.callbackNode=null,a.callbackExpirationTime=lt,a.callbackPriority=y0);return}var R=Fl(),j=$1(R,_);if(T!==null){var V=a.callbackPriority,te=a.callbackExpirationTime;if(te===_&&V>=j)return;er(T)}a.callbackExpirationTime=_,a.callbackPriority=j;var oe;_===bn?oe=En(A1.bind(null,a)):oo?oe=yn(j,H2.bind(null,a)):oe=yn(j,H2.bind(null,a),{timeout:bo(_)-vt()}),a.callbackNode=oe}function H2(a,c){if(x1=lt,c){var _=Fl();return qp(a,_),Uo(a),null}var T=q2(a);if(T!==lt){var R=a.callbackNode;if((Zn&(Do|Ss))!==cu)throw Error("Should not already be working.");if(Xa(),(a!==cl||T!==q0)&&(bf(a,T),ee(a,T)),qn!==null){var j=Zn;Zn|=Do;var V=pv(a),te=W2(a);ff(qn);do try{oy();break}catch(Ye){dv(a,Ye)}while(!0);if(mt(),Zn=j,hv(V),Ln&&V2(te),k0===b2){var oe=P2;throw Bp(),bf(a,T),Bf(a,T),Uo(a),oe}if(qn!==null)Bp();else{Tv();var Ie=a.finishedWork=a.current.alternate;a.finishedExpirationTime=T,Qm(a,Ie,k0,T)}if(Uo(a),a.callbackNode===R)return H2.bind(null,a)}}return null}function Qm(a,c,_,T){switch(cl=null,_){case Mf:case b2:throw Error("Root did not complete. This is a bug in React.");case wp:{qp(a,T>Qi?Qi:T);break}case D1:{Bf(a,T);var R=a.lastSuspendedTime;T===R&&(a.nextKnownPendingLevel=Op(c)),d();var j=Ll===bn;if(j&&!(Y0&&If.current)){var V=Tp+M0-vt();if(V>10){if(I2){var te=a.lastPingedTime;if(te===lt||te>=T){a.lastPingedTime=T,bf(a,T);break}}var oe=q2(a);if(oe!==lt&&oe!==T)break;if(R!==lt&&R!==T){a.lastPingedTime=R;break}a.timeoutHandle=St(r0.bind(null,a),V);break}}r0(a);break}case w1:{Bf(a,T);var Ie=a.lastSuspendedTime;if(T===Ie&&(a.nextKnownPendingLevel=Op(c)),d(),!(Y0&&If.current)){if(I2){var Ye=a.lastPingedTime;if(Ye===lt||Ye>=T){a.lastPingedTime=T,bf(a,T);break}}var pt=q2(a);if(pt!==lt&&pt!==T)break;if(Ie!==lt&&Ie!==T){a.lastPingedTime=Ie;break}var Nt;if(S1!==bn)Nt=bo(S1)-vt();else if(Ll===bn)Nt=0;else{var Vt=_v(Ll),zt=vt(),vn=bo(T)-zt,xr=zt-Vt;xr<0&&(xr=0),Nt=Pp(xr)-xr,vn<Nt&&(Nt=vn)}if(Nt>10){a.timeoutHandle=St(r0.bind(null,a),Nt);break}}r0(a);break}case Sp:{if(!(Y0&&If.current)&&Ll!==bn&&Dc!==null){var $r=Ip(Ll,T,Dc);if($r>10){Bf(a,T),a.timeoutHandle=St(r0.bind(null,a),$r);break}}r0(a);break}default:throw Error("Unknown root exit status.")}}function A1(a){var c=a.lastExpiredTime,_=c!==lt?c:bn;if(a.finishedExpirationTime===_)r0(a);else{if((Zn&(Do|Ss))!==cu)throw Error("Should not already be working.");if(Xa(),(a!==cl||_!==q0)&&(bf(a,_),ee(a,_)),qn!==null){var T=Zn;Zn|=Do;var R=pv(a),j=W2(a);ff(qn);do try{Ev();break}catch(te){dv(a,te)}whi Line truncated
Be sure to use the matching version of act() corresponding to your renderer:
// for react-dom:
import {act} from 'react-dom/test-utils';
// ...
act(() => ...);
// for react-test-renderer:
import TestRenderer from 'react-test-renderer';
const {act} = TestRenderer;
// ...
act(() => ...);%s`,_r(a))}function Av(a){fs===!0&&(a.mode&cr)!==Sr&&fl.current===!1&&If.current===!1&&Ke(!1,`An update to %s ran an effect, but was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act%s`,qt(a.type),_r(a))}function _y(a){fs===!0&&Zn===cu&&fl.current===!1&&If.current===!1&&Ke(!1,`An update to %s inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act%s`,qt(a.type),_r(a))}var Ey=_y,zp=!1;function Dy(a){zp===!1&&t.unstable_flushAllWithoutAsserting===void 0&&(a.mode&Y||a.mode&Qr?(zp=!0,Ke(!1,`In Concurrent or Sync modes, the "scheduler" module needs to be mocked to guarantee consistent behaviour across tests and browsers. For example, with jest:
jest.mock('scheduler', () => require('scheduler/unstable_mock'));
For more info, visit https://fb.me/react-mock-scheduler`)):Yi===!0&&(zp=!0,Ke(!1,`Starting from React v17, the "scheduler" module will need to be mocked to guarantee consistent behaviour across tests and browsers. For example, with jest:
jest.mock('scheduler', () => require('scheduler/unstable_mock'));
For more info, visit https://fb.me/react-mock-scheduler`)))}var Ys=null;function wy(a){{var c=Xt();if((a.mode&Qr)!==_i&&(c===Xr||c===Ci))for(var _=a;_!==null;){var T=_.alternate;if(T!==null)switch(_.tag){case N:var R=T.updateQueue;if(R!==null)for(var j=R.firstUpdate;j!==null;){var V=j.priority;if(V===Xr||V===Ci){Ys===null?Ys=new Set([qt(_.type)]):Ys.add(qt(_.type));break}j=j.next}break;case L:case ge:case le:if(_.memoizedState!==null&&_.memoizedState.baseUpdate!==null)for(var te=_.memoizedState.baseUpdate;te!==null;){var oe=te.priority;if(oe===Xr||oe===Ci){Ys===null?Ys=new Set([qt(_.type)]):Ys.add(qt(_.type));break}if(te.next===_.memoizedState.baseUpdate)break;te=te.next}break;default:break}_=_.return}}}function d(){if(Ys!==null){var a=[];Ys.forEach(function(c){return a.push(c)}),Ys=null,a.length>0&&Ke(!1,`%s triggered a user-blocking update that suspended.
The fix is to split the update into multiple parts: a user-blocking update to provide immediate feedback, and another update that triggers the bulk of the changes.
Refer to the documentation for useTransition to learn how to implement this pattern.`,a.sort().join(", "))}}function v(a,c){return c*1e3+a.interactionThreadID}function x(a){!Ln||(Lf===null?Lf=[a]:Lf.push(a))}function b(a,c,_){if(!!Ln&&_.size>0){var T=a.pendingInteractionMap,R=T.get(c);R!=null?_.forEach(function(te){R.has(te)||te.__count++,R.add(te)}):(T.set(c,new Set(_)),_.forEach(function(te){te.__count++}));var j=k.__subscriberRef.current;if(j!==null){var V=v(a,c);j.onWorkScheduled(_,V)}}}function H(a,c){!Ln||b(a,c,k.__interactionsRef.current)}function ee(a,c){if(!!Ln){var _=new Set;if(a.pendingInteractionMap.forEach(function(j,V){V>=c&&j.forEach(function(te){return _.add(te)})}),a.memoizedInteractions=_,_.size>0){var T=k.__subscriberRef.current;if(T!==null){var R=v(a,c);try{T.onWorkStarted(_,R)}catch(j){yn(Ci,function(){throw j})}}}}}function de(a,c){if(!!Ln){var _=a.firstPendingTime,T;try{if(T=k.__subscriberRef.current,T!==null&&a.memoizedInteractions.size>0){var R=v(a,c);T.onWorkStopped(a.memoizedInteractions,R)}}catch(V){yn(Ci,function(){throw V})}finally{var j=a.pendingInteractionMap;j.forEach(function(V,te){te>_&&(j.delete(te),V.forEach(function(oe){if(oe.__count--,T!==null&&oe.__count===0)try{T.onInteractionScheduledWorkCompleted(oe)}catch(Ie){yn(Ci,function(){throw Ie})}}))})}}}var ye=null,be=null,gt=!1,Dt=typeof __REACT_DEVTOOLS_GLOBAL_HOOK__!="undefined";function Rt(a){if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__=="undefined")return!1;var c=__REACT_DEVTOOLS_GLOBAL_HOOK__;if(c.isDisabled)return!0;if(!c.supportsFiber)return Ke(!1,"The installed version of React DevTools is too old and will not work with the current version of React. Please update React DevTools. https://fb.me/react-devtools"),!0;try{var _=c.inject(a);ye=function(T,R){try{var j=(T.current.effectTag&Hr)===Hr;if(Zt){var V=Ac(),te=$1(V,R);c.onCommitFiberRoot(_,T,te,j)}else c.onCommitFiberRoot(_,T,void 0,j)}catch(oe){gt||(gt=!0,Ke(!1,"React DevTools encountered an error: %s",oe))}},be=function(T){try{c.onCommitFiberUnmount(_,T)}catch(R){gt||(gt=!0,Ke(!1,"React DevTools encountered an error: %s",R))}}}catch(T){Ke(!1,"React DevTools encountered an error: %s.",T)}return!0}function rn(a,c){typeof ye=="function"&&ye(a,c)}function Rn(a){typeof be=="function"&&be(a)}var $n;{$n=!1;try{var Nr=Object.preventExtensions({}),ir=new Map([[Nr,null]]),Zr=new Set([Nr]);ir.set(0,0),Zr.add(0)}catch(a){$n=!0}}var ui=1;function bl(a,c,_,T){this.tag=a,this.key=_,this.elementType=null,this.type=null,this.stateNode=null,this.return=null,this.child=null,this.sibling=null,this.index=0,this.ref=null,this.pendingProps=c,this.memoizedProps=null,this.updateQueue=null,this.memoizedState=null,this.dependencies=null,this.mode=T,this.effectTag=_i,this.nextEffect=null,this.firstEffect=null,this.lastEffect=null,this.expirationTime=lt,this.childExpirationTime=lt,this.alternate=null,Zt&&(this.actualDuration=Number.NaN,this.actualStartTime=Number.NaN,this.selfBaseDuration=Number.NaN,this.treeBaseDuration=Number.NaN,this.actualDuration=0,this.actualStartTime=-1,this.selfBaseDuration=0,this.treeBaseDuration=0),Pr&&(this._debugID=ui++,this._debugIsCurrentlyTiming=!1),this._debugSource=null,this._debugOwner=null,this._debugNeedsRemount=!1,this._debugHookTypes=null,!$n&&typeof Object.preventExtensions=="function"&&Object.preventExtensions(this)}var Wi=function(a,c,_,T){return new bl(a,c,_,T)};function uo(a){var c=a.prototype;return!!(c&&c.isReactComponent)}function i0(a){return typeof a=="function"&&!uo(a)&&a.defaultProps===void 0}function Ts(a){if(typeof a=="function")return uo(a)?N:L;if(a!=null){var c=a.$$typeof;if(c===An)return ge;if(c===Wt)return Oe}return C}function wo(a,c,_){var T=a.alternate;T===null?(T=Wi(a.tag,c,a.key,a.mode),T.elementType=a.elementType,T.type=a.type,T.stateNode=a.stateNode,T._debugID=a._debugID,T._debugSource=a._debugSource,T._debugOwner=a._debugOwner,T._debugHookTypes=a._debugHookTypes,T.alternate=a,a.alternate=T):(T.pendingProps=c,T.effectTag=_i,T.nextEffect=null,T.firstEffect=null,T.lastEffect=null,Zt&&(T.actualDuration=0,T.actualStartTime=-1)),T.childExpirationTime=a.childExpirationTime,T.expirationTime=a.expirationTime,T.child=a.child,T.memoizedProps=a.memoizedProps,T.memoizedState=a.memoizedState,T.updateQueue=a.updateQueue;var R=a.dependencies;switch(T.dependencies=R===null?null:{expirationTime:R.expirationTime,firstContext:R.firstContext,responders:R.responders},T.sibling=a.sibling,T.index=a.index,T.ref=a.ref,Zt&&(T.selfBaseDuration=a.selfBaseDuration,T.treeBaseDuration=a.treeBaseDuration),T._debugNeedsRemount=a._debugNeedsRemount,T.tag){case C:case L:case le:T.type=Zu(a.type);break;case N:T.type=U0(a.type);break;case ge:T.type=vf(a.type);break;default:break}return T}function Rv(a,c){a.effectTag&=ai,a.nextEffect=null,a.firstEffect=null,a.lastEffect=null;var _=a.alternate;if(_===null)a.childExpirationTime=lt,a.expirationTime=c,a.child=null,a.memoizedProps=null,a.memoizedState=null,a.updateQueue=null,a.dependencies=null,Zt&&(a.selfBaseDuration=0,a.t Line truncated
Check the render method of \``+Ye+"`.")}throw Error("Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: "+(a==null?a:typeof a)+"."+Ie)}}}return V=Wi(te,_,c,R),V.elementType=a,V.type=oe,V.expirationTime=j,V}function Ty(a,c,_){var T=null;T=a._owner;var R=a.type,j=a.key,V=a.props,te=Sy(R,j,V,T,c,_);return te._debugSource=a._source,te._debugOwner=a._owner,te}function Qa(a,c,_,T){var R=Wi(m,a,T,c);return R.expirationTime=_,R}function r_(a,c,_,T,R){var j=Wi(xt,c,R,_);return j.elementType=a,j.type=a,j.expirationTime=T,j}function Q4(a,c,_,T,R){var j=Wi($e,c,R,_);return j.type=a,j.elementType=a,j.expirationTime=T,j}function J4(a,c,_,T){(typeof a.id!="string"||typeof a.onRender!="function")&&Ke(!1,'Profiler must specify an "id" string and "onRender" function as props');var R=Wi(ze,a,T,c|Jr);return R.elementType=ct,R.type=ct,R.expirationTime=_,R}function Z4(a,c,_,T){var R=Wi(pe,a,T,c);return R.type=nr,R.elementType=nr,R.expirationTime=_,R}function $4(a,c,_,T){var R=Wi(wt,a,T,c);return R.type=un,R.elementType=un,R.expirationTime=_,R}function Cy(a,c,_){var T=Wi(ne,a,null,c);return T.expirationTime=_,T}function eE(){var a=Wi(W,null,null,Sr);return a.elementType="DELETED",a.type="DELETED",a}function tE(a){var c=Wi(rt,null,null,Sr);return c.stateNode=a,c}function xy(a,c,_){var T=a.children!==null?a.children:[],R=Wi(q,T,a.key,c);return R.expirationTime=_,R.stateNode={containerInfo:a.containerInfo,pendingChildren:null,implementation:a.implementation},R}function i_(a,c){return a===null&&(a=Wi(C,null,null,Sr)),a.tag=c.tag,a.key=c.key,a.elementType=c.elementType,a.type=c.type,a.stateNode=c.stateNode,a.return=c.return,a.child=c.child,a.sibling=c.sibling,a.index=c.index,a.ref=c.ref,a.pendingProps=c.pendingProps,a.memoizedProps=c.memoizedProps,a.updateQueue=c.updateQueue,a.memoizedState=c.memoizedState,a.dependencies=c.dependencies,a.mode=c.mode,a.effectTag=c.effectTag,a.nextEffect=c.nextEffect,a.firstEffect=c.firstEffect,a.lastEffect=c.lastEffect,a.expirationTime=c.expirationTime,a.childExpirationTime=c.childExpirationTime,a.alternate=c.alternate,Zt&&(a.actualDuration=c.actualDuration,a.actualStartTime=c.actualStartTime,a.selfBaseDuration=c.selfBaseDuration,a.treeBaseDuration=c.treeBaseDuration),a._debugID=c._debugID,a._debugSource=c._debugSource,a._debugOwner=c._debugOwner,a._debugIsCurrentlyTiming=c._debugIsCurrentlyTiming,a._debugNeedsRemount=c._debugNeedsRemount,a._debugHookTypes=c._debugHookTypes,a}function nE(a,c,_){this.tag=c,this.current=null,this.containerInfo=a,this.pendingChildren=null,this.pingCache=null,this.finishedExpirationTime=lt,this.finishedWork=null,this.timeoutHandle=Jo,this.context=null,this.pendingContext=null,this.hydrate=_,this.callbackNode=null,this.callbackPriority=y0,this.firstPendingTime=lt,this.firstSuspendedTime=lt,this.lastSuspendedTime=lt,this.nextKnownPendingLevel=lt,this.lastPingedTime=lt,this.lastExpiredTime=lt,Ln&&(this.interactionThreadID=k.unstable_getThreadID(),this.memoizedInteractions=new Set,this.pendingInteractionMap=new Map),Ui&&(this.hydrationCallbacks=null)}function rE(a,c,_,T){var R=new nE(a,c,_);Ui&&(R.hydrationCallbacks=T);var j=X4(c);return R.current=j,j.stateNode=R,R}function u_(a,c){var _=a.firstSuspendedTime,T=a.lastSuspendedTime;return _!==lt&&_>=c&&T<=c}function Bf(a,c){var _=a.firstSuspendedTime,T=a.lastSuspendedTime;_<c&&(a.firstSuspendedTime=c),(T>c||_===lt)&&(a.lastSuspendedTime=c),c<=a.lastPingedTime&&(a.lastPingedTime=lt),c<=a.lastExpiredTime&&(a.lastExpiredTime=lt)}function o_(a,c){var _=a.firstPendingTime;c>_&&(a.firstPendingTime=c);var T=a.firstSuspendedTime;T!==lt&&(c>=T?a.firstSuspendedTime=a.lastSuspendedTime=a.nextKnownPendingLevel=lt:c>=a.lastSuspendedTime&&(a.lastSuspendedTime=c+1),c>a.nextKnownPendingLevel&&(a.nextKnownPendingLevel=c))}function iE(a,c,_){a.firstPendingTime=_,c<=a.lastSuspendedTime?a.firstSuspendedTime=a.lastSuspendedTime=a.nextKnownPendingLevel=lt:c<=a.firstSuspendedTime&&(a.firstSuspendedTime=c-1),c<=a.lastPingedTime&&(a.lastPingedTime=lt),c<=a.lastExpiredTime&&(a.lastExpiredTime=lt)}function qp(a,c){var _=a.lastExpiredTime;(_===lt||_>c)&&(a.lastExpiredTime=c)}var uE={debugTool:null},Ov=uE,Ay,Ry;Ay=!1,Ry={};function oE(a){if(!a)return Sn;var c=jt(a),_=El(c);if(c.tag===N){var T=c.type;if(zi(T))return A0(c,T,_)}return _}function Oy(a){var c=jt(a);if(c===void 0)throw typeof a.render=="function"?Error("Unable to find node on an unmounted component."):Error("Argument appears to not be a ReactComponent. Keys: "+Object.keys(a));var _=b0(c);return _===null?null:_.stateNode}function lE(a,c){{var _=jt(a);if(_===void 0)throw typeof a.render=="function"?Error("Unable to find node on an unmounted component."):Error("Argument appears to not be a ReactComponent. Keys: "+Object.keys(a));var T=b0(_);if(T===null)return null;if(T.mode&cr){var R=qt(_.type)||"Component";Ry[R]||(Ry[R]=!0,_.mode&cr?Ke(!1,"%s is deprecated in StrictMode. %s was passed an instance of %s which is inside StrictLine truncated
Check the render method of %s.`,qt(Cn.type)||"Unknown"));var Ie=gu(te,V);return Ie.payload={element:a},T=T===void 0?null:T,T!==null&&(typeof T!="function"&&Ke(!1,"render(...): Expected the last optional `callback` argument to be a function. Instead received: %s.",T),Ie.callback=T),Ua(R,Ie),dl(R,te),te}function aE(a){var c=a.current;if(!c.child)return null;switch(c.child.tag){case W:return Ro(c.child.stateNode);default:return c.child.stateNode}}function fE(a){switch(a.tag){case U:var c=a.stateNode;c.hydrate&&Zm(c,c.firstPendingTime);break;case pe:xp(function(){return dl(a,bn)});var _=La(Fl());kv(a,_);break}}function s_(a,c){var _=a.memoizedState;_!==null&&_.dehydrated!==null&&_.retryTime<c&&(_.retryTime=c)}function kv(a,c){s_(a,c);var _=a.alternate;_&&s_(_,c)}function cE(a){if(a.tag===pe){var c=La(Fl());dl(a,c),kv(a,c)}}function ky(a){if(a.tag===pe){var c=gd(Fl());dl(a,c),kv(a,c)}}function dE(a){if(a.tag===pe){var c=Fl(),_=Ff(c,a,null);dl(a,_),kv(a,_)}}function pE(a){var c=yl(a);return c===null?null:c.tag===xt?c.stateNode.instance:c.stateNode}var My=function(a){return!1};function a_(a){return My(a)}var f_=null,c_=null,d_=null,p_=null;{var Ny=function(a,c,_,T){if(_>=c.length)return T;var R=c[_],j=Array.isArray(a)?a.slice():f({},a);return j[R]=Ny(a[R],c,_+1,T),j},h_=function(a,c,_){return Ny(a,c,0,_)};f_=function(a,c,_,T){for(var R=a.memoizedState;R!==null&&c>0;)R=R.next,c--;if(R!==null){var j=h_(R.memoizedState,_,T);R.memoizedState=j,R.baseState=j,a.memoizedProps=f({},a.memoizedProps),dl(a,bn)}},c_=function(a,c,_){a.pendingProps=h_(a.memoizedProps,c,_),a.alternate&&(a.alternate.pendingProps=a.pendingProps),dl(a,bn)},d_=function(a){dl(a,bn)},p_=function(a){My=a}}function hE(a){var c=a.findFiberByHostInstance,_=at.ReactCurrentDispatcher;return Rt(f({},a,{overrideHookState:f_,overrideProps:c_,setSuspenseHandler:p_,scheduleUpdate:d_,currentDispatcherRef:_,findHostInstanceByFiber:function(T){var R=b0(T);return R===null?null:R.stateNode},findFiberByHostInstance:function(T){return c?c(T):null},findHostInstancesForRefresh:n2,scheduleRefresh:Sl,scheduleRoot:_s,setRefreshHandler:Ia,getCurrentFiber:function(){return Cn}}))}var v_=Object.freeze({createContainer:sE,updateContainer:l_,batchedEventUpdates:ny,batchedUpdates:ty,unbatchedUpdates:ry,deferredUpdates:$m,syncUpdates:fv,discreteUpdates:cv,flushDiscreteUpdates:av,flushControlled:iy,flushSync:xp,flushPassiveEffects:Xa,IsThisRendererActing:If,getPublicRootInstance:aE,attemptSynchronousHydration:fE,attemptUserBlockingHydration:cE,attemptContinuousHydration:ky,attemptHydrationAtCurrentPriority:dE,findHostInstance:Oy,findHostInstanceWithWarning:lE,findHostInstanceWithNoPortals:pE,shouldSuspend:a_,injectIntoDevTools:hE}),vE=v_.default||v_;hg.exports=vE;var mE=hg.exports;return hg.exports=i,mE})});var D9=ce((zne,cw)=>{"use strict";process.env.NODE_ENV==="production"?cw.exports=m9():cw.exports=E9()});var S9=ce((qne,w9)=>{"use strict";var ZK={ALIGN_COUNT:8,ALIGN_AUTO:0,ALIGN_FLEX_START:1,ALIGN_CENTER:2,ALIGN_FLEX_END:3,ALIGN_STRETCH:4,ALIGN_BASELINE:5,ALIGN_SPACE_BETWEEN:6,ALIGN_SPACE_AROUND:7,DIMENSION_COUNT:2,DIMENSION_WIDTH:0,DIMENSION_HEIGHT:1,DIRECTION_COUNT:3,DIRECTION_INHERIT:0,DIRECTION_LTR:1,DIRECTION_RTL:2,DISPLAY_COUNT:2,DISPLAY_FLEX:0,DISPLAY_NONE:1,EDGE_COUNT:9,EDGE_LEFT:0,EDGE_TOP:1,EDGE_RIGHT:2,EDGE_BOTTOM:3,EDGE_START:4,EDGE_END:5,EDGE_HORIZONTAL:6,EDGE_VERTICAL:7,EDGE_ALL:8,EXPERIMENTAL_FEATURE_COUNT:1,EXPERIMENTAL_FEATURE_WEB_FLEX_BASIS:0,FLEX_DIRECTION_COUNT:4,FLEX_DIRECTION_COLUMN:0,FLEX_DIRECTION_COLUMN_REVERSE:1,FLEX_DIRECTION_ROW:2,FLEX_DIRECTION_ROW_REVERSE:3,JUSTIFY_COUNT:6,JUSTIFY_FLEX_START:0,JUSTIFY_CENTER:1,JUSTIFY_FLEX_END:2,JUSTIFY_SPACE_BETWEEN:3,JUSTIFY_SPACE_AROUND:4,JUSTIFY_SPACE_EVENLY:5,LOG_LEVEL_COUNT:6,LOG_LEVEL_ERROR:0,LOG_LEVEL_WARN:1,LOG_LEVEL_INFO:2,LOG_LEVEL_DEBUG:3,LOG_LEVEL_VERBOSE:4,LOG_LEVEL_FATAL:5,MEASURE_MODE_COUNT:3,MEASURE_MODE_UNDEFINED:0,MEASURE_MODE_EXACTLY:1,MEASURE_MODE_AT_MOST:2,NODE_TYPE_COUNT:2,NODE_TYPE_DEFAULT:0,NODE_TYPE_TEXT:1,OVERFLOW_COUNT:3,OVERFLOW_VISIBLE:0,OVERFLOW_HIDDEN:1,OVERFLOW_SCROLL:2,POSITION_TYPE_COUNT:2,POSITION_TYPE_RELATIVE:0,POSITION_TYPE_ABSOLUTE:1,PRINT_OPTIONS_COUNT:3,PRINT_OPTIONS_LAYOUT:1,PRINT_OPTIONS_STYLE:2,PRINT_OPTIONS_CHILDREN:4,UNIT_COUNT:4,UNIT_UNDEFINED:0,UNIT_POINT:1,UNIT_PERCENT:2,UNIT_AUTO:3,WRAP_COUNT:3,WRAP_NO_WRAP:0,WRAP_WRAP:1,WRAP_WRAP_REVERSE:2};w9.exports=ZK});var A9=ce((Hne,T9)=>{"use strict";var $K=Object.assign||function(i){for(var o=1;o<arguments.length;o++){var f=arguments[o];for(var p in f)Object.prototype.hasOwnProperty.call(f,p)&&(i[p]=f[p])}return i},v4=function(){function i(o,f){for(var p=0;p<f.length;p++){var E=f[p];E.enumerable=E.enumerable||!1,E.configurable=!0,"value"in E&&(E.writable=!0),Object.defineProperty(o,E.key,E)}}return function(o,f,p){return f&&i(o.prototype,f),p&&i(o,p),o}}();function dw(i,o,f){return o in i?Object.defineProperty(i,o,{value:f,enumerable:!0,configurable:!0,writable:!0}):i[o]=f,i}function pw(i,o){if(!(i instanceof o))throw new T Line truncated
`+Module.extraStackTrace()),demangleAll(i)}Module.stackTrace=stackTrace;var HEAP,buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferViews(){Module.HEAP8=HEAP8=new Int8Array(buffer),Module.HEAP16=HEAP16=new Int16Array(buffer),Module.HEAP32=HEAP32=new Int32Array(buffer),Module.HEAPU8=HEAPU8=new Uint8Array(buffer),Module.HEAPU16=HEAPU16=new Uint16Array(buffer),Module.HEAPU32=HEAPU32=new Uint32Array(buffer),Module.HEAPF32=HEAPF32=new Float32Array(buffer),Module.HEAPF64=HEAPF64=new Float64Array(buffer)}var STATIC_BASE,STATICTOP,staticSealed,STACK_BASE,STACKTOP,STACK_MAX,DYNAMIC_BASE,DYNAMICTOP_PTR;STATIC_BASE=STATICTOP=STACK_BASE=STACKTOP=STACK_MAX=DYNAMIC_BASE=DYNAMICTOP_PTR=0,staticSealed=!1;function abortOnCannotGrowMemory(){abort("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+TOTAL_MEMORY+", (2) compile with -s ALLOW_MEMORY_GROWTH=1 which allows increasing the size at runtime but prevents some optimizations, (3) set Module.TOTAL_MEMORY to a higher value before the program runs, or (4) if you want malloc to return NULL (0) instead of this abort, compile with -s ABORTING_MALLOC=0 ")}function enlargeMemory(){abortOnCannotGrowMemory()}var TOTAL_STACK=Module.TOTAL_STACK||5242880,TOTAL_MEMORY=Module.TOTAL_MEMORY||134217728;TOTAL_MEMORY<TOTAL_STACK&&Module.printErr("TOTAL_MEMORY should be larger than TOTAL_STACK, was "+TOTAL_MEMORY+"! (TOTAL_STACK="+TOTAL_STACK+")"),Module.buffer?buffer=Module.buffer:buffer=new ArrayBuffer(TOTAL_MEMORY),updateGlobalBufferViews();function getTotalMemory(){return TOTAL_MEMORY}if(HEAP32[0]=1668509029,HEAP16[1]=25459,HEAPU8[2]!==115||HEAPU8[3]!==99)throw"Runtime error: expected the system to be little-endian!";Module.HEAP=HEAP,Module.buffer=buffer,Module.HEAP8=HEAP8,Module.HEAP16=HEAP16,Module.HEAP32=HEAP32,Module.HEAPU8=HEAPU8,Module.HEAPU16=HEAPU16,Module.HEAPU32=HEAPU32,Module.HEAPF32=HEAPF32,Module.HEAPF64=HEAPF64;function callRuntimeCallbacks(i){for(;i.length>0;){var o=i.shift();if(typeof o=="function"){o();continue}var f=o.func;typeof f=="number"?o.arg===void 0?Module.dynCall_v(f):Module.dynCall_vi(f,o.arg):f(o.arg===void 0?null:o.arg)}}var __ATPRERUN__=[],__ATINIT__=[],__ATMAIN__=[],__ATEXIT__=[],__ATPOSTRUN__=[],runtimeInitialized=!1,runtimeExited=!1;function preRun(){if(Module.preRun)for(typeof Module.preRun=="function"&&(Module.preRun=[Module.preRun]);Module.preRun.length;)addOnPreRun(Module.preRun.shift());callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){runtimeInitialized||(runtimeInitialized=!0,callRuntimeCallbacks(__ATINIT__))}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){callRuntimeCallbacks(__ATEXIT__),runtimeExited=!0}function postRun(){if(Module.postRun)for(typeof Module.postRun=="function"&&(Module.postRun=[Module.postRun]);Module.postRun.length;)addOnPostRun(Module.postRun.shift());callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(i){__ATPRERUN__.unshift(i)}Module.addOnPreRun=addOnPreRun;function addOnInit(i){__ATINIT__.unshift(i)}Module.addOnInit=addOnInit;function addOnPreMain(i){__ATMAIN__.unshift(i)}Module.addOnPreMain=addOnPreMain;function addOnExit(i){__ATEXIT__.unshift(i)}Module.addOnExit=addOnExit;function addOnPostRun(i){__ATPOSTRUN__.unshift(i)}Module.addOnPostRun=addOnPostRun;function intArrayFromString(i,o,f){var p=f>0?f:lengthBytesUTF8(i)+1,E=new Array(p),t=stringToUTF8Array(i,E,0,E.length);return o&&(E.length=t),E}Module.intArrayFromString=intArrayFromString;function intArrayToString(i){for(var o=[],f=0;f<i.length;f++){var p=i[f];p>255&&(p&=255),o.push(String.fromCharCode(p))}return o.join("")}Module.intArrayToString=intArrayToString;function writeStringToMemory(i,o,f){Runtime.warnOnce("writeStringToMemory is deprecated and should not be called! Use stringToUTF8() instead!");var p,E;f&&(E=o+lengthBytesUTF8(i),p=HEAP8[E]),stringToUTF8(i,o,Infinity),f&&(HEAP8[E]=p)}Module.writeStringToMemory=writeStringToMemory;function writeArrayToMemory(i,o){HEAP8.set(i,o)}Module.writeArrayToMemory=writeArrayToMemory;function writeAsciiToMemory(i,o,f){for(var p=0;p<i.length;++p)HEAP8[o++>>0]=i.charCodeAt(p);f||(HEAP8[o>>0]=0)}if(Module.writeAsciiToMemory=writeAsciiToMemory,(!Math.imul||Math.imul(4294967295,5)!==-5)&&(Math.imul=function(o,f){var p=o>>>16,E=o&65535,t=f>>>16,k=f&65535;return E*k+(p*k+E*t<<16)|0}),Math.imul=Math.imul,!Math.fround){var froundBuffer=new Float32Array(1);Math.fround=function(i){return froundBuffer[0]=i,froundBuffer[0]}}Math.fround=Math.fround,Math.clz32||(Math.clz32=function(i){i=i>>>0;for(var o=0;o<32;o++)if(i&1<<31-o)return o;return 32}),Math.clz32=Math.clz32,Math.trunc||(Math.trunc=function(i){return i<0?Math.ceil(i):Math.floor(i)}),Math.trunc=Math.trunc;var Math_abs=Math.abs,Math_cos=Math.cos,Math_sin=Math.sin,Math_tan=Math.tan,Math_acos=Math.acos,Math_asin=Math.asin,Math_atan=Math.atan,Math_atan2=Math.atan2,Math_exp=Math.exp,Math_log=Math.log,Math_sqrt=Math.sqrt,Math_ceil=Math. Line truncated
If this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.`,f="abort("+i+") at "+stackTrace()+o;throw abortDecorators&&abortDecorators.forEach(function(p){f=p(f,i)}),f}if(Module.abort=Module.abort=abort,Module.preInit)for(typeof Module.preInit=="function"&&(Module.preInit=[Module.preInit]);Module.preInit.length>0;)Module.preInit.pop()();var shouldRunNow=!0;Module.noInitialRun&&(shouldRunNow=!1),run()})});var eh=ce((Wne,O9)=>{"use strict";var tX=A9(),nX=R9(),hw=!1,vw=null;nX({},function(i,o){if(!hw){if(hw=!0,i)throw i;vw=o}});if(!hw)throw new Error("Failed to load the yoga module - it needed to be loaded synchronously, but didn't");O9.exports=tX(vw.bind,vw.lib)});var M9=ce((Vne,k9)=>{"use strict";k9.exports=({onlyFirst:i=!1}={})=>{let o=["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)","(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"].join("|");return new RegExp(o,i?void 0:"g")}});var mw=ce((Gne,N9)=>{"use strict";var rX=M9();N9.exports=i=>typeof i=="string"?i.replace(rX(),""):i});var gw=ce((Yne,yw)=>{"use strict";var L9=i=>Number.isNaN(i)?!1:i>=4352&&(i<=4447||i===9001||i===9002||11904<=i&&i<=12871&&i!==12351||12880<=i&&i<=19903||19968<=i&&i<=42182||43360<=i&&i<=43388||44032<=i&&i<=55203||63744<=i&&i<=64255||65040<=i&&i<=65049||65072<=i&&i<=65131||65281<=i&&i<=65376||65504<=i&&i<=65510||110592<=i&&i<=110593||127488<=i&&i<=127569||131072<=i&&i<=262141);yw.exports=L9;yw.exports.default=L9});var b9=ce((Kne,F9)=>{"use strict";F9.exports=function(){return/\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F|\uD83D\uDC68(?:\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68\uD83C\uDFFB|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|[\u2695\u2696\u2708]\uFE0F|\uD83D[\uDC66\uDC67]|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708])\uFE0F|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C[\uDFFB-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)\uD83C\uDFFB|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB\uDFFC])|\uD83D\uDC69(?:\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C Line truncated
`))o=Math.max(o,lX(f));return o};Ew.exports=I9;Ew.exports.default=I9});var B9=ce(vg=>{"use strict";var sX=vg&&vg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(vg,"__esModule",{value:!0});var aX=sX(Dw()),ww={};vg.default=i=>{if(i.length===0)return{width:0,height:0};if(ww[i])return ww[i];let o=aX.default(i),f=i.split(`
`).length;return ww[i]={width:o,height:f},{width:o,height:f}}});var U9=ce(mg=>{"use strict";var fX=mg&&mg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(mg,"__esModule",{value:!0});var Ii=fX(eh()),cX=(i,o)=>{"position"in o&&i.setPositionType(o.position==="absolute"?Ii.default.POSITION_TYPE_ABSOLUTE:Ii.default.POSITION_TYPE_RELATIVE)},dX=(i,o)=>{"marginLeft"in o&&i.setMargin(Ii.default.EDGE_START,o.marginLeft||0),"marginRight"in o&&i.setMargin(Ii.default.EDGE_END,o.marginRight||0),"marginTop"in o&&i.setMargin(Ii.default.EDGE_TOP,o.marginTop||0),"marginBottom"in o&&i.setMargin(Ii.default.EDGE_BOTTOM,o.marginBottom||0)},pX=(i,o)=>{"paddingLeft"in o&&i.setPadding(Ii.default.EDGE_LEFT,o.paddingLeft||0),"paddingRight"in o&&i.setPadding(Ii.default.EDGE_RIGHT,o.paddingRight||0),"paddingTop"in o&&i.setPadding(Ii.default.EDGE_TOP,o.paddingTop||0),"paddingBottom"in o&&i.setPadding(Ii.default.EDGE_BOTTOM,o.paddingBottom||0)},hX=(i,o)=>{var f;"flexGrow"in o&&i.setFlexGrow((f=o.flexGrow)!==null&&f!==void 0?f:0),"flexShrink"in o&&i.setFlexShrink(typeof o.flexShrink=="number"?o.flexShrink:1),"flexDirection"in o&&(o.flexDirection==="row"&&i.setFlexDirection(Ii.default.FLEX_DIRECTION_ROW),o.flexDirection==="row-reverse"&&i.setFlexDirection(Ii.default.FLEX_DIRECTION_ROW_REVERSE),o.flexDirection==="column"&&i.setFlexDirection(Ii.default.FLEX_DIRECTION_COLUMN),o.flexDirection==="column-reverse"&&i.setFlexDirection(Ii.default.FLEX_DIRECTION_COLUMN_REVERSE)),"flexBasis"in o&&(typeof o.flexBasis=="number"?i.setFlexBasis(o.flexBasis):typeof o.flexBasis=="string"?i.setFlexBasisPercent(Number.parseInt(o.flexBasis,10)):i.setFlexBasis(NaN)),"alignItems"in o&&((o.alignItems==="stretch"||!o.alignItems)&&i.setAlignItems(Ii.default.ALIGN_STRETCH),o.alignItems==="flex-start"&&i.setAlignItems(Ii.default.ALIGN_FLEX_START),o.alignItems==="center"&&i.setAlignItems(Ii.default.ALIGN_CENTER),o.alignItems==="flex-end"&&i.setAlignItems(Ii.default.ALIGN_FLEX_END)),"alignSelf"in o&&((o.alignSelf==="auto"||!o.alignSelf)&&i.setAlignSelf(Ii.default.ALIGN_AUTO),o.alignSelf==="flex-start"&&i.setAlignSelf(Ii.default.ALIGN_FLEX_START),o.alignSelf==="center"&&i.setAlignSelf(Ii.default.ALIGN_CENTER),o.alignSelf==="flex-end"&&i.setAlignSelf(Ii.default.ALIGN_FLEX_END)),"justifyContent"in o&&((o.justifyContent==="flex-start"||!o.justifyContent)&&i.setJustifyContent(Ii.default.JUSTIFY_FLEX_START),o.justifyContent==="center"&&i.setJustifyContent(Ii.default.JUSTIFY_CENTER),o.justifyContent==="flex-end"&&i.setJustifyContent(Ii.default.JUSTIFY_FLEX_END),o.justifyContent==="space-between"&&i.setJustifyContent(Ii.default.JUSTIFY_SPACE_BETWEEN),o.justifyContent==="space-around"&&i.setJustifyContent(Ii.default.JUSTIFY_SPACE_AROUND))},vX=(i,o)=>{var f,p;"width"in o&&(typeof o.width=="number"?i.setWidth(o.width):typeof o.width=="string"?i.setWidthPercent(Number.parseInt(o.width,10)):i.setWidthAuto()),"height"in o&&(typeof o.height=="number"?i.setHeight(o.height):typeof o.height=="string"?i.setHeightPercent(Number.parseInt(o.height,10)):i.setHeightAuto()),"minWidth"in o&&(typeof o.minWidth=="string"?i.setMinWidthPercent(Number.parseInt(o.minWidth,10)):i.setMinWidth((f=o.minWidth)!==null&&f!==void 0?f:0)),"minHeight"in o&&(typeof o.minHeight=="string"?i.setMinHeightPercent(Number.parseInt(o.minHeight,10)):i.setMinHeight((p=o.minHeight)!==null&&p!==void 0?p:0))},mX=(i,o)=>{"display"in o&&i.setDisplay(o.display==="flex"?Ii.default.DISPLAY_FLEX:Ii.default.DISPLAY_NONE)},yX=(i,o)=>{if("borderStyle"in o){let f=typeof o.borderStyle=="string"?1:0;i.setBorder(Ii.default.EDGE_TOP,f),i.setBorder(Ii.default.EDGE_BOTTOM,f),i.setBorder(Ii.default.EDGE_LEFT,f),i.setBorder(Ii.default.EDGE_RIGHT,f)}};mg.default=(i,o={})=>{cX(i,o),dX(i,o),pX(i,o),hX(i,o),vX(i,o),mX(i,o),yX(i,o)}});var z9=ce(($ne,j9)=>{"use strict";j9.exports={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite: Line truncated
`);for(let[N,C]of[...p].entries()){if(E+=C,xw.has(C)){let q=parseFloat(/\d[^m]*/.exec(p.slice(N,N+4)));t=q===kX?null:q}let U=OX.codes.get(Number(t));t&&U&&(p[N+1]===`
`?E+=eA(U):C===`
`&&(E+=eA(t)))}return E};$9.exports=(i,o,f)=>String(i).normalize().replace(/\r\n/g,`
`).split(`
`).map(p=>LX(p,o,f)).join(`
`)});var iA=ce((ure,nA)=>{"use strict";var rA="[\uD800-\uDBFF][\uDC00-\uDFFF]",FX=i=>i&&i.exact?new RegExp(`^${rA}$`):new RegExp(rA,"g");nA.exports=FX});var Rw=ce((ore,uA)=>{"use strict";var bX=gw(),PX=iA(),oA=_4(),lA=["","\x9B"],E4=i=>`${lA[0]}[${i}m`,sA=(i,o,f)=>{let p=[];i=[...i];for(let E of i){let t=E;E.match(";")&&(E=E.split(";")[0][0]+"0");let k=oA.codes.get(parseInt(E,10));if(k){let L=i.indexOf(k.toString());L>=0?i.splice(L,1):p.push(E4(o?k:t))}else if(o){p.push(E4(0));break}else p.push(E4(t))}if(o&&(p=p.filter((E,t)=>p.indexOf(E)===t),f!==void 0)){let E=E4(oA.codes.get(parseInt(f,10)));p=p.reduce((t,k)=>k===E?[k,...t]:[...t,k],[])}return p.join("")};uA.exports=(i,o,f)=>{let p=[...i.normalize()],E=[];f=typeof f=="number"?f:p.length;let t=!1,k,L=0,N="";for(let[C,U]of p.entries()){let q=!1;if(lA.includes(U)){let W=/\d[^m]*/.exec(i.slice(C,C+18));k=W&&W.length>0?W[0]:void 0,L<f&&(t=!0,k!==void 0&&E.push(k))}else t&&U==="m"&&(t=!1,q=!0);if(!t&&!q&&++L,!PX({exact:!0}).test(U)&&bX(U.codePointAt())&&++L,L>o&&L<=f)N+=U;else if(L===o&&!t&&k!==void 0)N=sA(E);else if(L>=f){N+=sA(E,!0,k);break}}return N}});var fA=ce((lre,aA)=>{"use strict";var pd=Rw(),IX=m4();function D4(i,o,f){if(i.charAt(o)===" ")return o;for(let p=1;p<=3;p++)if(f){if(i.charAt(o+p)===" ")return o+p}else if(i.charAt(o-p)===" ")return o-p;return o}aA.exports=(i,o,f)=>{f=E0({position:"end",preferTruncationOnSpace:!1},f);let{position:p,space:E,preferTruncationOnSpace:t}=f,k="\u2026",L=1;if(typeof i!="string")throw new TypeError(`Expected \`input\` to be a string, got ${typeof i}`);if(typeof o!="number")throw new TypeError(`Expected \`columns\` to be a number, got ${typeof o}`);if(o<1)return"";if(o===1)return k;let N=IX(i);if(N<=o)return i;if(p==="start"){if(t){let C=D4(i,N-o+1,!0);return k+pd(i,C,N).trim()}return E===!0&&(k+=" ",L=2),k+pd(i,N-o+L,N)}if(p==="middle"){E===!0&&(k=" "+k+" ",L=3);let C=Math.floor(o/2);if(t){let U=D4(i,C),q=D4(i,N-(o-C)+1,!0);return pd(i,0,U)+k+pd(i,q,N).trim()}return pd(i,0,C)+k+pd(i,N-(o-C)+L,N)}if(p==="end"){if(t){let C=D4(i,o-1);return pd(i,0,C)+k}return E===!0&&(k=" "+k,L=2),pd(i,0,o-L)+k}throw new Error(`Expected \`options.position\` to be either \`start\`, \`middle\` or \`end\`, got ${p}`)}});var kw=ce(_g=>{"use strict";var cA=_g&&_g.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(_g,"__esModule",{value:!0});var BX=cA(tA()),UX=cA(fA()),Ow={};_g.default=(i,o,f)=>{let p=i+String(o)+String(f);if(Ow[p])return Ow[p];let E=i;if(f==="wrap"&&(E=BX.default(i,o,{trim:!1,hard:!0})),f.startsWith("truncate")){let t="end";f==="truncate-middle"&&(t="middle"),f==="truncate-start"&&(t="start"),E=UX.default(i,o,{position:t})}return Ow[p]=E,E}});var Nw=ce(Mw=>{"use strict";Object.defineProperty(Mw,"__esModule",{value:!0});var dA=i=>{let o="";if(i.childNodes.length>0)for(let f of i.childNodes){let p="";f.nodeName==="#text"?p=f.nodeValue:((f.nodeName==="ink-text"||f.nodeName==="ink-virtual-text")&&(p=dA(f)),p.length>0&&typeof f.internal_transform=="function"&&(p=f.internal_transform(p))),o+=p}return o};Mw.default=dA});var Lw=ce(l0=>{"use strict";var Eg=l0&&l0.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(l0,"__esModule",{value:!0});l0.setTextNodeValue=l0.createTextNode=l0.setStyle=l0.setAttribute=l0.removeChildNode=l0.insertBeforeNode=l0.appendChildNode=l0.createNode=l0.TEXT_NAME=void 0;var jX=Eg(eh()),pA=Eg(B9()),zX=Eg(U9()),qX=Eg(kw()),HX=Eg(Nw());l0.TEXT_NAME="#text";l0.createNode=i=>{var o;let f={nodeName:i,style:{},attributes:{},childNodes:[],parentNode:null,yogaNode:i==="ink-virtual-text"?void 0:jX.default.Node.create()};return i==="ink-text"&&((o=f.yogaNode)===null||o===void 0||o.setMeasureFunc(WX.bind(null,f))),f};l0.appendChildNode=(i,o)=>{var f;o.parentNode&&l0.removeChildNode(o.parentNode,o),o.parentNode=i,i.childNodes.push(o),o.yogaNode&&((f=i.yogaNode)===null||f===void 0||f.insertChild(o.yogaNode,i.yogaNode.getChildCount())),(i.nodeName==="ink-text"||i.nodeName==="ink-virtual-text")&&w4(i)};l0.insertBeforeNode=(i,o,f)=>{var p,E;o.parentNode&&l0.removeChildNode(o.parentNode,o),o.parentNode=i;let t=i.childNodes.indexOf(f);if(t>=0){i.childNodes.splice(t,0,o),o.yogaNode&&((p=i.yogaNode)===null||p===void 0||p.insertChild(o.yogaNode,t));return}i.childNodes.push(o),o.yogaNode&&((E=i.yogaNode)===null||E===void 0||E.insertChild(o.yogaNode,i.yogaNode.getChildCount())),(i.nodeName==="ink-text"||i.nodeName==="ink-virtual-text")&&w4(i)};l0.removeChildNode=(i,o)=>{var f,p;o.yogaNode&&((p=(f=o.parentNode)===null||f===void 0?void 0:f.yogaNode)===null||p===void 0||p.removeChild(o.yogaNode)),o.parentNode=null;let E=i.childNodes.indexOf(o);E>=0&&i.childNodes.splice(E,1),(i.nodeName==="ink-text"||i.nodeName==="ink-virtual-text")&&w4(i)};l0.setAttribute=(i,o,f)=>{i.attributes[o]=f};l0.setStyle=(i,o)=>{i.style=o,i.yogaNode&&zX.default(i.yogaNode,o)};l0.createTextNode=i=>{let o={nodeName:"#text",nodeValue:i,yogaNode:void 0,parentNode:null,style:{}};return l0Line truncated
`).join(`\r
`)),E.removeListener("error",Qw),C.setSocket(E,t,this.options.maxPayload),this.clients&&(this.clients.add(C),C.on("close",()=>this.clients.delete(C))),k(C,p)}};sR.exports=aR;function KQ(i,o){for(let f of Object.keys(o))i.on(f,o[f]);return function(){for(let p of Object.keys(o))i.removeListener(p,o[p])}}function XQ(i){i.emit("close")}function Qw(){this.destroy()}function A4(i,o,f,p){i.writable&&(f=f||Xw[o],p=E0({Connection:"close","Content-Type":"text/html","Content-Length":Buffer.byteLength(f)},p),i.write(`HTTP/1.1 ${o} ${Xw[o]}\r
`+Object.keys(p).map(E=>`${E}: ${p[E]}`).join(`\r
`)+`\r
\r
`+f)),i.removeListener("error",Qw),i.destroy()}function QQ(i){return i.trim()}});var dR=ce((Sre,cR)=>{"use strict";var Rg=Kw();Rg.createWebSocketStream=lR();Rg.Server=fR();Rg.Receiver=zw();Rg.Sender=qw();cR.exports=Rg});var pR=ce(R4=>{"use strict";var JQ=R4&&R4.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(R4,"__esModule",{value:!0});var ZQ=JQ(dR()),Og=global;Og.WebSocket||(Og.WebSocket=ZQ.default);Og.window||(Og.window=global);Og.window.__REACT_DEVTOOLS_COMPONENT_FILTERS__=[{type:1,value:7,isEnabled:!0},{type:2,value:"InternalApp",isEnabled:!0,isValid:!0},{type:2,value:"InternalAppContext",isEnabled:!0,isValid:!0},{type:2,value:"InternalStdoutContext",isEnabled:!0,isValid:!0},{type:2,value:"InternalStderrContext",isEnabled:!0,isValid:!0},{type:2,value:"InternalStdinContext",isEnabled:!0,isValid:!0},{type:2,value:"InternalFocusContext",isEnabled:!0,isValid:!0}]});var hR=ce((O4,Jw)=>{(function(i,o){typeof O4=="object"&&typeof Jw=="object"?Jw.exports=o():typeof define=="function"&&define.amd?define([],o):typeof O4=="object"?O4.ReactDevToolsBackend=o():i.ReactDevToolsBackend=o()})(window,function(){return function(i){var o={};function f(p){if(o[p])return o[p].exports;var E=o[p]={i:p,l:!1,exports:{}};return i[p].call(E.exports,E,E.exports,f),E.l=!0,E.exports}return f.m=i,f.c=o,f.d=function(p,E,t){f.o(p,E)||Object.defineProperty(p,E,{enumerable:!0,get:t})},f.r=function(p){typeof Symbol!="undefined"&&Symbol.toStringTag&&Object.defineProperty(p,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(p,"__esModule",{value:!0})},f.t=function(p,E){if(1&E&&(p=f(p)),8&E||4&E&&typeof p=="object"&&p&&p.__esModule)return p;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:p}),2&E&&typeof p!="string")for(var k in p)f.d(t,k,function(L){return p[L]}.bind(null,k));return t},f.n=function(p){var E=p&&p.__esModule?function(){return p.default}:function(){return p};return f.d(E,"a",E),E},f.o=function(p,E){return Object.prototype.hasOwnProperty.call(p,E)},f.p="",f(f.s=20)}([function(i,o,f){"use strict";i.exports=f(12)},function(i,o,f){"use strict";var p=Object.getOwnPropertySymbols,E=Object.prototype.hasOwnProperty,t=Object.prototype.propertyIsEnumerable;function k(L){if(L==null)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(L)}i.exports=function(){try{if(!Object.assign)return!1;var L=new String("abc");if(L[5]="de",Object.getOwnPropertyNames(L)[0]==="5")return!1;for(var N={},C=0;C<10;C++)N["_"+String.fromCharCode(C)]=C;if(Object.getOwnPropertyNames(N).map(function(q){return N[q]}).join("")!=="0123456789")return!1;var U={};return"abcdefghijklmnopqrst".split("").forEach(function(q){U[q]=q}),Object.keys(Object.assign({},U)).join("")==="abcdefghijklmnopqrst"}catch(q){return!1}}()?Object.assign:function(L,N){for(var C,U,q=k(L),W=1;W<arguments.length;W++){for(var ne in C=Object(arguments[W]))E.call(C,ne)&&(q[ne]=C[ne]);if(p){U=p(C);for(var m=0;m<U.length;m++)t.call(C,U[m])&&(q[U[m]]=C[U[m]])}}return q}},function(i,o,f){(function(p){function E(Oe){return(E=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(le){return typeof le}:function(le){return le&&typeof Symbol=="function"&&le.constructor===Symbol&&le!==Symbol.prototype?"symbol":typeof le})(Oe)}var t=/^\s+|\s+$/g,k=/^[-+]0x[0-9a-f]+$/i,L=/^0b[01]+$/i,N=/^0o[0-7]+$/i,C=parseInt,U=(p===void 0?"undefined":E(p))=="object"&&p&&p.Object===Object&&p,q=(typeof self=="undefined"?"undefined":E(self))=="object"&&self&&self.Object===Object&&self,W=U||q||Function("return this")(),ne=Object.prototype.toString,m=Math.max,we=Math.min,Se=function(){return W.Date.now()};function he(Oe,le,Ue){var Ge,rt,wt,xt,$e,ft,Ke=0,jt=!1,$t=!1,at=!0;if(typeof Oe!="function")throw new TypeError("Expected a function");function Q(At){var en=Ge,ln=rt;return Ge=rt=void 0,Ke=At,xt=Oe.apply(ln,en)}function ae(At){return Ke=At,$e=setTimeout(ue,le),jt?Q(At):xt}function Ce(At){var en=At-ft;return ft===void 0||en>=le||en<0||$t&&At-Ke>=wt}function ue(){var At=Se();if(Ce(At))return je(At);$e=setTimeout(ue,function(en){var ln=le-(en-ft);return $t?we(ln,wt-(en-Ke)):ln}(At))}function je(At){return $e=void 0,at&&Ge?Q(At):(Ge=rt=void 0,xt)}function ct(){var At=Se(),en=Ce(At);if(Ge=arguments,rt=this,ft=At,en){if($e===void 0)return ae(ft);if($t)return $e=setTimeout(ue,le),Q(ft)}return $e===void 0&&($e=setTimeout(ue,le)),xt}return le=pe(le)||0,ge(Ue)&&(jt=!!Ue.leading,wt=($t="maxWait"in Ue)?m(pe(Ue.maxWait)||0,le):wt,at="trailing"in Ue?!!Ue.trailing:at),ct.cancel=function(){$e!==void 0&&clearTimeout($e),Ke=0,Ge=ft=rt=$e=void 0},ct.flush=function(){return $e===void 0?xt:je(Se())},ct}function ge(Oe){var le=E(Oe);return!!Oe&&(le=="object"||le=="function")}function ze(Oe){return E(Oe)=="symbol"||function(le){return!!le&&E(le)=="object"}(Oe)&&ne.call(Oe)=="[object Symbol]"}function pe(Oe){if(typeof Oe=="number")return Oe;if(ze(Oe))return NaN;if(ge(Oe)){var le=typeof Oe.valueOf=="function"?Oe.va Line truncated
/**
* IMPORTANT: When new theme variables are added below\u2013 also add them to SettingsContext updateThemeVariables()
*/
/* Light theme */
--light-color-attribute-name: #ef6632;
--light-color-attribute-name-not-editable: #23272f;
--light-color-attribute-name-inverted: rgba(255, 255, 255, 0.7);
--light-color-attribute-value: #1a1aa6;
--light-color-attribute-value-inverted: #ffffff;
--light-color-attribute-editable-value: #1a1aa6;
--light-color-background: #ffffff;
--light-color-background-hover: rgba(0, 136, 250, 0.1);
--light-color-background-inactive: #e5e5e5;
--light-color-background-invalid: #fff0f0;
--light-color-background-selected: #0088fa;
--light-color-button-background: #ffffff;
--light-color-button-background-focus: #ededed;
--light-color-button: #5f6673;
--light-color-button-disabled: #cfd1d5;
--light-color-button-active: #0088fa;
--light-color-button-focus: #23272f;
--light-color-button-hover: #23272f;
--light-color-border: #eeeeee;
--light-color-commit-did-not-render-fill: #cfd1d5;
--light-color-commit-did-not-render-fill-text: #000000;
--light-color-commit-did-not-render-pattern: #cfd1d5;
--light-color-commit-did-not-render-pattern-text: #333333;
--light-color-commit-gradient-0: #37afa9;
--light-color-commit-gradient-1: #63b19e;
--light-color-commit-gradient-2: #80b393;
--light-color-commit-gradient-3: #97b488;
--light-color-commit-gradient-4: #abb67d;
--light-color-commit-gradient-5: #beb771;
--light-color-commit-gradient-6: #cfb965;
--light-color-commit-gradient-7: #dfba57;
--light-color-commit-gradient-8: #efbb49;
--light-color-commit-gradient-9: #febc38;
--light-color-commit-gradient-text: #000000;
--light-color-component-name: #6a51b2;
--light-color-component-name-inverted: #ffffff;
--light-color-component-badge-background: rgba(0, 0, 0, 0.1);
--light-color-component-badge-background-inverted: rgba(255, 255, 255, 0.25);
--light-color-component-badge-count: #777d88;
--light-color-component-badge-count-inverted: rgba(255, 255, 255, 0.7);
--light-color-context-background: rgba(0,0,0,.9);
--light-color-context-background-hover: rgba(255, 255, 255, 0.1);
--light-color-context-background-selected: #178fb9;
--light-color-context-border: #3d424a;
--light-color-context-text: #ffffff;
--light-color-context-text-selected: #ffffff;
--light-color-dim: #777d88;
--light-color-dimmer: #cfd1d5;
--light-color-dimmest: #eff0f1;
--light-color-error-background: hsl(0, 100%, 97%);
--light-color-error-border: hsl(0, 100%, 92%);
--light-color-error-text: #ff0000;
--light-color-expand-collapse-toggle: #777d88;
--light-color-link: #0000ff;
--light-color-modal-background: rgba(255, 255, 255, 0.75);
--light-color-record-active: #fc3a4b;
--light-color-record-hover: #3578e5;
--light-color-record-inactive: #0088fa;
--light-color-scroll-thumb: #c2c2c2;
--light-color-scroll-track: #fafafa;
--light-color-search-match: yellow;
--light-color-search-match-current: #f7923b;
--light-color-selected-tree-highlight-active: rgba(0, 136, 250, 0.1);
--light-color-selected-tree-highlight-inactive: rgba(0, 0, 0, 0.05);
--light-color-shadow: rgba(0, 0, 0, 0.25);
--light-color-tab-selected-border: #0088fa;
--light-color-text: #000000;
--light-color-text-invalid: #ff0000;
--light-color-text-selected: #ffffff;
--light-color-toggle-background-invalid: #fc3a4b;
--light-color-toggle-background-on: #0088fa;
--light-color-toggle-background-off: #cfd1d5;
--light-color-toggle-text: #ffffff;
--light-color-tooltip-background: rgba(0, 0, 0, 0.9);
--light-color-tooltip-text: #ffffff;
/* Dark theme */
--dark-color-attribute-name: #9d87d2;
--dark-color-attribute-name-not-editable: #ededed;
--dark-color-attribute-name-inverted: #282828;
--dark-color-attribute-value: #cedae0;
--dark-color-attribute-value-inverted: #ffffff;
--dark-color-attribute-editable-value: yellow;
--dark-color-background: #282c34;
--dark-color-background-hover: rgba(255, 255, 255, 0.1);
--dark-color-background-inactive: #3d424a;
--dark-color-background-invalid: #5c0000;
--dark-color-background-selected: #178fb9;
--dark-color-button-background: #282c34;
--dark-color-button-background-focus: #3d424a;
--dark-color-button: #afb3b9;
--dark-color-button-active: #61dafb;
--dark-color-button-disabled: #4f5766;
--dark-color-button-focus: #a2e9fc;
--dark-color-button-hover: #ededed;
--dark-color-border: #3d424a;
--dark-color-commit-did-not-render-fill: #777d88;
--dark-color-commit-did-not-render-fill-text: #000000;
--dark-color-commit-did-not-render-pattern: #666c77;
--dark-color-commit-did-not-render-pattern-text: #ffffff;
--dark-color-commit-gradient-0: #37afa9;
--dark-color-commit-gradient-1: #63b19e;
--dark-color-commit-gradient-2: #80b393;
--dark-color-commit-gradient-3: #97b488;
--dark-color-commit-gradient-4: #abb67d;
--dark-color-commit-gradient-5: #beb771;
--dark-color-commit-gradient-6: #cfb965;
--dark-color-commit-gradient-7: #dfba57;
--dark-color-commit-gradient-8: #efbb49;
--dark-color-commit-gradient-9: #febc38;
--dark-color-commit-gradient-text: #000000;
--dark-color-component-name: #61dafb;
--dark-color-component-name-inverted: #282828;
--dark-color-component-badge-background: rgba(255, 255, 255, 0.25);
--dark-color-component-badge-background-inverted: rgba(0, 0, 0, 0.25);
--dark-color-component-badge-count: #8f949d;
--dark-color-component-badge-count-inverted: rgba(255, 255, 255, 0.7);
--dark-color-context-background: rgba(255,255,255,.9);
--dark-color-context-background-hover: rgba(0, 136, 250, 0.1);
--dark-color-context-background-selected: #0088fa;
--dark-color-context-border: #eeeeee;
--dark-color-context-text: #000000;
--dark-color-context-text-selected: #ffffff;
--dark-color-dim: #8f949d;
--dark-color-dimmer: #777d88;
--dark-color-dimmest: #4f5766;
--dark-color-error-background: #200;
--dark-color-error-border: #900;
--dark-color-error-text: #f55;
--dark-color-expand-collapse-toggle: #8f949d;
--dark-color-link: #61dafb;
--dark-color-modal-background: rgba(0, 0, 0, 0.75);
--dark-color-record-active: #fc3a4b;
--dark-color-record-hover: #a2e9fc;
--dark-color-record-inactive: #61dafb;
--dark-color-scroll-thumb: #afb3b9;
--dark-color-scroll-track: #313640;
--dark-color-search-match: yellow;
--dark-color-search-match-current: #f7923b;
--dark-color-selected-tree-highlight-active: rgba(23, 143, 185, 0.15);
--dark-color-selected-tree-highlight-inactive: rgba(255, 255, 255, 0.05);
--dark-color-shadow: rgba(0, 0, 0, 0.5);
--dark-color-tab-selected-border: #178fb9;
--dark-color-text: #ffffff;
--dark-color-text-invalid: #ff8080;
--dark-color-text-selected: #ffffff;
--dark-color-toggle-background-invalid: #fc3a4b;
--dark-color-toggle-background-on: #178fb9;
--dark-color-toggle-background-off: #777d88;
--dark-color-toggle-text: #ffffff;
--dark-color-tooltip-background: rgba(255, 255, 255, 0.9);
--dark-color-tooltip-text: #000000;
/* Font smoothing */
--light-font-smoothing: auto;
--dark-font-smoothing: antialiased;
--font-smoothing: auto;
/* Compact density */
--compact-font-size-monospace-small: 9px;
--compact-font-size-monospace-normal: 11px;
--compact-font-size-monospace-large: 15px;
--compact-font-size-sans-small: 10px;
--compact-font-size-sans-normal: 12px;
--compact-font-size-sans-large: 14px;
--compact-line-height-data: 18px;
--compact-root-font-size: 16px;
/* Comfortable density */
--comfortable-font-size-monospace-small: 10px;
--comfortable-font-size-monospace-normal: 13px;
--comfortable-font-size-monospace-large: 17px;
--comfortable-font-size-sans-small: 12px;
--comfortable-font-size-sans-normal: 14px;
--comfortable-font-size-sans-large: 16px;
--comfortable-line-height-data: 22px;
--comfortable-root-font-size: 20px;
/* GitHub.com system fonts */
--font-family-monospace: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo,
Courier, monospace;
--font-family-sans: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica,
Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
/* Constant values shared between JS and CSS */
--interaction-commit-size: 10px;
--interaction-label-width: 200px;
}
`},function(i,o,f){"use strict";function p(N){var C=this;if(C instanceof p||(C=new p),C.tail=null,C.head=null,C.length=0,N&&typeof N.forEach=="function")N.forEach(function(W){C.push(W)});else if(arguments.length>0)for(var U=0,q=arguments.length;U<q;U++)C.push(arguments[U]);return C}function E(N,C,U){var q=C===N.head?new L(U,null,C,N):new L(U,C,C.next,N);return q.next===null&&(N.tail=q),q.prev===null&&(N.head=q),N.length++,q}function t(N,C){N.tail=new L(C,N.tail,null,N),N.head||(N.head=N.tail),N.length++}function k(N,C){N.head=new L(C,null,N.head,N),N.tail||(N.tail=N.head),N.length++}function L(N,C,U,q){if(!(this instanceof L))return new L(N,C,U,q);this.list=q,this.value=N,C?(C.next=this,this.prev=C):this.prev=null,U?(U.prev=this,this.next=U):this.next=null}i.exports=p,p.Node=L,p.create=p,p.prototype.removeNode=function(N){if(N.list!==this)throw new Error("removing node which does not belong to this list");var C=N.next,U=N.prev;return C&&(C.prev=U),U&&(U.next=C),N===this.head&&(this.head=C),N===this.tail&&(this.tail=U),N.list.length--,N.next=null,N.prev=null,N.list=null,C},p.prototype.unshiftNode=function(N){if(N!==this.head){N.list&&N.list.removeNode(N);var C=this.head;N.list=this,N.next=C,C&&(C.prev=N),this.head=N,this.tail||(this.tail=N),this.length++}},p.prototype.pushNode=function(N){if(N!==this.tail){N.list&&N.list.removeNode(N);var C=this.tail;N.list=this,N.prev=C,C&&(C.next=N),this.tail=N,this.head||(this.head=N),this.length++}},p.prototype.push=function(){for(var N=0,C=arguments.length;N<C;N++)t(this,arguments[N]);return this.length},p.prototype.unshift=function(){for(var N=0,C=arguments.length;N<C;N++)k(this,arguments[N]);return this.length},p.prototype.pop=function(){if(this.tail){var N=this.tail.value;return this.tail=this.tail.prev,this.tail?this.tail.next=null:this.head=null,this.length--,N}},p.prototype.shift=function(){if(this.head){var N=this.head.value;return this.head=this.head.next,this.head?this.head.prev=null:this.tail=null,this.length--,N}},p.prototype.forEach=function(N,C){C=C||this;for(var U=this.head,q=0;U!==null;q++)N.call(C,U.value,q,this),U=U.next},p.prototype.forEachReverse=function(N,C){C=C||this;for(var U=this.tail,q=this.length-1;U!==null;q--)N.call(C,U.value,q,this),U=U.prev},p.prototype.get=function(N){for(var C=0,U=this.head;U!==null&&C<N;C++)U=U.next;if(C===N&&U!==null)return U.value},p.prototype.getReverse=function(N){for(var C=0,U=this.tail;U!==null&&C<N;C++)U=U.prev;if(C===N&&U!==null)return U.value},p.prototype.map=function(N,C){C=C||this;for(var U=new p,q=this.head;q!==null;)U.push(N.call(C,q.value,this)),q=q.next;return U},p.prototype.mapReverse=function(N,C){C=C||this;for(var U=new p,q=this.tail;q!==null;)U.push(N.call(C,q.value,this)),q=q.prev;return U},p.prototype.reduce=function(N,C){var U,q=this.head;if(arguments.length>1)U=C;else{if(!this.head)throw new TypeError("Reduce of empty list with no initial value");q=this.head.next,U=this.head.value}for(var W=0;q!==null;W++)U=N(U,q.value,W),q=q.next;return U},p.prototype.reduceReverse=function(N,C){var U,q=this.tail;if(arguments.length>1)U=C;else{if(!this.tail)throw new TypeError("Reduce of empty list with no initial value");q=this.tail.prev,U=this.tail.value}for(var W=this.length-1;q!==null;W--)U=N(U,q.value,W),q=q.prev;return U},p.prototype.toArray=function(){for(var N=new Array(this.length),C=0,U=this.head;U!==null;C++)N[C]=U.value,U=U.next;return N},p.prototype.toArrayReverse=function(){for(var N=new Array(this.length),C=0,U=this.tail;U!==null;C++)N[C]=U.value,U=U.prev;return N},p.prototype.slice=function(N,C){(C=C||this.length)<0&&(C+=this.length),(N=N||0)<0&&(N+=this.length);var U=new p;if(C<N||C<0)return U;N<0&&(N=0),C>this.length&&(C=this.length);for(var q=0,W=this.head;W!==null&&q<N;q++)W=W.next;for(;W!==null&&q<C;q++,W=W.next)U.push(W.value);return U},p.prototype.sliceReverse=function(N,C){(C=C||this.length)<0&&(C+=this.length),(N=N||0)<0&&(N+=this.length);var U=new p;if(C<N||C<0)return U;N<0&&(N=0),C>this.length&&(C=this.length);for(var q=this.length,W=this.tail;W!==null&&q>C;q--)W=W.prev;for(;W!==null&&q>N;q--,W=W.prev)U.push(W.value);return U},p.prototype.splice=function(N,C){N>this.length&&(N=this.length-1),N<0&&(N=this.length+N);for(var U=0,q=this.head;q!==null&&U<N;U++)q=q.next;var W=[];for(U=0;q&&U<C;U++)W.push(q.value),q=this.removeNode(q);for(q===null&&(q=this.tail),q!==this.head&&q!==this.tail&&(q=q.prev),U=2;U<arguments.length;U++)q=E(this,q,arguments[U]);return W},p.prototype.reverse=function(){for(var N=this.head,C=this.tail,U=N;U!==null;U=U.prev){var q=U.prev;U.prev=U.next,U.next=q}return this.head=C,this.tail=N,this};try{f(11)(p)}catch(N){}},function(i,o,f){"use strict";i.exports=function(p){p.prototype[Symbol.iterator]=regeneratorRuntime.mark(function E(){var t;return regeneratorRuntime.wrap(function(k){for(;;)switch(k.prev=k.next){case 0:t=this.head;case 1:if(!t){k.next=7;break}return k.next=4,t.value;case 4:t=t.next,k.next=1;break;case 7:case"end":return k.stop()}},E,this)})}},function(i, Line truncated
`).filter(function(ne){return!!ne.match(U)},this).map(function(ne){ne.indexOf("(eval ")>-1&&(ne=ne.replace(/eval code/g,"eval").replace(/(\(eval at [^()]*)|(\),.*$)/g,""));var m=ne.replace(/^\s+/,"").replace(/\(eval code/g,"("),we=m.match(/ (\((.+):(\d+):(\d+)\)$)/),Se=(m=we?m.replace(we[0],""):m).split(/\s+/).slice(1),he=this.extractLocation(we?we[1]:Se.pop()),ge=Se.join(" ")||void 0,ze=["eval","<anonymous>"].indexOf(he[0])>-1?void 0:he[0];return new N({functionName:ge,fileName:ze,lineNumber:he[1],columnNumber:he[2],source:ne})},this)},parseFFOrSafari:function(W){return W.stack.split(`
`).filter(function(ne){return!ne.match(q)},this).map(function(ne){if(ne.indexOf(" > eval")>-1&&(ne=ne.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g,":$1")),ne.indexOf("@")===-1&&ne.indexOf(":")===-1)return new N({functionName:ne});var m=/((.*".+"[^@]*)?[^@]*)(?:@)/,we=ne.match(m),Se=we&&we[1]?we[1]:void 0,he=this.extractLocation(ne.replace(m,""));return new N({functionName:Se,fileName:he[0],lineNumber:he[1],columnNumber:he[2],source:ne})},this)},parseOpera:function(W){return!W.stacktrace||W.message.indexOf(`
`)>-1&&W.message.split(`
`).length>W.stacktrace.split(`
`).length?this.parseOpera9(W):W.stack?this.parseOpera11(W):this.parseOpera10(W)},parseOpera9:function(W){for(var ne=/Line (\d+).*script (?:in )?(\S+)/i,m=W.message.split(`
`),we=[],Se=2,he=m.length;Se<he;Se+=2){var ge=ne.exec(m[Se]);ge&&we.push(new N({fileName:ge[2],lineNumber:ge[1],source:m[Se]}))}return we},parseOpera10:function(W){for(var ne=/Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i,m=W.stacktrace.split(`
`),we=[],Se=0,he=m.length;Se<he;Se+=2){var ge=ne.exec(m[Se]);ge&&we.push(new N({functionName:ge[3]||void 0,fileName:ge[2],lineNumber:ge[1],source:m[Se]}))}return we},parseOpera11:function(W){return W.stack.split(`
`).filter(function(ne){return!!ne.match(C)&&!ne.match(/^Error created at/)},this).map(function(ne){var m,we=ne.split("@"),Se=this.extractLocation(we.pop()),he=we.shift()||"",ge=he.replace(/<anonymous function(: (\w+))?>/,"$2").replace(/\([^)]*\)/g,"")||void 0;he.match(/\(([^)]*)\)/)&&(m=he.replace(/^[^(]+\(([^)]*)\)$/,"$1"));var ze=m===void 0||m==="[arguments not available]"?void 0:m.split(",");return new N({functionName:ge,args:ze,fileName:Se[0],lineNumber:Se[1],columnNumber:Se[2],source:ne})},this)}}})=="function"?p.apply(o,E):p)===void 0||(i.exports=t)})()},function(i,o,f){var p,E,t;(function(k,L){"use strict";E=[],(t=typeof(p=function(){function N(ge){return ge.charAt(0).toUpperCase()+ge.substring(1)}function C(ge){return function(){return this[ge]}}var U=["isConstructor","isEval","isNative","isToplevel"],q=["columnNumber","lineNumber"],W=["fileName","functionName","source"],ne=U.concat(q,W,["args"]);function m(ge){if(ge)for(var ze=0;ze<ne.length;ze++)ge[ne[ze]]!==void 0&&this["set"+N(ne[ze])](ge[ne[ze]])}m.prototype={getArgs:function(){return this.args},setArgs:function(ge){if(Object.prototype.toString.call(ge)!=="[object Array]")throw new TypeError("Args must be an Array");this.args=ge},getEvalOrigin:function(){return this.evalOrigin},setEvalOrigin:function(ge){if(ge instanceof m)this.evalOrigin=ge;else{if(!(ge instanceof Object))throw new TypeError("Eval Origin must be an Object or StackFrame");this.evalOrigin=new m(ge)}},toString:function(){var ge=this.getFileName()||"",ze=this.getLineNumber()||"",pe=this.getColumnNumber()||"",Oe=this.getFunctionName()||"";return this.getIsEval()?ge?"[eval] ("+ge+":"+ze+":"+pe+")":"[eval]:"+ze+":"+pe:Oe?Oe+" ("+ge+":"+ze+":"+pe+")":ge+":"+ze+":"+pe}},m.fromString=function(ge){var ze=ge.indexOf("("),pe=ge.lastIndexOf(")"),Oe=ge.substring(0,ze),le=ge.substring(ze+1,pe).split(","),Ue=ge.substring(pe+1);if(Ue.indexOf("@")===0)var Ge=/@(.+?)(?::(\d+))?(?::(\d+))?$/.exec(Ue,""),rt=Ge[1],wt=Ge[2],xt=Ge[3];return new m({functionName:Oe,args:le||void 0,fileName:rt,lineNumber:wt||void 0,columnNumber:xt||void 0})};for(var we=0;we<U.length;we++)m.prototype["get"+N(U[we])]=C(U[we]),m.prototype["set"+N(U[we])]=function(ge){return function(ze){this[ge]=Boolean(ze)}}(U[we]);for(var Se=0;Se<q.length;Se++)m.prototype["get"+N(q[Se])]=C(q[Se]),m.prototype["set"+N(q[Se])]=function(ge){return function(ze){if(pe=ze,isNaN(parseFloat(pe))||!isFinite(pe))throw new TypeError(ge+" must be a Number");var pe;this[ge]=Number(ze)}}(q[Se]);for(var he=0;he<W.length;he++)m.prototype["get"+N(W[he])]=C(W[he]),m.prototype["set"+N(W[he])]=function(ge){return function(ze){this[ge]=String(ze)}}(W[he]);return m})=="function"?p.apply(o,E):p)===void 0||(i.exports=t)})()},function(i,o,f){"use strict";i.exports=f(19)},function(i,o,f){"use strict";function p(ue){return(p=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(je){return typeof je}:function(je){return je&&typeof Symbol=="function"&&je.constructor===Symbol&&je!==Symbol.prototype?"symbol":typeof je})(ue)}var E=f(1),t=60103,k=60106;o.Fragment=60107,o.StrictMode=60108,o.Profiler=60114;var L=60109,N=60110,C=60112;o.Suspense=60113,o.unstable_SuspenseList=60120;var U=60115,q=60116,W=60121;if(o.unstable_DebugTracingMode=60129,o.unstable_LegacyHidden=60131,typeof Symbol=="function"&&Symbol.for){var ne=Symbol.for;t=ne("react.element"),k=ne("react.portal"),o.Fragment=ne("react.fragment"),o.StrictMode=ne("react.strict_mode"),o.Profiler=ne("react.profiler"),L=ne("react.provider"),N=ne("react.context"),C=ne("react.forward_ref"),o.Suspense=ne("react.suspense"),o.unstable_SuspenseList=ne("react.suspense_list"),U=ne("react.memo"),q=ne("react.lazy"),W=ne("react.block"),o.unstable_DebugTracingMode=ne("react.debug_trace_mode"),o.unstable_LegacyHidden=ne("react.legacy_hidden")}var m=typeof Symbol=="function"&&Symbol.iterator;function we(ue){for(var je="https://reactjs.org/docs/error-decoder.html?invariant="+ue,ct=1;ct<arguments.length;ct++)je+="&args[]="+encodeURIComponent(arguments[ct]);return"Minified React error #"+ue+"; visit "+je+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}var Se={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},he={};function ge(ue,je,ct){this.props=ue,this.context=je,this.refs=he,this.updater=ct||Se}function ze(){}function pe(ue,je,ct){this.props=ue,this.context=je,this.refs=he,this.updater=ct||Se}ge.prototype.isReactComponent={},ge.prototype.setState=function(ue,je){if(p(ue)!=="object"&&typeof ue!="function"&&ue!=null)throw Error(we(85));this.updater.enqueueSetState(this,ue,je,"setState")},ge.prototype.forceUpdate=function(ue){this.updater.enqueueForceUpdate(this,ue,"forceUpdate")},ze.prototype=ge.prototype;var Oe=pe.prototype=new ze;Oe.constructor=pe,E(Oe,ge.prototype),Oe.isPureReactComponent=!0;var le={current:null},Ue=Object.prototype.hasOwnProperty,Ge={key:!0,ref:!0,__self:!0,__sou Line truncated
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var me,xe=!0,Z=!1;return{s:function(){G=B[Symbol.iterator]()},n:function(){var ke=G.next();return xe=ke.done,ke},e:function(ke){Z=!0,me=ke},f:function(){try{xe||G.return==null||G.return()}finally{if(Z)throw me}}}}function le(B,z){(z==null||z>B.length)&&(z=B.length);for(var G=0,$=new Array(z);G<z;G++)$[G]=B[G];return $}function Ue(B,z){if(!(B instanceof z))throw new TypeError("Cannot call a class as a function")}function Ge(B,z){for(var G=0;G<z.length;G++){var $=z[G];$.enumerable=$.enumerable||!1,$.configurable=!0,"value"in $&&($.writable=!0),Object.defineProperty(B,$.key,$)}}function rt(B,z,G){return z&&Ge(B.prototype,z),G&&Ge(B,G),B}var wt=function(){function B(z,G){Ue(this,B),this.node=z.createElement("div"),this.border=z.createElement("div"),this.padding=z.createElement("div"),this.content=z.createElement("div"),this.border.style.borderColor=Ke.border,this.padding.style.borderColor=Ke.padding,this.content.style.backgroundColor=Ke.background,m()(this.node.style,{borderColor:Ke.margin,pointerEvents:"none",position:"fixed"}),this.node.style.zIndex="10000000",this.node.appendChild(this.border),this.border.appendChild(this.padding),this.padding.appendChild(this.content),G.appendChild(this.node)}return rt(B,[{key:"remove",value:function(){this.node.parentNode&&this.node.parentNode.removeChild(this.node)}},{key:"update",value:function(z,G){ft(G,"margin",this.node),ft(G,"border",this.border),ft(G,"padding",this.padding),m()(this.content.style,{height:z.height-G.borderTop-G.borderBottom-G.paddingTop-G.paddingBottom+"px",width:z.width-G.borderLeft-G.borderRight-G.paddingLeft-G.paddingRight+"px"}),m()(this.node.style,{top:z.top-G.marginTop+"px",left:z.left-G.marginLeft+"px"})}}]),B}(),xt=function(){function B(z,G){Ue(this,B),this.tip=z.createElement("div"),m()(this.tip.style,{display:"flex",flexFlow:"row nowrap",backgroundColor:"#333740",borderRadius:"2px",fontFamily:'"SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace',fontWeight:"bold",padding:"3px 5px",pointerEvents:"none",position:"fixed",fontSize:"12px",whiteSpace:"nowrap"}),this.nameSpan=z.createElement("span"),this.tip.appendChild(this.nameSpan),m()(this.nameSpan.style,{color:"#ee78e6",borderRight:"1px solid #aaaaaa",paddingRight:"0.5rem",marginRight:"0.5rem"}),this.dimSpan=z.createElement("span"),this.tip.appendChild(this.dimSpan),m()(this.dimSpan.style,{color:"#d7d7d7"}),this.tip.style.zIndex="10000000",G.appendChild(this.tip)}return rt(B,[{key:"remove",value:function(){this.tip.parentNode&&this.tip.parentNode.removeChild(this.tip)}},{key:"updateText",value:function(z,G,$){this.nameSpan.textContent=z,this.dimSpan.textContent=Math.round(G)+"px \xD7 "+Math.round($)+"px"}},{key:"updatePosition",value:function(z,G){var $=this.tip.getBoundingClientRect(),De=function(me,xe,Z){var ke,Xe=Math.max(Z.height,20),ht=Math.max(Z.width,60);ke=me.top+me.height+Xe<=xe.top+xe.height?me.top+me.height<xe.top+0?xe.top+5:me.top+me.height+5:me.top-Xe<=xe.top+xe.height?me.top-Xe-5<xe.top+5?xe.top+5:me.top-Xe-5:xe.top+xe.height-Xe-5;var ie=me.left+5;return me.left<xe.left&&(ie=xe.left+5),me.left+ht>xe.left+xe.width&&(ie=xe.left+xe.width-ht-5),{style:{top:ke+="px",left:ie+="px"}}}(z,G,{width:$.width,height:$.height});m()(this.tip.style,De.style)}}]),B}(),$e=function(){function B(){Ue(this,B);var z=window.__REACT_DEVTOOLS_TARGET_WINDOW__||window;this.window=z;var G=window.__REACT_DEVTOOLS_TARGET_WINDOW__||window;this.tipBoundsWindow=G;var $=z.document;this.container=$.createElement("div"),this.container.style.zIndex="10000000",this.tip=new xt($,this.container),this.rects=[],$.body.appendChild(this.container)}return rt(B,[{key:"remove",value:function(){this.tip.remove(),this.rects.forEach(function(z){z.remove()}),this.rects.length=0,this.container.parentNode&&this.container.parentNode.removeChild(this.container)}},{key:"inspect",value:function(z,G){for(var $=this,De=z.filter(function(Tt){return Tt.nodeType===Node.ELEMENT_NODE});this.rects.length>De.length;)this.rects.pop().remove();if(De.length!==0){for(;this.rects.length<De.length;)this.rects.push(new wt(this.window.document,this.container));var me={top:Number.POSITIVE_INFINITY,right:Number.NEGATIVE_INFINITY,bottom:Number.NEGATIVE_INFINITY,left:Number.POSITIVE_INFINITY};if(De.forEach(function(Tt,kt){var bt=ze(Tt,$.window),on=pe(Tt);me.top=Math.min(me.top,bt.top-on.marginTop),me.right=Math.max(me.right,bt.left+bt.width+on.marginRight),me.bottom=Math.max(me.bottom,bt.top+bt.height+on.marginBottom),me.left=Math.min(me.left,bt.left-on.marginLeft),$.rects[kt].update(bt,on)}),!G){G=De[0].nodeName.toLowerCase();var xe=De[0],Z=xe.ownerDocument.defaultView.__REACT_DEVTOOLS_GLOBAL_HOOK__;if(Z!=null&&Z.rendererInterfaces!=null){var ke,Xe=null,ht=Oe(Z.rendererInterfaces.values());try{for(ht.s();!(ke=ht.n()).done;){var ie=ke.value,qe=ie.getFiberIDForNative(xe,!0);if(qe!==null){Xe=ie.getDisplayNameForFiberID(qe,!0);break}}}catch(Tt){ht.e(Tt) Line truncated
xx-background-color: red;
xx-opacity: 0.5;
bottom: 0;
left: 0;
pointer-events: none;
position: fixed;
right: 0;
top: 0;
z-index: 1000000000;
`;var $=window.document.documentElement;$.insertBefore(je,$.firstChild)}();var z=je;z.width=window.innerWidth,z.height=window.innerHeight;var G=z.getContext("2d");G.clearRect(0,0,z.width,z.height),B.forEach(function($){var De=$.count,me=$.rect;if(me!==null){var xe=Math.min(ue.length-1,De-1);(function(Z,ke,Xe){var ht=ke.height,ie=ke.left,qe=ke.top,tt=ke.width;Z.lineWidth=1,Z.strokeStyle="#f0f0f0",Z.strokeRect(ie-1,qe-1,tt+2,ht+2),Z.lineWidth=1,Z.strokeStyle="#f0f0f0",Z.strokeRect(ie+1,qe+1,tt-1,ht-1),Z.strokeStyle=Xe,Z.setLineDash([0]),Z.lineWidth=1,Z.strokeRect(ie,qe,tt-1,ht-1),Z.setLineDash([0])})(G,me,ue[xe])}})}function At(B){return(At=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(z){return typeof z}:function(z){return z&&typeof Symbol=="function"&&z.constructor===Symbol&&z!==Symbol.prototype?"symbol":typeof z})(B)}var en=(typeof performance=="undefined"?"undefined":At(performance))==="object"&&typeof performance.now=="function"?function(){return performance.now()}:function(){return Date.now()},ln=new Map,An=null,nr=!1,un=null;function Wt(B){(nr=B)||(ln.clear(),An!==null&&(cancelAnimationFrame(An),An=null),un!==null&&(clearTimeout(un),un=null),je!==null&&(je.parentNode!=null&&je.parentNode.removeChild(je),je=null))}function vr(B){nr&&(B.forEach(function(z){var G=ln.get(z),$=en(),De=G!=null?G.lastMeasuredAt:0,me=G!=null?G.rect:null;(me===null||De+250<$)&&(De=$,me=function(xe){if(!xe||typeof xe.getBoundingClientRect!="function")return null;var Z=window.__REACT_DEVTOOLS_TARGET_WINDOW__||window;return ze(xe,Z)}(z)),ln.set(z,{count:G!=null?G.count+1:1,expirationTime:G!=null?Math.min($+3e3,G.expirationTime+250):$+250,lastMeasuredAt:De,rect:me})}),un!==null&&(clearTimeout(un),un=null),An===null&&(An=requestAnimationFrame(w)))}function w(){An=null,un=null;var B=en(),z=Number.MAX_VALUE;ln.forEach(function(G,$){G.expirationTime<B?ln.delete($):z=Math.min(z,G.expirationTime)}),ct(ln),z!==Number.MAX_VALUE&&(un=setTimeout(w,z-B))}var Ut=f(3),Vn=f(6),fr=f.n(Vn),Fr=f(0),ur=60120;if(typeof Symbol=="function"&&Symbol.for){var br=Symbol.for;br("react.element"),br("react.portal"),br("react.fragment"),br("react.strict_mode"),br("react.profiler"),br("react.provider"),br("react.context"),br("react.forward_ref"),br("react.suspense"),ur=br("react.suspense_list"),br("react.memo"),br("react.lazy"),br("react.block"),br("react.server.block"),br("react.fundamental"),br("react.scope"),br("react.opaque.id"),br("react.debug_trace_mode"),br("react.offscreen"),br("react.legacy_hidden")}typeof Symbol=="function"&&Symbol.iterator;function Kt(B){return(Kt=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(z){return typeof z}:function(z){return z&&typeof Symbol=="function"&&z.constructor===Symbol&&z!==Symbol.prototype?"symbol":typeof z})(B)}var vu={inspectable:Symbol("inspectable"),inspected:Symbol("inspected"),name:Symbol("name"),preview_long:Symbol("preview_long"),preview_short:Symbol("preview_short"),readonly:Symbol("readonly"),size:Symbol("size"),type:Symbol("type"),unserializable:Symbol("unserializable")};function a0(B,z,G,$,De){$.push(De);var me={inspectable:z,type:B,preview_long:Si(G,!0),preview_short:Si(G,!1),name:G.constructor&&G.constructor.name!=="Object"?G.constructor.name:""};return B==="array"||B==="typed_array"?me.size=G.length:B==="object"&&(me.size=Object.keys(G).length),B!=="iterator"&&B!=="typed_array"||(me.readonly=!0),me}function So(B,z,G,$,De){var me,xe=arguments.length>5&&arguments[5]!==void 0?arguments[5]:0,Z=Co(B);switch(Z){case"html_element":return z.push($),{inspectable:!1,preview_short:Si(B,!1),preview_long:Si(B,!0),name:B.tagName,type:Z};case"function":return z.push($),{inspectable:!1,preview_short:Si(B,!1),preview_long:Si(B,!0),name:typeof B.name!="function"&&B.name?B.name:"function",type:Z};case"string":return B.length<=500?B:B.slice(0,500)+"...";case"bigint":case"symbol":return z.push($),{inspectable:!1,preview_short:Si(B,!1),preview_long:Si(B,!0),name:B.toString(),type:Z};case"react_element":return z.push($),{inspectable:!1,preview_short:Si(B,!1),preview_long:Si(B,!0),name:L0(B)||"Unknown",type:Z};case"array_buffer":case"data_view":return z.push($),{inspectable:!1,preview_short:Si(B,!1),preview_long:Si(B,!0),name:Z==="data_view"?"DataView":"ArrayBuffer",size:B.byteLength,type:Z};case"array":return me=De($),xe>=2&&!me?a0(Z,!0,B,z,$):B.map(function(ht,ie){return So(ht,z,G,$.concat([ie]),De,me?1:xe+1)});case"html_all_collection":case"typed_array":case"iterator":if(me=De($),xe>=2&&!me)return a0(Z,!0,B,z,$);var ke={unserializable:!0,type:Z,readonly:!0,size:Z==="typed_array"?B.length:void 0,preview_short:Si(B,!1),preview_long:Si(B,!0),name:B.constructor&&B.constructor.name!=="Object"?B.constructor.name:""};return Kt(B[Symbol.iterator])&&Array.from(B).forEach(function(ht,ie){return ke[ie]=So(ht,z,G,$.concat([ie]),De,me?1:xe+1)}),G.push($),ke;case"opaque_iterator":return z.push($),{inspectable:!1,preview_short:Si(B,!1),preview_long:Si(B,!0),name:B[Symbol.toStringTag],type:Z} Line truncated
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}()}function Yo(B,z){(z==null||z>B.length)&&(z=B.length);for(var G=0,$=new Array(z);G<z;G++)$[G]=B[G];return $}var Ko=new WeakMap,qt=new fr.a({max:1e3});function _i(B,z){return B.toString()>z.toString()?1:z.toString()>B.toString()?-1:0}function eu(B){for(var z=[],G=B,$=function(){var De=[].concat(Os(Object.keys(G)),Os(Object.getOwnPropertySymbols(G))),me=Object.getOwnPropertyDescriptors(G);De.forEach(function(xe){me[xe].enumerable&&z.push(xe)}),G=Object.getPrototypeOf(G)};G!=null;)$();return z}function ai(B){var z=arguments.length>1&&arguments[1]!==void 0?arguments[1]:"Anonymous",G=Ko.get(B);if(G!=null)return G;var $=z;return typeof B.displayName=="string"?$=B.displayName:typeof B.name=="string"&&B.name!==""&&($=B.name),Ko.set(B,$),$}var mr=0;function Xo(){return++mr}function W0(B){var z=qt.get(B);if(z!==void 0)return z;for(var G=new Array(B.length),$=0;$<B.length;$++)G[$]=B.codePointAt($);return qt.set(B,G),G}function Lu(B,z){return z.reduce(function(G,$){if(G){if(hasOwnProperty.call(G,$))return G[$];if(typeof G[Symbol.iterator]=="function")return Array.from(G)[$]}return null},B)}function V0(B,z){var G=z.length,$=z[G-1];if(B!=null){var De=Lu(B,z.slice(0,G-1));De&&(Array.isArray(De)?De.splice($,1):delete De[$])}}function Hr(B,z,G){var $=z.length;if(B!=null){var De=Lu(B,z.slice(0,$-1));if(De){var me=z[$-1];De[G[$-1]]=De[me],Array.isArray(De)?De.splice(me,1):delete De[me]}}}function To(B,z,G){var $=z.length,De=z[$-1];if(B!=null){var me=Lu(B,z.slice(0,$-1));me&&(me[De]=G)}}function Co(B){if(B===null)return"null";if(B===void 0)return"undefined";if(Object(Fr.isElement)(B))return"react_element";if(typeof HTMLElement!="undefined"&&B instanceof HTMLElement)return"html_element";switch(Go(B)){case"bigint":return"bigint";case"boolean":return"boolean";case"function":return"function";case"number":return Number.isNaN(B)?"nan":Number.isFinite(B)?"number":"infinity";case"object":if(Array.isArray(B))return"array";if(ArrayBuffer.isView(B))return hasOwnProperty.call(B.constructor,"BYTES_PER_ELEMENT")?"typed_array":"data_view";if(B.constructor&&B.constructor.name==="ArrayBuffer")return"array_buffer";if(typeof B[Symbol.iterator]=="function")return B[Symbol.iterator]()===B?"opaque_iterator":"iterator";if(B.constructor&&B.constructor.name==="RegExp")return"regexp";var z=Object.prototype.toString.call(B);return z==="[object Date]"?"date":z==="[object HTMLAllCollection]"?"html_all_collection":"object";case"string":return"string";case"symbol":return"symbol";case"undefined":return Object.prototype.toString.call(B)==="[object HTMLAllCollection]"?"html_all_collection":"undefined";default:return"unknown"}}function L0(B){switch(Object(Fr.typeOf)(B)){case Fr.ContextConsumer:return"ContextConsumer";case Fr.ContextProvider:return"ContextProvider";case Fr.ForwardRef:return"ForwardRef";case Fr.Fragment:return"Fragment";case Fr.Lazy:return"Lazy";case Fr.Memo:return"Memo";case Fr.Portal:return"Portal";case Fr.Profiler:return"Profiler";case Fr.StrictMode:return"StrictMode";case Fr.Suspense:return"Suspense";case ur:return"SuspenseList";default:var z=B.type;return typeof z=="string"?z:typeof z=="function"?ai(z,"Anonymous"):z!=null?"NotImplementedInDevtools":"Element"}}function tu(B){var z=arguments.length>1&&arguments[1]!==void 0?arguments[1]:50;return B.length>z?B.substr(0,z)+"\u2026":B}function Si(B,z){if(B!=null&&hasOwnProperty.call(B,vu.type))return z?B[vu.preview_long]:B[vu.preview_short];switch(Co(B)){case"html_element":return"<".concat(tu(B.tagName.toLowerCase())," />");case"function":return tu("\u0192 ".concat(typeof B.name=="function"?"":B.name,"() {}"));case"string":return'"'.concat(B,'"');case"bigint":return tu(B.toString()+"n");case"regexp":case"symbol":return tu(B.toString());case"react_element":return"<".concat(tu(L0(B)||"Unknown")," />");case"array_buffer":return"ArrayBuffer(".concat(B.byteLength,")");case"data_view":return"DataView(".concat(B.buffer.byteLength,")");case"array":if(z){for(var G="",$=0;$<B.length&&($>0&&(G+=", "),!((G+=Si(B[$],!1)).length>50));$++);return"[".concat(tu(G),"]")}var De=hasOwnProperty.call(B,vu.size)?B[vu.size]:B.length;return"Array(".concat(De,")");case"typed_array":var me="".concat(B.constructor.name,"(").concat(B.length,")");if(z){for(var xe="",Z=0;Z<B.length&&(Z>0&&(xe+=", "),!((xe+=B[Z]).length>50));Z++);return"".concat(me," [").concat(tu(xe),"]")}return me;case"iterator":var ke=B.constructor.name;if(z){for(var Xe=Array.from(B),ht="",ie=0;ie<Xe.length;ie++){var qe=Xe[ie];if(ie>0&&(ht+=", "),Array.isArray(qe)){var tt=Si(qe[0],!0),Tt=Si(qe[1],!1);ht+="".concat(tt," => ").concat(Tt)}else ht+=Si(qe,!1);if(ht.length>50)break}return"".concat(ke,"(").concat(B.size,") {").concat(tu(ht),"}")}return"".concat(ke,"(").concat(B.size,")");case"opaque_iterator":return B[Symbol.toStringTag];case"date":return B.toString();case"object":if(z){for(var kt=eu(B).sort(_i),bt="",on=0;on<kt.length;on++){var tn=kt[on];if(on>0&&(bt+=", "),(bt+ Line truncated
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}()}function Y0(B,z){return function(G){if(Array.isArray(G))return G}(B)||function(G,$){if(!(typeof Symbol=="undefined"||!(Symbol.iterator in Object(G)))){var De=[],me=!0,xe=!1,Z=void 0;try{for(var ke,Xe=G[Symbol.iterator]();!(me=(ke=Xe.next()).done)&&(De.push(ke.value),!$||De.length!==$);me=!0);}catch(ht){xe=!0,Z=ht}finally{try{me||Xe.return==null||Xe.return()}finally{if(xe)throw Z}}return De}}(B,z)||Wl(B,z)||function(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}()}function Ui(B,z){var G;if(typeof Symbol=="undefined"||B[Symbol.iterator]==null){if(Array.isArray(B)||(G=Wl(B))||z&&B&&typeof B.length=="number"){G&&(B=G);var $=0,De=function(){};return{s:De,n:function(){return $>=B.length?{done:!0}:{done:!1,value:B[$++]}},e:function(ke){throw ke},f:De}}throw new TypeError(`Invalid attempt to iterate non-iterable instance.
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var me,xe=!0,Z=!1;return{s:function(){G=B[Symbol.iterator]()},n:function(){var ke=G.next();return xe=ke.done,ke},e:function(ke){Z=!0,me=ke},f:function(){try{xe||G.return==null||G.return()}finally{if(Z)throw me}}}}function Wl(B,z){if(B){if(typeof B=="string")return xo(B,z);var G=Object.prototype.toString.call(B).slice(8,-1);return G==="Object"&&B.constructor&&(G=B.constructor.name),G==="Map"||G==="Set"?Array.from(B):G==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(G)?xo(B,z):void 0}}function xo(B,z){(z==null||z>B.length)&&(z=B.length);for(var G=0,$=new Array(z);G<z;G++)$[G]=B[G];return $}function ni(B){return(ni=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(z){return typeof z}:function(z){return z&&typeof Symbol=="function"&&z.constructor===Symbol&&z!==Symbol.prototype?"symbol":typeof z})(B)}function oo(B){return B.flags!==void 0?B.flags:B.effectTag}var Vl=(typeof performance=="undefined"?"undefined":ni(performance))==="object"&&typeof performance.now=="function"?function(){return performance.now()}:function(){return Date.now()};function Ao(B){var z=null;function G(tn){var Lt=ni(tn)==="object"&&tn!==null?tn.$$typeof:tn;return ni(Lt)==="symbol"?Lt.toString():Lt}var $=z=Object(Ut.gte)(B,"17.0.0-alpha")?{Block:22,ClassComponent:1,ContextConsumer:9,ContextProvider:10,CoroutineComponent:-1,CoroutineHandlerPhase:-1,DehydratedSuspenseComponent:18,ForwardRef:11,Fragment:7,FunctionComponent:0,HostComponent:5,HostPortal:4,HostRoot:3,HostText:6,IncompleteClassComponent:17,IndeterminateComponent:2,LazyComponent:16,MemoComponent:14,Mode:8,OffscreenComponent:23,Profiler:12,SimpleMemoComponent:15,SuspenseComponent:13,SuspenseListComponent:19,YieldComponent:-1}:Object(Ut.gte)(B,"16.6.0-beta.0")?{Block:22,ClassComponent:1,ContextConsumer:9,ContextProvider:10,CoroutineComponent:-1,CoroutineHandlerPhase:-1,DehydratedSuspenseComponent:18,ForwardRef:11,Fragment:7,FunctionComponent:0,HostComponent:5,HostPortal:4,HostRoot:3,HostText:6,IncompleteClassComponent:17,IndeterminateComponent:2,LazyComponent:16,MemoComponent:14,Mode:8,OffscreenComponent:-1,Profiler:12,SimpleMemoComponent:15,SuspenseComponent:13,SuspenseListComponent:19,YieldComponent:-1}:Object(Ut.gte)(B,"16.4.3-alpha")?{Block:-1,ClassComponent:2,ContextConsumer:11,ContextProvider:12,CoroutineComponent:-1,CoroutineHandlerPhase:-1,DehydratedSuspenseComponent:-1,ForwardRef:13,Fragment:9,FunctionComponent:0,HostComponent:7,HostPortal:6,HostRoot:5,HostText:8,IncompleteClassComponent:-1,IndeterminateComponent:4,LazyComponent:-1,MemoComponent:-1,Mode:10,OffscreenComponent:-1,Profiler:15,SimpleMemoComponent:-1,SuspenseComponent:16,SuspenseListComponent:-1,YieldComponent:-1}:{Block:-1,ClassComponent:2,ContextConsumer:12,ContextProvider:13,CoroutineComponent:7,CoroutineHandlerPhase:8,DehydratedSuspenseComponent:-1,ForwardRef:14,Fragment:10,FunctionComponent:1,HostComponent:5,HostPortal:4,HostRoot:3,HostText:6,IncompleteClassComponent:-1,IndeterminateComponent:0,LazyComponent:-1,MemoComponent:-1,Mode:11,OffscreenComponent:-1,Profiler:15,SimpleMemoComponent:-1,SuspenseComponent:16,SuspenseListComponent:-1,YieldComponent:9},De=$.ClassComponent,me=$.IncompleteClassComponent,xe=$.FunctionComponent,Z=$.IndeterminateComponent,ke=$.ForwardRef,Xe=$.HostRoot,ht=$.HostComponent,ie=$.HostPortal,qe=$.HostText,tt=$.Fragment,Tt=$.MemoComponent,kt=$.SimpleMemoComponent,bt=$.SuspenseComponent,on=$.SuspenseListComponent;return{getDisplayNameForFiber:function(tn){var Lt=tn.type,gn=tn.tag,lr=Lt;ni(Lt)==="object"&&Lt!==null&&(lr=function _r(Cn){switch(G(Cn)){case 60115:case"Symbol(react.memo)":return _r(Cn.type);case 60112:case"Symbol(react.forward_ref)":return Cn.render;default:return Cn}}(Lt));var Qn=null;switch(gn){case De:case me:return ai(lr);case xe:case Z:return ai(lr);case ke:return Lt&&Lt.displayName||ai(lr,"Anonymous");case Xe:return null;case ht:return Lt;case ie:case qe:case tt:return null;case Tt:case kt:return ai(lr,"Anonymous");case bt:return"Suspense";case on:return"SuspenseList";default:switch(G(Lt)){case 60111:case"Symbol(react.concurrent_mode)":case"Symbol(react.async_mode)":return null;case 60109:case"Symbol(react.provider)":return Qn=tn.type._context||tn.type.context,"".concat(Qn.displayName||"Context",".Provider");case 60110:case"Symbol(react.context)":return Qn=tn.type._context||tn.type,"".concat(Qn.displayName||"Context",".Consumer");case 60108:case"Symbol(react.strict_mode)":return null;case 60114:case"Symbol(react.profiler)":return"Profiler(".concat(tn.memoizedProps.id,")");case 60119:case"Symbol(react.scope)":return"Scope";default:return null}}},getTypeSymbol:G,ReactPriorityLevels:{ImmediatePriority:99,UserBlockingPriority:98,NormalPriority:97,LowPriority:96,IdlePriority:95,NoPriority:90},ReactTypeOfWork:z,ReactTypeOfSideEffect:{NoFlags:0,PerformedWork:1,Placement:2}}}function Ms(B,z,G,$){var De=Ao(G.version),me=De.getDisplayNameForFiber,xe=De.getTypeS Line truncated
`+Xn+B}var b0=!1;function yl(B,z,G){if(!B||b0)return"";var $,De=Error.prepareStackTrace;Error.prepareStackTrace=void 0,b0=!0;var me=G.current;G.current=null;try{if(z){var xe=function(){throw Error()};if(Object.defineProperty(xe.prototype,"props",{set:function(){throw Error()}}),(typeof Reflect=="undefined"?"undefined":Qo(Reflect))==="object"&&Reflect.construct){try{Reflect.construct(xe,[])}catch(qe){$=qe}Reflect.construct(B,[],xe)}else{try{xe.call()}catch(qe){$=qe}B.call(xe.prototype)}}else{try{throw Error()}catch(qe){$=qe}B()}}catch(qe){if(qe&&$&&typeof qe.stack=="string"){for(var Z=qe.stack.split(`
`),ke=$.stack.split(`
`),Xe=Z.length-1,ht=ke.length-1;Xe>=1&&ht>=0&&Z[Xe]!==ke[ht];)ht--;for(;Xe>=1&&ht>=0;Xe--,ht--)if(Z[Xe]!==ke[ht]){if(Xe!==1||ht!==1)do if(Xe--,--ht<0||Z[Xe]!==ke[ht])return`
`+Z[Xe].replace(" at new "," at ");while(Xe>=1&&ht>=0);break}}}finally{b0=!1,Error.prepareStackTrace=De,G.current=me}var ie=B?B.displayName||B.name:"";return ie?lo(ie):""}function Ro(B,z,G,$){return yl(B,!1,$)}function Et(B,z,G){var $=B.HostComponent,De=B.LazyComponent,me=B.SuspenseComponent,xe=B.SuspenseListComponent,Z=B.FunctionComponent,ke=B.IndeterminateComponent,Xe=B.SimpleMemoComponent,ht=B.ForwardRef,ie=B.Block,qe=B.ClassComponent;switch(z.tag){case $:return lo(z.type);case De:return lo("Lazy");case me:return lo("Suspense");case xe:return lo("SuspenseList");case Z:case ke:case Xe:return Ro(z.type,0,0,G);case ht:return Ro(z.type.render,0,0,G);case ie:return Ro(z.type._render,0,0,G);case qe:return function(tt,Tt,kt,bt){return yl(tt,!0,bt)}(z.type,0,0,G);default:return""}}function Pt(B,z,G){try{var $="",De=z;do $+=Et(B,De,G),De=De.return;while(De);return $}catch(me){return`
Error generating stack: `+me.message+`
`+me.stack}}function Bn(B,z){var G;if(typeof Symbol=="undefined"||B[Symbol.iterator]==null){if(Array.isArray(B)||(G=function(ke,Xe){if(!!ke){if(typeof ke=="string")return Ir(ke,Xe);var ht=Object.prototype.toString.call(ke).slice(8,-1);if(ht==="Object"&&ke.constructor&&(ht=ke.constructor.name),ht==="Map"||ht==="Set")return Array.from(ke);if(ht==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(ht))return Ir(ke,Xe)}}(B))||z&&B&&typeof B.length=="number"){G&&(B=G);var $=0,De=function(){};return{s:De,n:function(){return $>=B.length?{done:!0}:{done:!1,value:B[$++]}},e:function(ke){throw ke},f:De}}throw new TypeError(`Invalid attempt to iterate non-iterable instance.
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var me,xe=!0,Z=!1;return{s:function(){G=B[Symbol.iterator]()},n:function(){var ke=G.next();return xe=ke.done,ke},e:function(ke){Z=!0,me=ke},f:function(){try{xe||G.return==null||G.return()}finally{if(Z)throw me}}}}function Ir(B,z){(z==null||z>B.length)&&(z=B.length);for(var G=0,$=new Array(z);G<z;G++)$[G]=B[G];return $}var ji=["error","trace","warn"],Wr=/\s{4}(in|at)\s{1}/,wu=/:\d+:\d+(\n|$)/,c0=new Map,Ti=console,d0={};for(var as in console)d0[as]=console[as];var St=null;function so(B){var z=B.currentDispatcherRef,G=B.getCurrentFiber,$=B.findFiberByHostInstance,De=B.version;if(typeof $=="function"&&z!=null&&typeof G=="function"){var me=Ao(De).ReactTypeOfWork;c0.set(B,{currentDispatcherRef:z,getCurrentFiber:G,workTagMap:me})}}var Jo={appendComponentStack:!1,breakOnConsoleErrors:!1};function Gl(B){var z=B.appendComponentStack,G=B.breakOnConsoleErrors;if(Jo.appendComponentStack=z,Jo.breakOnConsoleErrors=G,St===null){var $={};St=function(){for(var De in $)try{Ti[De]=$[De]}catch(me){}},ji.forEach(function(De){try{var me=$[De]=Ti[De],xe=function(){for(var Z=Jo.appendComponentStack,ke=(Jo.breakOnConsoleErrors,arguments.length),Xe=new Array(ke),ht=0;ht<ke;ht++)Xe[ht]=arguments[ht];if(Z)try{var ie=Xe.length>0?Xe[Xe.length-1]:null,qe=ie!==null&&(Wr.test(ie)||wu.test(ie));if(!qe){var tt,Tt=Bn(c0.values());try{for(Tt.s();!(tt=Tt.n()).done;){var kt=tt.value,bt=kt.currentDispatcherRef,on=kt.getCurrentFiber,tn=kt.workTagMap,Lt=on();if(Lt!=null){var gn=Pt(tn,Lt,bt);gn!==""&&Xe.push(gn);break}}}catch(lr){Tt.e(lr)}finally{Tt.f()}}}catch(lr){}me.apply(void 0,Xe)};xe.__REACT_DEVTOOLS_ORIGINAL_METHOD__=me,Ti[De]=xe}catch(Z){}})}}function Fu(B){return(Fu=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(z){return typeof z}:function(z){return z&&typeof Symbol=="function"&&z.constructor===Symbol&&z!==Symbol.prototype?"symbol":typeof z})(B)}function fs(B,z){for(var G=0;G<z.length;G++){var $=z[G];$.enumerable=$.enumerable||!1,$.configurable=!0,"value"in $&&($.writable=!0),Object.defineProperty(B,$.key,$)}}function P0(B,z){return(P0=Object.setPrototypeOf||function(G,$){return G.__proto__=$,G})(B,z)}function X(B){var z=function(){if(typeof Reflect=="undefined"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(G){return!1}}();return function(){var G,$=Me(B);if(z){var De=Me(this).constructor;G=Reflect.construct($,arguments,De)}else G=$.apply(this,arguments);return _e(this,G)}}function _e(B,z){return!z||Fu(z)!=="object"&&typeof z!="function"?Ne(B):z}function Ne(B){if(B===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return B}function Me(B){return(Me=Object.setPrototypeOf?Object.getPrototypeOf:function(z){return z.__proto__||Object.getPrototypeOf(z)})(B)}function dt(B,z,G){return z in B?Object.defineProperty(B,z,{value:G,enumerable:!0,configurable:!0,writable:!0}):B[z]=G,B}var Hn=function(B){(function(xe,Z){if(typeof Z!="function"&&Z!==null)throw new TypeError("Super expression must either be null or a function");xe.prototype=Object.create(Z&&Z.prototype,{constructor:{value:xe,writable:!0,configurable:!0}}),Z&&P0(xe,Z)})(me,B);var z,G,$,De=X(me);function me(xe){var Z;(function(ie,qe){if(!(ie instanceof qe))throw new TypeError("Cannot call a class as a function")})(this,me),dt(Ne(Z=De.call(this)),"_isProfiling",!1),dt(Ne(Z),"_recordChangeDescriptions",!1),dt(Ne(Z),"_rendererInterfaces",{}),dt(Ne(Z),"_persistedSelection",null),dt(Ne(Z),"_persistedSelectionMatch",null),dt(Ne(Z),"_traceUpdatesEnabled",!1),dt(Ne(Z),"copyElementPath",function(ie){var qe=ie.id,tt=ie.path,Tt=ie.rendererID,kt=Z._rendererInterfaces[Tt];kt==null?console.warn('Invalid renderer id "'.concat(Tt,'" for element "').concat(qe,'"')):kt.copyElementPath(qe,tt)}),dt(Ne(Z),"deletePath",function(ie){var qe=ie.hookID,tt=ie.id,Tt=ie.path,kt=ie.rendererID,bt=ie.type,on=Z._rendererInterfaces[kt];on==null?console.warn('Invalid renderer id "'.concat(kt,'" for element "').concat(tt,'"')):on.deletePath(bt,tt,qe,Tt)}),dt(Ne(Z),"getProfilingData",function(ie){var qe=ie.rendererID,tt=Z._rendererInterfaces[qe];tt==null&&console.warn('Invalid renderer id "'.concat(qe,'"')),Z._bridge.send("profilingData",tt.getProfilingData())}),dt(Ne(Z),"getProfilingStatus",function(){Z._bridge.send("profilingStatus",Z._isProfiling)}),dt(Ne(Z),"getOwnersList",function(ie){var qe=ie.id,tt=ie.rendererID,Tt=Z._rendererInterfaces[tt];if(Tt==null)console.warn('Invalid renderer id "'.concat(tt,'" for element "').concat(qe,'"'));else{var kt=Tt.getOwnersList(qe);Z._bridge.send("ownersList",{id:qe,owners:kt})}}),dt(Ne(Z),"inspectElement",function(ie){var qe=ie.id,tt=ie.path,Tt=ie.rendererID,kt=Z._rendererInterfaces[Tt];kt==null?console.warn('Invalid renderer id "'.concat(Tt,'" for element "').concat(qe,'"')):(Z._bridge.send("inspectedEleme Line truncated
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}()}function mi(B,z){(z==null||z>B.length)&&(z=B.length);for(var G=0,$=new Array(z);G<z;G++)$[G]=B[G];return $}function Su(B,z){for(var G=0;G<z.length;G++){var $=z[G];$.enumerable=$.enumerable||!1,$.configurable=!0,"value"in $&&($.writable=!0),Object.defineProperty(B,$.key,$)}}function bu(B,z){return(bu=Object.setPrototypeOf||function(G,$){return G.__proto__=$,G})(B,z)}function Pu(B){var z=function(){if(typeof Reflect=="undefined"||!Reflect.construct||Reflect.construct.sham)return!1;if(typeof Proxy=="function")return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(G){return!1}}();return function(){var G,$=Oo(B);if(z){var De=Oo(this).constructor;G=Reflect.construct($,arguments,De)}else G=$.apply(this,arguments);return mu(this,G)}}function mu(B,z){return!z||Dn(z)!=="object"&&typeof z!="function"?yi(B):z}function yi(B){if(B===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return B}function Oo(B){return(Oo=Object.setPrototypeOf?Object.getPrototypeOf:function(z){return z.__proto__||Object.getPrototypeOf(z)})(B)}function Tu(B,z,G){return z in B?Object.defineProperty(B,z,{value:G,enumerable:!0,configurable:!0,writable:!0}):B[z]=G,B}var ao=function(B){(function(xe,Z){if(typeof Z!="function"&&Z!==null)throw new TypeError("Super expression must either be null or a function");xe.prototype=Object.create(Z&&Z.prototype,{constructor:{value:xe,writable:!0,configurable:!0}}),Z&&bu(xe,Z)})(me,B);var z,G,$,De=Pu(me);function me(xe){var Z;return function(ke,Xe){if(!(ke instanceof Xe))throw new TypeError("Cannot call a class as a function")}(this,me),Tu(yi(Z=De.call(this)),"_isShutdown",!1),Tu(yi(Z),"_messageQueue",[]),Tu(yi(Z),"_timeoutID",null),Tu(yi(Z),"_wallUnlisten",null),Tu(yi(Z),"_flush",function(){if(Z._timeoutID!==null&&(clearTimeout(Z._timeoutID),Z._timeoutID=null),Z._messageQueue.length){for(var ke=0;ke<Z._messageQueue.length;ke+=2){var Xe;(Xe=Z._wall).send.apply(Xe,[Z._messageQueue[ke]].concat(or(Z._messageQueue[ke+1])))}Z._messageQueue.length=0,Z._timeoutID=setTimeout(Z._flush,100)}}),Tu(yi(Z),"overrideValueAtPath",function(ke){var Xe=ke.id,ht=ke.path,ie=ke.rendererID,qe=ke.type,tt=ke.value;switch(qe){case"context":Z.send("overrideContext",{id:Xe,path:ht,rendererID:ie,wasForwarded:!0,value:tt});break;case"hooks":Z.send("overrideHookState",{id:Xe,path:ht,rendererID:ie,wasForwarded:!0,value:tt});break;case"props":Z.send("overrideProps",{id:Xe,path:ht,rendererID:ie,wasForwarded:!0,value:tt});break;case"state":Z.send("overrideState",{id:Xe,path:ht,rendererID:ie,wasForwarded:!0,value:tt})}}),Z._wall=xe,Z._wallUnlisten=xe.listen(function(ke){yi(Z).emit(ke.event,ke.payload)})||null,Z.addListener("overrideValueAtPath",Z.overrideValueAtPath),Z}return z=me,(G=[{key:"send",value:function(xe){if(this._isShutdown)console.warn('Cannot send message "'.concat(xe,'" through a Bridge that has been shutdown.'));else{for(var Z=arguments.length,ke=new Array(Z>1?Z-1:0),Xe=1;Xe<Z;Xe++)ke[Xe-1]=arguments[Xe];this._messageQueue.push(xe,ke),this._timeoutID||(this._timeoutID=setTimeout(this._flush,0))}}},{key:"shutdown",value:function(){if(this._isShutdown)console.warn("Bridge was already shutdown.");else{this.send("shutdown"),this._isShutdown=!0,this.addListener=function(){},this.emit=function(){},this.removeAllListeners();var xe=this._wallUnlisten;xe&&xe();do this._flush();while(this._messageQueue.length);this._timeoutID!==null&&(clearTimeout(this._timeoutID),this._timeoutID=null)}}},{key:"wall",get:function(){return this._wall}}])&&Su(z.prototype,G),$&&Su(z,$),me}(E);function Iu(B,z,G){var $=B[z];return B[z]=function(De){return G.call(this,$,arguments)},$}function Oa(B,z){for(var G in z)B[G]=z[G]}function p0(B){typeof B.forceUpdate=="function"?B.forceUpdate():B.updater!=null&&typeof B.updater.enqueueForceUpdate=="function"&&B.updater.enqueueForceUpdate(this,function(){},"forceUpdate")}function Zs(B,z){var G=Object.keys(B);if(Object.getOwnPropertySymbols){var $=Object.getOwnPropertySymbols(B);z&&($=$.filter(function(De){return Object.getOwnPropertyDescriptor(B,De).enumerable})),G.push.apply(G,$)}return G}function K0(B){for(var z=1;z<arguments.length;z++){var G=arguments[z]!=null?arguments[z]:{};z%2?Zs(Object(G),!0).forEach(function($){$s(B,$,G[$])}):Object.getOwnPropertyDescriptors?Object.defineProperties(B,Object.getOwnPropertyDescriptors(G)):Zs(Object(G)).forEach(function($){Object.defineProperty(B,$,Object.getOwnPropertyDescriptor(G,$))})}return B}function $s(B,z,G){return z in B?Object.defineProperty(B,z,{value:G,enumerable:!0,configurable:!0,writable:!0}):B[z]=G,B}function ka(B){return(ka=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(z){return typeof z}:function(z){return z&&typeof Symbol=="function"&&z.constructor===Symbol&&z!==Symbol.prototype?"symbol":typeof z})(B)}function cs(B){var z=null,G=null;if(B._currentElement!=null){B._currentE Line truncated
`:`
`)+f,E=p+1,p=i.indexOf(`
`,E)}while(p!==-1);return t+=i.substr(E),t};FR.exports={stringReplaceAll:iJ,stringEncaseCRLFWithFirstIndex:uJ}});var jR=ce((Fre,PR)=>{"use strict";var oJ=/(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi,IR=/(?:^|\.)(\w+)(?:\(([^)]*)\))?/g,lJ=/^(['"])((?:\\.|(?!\1)[^\\])*)\1$/,sJ=/\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.)|([^\\])/gi,aJ=new Map([["n",`
`],["r","\r"],["t"," "],["b","\b"],["f","\f"],["v","\v"],["0","\0"],["\\","\\"],["e",""],["a","\x07"]]);function BR(i){let o=i[0]==="u",f=i[1]==="{";return o&&!f&&i.length===5||i[0]==="x"&&i.length===3?String.fromCharCode(parseInt(i.slice(1),16)):o&&f?String.fromCodePoint(parseInt(i.slice(2,-1),16)):aJ.get(i)||i}function fJ(i,o){let f=[],p=o.trim().split(/\s*,\s*/g),E;for(let t of p){let k=Number(t);if(!Number.isNaN(k))f.push(k);else if(E=t.match(lJ))f.push(E[2].replace(sJ,(L,N,C)=>N?BR(N):C));else throw new Error(`Invalid Chalk template style argument: ${t} (in style '${i}')`)}return f}function cJ(i){IR.lastIndex=0;let o=[],f;for(;(f=IR.exec(i))!==null;){let p=f[1];if(f[2]){let E=fJ(p,f[2]);o.push([p].concat(E))}else o.push([p])}return o}function UR(i,o){let f={};for(let E of o)for(let t of E.styles)f[t[0]]=E.inverse?null:t.slice(1);let p=i;for(let[E,t]of Object.entries(f))if(!!Array.isArray(t)){if(!(E in p))throw new Error(`Unknown Chalk style: ${E}`);p=t.length>0?p[E](...t):p[E]}return p}PR.exports=(i,o)=>{let f=[],p=[],E=[];if(o.replace(oJ,(t,k,L,N,C,U)=>{if(k)E.push(BR(k));else if(N){let q=E.join("");E=[],p.push(f.length===0?q:UR(i,f)(q)),f.push({inverse:L,styles:cJ(N)})}else if(C){if(f.length===0)throw new Error("Found extraneous } in Chalk template literal");p.push(UR(i,f)(E.join(""))),E=[],f.pop()}else E.push(U)}),p.push(E.join("")),f.length>0){let t=`Chalk template literal is missing ${f.length} closing bracket${f.length===1?"":"s"} (\`}\`)`;throw new Error(t)}return p.join("")}});var u3=ce((bre,zR)=>{"use strict";var Ng=_4(),{stdout:t3,stderr:n3}=LR(),{stringReplaceAll:dJ,stringEncaseCRLFWithFirstIndex:pJ}=bR(),{isArray:M4}=Array,qR=["ansi","ansi","ansi256","ansi16m"],cm=Object.create(null),hJ=(i,o={})=>{if(o.level&&!(Number.isInteger(o.level)&&o.level>=0&&o.level<=3))throw new Error("The `level` option should be an integer from 0 to 3");let f=t3?t3.level:0;i.level=o.level===void 0?f:o.level},HR=class{constructor(o){return WR(o)}},WR=i=>{let o={};return hJ(o,i),o.template=(...f)=>VR(o.template,...f),Object.setPrototypeOf(o,N4.prototype),Object.setPrototypeOf(o.template,o),o.template.constructor=()=>{throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.")},o.template.Instance=HR,o.template};function N4(i){return WR(i)}for(let[i,o]of Object.entries(Ng))cm[i]={get(){let f=L4(this,r3(o.open,o.close,this._styler),this._isEmpty);return Object.defineProperty(this,i,{value:f}),f}};cm.visible={get(){let i=L4(this,this._styler,!0);return Object.defineProperty(this,"visible",{value:i}),i}};var GR=["rgb","hex","keyword","hsl","hsv","hwb","ansi","ansi256"];for(let i of GR)cm[i]={get(){let{level:o}=this;return function(...f){let p=r3(Ng.color[qR[o]][i](...f),Ng.color.close,this._styler);return L4(this,p,this._isEmpty)}}};for(let i of GR){let o="bg"+i[0].toUpperCase()+i.slice(1);cm[o]={get(){let{level:f}=this;return function(...p){let E=r3(Ng.bgColor[qR[f]][i](...p),Ng.bgColor.close,this._styler);return L4(this,E,this._isEmpty)}}}}var vJ=Object.defineProperties(()=>{},Gf(E0({},cm),{level:{enumerable:!0,get(){return this._generator.level},set(i){this._generator.level=i}}})),r3=(i,o,f)=>{let p,E;return f===void 0?(p=i,E=o):(p=f.openAll+i,E=o+f.closeAll),{open:i,close:o,openAll:p,closeAll:E,parent:f}},L4=(i,o,f)=>{let p=(...E)=>M4(E[0])&&M4(E[0].raw)?YR(p,VR(p,...E)):YR(p,E.length===1?""+E[0]:E.join(" "));return Object.setPrototypeOf(p,vJ),p._generator=i,p._styler=o,p._isEmpty=f,p},YR=(i,o)=>{if(i.level<=0||!o)return i._isEmpty?"":o;let f=i._styler;if(f===void 0)return o;let{openAll:p,closeAll:E}=f;if(o.indexOf("")!==-1)for(;f!==void 0;)o=dJ(o,f.close,f.open),f=f.parent;let t=o.indexOf(`
`);return t!==-1&&(o=pJ(o,E,p,t)),p+o+E},i3,VR=(i,...o)=>{let[f]=o;if(!M4(f)||!M4(f.raw))return o.join(" ");let p=o.slice(1),E=[f.raw[0]];for(let t=1;t<f.length;t++)E.push(String(p[t-1]).replace(/[{}\\]/g,"\\$&"),String(f.raw[t]));return i3===void 0&&(i3=jR()),i3(i,E.join(""))};Object.defineProperties(N4.prototype,cm);var F4=N4();F4.supportsColor=t3;F4.stderr=N4({level:n3?n3.level:0});F4.stderr.supportsColor=n3;zR.exports=F4});var o3=ce(Lg=>{"use strict";var mJ=Lg&&Lg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Lg,"__esModule",{value:!0});var Fg=mJ(u3()),yJ=/^(rgb|hsl|hsv|hwb)\(\s?(\d+),\s?(\d+),\s?(\d+)\s?\)$/,gJ=/^(ansi|ansi256)\(\s?(\d+)\s?\)$/,b4=(i,o)=>o==="foreground"?i:"bg"+i[0].toUpperCase()+i.slice(1);Lg.default=(i,o,f)=>{if(!o)return i;if(o in Fg.default){let E=b4(o,f);return Fg.default[E](i)}if(o.startsWith("#")){let E=b4("hex",f);return Fg.default[E](o)(i)}if(o.startsWith("ansi")){let E=gJ.exec(o);if(!E)return i;let t=b4(E[1],f),k=Number(E[2]);return Fg.default[t](k)(i)}if(o.startsWith("rgb")||o.startsWith("hsl")||o.startsWith("hsv")||o.startsWith("hwb")){let E=yJ.exec(o);if(!E)return i;let t=b4(E[1],f),k=Number(E[2]),L=Number(E[3]),N=Number(E[4]);return Fg.default[t](k,L,N)(i)}return i}});var XR=ce(bg=>{"use strict";var KR=bg&&bg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(bg,"__esModule",{value:!0});var _J=KR(RR()),l3=KR(o3());bg.default=(i,o,f,p)=>{if(typeof f.style.borderStyle=="string"){let E=f.yogaNode.getComputedWidth(),t=f.yogaNode.getComputedHeight(),k=f.style.borderColor,L=_J.default[f.style.borderStyle],N=l3.default(L.topLeft+L.horizontal.repeat(E-2)+L.topRight,k,"foreground"),C=(l3.default(L.vertical,k,"foreground")+`
`).repeat(t-2),U=l3.default(L.bottomLeft+L.horizontal.repeat(E-2)+L.bottomRight,k,"foreground");p.write(i,o,N,{transformers:[]}),p.write(i,o+1,C,{transformers:[]}),p.write(i+E-1,o+1,C,{transformers:[]}),p.write(i,o+t-1,U,{transformers:[]})}}});var JR=ce(Pg=>{"use strict";var ih=Pg&&Pg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Pg,"__esModule",{value:!0});var EJ=ih(eh()),DJ=ih(Dw()),wJ=ih(SR()),SJ=ih(kw()),TJ=ih(TR()),CJ=ih(Nw()),xJ=ih(XR()),AJ=(i,o)=>{var f;let p=(f=i.childNodes[0])===null||f===void 0?void 0:f.yogaNode;if(p){let E=p.getComputedLeft(),t=p.getComputedTop();o=`
`.repeat(t)+wJ.default(o,E)}return o},QR=(i,o,f)=>{var p;let{offsetX:E=0,offsetY:t=0,transformers:k=[],skipStaticElements:L}=f;if(L&&i.internal_static)return;let{yogaNode:N}=i;if(N){if(N.getDisplay()===EJ.default.DISPLAY_NONE)return;let C=E+N.getComputedLeft(),U=t+N.getComputedTop(),q=k;if(typeof i.internal_transform=="function"&&(q=[i.internal_transform,...k]),i.nodeName==="ink-text"){let W=CJ.default(i);if(W.length>0){let ne=DJ.default(W),m=TJ.default(N);if(ne>m){let we=(p=i.style.textWrap)!==null&&p!==void 0?p:"wrap";W=SJ.default(W,m,we)}W=AJ(i,W),o.write(C,U,W,{transformers:q})}return}if(i.nodeName==="ink-box"&&xJ.default(C,U,i,o),i.nodeName==="ink-root"||i.nodeName==="ink-box")for(let W of i.childNodes)QR(W,o,{offsetX:C,offsetY:U,transformers:q,skipStaticElements:L})}};Pg.default=QR});var $R=ce((Ure,ZR)=>{"use strict";ZR.exports=i=>{i=Object.assign({onlyFirst:!1},i);let o=["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)","(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"].join("|");return new RegExp(o,i.onlyFirst?void 0:"g")}});var t7=ce((jre,s3)=>{"use strict";var RJ=$R(),e7=i=>typeof i=="string"?i.replace(RJ(),""):i;s3.exports=e7;s3.exports.default=e7});var i7=ce((zre,n7)=>{"use strict";var r7="[\uD800-\uDBFF][\uDC00-\uDFFF]";n7.exports=i=>i&&i.exact?new RegExp(`^${r7}$`):new RegExp(r7,"g")});var o7=ce((qre,a3)=>{"use strict";var OJ=t7(),kJ=i7(),u7=i=>OJ(i).replace(kJ()," ").length;a3.exports=u7;a3.exports.default=u7});var f7=ce(Ig=>{"use strict";var l7=Ig&&Ig.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Ig,"__esModule",{value:!0});var s7=l7(Rw()),MJ=l7(o7()),a7=class{constructor(o){this.writes=[];let{width:f,height:p}=o;this.width=f,this.height=p}write(o,f,p,E){let{transformers:t}=E;!p||this.writes.push({x:o,y:f,text:p,transformers:t})}get(){let o=[];for(let p=0;p<this.height;p++)o.push(" ".repeat(this.width));for(let p of this.writes){let{x:E,y:t,text:k,transformers:L}=p,N=k.split(`
`),C=0;for(let U of N){let q=o[t+C];if(!q)continue;let W=MJ.default(U);for(let ne of L)U=ne(U);o[t+C]=s7.default(q,0,E)+U+s7.default(q,E+W),C++}}return{output:o.map(p=>p.trimRight()).join(`
`),height:o.length}}};Ig.default=a7});var p7=ce(Bg=>{"use strict";var f3=Bg&&Bg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Bg,"__esModule",{value:!0});var NJ=f3(eh()),c7=f3(JR()),d7=f3(f7());Bg.default=(i,o)=>{var f;if(i.yogaNode.setWidth(o),i.yogaNode){i.yogaNode.calculateLayout(void 0,void 0,NJ.default.DIRECTION_LTR);let p=new d7.default({width:i.yogaNode.getComputedWidth(),height:i.yogaNode.getComputedHeight()});c7.default(i,p,{skipStaticElements:!0});let E;((f=i.staticNode)===null||f===void 0?void 0:f.yogaNode)&&(E=new d7.default({width:i.staticNode.yogaNode.getComputedWidth(),height:i.staticNode.yogaNode.getComputedHeight()}),c7.default(i.staticNode,E,{skipStaticElements:!1}));let{output:t,height:k}=p.get();return{output:t,outputHeight:k,staticOutput:E?`${E.get().output}
`:""}}return{output:"",outputHeight:0,staticOutput:""}}});var y7=ce((Vre,h7)=>{"use strict";var v7=require("stream"),m7=["assert","count","countReset","debug","dir","dirxml","error","group","groupCollapsed","groupEnd","info","log","table","time","timeEnd","timeLog","trace","warn"],c3={},LJ=i=>{let o=new v7.PassThrough,f=new v7.PassThrough;o.write=E=>i("stdout",E),f.write=E=>i("stderr",E);let p=new console.Console(o,f);for(let E of m7)c3[E]=console[E],console[E]=p[E];return()=>{for(let E of m7)console[E]=c3[E];c3={}}};h7.exports=LJ});var p3=ce(d3=>{"use strict";Object.defineProperty(d3,"__esModule",{value:!0});d3.default=new WeakMap});var v3=ce(h3=>{"use strict";Object.defineProperty(h3,"__esModule",{value:!0});var FJ=su(),g7=FJ.createContext({exit:()=>{}});g7.displayName="InternalAppContext";h3.default=g7});var y3=ce(m3=>{"use strict";Object.defineProperty(m3,"__esModule",{value:!0});var bJ=su(),_7=bJ.createContext({stdin:void 0,setRawMode:()=>{},isRawModeSupported:!1,internal_exitOnCtrlC:!0});_7.displayName="InternalStdinContext";m3.default=_7});var _3=ce(g3=>{"use strict";Object.defineProperty(g3,"__esModule",{value:!0});var PJ=su(),E7=PJ.createContext({stdout:void 0,write:()=>{}});E7.displayName="InternalStdoutContext";g3.default=E7});var D3=ce(E3=>{"use strict";Object.defineProperty(E3,"__esModule",{value:!0});var IJ=su(),D7=IJ.createContext({stderr:void 0,write:()=>{}});D7.displayName="InternalStderrContext";E3.default=D7});var P4=ce(w3=>{"use strict";Object.defineProperty(w3,"__esModule",{value:!0});var BJ=su(),w7=BJ.createContext({activeId:void 0,add:()=>{},remove:()=>{},activate:()=>{},deactivate:()=>{},enableFocus:()=>{},disableFocus:()=>{},focusNext:()=>{},focusPrevious:()=>{}});w7.displayName="InternalFocusContext";w3.default=w7});var T7=ce((Zre,S7)=>{"use strict";var UJ=/[|\\{}()[\]^$+*?.-]/g;S7.exports=i=>{if(typeof i!="string")throw new TypeError("Expected a string");return i.replace(UJ,"\\$&")}});var R7=ce(($re,C7)=>{"use strict";var jJ=T7(),x7=[].concat(require("module").builtinModules,"bootstrap_node","node").map(i=>new RegExp(`(?:\\(${i}\\.js:\\d+:\\d+\\)$|^\\s*at ${i}\\.js:\\d+:\\d+$)`));x7.push(/\(internal\/[^:]+:\d+:\d+\)$/,/\s*at internal\/[^:]+:\d+:\d+$/,/\/\.node-spawn-wrap-\w+-\w+\/node:\d+:\d+\)?$/);var I4=class{constructor(o){o=E0({ignoredPackages:[]},o),"internals"in o||(o.internals=I4.nodeInternals()),"cwd"in o||(o.cwd=process.cwd()),this._cwd=o.cwd.replace(/\\/g,"/"),this._internals=[].concat(o.internals,zJ(o.ignoredPackages)),this._wrapCallSite=o.wrapCallSite||!1}static nodeInternals(){return[...x7]}clean(o,f=0){f=" ".repeat(f),Array.isArray(o)||(o=o.split(`
`)),!/^\s*at /.test(o[0])&&/^\s*at /.test(o[1])&&(o=o.slice(1));let p=!1,E=null,t=[];return o.forEach(k=>{if(k=k.replace(/\\/g,"/"),this._internals.some(N=>N.test(k)))return;let L=/^\s*at /.test(k);p?k=k.trimEnd().replace(/^(\s+)at /,"$1"):(k=k.trim(),L&&(k=k.slice(3))),k=k.replace(`${this._cwd}/`,""),k&&(L?(E&&(t.push(E),E=null),t.push(k)):(p=!0,E=k))}),t.map(k=>`${f}${k}
`).join("")}captureString(o,f=this.captureString){typeof o=="function"&&(f=o,o=Infinity);let{stackTraceLimit:p}=Error;o&&(Error.stackTraceLimit=o);let E={};Error.captureStackTrace(E,f);let{stack:t}=E;return Error.stackTraceLimit=p,this.clean(t)}capture(o,f=this.capture){typeof o=="function"&&(f=o,o=Infinity);let{prepareStackTrace:p,stackTraceLimit:E}=Error;Error.prepareStackTrace=(L,N)=>this._wrapCallSite?N.map(this._wrapCallSite):N,o&&(Error.stackTraceLimit=o);let t={};Error.captureStackTrace(t,f);let{stack:k}=t;return Object.assign(Error,{prepareStackTrace:p,stackTraceLimit:E}),k}at(o=this.at){let[f]=this.capture(1,o);if(!f)return{};let p={line:f.getLineNumber(),column:f.getColumnNumber()};A7(p,f.getFileName(),this._cwd),f.isConstructor()&&(p.constructor=!0),f.isEval()&&(p.evalOrigin=f.getEvalOrigin()),f.isNative()&&(p.native=!0);let E;try{E=f.getTypeName()}catch(L){}E&&E!=="Object"&&E!=="[object Object]"&&(p.type=E);let t=f.getFunctionName();t&&(p.function=t);let k=f.getMethodName();return k&&t!==k&&(p.method=k),p}parseLine(o){let f=o&&o.match(qJ);if(!f)return null;let p=f[1]==="new",E=f[2],t=f[3],k=f[4],L=Number(f[5]),N=Number(f[6]),C=f[7],U=f[8],q=f[9],W=f[10]==="native",ne=f[11]===")",m,we={};if(U&&(we.line=Number(U)),q&&(we.column=Number(q)),ne&&C){let Se=0;for(let he=C.length-1;he>0;he--)if(C.charAt(he)===")")Se++;else if(C.charAt(he)==="("&&C.charAt(he-1)===" "&&(Se--,Se===-1&&C.charAt(he-1)===" ")){let ge=C.slice(0,he-1);C=C.slice(he+1),E+=` (${ge}`;break}}if(E){let Se=E.match(HJ);Se&&(E=Se[1],m=Se[2])}return A7(we,C,this._cwd),p&&(we.constructor=!0),t&&(we.evalOrigin=t,we.evalLine=L,we.evalColumn=N,we.evalFile=k&&k.replace(/\\/g,"/")),W&&(we.native=!0),E&&(we.function=E),m&&E!==m&&(we.method=m),we}};function A7(i,o,f){o&&(o=o.replace(/\\/g,"/"),o.startsWith(`${f}/`)&&(o=o.slice(f.length+1)),i.file=o)}function zJ(i){if(i.length===0)return[];let o=i.map(f=>jJ(f));return new RegExp(`[/\\\\]node_modules[/\\\\](?:${o.join("|")})[/\\\\][^:]+:\\d+:\\d+`)}var qJ=new RegExp("^(?:\\s*at )?(?:(new) )?(?:(.*?) \\()?(?:eval at ([^ ]+) \\((.+?):(\\d+):(\\d+)\\), )?(?:(.+?):(\\d+):(\\d+)|(native))(\\)?)$"),HJ=/^(.*?) \[as (.*?)\]$/;C7.exports=I4});var k7=ce((eie,O7)=>{"use strict";O7.exports=(i,o)=>i.replace(/^\t+/gm,f=>" ".repeat(f.length*(o||2)))});var N7=ce((tie,M7)=>{"use strict";var WJ=k7(),VJ=(i,o)=>{let f=[],p=i-o,E=i+o;for(let t=p;t<=E;t++)f.push(t);return f};M7.exports=(i,o,f)=>{if(typeof i!="string")throw new TypeError("Source code is missing.");if(!o||o<1)throw new TypeError("Line number must start from `1`.");if(i=WJ(i).split(/\r?\n/),!(o>i.length))return f=E0({around:3},f),VJ(o,f.around).filter(p=>i[p-1]!==void 0).map(p=>({line:p,value:i[p-1]}))}});var B4=ce(Zf=>{"use strict";var GJ=Zf&&Zf.__createBinding||(Object.create?function(i,o,f,p){p===void 0&&(p=f),Object.defineProperty(i,p,{enumerable:!0,get:function(){return o[f]}})}:function(i,o,f,p){p===void 0&&(p=f),i[p]=o[f]}),YJ=Zf&&Zf.__setModuleDefault||(Object.create?function(i,o){Object.defineProperty(i,"default",{enumerable:!0,value:o})}:function(i,o){i.default=o}),KJ=Zf&&Zf.__importStar||function(i){if(i&&i.__esModule)return i;var o={};if(i!=null)for(var f in i)f!=="default"&&Object.hasOwnProperty.call(i,f)&&GJ(o,i,f);return YJ(o,i),o},XJ=Zf&&Zf.__rest||function(i,o){var f={};for(var p in i)Object.prototype.hasOwnProperty.call(i,p)&&o.indexOf(p)<0&&(f[p]=i[p]);if(i!=null&&typeof Object.getOwnPropertySymbols=="function")for(var E=0,p=Object.getOwnPropertySymbols(i);E<p.length;E++)o.indexOf(p[E])<0&&Object.prototype.propertyIsEnumerable.call(i,p[E])&&(f[p[E]]=i[p[E]]);return f};Object.defineProperty(Zf,"__esModule",{value:!0});var L7=KJ(su()),S3=L7.forwardRef((i,o)=>{var{children:f}=i,p=XJ(i,["children"]);let E=Object.assign(Object.assign({},p),{marginLeft:p.marginLeft||p.marginX||p.margin||0,marginRight:p.marginRight||p.marginX||p.margin||0,marginTop:p.marginTop||p.marginY||p.margin||0,marginBottom:p.marginBottom||p.marginY||p.margin||0,paddingLeft:p.paddingLeft||p.paddingX||p.padding||0,paddingRight:p.paddingRight||p.paddingX||p.padding||0,paddingTop:p.paddingTop||p.paddingY||p.padding||0,paddingBottom:p.paddingBottom||p.paddingY||p.padding||0});return L7.default.createElement("ink-box",{ref:o,style:E},f)});S3.displayName="Box";S3.defaultProps={flexDirection:"row",flexGrow:0,flexShrink:1};Zf.default=S3});var x3=ce(Ug=>{"use strict";var T3=Ug&&Ug.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Ug,"__esModule",{value:!0});var QJ=T3(su()),dm=T3(u3()),F7=T3(o3()),C3=({color:i,backgroundColor:o,dimColor:f,bold:p,italic:E,underline:t,strikethrough:k,inverse:L,wrap:N,children:C})=>{if(C==null)return null;let U=q=>(f&&(q=dm.default.dim(q)),i&&(q=F7.default(q,i,"foreground")),o&&(q=F7.default(q,o,"background")),p&&(q=dm.default.bold(q)),E&&(q=dm.default.italic(q)),t&&(q=dm.default.underline(q)),k&&(q=dm.default.strikethrough(q)),L&&(q=dm.default.inverse(q)),q);return QJ.default.createElement("ink-text",{style:{fLine truncated
`).slice(1):void 0,f=o?I7.parseLine(o[0]):void 0,p,E=0;if((f==null?void 0:f.file)&&(f==null?void 0:f.line)&&b7.existsSync(f.file)){let t=b7.readFileSync(f.file,"utf8");if(p=eZ.default(t,f.line),p)for(let{line:k}of p)E=Math.max(E,String(k).length)}return Vo.default.createElement(Q1.default,{flexDirection:"column",padding:1},Vo.default.createElement(Q1.default,null,Vo.default.createElement(Ic.default,{backgroundColor:"red",color:"white"}," ","ERROR"," "),Vo.default.createElement(Ic.default,null," ",i.message)),f&&Vo.default.createElement(Q1.default,{marginTop:1},Vo.default.createElement(Ic.default,{dimColor:!0},f.file,":",f.line,":",f.column)),f&&p&&Vo.default.createElement(Q1.default,{marginTop:1,flexDirection:"column"},p.map(({line:t,value:k})=>Vo.default.createElement(Q1.default,{key:t},Vo.default.createElement(Q1.default,{width:E+1},Vo.default.createElement(Ic.default,{dimColor:t!==f.line,backgroundColor:t===f.line?"red":void 0,color:t===f.line?"white":void 0},String(t).padStart(E," "),":")),Vo.default.createElement(Ic.default,{key:t,backgroundColor:t===f.line?"red":void 0,color:t===f.line?"white":void 0}," "+k)))),i.stack&&Vo.default.createElement(Q1.default,{marginTop:1,flexDirection:"column"},i.stack.split(`
`).slice(1).map(t=>{let k=I7.parseLine(t);return k?Vo.default.createElement(Q1.default,{key:t},Vo.default.createElement(Ic.default,{dimColor:!0},"- "),Vo.default.createElement(Ic.default,{dimColor:!0,bold:!0},k.function),Vo.default.createElement(Ic.default,{dimColor:!0,color:"gray"}," ","(",k.file,":",k.line,":",k.column,")")):Vo.default.createElement(Q1.default,{key:t},Vo.default.createElement(Ic.default,{dimColor:!0},"- "),Vo.default.createElement(Ic.default,{dimColor:!0,bold:!0},t))})))};$f.default=tZ});var j7=ce(ec=>{"use strict";var nZ=ec&&ec.__createBinding||(Object.create?function(i,o,f,p){p===void 0&&(p=f),Object.defineProperty(i,p,{enumerable:!0,get:function(){return o[f]}})}:function(i,o,f,p){p===void 0&&(p=f),i[p]=o[f]}),rZ=ec&&ec.__setModuleDefault||(Object.create?function(i,o){Object.defineProperty(i,"default",{enumerable:!0,value:o})}:function(i,o){i.default=o}),iZ=ec&&ec.__importStar||function(i){if(i&&i.__esModule)return i;var o={};if(i!=null)for(var f in i)f!=="default"&&Object.hasOwnProperty.call(i,f)&&nZ(o,i,f);return rZ(o,i),o},uh=ec&&ec.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(ec,"__esModule",{value:!0});var oh=iZ(su()),U7=uh(ZD()),uZ=uh(v3()),oZ=uh(y3()),lZ=uh(_3()),sZ=uh(D3()),aZ=uh(P4()),fZ=uh(B7()),cZ=" ",dZ="",pZ="",A3=class extends oh.PureComponent{constructor(){super(...arguments);this.state={isFocusEnabled:!0,activeFocusId:void 0,focusables:[],error:void 0},this.rawModeEnabledCount=0,this.handleSetRawMode=o=>{let{stdin:f}=this.props;if(!this.isRawModeSupported())throw f===process.stdin?new Error(`Raw mode is not supported on the current process.stdin, which Ink uses as input stream by default.
Read about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported`):new Error(`Raw mode is not supported on the stdin provided to Ink.
Read about how to prevent this error on https://github.com/vadimdemedes/ink/#israwmodesupported`);if(f.setEncoding("utf8"),o){this.rawModeEnabledCount===0&&(f.addListener("data",this.handleInput),f.resume(),f.setRawMode(!0)),this.rawModeEnabledCount++;return}--this.rawModeEnabledCount==0&&(f.setRawMode(!1),f.removeListener("data",this.handleInput),f.pause())},this.handleInput=o=>{o===""&&this.props.exitOnCtrlC&&this.handleExit(),o===pZ&&this.state.activeFocusId&&this.setState({activeFocusId:void 0}),this.state.isFocusEnabled&&this.state.focusables.length>0&&(o===cZ&&this.focusNext(),o===dZ&&this.focusPrevious())},this.handleExit=o=>{this.isRawModeSupported()&&this.handleSetRawMode(!1),this.props.onExit(o)},this.enableFocus=()=>{this.setState({isFocusEnabled:!0})},this.disableFocus=()=>{this.setState({isFocusEnabled:!1})},this.focusNext=()=>{this.setState(o=>{let f=o.focusables[0].id;return{activeFocusId:this.findNextFocusable(o)||f}})},this.focusPrevious=()=>{this.setState(o=>{let f=o.focusables[o.focusables.length-1].id;return{activeFocusId:this.findPreviousFocusable(o)||f}})},this.addFocusable=(o,{autoFocus:f})=>{this.setState(p=>{let E=p.activeFocusId;return!E&&f&&(E=o),{activeFocusId:E,focusables:[...p.focusables,{id:o,isActive:!0}]}})},this.removeFocusable=o=>{this.setState(f=>({activeFocusId:f.activeFocusId===o?void 0:f.activeFocusId,focusables:f.focusables.filter(p=>p.id!==o)}))},this.activateFocusable=o=>{this.setState(f=>({focusables:f.focusables.map(p=>p.id!==o?p:{id:o,isActive:!0})}))},this.deactivateFocusable=o=>{this.setState(f=>({activeFocusId:f.activeFocusId===o?void 0:f.activeFocusId,focusables:f.focusables.map(p=>p.id!==o?p:{id:o,isActive:!1})}))},this.findNextFocusable=o=>{let f=o.focusables.findIndex(p=>p.id===o.activeFocusId);for(let p=f+1;p<o.focusables.length;p++)if(o.focusables[p].isActive)return o.focusables[p].id},this.findPreviousFocusable=o=>{let f=o.focusables.findIndex(p=>p.id===o.activeFocusId);for(let p=f-1;p>=0;p--)if(o.focusables[p].isActive)return o.focusables[p].id}}static getDerivedStateFromError(o){return{error:o}}isRawModeSupported(){return this.props.stdin.isTTY}render(){return oh.default.createElement(uZ.default.Provider,{value:{exit:this.handleExit}},oh.default.createElement(oZ.default.Provider,{value:{stdin:this.props.stdin,setRawMode:this.handleSetRawMode,isRawModeSupported:this.isRawModeSupported(),internal_exitOnCtrlC:this.props.exitOnCtrlC}},oh.default.createElement(lZ.default.Provider,{value:{stdout:this.props.stdout,write:this.props.writeToStdout}},oh.default.createElement(sZ.default.Provider,{value:{stderr:this.props.stderr,write:this.props.writeToStderr}},oh.default.createElement(aZ.default.Provider,{value:{activeId:this.state.activeFocusId,add:this.addFocusable,remove:this.removeFocusable,activate:this.activateFocusable,deactivate:this.deactivateFocusable,enableFocus:this.enableFocus,disableFocus:this.disableFocus,focusNext:this.focusNext,focusPrevious:this.focusPrevious}},this.state.error?oh.default.createElement(fZ.default,{error:this.state.error}):this.props.children)))))}componentDidMount(){U7.default.hide(this.props.stdout)}componentWillUnmount(){U7.default.show(this.props.stdout),this.isRawModeSupported()&&this.handleSetRawMode(!1)}componentDidCatch(o){this.handleExit(o)}};ec.default=A3;A3.displayName="InternalApp"});var W7=ce(tc=>{"use strict";var hZ=tc&&tc.__createBinding||(Object.create?function(i,o,f,p){p===void 0&&(p=f),Object.defineProperty(i,p,{enumerable:!0,get:function(){return o[f]}})}:function(i,o,f,p){p===void 0&&(p=f),i[p]=o[f]}),vZ=tc&&tc.__setModuleDefault||(Object.create?function(i,o){Object.defineProperty(i,"default",{enumerable:!0,value:o})}:function(i,o){i.default=o}),mZ=tc&&tc.__importStar||function(i){if(i&&i.__esModule)return i;var o={};if(i!=null)for(var f in i)f!=="default"&&Object.hasOwnProperty.call(i,f)&&hZ(o,i,f);return vZ(o,i),o},nc=tc&&tc.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(tc,"__esModule",{value:!0});var yZ=nc(su()),z7=B5(),gZ=nc(Z5()),_Z=nc(GD()),EZ=nc(u9()),DZ=nc(l9()),U4=nc(DR()),wZ=nc(p7()),SZ=nc(JD()),TZ=nc(y7()),CZ=mZ(Lw()),xZ=nc(p3()),AZ=nc(j7()),pm=process.env.CI==="false"?!1:EZ.default,q7=()=>{},H7=class{constructor(o){this.resolveExitPromise=()=>{},this.rejectExitPromise=()=>{},this.unsubscribeExit=()=>{},this.onRender=()=>{if(this.isUnmounted)return;let{output:f,outputHeight:p,staticOutput:E}=wZ.default(this.rootNode,this.options.stdout.columns||80),t=E&&E!==`
`;if(this.options.debug){t&&(this.fullStaticOutput+=E),this.options.stdout.write(this.fullStaticOutput+f);return}if(pm){t&&this.options.stdout.write(E),this.lastOutput=f;return}if(t&&(this.fullStaticOutput+=E),p>=this.options.stdout.rows){this.options.stdout.write(_Z.default.clearTerminal+this.fullStaticOutput+f),this.lastOutput=f;return}t&&(this.log.clear(),this.options.stdout.write(E),this.log(f)),!t&&f!==this.lastOutput&&this.throttledLog(f),this.lastOutput=f},DZ.default(this),this.options=o,this.rootNode=CZ.createNode("ink-root"),this.rootNode.onRender=o.debug?this.onRender:z7.throttle(this.onRender,32,{leading:!0,trailing:!0}),this.rootNode.onImmediateRender=this.onRender,this.log=gZ.default.create(o.stdout),this.throttledLog=o.debug?this.log:z7.throttle(this.log,void 0,{leading:!0,trailing:!0}),this.isUnmounted=!1,this.lastOutput="",this.fullStaticOutput="",this.container=U4.default.createContainer(this.rootNode,!1,!1),this.unsubscribeExit=SZ.default(this.unmount,{alwaysLast:!1}),process.env.DEV==="true"&&U4.default.injectIntoDevTools({bundleType:0,version:"16.13.1",rendererPackageName:"ink"}),o.patchConsole&&this.patchConsole(),pm||(o.stdout.on("resize",this.onRender),this.unsubscribeResize=()=>{o.stdout.off("resize",this.onRender)})}render(o){let f=yZ.default.createElement(AZ.default,{stdin:this.options.stdin,stdout:this.options.stdout,stderr:this.options.stderr,writeToStdout:this.writeToStdout,writeToStderr:this.writeToStderr,exitOnCtrlC:this.options.exitOnCtrlC,onExit:this.unmount},o);U4.default.updateContainer(f,this.container,null,q7)}writeToStdout(o){if(!this.isUnmounted){if(this.options.debug){this.options.stdout.write(o+this.fullStaticOutput+this.lastOutput);return}if(pm){this.options.stdout.write(o);return}this.log.clear(),this.options.stdout.write(o),this.log(this.lastOutput)}}writeToStderr(o){if(!this.isUnmounted){if(this.options.debug){this.options.stderr.write(o),this.options.stdout.write(this.fullStaticOutput+this.lastOutput);return}if(pm){this.options.stderr.write(o);return}this.log.clear(),this.options.stderr.write(o),this.log(this.lastOutput)}}unmount(o){this.isUnmounted||(this.onRender(),this.unsubscribeExit(),typeof this.restoreConsole=="function"&&this.restoreConsole(),typeof this.unsubscribeResize=="function"&&this.unsubscribeResize(),pm?this.options.stdout.write(this.lastOutput+`
`):this.options.debug||this.log.done(),this.isUnmounted=!0,U4.default.updateContainer(null,this.container,null,q7),xZ.default.delete(this.options.stdout),o instanceof Error?this.rejectExitPromise(o):this.resolveExitPromise())}waitUntilExit(){return this.exitPromise||(this.exitPromise=new Promise((o,f)=>{this.resolveExitPromise=o,this.rejectExitPromise=f})),this.exitPromise}clear(){!pm&&!this.options.debug&&this.log.clear()}patchConsole(){this.options.debug||(this.restoreConsole=TZ.default((o,f)=>{o==="stdout"&&this.writeToStdout(f),o==="stderr"&&(f.startsWith("The above error occurred")||this.writeToStderr(f))}))}};tc.default=H7});var G7=ce(zg=>{"use strict";var V7=zg&&zg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(zg,"__esModule",{value:!0});var RZ=V7(W7()),j4=V7(p3()),OZ=require("stream"),NZ=(i,o)=>{let f=Object.assign({stdout:process.stdout,stdin:process.stdin,stderr:process.stderr,debug:!1,exitOnCtrlC:!0,patchConsole:!0},kZ(o)),p=MZ(f.stdout,()=>new RZ.default(f));return p.render(i),{rerender:p.render,unmount:()=>p.unmount(),waitUntilExit:p.waitUntilExit,cleanup:()=>j4.default.delete(f.stdout),clear:p.clear}};zg.default=NZ;var kZ=(i={})=>i instanceof OZ.Stream?{stdout:i,stdin:process.stdin}:i,MZ=(i,o)=>{let f;return j4.default.has(i)?f=j4.default.get(i):(f=o(),j4.default.set(i,f)),f}});var K7=ce(J1=>{"use strict";var LZ=J1&&J1.__createBinding||(Object.create?function(i,o,f,p){p===void 0&&(p=f),Object.defineProperty(i,p,{enumerable:!0,get:function(){return o[f]}})}:function(i,o,f,p){p===void 0&&(p=f),i[p]=o[f]}),FZ=J1&&J1.__setModuleDefault||(Object.create?function(i,o){Object.defineProperty(i,"default",{enumerable:!0,value:o})}:function(i,o){i.default=o}),bZ=J1&&J1.__importStar||function(i){if(i&&i.__esModule)return i;var o={};if(i!=null)for(var f in i)f!=="default"&&Object.hasOwnProperty.call(i,f)&&LZ(o,i,f);return FZ(o,i),o};Object.defineProperty(J1,"__esModule",{value:!0});var qg=bZ(su()),Y7=i=>{let{items:o,children:f,style:p}=i,[E,t]=qg.useState(0),k=qg.useMemo(()=>o.slice(E),[o,E]);qg.useLayoutEffect(()=>{t(o.length)},[o.length]);let L=k.map((C,U)=>f(C,E+U)),N=qg.useMemo(()=>Object.assign({position:"absolute",flexDirection:"column"},p),[p]);return qg.default.createElement("ink-box",{internal_static:!0,style:N},L)};Y7.displayName="Static";J1.default=Y7});var Q7=ce(Hg=>{"use strict";var PZ=Hg&&Hg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Hg,"__esModule",{value:!0});var IZ=PZ(su()),X7=({children:i,transform:o})=>i==null?null:IZ.default.createElement("ink-text",{style:{flexGrow:0,flexShrink:1,flexDirection:"row"},internal_transform:o},i);X7.displayName="Transform";Hg.default=X7});var Z7=ce(Wg=>{"use strict";var BZ=Wg&&Wg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Wg,"__esModule",{value:!0});var UZ=BZ(su()),J7=({count:i=1})=>UZ.default.createElement("ink-text",null,`
`.repeat(i));J7.displayName="Newline";Wg.default=J7});var tO=ce(Vg=>{"use strict";var $7=Vg&&Vg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Vg,"__esModule",{value:!0});var jZ=$7(su()),zZ=$7(B4()),eO=()=>jZ.default.createElement(zZ.default,{flexGrow:1});eO.displayName="Spacer";Vg.default=eO});var z4=ce(Gg=>{"use strict";var qZ=Gg&&Gg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Gg,"__esModule",{value:!0});var HZ=su(),WZ=qZ(y3()),VZ=()=>HZ.useContext(WZ.default);Gg.default=VZ});var rO=ce(Yg=>{"use strict";var GZ=Yg&&Yg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Yg,"__esModule",{value:!0});var nO=su(),YZ=GZ(z4()),KZ=(i,o={})=>{let{stdin:f,setRawMode:p,internal_exitOnCtrlC:E}=YZ.default();nO.useEffect(()=>{if(o.isActive!==!1)return p(!0),()=>{p(!1)}},[o.isActive,p]),nO.useEffect(()=>{if(o.isActive===!1)return;let t=k=>{let L=String(k),N={upArrow:L==="",downArrow:L==="",leftArrow:L==="",rightArrow:L==="",pageDown:L==="[6~",pageUp:L==="[5~",return:L==="\r",escape:L==="",ctrl:!1,shift:!1,tab:L===" "||L==="",backspace:L==="\b",delete:L==="\x7F"||L==="[3~",meta:!1};L<=""&&!N.return&&(L=String.fromCharCode(L.charCodeAt(0)+"a".charCodeAt(0)-1),N.ctrl=!0),L.startsWith("")&&(L=L.slice(1),N.meta=!0);let C=L>="A"&&L<="Z",U=L>="\u0410"&&L<="\u042F";L.length===1&&(C||U)&&(N.shift=!0),N.tab&&L==="[Z"&&(N.shift=!0),(N.tab||N.backspace||N.delete)&&(L=""),(!(L==="c"&&N.ctrl)||!E)&&i(L,N)};return f==null||f.on("data",t),()=>{f==null||f.off("data",t)}},[o.isActive,f,E,i])};Yg.default=KZ});var iO=ce(Kg=>{"use strict";var XZ=Kg&&Kg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Kg,"__esModule",{value:!0});var QZ=su(),JZ=XZ(v3()),ZZ=()=>QZ.useContext(JZ.default);Kg.default=ZZ});var uO=ce(Xg=>{"use strict";var $Z=Xg&&Xg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Xg,"__esModule",{value:!0});var e$=su(),t$=$Z(_3()),n$=()=>e$.useContext(t$.default);Xg.default=n$});var oO=ce(Qg=>{"use strict";var r$=Qg&&Qg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Qg,"__esModule",{value:!0});var i$=su(),u$=r$(D3()),o$=()=>i$.useContext(u$.default);Qg.default=o$});var sO=ce(Jg=>{"use strict";var lO=Jg&&Jg.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(Jg,"__esModule",{value:!0});var Zg=su(),l$=lO(P4()),s$=lO(z4()),a$=({isActive:i=!0,autoFocus:o=!1}={})=>{let{isRawModeSupported:f,setRawMode:p}=s$.default(),{activeId:E,add:t,remove:k,activate:L,deactivate:N}=Zg.useContext(l$.default),C=Zg.useMemo(()=>Math.random().toString().slice(2,7),[]);return Zg.useEffect(()=>(t(C,{autoFocus:o}),()=>{k(C)}),[C,o]),Zg.useEffect(()=>{i?L(C):N(C)},[i,C]),Zg.useEffect(()=>{if(!(!f||!i))return p(!0),()=>{p(!1)}},[i]),{isFocused:Boolean(C)&&E===C}};Jg.default=a$});var aO=ce($g=>{"use strict";var f$=$g&&$g.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty($g,"__esModule",{value:!0});var c$=su(),d$=f$(P4()),p$=()=>{let i=c$.useContext(d$.default);return{enableFocus:i.enableFocus,disableFocus:i.disableFocus,focusNext:i.focusNext,focusPrevious:i.focusPrevious}};$g.default=p$});var fO=ce(R3=>{"use strict";Object.defineProperty(R3,"__esModule",{value:!0});R3.default=i=>{var o,f,p,E;return{width:(f=(o=i.yogaNode)===null||o===void 0?void 0:o.getComputedWidth())!==null&&f!==void 0?f:0,height:(E=(p=i.yogaNode)===null||p===void 0?void 0:p.getComputedHeight())!==null&&E!==void 0?E:0}}});var lh=ce(ql=>{"use strict";Object.defineProperty(ql,"__esModule",{value:!0});var h$=G7();Object.defineProperty(ql,"render",{enumerable:!0,get:function(){return h$.default}});var v$=B4();Object.defineProperty(ql,"Box",{enumerable:!0,get:function(){return v$.default}});var m$=x3();Object.defineProperty(ql,"Text",{enumerable:!0,get:function(){return m$.default}});var y$=K7();Object.defineProperty(ql,"Static",{enumerable:!0,get:function(){return y$.default}});var g$=Q7();Object.defineProperty(ql,"Transform",{enumerable:!0,get:function(){return g$.default}});var _$=Z7();Object.defineProperty(ql,"Newline",{enumerable:!0,get:function(){return _$.default}});var E$=tO();Object.defineProperty(ql,"Spacer",{enumerable:!0,get:function(){return E$.default}});var D$=rO();Object.defineProperty(ql,"useInput",{enumerable:!0,get:function(){return D$.default}});var w$=iO();Object.defineProperty(ql,"useApp",{enumerable:!0,get:function(){return w$.default}});var S$=z4();Object.defineProperty(ql,"useStdin",{enumerable:!0,get:function(){return S$.default}});var T$=uO();Object.defineProperty(ql,"useStdout",{enumerable:!0,get:function(){return T$.default}});var C$=oO();Object.defineProperty(ql,"useStderr",{enumerable:!0,get:function(){return C$.default}});var x$=sO();Object.defineProperty(ql,"useFocus",{enumerable:!0,get:function(){return x$.default}});var A$=aO() Line truncated
- ${L.map(ne=>D0.npath.fromPortablePath(ne)).join(`
- `)}`);let N=new Set(Gi.miscUtils.mapAndFilter(t,ne=>{let m=i.tryWorkspaceByFilePath(ne);return m===null?Gi.miscUtils.mapAndFilter.skip:m}));if(L.length===0&&N.size===0&&!o)return null;let C=L.length===1?L[0]:D0.ppath.join(k,`${Gi.hashUtils.makeHash(Math.random().toString()).slice(0,8)}.yml`),U=D0.xfs.existsSync(C)?await D0.xfs.readFilePromise(C,"utf8"):"{}",q=(0,W1.parseSyml)(U),W=new Map;for(let ne of q.declined||[]){let m=Gi.structUtils.parseIdent(ne),we=i.getWorkspaceByIdent(m);W.set(we,Nu.DECLINE)}for(let[ne,m]of Object.entries(q.releases||{})){let we=Gi.structUtils.parseIdent(ne),Se=i.getWorkspaceByIdent(we);W.set(Se,Zv(m))}return{project:i,root:p,baseHash:E!==null?E.hash:null,baseTitle:E!==null?E.title:null,changedFiles:new Set(t),changedWorkspaces:N,releaseRoots:new Set([...N].filter(ne=>ne.manifest.version!==null)),releases:W,async saveAll(){let ne={},m=[],we=[];for(let Se of i.workspaces){if(Se.manifest.version===null)continue;let he=Gi.structUtils.stringifyIdent(Se.locator),ge=W.get(Se);ge===Nu.DECLINE?m.push(he):typeof ge!="undefined"?ne[he]=Zv(ge):N.has(Se)&&we.push(he)}await D0.xfs.mkdirPromise(D0.ppath.dirname(C),{recursive:!0}),await D0.xfs.changeFilePromise(C,(0,W1.stringifySyml)(new W1.stringifySyml.PreserveOrdering({releases:Object.keys(ne).length>0?ne:void 0,declined:m.length>0?m:void 0,undecided:we.length>0?we:void 0})))}}}function yK(i){return K_(i).size>0||Zy(i).length>0}function K_(i){let o=new Set;for(let f of i.changedWorkspaces)f.manifest.version!==null&&(i.releases.has(f)||o.add(f));return o}function Zy(i,{include:o=new Set}={}){let f=[],p=new Map(Gi.miscUtils.mapAndFilter([...i.releases],([t,k])=>k===Nu.DECLINE?Gi.miscUtils.mapAndFilter.skip:[t.anchoredLocator.locatorHash,t])),E=new Map(Gi.miscUtils.mapAndFilter([...i.releases],([t,k])=>k!==Nu.DECLINE?Gi.miscUtils.mapAndFilter.skip:[t.anchoredLocator.locatorHash,t]));for(let t of i.project.workspaces)if(!(!o.has(t)&&(E.has(t.anchoredLocator.locatorHash)||p.has(t.anchoredLocator.locatorHash)))&&t.manifest.version!==null)for(let k of Gi.Manifest.hardDependencies)for(let L of t.manifest.getForScope(k).values()){let N=i.project.tryWorkspaceByDescriptor(L);N!==null&&p.has(N.anchoredLocator.locatorHash)&&f.push([t,N])}return f}function MD(i,o){let f=Fc.default.clean(o);for(let p of Object.values(Nu))if(p!==Nu.UNDECIDED&&p!==Nu.DECLINE&&Fc.default.inc(i,p)===f)return p;return null}function Y_(i,o){if(Fc.default.valid(o))return o;if(i===null)throw new Jv.UsageError(`Cannot apply the release strategy "${o}" unless the workspace already has a valid version`);if(!Fc.default.valid(i))throw new Jv.UsageError(`Cannot apply the release strategy "${o}" on a non-semver version (${i})`);let f=Fc.default.inc(i,o);if(f===null)throw new Jv.UsageError(`Cannot apply the release strategy "${o}" on the specified version (${i})`);return f}function ND(i,o,{report:f}){let p=new Map;for(let E of i.workspaces)for(let t of Gi.Manifest.allDependencies)for(let k of E.manifest[t].values()){let L=i.tryWorkspaceByDescriptor(k);if(L===null||!o.has(L))continue;Gi.miscUtils.getArrayWithDefault(p,L).push([E,t,k.identHash])}for(let[E,t]of o){let k=E.manifest.version;E.manifest.version=t,Fc.default.prerelease(t)===null?delete E.manifest.raw.stableVersion:E.manifest.raw.stableVersion||(E.manifest.raw.stableVersion=k);let L=E.manifest.name!==null?Gi.structUtils.stringifyIdent(E.manifest.name):null;f.reportInfo(Gi.MessageName.UNNAMED,`${Gi.structUtils.prettyLocator(i.configuration,E.anchoredLocator)}: Bumped to ${t}`),f.reportJson({cwd:D0.npath.fromPortablePath(E.cwd),ident:L,oldVersion:k,newVersion:t});let N=p.get(E);if(typeof N!="undefined")for(let[C,U,q]of N){let W=C.manifest[U].get(q);if(typeof W=="undefined")throw new Error("Assertion failed: The dependency should have existed");let ne=W.range,m=!1;if(ne.startsWith(Gi.WorkspaceResolver.protocol)&&(ne=ne.slice(Gi.WorkspaceResolver.protocol.length),m=!0,ne===E.relativeCwd))continue;let we=ne.match(mK);if(!we){f.reportWarning(Gi.MessageName.UNNAMED,`Couldn't auto-upgrade range ${ne} (in ${Gi.structUtils.prettyLocator(i.configuration,C.anchoredLocator)})`);continue}let Se=`${we[1]}${t}`;m&&(Se=`${Gi.WorkspaceResolver.protocol}${Se}`);let he=Gi.structUtils.makeDescriptor(W,Se);C.manifest[U].set(q,he)}}}var gK=new Map([["%n",{extract:i=>i.length>=1?[i[0],i.slice(1)]:null,generate:(i=0)=>`${i+1}`}]]);function v5(i,{current:o,prerelease:f}){let p=new Fc.default.SemVer(o),E=p.prerelease.slice(),t=[];p.prerelease=[],p.format()!==i&&(E.length=0);let k=!0,L=f.split(/\./g);for(let N of L){let C=gK.get(N);if(typeof C=="undefined")t.push(N),E[0]===N?E.shift():k=!1;else{let U=k?C.extract(E):null;U!==null&&typeof U[0]=="number"?(t.push(C.generate(U[0])),E=U[1]):(t.push(C.generate()),k=!1)}}return p.prerelease&&(p.prerelease=[]),`${i}-${t.join(".")}`}var $y=class extends X_.BaseCommand{constructor(){super(...arguments);this.all=cd.Option.Boolean("--all",!1,{description:"Apply the deferred version changes on all workspaces"});this.dryLine truncated
This command will apply the deferred version changes and remove their definitions from the repository.
Note that if \`--prerelease\` is set, the given prerelease identifier (by default \`rc.%d\`) will be used on all new versions and the version definitions will be kept as-is.
By default only the current workspace will be bumped, but you can configure this behavior by using one of:
- \`--recursive\` to also apply the version bump on its dependencies
- \`--all\` to apply the version bump on all packages in the repository
Note that this command will also update the \`workspace:\` references across all your local workspaces, thus ensuring that they keep referring to the same workspaces even after the version bump.
`,examples:[["Apply the version change to the local workspace","yarn version apply"],["Apply the version change to all the workspaces in the local workspace","yarn version apply --all"]]});var m5=$y;var e_=Mi(require("@yarnpkg/cli")),s0=Mi(require("@yarnpkg/core")),rc=Mi(require("@yarnpkg/fslib"));var cO=Mi(lh()),sh=Mi(su()),dO=(0,sh.memo)(({active:i})=>{let o=(0,sh.useMemo)(()=>i?"\u25C9":"\u25EF",[i]),f=(0,sh.useMemo)(()=>i?"green":"yellow",[i]);return sh.default.createElement(cO.Text,{color:f},o)});var yd=Mi(lh()),Js=Mi(su());var pO=Mi(lh()),q4=Mi(su());function hm({active:i},o,f){let{stdin:p}=(0,pO.useStdin)(),E=(0,q4.useCallback)((t,k)=>o(t,k),f);(0,q4.useEffect)(()=>{if(!(!i||!p))return p.on("keypress",E),()=>{p.off("keypress",E)}},[i,E,p])}var ah;(function(f){f.BEFORE="before",f.AFTER="after"})(ah||(ah={}));var hO=function({active:i},o,f){hm({active:i},(p,E)=>{E.name==="tab"&&(E.shift?o(ah.BEFORE):o(ah.AFTER))},f)};var H4=function(i,o,{active:f,minus:p,plus:E,set:t,loop:k=!0}){hm({active:f},(L,N)=>{let C=o.indexOf(i);switch(N.name){case p:{let U=C-1;if(k){t(o[(o.length+U)%o.length]);return}if(U<0)return;t(o[U])}break;case E:{let U=C+1;if(k){t(o[U%o.length]);return}if(U>=o.length)return;t(o[U])}break}},[o,i,E,t,k])};var O3=({active:i=!0,children:o=[],radius:f=10,size:p=1,loop:E=!0,onFocusRequest:t,willReachEnd:k})=>{let L=Se=>{if(Se.key===null)throw new Error("Expected all children to have a key");return Se.key},N=Js.default.Children.map(o,Se=>L(Se)),C=N[0],[U,q]=(0,Js.useState)(C),W=N.indexOf(U);(0,Js.useEffect)(()=>{N.includes(U)||q(C)},[o]),(0,Js.useEffect)(()=>{k&&W>=N.length-2&&k()},[W]),hO({active:i&&!!t},Se=>{t==null||t(Se)},[t]),H4(U,N,{active:i,minus:"up",plus:"down",set:q,loop:E});let ne=W-f,m=W+f;m>N.length&&(ne-=m-N.length,m=N.length),ne<0&&(m+=-ne,ne=0),m>=N.length&&(m=N.length-1);let we=[];for(let Se=ne;Se<=m;++Se){let he=N[Se],ge=i&&he===U;we.push(Js.default.createElement(yd.Box,{key:he,height:p},Js.default.createElement(yd.Box,{marginLeft:1,marginRight:1},Js.default.createElement(yd.Text,null,ge?Js.default.createElement(yd.Text,{color:"cyan",bold:!0},">"):" ")),Js.default.createElement(yd.Box,null,Js.default.cloneElement(o[Se],{active:ge}))))}return Js.default.createElement(yd.Box,{flexDirection:"column",width:"100%"},we)};var W4=Mi(lh()),k3=Mi(su());var vO=Mi(lh()),Z1=Mi(su()),mO=Mi(require("readline")),O$=Z1.default.createContext(null),yO=({children:i})=>{let{stdin:o,setRawMode:f}=(0,vO.useStdin)();(0,Z1.useEffect)(()=>{f&&f(!0),o&&(0,mO.emitKeypressEvents)(o)},[o,f]);let[p,E]=(0,Z1.useState)(new Map),t=(0,Z1.useMemo)(()=>({getAll:()=>p,get:k=>p.get(k),set:(k,L)=>E(new Map([...p,[k,L]]))}),[p,E]);return Z1.default.createElement(O$.Provider,{value:t,children:i})};async function gO(i,o,{stdin:f,stdout:p,stderr:E}={}){let t,k=N=>{let{exit:C}=(0,W4.useApp)();hm({active:!0},(U,q)=>{q.name==="return"&&(t=N,C())},[C,N])},{waitUntilExit:L}=(0,W4.render)(k3.default.createElement(yO,null,k3.default.createElement(i,Gf(E0({},o),{useSubmit:k}))),{stdin:f,stdout:p,stderr:E});return await L(),t}var fh=Mi(require("clipanion")),Dr=Mi(lh()),Tn=Mi(su()),V4=Mi(require("semver"));var t_=class extends e_.BaseCommand{constructor(){super(...arguments);this.interactive=fh.Option.Boolean("-i,--interactive",{description:"Open an interactive interface used to set version bumps"})}async execute(){return this.interactive?await this.executeInteractive():await this.executeStandard()}async executeInteractive(){let o=await s0.Configuration.find(this.context.cwd,this.context.plugins),{project:f,workspace:p}=await s0.Project.find(o,this.context.cwd);if(!p)throw new e_.WorkspaceRequiredError(f.cwd,this.context.cwd);await f.restoreInstallState();let E=await $v(f);if(E===null||E.releaseRoots.size===0)return 0;if(E.root===null)throw new fh.UsageError("This command can only be run on Git repositories");let t=()=>Tn.default.createElement(Dr.Box,{flexDirection:"row",paddingBottom:1},Tn.default.createElement(Dr.Box,{flexDirection:"column",width:60},Tn.default.createElement(Dr.Box,null,Tn.default.createElement(Dr.Text,null,"Press ",Tn.default.createElement(Dr.Text,{bold:!0,color:"cyanBright"},"<up>"),"/",Tn.default.createElement(Dr.Text,{bold:!0,color:"cyanBright"},"<down>")," to select workspaces.")),Tn.default.createElement(Dr.Box,null,Tn.default.createElement(Dr.Text,null,"Press ",Tn.default.createElement(Dr.Text,{bold:!0,color:"cyanBright"},"<left>"),"/",Tn.default.createElement(Dr.Text,{bold:!0,color:"cyanBright"},"<right>")," to select release strategies."))),Tn.default.createElement(Dr.Box,{flexDirection:"column"},Tn.default.createElement(Dr.Box,{marginLeft:1},Tn.default.createElement(Dr.Text,null,"Press ",Tn.default.createElement(Dr.Text,{bold:!0,color:"cyanBright"},"<enter>")," to save.")),Tn.default.createElement(Dr.Box,{marginLeft:1},Tn.default.createElement(Dr.Text,null,"Press ",Tn.default.createElement(Dr.Text,{bold:!0,color:"cyanBright"},"<ctrl+c>")," to abort.")))),k=({workspace:W,active:ne,decision:m,setDec Line truncated
/*
object-assign
(c) Sindre Sorhus
@license MIT
*/
/**
* @license
* Lodash <https://lodash.com/>
* Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
* Released under MIT license <https://lodash.com/license>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
*/
/** @license React v0.0.0-experimental-51a3aa6af
* react-debug-tools.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v0.0.0-experimental-51a3aa6af
* react-is.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v0.0.0-experimental-51a3aa6af
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v0.18.0
* scheduler-tracing.development.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v0.18.0
* scheduler-tracing.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v0.18.0
* scheduler.development.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v0.18.0
* scheduler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v0.24.0
* react-reconciler.development.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v0.24.0
* react-reconciler.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.13.1
* react.development.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/** @license React v16.13.1
* react.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
return plugin;
}
};
+8
View File
@@ -0,0 +1,8 @@
undecided:
- calcom-monorepo
- "@calcom/app-store-cli"
- "@calcom/platform-constants"
- "@calcom/platform-enums"
- "@calcom/platform-types"
- "@calcom/platform-utils"
- "@calcom/prisma"
View File
Whitespace-only changes.
View File
Whitespace-only changes.
View File
Whitespace-only changes.
+8
View File
@@ -0,0 +1,8 @@
undecided:
- calcom-monorepo
- "@calcom/app-store-cli"
- "@calcom/platform-constants"
- "@calcom/platform-enums"
- "@calcom/platform-types"
- "@calcom/platform-utils"
- "@calcom/prisma"
+8
View File
@@ -0,0 +1,8 @@
undecided:
- calcom-monorepo
- "@calcom/app-store-cli"
- "@calcom/platform-constants"
- "@calcom/platform-enums"
- "@calcom/platform-types"
- "@calcom/platform-utils"
- "@calcom/prisma"
View File
Whitespace-only changes.
+2
View File
@@ -0,0 +1,2 @@
undecided:
- "@calcom/prisma"
+8
View File
@@ -0,0 +1,8 @@
undecided:
- calcom-monorepo
- "@calcom/app-store-cli"
- "@calcom/platform-constants"
- "@calcom/platform-enums"
- "@calcom/platform-types"
- "@calcom/platform-utils"
- "@calcom/prisma"
View File
Whitespace-only changes.
View File
Whitespace-only changes.
View File
Whitespace-only changes.
+8
View File
@@ -0,0 +1,8 @@
undecided:
- calcom-monorepo
- "@calcom/app-store-cli"
- "@calcom/platform-constants"
- "@calcom/platform-enums"
- "@calcom/platform-types"
- "@calcom/platform-utils"
- "@calcom/prisma"
+7
View File
@@ -0,0 +1,7 @@
undecided:
- "@calcom/app-store-cli"
- "@calcom/platform-constants"
- "@calcom/platform-enums"
- "@calcom/platform-types"
- "@calcom/platform-utils"
- "@calcom/prisma"
View File
Whitespace-only changes.
+2
View File
@@ -0,0 +1,2 @@
undecided:
- "@calcom/prisma"
+8
View File
@@ -0,0 +1,8 @@
undecided:
- calcom-monorepo
- "@calcom/app-store-cli"
- "@calcom/platform-constants"
- "@calcom/platform-enums"
- "@calcom/platform-types"
- "@calcom/platform-utils"
- "@calcom/prisma"
View File
Whitespace-only changes.
+8
View File
@@ -0,0 +1,8 @@
undecided:
- calcom-monorepo
- "@calcom/app-store-cli"
- "@calcom/platform-constants"
- "@calcom/platform-enums"
- "@calcom/platform-types"
- "@calcom/platform-utils"
- "@calcom/prisma"
+8
View File
@@ -0,0 +1,8 @@
undecided:
- calcom-monorepo
- "@calcom/app-store-cli"
- "@calcom/platform-constants"
- "@calcom/platform-enums"
- "@calcom/platform-types"
- "@calcom/platform-utils"
- "@calcom/prisma"
+8
View File
@@ -0,0 +1,8 @@
undecided:
- calcom-monorepo
- "@calcom/app-store-cli"
- "@calcom/platform-constants"
- "@calcom/platform-enums"
- "@calcom/platform-types"
- "@calcom/platform-utils"
- "@calcom/prisma"
View File
Whitespace-only changes.
+2
View File
@@ -5,5 +5,7 @@ plugins:
spec: "@yarnpkg/plugin-interactive-tools" spec: "@yarnpkg/plugin-interactive-tools"
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools" spec: "@yarnpkg/plugin-workspace-tools"
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
spec: "@yarnpkg/plugin-version"
yarnPath: .yarn/releases/yarn-3.4.1.cjs yarnPath: .yarn/releases/yarn-3.4.1.cjs
+22 -9
View File
@@ -6,6 +6,7 @@ import { IS_PRODUCTION } from "@calcom/lib/constants";
import prisma from "@calcom/prisma"; import prisma from "@calcom/prisma";
import { isAdminGuard } from "../utils/isAdmin"; import { isAdminGuard } from "../utils/isAdmin";
import { isLockedOrBlocked } from "../utils/isLockedOrBlocked";
import { ScopeOfAdmin } from "../utils/scopeOfAdmin"; import { ScopeOfAdmin } from "../utils/scopeOfAdmin";
// Used to check if the apiKey is not expired, could be extracted if reused. but not for now. // Used to check if the apiKey is not expired, could be extracted if reused. but not for now.
@@ -24,23 +25,35 @@ export const verifyApiKey: NextMiddleware = async (req, res, next) => {
if (!hasValidLicense && IS_PRODUCTION) { if (!hasValidLicense && IS_PRODUCTION) {
return res.status(401).json({ error: "Invalid or missing CALCOM_LICENSE_KEY environment variable" }); return res.status(401).json({ error: "Invalid or missing CALCOM_LICENSE_KEY environment variable" });
} }
// Check if the apiKey query param is provided.
if (!req.query.apiKey) return res.status(401).json({ message: "No apiKey provided" }); if (!req.query.apiKey) return res.status(401).json({ message: "No apiKey provided" });
// remove the prefix from the user provided api_key. If no env set default to "cal_"
const strippedApiKey = `${req.query.apiKey}`.replace(process.env.API_KEY_PREFIX || "cal_", ""); const strippedApiKey = `${req.query.apiKey}`.replace(process.env.API_KEY_PREFIX || "cal_", "");
// Hash the key again before matching against the database records.
const hashedKey = hashAPIKey(strippedApiKey); const hashedKey = hashAPIKey(strippedApiKey);
// Check if the hashed api key exists in database. const apiKey = await prisma.apiKey.findUnique({
const apiKey = await prisma.apiKey.findUnique({ where: { hashedKey } }); where: { hashedKey },
// If cannot find any api key. Throw a 401 Unauthorized. include: {
if (!apiKey) return res.status(401).json({ error: "Your apiKey is not valid" }); user: {
select: { role: true, locked: true, email: true },
},
},
});
if (!apiKey) return res.status(401).json({ error: "Your API key is not valid." });
if (apiKey.expiresAt && dateNotInPast(apiKey.expiresAt)) { if (apiKey.expiresAt && dateNotInPast(apiKey.expiresAt)) {
return res.status(401).json({ error: "This apiKey is expired" }); return res.status(401).json({ error: "This API key is expired." });
} }
if (!apiKey.userId) return res.status(404).json({ error: "No user found for this apiKey" }); if (!apiKey.userId || !apiKey.user)
return res.status(404).json({ error: "No user found for this API key." });
// save the user id in the request for later use // save the user id in the request for later use
req.userId = apiKey.userId; req.userId = apiKey.userId;
req.user = apiKey.user;
const { isAdmin, scope } = await isAdminGuard(req); const { isAdmin, scope } = await isAdminGuard(req);
const userIsLockedOrBlocked = await isLockedOrBlocked(req);
if (userIsLockedOrBlocked)
return res.status(403).json({ error: "You are not authorized to perform this request." });
req.isSystemWideAdmin = isAdmin && scope === ScopeOfAdmin.SystemWide; req.isSystemWideAdmin = isAdmin && scope === ScopeOfAdmin.SystemWide;
req.isOrganizationOwnerOrAdmin = isAdmin && scope === ScopeOfAdmin.OrgOwnerOrAdmin; req.isOrganizationOwnerOrAdmin = isAdmin && scope === ScopeOfAdmin.OrgOwnerOrAdmin;
+3 -4
View File
@@ -6,8 +6,7 @@ import { UserPermissionRole, MembershipRole } from "@calcom/prisma/enums";
import { ScopeOfAdmin } from "./scopeOfAdmin"; import { ScopeOfAdmin } from "./scopeOfAdmin";
export const isAdminGuard = async (req: NextApiRequest) => { export const isAdminGuard = async (req: NextApiRequest) => {
const { userId } = req; const { user, userId } = req;
const user = await prisma.user.findUnique({ where: { id: userId }, select: { role: true } });
if (!user) return { isAdmin: false, scope: null }; if (!user) return { isAdmin: false, scope: null };
const { role: userRole } = user; const { role: userRole } = user;
@@ -34,7 +33,7 @@ export const isAdminGuard = async (req: NextApiRequest) => {
}, },
}, },
}); });
if (!orgOwnerOrAdminMemberships.length) return { isAdmin: false, scope: null }; if (orgOwnerOrAdminMemberships.length > 0) return { isAdmin: true, scope: ScopeOfAdmin.OrgOwnerOrAdmin };
return { isAdmin: true, scope: ScopeOfAdmin.OrgOwnerOrAdmin }; return { isAdmin: false, scope: null };
}; };
@@ -0,0 +1,9 @@
import type { NextApiRequest } from "next";
import { checkIfEmailInWatchlistController } from "@calcom/features/watchlist/operations/check-if-email-in-watchlist.controller";
export async function isLockedOrBlocked(req: NextApiRequest) {
const user = req.user;
if (!user?.email) return false;
return user.locked || (await checkIfEmailInWatchlistController(user.email));
}
+2
View File
@@ -23,6 +23,7 @@ const schemaBookingBaseBodyParams = Booking.pick({
status: true, status: true,
rescheduledBy: true, rescheduledBy: true,
cancelledBy: true, cancelledBy: true,
createdAt: true,
}).partial(); }).partial();
export const schemaBookingCreateBodyParams = extendedBookingCreateBody.merge(schemaQueryUserId.partial()); export const schemaBookingCreateBodyParams = extendedBookingCreateBody.merge(schemaQueryUserId.partial());
@@ -119,4 +120,5 @@ export const schemaBookingReadPublic = Booking.extend({
fromReschedule: true, fromReschedule: true,
cancelledBy: true, cancelledBy: true,
rescheduledBy: true, rescheduledBy: true,
createdAt: true,
}); });
+1
View File
@@ -8,6 +8,7 @@ export declare module "next" {
session?: Session | null; session?: Session | null;
userId: number; userId: number;
user?: { role: string; locked: boolean; email: string } | null;
method: string; method: string;
// session: { user: { id: number } }; // session: { user: { id: number } };
// query: Partial<{ [key: string]: string | string[] }>; // query: Partial<{ [key: string]: string | string[] }>;
@@ -1,17 +1,17 @@
import prismaMock from "../../../../../../tests/libs/__mocks__/prismaMock"; import prismock from "../../../../../../tests/libs/__mocks__/prisma";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import type { NextApiRequest, NextApiResponse } from "next"; import type { NextApiRequest, NextApiResponse } from "next";
import { createMocks } from "node-mocks-http"; import { createMocks } from "node-mocks-http";
import { describe, vi, it, expect, afterEach, beforeEach } from "vitest"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import LicenseKeyService from "@calcom/ee/common/server/LicenseKeyService"; import LicenseKeyService from "@calcom/ee/common/server/LicenseKeyService";
import prisma from "@calcom/prisma"; import prisma from "@calcom/prisma";
import { MembershipRole, UserPermissionRole } from "@calcom/prisma/enums";
import { isAdminGuard } from "~/lib/utils/isAdmin"; import { hashAPIKey } from "~/../../../packages/features/ee/api-keys/lib/apiKeys";
import { verifyApiKey } from "../../../lib/helpers/verifyApiKey"; import { verifyApiKey } from "../../../lib/helpers/verifyApiKey";
import { ScopeOfAdmin } from "../../../lib/utils/scopeOfAdmin";
type CustomNextApiRequest = NextApiRequest & Request; type CustomNextApiRequest = NextApiRequest & Request;
type CustomNextApiResponse = NextApiResponse & Response; type CustomNextApiResponse = NextApiResponse & Response;
@@ -20,11 +20,6 @@ afterEach(() => {
vi.resetAllMocks(); vi.resetAllMocks();
}); });
vi.mock("@calcom/prisma");
vi.mock("~/lib/utils/isAdmin", () => ({
isAdminGuard: vi.fn(),
}));
describe("Verify API key", () => { describe("Verify API key", () => {
let service: LicenseKeyService; let service: LicenseKeyService;
@@ -45,7 +40,6 @@ describe("Verify API key", () => {
}; };
vi.mocked(service.checkLicense).mockResolvedValue(false); vi.mocked(service.checkLicense).mockResolvedValue(false);
vi.mocked(isAdminGuard).mockResolvedValue({ isAdmin: false, scope: null });
const serverNext = vi.fn((next: void) => Promise.resolve(next)); const serverNext = vi.fn((next: void) => Promise.resolve(next));
@@ -69,7 +63,6 @@ describe("Verify API key", () => {
}; };
vi.mocked(service.checkLicense).mockResolvedValue(true); vi.mocked(service.checkLicense).mockResolvedValue(true);
vi.mocked(isAdminGuard).mockResolvedValue({ isAdmin: false, scope: null });
const serverNext = vi.fn((next: void) => Promise.resolve(next)); const serverNext = vi.fn((next: void) => Promise.resolve(next));
@@ -91,10 +84,18 @@ describe("Verify API key", () => {
}, },
prisma, prisma,
}); });
const hashedKey = hashAPIKey("test_key");
prismaMock.apiKey.findUnique.mockResolvedValue({ await prismock.apiKey.create({
id: 1, data: {
userId: 2, hashedKey,
user: {
create: {
email: "admin@example.com",
role: UserPermissionRole.ADMIN,
locked: false,
},
},
},
}); });
const middleware = { const middleware = {
@@ -102,7 +103,6 @@ describe("Verify API key", () => {
}; };
vi.mocked(service.checkLicense).mockResolvedValue(true); vi.mocked(service.checkLicense).mockResolvedValue(true);
vi.mocked(isAdminGuard).mockResolvedValue({ isAdmin: true, scope: ScopeOfAdmin.SystemWide });
const serverNext = vi.fn((next: void) => Promise.resolve(next)); const serverNext = vi.fn((next: void) => Promise.resolve(next));
@@ -125,10 +125,36 @@ describe("Verify API key", () => {
}, },
prisma, prisma,
}); });
const hashedKey = hashAPIKey("test_key");
prismaMock.apiKey.findUnique.mockResolvedValue({ await prismock.apiKey.create({
id: 1, data: {
userId: 2, hashedKey,
user: {
create: {
email: "org-admin@acme.com",
role: UserPermissionRole.USER,
locked: false,
teams: {
create: {
accepted: true,
role: MembershipRole.OWNER,
team: {
create: {
name: "ACME",
isOrganization: true,
organizationSettings: {
create: {
isAdminAPIEnabled: true,
orgAutoAcceptEmail: "acme.com",
},
},
},
},
},
},
},
},
},
}); });
const middleware = { const middleware = {
@@ -136,7 +162,6 @@ describe("Verify API key", () => {
}; };
vi.mocked(service.checkLicense).mockResolvedValue(true); vi.mocked(service.checkLicense).mockResolvedValue(true);
vi.mocked(isAdminGuard).mockResolvedValue({ isAdmin: true, scope: ScopeOfAdmin.OrgOwnerOrAdmin });
const serverNext = vi.fn((next: void) => Promise.resolve(next)); const serverNext = vi.fn((next: void) => Promise.resolve(next));
@@ -149,4 +174,44 @@ describe("Verify API key", () => {
expect(req.isSystemWideAdmin).toBe(false); expect(req.isSystemWideAdmin).toBe(false);
expect(req.isOrganizationOwnerOrAdmin).toBe(true); expect(req.isOrganizationOwnerOrAdmin).toBe(true);
}); });
it("should return 403 if user is locked or blocked", async () => {
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
method: "POST",
body: {},
query: {
apiKey: "cal_test_key",
},
prisma,
});
const hashedKey = hashAPIKey("test_key");
await prismock.apiKey.create({
data: {
hashedKey,
user: {
create: {
email: "locked@example.com",
role: UserPermissionRole.USER,
locked: true,
},
},
},
});
const middleware = {
fn: verifyApiKey,
};
vi.mocked(service.checkLicense).mockResolvedValue(true);
const serverNext = vi.fn((next: void) => Promise.resolve(next));
const middlewareSpy = vi.spyOn(middleware, "fn");
await middleware.fn(req, res, serverNext);
expect(middlewareSpy).toBeCalled();
expect(res.statusCode).toBe(403);
expect(JSON.parse(res._getData())).toEqual({ error: "You are not authorized to perform this request." });
expect(serverNext).not.toHaveBeenCalled();
});
}); });
@@ -19,6 +19,7 @@ describe("isAdmin guard", () => {
}); });
req.userId = 0; req.userId = 0;
req.user = undefined;
const { isAdmin, scope } = await isAdminGuard(req); const { isAdmin, scope } = await isAdminGuard(req);
@@ -35,6 +36,7 @@ describe("isAdmin guard", () => {
const memberUser = await prisma.user.findFirstOrThrow({ where: { email: "member2-acme@example.com" } }); const memberUser = await prisma.user.findFirstOrThrow({ where: { email: "member2-acme@example.com" } });
req.userId = memberUser.id; req.userId = memberUser.id;
req.user = memberUser;
const { isAdmin, scope } = await isAdminGuard(req); const { isAdmin, scope } = await isAdminGuard(req);
@@ -51,6 +53,7 @@ describe("isAdmin guard", () => {
const adminUser = await prisma.user.findFirstOrThrow({ where: { email: "admin@example.com" } }); const adminUser = await prisma.user.findFirstOrThrow({ where: { email: "admin@example.com" } });
req.userId = adminUser.id; req.userId = adminUser.id;
req.user = adminUser;
const { isAdmin, scope } = await isAdminGuard(req); const { isAdmin, scope } = await isAdminGuard(req);
@@ -67,6 +70,8 @@ describe("isAdmin guard", () => {
const adminUser = await prisma.user.findFirstOrThrow({ where: { email: "owner1-acme@example.com" } }); const adminUser = await prisma.user.findFirstOrThrow({ where: { email: "owner1-acme@example.com" } });
req.userId = adminUser.id; req.userId = adminUser.id;
req.user = adminUser;
const { isAdmin, scope } = await isAdminGuard(req); const { isAdmin, scope } = await isAdminGuard(req);
expect(isAdmin).toBe(true); expect(isAdmin).toBe(true);
expect(scope).toBe(ScopeOfAdmin.OrgOwnerOrAdmin); expect(scope).toBe(ScopeOfAdmin.OrgOwnerOrAdmin);
@@ -81,6 +86,8 @@ describe("isAdmin guard", () => {
const adminUser = await prisma.user.findFirstOrThrow({ where: { email: "owner1-dunder@example.com" } }); const adminUser = await prisma.user.findFirstOrThrow({ where: { email: "owner1-dunder@example.com" } });
req.userId = adminUser.id; req.userId = adminUser.id;
req.user = adminUser;
const { isAdmin } = await isAdminGuard(req); const { isAdmin } = await isAdminGuard(req);
expect(isAdmin).toBe(false); expect(isAdmin).toBe(false);
}); });
@@ -0,0 +1,88 @@
import prismock from "../../../../../../tests/libs/__mocks__/prisma";
import { describe, expect, it, beforeEach } from "vitest";
import { isLockedOrBlocked } from "../../../lib/utils/isLockedOrBlocked";
describe("isLockedOrBlocked", () => {
beforeEach(async () => {
await prismock.watchlist.createMany({
data: [
{
type: "DOMAIN",
value: "spam.com",
createdById: 1,
},
{
type: "DOMAIN",
value: "blocked.com",
createdById: 1,
},
],
});
});
it("should return false if no user in request", async () => {
const req = { userId: null, user: null } as any;
const result = await isLockedOrBlocked(req);
expect(result).toBe(false);
});
it("should return false if user has no email", async () => {
const req = { userId: 123, user: { email: null } } as any;
const result = await isLockedOrBlocked(req);
expect(result).toBe(false);
});
it("should return true if user is locked", async () => {
const req = {
userId: 123,
user: {
locked: true,
email: "test@example.com",
},
} as any;
const result = await isLockedOrBlocked(req);
expect(result).toBe(true);
});
it("should return true if user email domain is watchlisted", async () => {
const req = {
userId: 123,
user: {
locked: false,
email: "test@blocked.com",
},
} as any;
const result = await isLockedOrBlocked(req);
expect(result).toBe(true);
});
it("should return false if user is not locked and email domain is not watchlisted", async () => {
const req = {
userId: 123,
user: {
locked: false,
email: "test@example.com",
},
} as any;
const result = await isLockedOrBlocked(req);
expect(result).toBe(false);
});
it("should handle email domains case-insensitively", async () => {
const req = {
userId: 123,
user: {
locked: false,
email: "test@BLOCKED.COM",
},
} as any;
const result = await isLockedOrBlocked(req);
expect(result).toBe(true);
});
});
+1
View File
@@ -12,6 +12,7 @@
}, },
"include": [ "include": [
"next-env.d.ts", "next-env.d.ts",
"./next.d.ts",
"**/*.ts", "**/*.ts",
"**/*.tsx", "**/*.tsx",
"../../../packages/types/*.d.ts", "../../../packages/types/*.d.ts",
+1
View File
@@ -6,6 +6,7 @@ ARG DATABASE_URL
WORKDIR /calcom WORKDIR /calcom
RUN set -eux; RUN set -eux;
RUN ln -s /usr/lib/libssl.so.3 /lib/libssl.so.3
ENV NODE_ENV="production" ENV NODE_ENV="production"
ENV NODE_OPTIONS="--max-old-space-size=8192" ENV NODE_OPTIONS="--max-old-space-size=8192"
+1 -1
View File
@@ -10,7 +10,7 @@
"transform": { "transform": {
"^.+\\.(t|j)s$": "ts-jest" "^.+\\.(t|j)s$": "ts-jest"
}, },
"setupFiles": ["<rootDir>/test/setEnvVars.ts"], "setupFiles": ["<rootDir>/test/setEnvVars.ts", "jest-date-mock"],
"reporters": ["default", "jest-summarizing-reporter"], "reporters": ["default", "jest-summarizing-reporter"],
"workerIdleMemoryLimit": "512MB", "workerIdleMemoryLimit": "512MB",
"maxWorkers": 2 "maxWorkers": 2
+3 -2
View File
@@ -11,7 +11,7 @@
"start": "nest start", "start": "nest start",
"dev:build:watch": "yarn workspace @calcom/platform-constants build:watch & yarn workspace @calcom/platform-utils build:watch & yarn workspace @calcom/platform-types build:watch", "dev:build:watch": "yarn workspace @calcom/platform-constants build:watch & yarn workspace @calcom/platform-utils build:watch & yarn workspace @calcom/platform-types build:watch",
"dev:build": "yarn workspace @calcom/platform-constants build && yarn workspace @calcom/platform-enums build && yarn workspace @calcom/platform-utils build && yarn workspace @calcom/platform-types build ", "dev:build": "yarn workspace @calcom/platform-constants build && yarn workspace @calcom/platform-enums build && yarn workspace @calcom/platform-utils build && yarn workspace @calcom/platform-types build ",
"dev": "yarn dev:build && docker-compose up -d && yarn copy-swagger-module && yarn start --watch", "dev": "yarn dev:build && ts-node scripts/docker-start.ts && yarn copy-swagger-module && yarn start --watch",
"dev:no-docker": "yarn dev:build && yarn copy-swagger-module && yarn start --watch", "dev:no-docker": "yarn dev:build && yarn copy-swagger-module && yarn start --watch",
"start:debug": "nest start --debug --watch", "start:debug": "nest start --debug --watch",
"start:prod": "node ./dist/apps/api/v2/src/main.js", "start:prod": "node ./dist/apps/api/v2/src/main.js",
@@ -29,7 +29,7 @@
"@axiomhq/winston": "^1.2.0", "@axiomhq/winston": "^1.2.0",
"@calcom/platform-constants": "*", "@calcom/platform-constants": "*",
"@calcom/platform-enums": "*", "@calcom/platform-enums": "*",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.61", "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.73",
"@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2", "@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2",
"@calcom/platform-types": "*", "@calcom/platform-types": "*",
"@calcom/platform-utils": "*", "@calcom/platform-utils": "*",
@@ -87,6 +87,7 @@
"@types/passport-jwt": "^3.0.13", "@types/passport-jwt": "^3.0.13",
"@types/supertest": "^2.0.12", "@types/supertest": "^2.0.12",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-date-mock": "^1.0.10",
"prettier": "^2.8.6", "prettier": "^2.8.6",
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"supertest": "^6.3.3", "supertest": "^6.3.3",
+35
View File
@@ -0,0 +1,35 @@
import { execSync } from "child_process";
function checkCommandExists(command: string): boolean {
try {
execSync(`${command} --version`, { stdio: "ignore" });
return true;
} catch (e) {
return false;
}
}
try {
// Check if docker is installed
if (!checkCommandExists("docker")) {
throw new Error("Docker is not installed");
}
// Try docker compose first (new syntax)
try {
execSync("docker compose version", { stdio: "ignore" });
console.log("Starting containers with docker compose...");
execSync("docker compose up -d", { stdio: "inherit" });
} catch (e) {
// Fall back to docker-compose if the above fails
if (checkCommandExists("docker-compose")) {
console.log("Starting containers with docker-compose...");
execSync("docker-compose up -d", { stdio: "inherit" });
} else {
throw new Error("Neither 'docker compose' nor 'docker-compose' command is available");
}
}
} catch (error) {
console.error(`Error: ${error instanceof Error ? error.message : "Unknown error occurred"}`);
process.exit(1);
}
+2
View File
@@ -30,6 +30,8 @@ const loadConfig = (): AppConfig => {
stripe: { stripe: {
apiKey: getEnv("STRIPE_API_KEY"), apiKey: getEnv("STRIPE_API_KEY"),
webhookSecret: getEnv("STRIPE_WEBHOOK_SECRET"), webhookSecret: getEnv("STRIPE_WEBHOOK_SECRET"),
teamMonthlyPriceId: getEnv("STRIPE_TEAM_MONTHLY_PRICE_ID", "set-team-monthly-price-in-your-env"),
isTeamBillingEnabled: getEnv("IS_TEAM_BILLING_ENABLED", true),
}, },
app: { app: {
baseUrl: getEnv("WEB_APP_URL", "https://app.cal.com"), baseUrl: getEnv("WEB_APP_URL", "https://app.cal.com"),
+2
View File
@@ -21,6 +21,8 @@ export type AppConfig = {
stripe: { stripe: {
apiKey: string; apiKey: string;
webhookSecret: string; webhookSecret: string;
teamMonthlyPriceId: string;
isTeamBillingEnabled: boolean;
}; };
app: { app: {
baseUrl: string; baseUrl: string;
@@ -333,7 +333,7 @@ export class BookingsController_2024_04_15 {
} }
private async getOAuthClientsParams(clientId: string): Promise<OAuthRequestParams> { private async getOAuthClientsParams(clientId: string): Promise<OAuthRequestParams> {
const res = DEFAULT_PLATFORM_PARAMS; const res = { ...DEFAULT_PLATFORM_PARAMS };
try { try {
const client = await this.oAuthClientRepository.getOAuthClient(clientId); const client = await this.oAuthClientRepository.getOAuthClient(clientId);
// fetch oAuthClient from db and use data stored in db to set these values // fetch oAuthClient from db and use data stored in db to set these values
@@ -1,3 +1,4 @@
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Transform, Type } from "class-transformer"; import { Transform, Type } from "class-transformer";
import { import {
IsBoolean, IsBoolean,
@@ -13,53 +14,66 @@ import {
class Location { class Location {
@IsString() @IsString()
@ApiProperty()
optionValue!: string; optionValue!: string;
@IsString() @IsString()
@ApiProperty()
value!: string; value!: string;
} }
class Response { class Response {
@IsString() @IsString()
@ApiProperty()
name!: string; name!: string;
@IsEmail() @IsEmail()
@ApiProperty()
email!: string; email!: string;
@IsArray() @IsArray()
@IsString({ each: true }) @IsString({ each: true })
@ApiProperty({ type: [String] })
guests!: string[]; guests!: string[];
@IsOptional() @IsOptional()
@ValidateNested() @ValidateNested()
@Type(() => Location) @Type(() => Location)
@ApiPropertyOptional({ type: Location })
location?: Location; location?: Location;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
notes?: string; notes?: string;
} }
export class CreateBookingInput_2024_04_15 { export class CreateBookingInput_2024_04_15 {
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
end?: string; end?: string;
@IsString() @IsString()
@ApiProperty()
start!: string; start!: string;
@IsNumber() @IsNumber()
@ApiProperty()
eventTypeId!: number; eventTypeId!: number;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
eventTypeSlug?: string; eventTypeSlug?: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
rescheduleUid?: string; rescheduleUid?: string;
@IsTimeZone() @IsTimeZone()
@ApiProperty()
timeZone!: string; timeZone!: string;
@Transform(({ value }: { value: string | string[] }) => { @Transform(({ value }: { value: string | string[] }) => {
@@ -68,38 +82,48 @@ export class CreateBookingInput_2024_04_15 {
@IsOptional() @IsOptional()
@IsArray() @IsArray()
@IsString({ each: true }) @IsString({ each: true })
@ApiPropertyOptional({ type: [String] })
user?: string[]; user?: string[];
@IsString() @IsString()
@ApiProperty()
language!: string; language!: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
bookingUid?: string; bookingUid?: string;
@IsObject() @IsObject()
@ApiProperty({ type: Object })
metadata!: Record<string, string>; metadata!: Record<string, string>;
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
hasHashedBookingLink?: boolean; hasHashedBookingLink?: boolean;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
hashedLink!: string | null; hashedLink!: string | null;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
seatReferenceUid?: string; seatReferenceUid?: string;
@Type(() => Response) @Type(() => Response)
@ApiProperty({ type: Response })
responses!: Response; responses!: Response;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
orgSlug?: string; orgSlug?: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
locationUrl?: string; locationUrl?: string;
} }
@@ -1,4 +1,5 @@
import { CreateBookingInput_2024_04_15 } from "@/ee/bookings/2024-04-15/inputs/create-booking.input"; import { CreateBookingInput_2024_04_15 } from "@/ee/bookings/2024-04-15/inputs/create-booking.input";
import { ApiPropertyOptional } from "@nestjs/swagger";
import { IsBoolean, IsString, IsNumber, IsOptional } from "class-validator"; import { IsBoolean, IsString, IsNumber, IsOptional } from "class-validator";
import type { AppsStatus } from "@calcom/platform-libraries"; import type { AppsStatus } from "@calcom/platform-libraries";
@@ -6,23 +7,29 @@ import type { AppsStatus } from "@calcom/platform-libraries";
export class CreateRecurringBookingInput_2024_04_15 extends CreateBookingInput_2024_04_15 { export class CreateRecurringBookingInput_2024_04_15 extends CreateBookingInput_2024_04_15 {
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
noEmail?: boolean; noEmail?: boolean;
@IsOptional() @IsOptional()
@IsNumber() @IsNumber()
@ApiPropertyOptional()
recurringCount?: number; recurringCount?: number;
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: [Object] })
appsStatus?: AppsStatus[] | undefined; appsStatus?: AppsStatus[] | undefined;
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: [Object] })
allRecurringDates?: Record<string, string>[]; allRecurringDates?: Record<string, string>[];
@IsOptional() @IsOptional()
@IsNumber() @IsNumber()
@ApiPropertyOptional()
currentRecurringIndex?: number; currentRecurringIndex?: number;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
recurringEventId?: string; recurringEventId?: string;
} }
@@ -1,22 +1,27 @@
import { ApiPropertyOptional, ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { IsOptional, IsArray, IsEmail, IsBoolean, ValidateNested } from "class-validator"; import { IsOptional, IsArray, IsEmail, IsBoolean, ValidateNested } from "class-validator";
class Attendee { class Attendee {
@IsEmail() @IsEmail()
@ApiProperty()
email!: string; email!: string;
@IsBoolean() @IsBoolean()
@ApiProperty()
noShow!: boolean; noShow!: boolean;
} }
export class MarkNoShowInput_2024_04_15 { export class MarkNoShowInput_2024_04_15 {
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
noShowHost?: boolean; noShowHost?: boolean;
@ValidateNested() @ValidateNested()
@Type(() => Attendee) @Type(() => Attendee)
@IsArray() @IsArray()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: [Attendee] })
attendees?: Attendee[]; attendees?: Attendee[];
} }
@@ -1,4 +1,4 @@
import { ApiProperty } from "@nestjs/swagger"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { import {
IsString, IsString,
@@ -17,145 +17,185 @@ import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
class Metadata { class Metadata {
@IsUrl() @IsUrl()
@ApiProperty()
videoCallUrl!: string; videoCallUrl!: string;
} }
class Location { class Location {
@IsString() @IsString()
@ApiProperty()
optionValue!: string; optionValue!: string;
@IsString() @IsString()
@ApiProperty()
value!: string; value!: string;
} }
class Response { class Response {
@IsString() @IsString()
@ApiProperty()
name!: string; name!: string;
@IsEmail() @IsEmail()
@ApiProperty()
email!: string; email!: string;
@IsString() @IsString()
@ApiProperty()
notes!: string; notes!: string;
@IsArray() @IsArray()
@IsString({ each: true }) @IsString({ each: true })
@ApiProperty({ type: [String] })
guests!: string[]; guests!: string[];
@ValidateNested() @ValidateNested()
@Type(() => Location) @Type(() => Location)
@ApiProperty({ type: Location })
location!: Location; location!: Location;
} }
class User { class User {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
name!: string | null; name!: string | null;
@IsEmail() @IsEmail()
@ApiProperty()
email!: string; email!: string;
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
username!: string | null; username!: string | null;
@IsString() @IsString()
@ApiProperty()
timeZone!: string; timeZone!: string;
} }
class Attendee { class Attendee {
@IsString() @IsString()
@ApiProperty()
name!: string; name!: string;
@IsEmail() @IsEmail()
@ApiProperty()
email!: string; email!: string;
@IsString() @IsString()
@ApiProperty()
timeZone!: string; timeZone!: string;
} }
class EventType { class EventType {
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
eventName!: string | null; eventName!: string | null;
@IsString() @IsString()
@ApiProperty()
slug!: string; slug!: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
timeZone!: string | null; timeZone!: string | null;
} }
class GetBookingData_2024_04_15 { class GetBookingData_2024_04_15 {
@IsString() @IsString()
@ApiProperty()
title!: string; title!: string;
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsString() @IsString()
@ApiProperty()
uid!: string; uid!: string;
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
description!: string | null; description!: string | null;
@IsObject() @IsObject()
@ApiProperty({ type: Object })
customInputs!: any; customInputs!: any;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
smsReminderNumber!: string | null; smsReminderNumber!: string | null;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
recurringEventId!: string | null; recurringEventId!: string | null;
@IsDateString() @IsDateString()
@ApiProperty()
startTime!: Date; startTime!: Date;
@IsDateString() @IsDateString()
@ApiProperty()
endTime!: Date; endTime!: Date;
@IsUrl() @IsUrl()
@ApiProperty({ type: String, nullable: true })
location!: string | null; location!: string | null;
@IsString() @IsString()
@ApiProperty()
status!: string; status!: string;
@Type(() => Metadata)
@ApiProperty({ type: Metadata })
metadata!: Metadata | any; metadata!: Metadata | any;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
cancellationReason!: string | null; cancellationReason!: string | null;
@ValidateNested() @ValidateNested()
@Type(() => Response) @Type(() => Response)
@ApiProperty({ type: Response })
responses!: Response | any; responses!: Response | any;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
rejectionReason!: string | null; rejectionReason!: string | null;
@IsString() @IsString()
@IsEmail() @IsEmail()
@ApiProperty({ type: String, nullable: true })
userPrimaryEmail!: string | null; userPrimaryEmail!: string | null;
@ValidateNested() @ValidateNested()
@Type(() => User) @Type(() => User)
@ApiProperty({ type: User, nullable: true })
user!: User | null; user!: User | null;
@ValidateNested() @ValidateNested()
@Type(() => Attendee) @Type(() => Attendee)
@IsArray() @IsArray()
@ApiProperty({ type: [Attendee] })
attendees!: Attendee[]; attendees!: Attendee[];
@IsInt() @IsInt()
@ApiProperty({ type: Number, nullable: true })
eventTypeId!: number | null; eventTypeId!: number | null;
@ValidateNested() @ValidateNested()
@Type(() => EventType) @Type(() => EventType)
@ApiProperty({ type: EventType, nullable: true })
eventType!: EventType | null; eventType!: EventType | null;
} }
@@ -1,4 +1,4 @@
import { ApiProperty } from "@nestjs/swagger"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { import {
IsString, IsString,
@@ -26,179 +26,230 @@ enum Status {
class Attendee { class Attendee {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsEmail() @IsEmail()
@ApiProperty()
email!: string; email!: string;
@IsString() @IsString()
@ApiProperty()
name!: string; name!: string;
@IsString() @IsString()
@ApiProperty()
timeZone!: string; timeZone!: string;
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
locale!: string | null; locale!: string | null;
@IsInt() @IsInt()
@ApiProperty({ type: Number, nullable: true })
bookingId!: number | null; bookingId!: number | null;
} }
class EventType { class EventType {
@IsString()
@IsOptional() @IsOptional()
@IsString()
@ApiPropertyOptional()
slug?: string; slug?: string;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiPropertyOptional()
id?: number; id?: number;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
eventName?: string | null; eventName?: string | null;
@IsInt() @IsInt()
@ApiProperty()
price!: number; price!: number;
@IsOptional() @IsOptional()
@ApiPropertyOptional()
recurringEvent?: any; recurringEvent?: any;
@IsString() @IsString()
@ApiProperty()
currency!: string; currency!: string;
@IsObject() @IsObject()
@ApiProperty()
metadata!: any; metadata!: any;
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
seatsShowAttendees?: boolean | undefined | null; @ApiPropertyOptional({ type: Boolean, nullable: true })
seatsShowAttendees?: boolean | null;
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
seatsShowAvailabilityCount?: boolean | undefined | null; @ApiPropertyOptional({ type: Boolean, nullable: true })
seatsShowAvailabilityCount?: boolean | null;
@IsOptional() @IsOptional()
@ApiPropertyOptional({ nullable: true })
team?: any | null; team?: any | null;
} }
class Reference { class Reference {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsString() @IsString()
@ApiProperty()
type!: string; type!: string;
@IsString() @IsString()
@ApiProperty()
uid!: string; uid!: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
meetingId?: string | null; meetingId?: string | null;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
thirdPartyRecurringEventId?: string | null; thirdPartyRecurringEventId?: string | null;
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
meetingPassword!: string | null; meetingPassword!: string | null;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
meetingUrl?: string | null; meetingUrl?: string | null;
@IsInt() @IsInt()
@ApiProperty({ type: Number, nullable: true })
bookingId!: number | null; bookingId!: number | null;
@IsEmail() @IsEmail()
@ApiProperty({ type: String, nullable: true })
externalCalendarId!: string | null; externalCalendarId!: string | null;
@IsOptional() @IsOptional()
@ApiPropertyOptional()
deleted?: any; deleted?: any;
@IsInt() @IsInt()
@ApiProperty({ type: Number, nullable: true })
credentialId!: number | null; credentialId!: number | null;
} }
class User { class User {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
name!: string | null; name!: string | null;
@IsEmail() @IsEmail()
@ApiProperty()
email!: string; email!: string;
} }
class GetBookingsDataEntry { class GetBookingsDataEntry {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsString() @IsString()
@ApiProperty()
title!: string; title!: string;
@IsOptional() @IsOptional()
@IsEmail() @IsEmail()
@ApiProperty({ type: String, nullable: true })
userPrimaryEmail?: string | null; userPrimaryEmail?: string | null;
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
description!: string | null; description!: string | null;
@IsObject() @IsObject()
@ApiProperty({ type: Object })
customInputs!: object | any; customInputs!: object | any;
@IsDateString() @IsDateString()
@ApiProperty()
startTime!: string; startTime!: string;
@IsDateString() @IsDateString()
@ApiProperty()
endTime!: string; endTime!: string;
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => Attendee) @Type(() => Attendee)
@IsArray() @IsArray()
@ApiProperty({ type: [Attendee] })
attendees!: Attendee[]; attendees!: Attendee[];
@ApiProperty()
metadata!: any; metadata!: any;
@IsString() @IsString()
@ApiProperty()
uid!: string; uid!: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
recurringEventId!: string | null; recurringEventId!: string | null;
@IsUrl() @IsUrl()
@ApiProperty({ type: String, nullable: true })
location!: string | null; location!: string | null;
@ValidateNested() @ValidateNested()
@Type(() => EventType) @Type(() => EventType)
@ApiProperty({ type: EventType })
eventType!: EventType; eventType!: EventType;
@IsEnum(Status) @IsEnum(Status)
status!: "CANCELLED" | "REJECTED" | "ACCEPTED" | "PENDING" | "AWAITING_HOST"; @ApiProperty({ enum: Status, type: String })
status!: Status;
@IsBoolean() @IsBoolean()
@ApiProperty({ type: Boolean })
paid!: boolean; paid!: boolean;
@IsArray() @IsArray()
@ApiProperty()
payment!: any[]; payment!: any[];
@ValidateNested() @ValidateNested()
@Type(() => Reference) @Type(() => Reference)
@IsArray() @IsArray()
@ApiProperty({ type: [Reference] })
references!: Reference[]; references!: Reference[];
@IsBoolean() @IsBoolean()
@ApiProperty({ type: Boolean })
isRecorded!: boolean; isRecorded!: boolean;
@IsArray() @IsArray()
@ApiProperty()
seatsReferences!: any[]; seatsReferences!: any[];
@ValidateNested() @ValidateNested()
@Type(() => User) @Type(() => User)
@ApiProperty({ type: User })
user!: User | null; user!: User | null;
@IsOptional() @IsOptional()
@ApiPropertyOptional()
rescheduled?: any; rescheduled?: any;
} }
@@ -206,17 +257,20 @@ class GetBookingsData_2024_04_15 {
@ValidateNested() @ValidateNested()
@Type(() => GetBookingsDataEntry) @Type(() => GetBookingsDataEntry)
@IsArray() @IsArray()
@ApiProperty({ type: [GetBookingsDataEntry] })
bookings!: GetBookingsDataEntry[]; bookings!: GetBookingsDataEntry[];
@IsArray() @IsArray()
@ApiProperty()
recurringInfo!: any[]; recurringInfo!: any[];
@IsInt() @IsInt()
@ApiProperty({ type: Number, nullable: true })
nextCursor!: number | null; nextCursor!: number | null;
} }
export class GetBookingsOutput_2024_04_15 { export class GetBookingsOutput_2024_04_15 {
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] }) @ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS], type: String })
@IsEnum([SUCCESS_STATUS, ERROR_STATUS]) @IsEnum([SUCCESS_STATUS, ERROR_STATUS])
status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS; status!: typeof SUCCESS_STATUS | typeof ERROR_STATUS;
@@ -1,4 +1,4 @@
import { ApiProperty } from "@nestjs/swagger"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { IsString, IsEnum, IsOptional, ValidateNested, IsArray, IsEmail, IsBoolean } from "class-validator"; import { IsString, IsEnum, IsOptional, ValidateNested, IsArray, IsEmail, IsBoolean } from "class-validator";
@@ -6,24 +6,29 @@ import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
class Attendee { class Attendee {
@IsEmail() @IsEmail()
@ApiProperty()
email!: string; email!: string;
@IsBoolean() @IsBoolean()
@ApiProperty()
noShow!: boolean; noShow!: boolean;
} }
class HandleMarkNoShowData_2024_04_15 { class HandleMarkNoShowData_2024_04_15 {
@IsString() @IsString()
@ApiProperty()
message!: string; message!: string;
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
noShowHost?: boolean; noShowHost?: boolean;
@ValidateNested() @ValidateNested()
@Type(() => Attendee) @Type(() => Attendee)
@IsArray() @IsArray()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: [Attendee] })
attendees?: Attendee[]; attendees?: Attendee[];
} }
@@ -1,160 +0,0 @@
import { bootstrap } from "@/app";
import { AppModule } from "@/app.module";
import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output";
import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input";
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service";
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { ApiKeysRepositoryFixture } from "test/fixtures/repository/api-keys.repository.fixture";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
import { withApiAuth } from "test/utils/withApiAuth";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import { CreateBookingInput_2024_08_13, BookingOutput_2024_08_13 } from "@calcom/platform-types";
import { Team } from "@calcom/prisma/client";
describe("Bookings Endpoints 2024-08-13", () => {
describe("With api key", () => {
let app: INestApplication;
let organization: Team;
let userRepositoryFixture: UserRepositoryFixture;
let bookingsRepositoryFixture: BookingsRepositoryFixture;
let schedulesService: SchedulesService_2024_04_15;
let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
let teamRepositoryFixture: TeamRepositoryFixture;
let apiKeysRepositoryFixture: ApiKeysRepositoryFixture;
let apiKeyString: string;
const userEmail = "bookings-controller-e2e@api.com";
let user: User;
let eventTypeId: number;
beforeAll(async () => {
const moduleRef = await withApiAuth(
userEmail,
Test.createTestingModule({
imports: [AppModule, PrismaModule, UsersModule, SchedulesModule_2024_04_15],
})
)
.overrideGuard(PermissionsGuard)
.useValue({
canActivate: () => true,
})
.compile();
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
bookingsRepositoryFixture = new BookingsRepositoryFixture(moduleRef);
eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef);
teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
schedulesService = moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15);
apiKeysRepositoryFixture = new ApiKeysRepositoryFixture(moduleRef);
organization = await teamRepositoryFixture.create({ name: "organization bookings" });
user = await userRepositoryFixture.create({
email: userEmail,
});
const { keyString } = await apiKeysRepositoryFixture.createApiKey(user.id, null);
apiKeyString = keyString;
const userSchedule: CreateScheduleInput_2024_04_15 = {
name: "working time",
timeZone: "Europe/Rome",
isDefault: true,
};
await schedulesService.createUserSchedule(user.id, userSchedule);
const event = await eventTypesRepositoryFixture.create(
{ title: "peer coding", slug: "peer-coding", length: 60 },
user.id
);
eventTypeId = event.id;
app = moduleRef.createNestApplication();
bootstrap(app as NestExpressApplication);
await app.init();
});
it("should be defined", () => {
expect(userRepositoryFixture).toBeDefined();
expect(user).toBeDefined();
});
describe("create bookings", () => {
it("should create a booking with api key", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
eventTypeId,
attendee: {
name: "Mr Key",
email: "mr_key@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
meetingUrl: "https://meet.google.com/abc-def-ghi",
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseDataIsBooking(responseBody.data)).toBe(true);
if (responseDataIsBooking(responseBody.data)) {
const data: BookingOutput_2024_08_13 = responseBody.data;
expect(data.id).toBeDefined();
expect(data.uid).toBeDefined();
expect(data.hosts[0].id).toEqual(user.id);
expect(data.status).toEqual("accepted");
expect(data.start).toEqual(body.start);
expect(data.end).toEqual(new Date(Date.UTC(2030, 0, 8, 14, 0, 0)).toISOString());
expect(data.duration).toEqual(60);
expect(data.eventTypeId).toEqual(eventTypeId);
expect(data.attendees[0]).toEqual({
name: body.attendee.name,
timeZone: body.attendee.timeZone,
language: body.attendee.language,
absent: false,
});
expect(data.meetingUrl).toEqual(body.meetingUrl);
expect(data.absentHost).toEqual(false);
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
});
function responseDataIsBooking(data: any): data is BookingOutput_2024_08_13 {
return !Array.isArray(data) && typeof data === "object" && data && "id" in data;
}
afterAll(async () => {
await teamRepositoryFixture.delete(organization.id);
await userRepositoryFixture.deleteByEmail(user.email);
await bookingsRepositoryFixture.deleteAllBookings(user.id, user.email);
await app.close();
});
});
});
@@ -0,0 +1,292 @@
import { bootstrap } from "@/app";
import { AppModule } from "@/app.module";
import { CancelBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/cancel-booking.output";
import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output";
import { RescheduleBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/reschedule-booking.output";
import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input";
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service";
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { ApiKeysRepositoryFixture } from "test/fixtures/repository/api-keys.repository.fixture";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
import { withApiAuth } from "test/utils/withApiAuth";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import {
AttendeeScheduledEmail,
OrganizerScheduledEmail,
AttendeeRescheduledEmail,
OrganizerRescheduledEmail,
AttendeeCancelledEmail,
OrganizerCancelledEmail,
} from "@calcom/platform-libraries";
import {
CreateBookingInput_2024_08_13,
BookingOutput_2024_08_13,
RescheduleBookingInput_2024_08_13,
CancelBookingInput_2024_08_13,
} from "@calcom/platform-types";
import { Team } from "@calcom/prisma/client";
jest.spyOn(AttendeeScheduledEmail.prototype as any, "getHtml").mockImplementation(async function () {
return "<html><body>Mocked Email Content</body></html>";
});
jest.spyOn(OrganizerScheduledEmail.prototype as any, "getHtml").mockImplementation(async function () {
return "<html><body>Mocked Email Content</body></html>";
});
jest
.spyOn(AttendeeRescheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(OrganizerRescheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(AttendeeCancelledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(OrganizerCancelledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
describe("Bookings Endpoints 2024-08-13", () => {
describe("With api key", () => {
let app: INestApplication;
let organization: Team;
let userRepositoryFixture: UserRepositoryFixture;
let bookingsRepositoryFixture: BookingsRepositoryFixture;
let schedulesService: SchedulesService_2024_04_15;
let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
let teamRepositoryFixture: TeamRepositoryFixture;
let apiKeysRepositoryFixture: ApiKeysRepositoryFixture;
let apiKeyString: string;
const userEmail = "bookings-controller-e2e@api.com";
let user: User;
let eventTypeId: number;
let createdBooking: BookingOutput_2024_08_13;
let rescheduledBooking: BookingOutput_2024_08_13;
beforeAll(async () => {
const moduleRef = await withApiAuth(
userEmail,
Test.createTestingModule({
imports: [AppModule, PrismaModule, UsersModule, SchedulesModule_2024_04_15],
})
)
.overrideGuard(PermissionsGuard)
.useValue({
canActivate: () => true,
})
.compile();
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
bookingsRepositoryFixture = new BookingsRepositoryFixture(moduleRef);
eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef);
teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
schedulesService = moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15);
apiKeysRepositoryFixture = new ApiKeysRepositoryFixture(moduleRef);
organization = await teamRepositoryFixture.create({ name: "organization bookings" });
user = await userRepositoryFixture.create({
email: userEmail,
});
const { keyString } = await apiKeysRepositoryFixture.createApiKey(user.id, null);
apiKeyString = keyString;
const userSchedule: CreateScheduleInput_2024_04_15 = {
name: "working time",
timeZone: "Europe/Rome",
isDefault: true,
};
await schedulesService.createUserSchedule(user.id, userSchedule);
const event = await eventTypesRepositoryFixture.create(
{ title: "peer coding", slug: "peer-coding", length: 60 },
user.id
);
eventTypeId = event.id;
app = moduleRef.createNestApplication();
bootstrap(app as NestExpressApplication);
await app.init();
});
it("should be defined", () => {
expect(userRepositoryFixture).toBeDefined();
expect(user).toBeDefined();
});
it("should create a booking with api key", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
eventTypeId,
attendee: {
name: "Mr Key",
email: "mr_key@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
meetingUrl: "https://meet.google.com/abc-def-ghi",
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseDataIsBooking(responseBody.data)).toBe(true);
if (responseDataIsBooking(responseBody.data)) {
const data: BookingOutput_2024_08_13 = responseBody.data;
expect(data.id).toBeDefined();
expect(data.uid).toBeDefined();
expect(data.hosts[0].id).toEqual(user.id);
expect(data.status).toEqual("accepted");
expect(data.start).toEqual(body.start);
expect(data.end).toEqual(new Date(Date.UTC(2030, 0, 8, 14, 0, 0)).toISOString());
expect(data.duration).toEqual(60);
expect(data.eventTypeId).toEqual(eventTypeId);
expect(data.attendees[0]).toEqual({
name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone,
language: body.attendee.language,
absent: false,
});
expect(data.meetingUrl).toEqual(body.meetingUrl);
expect(data.absentHost).toEqual(false);
expect(AttendeeScheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).toHaveBeenCalled();
createdBooking = data;
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
it("should reschedule a booking with api key", async () => {
const body: RescheduleBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2035, 0, 8, 14, 0, 0)).toISOString(),
reschedulingReason: "Flying to mars that day",
};
return request(app.getHttpServer())
.post(`/v2/bookings/${createdBooking.uid}/reschedule`)
.send(body)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: RescheduleBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseDataIsBooking(responseBody.data)).toBe(true);
if (responseDataIsBooking(responseBody.data)) {
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const data: BookingOutput_2024_08_13 = responseBody.data;
expect(data.reschedulingReason).toEqual(body.reschedulingReason);
expect(data.start).toEqual(body.start);
expect(data.end).toEqual(new Date(Date.UTC(2035, 0, 8, 15, 0, 0)).toISOString());
expect(data.rescheduledFromUid).toEqual(createdBooking.uid);
expect(data.id).toBeDefined();
expect(data.uid).toBeDefined();
expect(data.hosts[0].id).toEqual(user.id);
expect(data.status).toEqual(createdBooking.status);
expect(data.duration).toEqual(createdBooking.duration);
expect(data.eventTypeId).toEqual(createdBooking.eventTypeId);
expect(data.attendees[0]).toEqual(createdBooking.attendees[0]);
expect(data.location).toEqual(createdBooking.location);
expect(data.absentHost).toEqual(createdBooking.absentHost);
expect(data.metadata).toEqual(createdBooking.metadata);
expect(AttendeeRescheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerRescheduledEmail.prototype.getHtml).toHaveBeenCalled();
rescheduledBooking = data;
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
it("should cancel booking", async () => {
const body: CancelBookingInput_2024_08_13 = {
cancellationReason: "Going on a vacation",
};
const booking = await bookingsRepositoryFixture.getByUid(rescheduledBooking.uid);
expect(booking).toBeDefined();
expect(booking?.status).toEqual("ACCEPTED");
return request(app.getHttpServer())
.post(`/v2/bookings/${rescheduledBooking.uid}/cancel`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: CancelBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const data: BookingOutput_2024_08_13 = responseBody.data;
expect(data.id).toBeDefined();
expect(data.uid).toBeDefined();
expect(data.hosts[0].id).toEqual(user.id);
expect(data.status).toEqual("cancelled");
expect(data.cancellationReason).toEqual(body.cancellationReason);
expect(data.start).toEqual(rescheduledBooking.start);
expect(data.end).toEqual(rescheduledBooking.end);
expect(data.duration).toEqual(rescheduledBooking.duration);
expect(data.eventTypeId).toEqual(rescheduledBooking.eventTypeId);
expect(data.attendees[0]).toEqual(rescheduledBooking.attendees[0]);
expect(data.location).toEqual(rescheduledBooking.location);
expect(data.absentHost).toEqual(rescheduledBooking.absentHost);
const cancelledBooking = await bookingsRepositoryFixture.getByUid(rescheduledBooking.uid);
expect(cancelledBooking).toBeDefined();
expect(cancelledBooking?.status).toEqual("CANCELLED");
expect(AttendeeCancelledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerCancelledEmail.prototype.getHtml).toHaveBeenCalled();
});
});
function responseDataIsBooking(data: any): data is BookingOutput_2024_08_13 {
return !Array.isArray(data) && typeof data === "object" && data && "id" in data;
}
afterAll(async () => {
await teamRepositoryFixture.delete(organization.id);
await userRepositoryFixture.deleteByEmail(user.email);
await bookingsRepositoryFixture.deleteAllBookings(user.id, user.email);
await app.close();
});
});
});
@@ -0,0 +1,526 @@
import { bootstrap } from "@/app";
import { AppModule } from "@/app.module";
import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output";
import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input";
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service";
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
import { randomNumber } from "test/utils/randomNumber";
import { withApiAuth } from "test/utils/withApiAuth";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import {
CreateBookingInput_2024_08_13,
GetBookingOutput_2024_08_13,
GetSeatedBookingOutput_2024_08_13,
} from "@calcom/platform-types";
import { BookingOutput_2024_08_13 } from "@calcom/platform-types";
import { Booking, PlatformOAuthClient, Team, User } from "@calcom/prisma/client";
const suffix = randomNumber();
describe("Bookings Endpoints 2024-08-13", () => {
describe("Booking fields", () => {
let app: INestApplication;
let organization: Team;
let userRepositoryFixture: UserRepositoryFixture;
let bookingsRepositoryFixture: BookingsRepositoryFixture;
let schedulesService: SchedulesService_2024_04_15;
let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
let oauthClientRepositoryFixture: OAuthClientRepositoryFixture;
let oAuthClient: PlatformOAuthClient;
let teamRepositoryFixture: TeamRepositoryFixture;
const userEmail = `alice-${suffix}@api.com`;
let user: User;
let bookingWithSplitName: Booking;
const splitName = {
firstName: "Oldie",
lastName: "Goldie",
};
let seatedBookingWithSplitName: Booking;
let eventTypeWithBookingFieldsId: number;
beforeAll(async () => {
const moduleRef = await withApiAuth(
userEmail,
Test.createTestingModule({
imports: [AppModule, PrismaModule, UsersModule, SchedulesModule_2024_04_15],
})
)
.overrideGuard(PermissionsGuard)
.useValue({
canActivate: () => true,
})
.compile();
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
bookingsRepositoryFixture = new BookingsRepositoryFixture(moduleRef);
eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef);
oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef);
teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
schedulesService = moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15);
organization = await teamRepositoryFixture.create({ name: `booking fields ${suffix}` });
oAuthClient = await createOAuthClient(organization.id);
user = await userRepositoryFixture.create({
email: userEmail,
platformOAuthClients: {
connect: {
id: oAuthClient.id,
},
},
});
const userSchedule: CreateScheduleInput_2024_04_15 = {
name: "working time",
timeZone: "Europe/Rome",
isDefault: true,
};
await schedulesService.createUserSchedule(user.id, userSchedule);
const event = await eventTypesRepositoryFixture.create(
{ title: "peer coding", slug: `normal-booking-${randomNumber()}`, length: 60 },
user.id
);
bookingWithSplitName = await bookingsRepositoryFixture.create({
user: {
connect: {
id: user.id,
},
},
startTime: new Date(Date.UTC(2020, 0, 8, 12, 0, 0)),
endTime: new Date(Date.UTC(2020, 0, 8, 13, 0, 0)),
title: "peer coding lets goo",
uid: "booking",
eventType: {
connect: {
id: event.id,
},
},
location: "integrations:daily",
customInputs: {},
metadata: {},
responses: {
name: splitName,
email: "oldie@gmail.com",
},
attendees: {
create: {
email: "oldie@gmail.com",
name: "Oldie Goldie",
locale: "lv",
timeZone: "Europe/Rome",
},
},
});
const seatedEvent = await eventTypesRepositoryFixture.create(
{ title: "peer coding", slug: `seated-${randomNumber()}`, length: 60, seatsPerTimeSlot: 3 },
user.id
);
seatedBookingWithSplitName = await bookingsRepositoryFixture.create({
user: {
connect: {
id: user.id,
},
},
startTime: new Date(Date.UTC(2020, 0, 8, 14, 0, 0)),
endTime: new Date(Date.UTC(2020, 0, 8, 15, 0, 0)),
title: "peer coding lets goo",
uid: "seated-booking",
eventType: {
connect: {
id: seatedEvent.id,
},
},
location: "integrations:daily",
customInputs: {},
metadata: {},
responses: {
name: splitName,
email: "oldie@gmail.com",
},
attendees: {
create: {
email: "oldie@gmail.com",
name: "Oldie Goldie",
locale: "lv",
timeZone: "Europe/Rome",
bookingSeat: {
create: {
referenceUid: "unique-seat-uid",
data: {
responses: {
email: "oldie@gmail.com",
name: {
firstName: "Oldie",
lastName: "Goldie",
},
},
},
metadata: {},
booking: {
connect: {
uid: "seated-booking",
},
},
},
},
},
},
});
const eventTypeWithBookingFields = await eventTypesRepositoryFixture.create(
{
title: "peer coding with booking fields",
slug: `with-custom-booking-fields-${randomNumber()}`,
length: 60,
bookingFields: [
{
name: "name",
type: "name",
label: "",
sources: [
{
id: "default",
type: "default",
label: "Default",
},
],
variant: "fullName",
editable: "system",
required: true,
placeholder: "",
defaultLabel: "your_name",
variantsConfig: {
variants: {
fullName: {
fields: [
{
name: "fullName",
type: "text",
label: "your_name",
required: true,
placeholder: "",
},
],
},
firstAndLastName: {
fields: [
{
name: "firstName",
type: "text",
label: "name",
required: true,
placeholder: "lauris",
},
{
name: "lastName",
type: "text",
label: "surname",
required: true,
placeholder: "skraucis",
},
],
},
},
},
disableOnPrefill: false,
},
{
name: "email",
type: "email",
sources: [
{
id: "default",
type: "default",
label: "Default",
},
],
editable: "system",
required: true,
defaultLabel: "email_address",
},
{
name: "location",
type: "radioInput",
sources: [
{
id: "default",
type: "default",
label: "Default",
},
],
editable: "system",
required: false,
defaultLabel: "location",
getOptionsAt: "locations",
optionsInputs: {
phone: {
type: "phone",
required: true,
placeholder: "",
},
somewhereElse: {
type: "text",
required: true,
placeholder: "",
},
attendeeInPerson: {
type: "address",
required: true,
placeholder: "",
},
},
hideWhenJustOneOption: true,
},
{
name: "title",
type: "text",
hidden: true,
sources: [
{
id: "default",
type: "default",
label: "Default",
},
],
editable: "system-but-optional",
required: true,
defaultLabel: "what_is_this_meeting_about",
defaultPlaceholder: "",
},
{
name: "notes",
type: "textarea",
sources: [
{
id: "default",
type: "default",
label: "Default",
},
],
editable: "system-but-optional",
required: false,
defaultLabel: "additional_notes",
defaultPlaceholder: "share_additional_notes",
},
{
name: "guests",
type: "multiemail",
hidden: false,
sources: [
{
id: "default",
type: "default",
label: "Default",
},
],
editable: "system-but-optional",
required: false,
defaultLabel: "additional_guests",
defaultPlaceholder: "email",
},
{
name: "rescheduleReason",
type: "textarea",
views: [
{
id: "reschedule",
label: "Reschedule View",
},
],
sources: [
{
id: "default",
type: "default",
label: "Default",
},
],
editable: "system-but-optional",
required: false,
defaultLabel: "reason_for_reschedule",
defaultPlaceholder: "reschedule_placeholder",
},
{
name: "favorite-movie",
type: "text",
label: "favorite movie",
sources: [
{
id: "user",
type: "user",
label: "User",
fieldRequired: true,
},
],
editable: "user",
required: true,
placeholder: "matrix",
disableOnPrefill: false,
},
],
},
user.id
);
eventTypeWithBookingFieldsId = eventTypeWithBookingFields.id;
app = moduleRef.createNestApplication();
bootstrap(app as NestExpressApplication);
await app.init();
});
async function createOAuthClient(organizationId: number) {
const data = {
logo: "logo-url",
name: "name",
redirectUris: ["http://localhost:5555"],
permissions: 32,
};
const secret = "secret";
const client = await oauthClientRepositoryFixture.create(organizationId, data, secret);
return client;
}
describe("get individual booking", () => {
it("should should get a seated booking with split name responses", async () => {
return request(app.getHttpServer())
.get(`/v2/bookings/${bookingWithSplitName.uid}`)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: GetBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseDataIsBooking(responseBody.data)).toBe(true);
if (responseDataIsBooking(responseBody.data)) {
const data: BookingOutput_2024_08_13 = responseBody.data;
expect(data.attendees[0].name).toEqual(`${splitName.firstName} ${splitName.lastName}`);
expect(data.bookingFieldsResponses.name).toEqual(splitName);
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
it("should should get a seated booking with split name responses", async () => {
return request(app.getHttpServer())
.get(`/v2/bookings/${seatedBookingWithSplitName.uid}`)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: GetBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseDataIsBooking(responseBody.data)).toBe(true);
if (responseDataIsBooking(responseBody.data)) {
// eslint-disable-next-line
// @ts-ignore
const data: GetSeatedBookingOutput_2024_08_13 = responseBody.data;
console.log("asap data", JSON.stringify(data, null, 2));
expect(data.attendees[0].name).toEqual(`${splitName.firstName} ${splitName.lastName}`);
expect(data.attendees[0].bookingFieldsResponses.name).toEqual(splitName);
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
});
describe("make booking", () => {
it("should not be able to book an event type with custom required booking fields if they are missing in bookingFieldsResponses", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
eventTypeId: eventTypeWithBookingFieldsId,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
};
return request(app.getHttpServer())
.post(`/v2/bookings`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(400);
});
it("should be able to book an event type with custom required booking fields", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
eventTypeId: eventTypeWithBookingFieldsId,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
"favorite-movie": "lord of the rings",
},
};
return request(app.getHttpServer())
.post(`/v2/bookings`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseDataIsBooking(responseBody.data)).toBe(true);
if (responseDataIsBooking(responseBody.data)) {
const data: BookingOutput_2024_08_13 = responseBody.data;
expect(data.bookingFieldsResponses["favorite-movie"]).toEqual("lord of the rings");
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
});
afterAll(async () => {
await oauthClientRepositoryFixture.delete(oAuthClient.id);
await teamRepositoryFixture.delete(organization.id);
await userRepositoryFixture.deleteByEmail(user.email);
await bookingsRepositoryFixture.deleteAllBookings(user.id, user.email);
await app.close();
});
});
function responseDataIsBooking(data: any): data is BookingOutput_2024_08_13 {
return !Array.isArray(data) && typeof data === "object" && data && "id" in data;
}
});
@@ -1,6 +1,5 @@
import { bootstrap } from "@/app"; import { bootstrap } from "@/app";
import { AppModule } from "@/app.module"; import { AppModule } from "@/app.module";
import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output";
import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input"; import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input";
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module"; import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service"; import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service";
@@ -20,26 +19,9 @@ import { UserRepositoryFixture } from "test/fixtures/repository/users.repository
import { withApiAuth } from "test/utils/withApiAuth"; import { withApiAuth } from "test/utils/withApiAuth";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants"; import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import {
AttendeeScheduledEmail,
OrganizerScheduledEmail,
AttendeeDeclinedEmail,
} from "@calcom/platform-libraries";
import { BookingOutput_2024_08_13, GetBookingOutput_2024_08_13 } from "@calcom/platform-types"; import { BookingOutput_2024_08_13, GetBookingOutput_2024_08_13 } from "@calcom/platform-types";
import { Booking, PlatformOAuthClient, Team } from "@calcom/prisma/client"; import { Booking, PlatformOAuthClient, Team } from "@calcom/prisma/client";
jest.spyOn(AttendeeScheduledEmail.prototype as any, "getHtml").mockImplementation(async function () {
return "<html><body>Mocked Email Content</body></html>";
});
jest.spyOn(OrganizerScheduledEmail.prototype as any, "getHtml").mockImplementation(async function () {
return "<html><body>Mocked Email Content</body></html>";
});
jest.spyOn(AttendeeDeclinedEmail.prototype as any, "getHtml").mockImplementation(async function () {
return "<html><body>Mocked Email Content</body></html>";
});
describe("Bookings Endpoints 2024-08-13", () => { describe("Bookings Endpoints 2024-08-13", () => {
describe("Bookings confirmation", () => { describe("Bookings confirmation", () => {
let app: INestApplication; let app: INestApplication;
@@ -87,6 +69,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
user = await userRepositoryFixture.create({ user = await userRepositoryFixture.create({
email: userEmail, email: userEmail,
platformOAuthClients: {
connect: {
id: oAuthClient.id,
},
},
}); });
const userSchedule: CreateScheduleInput_2024_04_15 = { const userSchedule: CreateScheduleInput_2024_04_15 = {
@@ -0,0 +1,474 @@
import { bootstrap } from "@/app";
import { AppModule } from "@/app.module";
import { CancelBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/cancel-booking.output";
import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output";
import { RescheduleBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/reschedule-booking.output";
import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input";
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
import { withApiAuth } from "test/utils/withApiAuth";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import {
OrganizerScheduledEmail,
AttendeeScheduledEmail,
OrganizerRescheduledEmail,
AttendeeRescheduledEmail,
OrganizerCancelledEmail,
AttendeeCancelledEmail,
AttendeeRequestEmail,
OrganizerRequestEmail,
AttendeeDeclinedEmail,
} from "@calcom/platform-libraries";
import {
CreateBookingInput_2024_08_13,
BookingOutput_2024_08_13,
RescheduleBookingInput_2024_08_13,
GetBookingOutput_2024_08_13,
} from "@calcom/platform-types";
import { CancelBookingInput_2024_08_13 } from "@calcom/platform-types";
import { Team } from "@calcom/prisma/client";
jest
.spyOn(AttendeeScheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(OrganizerScheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(AttendeeRescheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(OrganizerRescheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(AttendeeCancelledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(OrganizerCancelledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(AttendeeRequestEmail.prototype, "getHtmlRequestEmail")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(OrganizerRequestEmail.prototype, "getHtmlRequestEmail")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(AttendeeDeclinedEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
type EmailSetup = {
user: User;
eventTypeId: number;
createdBookingUid: string;
rescheduledBookingUid: string;
};
describe("Bookings Endpoints 2024-08-13 confirm emails", () => {
let app: INestApplication;
let organization: Team;
let userRepositoryFixture: UserRepositoryFixture;
let bookingsRepositoryFixture: BookingsRepositoryFixture;
let schedulesService: SchedulesService_2024_04_15;
let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
let oauthClientRepositoryFixture: OAuthClientRepositoryFixture;
let teamRepositoryFixture: TeamRepositoryFixture;
let emailsEnabledSetup: EmailSetup;
let emailsDisabledSetup: EmailSetup;
const authEmail = `admin-${Math.floor(Math.random() * 1000)}@example.com`;
beforeAll(async () => {
const moduleRef = await withApiAuth(
authEmail,
Test.createTestingModule({
imports: [AppModule, PrismaModule, UsersModule, SchedulesModule_2024_04_15],
})
)
.overrideGuard(PermissionsGuard)
.useValue({
canActivate: () => true,
})
.compile();
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
bookingsRepositoryFixture = new BookingsRepositoryFixture(moduleRef);
eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef);
oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef);
teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
schedulesService = moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15);
organization = await teamRepositoryFixture.create({ name: "organization bookings" });
await setupEnabledEmails();
await setupDisabledEmails();
await userRepositoryFixture.create({
email: authEmail,
organization: {
connect: {
id: organization.id,
},
},
role: "ADMIN",
});
app = moduleRef.createNestApplication();
bootstrap(app as NestExpressApplication);
await app.init();
});
async function setupEnabledEmails() {
const oAuthClientEmailsEnabled = await createOAuthClient(organization.id, true);
const user = await userRepositoryFixture.create({
email: `alice-${Math.floor(Math.random() * 1000)}@gmail.com`,
platformOAuthClients: {
connect: {
id: oAuthClientEmailsEnabled.id,
},
},
});
const userSchedule: CreateScheduleInput_2024_04_15 = {
name: "working time",
timeZone: "Europe/Rome",
isDefault: true,
};
await schedulesService.createUserSchedule(user.id, userSchedule);
const event = await eventTypesRepositoryFixture.create(
{ title: "peer coding", slug: "peer-coding", length: 60, requiresConfirmation: true },
user.id
);
emailsEnabledSetup = {
user,
eventTypeId: event.id,
createdBookingUid: "",
rescheduledBookingUid: "",
};
}
async function setupDisabledEmails() {
const oAuthClientEmailsDisabled = await createOAuthClient(organization.id, false);
const user = await userRepositoryFixture.create({
email: `bob-${Math.floor(Math.random() * 1000)}@gmail.com`,
platformOAuthClients: {
connect: {
id: oAuthClientEmailsDisabled.id,
},
},
});
const userSchedule: CreateScheduleInput_2024_04_15 = {
name: "working time",
timeZone: "Europe/Rome",
isDefault: true,
};
await schedulesService.createUserSchedule(user.id, userSchedule);
const event = await eventTypesRepositoryFixture.create(
{ title: "peer coding", slug: "peer-coding", length: 60, requiresConfirmation: true },
user.id
);
emailsDisabledSetup = {
user,
eventTypeId: event.id,
createdBookingUid: "",
rescheduledBookingUid: "",
};
}
async function createOAuthClient(organizationId: number, emailsEnabled: boolean) {
const data = {
logo: "logo-url",
name: "name",
redirectUris: ["http://localhost:5555"],
permissions: 32,
areEmailsEnabled: emailsEnabled,
};
const secret = "secret";
const client = await oauthClientRepositoryFixture.create(organizationId, data, secret);
return client;
}
describe("OAuth client managed user bookings - emails disabled", () => {
beforeEach(async () => {
jest.clearAllMocks();
});
it("should not send an email when creating a booking that requires confirmation", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
eventTypeId: emailsDisabledSetup.eventTypeId,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
userId: "100",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
if (responseDataIsBooking(responseBody.data)) {
expect(responseBody.data.status).toEqual("pending");
expect(AttendeeScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(AttendeeRequestEmail.prototype.getHtmlRequestEmail).not.toHaveBeenCalled();
expect(OrganizerRequestEmail.prototype.getHtmlRequestEmail).not.toHaveBeenCalled();
emailsDisabledSetup.createdBookingUid = responseBody.data.uid;
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
it("should not send an email when confirming a booking", async () => {
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsDisabledSetup.createdBookingUid}/confirm`)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: GetBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
emailsDisabledSetup.rescheduledBookingUid = responseBody.data.uid;
});
});
it("should not send an email when creating a booking that requires confirmation", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 10, 0, 0)).toISOString(),
eventTypeId: emailsDisabledSetup.eventTypeId,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
userId: "100",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
if (responseDataIsBooking(responseBody.data)) {
expect(responseBody.data.status).toEqual("pending");
expect(AttendeeRequestEmail.prototype.getHtmlRequestEmail).not.toHaveBeenCalled();
expect(OrganizerRequestEmail.prototype.getHtmlRequestEmail).not.toHaveBeenCalled();
emailsDisabledSetup.createdBookingUid = responseBody.data.uid;
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
it("should not send an email when declining a booking", async () => {
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsDisabledSetup.createdBookingUid}/decline`)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: GetBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeDeclinedEmail.prototype.getHtml).not.toHaveBeenCalled();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
emailsDisabledSetup.rescheduledBookingUid = responseBody.data.uid;
});
});
});
describe("OAuth client managed user bookings - emails enabled", () => {
beforeEach(async () => {
jest.clearAllMocks();
});
it("should send an email when creating a booking that requires confirmation", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
eventTypeId: emailsEnabledSetup.eventTypeId,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
userId: "100",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
if (responseDataIsBooking(responseBody.data)) {
expect(responseBody.data.status).toEqual("pending");
expect(AttendeeRequestEmail.prototype.getHtmlRequestEmail).toHaveBeenCalled();
expect(OrganizerRequestEmail.prototype.getHtmlRequestEmail).toHaveBeenCalled();
emailsEnabledSetup.createdBookingUid = responseBody.data.uid;
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
it("should send an email when confirming a booking", async () => {
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsEnabledSetup.createdBookingUid}/confirm`)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: GetBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeScheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).toHaveBeenCalled();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
emailsEnabledSetup.rescheduledBookingUid = responseBody.data.uid;
});
});
it("should send an email when creating a booking that requires confirmation", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 10, 0, 0)).toISOString(),
eventTypeId: emailsEnabledSetup.eventTypeId,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
userId: "100",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
if (responseDataIsBooking(responseBody.data)) {
expect(responseBody.data.status).toEqual("pending");
expect(AttendeeRequestEmail.prototype.getHtmlRequestEmail).toHaveBeenCalled();
expect(OrganizerRequestEmail.prototype.getHtmlRequestEmail).toHaveBeenCalled();
emailsEnabledSetup.createdBookingUid = responseBody.data.uid;
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
it("should send an email when declining a booking", async () => {
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsEnabledSetup.createdBookingUid}/decline`)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: GetBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeDeclinedEmail.prototype.getHtml).toHaveBeenCalled();
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
emailsEnabledSetup.rescheduledBookingUid = responseBody.data.uid;
});
});
});
afterAll(async () => {
await teamRepositoryFixture.delete(organization.id);
await userRepositoryFixture.deleteByEmail(emailsEnabledSetup.user.email);
await userRepositoryFixture.deleteByEmail(emailsDisabledSetup.user.email);
await userRepositoryFixture.deleteByEmail(authEmail);
await bookingsRepositoryFixture.deleteAllBookings(
emailsEnabledSetup.user.id,
emailsEnabledSetup.user.email
);
await bookingsRepositoryFixture.deleteAllBookings(
emailsDisabledSetup.user.id,
emailsDisabledSetup.user.email
);
await app.close();
});
function responseDataIsBooking(data: any): data is BookingOutput_2024_08_13 {
return !Array.isArray(data) && typeof data === "object" && data && "id" in data;
}
});
@@ -0,0 +1,986 @@
import { bootstrap } from "@/app";
import { AppModule } from "@/app.module";
import { CancelBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/cancel-booking.output";
import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output";
import { RescheduleBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/reschedule-booking.output";
import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input";
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { UsersModule } from "@/modules/users/users.module";
import { createParamDecorator, INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
import { HostsRepositoryFixture } from "test/fixtures/repository/hosts.repository.fixture";
import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture";
import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
import { ProfileRepositoryFixture } from "test/fixtures/repository/profiles.repository.fixture";
import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
import { withApiAuth } from "test/utils/withApiAuth";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import {
OrganizerScheduledEmail,
AttendeeScheduledEmail,
OrganizerRescheduledEmail,
AttendeeRescheduledEmail,
OrganizerCancelledEmail,
AttendeeCancelledEmail,
OrganizerReassignedEmail,
AttendeeUpdatedEmail,
} from "@calcom/platform-libraries";
import {
CreateBookingInput_2024_08_13,
BookingOutput_2024_08_13,
RescheduleBookingInput_2024_08_13,
} from "@calcom/platform-types";
import { CancelBookingInput_2024_08_13 } from "@calcom/platform-types";
import { Team } from "@calcom/prisma/client";
jest
.spyOn(AttendeeScheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(OrganizerScheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(AttendeeRescheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(OrganizerRescheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(AttendeeCancelledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(OrganizerCancelledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(OrganizerReassignedEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(AttendeeUpdatedEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
type EmailSetup = {
team: Team;
member1: User;
member2: User;
collectiveEventType: {
id: number;
createdBookingUid: string;
rescheduledBookingUid: string;
};
roundRobinEventType: {
id: number;
createdBookingUid: string;
rescheduledBookingUid: string;
currentHostId: number;
};
};
describe("Bookings Endpoints 2024-08-13 team emails", () => {
let app: INestApplication;
let organization: Team;
let userRepositoryFixture: UserRepositoryFixture;
let bookingsRepositoryFixture: BookingsRepositoryFixture;
let schedulesService: SchedulesService_2024_04_15;
let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
let oauthClientRepositoryFixture: OAuthClientRepositoryFixture;
let teamRepositoryFixture: TeamRepositoryFixture;
let profileRepositoryFixture: ProfileRepositoryFixture;
let membershipsRepositoryFixture: MembershipRepositoryFixture;
let hostsRepositoryFixture: HostsRepositoryFixture;
let emailsEnabledSetup: EmailSetup;
let emailsDisabledSetup: EmailSetup;
const authEmail = "admin@example.com";
beforeAll(async () => {
const moduleRef = await withApiAuth(
authEmail,
Test.createTestingModule({
imports: [AppModule, PrismaModule, UsersModule, SchedulesModule_2024_04_15],
})
)
.overrideGuard(PermissionsGuard)
.useValue({
canActivate: () => true,
})
.compile();
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
bookingsRepositoryFixture = new BookingsRepositoryFixture(moduleRef);
eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef);
oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef);
teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
profileRepositoryFixture = new ProfileRepositoryFixture(moduleRef);
membershipsRepositoryFixture = new MembershipRepositoryFixture(moduleRef);
hostsRepositoryFixture = new HostsRepositoryFixture(moduleRef);
schedulesService = moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15);
organization = await teamRepositoryFixture.create({ name: "organization bookings" });
await setupEnabledEmails();
await setupDisabledEmails();
await userRepositoryFixture.create({
email: authEmail,
organization: {
connect: {
id: organization.id,
},
},
});
app = moduleRef.createNestApplication();
bootstrap(app as NestExpressApplication);
await app.init();
});
async function setupEnabledEmails() {
const oAuthClientEmailsEnabled = await createOAuthClient(organization.id, true);
const team = await teamRepositoryFixture.create({
name: "team 1",
isOrganization: false,
parent: { connect: { id: organization.id } },
createdByOAuthClient: {
connect: {
id: oAuthClientEmailsEnabled.id,
},
},
});
const member1 = await userRepositoryFixture.create({
email: "alice@gmail.com",
platformOAuthClients: {
connect: {
id: oAuthClientEmailsEnabled.id,
},
},
});
const member2 = await userRepositoryFixture.create({
email: "bob@gmail.com",
platformOAuthClients: {
connect: {
id: oAuthClientEmailsEnabled.id,
},
},
});
const userSchedule: CreateScheduleInput_2024_04_15 = {
name: "working time",
timeZone: "Europe/Rome",
isDefault: true,
};
await schedulesService.createUserSchedule(member1.id, userSchedule);
await schedulesService.createUserSchedule(member2.id, userSchedule);
await profileRepositoryFixture.create({
uid: `usr-${member1.id}`,
username: member1.email,
organization: {
connect: {
id: organization.id,
},
},
user: {
connect: {
id: member1.id,
},
},
});
await profileRepositoryFixture.create({
uid: `usr-${member2.id}`,
username: member2.email,
organization: {
connect: {
id: organization.id,
},
},
user: {
connect: {
id: member2.id,
},
},
});
await membershipsRepositoryFixture.create({
role: "MEMBER",
user: { connect: { id: member1.id } },
team: { connect: { id: team.id } },
accepted: true,
});
await membershipsRepositoryFixture.create({
role: "MEMBER",
user: { connect: { id: member2.id } },
team: { connect: { id: team.id } },
accepted: true,
});
const collectiveEvent = await eventTypesRepositoryFixture.createTeamEventType({
schedulingType: "COLLECTIVE",
team: {
connect: { id: team.id },
},
title: "Collective Event Type",
slug: "collective-event-type",
length: 60,
assignAllTeamMembers: true,
bookingFields: [],
locations: [],
});
await hostsRepositoryFixture.create({
isFixed: true,
user: {
connect: {
id: member1.id,
},
},
eventType: {
connect: {
id: collectiveEvent.id,
},
},
});
await hostsRepositoryFixture.create({
isFixed: true,
user: {
connect: {
id: member2.id,
},
},
eventType: {
connect: {
id: collectiveEvent.id,
},
},
});
const roundRobinEvent = await eventTypesRepositoryFixture.createTeamEventType({
schedulingType: "ROUND_ROBIN",
team: {
connect: { id: team.id },
},
title: "Round Robin Event Type",
slug: "round-robin-event-type",
length: 60,
assignAllTeamMembers: false,
bookingFields: [],
locations: [],
});
await hostsRepositoryFixture.create({
isFixed: false,
user: {
connect: {
id: member1.id,
},
},
eventType: {
connect: {
id: roundRobinEvent.id,
},
},
});
await hostsRepositoryFixture.create({
isFixed: false,
user: {
connect: {
id: member2.id,
},
},
eventType: {
connect: {
id: roundRobinEvent.id,
},
},
});
emailsEnabledSetup = {
team,
member1: member1,
member2: member2,
collectiveEventType: {
id: collectiveEvent.id,
createdBookingUid: "",
rescheduledBookingUid: "",
},
roundRobinEventType: {
id: roundRobinEvent.id,
createdBookingUid: "",
rescheduledBookingUid: "",
currentHostId: 0,
},
};
}
async function setupDisabledEmails() {
const oAuthClientEmailsDisabled = await createOAuthClient(organization.id, false);
const team = await teamRepositoryFixture.create({
name: "team 2",
isOrganization: false,
parent: { connect: { id: organization.id } },
createdByOAuthClient: {
connect: {
id: oAuthClientEmailsDisabled.id,
},
},
});
const member1 = await userRepositoryFixture.create({
email: "charlie@gmail.com",
platformOAuthClients: {
connect: {
id: oAuthClientEmailsDisabled.id,
},
},
});
const member2 = await userRepositoryFixture.create({
email: "dean@gmail.com",
platformOAuthClients: {
connect: {
id: oAuthClientEmailsDisabled.id,
},
},
});
const userSchedule: CreateScheduleInput_2024_04_15 = {
name: "working time",
timeZone: "Europe/Rome",
isDefault: true,
};
await schedulesService.createUserSchedule(member1.id, userSchedule);
await schedulesService.createUserSchedule(member2.id, userSchedule);
await profileRepositoryFixture.create({
uid: `usr-${member1.id}`,
username: member1.email,
organization: {
connect: {
id: organization.id,
},
},
user: {
connect: {
id: member1.id,
},
},
});
await profileRepositoryFixture.create({
uid: `usr-${member2.id}`,
username: member2.email,
organization: {
connect: {
id: organization.id,
},
},
user: {
connect: {
id: member2.id,
},
},
});
await membershipsRepositoryFixture.create({
role: "MEMBER",
user: { connect: { id: member1.id } },
team: { connect: { id: team.id } },
accepted: true,
});
await membershipsRepositoryFixture.create({
role: "MEMBER",
user: { connect: { id: member2.id } },
team: { connect: { id: team.id } },
accepted: true,
});
const collectiveEvent = await eventTypesRepositoryFixture.createTeamEventType({
schedulingType: "COLLECTIVE",
team: {
connect: { id: team.id },
},
title: "Collective Event Type",
slug: "collective-event-type",
length: 60,
assignAllTeamMembers: true,
bookingFields: [],
locations: [],
});
await hostsRepositoryFixture.create({
isFixed: true,
user: {
connect: {
id: member1.id,
},
},
eventType: {
connect: {
id: collectiveEvent.id,
},
},
});
await hostsRepositoryFixture.create({
isFixed: true,
user: {
connect: {
id: member2.id,
},
},
eventType: {
connect: {
id: collectiveEvent.id,
},
},
});
const roundRobinEvent = await eventTypesRepositoryFixture.createTeamEventType({
schedulingType: "ROUND_ROBIN",
team: {
connect: { id: team.id },
},
title: "Round Robin Event Type",
slug: "round-robin-event-type",
length: 60,
assignAllTeamMembers: false,
bookingFields: [],
locations: [],
});
await hostsRepositoryFixture.create({
isFixed: false,
user: {
connect: {
id: member1.id,
},
},
eventType: {
connect: {
id: roundRobinEvent.id,
},
},
});
await hostsRepositoryFixture.create({
isFixed: false,
user: {
connect: {
id: member2.id,
},
},
eventType: {
connect: {
id: roundRobinEvent.id,
},
},
});
emailsDisabledSetup = {
team,
member1: member1,
member2: member2,
collectiveEventType: {
id: collectiveEvent.id,
createdBookingUid: "",
rescheduledBookingUid: "",
},
roundRobinEventType: {
id: roundRobinEvent.id,
createdBookingUid: "",
rescheduledBookingUid: "",
currentHostId: 0,
},
};
}
async function createOAuthClient(organizationId: number, emailsEnabled: boolean) {
const data = {
logo: "logo-url",
name: "name",
redirectUris: ["http://localhost:5555"],
permissions: 32,
areEmailsEnabled: emailsEnabled,
};
const secret = "secret";
const client = await oauthClientRepositoryFixture.create(organizationId, data, secret);
return client;
}
beforeEach(async () => {
jest.clearAllMocks();
});
describe("OAuth client team bookings - emails disabled", () => {
describe("book", () => {
it("should not send an email when booking collective event", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 9, 0, 0)).toISOString(),
eventTypeId: emailsDisabledSetup.collectiveEventType.id,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
userId: "100",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
if (responseDataIsBooking(responseBody.data)) {
expect(AttendeeScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
emailsDisabledSetup.collectiveEventType.createdBookingUid = responseBody.data.uid;
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
it("should not send an email when booking round robin event", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 10, 0, 0)).toISOString(),
eventTypeId: emailsDisabledSetup.roundRobinEventType.id,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
userId: "100",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
if (responseDataIsBooking(responseBody.data)) {
expect(AttendeeScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
emailsDisabledSetup.roundRobinEventType.createdBookingUid = responseBody.data.uid;
emailsDisabledSetup.roundRobinEventType.currentHostId = responseBody.data.hosts[0].id;
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
});
describe("reschedule", () => {
it("should not send an email when rescheduling collective booking", async () => {
const body: RescheduleBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2035, 0, 8, 11, 0, 0)).toISOString(),
reschedulingReason: "Flying to mars that day",
};
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsDisabledSetup.collectiveEventType.createdBookingUid}/reschedule`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: RescheduleBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(AttendeeRescheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerRescheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
emailsDisabledSetup.collectiveEventType.rescheduledBookingUid = responseBody.data.uid;
});
});
it("should not send an email when rescheduling round robin booking", async () => {
const body: RescheduleBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2035, 0, 8, 12, 0, 0)).toISOString(),
reschedulingReason: "Flying to mars that day",
};
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsDisabledSetup.roundRobinEventType.createdBookingUid}/reschedule`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: RescheduleBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(AttendeeRescheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerRescheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
emailsDisabledSetup.roundRobinEventType.rescheduledBookingUid = responseBody.data.uid;
emailsDisabledSetup.roundRobinEventType.currentHostId = responseBody.data.hosts[0].id;
});
});
});
describe("reassign", () => {
it("should not send an email when manually reassigning round robin booking", async () => {
const reassignToId =
emailsDisabledSetup.roundRobinEventType.currentHostId === emailsDisabledSetup.member1.id
? emailsDisabledSetup.member2.id
: emailsDisabledSetup.member1.id;
return request(app.getHttpServer())
.post(
`/v2/bookings/${emailsDisabledSetup.roundRobinEventType.rescheduledBookingUid}/reassign/${reassignToId}`
)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: CancelBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeCancelledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(AttendeeUpdatedEmail.prototype.getHtml).not.toHaveBeenCalled();
emailsDisabledSetup.roundRobinEventType.currentHostId = reassignToId;
});
});
it("should not send an email when automatically reassigning round robin booking", async () => {
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsDisabledSetup.roundRobinEventType.rescheduledBookingUid}/reassign`)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: CancelBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeCancelledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerReassignedEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(AttendeeUpdatedEmail.prototype.getHtml).not.toHaveBeenCalled();
});
});
});
describe("cancel", () => {
it("should not send an email when cancelling a collective booking", async () => {
const body: CancelBookingInput_2024_08_13 = {
cancellationReason: "Going on a vacation",
};
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsDisabledSetup.collectiveEventType.rescheduledBookingUid}/cancel`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: CancelBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeCancelledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerCancelledEmail.prototype.getHtml).not.toHaveBeenCalled();
});
});
});
it("should not send an email when cancelling a round robin booking", async () => {
const body: CancelBookingInput_2024_08_13 = {
cancellationReason: "Going on a vacation",
};
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsDisabledSetup.roundRobinEventType.rescheduledBookingUid}/cancel`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: CancelBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeCancelledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerCancelledEmail.prototype.getHtml).not.toHaveBeenCalled();
});
});
});
describe("OAuth client team bookings - emails enabled", () => {
beforeEach(async () => {
jest.clearAllMocks();
});
describe("book", () => {
it("should send an email when booking collective event", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 9, 0, 0)).toISOString(),
eventTypeId: emailsEnabledSetup.collectiveEventType.id,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
userId: "100",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
if (responseDataIsBooking(responseBody.data)) {
expect(AttendeeScheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).toHaveBeenCalled();
emailsEnabledSetup.collectiveEventType.createdBookingUid = responseBody.data.uid;
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
it("should send an email when booking round robin event", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 10, 0, 0)).toISOString(),
eventTypeId: emailsEnabledSetup.roundRobinEventType.id,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
userId: "100",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
if (responseDataIsBooking(responseBody.data)) {
expect(AttendeeScheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).toHaveBeenCalled();
emailsEnabledSetup.roundRobinEventType.createdBookingUid = responseBody.data.uid;
emailsEnabledSetup.roundRobinEventType.currentHostId = responseBody.data.hosts[0].id;
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
});
describe("reschedule", () => {
it("should send an email when rescheduling collective booking", async () => {
const body: RescheduleBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2035, 0, 8, 11, 0, 0)).toISOString(),
reschedulingReason: "Flying to mars that day",
};
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsEnabledSetup.collectiveEventType.createdBookingUid}/reschedule`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: RescheduleBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(AttendeeRescheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerRescheduledEmail.prototype.getHtml).toHaveBeenCalled();
emailsEnabledSetup.collectiveEventType.rescheduledBookingUid = responseBody.data.uid;
});
});
it("should send an email when rescheduling round robin booking", async () => {
const body: RescheduleBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2035, 0, 8, 12, 0, 0)).toISOString(),
reschedulingReason: "Flying to mars that day",
};
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsEnabledSetup.roundRobinEventType.createdBookingUid}/reschedule`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: RescheduleBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(AttendeeRescheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerRescheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerCancelledEmail.prototype.getHtml).toHaveBeenCalled();
emailsEnabledSetup.roundRobinEventType.rescheduledBookingUid = responseBody.data.uid;
emailsEnabledSetup.roundRobinEventType.currentHostId = responseBody.data.hosts[0].id;
});
});
});
describe("reassign", () => {
it("should send an email when manually reassigning round robin booking", async () => {
const reassignToId =
emailsEnabledSetup.roundRobinEventType.currentHostId === emailsEnabledSetup.member1.id
? emailsEnabledSetup.member2.id
: emailsEnabledSetup.member1.id;
return request(app.getHttpServer())
.post(
`/v2/bookings/${emailsEnabledSetup.roundRobinEventType.rescheduledBookingUid}/reassign/${reassignToId}`
)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: CancelBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeCancelledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(AttendeeUpdatedEmail.prototype.getHtml).toHaveBeenCalled();
emailsDisabledSetup.roundRobinEventType.currentHostId = reassignToId;
});
});
it("should send an email when automatically reassigning round robin booking", async () => {
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsEnabledSetup.roundRobinEventType.rescheduledBookingUid}/reassign`)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: CancelBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeCancelledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerReassignedEmail.prototype.getHtml).toHaveBeenCalled();
expect(AttendeeUpdatedEmail.prototype.getHtml).toHaveBeenCalled();
});
});
});
describe("cancel", () => {
it("should send an email when cancelling a collective booking", async () => {
const body: CancelBookingInput_2024_08_13 = {
cancellationReason: "Going on a vacation",
};
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsEnabledSetup.collectiveEventType.rescheduledBookingUid}/cancel`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: CancelBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeCancelledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerCancelledEmail.prototype.getHtml).toHaveBeenCalled();
});
});
it("should send an email when cancelling round robin booking", async () => {
const body: CancelBookingInput_2024_08_13 = {
cancellationReason: "Going on a vacation",
};
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsEnabledSetup.roundRobinEventType.rescheduledBookingUid}/cancel`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: CancelBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeCancelledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerCancelledEmail.prototype.getHtml).toHaveBeenCalled();
});
});
});
});
afterAll(async () => {
await teamRepositoryFixture.delete(organization.id);
await userRepositoryFixture.deleteByEmail(emailsEnabledSetup.member1.email);
await userRepositoryFixture.deleteByEmail(emailsDisabledSetup.member1.email);
await bookingsRepositoryFixture.deleteAllBookings(
emailsEnabledSetup.member1.id,
emailsEnabledSetup.member1.email
);
await bookingsRepositoryFixture.deleteAllBookings(
emailsDisabledSetup.member1.id,
emailsDisabledSetup.member1.email
);
await app.close();
});
function responseDataIsBooking(data: any): data is BookingOutput_2024_08_13 {
return !Array.isArray(data) && typeof data === "object" && data && "id" in data;
}
});
@@ -0,0 +1,482 @@
import { bootstrap } from "@/app";
import { AppModule } from "@/app.module";
import { CancelBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/cancel-booking.output";
import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output";
import { RescheduleBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/reschedule-booking.output";
import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input";
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing";
import { User } from "@prisma/client";
import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
import {
OrganizerScheduledEmail,
AttendeeScheduledEmail,
OrganizerRescheduledEmail,
AttendeeRescheduledEmail,
OrganizerCancelledEmail,
AttendeeCancelledEmail,
} from "@calcom/platform-libraries";
import {
CreateBookingInput_2024_08_13,
BookingOutput_2024_08_13,
RescheduleBookingInput_2024_08_13,
RecurringBookingOutput_2024_08_13,
} from "@calcom/platform-types";
import { CancelBookingInput_2024_08_13 } from "@calcom/platform-types";
import { Team } from "@calcom/prisma/client";
jest
.spyOn(AttendeeScheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(OrganizerScheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(AttendeeRescheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(OrganizerRescheduledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(AttendeeCancelledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
jest
.spyOn(OrganizerCancelledEmail.prototype, "getHtml")
.mockImplementation(() => Promise.resolve("<p>email</p>"));
type EmailSetup = {
user: User;
eventTypeId: number;
recurringEventTypeId: number;
createdBookingUid: string;
rescheduledBookingUid: string;
};
describe("Bookings Endpoints 2024-08-13 user emails", () => {
let app: INestApplication;
let organization: Team;
let userRepositoryFixture: UserRepositoryFixture;
let bookingsRepositoryFixture: BookingsRepositoryFixture;
let schedulesService: SchedulesService_2024_04_15;
let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
let oauthClientRepositoryFixture: OAuthClientRepositoryFixture;
let teamRepositoryFixture: TeamRepositoryFixture;
let emailsEnabledSetup: EmailSetup;
let emailsDisabledSetup: EmailSetup;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
imports: [AppModule, PrismaModule, UsersModule, SchedulesModule_2024_04_15],
})
.overrideGuard(PermissionsGuard)
.useValue({
canActivate: () => true,
})
.overrideGuard(ApiAuthGuard)
.useValue({
canActivate: () => true,
})
.compile();
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
bookingsRepositoryFixture = new BookingsRepositoryFixture(moduleRef);
eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef);
oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef);
teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
schedulesService = moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15);
organization = await teamRepositoryFixture.create({ name: "organization bookings" });
await setupEnabledEmails();
await setupDisabledEmails();
app = moduleRef.createNestApplication();
bootstrap(app as NestExpressApplication);
await app.init();
});
async function setupEnabledEmails() {
const oAuthClientEmailsEnabled = await createOAuthClient(organization.id, true);
const user = await userRepositoryFixture.create({
email: `alice-${Math.floor(Math.random() * 1000)}@gmail.com`,
platformOAuthClients: {
connect: {
id: oAuthClientEmailsEnabled.id,
},
},
});
const userSchedule: CreateScheduleInput_2024_04_15 = {
name: "working time",
timeZone: "Europe/Rome",
isDefault: true,
};
await schedulesService.createUserSchedule(user.id, userSchedule);
const event = await eventTypesRepositoryFixture.create(
{ title: "peer coding", slug: "peer-coding", length: 60 },
user.id
);
const recurringEvent = await eventTypesRepositoryFixture.create(
// note(Lauris): freq 2 means weekly, interval 1 means every week and count 3 means 3 weeks in a row
{
title: "peer coding recurring",
slug: "peer-coding-recurring",
length: 60,
recurringEvent: { freq: 2, count: 3, interval: 1 },
},
user.id
);
emailsEnabledSetup = {
user,
eventTypeId: event.id,
recurringEventTypeId: recurringEvent.id,
createdBookingUid: "",
rescheduledBookingUid: "",
};
}
async function setupDisabledEmails() {
const oAuthClientEmailsDisabled = await createOAuthClient(organization.id, false);
const user = await userRepositoryFixture.create({
email: `bob-${Math.floor(Math.random() * 1000)}@gmail.com`,
platformOAuthClients: {
connect: {
id: oAuthClientEmailsDisabled.id,
},
},
});
const userSchedule: CreateScheduleInput_2024_04_15 = {
name: "working time",
timeZone: "Europe/Rome",
isDefault: true,
};
await schedulesService.createUserSchedule(user.id, userSchedule);
const event = await eventTypesRepositoryFixture.create(
{ title: "peer coding", slug: "peer-coding", length: 60 },
user.id
);
const recurringEvent = await eventTypesRepositoryFixture.create(
// note(Lauris): freq 2 means weekly, interval 1 means every week and count 3 means 3 weeks in a row
{
title: "peer coding recurring",
slug: "peer-coding-recurring",
length: 60,
recurringEvent: { freq: 2, count: 3, interval: 1 },
},
user.id
);
emailsDisabledSetup = {
user,
eventTypeId: event.id,
recurringEventTypeId: recurringEvent.id,
createdBookingUid: "",
rescheduledBookingUid: "",
};
}
async function createOAuthClient(organizationId: number, emailsEnabled: boolean) {
const data = {
logo: "logo-url",
name: "name",
redirectUris: ["http://localhost:5555"],
permissions: 32,
areEmailsEnabled: emailsEnabled,
};
const secret = "secret";
const client = await oauthClientRepositoryFixture.create(organizationId, data, secret);
return client;
}
beforeEach(async () => {
jest.clearAllMocks();
});
describe("OAuth client managed user bookings - emails disabled", () => {
it("should not send an email when creating a booking", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
eventTypeId: emailsDisabledSetup.eventTypeId,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
userId: "100",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
if (responseDataIsBooking(responseBody.data)) {
expect(AttendeeScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
emailsDisabledSetup.createdBookingUid = responseBody.data.uid;
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
it("should not send an email when creating a recurring booking", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 9, 0, 0)).toISOString(),
eventTypeId: emailsDisabledSetup.recurringEventTypeId,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
userId: "100",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
if (responseDataIsRecurringBooking(responseBody.data)) {
expect(AttendeeScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
} else {
throw new Error("Invalid response data - expected booking array but received single booking");
}
});
});
it("should not send an email when rescheduling a booking", async () => {
const body: RescheduleBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2035, 0, 8, 14, 0, 0)).toISOString(),
reschedulingReason: "Flying to mars that day",
};
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsDisabledSetup.createdBookingUid}/reschedule`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: RescheduleBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(AttendeeRescheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerRescheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
emailsDisabledSetup.rescheduledBookingUid = responseBody.data.uid;
});
});
it("should not send an email when cancelling a booking", async () => {
const body: CancelBookingInput_2024_08_13 = {
cancellationReason: "Going on a vacation",
};
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsDisabledSetup.rescheduledBookingUid}/cancel`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: CancelBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeCancelledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerCancelledEmail.prototype.getHtml).not.toHaveBeenCalled();
});
});
});
describe("OAuth client managed user bookings - emails enabled", () => {
it("should send an email when creating a booking", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
eventTypeId: emailsEnabledSetup.eventTypeId,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
userId: "100",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
if (responseDataIsBooking(responseBody.data)) {
const data: BookingOutput_2024_08_13 = responseBody.data;
expect(AttendeeScheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).toHaveBeenCalled();
emailsEnabledSetup.createdBookingUid = responseBody.data.uid;
} else {
throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings"
);
}
});
});
it("should send an email when creating a recurring booking", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 9, 0, 0)).toISOString(),
eventTypeId: emailsEnabledSetup.recurringEventTypeId,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
userId: "100",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
if (responseDataIsRecurringBooking(responseBody.data)) {
expect(AttendeeScheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).toHaveBeenCalled();
} else {
throw new Error("Invalid response data - expected booking array but received single booking");
}
});
});
it("should send an email when rescheduling a booking", async () => {
const body: RescheduleBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2035, 0, 8, 14, 0, 0)).toISOString(),
reschedulingReason: "Flying to mars that day",
};
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsEnabledSetup.createdBookingUid}/reschedule`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201)
.then(async (response) => {
const responseBody: RescheduleBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(OrganizerScheduledEmail.prototype.getHtml).not.toHaveBeenCalled();
expect(AttendeeRescheduledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerRescheduledEmail.prototype.getHtml).toHaveBeenCalled();
emailsEnabledSetup.rescheduledBookingUid = responseBody.data.uid;
});
});
it("should send an email when cancelling a booking", async () => {
const body: CancelBookingInput_2024_08_13 = {
cancellationReason: "Going on a vacation",
};
return request(app.getHttpServer())
.post(`/v2/bookings/${emailsEnabledSetup.rescheduledBookingUid}/cancel`)
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(200)
.then(async (response) => {
const responseBody: CancelBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(AttendeeCancelledEmail.prototype.getHtml).toHaveBeenCalled();
expect(OrganizerCancelledEmail.prototype.getHtml).toHaveBeenCalled();
});
});
});
afterAll(async () => {
await teamRepositoryFixture.delete(organization.id);
await userRepositoryFixture.deleteByEmail(emailsEnabledSetup.user.email);
await userRepositoryFixture.deleteByEmail(emailsDisabledSetup.user.email);
await bookingsRepositoryFixture.deleteAllBookings(
emailsEnabledSetup.user.id,
emailsEnabledSetup.user.email
);
await bookingsRepositoryFixture.deleteAllBookings(
emailsDisabledSetup.user.id,
emailsDisabledSetup.user.email
);
await app.close();
});
function responseDataIsBooking(data: any): data is BookingOutput_2024_08_13 {
return !Array.isArray(data) && typeof data === "object" && data && "id" in data;
}
function responseDataIsRecurringBooking(data: any): data is RecurringBookingOutput_2024_08_13[] {
return Array.isArray(data);
}
});
@@ -1,6 +1,5 @@
import { bootstrap } from "@/app"; import { bootstrap } from "@/app";
import { AppModule } from "@/app.module"; import { AppModule } from "@/app.module";
import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output";
import { ReassignBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/reassign-booking.output"; import { ReassignBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/reassign-booking.output";
import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input"; import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input";
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module"; import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
@@ -80,14 +79,19 @@ describe("Bookings Endpoints 2024-08-13", () => {
schedulesService = moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15); schedulesService = moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15);
organization = await organizationsRepositoryFixture.create({ name: "organization team bookings" }); organization = await organizationsRepositoryFixture.create({ name: "organization team bookings" });
oAuthClient = await createOAuthClient(organization.id);
team = await teamRepositoryFixture.create({ team = await teamRepositoryFixture.create({
name: "team 1", name: "team 1",
isOrganization: false, isOrganization: false,
parent: { connect: { id: organization.id } }, parent: { connect: { id: organization.id } },
createdByOAuthClient: {
connect: {
id: oAuthClient.id,
},
},
}); });
oAuthClient = await createOAuthClient(organization.id);
teamUser1 = await userRepositoryFixture.create({ teamUser1 = await userRepositoryFixture.create({
email: teamUserEmail, email: teamUserEmail,
locale: "it", locale: "it",
@@ -72,6 +72,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
user = await userRepositoryFixture.create({ user = await userRepositoryFixture.create({
email: userEmail, email: userEmail,
platformOAuthClients: {
connect: {
id: oAuthClient.id,
},
},
}); });
const userSchedule: CreateScheduleInput_2024_04_15 = { const userSchedule: CreateScheduleInput_2024_04_15 = {
@@ -176,6 +181,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(firstBooking.eventTypeId).toEqual(recurringEventTypeId); expect(firstBooking.eventTypeId).toEqual(recurringEventTypeId);
expect(firstBooking.attendees[0]).toEqual({ expect(firstBooking.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -196,6 +202,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(secondBooking.recurringBookingUid).toBeDefined(); expect(secondBooking.recurringBookingUid).toBeDefined();
expect(secondBooking.attendees[0]).toEqual({ expect(secondBooking.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -251,6 +258,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(firstBooking.eventTypeId).toEqual(recurringEventTypeId); expect(firstBooking.eventTypeId).toEqual(recurringEventTypeId);
expect(firstBooking.attendees[0]).toEqual({ expect(firstBooking.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -272,6 +280,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(secondBooking.recurringBookingUid).toBeDefined(); expect(secondBooking.recurringBookingUid).toBeDefined();
expect(secondBooking.attendees[0]).toEqual({ expect(secondBooking.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -291,6 +300,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(thirdBooking.recurringBookingUid).toBeDefined(); expect(thirdBooking.recurringBookingUid).toBeDefined();
expect(thirdBooking.attendees[0]).toEqual({ expect(thirdBooking.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -375,6 +385,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
user = await userRepositoryFixture.create({ user = await userRepositoryFixture.create({
email: userEmail, email: userEmail,
platformOAuthClients: {
connect: {
id: oAuthClient.id,
},
},
}); });
const userSchedule: CreateScheduleInput_2024_04_15 = { const userSchedule: CreateScheduleInput_2024_04_15 = {
@@ -577,6 +592,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
user = await userRepositoryFixture.create({ user = await userRepositoryFixture.create({
email: userEmail, email: userEmail,
platformOAuthClients: {
connect: {
id: oAuthClient.id,
},
},
}); });
const userSchedule: CreateScheduleInput_2024_04_15 = { const userSchedule: CreateScheduleInput_2024_04_15 = {
@@ -93,6 +93,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
user = await userRepositoryFixture.create({ user = await userRepositoryFixture.create({
email: userEmail, email: userEmail,
platformOAuthClients: {
connect: {
id: oAuthClient.id,
},
},
}); });
const userSchedule: CreateScheduleInput_2024_04_15 = { const userSchedule: CreateScheduleInput_2024_04_15 = {
@@ -163,6 +168,9 @@ describe("Bookings Endpoints 2024-08-13", () => {
bookingFieldsResponses: { bookingFieldsResponses: {
codingLanguage: "TypeScript", codingLanguage: "TypeScript",
}, },
metadata: {
userId: "100",
},
}; };
return request(app.getHttpServer()) return request(app.getHttpServer())
@@ -197,6 +205,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(data.attendees.length).toEqual(1); expect(data.attendees.length).toEqual(1);
expect(data.attendees[0]).toEqual({ expect(data.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -205,6 +214,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
name: body.attendee.name, name: body.attendee.name,
...body.bookingFieldsResponses, ...body.bookingFieldsResponses,
}, },
metadata: body.metadata,
}); });
expect(data.location).toBeDefined(); expect(data.location).toBeDefined();
expect(data.absentHost).toEqual(false); expect(data.absentHost).toEqual(false);
@@ -230,6 +240,9 @@ describe("Bookings Endpoints 2024-08-13", () => {
bookingFieldsResponses: { bookingFieldsResponses: {
codingLanguage: "Rust", codingLanguage: "Rust",
}, },
metadata: {
userId: "200",
},
}; };
return request(app.getHttpServer()) return request(app.getHttpServer())
@@ -266,6 +279,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
const firstAttendee = data.attendees.find((attendee) => attendee.name === nameAttendeeOne); const firstAttendee = data.attendees.find((attendee) => attendee.name === nameAttendeeOne);
expect(firstAttendee).toEqual({ expect(firstAttendee).toEqual({
name: createdSeatedBooking.attendees[0].name, name: createdSeatedBooking.attendees[0].name,
email: createdSeatedBooking.attendees[0].email,
timeZone: createdSeatedBooking.attendees[0].timeZone, timeZone: createdSeatedBooking.attendees[0].timeZone,
language: createdSeatedBooking.attendees[0].language, language: createdSeatedBooking.attendees[0].language,
absent: false, absent: false,
@@ -274,10 +288,12 @@ describe("Bookings Endpoints 2024-08-13", () => {
name: createdSeatedBooking.attendees[0].name, name: createdSeatedBooking.attendees[0].name,
...createdSeatedBooking.attendees[0].bookingFieldsResponses, ...createdSeatedBooking.attendees[0].bookingFieldsResponses,
}, },
metadata: createdSeatedBooking.attendees[0].metadata,
}); });
const secondAttendee = data.attendees.find((attendee) => attendee.name === nameAttendeeTwo); const secondAttendee = data.attendees.find((attendee) => attendee.name === nameAttendeeTwo);
expect(secondAttendee).toEqual({ expect(secondAttendee).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -286,6 +302,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
name: body.attendee.name, name: body.attendee.name,
...body.bookingFieldsResponses, ...body.bookingFieldsResponses,
}, },
metadata: body.metadata,
}); });
expect(data.location).toBeDefined(); expect(data.location).toBeDefined();
expect(data.absentHost).toEqual(false); expect(data.absentHost).toEqual(false);
@@ -314,6 +331,9 @@ describe("Bookings Endpoints 2024-08-13", () => {
codingLanguage: "TypeScript", codingLanguage: "TypeScript",
}, },
recurrenceCount, recurrenceCount,
metadata: {
userId: "300",
},
}; };
return request(app.getHttpServer()) return request(app.getHttpServer())
@@ -351,6 +371,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(firstBooking.attendees.length).toEqual(1); expect(firstBooking.attendees.length).toEqual(1);
expect(firstBooking.attendees[0]).toEqual({ expect(firstBooking.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -359,6 +380,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
name: body.attendee.name, name: body.attendee.name,
...body.bookingFieldsResponses, ...body.bookingFieldsResponses,
}, },
metadata: body.metadata,
}); });
expect(firstBooking.location).toEqual(body.location); expect(firstBooking.location).toEqual(body.location);
expect(firstBooking.absentHost).toEqual(false); expect(firstBooking.absentHost).toEqual(false);
@@ -384,6 +406,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(secondBooking.attendees.length).toEqual(1); expect(secondBooking.attendees.length).toEqual(1);
expect(secondBooking.attendees[0]).toEqual({ expect(secondBooking.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -392,6 +415,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
name: body.attendee.name, name: body.attendee.name,
...body.bookingFieldsResponses, ...body.bookingFieldsResponses,
}, },
metadata: body.metadata,
}); });
expect(secondBooking.location).toEqual(body.location); expect(secondBooking.location).toEqual(body.location);
expect(secondBooking.absentHost).toEqual(false); expect(secondBooking.absentHost).toEqual(false);
@@ -557,6 +581,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
const attendee = createdSeatedBooking.attendees.find((a) => a.seatUid === body.seatUid); const attendee = createdSeatedBooking.attendees.find((a) => a.seatUid === body.seatUid);
expect(data.attendees[0]).toEqual({ expect(data.attendees[0]).toEqual({
name: attendee?.name, name: attendee?.name,
email: attendee?.email,
timeZone: attendee?.timeZone, timeZone: attendee?.timeZone,
language: attendee?.language, language: attendee?.language,
absent: false, absent: false,
@@ -565,6 +590,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
name: attendee?.name, name: attendee?.name,
...attendee?.bookingFieldsResponses, ...attendee?.bookingFieldsResponses,
}, },
metadata: attendee?.metadata,
}); });
expect(data.location).toBeDefined(); expect(data.location).toBeDefined();
expect(data.absentHost).toEqual(false); expect(data.absentHost).toEqual(false);
@@ -697,6 +723,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
user = await userRepositoryFixture.create({ user = await userRepositoryFixture.create({
email: userEmail, email: userEmail,
platformOAuthClients: {
connect: {
id: oAuthClient.id,
},
},
}); });
const userSchedule: CreateScheduleInput_2024_04_15 = { const userSchedule: CreateScheduleInput_2024_04_15 = {
@@ -769,6 +800,9 @@ describe("Bookings Endpoints 2024-08-13", () => {
bookingFieldsResponses: { bookingFieldsResponses: {
codingLanguage: "TypeScript", codingLanguage: "TypeScript",
}, },
metadata: {
userId: "1000",
},
recurrenceCount, recurrenceCount,
}; };
@@ -807,6 +841,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(firstBooking.attendees.length).toEqual(1); expect(firstBooking.attendees.length).toEqual(1);
expect(firstBooking.attendees[0]).toEqual({ expect(firstBooking.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -815,6 +850,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
name: body.attendee.name, name: body.attendee.name,
...body.bookingFieldsResponses, ...body.bookingFieldsResponses,
}, },
metadata: body.metadata,
}); });
expect(firstBooking.location).toEqual(body.location); expect(firstBooking.location).toEqual(body.location);
expect(firstBooking.absentHost).toEqual(false); expect(firstBooking.absentHost).toEqual(false);
@@ -840,6 +876,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(secondBooking.attendees.length).toEqual(1); expect(secondBooking.attendees.length).toEqual(1);
expect(secondBooking.attendees[0]).toEqual({ expect(secondBooking.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -848,6 +885,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
name: body.attendee.name, name: body.attendee.name,
...body.bookingFieldsResponses, ...body.bookingFieldsResponses,
}, },
metadata: body.metadata,
}); });
expect(secondBooking.location).toEqual(body.location); expect(secondBooking.location).toEqual(body.location);
expect(secondBooking.absentHost).toEqual(false); expect(secondBooking.absentHost).toEqual(false);
@@ -875,6 +913,9 @@ describe("Bookings Endpoints 2024-08-13", () => {
bookingFieldsResponses: { bookingFieldsResponses: {
codingLanguage: "TypeScript", codingLanguage: "TypeScript",
}, },
metadata: {
userId: "2000",
},
recurrenceCount, recurrenceCount,
}; };
@@ -913,6 +954,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(firstBooking.attendees.length).toEqual(2); expect(firstBooking.attendees.length).toEqual(2);
expect(firstBooking.attendees[0]).toEqual({ expect(firstBooking.attendees[0]).toEqual({
name: nameAttendeeOne, name: nameAttendeeOne,
email: emailAttendeeOne,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -921,9 +963,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
name: nameAttendeeOne, name: nameAttendeeOne,
...body.bookingFieldsResponses, ...body.bookingFieldsResponses,
}, },
metadata: createdRecurringSeatedBooking[0].attendees[0].metadata,
}); });
expect(firstBooking.attendees[1]).toEqual({ expect(firstBooking.attendees[1]).toEqual({
name: nameAttendeeTwo, name: nameAttendeeTwo,
email: emailAttendeeTwo,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -932,6 +976,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
name: nameAttendeeTwo, name: nameAttendeeTwo,
...body.bookingFieldsResponses, ...body.bookingFieldsResponses,
}, },
metadata: body.metadata,
}); });
expect(firstBooking.location).toEqual(body.location); expect(firstBooking.location).toEqual(body.location);
expect(firstBooking.absentHost).toEqual(false); expect(firstBooking.absentHost).toEqual(false);
@@ -957,6 +1002,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(secondBooking.attendees.length).toEqual(2); expect(secondBooking.attendees.length).toEqual(2);
expect(secondBooking.attendees[0]).toEqual({ expect(secondBooking.attendees[0]).toEqual({
name: nameAttendeeOne, name: nameAttendeeOne,
email: emailAttendeeOne,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -965,9 +1011,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
name: nameAttendeeOne, name: nameAttendeeOne,
...body.bookingFieldsResponses, ...body.bookingFieldsResponses,
}, },
metadata: createdRecurringSeatedBooking[0].attendees[0].metadata,
}); });
expect(secondBooking.attendees[1]).toEqual({ expect(secondBooking.attendees[1]).toEqual({
name: nameAttendeeTwo, name: nameAttendeeTwo,
email: emailAttendeeTwo,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -976,6 +1024,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
name: nameAttendeeTwo, name: nameAttendeeTwo,
...body.bookingFieldsResponses, ...body.bookingFieldsResponses,
}, },
metadata: body.metadata,
}); });
expect(secondBooking.location).toEqual(body.location); expect(secondBooking.location).toEqual(body.location);
expect(secondBooking.absentHost).toEqual(false); expect(secondBooking.absentHost).toEqual(false);
@@ -85,30 +85,50 @@ describe("Bookings Endpoints 2024-08-13", () => {
schedulesService = moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15); schedulesService = moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15);
organization = await organizationsRepositoryFixture.create({ name: "organization team bookings" }); organization = await organizationsRepositoryFixture.create({ name: "organization team bookings" });
oAuthClient = await createOAuthClient(organization.id);
team1 = await teamRepositoryFixture.create({ team1 = await teamRepositoryFixture.create({
name: "team 1", name: "team 1",
isOrganization: false, isOrganization: false,
parent: { connect: { id: organization.id } }, parent: { connect: { id: organization.id } },
createdByOAuthClient: {
connect: {
id: oAuthClient.id,
},
},
}); });
team2 = await teamRepositoryFixture.create({ team2 = await teamRepositoryFixture.create({
name: "team 2", name: "team 2",
isOrganization: false, isOrganization: false,
parent: { connect: { id: organization.id } }, parent: { connect: { id: organization.id } },
createdByOAuthClient: {
connect: {
id: oAuthClient.id,
},
},
}); });
oAuthClient = await createOAuthClient(organization.id);
teamUser = await userRepositoryFixture.create({ teamUser = await userRepositoryFixture.create({
email: teamUserEmail, email: teamUserEmail,
locale: "it", locale: "it",
name: "orgUser1team1", name: "orgUser1team1",
platformOAuthClients: {
connect: {
id: oAuthClient.id,
},
},
}); });
teamUser2 = await userRepositoryFixture.create({ teamUser2 = await userRepositoryFixture.create({
email: teamUserEmail2, email: teamUserEmail2,
locale: "es", locale: "es",
name: "orgUser2team1", name: "orgUser2team1",
platformOAuthClients: {
connect: {
id: oAuthClient.id,
},
},
}); });
const userSchedule: CreateScheduleInput_2024_04_15 = { const userSchedule: CreateScheduleInput_2024_04_15 = {
@@ -287,6 +307,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(data.attendees.length).toEqual(1); expect(data.attendees.length).toEqual(1);
expect(data.attendees[0]).toEqual({ expect(data.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -339,12 +360,14 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(data.attendees.length).toEqual(2); expect(data.attendees.length).toEqual(2);
expect(data.attendees[0]).toEqual({ expect(data.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
}); });
expect(data.attendees[1]).toEqual({ expect(data.attendees[1]).toEqual({
name: teamUser2.name, name: teamUser2.name,
email: teamUser2.email,
timeZone: teamUser2.timeZone, timeZone: teamUser2.timeZone,
language: teamUser2.locale, language: teamUser2.locale,
absent: false, absent: false,
@@ -14,6 +14,7 @@ import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express"; import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing"; import { Test } from "@nestjs/testing";
import { User } from "@prisma/client"; import { User } from "@prisma/client";
import { advanceTo, clear } from "jest-date-mock";
import * as request from "supertest"; import * as request from "supertest";
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture"; import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture"; import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
@@ -95,6 +96,12 @@ describe("Bookings Endpoints 2024-08-13", () => {
user = await userRepositoryFixture.create({ user = await userRepositoryFixture.create({
email: userEmail, email: userEmail,
username: `bob-${Math.floor(Math.random() * 1000)}@gmail.com`,
platformOAuthClients: {
connect: {
id: oAuthClient.id,
},
},
}); });
const userSchedule: CreateScheduleInput_2024_04_15 = { const userSchedule: CreateScheduleInput_2024_04_15 = {
@@ -178,6 +185,136 @@ describe("Bookings Endpoints 2024-08-13", () => {
}); });
describe("create bookings", () => { describe("create bookings", () => {
describe("invalid metadata", () => {
it("should not be able to create a booking with metadata with more than 50 keys", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
eventTypeId,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
key1: "1",
key2: "2",
key3: "3",
key4: "4",
key5: "5",
key6: "6",
key7: "7",
key8: "8",
key9: "9",
key10: "10",
key11: "11",
key12: "12",
key13: "13",
key14: "14",
key15: "15",
key16: "16",
key17: "17",
key18: "18",
key19: "19",
key20: "20",
key21: "21",
key22: "22",
key23: "23",
key24: "24",
key25: "25",
key26: "26",
key27: "27",
key28: "28",
key29: "29",
key30: "30",
key31: "31",
key32: "32",
key33: "33",
key34: "34",
key35: "35",
key36: "36",
key37: "37",
key38: "38",
key39: "39",
key40: "40",
key41: "41",
key42: "42",
key43: "43",
key44: "44",
key45: "45",
key46: "46",
key47: "47",
key48: "48",
key49: "49",
key50: "50",
key51: "51",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(400);
});
it("should not be able to create a booking with metadata with a key longer than 40 characters", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
eventTypeId,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
aaaaaaaaaabbbbbbbbbbccccccccccdddddddddde: "1",
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(400);
});
it("should not be able to create a booking with metadata with a value longer than 500 characters", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
eventTypeId,
attendee: {
name: "Mr Proper",
email: "mr_proper@gmail.com",
timeZone: "Europe/Rome",
language: "it",
},
location: "https://meet.google.com/abc-def-ghi",
bookingFieldsResponses: {
customField: "customValue",
},
metadata: {
key: `${"a".repeat(501)}`,
},
};
return request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(400);
});
});
it("should create a booking", async () => { it("should create a booking", async () => {
const body: CreateBookingInput_2024_08_13 = { const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(), start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
@@ -192,14 +329,19 @@ describe("Bookings Endpoints 2024-08-13", () => {
bookingFieldsResponses: { bookingFieldsResponses: {
customField: "customValue", customField: "customValue",
}, },
metadata: {
userId: "100",
},
}; };
const beforeCreate = new Date();
return request(app.getHttpServer()) return request(app.getHttpServer())
.post("/v2/bookings") .post("/v2/bookings")
.send(body) .send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13) .set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201) .expect(201)
.then(async (response) => { .then(async (response) => {
const afterCreate = new Date();
const responseBody: CreateBookingOutput_2024_08_13 = response.body; const responseBody: CreateBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS); expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined(); expect(responseBody.data).toBeDefined();
@@ -210,6 +352,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(data.id).toBeDefined(); expect(data.id).toBeDefined();
expect(data.uid).toBeDefined(); expect(data.uid).toBeDefined();
expect(data.hosts[0].id).toEqual(user.id); expect(data.hosts[0].id).toEqual(user.id);
expect(data.hosts[0].username).toEqual(user.username);
expect(data.status).toEqual("accepted"); expect(data.status).toEqual("accepted");
expect(data.start).toEqual(body.start); expect(data.start).toEqual(body.start);
expect(data.end).toEqual(new Date(Date.UTC(2030, 0, 8, 14, 0, 0)).toISOString()); expect(data.end).toEqual(new Date(Date.UTC(2030, 0, 8, 14, 0, 0)).toISOString());
@@ -221,6 +364,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
}); });
expect(data.attendees[0]).toEqual({ expect(data.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -233,6 +377,13 @@ describe("Bookings Endpoints 2024-08-13", () => {
email: body.attendee.email, email: body.attendee.email,
...body.bookingFieldsResponses, ...body.bookingFieldsResponses,
}); });
// Check createdAt date is between the time of the request and after the request
const createdAtDate = new Date(data.createdAt);
expect(createdAtDate.getTime()).toBeGreaterThanOrEqual(beforeCreate.getTime());
expect(createdAtDate.getTime()).toBeLessThanOrEqual(afterCreate.getTime());
expect(data.metadata).toEqual(body.metadata);
createdBooking = data; createdBooking = data;
} else { } else {
throw new Error( throw new Error(
@@ -253,6 +404,9 @@ describe("Bookings Endpoints 2024-08-13", () => {
language: "it", language: "it",
}, },
location: "https://meet.google.com/abc-def-ghi", location: "https://meet.google.com/abc-def-ghi",
metadata: {
userId: "1000",
},
}; };
return request(app.getHttpServer()) return request(app.getHttpServer())
@@ -281,6 +435,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(firstBooking.eventTypeId).toEqual(recurringEventTypeId); expect(firstBooking.eventTypeId).toEqual(recurringEventTypeId);
expect(firstBooking.attendees[0]).toEqual({ expect(firstBooking.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
@@ -288,6 +443,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(firstBooking.location).toEqual(body.location); expect(firstBooking.location).toEqual(body.location);
expect(firstBooking.recurringBookingUid).toBeDefined(); expect(firstBooking.recurringBookingUid).toBeDefined();
expect(firstBooking.absentHost).toEqual(false); expect(firstBooking.absentHost).toEqual(false);
expect(firstBooking.metadata).toEqual(body.metadata);
const secondBooking = data[1]; const secondBooking = data[1];
expect(secondBooking.id).toBeDefined(); expect(secondBooking.id).toBeDefined();
@@ -301,12 +457,14 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(secondBooking.recurringBookingUid).toBeDefined(); expect(secondBooking.recurringBookingUid).toBeDefined();
expect(secondBooking.attendees[0]).toEqual({ expect(secondBooking.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
}); });
expect(secondBooking.location).toEqual(body.location); expect(secondBooking.location).toEqual(body.location);
expect(secondBooking.absentHost).toEqual(false); expect(secondBooking.absentHost).toEqual(false);
expect(secondBooking.metadata).toEqual(body.metadata);
const thirdBooking = data[2]; const thirdBooking = data[2];
expect(thirdBooking.id).toBeDefined(); expect(thirdBooking.id).toBeDefined();
@@ -320,12 +478,14 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(thirdBooking.recurringBookingUid).toBeDefined(); expect(thirdBooking.recurringBookingUid).toBeDefined();
expect(thirdBooking.attendees[0]).toEqual({ expect(thirdBooking.attendees[0]).toEqual({
name: body.attendee.name, name: body.attendee.name,
email: body.attendee.email,
timeZone: body.attendee.timeZone, timeZone: body.attendee.timeZone,
language: body.attendee.language, language: body.attendee.language,
absent: false, absent: false,
}); });
expect(thirdBooking.location).toEqual(body.location); expect(thirdBooking.location).toEqual(body.location);
expect(thirdBooking.absentHost).toEqual(false); expect(thirdBooking.absentHost).toEqual(false);
expect(thirdBooking.metadata).toEqual(body.metadata);
createdRecurringBooking = data; createdRecurringBooking = data;
} else { } else {
@@ -354,6 +514,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(data.id).toEqual(createdBooking.id); expect(data.id).toEqual(createdBooking.id);
expect(data.uid).toEqual(createdBooking.uid); expect(data.uid).toEqual(createdBooking.uid);
expect(data.hosts[0].id).toEqual(user.id); expect(data.hosts[0].id).toEqual(user.id);
expect(data.hosts[0].username).toEqual(user.username);
expect(data.status).toEqual(createdBooking.status); expect(data.status).toEqual(createdBooking.status);
expect(data.start).toEqual(createdBooking.start); expect(data.start).toEqual(createdBooking.start);
expect(data.end).toEqual(createdBooking.end); expect(data.end).toEqual(createdBooking.end);
@@ -362,6 +523,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(data.attendees[0]).toEqual(createdBooking.attendees[0]); expect(data.attendees[0]).toEqual(createdBooking.attendees[0]);
expect(data.location).toEqual(createdBooking.location); expect(data.location).toEqual(createdBooking.location);
expect(data.absentHost).toEqual(createdBooking.absentHost); expect(data.absentHost).toEqual(createdBooking.absentHost);
expect(data.createdAt).toEqual(createdBooking.createdAt);
} else { } else {
throw new Error( throw new Error(
"Invalid response data - expected booking but received array of possibily recurring bookings" "Invalid response data - expected booking but received array of possibily recurring bookings"
@@ -835,12 +997,14 @@ describe("Bookings Endpoints 2024-08-13", () => {
reschedulingReason: "Flying to mars that day", reschedulingReason: "Flying to mars that day",
}; };
const beforeCreate = new Date();
return request(app.getHttpServer()) return request(app.getHttpServer())
.post(`/v2/bookings/${createdBooking.uid}/reschedule`) .post(`/v2/bookings/${createdBooking.uid}/reschedule`)
.send(body) .send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13) .set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
.expect(201) .expect(201)
.then(async (response) => { .then(async (response) => {
const afterCreate = new Date();
const responseBody: RescheduleBookingOutput_2024_08_13 = response.body; const responseBody: RescheduleBookingOutput_2024_08_13 = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS); expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined(); expect(responseBody.data).toBeDefined();
@@ -860,6 +1024,13 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(data.attendees[0]).toEqual(createdBooking.attendees[0]); expect(data.attendees[0]).toEqual(createdBooking.attendees[0]);
expect(data.location).toEqual(createdBooking.location); expect(data.location).toEqual(createdBooking.location);
expect(data.absentHost).toEqual(createdBooking.absentHost); expect(data.absentHost).toEqual(createdBooking.absentHost);
expect(data.metadata).toEqual(createdBooking.metadata);
// When a booking is rescheduled, a new booking is created and the old booking is cancelled.
// We want to make sure the createdAt date of the new booking is between the beforeCreate and afterCreate dates.
const createdAtDate = new Date(data.createdAt);
expect(createdAtDate.getTime()).toBeGreaterThanOrEqual(beforeCreate.getTime());
expect(createdAtDate.getTime()).toBeLessThanOrEqual(afterCreate.getTime());
rescheduledBooking = data; rescheduledBooking = data;
}); });
@@ -913,6 +1084,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
expect(data.attendees[0]).toEqual(createdRecurringBooking[0].attendees[0]); expect(data.attendees[0]).toEqual(createdRecurringBooking[0].attendees[0]);
expect(data.location).toEqual(createdRecurringBooking[0].location); expect(data.location).toEqual(createdRecurringBooking[0].location);
expect(data.absentHost).toEqual(createdRecurringBooking[0].absentHost); expect(data.absentHost).toEqual(createdRecurringBooking[0].absentHost);
expect(data.metadata).toEqual(createdRecurringBooking[0].metadata);
const oldBooking = await bookingsRepositoryFixture.getByUid(createdRecurringBooking[0].uid); const oldBooking = await bookingsRepositoryFixture.getByUid(createdRecurringBooking[0].uid);
expect(oldBooking).toBeDefined(); expect(oldBooking).toBeDefined();
@@ -952,6 +1124,14 @@ describe("Bookings Endpoints 2024-08-13", () => {
}); });
describe("mark absent", () => { describe("mark absent", () => {
beforeAll(() => {
advanceTo(new Date(2035, 0, 9, 15, 0, 0));
});
afterAll(() => {
clear();
});
it("should mark host absent", async () => { it("should mark host absent", async () => {
const body: MarkAbsentBookingInput_2024_08_13 = { const body: MarkAbsentBookingInput_2024_08_13 = {
host: true, host: true,
@@ -1018,6 +1198,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
}); });
}); });
}); });
describe("cancel bookings", () => { describe("cancel bookings", () => {
it("should cancel booking", async () => { it("should cancel booking", async () => {
const body: CancelBookingInput_2024_08_13 = { const body: CancelBookingInput_2024_08_13 = {
@@ -1,9 +1,6 @@
import { bootstrap } from "@/app"; import { bootstrap } from "@/app";
import { AppModule } from "@/app.module"; import { AppModule } from "@/app.module";
import { CancelBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/cancel-booking.output";
import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output"; import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output";
import { MarkAbsentBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/mark-absent.output";
import { RescheduleBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/reschedule-booking.output";
import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input"; import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input";
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module"; import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service"; import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service";
@@ -71,6 +68,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
user = await userRepositoryFixture.create({ user = await userRepositoryFixture.create({
email: userEmail, email: userEmail,
platformOAuthClients: {
connect: {
id: oAuthClient.id,
},
},
}); });
const userSchedule: CreateScheduleInput_2024_04_15 = { const userSchedule: CreateScheduleInput_2024_04_15 = {
@@ -10,7 +10,12 @@ import {
RecurringBookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13,
} from "@calcom/platform-types"; } from "@calcom/platform-types";
@ApiExtraModels(BookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13) @ApiExtraModels(
BookingOutput_2024_08_13,
RecurringBookingOutput_2024_08_13,
GetSeatedBookingOutput_2024_08_13,
GetRecurringSeatedBookingOutput_2024_08_13
)
export class CancelBookingOutput_2024_08_13 { export class CancelBookingOutput_2024_08_13 {
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] }) @ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
@IsEnum([SUCCESS_STATUS, ERROR_STATUS]) @IsEnum([SUCCESS_STATUS, ERROR_STATUS])
@@ -10,7 +10,12 @@ import {
CreateRecurringSeatedBookingOutput_2024_08_13, CreateRecurringSeatedBookingOutput_2024_08_13,
} from "@calcom/platform-types"; } from "@calcom/platform-types";
@ApiExtraModels(BookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13) @ApiExtraModels(
BookingOutput_2024_08_13,
RecurringBookingOutput_2024_08_13,
CreateSeatedBookingOutput_2024_08_13,
CreateRecurringSeatedBookingOutput_2024_08_13
)
export class CreateBookingOutput_2024_08_13 { export class CreateBookingOutput_2024_08_13 {
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] }) @ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
@IsEnum([SUCCESS_STATUS, ERROR_STATUS]) @IsEnum([SUCCESS_STATUS, ERROR_STATUS])
@@ -21,6 +26,8 @@ export class CreateBookingOutput_2024_08_13 {
oneOf: [ oneOf: [
{ $ref: getSchemaPath(BookingOutput_2024_08_13) }, { $ref: getSchemaPath(BookingOutput_2024_08_13) },
{ type: "array", items: { $ref: getSchemaPath(RecurringBookingOutput_2024_08_13) } }, { type: "array", items: { $ref: getSchemaPath(RecurringBookingOutput_2024_08_13) } },
{ $ref: getSchemaPath(CreateSeatedBookingOutput_2024_08_13) },
{ type: "array", items: { $ref: getSchemaPath(CreateRecurringSeatedBookingOutput_2024_08_13) } },
], ],
description: description:
"Booking data, which can be either a BookingOutput object or an array of RecurringBookingOutput objects", "Booking data, which can be either a BookingOutput object or an array of RecurringBookingOutput objects",
@@ -12,7 +12,12 @@ import {
RecurringBookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13,
} from "@calcom/platform-types"; } from "@calcom/platform-types";
@ApiExtraModels(BookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13) @ApiExtraModels(
BookingOutput_2024_08_13,
RecurringBookingOutput_2024_08_13,
CreateSeatedBookingOutput_2024_08_13,
CreateRecurringSeatedBookingOutput_2024_08_13
)
export class RescheduleBookingOutput_2024_08_13 { export class RescheduleBookingOutput_2024_08_13 {
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] }) @ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
@IsEnum([SUCCESS_STATUS, ERROR_STATUS]) @IsEnum([SUCCESS_STATUS, ERROR_STATUS])
@@ -10,6 +10,7 @@ import { UsersRepository, UserWithProfile } from "@/modules/users/users.reposito
import { Injectable, Logger, NotFoundException } from "@nestjs/common"; import { Injectable, Logger, NotFoundException } from "@nestjs/common";
import { BadRequestException } from "@nestjs/common"; import { BadRequestException } from "@nestjs/common";
import { Request } from "express"; import { Request } from "express";
import { z } from "zod";
import { import {
handleNewBooking, handleNewBooking,
@@ -38,6 +39,7 @@ import {
CancelBookingInput, CancelBookingInput,
} from "@calcom/platform-types"; } from "@calcom/platform-types";
import { PrismaClient } from "@calcom/prisma"; import { PrismaClient } from "@calcom/prisma";
import { EventType } from "@calcom/prisma/client";
type CreatedBooking = { type CreatedBooking = {
hosts: { id: number }[]; hosts: { id: number }[];
@@ -45,6 +47,14 @@ type CreatedBooking = {
start: string; start: string;
}; };
const eventTypeBookingFieldSchema = z.object({
name: z.string(),
required: z.boolean(),
editable: z.string(),
});
const eventTypeBookingFieldsSchema = z.array(eventTypeBookingFieldSchema);
@Injectable() @Injectable()
export class BookingsService_2024_08_13 { export class BookingsService_2024_08_13 {
private readonly logger = new Logger("BookingsService"); private readonly logger = new Logger("BookingsService");
@@ -70,6 +80,8 @@ export class BookingsService_2024_08_13 {
const isRecurring = !!eventType?.recurringEvent; const isRecurring = !!eventType?.recurringEvent;
const isSeated = !!eventType?.seatsPerTimeSlot; const isSeated = !!eventType?.seatsPerTimeSlot;
await this.hasRequiredBookingFieldsResponses(body, eventType);
if (isRecurring && isSeated) { if (isRecurring && isSeated) {
return await this.createRecurringSeatedBooking(request, body); return await this.createRecurringSeatedBooking(request, body);
} }
@@ -91,6 +103,28 @@ export class BookingsService_2024_08_13 {
} }
} }
async hasRequiredBookingFieldsResponses(body: CreateBookingInput, eventType: EventType | null) {
const bookingFields = body.bookingFieldsResponses;
if (!bookingFields || !eventType || !eventType.bookingFields) {
return true;
}
// note(Lauris): we filter out system fields, because some of them are set by default and name and email are passed in the body.attendee
const eventTypeBookingFields = eventTypeBookingFieldsSchema
.parse(eventType.bookingFields)
.filter((field) => !field.editable.startsWith("system"));
for (const field of eventTypeBookingFields) {
if (field.required && !(field.name in bookingFields)) {
throw new BadRequestException(`
Missing required booking field response: ${field.name} - it is required by the event type booking fields, but missing in the bookingFieldsResponses.
You can fetch the event type with ID ${eventType.id} to see the required fields.`);
}
}
return true;
}
async createInstantBooking(request: Request, body: CreateInstantBookingInput_2024_08_13) { async createInstantBooking(request: Request, body: CreateInstantBookingInput_2024_08_13) {
const bookingRequest = await this.inputService.createBookingRequest(request, body); const bookingRequest = await this.inputService.createBookingRequest(request, body);
const booking = await handleInstantMeeting(bookingRequest); const booking = await handleInstantMeeting(bookingRequest);
@@ -329,12 +363,17 @@ export class BookingsService_2024_08_13 {
async markAbsent(bookingUid: string, bookingOwnerId: number, body: MarkAbsentBookingInput_2024_08_13) { async markAbsent(bookingUid: string, bookingOwnerId: number, body: MarkAbsentBookingInput_2024_08_13) {
const bodyTransformed = this.inputService.transformInputMarkAbsentBooking(body); const bodyTransformed = this.inputService.transformInputMarkAbsentBooking(body);
const bookingBefore = await this.bookingsRepository.getByUid(bookingUid);
const platformClientParams = bookingBefore?.eventTypeId
? await this.inputService.getOAuthClientParams(bookingBefore.eventTypeId)
: undefined;
await handleMarkNoShow({ await handleMarkNoShow({
bookingUid, bookingUid,
attendees: bodyTransformed.attendees, attendees: bodyTransformed.attendees,
noShowHost: bodyTransformed.noShowHost, noShowHost: bodyTransformed.noShowHost,
userId: bookingOwnerId, userId: bookingOwnerId,
platformClientParams,
}); });
const booking = await this.bookingsRepository.getByUidWithAttendeesAndUserAndEvent(bookingUid); const booking = await this.bookingsRepository.getByUidWithAttendeesAndUserAndEvent(bookingUid);
@@ -389,11 +428,19 @@ export class BookingsService_2024_08_13 {
throw new NotFoundException(`Booking with uid=${bookingUid} was not found in the database`); throw new NotFoundException(`Booking with uid=${bookingUid} was not found in the database`);
} }
const platformClientParams = booking.eventTypeId
? await this.inputService.getOAuthClientParams(booking.eventTypeId)
: undefined;
const emailsEnabled = platformClientParams ? platformClientParams.arePlatformEmailsEnabled : true;
const profile = this.usersService.getUserMainProfile(requestUser); const profile = this.usersService.getUserMainProfile(requestUser);
await roundRobinReassignment({ await roundRobinReassignment({
bookingId: booking.id, bookingId: booking.id,
orgId: profile?.organizationId || null, orgId: profile?.organizationId || null,
emailsEnabled,
platformClientParams,
}); });
const reassigned = await this.bookingsRepository.getByUidWithUser(bookingUid); const reassigned = await this.bookingsRepository.getByUidWithUser(bookingUid);
@@ -420,6 +467,12 @@ export class BookingsService_2024_08_13 {
throw new NotFoundException(`User with id=${newUserId} was not found in the database`); throw new NotFoundException(`User with id=${newUserId} was not found in the database`);
} }
const platformClientParams = booking.eventTypeId
? await this.inputService.getOAuthClientParams(booking.eventTypeId)
: undefined;
const emailsEnabled = platformClientParams ? platformClientParams.arePlatformEmailsEnabled : true;
const profile = this.usersService.getUserMainProfile(user); const profile = this.usersService.getUserMainProfile(user);
const reassigned = await roundRobinManualReassignment({ const reassigned = await roundRobinManualReassignment({
@@ -428,6 +481,8 @@ export class BookingsService_2024_08_13 {
orgId: profile?.organizationId || null, orgId: profile?.organizationId || null,
reassignReason: body.reason, reassignReason: body.reason,
reassignedById, reassignedById,
emailsEnabled,
platformClientParams,
}); });
return this.outputService.getOutputReassignedBooking(reassigned); return this.outputService.getOutputReassignedBooking(reassigned);
@@ -439,6 +494,12 @@ export class BookingsService_2024_08_13 {
throw new NotFoundException(`Booking with uid=${bookingUid} was not found in the database`); throw new NotFoundException(`Booking with uid=${bookingUid} was not found in the database`);
} }
const platformClientParams = booking.eventTypeId
? await this.inputService.getOAuthClientParams(booking.eventTypeId)
: undefined;
const emailsEnabled = platformClientParams ? platformClientParams.arePlatformEmailsEnabled : true;
await confirmBookingHandler({ await confirmBookingHandler({
ctx: { ctx: {
user: requestUser, user: requestUser,
@@ -447,6 +508,8 @@ export class BookingsService_2024_08_13 {
bookingId: booking.id, bookingId: booking.id,
confirmed: true, confirmed: true,
recurringEventId: booking.recurringEventId, recurringEventId: booking.recurringEventId,
emailsEnabled,
platformClientParams,
}, },
}); });
@@ -459,6 +522,12 @@ export class BookingsService_2024_08_13 {
throw new NotFoundException(`Booking with uid=${bookingUid} was not found in the database`); throw new NotFoundException(`Booking with uid=${bookingUid} was not found in the database`);
} }
const platformClientParams = booking.eventTypeId
? await this.inputService.getOAuthClientParams(booking.eventTypeId)
: undefined;
const emailsEnabled = platformClientParams ? platformClientParams.arePlatformEmailsEnabled : true;
await confirmBookingHandler({ await confirmBookingHandler({
ctx: { ctx: {
user: requestUser, user: requestUser,
@@ -468,6 +537,8 @@ export class BookingsService_2024_08_13 {
confirmed: false, confirmed: false,
recurringEventId: booking.recurringEventId, recurringEventId: booking.recurringEventId,
reason, reason,
emailsEnabled,
platformClientParams,
}, },
}); });
@@ -1,7 +1,7 @@
import { BookingsRepository_2024_08_13 } from "@/ee/bookings/2024-08-13/bookings.repository"; import { BookingsRepository_2024_08_13 } from "@/ee/bookings/2024-08-13/bookings.repository";
import { import {
bookingResponsesSchema, bookingResponsesSchema,
seatedBookingResponsesSchema, seatedBookingDataSchema,
} from "@/ee/bookings/2024-08-13/services/output.service"; } from "@/ee/bookings/2024-08-13/services/output.service";
import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository"; import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository";
import { hashAPIKey, isApiKey, stripApiKey } from "@/lib/api-key"; import { hashAPIKey, isApiKey, stripApiKey } from "@/lib/api-key";
@@ -18,7 +18,6 @@ import { NextApiRequest } from "next/types";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { z } from "zod"; import { z } from "zod";
import { X_CAL_CLIENT_ID } from "@calcom/platform-constants";
import { EventTypeMetaDataSchema } from "@calcom/platform-libraries"; import { EventTypeMetaDataSchema } from "@calcom/platform-libraries";
import { import {
CancelBookingInput, CancelBookingInput,
@@ -33,19 +32,10 @@ import {
RescheduleBookingInput_2024_08_13, RescheduleBookingInput_2024_08_13,
RescheduleSeatedBookingInput_2024_08_13, RescheduleSeatedBookingInput_2024_08_13,
} from "@calcom/platform-types"; } from "@calcom/platform-types";
import { EventType } from "@calcom/prisma/client"; import { EventType, PlatformOAuthClient } from "@calcom/prisma/client";
type BookingRequest = NextApiRequest & { userId: number | undefined } & OAuthRequestParams; type BookingRequest = NextApiRequest & { userId: number | undefined } & OAuthRequestParams;
const DEFAULT_PLATFORM_PARAMS = {
platformClientId: "",
platformCancelUrl: "",
platformRescheduleUrl: "",
platformBookingUrl: "",
arePlatformEmailsEnabled: false,
platformBookingLocation: undefined,
};
type OAuthRequestParams = { type OAuthRequestParams = {
platformClientId: string; platformClientId: string;
platformRescheduleUrl: string; platformRescheduleUrl: string;
@@ -92,30 +82,53 @@ export class InputBookingsService_2024_08_13 {
body: CreateBookingInput_2024_08_13 | CreateInstantBookingInput_2024_08_13 body: CreateBookingInput_2024_08_13 | CreateInstantBookingInput_2024_08_13
): Promise<BookingRequest> { ): Promise<BookingRequest> {
const bodyTransformed = await this.transformInputCreateBooking(body); const bodyTransformed = await this.transformInputCreateBooking(body);
const oAuthClientId = request.get(X_CAL_CLIENT_ID); const oAuthClientParams = await this.getOAuthClientParams(body.eventTypeId);
const requestId = request.get("X-Request-Id");
const newRequest = { ...request }; const newRequest = { ...request };
const userId = (await this.createBookingRequestOwnerId(request)) ?? undefined; const userId = (await this.createBookingRequestOwnerId(request)) ?? undefined;
const oAuthParams = oAuthClientId
? await this.createBookingRequestOAuthClientParams(oAuthClientId)
: DEFAULT_PLATFORM_PARAMS;
const location = request.body.location || request.body.meetingUrl; const location = request.body.location || request.body.meetingUrl;
this.logger.log(`createBookingRequest_2024_04_15`, { this.logger.log(`createBookingRequest_2024_04_15`, {
requestId, requestId: request.get("X-Request-Id"),
ownerId: userId, ownerId: userId,
location, location,
oAuthClientId, oAuthClientParams,
...oAuthParams,
}); });
Object.assign(newRequest, { userId, ...oAuthParams, platformBookingLocation: location });
newRequest.body = { ...bodyTransformed, noEmail: !oAuthParams.arePlatformEmailsEnabled }; if (oAuthClientParams) {
Object.assign(newRequest, { userId, ...oAuthClientParams, platformBookingLocation: location });
newRequest.body = { ...bodyTransformed, noEmail: !oAuthClientParams.arePlatformEmailsEnabled };
} else {
Object.assign(newRequest, { userId, platformBookingLocation: location });
newRequest.body = { ...bodyTransformed, noEmail: false };
}
return newRequest as unknown as BookingRequest; return newRequest as unknown as BookingRequest;
} }
async getOAuthClientParams(eventTypeId: number) {
const eventType = await this.eventTypesRepository.getEventTypeById(eventTypeId);
let oAuthClient: PlatformOAuthClient | null = null;
if (eventType?.userId) {
oAuthClient = await this.oAuthClientRepository.getByUserId(eventType.userId);
} else if (eventType?.teamId) {
oAuthClient = await this.oAuthClientRepository.getByTeamId(eventType.teamId);
}
if (oAuthClient) {
return {
platformClientId: oAuthClient.id,
platformCancelUrl: oAuthClient.bookingCancelRedirectUri,
platformRescheduleUrl: oAuthClient.bookingRescheduleRedirectUri,
platformBookingUrl: oAuthClient.bookingRedirectUri,
arePlatformEmailsEnabled: oAuthClient.areEmailsEnabled,
};
}
return undefined;
}
async transformInputCreateBooking(inputBooking: CreateBookingInput_2024_08_13) { async transformInputCreateBooking(inputBooking: CreateBookingInput_2024_08_13) {
const eventType = await this.eventTypesRepository.getEventTypeByIdWithOwnerAndTeam( const eventType = await this.eventTypesRepository.getEventTypeByIdWithOwnerAndTeam(
inputBooking.eventTypeId inputBooking.eventTypeId
@@ -139,9 +152,7 @@ export class InputBookingsService_2024_08_13 {
eventTypeId: inputBooking.eventTypeId, eventTypeId: inputBooking.eventTypeId,
timeZone: inputBooking.attendee.timeZone, timeZone: inputBooking.attendee.timeZone,
language: inputBooking.attendee.language || "en", language: inputBooking.attendee.language || "en",
// todo(Lauris): expose after refactoring metadata https://app.campsite.co/cal/posts/zysq8w9rwm9c metadata: inputBooking.metadata || {},
// metadata: inputBooking.metadata || {},
metadata: {},
hasHashedBookingLink: false, hasHashedBookingLink: false,
guests: inputBooking.guests, guests: inputBooking.guests,
// note(Lauris): responses with name and email are required by the handleNewBooking // note(Lauris): responses with name and email are required by the handleNewBooking
@@ -180,21 +191,23 @@ export class InputBookingsService_2024_08_13 {
): Promise<BookingRequest> { ): Promise<BookingRequest> {
// note(Lauris): update to this.transformInputCreate when rescheduling is implemented // note(Lauris): update to this.transformInputCreate when rescheduling is implemented
const bodyTransformed = await this.transformInputCreateRecurringBooking(body); const bodyTransformed = await this.transformInputCreateRecurringBooking(body);
const oAuthClientId = request.get(X_CAL_CLIENT_ID); const oAuthClientParams = await this.getOAuthClientParams(body.eventTypeId);
const newRequest = { ...request }; const newRequest = { ...request };
const userId = (await this.createBookingRequestOwnerId(request)) ?? undefined; const userId = (await this.createBookingRequestOwnerId(request)) ?? undefined;
const oAuthParams = oAuthClientId
? await this.createBookingRequestOAuthClientParams(oAuthClientId)
: DEFAULT_PLATFORM_PARAMS;
const location = request.body.location || request.body.meetingUrl; const location = request.body.location || request.body.meetingUrl;
Object.assign(newRequest, {
userId, if (oAuthClientParams) {
...oAuthParams, Object.assign(newRequest, {
platformBookingLocation: location, userId,
noEmail: !oAuthParams.arePlatformEmailsEnabled, ...oAuthClientParams,
}); platformBookingLocation: location,
noEmail: !oAuthClientParams.arePlatformEmailsEnabled,
});
} else {
Object.assign(newRequest, { userId, platformBookingLocation: location });
}
newRequest.body = bodyTransformed.map((event) => ({ newRequest.body = bodyTransformed.map((event) => ({
...event, ...event,
@@ -243,9 +256,7 @@ export class InputBookingsService_2024_08_13 {
recurringEventId, recurringEventId,
timeZone: inputBooking.attendee.timeZone, timeZone: inputBooking.attendee.timeZone,
language: inputBooking.attendee.language || "en", language: inputBooking.attendee.language || "en",
// todo(Lauris): expose after refactoring metadata https://app.campsite.co/cal/posts/zysq8w9rwm9c metadata: inputBooking.metadata || {},
// metadata: inputBooking.metadata || {},
metadata: {},
hasHashedBookingLink: false, hasHashedBookingLink: false,
guests: inputBooking.guests, guests: inputBooking.guests,
// note(Lauris): responses with name and email are required by the handleNewBooking // note(Lauris): responses with name and email are required by the handleNewBooking
@@ -285,18 +296,20 @@ export class InputBookingsService_2024_08_13 {
const bodyTransformed = this.isRescheduleSeatedBody(body) const bodyTransformed = this.isRescheduleSeatedBody(body)
? await this.transformInputRescheduleSeatedBooking(bookingUid, body) ? await this.transformInputRescheduleSeatedBooking(bookingUid, body)
: await this.transformInputRescheduleBooking(bookingUid, body); : await this.transformInputRescheduleBooking(bookingUid, body);
const oAuthClientId = request.get(X_CAL_CLIENT_ID);
const oAuthClientParams = await this.getOAuthClientParams(bodyTransformed.eventTypeId);
const newRequest = { ...request }; const newRequest = { ...request };
const userId = (await this.createBookingRequestOwnerId(request)) ?? undefined; const userId = (await this.createBookingRequestOwnerId(request)) ?? undefined;
const oAuthParams = oAuthClientId
? await this.createBookingRequestOAuthClientParams(oAuthClientId)
: DEFAULT_PLATFORM_PARAMS;
const location = await this.getRescheduleBookingLocation(bookingUid); const location = await this.getRescheduleBookingLocation(bookingUid);
Object.assign(newRequest, { userId, ...oAuthParams, platformBookingLocation: location }); if (oAuthClientParams) {
Object.assign(newRequest, { userId, ...oAuthClientParams, platformBookingLocation: location });
newRequest.body = { ...bodyTransformed, noEmail: !oAuthParams.arePlatformEmailsEnabled }; newRequest.body = { ...bodyTransformed, noEmail: !oAuthClientParams.arePlatformEmailsEnabled };
} else {
Object.assign(newRequest, { userId, platformBookingLocation: location });
newRequest.body = { ...bodyTransformed, noEmail: false };
}
return newRequest as unknown as BookingRequest; return newRequest as unknown as BookingRequest;
} }
@@ -327,7 +340,7 @@ export class InputBookingsService_2024_08_13 {
throw new NotFoundException(`Seat with uid=${inputBooking.seatUid} does not exist.`); throw new NotFoundException(`Seat with uid=${inputBooking.seatUid} does not exist.`);
} }
const { responses: bookingResponses } = seatedBookingResponsesSchema.parse(seat.data); const { responses: bookingResponses } = seatedBookingDataSchema.parse(seat.data);
const attendee = booking.attendees.find((attendee) => attendee.email === bookingResponses.email); const attendee = booking.attendees.find((attendee) => attendee.email === bookingResponses.email);
if (!attendee) { if (!attendee) {
@@ -345,9 +358,7 @@ export class InputBookingsService_2024_08_13 {
eventTypeId: eventType.id, eventTypeId: eventType.id,
timeZone: attendee.timeZone, timeZone: attendee.timeZone,
language: attendee.locale, language: attendee.locale,
// todo(Lauris): expose after refactoring metadata https://app.campsite.co/cal/posts/zysq8w9rwm9c metadata: seat.metadata || {},
// metadata: booking.metadata || {},
metadata: {},
hasHashedBookingLink: false, hasHashedBookingLink: false,
guests: [], guests: [],
responses: { ...bookingResponses }, responses: { ...bookingResponses },
@@ -386,9 +397,7 @@ export class InputBookingsService_2024_08_13 {
eventTypeId: eventType.id, eventTypeId: eventType.id,
timeZone: attendee.timeZone, timeZone: attendee.timeZone,
language: attendee.locale, language: attendee.locale,
// todo(Lauris): expose after refactoring metadata https://app.campsite.co/cal/posts/zysq8w9rwm9c metadata: booking.metadata || {},
// metadata: booking.metadata || {},
metadata: {},
hasHashedBookingLink: false, hasHashedBookingLink: false,
guests: bookingResponses.guests, guests: bookingResponses.guests,
responses: { ...bookingResponses, rescheduledReason: inputBooking.reschedulingReason }, responses: { ...bookingResponses, rescheduledReason: inputBooking.reschedulingReason },
@@ -423,24 +432,6 @@ export class InputBookingsService_2024_08_13 {
} }
} }
private async createBookingRequestOAuthClientParams(clientId: string) {
const params = DEFAULT_PLATFORM_PARAMS;
try {
const client = await this.oAuthClientRepository.getOAuthClient(clientId);
if (client) {
params.platformClientId = clientId;
params.platformCancelUrl = client.bookingCancelRedirectUri ?? "";
params.platformRescheduleUrl = client.bookingRescheduleRedirectUri ?? "";
params.platformBookingUrl = client.bookingRedirectUri ?? "";
params.arePlatformEmailsEnabled = client.areEmailsEnabled ?? false;
}
return params;
} catch (err) {
this.logger.error(err);
return params;
}
}
transformGetBookingsFilters(queryParams: GetBookingsInput_2024_08_13) { transformGetBookingsFilters(queryParams: GetBookingsInput_2024_08_13) {
return { return {
attendeeEmail: queryParams.attendeeEmail, attendeeEmail: queryParams.attendeeEmail,
@@ -473,17 +464,26 @@ export class InputBookingsService_2024_08_13 {
const bodyTransformed = this.isCancelSeatedBody(body) const bodyTransformed = this.isCancelSeatedBody(body)
? await this.transformInputCancelSeatedBooking(bookingUid, body) ? await this.transformInputCancelSeatedBooking(bookingUid, body)
: await this.transformInputCancelBooking(bookingUid, body); : await this.transformInputCancelBooking(bookingUid, body);
const oAuthClientId = request.get(X_CAL_CLIENT_ID);
const booking = await this.bookingsRepository.getByUid(bodyTransformed.uid);
if (!booking) {
throw new NotFoundException(`Booking with uid=${bookingUid} not found`);
}
const oAuthClientParams = booking.eventTypeId
? await this.getOAuthClientParams(booking.eventTypeId)
: undefined;
const newRequest = { ...request }; const newRequest = { ...request };
const userId = (await this.createBookingRequestOwnerId(request)) ?? undefined; const userId = (await this.createBookingRequestOwnerId(request)) ?? undefined;
const oAuthParams = oAuthClientId
? await this.createBookingRequestOAuthClientParams(oAuthClientId)
: DEFAULT_PLATFORM_PARAMS;
Object.assign(newRequest, { userId, ...oAuthParams }); if (oAuthClientParams) {
Object.assign(newRequest, { userId, ...oAuthClientParams });
newRequest.body = { ...bodyTransformed, noEmail: !oAuthParams.arePlatformEmailsEnabled }; newRequest.body = { ...bodyTransformed, noEmail: !oAuthClientParams.arePlatformEmailsEnabled };
} else {
Object.assign(newRequest, { userId });
newRequest.body = { ...bodyTransformed, noEmail: false };
}
return newRequest as unknown as BookingRequest; return newRequest as unknown as BookingRequest;
} }
@@ -1,9 +1,17 @@
import { BookingsRepository_2024_08_13 } from "@/ee/bookings/2024-08-13/bookings.repository"; import { BookingsRepository_2024_08_13 } from "@/ee/bookings/2024-08-13/bookings.repository";
import {
defaultBookingMetadata,
defaultBookingResponses,
defaultSeatedBookingData,
defaultSeatedBookingMetadata,
} from "@/lib/safe-parse/default-responses-booking";
import { safeParse } from "@/lib/safe-parse/safe-parse";
import { Injectable } from "@nestjs/common"; import { Injectable } from "@nestjs/common";
import { plainToClass } from "class-transformer"; import { plainToClass } from "class-transformer";
import { DateTime } from "luxon"; import { DateTime } from "luxon";
import { z } from "zod"; import { z } from "zod";
import { bookingMetadataSchema } from "@calcom/platform-libraries";
import { import {
BookingOutput_2024_08_13, BookingOutput_2024_08_13,
CreateRecurringSeatedBookingOutput_2024_08_13, CreateRecurringSeatedBookingOutput_2024_08_13,
@@ -19,22 +27,40 @@ import { Booking, BookingSeat } from "@calcom/prisma/client";
export const bookingResponsesSchema = z export const bookingResponsesSchema = z
.object({ .object({
email: z.string(), email: z.string(),
name: z.string(), name: z.union([
z.string(),
z.object({
firstName: z.string(),
lastName: z.string(),
}),
]),
guests: z.array(z.string()).optional(), guests: z.array(z.string()).optional(),
rescheduleReason: z.string().optional(), rescheduleReason: z.string().optional(),
}) })
.passthrough(); .passthrough()
.describe("BookingResponses");
export const seatedBookingResponsesSchema = z export const seatedBookingDataSchema = z
.object({ .object({
responses: z responses: z
.object({ .object({
email: z.string(), email: z.string(),
name: z.string(), name: z.union([
z.string(),
z.object({
firstName: z.string(),
lastName: z.string(),
}),
]),
}) })
.passthrough(), .passthrough(),
}) })
.passthrough(); .passthrough()
.describe("SeatedBookingData");
const seatedBookingMetadataSchema = z.object({}).catchall(z.string()).describe("SeatedBookingMetadata");
type DatabaseUser = { id: number; name: string | null; email: string; username: string | null };
type DatabaseBooking = Booking & { type DatabaseBooking = Booking & {
eventType: { eventType: {
@@ -49,9 +75,13 @@ type DatabaseBooking = Booking & {
noShow: boolean | null; noShow: boolean | null;
bookingSeat?: BookingSeat | null; bookingSeat?: BookingSeat | null;
}[]; }[];
} & { user: { id: number; name: string | null; email: string } | null }; user: DatabaseUser | null;
createdAt: Date;
};
type BookingWithUser = Booking & { user: { id: number; name: string | null; email: string } | null }; type BookingWithUser = Booking & { user: DatabaseUser | null };
type DatabaseMetadata = z.infer<typeof bookingMetadataSchema>;
@Injectable() @Injectable()
export class OutputBookingsService_2024_08_13 { export class OutputBookingsService_2024_08_13 {
@@ -61,15 +91,20 @@ export class OutputBookingsService_2024_08_13 {
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString()); const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString()); const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
const duration = dateEnd.diff(dateStart, "minutes").minutes; const duration = dateEnd.diff(dateStart, "minutes").minutes;
const bookingResponses = safeParse(
const bookingResponses = bookingResponsesSchema.parse(databaseBooking.responses); bookingResponsesSchema,
databaseBooking.responses,
defaultBookingResponses
);
const metadata = safeParse(bookingMetadataSchema, databaseBooking.metadata, defaultBookingMetadata);
const location = metadata?.videoCallUrl || databaseBooking.location;
const booking = { const booking = {
id: databaseBooking.id, id: databaseBooking.id,
uid: databaseBooking.uid, uid: databaseBooking.uid,
title: databaseBooking.title, title: databaseBooking.title,
description: databaseBooking.description, description: databaseBooking.description,
hosts: [databaseBooking.user], hosts: [this.getHost(databaseBooking.user)],
status: databaseBooking.status.toLowerCase(), status: databaseBooking.status.toLowerCase(),
cancellationReason: databaseBooking.cancellationReason || undefined, cancellationReason: databaseBooking.cancellationReason || undefined,
reschedulingReason: bookingResponses?.rescheduledReason, reschedulingReason: bookingResponses?.rescheduledReason,
@@ -88,18 +123,44 @@ export class OutputBookingsService_2024_08_13 {
absent: !!attendee.noShow, absent: !!attendee.noShow,
})), })),
guests: bookingResponses.guests, guests: bookingResponses.guests,
location: databaseBooking.location, location,
// note(Lauris): meetingUrl is deprecated // note(Lauris): meetingUrl is deprecated
meetingUrl: databaseBooking.location, meetingUrl: location,
absentHost: !!databaseBooking.noShowHost, absentHost: !!databaseBooking.noShowHost,
createdAt: databaseBooking.createdAt,
}; };
const bookingTransformed = plainToClass(BookingOutput_2024_08_13, booking, { strategy: "excludeAll" }); const bookingTransformed = plainToClass(BookingOutput_2024_08_13, booking, { strategy: "excludeAll" });
// note(Lauris): I don't know why plainToClass erases bookings responses so attaching manually // note(Lauris): I don't know why plainToClass erases bookings responses and metadata so attaching manually
bookingTransformed.bookingFieldsResponses = bookingResponses; bookingTransformed.bookingFieldsResponses = bookingResponses;
bookingTransformed.metadata = this.getUserDefinedMetadata(metadata);
return bookingTransformed; return bookingTransformed;
} }
getUserDefinedMetadata(databaseMetadata: DatabaseMetadata) {
if (databaseMetadata === null) return {};
const { videoCallUrl, ...userDefinedMetadata } = databaseMetadata;
return userDefinedMetadata;
}
getHost(user: DatabaseUser | null) {
if (!user) {
return {
id: "unknown",
name: "unknown",
email: "unknown",
username: "unknown",
};
}
return {
...user,
username: user.username || "unknown",
};
}
async getOutputRecurringBookings(bookingsIds: number[]) { async getOutputRecurringBookings(bookingsIds: number[]) {
const transformed = []; const transformed = [];
@@ -119,15 +180,20 @@ export class OutputBookingsService_2024_08_13 {
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString()); const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString()); const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
const duration = dateEnd.diff(dateStart, "minutes").minutes; const duration = dateEnd.diff(dateStart, "minutes").minutes;
const bookingResponses = safeParse(
const bookingResponses = bookingResponsesSchema.parse(databaseBooking.responses); bookingResponsesSchema,
databaseBooking.responses,
defaultBookingResponses
);
const metadata = safeParse(bookingMetadataSchema, databaseBooking.metadata, defaultBookingMetadata);
const location = metadata?.videoCallUrl || databaseBooking.location;
const booking = { const booking = {
id: databaseBooking.id, id: databaseBooking.id,
uid: databaseBooking.uid, uid: databaseBooking.uid,
title: databaseBooking.title, title: databaseBooking.title,
description: databaseBooking.description, description: databaseBooking.description,
hosts: [databaseBooking.user], hosts: [this.getHost(databaseBooking.user)],
status: databaseBooking.status.toLowerCase(), status: databaseBooking.status.toLowerCase(),
cancellationReason: databaseBooking.cancellationReason || undefined, cancellationReason: databaseBooking.cancellationReason || undefined,
reschedulingReason: bookingResponses?.rescheduledReason, reschedulingReason: bookingResponses?.rescheduledReason,
@@ -146,15 +212,22 @@ export class OutputBookingsService_2024_08_13 {
absent: !!attendee.noShow, absent: !!attendee.noShow,
})), })),
guests: bookingResponses.guests, guests: bookingResponses.guests,
location: databaseBooking.location, location,
// note(Lauris): meetingUrl is deprecated // note(Lauris): meetingUrl is deprecated
meetingUrl: databaseBooking.location, meetingUrl: location,
recurringBookingUid: databaseBooking.recurringEventId, recurringBookingUid: databaseBooking.recurringEventId,
absentHost: !!databaseBooking.noShowHost, absentHost: !!databaseBooking.noShowHost,
bookingFieldsResponses: databaseBooking.responses, bookingFieldsResponses: databaseBooking.responses,
createdAt: databaseBooking.createdAt,
}; };
return plainToClass(RecurringBookingOutput_2024_08_13, booking, { strategy: "excludeAll" }); const bookingTransformed = plainToClass(RecurringBookingOutput_2024_08_13, booking, {
strategy: "excludeAll",
});
// note(Lauris): I don't know why plainToClass erases bookings responses and metadata so attaching manually
bookingTransformed.bookingFieldsResponses = bookingResponses;
bookingTransformed.metadata = this.getUserDefinedMetadata(metadata);
return bookingTransformed;
} }
getOutputCreateSeatedBooking( getOutputCreateSeatedBooking(
@@ -169,13 +242,15 @@ export class OutputBookingsService_2024_08_13 {
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString()); const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString()); const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
const duration = dateEnd.diff(dateStart, "minutes").minutes; const duration = dateEnd.diff(dateStart, "minutes").minutes;
const metadata = safeParse(bookingMetadataSchema, databaseBooking.metadata, defaultBookingMetadata);
const location = metadata?.videoCallUrl || databaseBooking.location;
const booking = { const booking = {
id: databaseBooking.id, id: databaseBooking.id,
uid: databaseBooking.uid, uid: databaseBooking.uid,
title: databaseBooking.title, title: databaseBooking.title,
description: databaseBooking.description, description: databaseBooking.description,
hosts: [databaseBooking.user], hosts: [this.getHost(databaseBooking.user)],
status: databaseBooking.status.toLowerCase(), status: databaseBooking.status.toLowerCase(),
rescheduledFromUid: databaseBooking.fromReschedule || undefined, rescheduledFromUid: databaseBooking.fromReschedule || undefined,
start: databaseBooking.startTime, start: databaseBooking.startTime,
@@ -185,17 +260,22 @@ export class OutputBookingsService_2024_08_13 {
// note(Lauris): eventTypeId is deprecated // note(Lauris): eventTypeId is deprecated
eventTypeId: databaseBooking.eventTypeId, eventTypeId: databaseBooking.eventTypeId,
attendees: [], attendees: [],
location: databaseBooking.location, location,
// note(Lauris): meetingUrl is deprecated // note(Lauris): meetingUrl is deprecated
meetingUrl: databaseBooking.location, meetingUrl: location,
absentHost: !!databaseBooking.noShowHost, absentHost: !!databaseBooking.noShowHost,
createdAt: databaseBooking.createdAt,
}; };
const parsed = plainToClass(GetSeatedBookingOutput_2024_08_13, booking, { strategy: "excludeAll" }); const parsed = plainToClass(GetSeatedBookingOutput_2024_08_13, booking, { strategy: "excludeAll" });
// note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually // note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually
parsed.attendees = databaseBooking.attendees.map((attendee) => { parsed.attendees = databaseBooking.attendees.map((attendee) => {
const { responses } = seatedBookingResponsesSchema.parse(attendee.bookingSeat?.data); const { responses } = safeParse(
seatedBookingDataSchema,
attendee.bookingSeat?.data,
defaultSeatedBookingData
);
const attendeeData = { const attendeeData = {
name: attendee.name, name: attendee.name,
@@ -208,6 +288,11 @@ export class OutputBookingsService_2024_08_13 {
}; };
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" }); const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
attendeeParsed.bookingFieldsResponses = responses || {}; attendeeParsed.bookingFieldsResponses = responses || {};
attendeeParsed.metadata = safeParse(
seatedBookingMetadataSchema,
attendee.bookingSeat?.metadata,
defaultSeatedBookingMetadata
);
// note(Lauris): as of now email is not returned for privacy // note(Lauris): as of now email is not returned for privacy
delete attendeeParsed.bookingFieldsResponses.email; delete attendeeParsed.bookingFieldsResponses.email;
@@ -260,13 +345,15 @@ export class OutputBookingsService_2024_08_13 {
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString()); const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString()); const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
const duration = dateEnd.diff(dateStart, "minutes").minutes; const duration = dateEnd.diff(dateStart, "minutes").minutes;
const metadata = safeParse(bookingMetadataSchema, databaseBooking.metadata, defaultBookingMetadata);
const location = metadata?.videoCallUrl || databaseBooking.location;
const booking = { const booking = {
id: databaseBooking.id, id: databaseBooking.id,
uid: databaseBooking.uid, uid: databaseBooking.uid,
title: databaseBooking.title, title: databaseBooking.title,
description: databaseBooking.description, description: databaseBooking.description,
hosts: [databaseBooking.user], hosts: [this.getHost(databaseBooking.user)],
status: databaseBooking.status.toLowerCase(), status: databaseBooking.status.toLowerCase(),
cancellationReason: databaseBooking.cancellationReason || undefined, cancellationReason: databaseBooking.cancellationReason || undefined,
rescheduledFromUid: databaseBooking.fromReschedule || undefined, rescheduledFromUid: databaseBooking.fromReschedule || undefined,
@@ -277,11 +364,12 @@ export class OutputBookingsService_2024_08_13 {
// note(Lauris): eventTypeId is deprecated // note(Lauris): eventTypeId is deprecated
eventTypeId: databaseBooking.eventTypeId, eventTypeId: databaseBooking.eventTypeId,
attendees: [], attendees: [],
location: databaseBooking.location, location,
// note(Lauris): meetingUrl is deprecated // note(Lauris): meetingUrl is deprecated
meetingUrl: databaseBooking.location, meetingUrl: location,
recurringBookingUid: databaseBooking.recurringEventId, recurringBookingUid: databaseBooking.recurringEventId,
absentHost: !!databaseBooking.noShowHost, absentHost: !!databaseBooking.noShowHost,
createdAt: databaseBooking.createdAt,
}; };
const parsed = plainToClass(GetRecurringSeatedBookingOutput_2024_08_13, booking, { const parsed = plainToClass(GetRecurringSeatedBookingOutput_2024_08_13, booking, {
@@ -290,7 +378,11 @@ export class OutputBookingsService_2024_08_13 {
// note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually // note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually
parsed.attendees = databaseBooking.attendees.map((attendee) => { parsed.attendees = databaseBooking.attendees.map((attendee) => {
const { responses } = seatedBookingResponsesSchema.parse(attendee.bookingSeat?.data); const { responses } = safeParse(
seatedBookingDataSchema,
attendee.bookingSeat?.data,
defaultSeatedBookingData
);
const attendeeData = { const attendeeData = {
name: attendee.name, name: attendee.name,
@@ -303,9 +395,13 @@ export class OutputBookingsService_2024_08_13 {
}; };
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" }); const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
attendeeParsed.bookingFieldsResponses = responses || {}; attendeeParsed.bookingFieldsResponses = responses || {};
attendeeParsed.metadata = safeParse(
seatedBookingMetadataSchema,
attendee.bookingSeat?.metadata,
defaultSeatedBookingMetadata
);
// note(Lauris): as of now email is not returned for privacy // note(Lauris): as of now email is not returned for privacy
delete attendeeParsed.bookingFieldsResponses.email; delete attendeeParsed.bookingFieldsResponses.email;
return attendeeParsed; return attendeeParsed;
}); });
@@ -113,7 +113,7 @@ describe("Platform Calendars Endpoints", () => {
}); });
// TODO: Uncomment this once CI is ready to run proper Office365 tests // TODO: Uncomment this once CI is ready to run proper Office365 tests
// it(`/GET/v2/calendars/${OFFICE_365_CALENDAR}/connect: it should redirect to auth-url for office 365 calendar oauth with valid access token `, async () => { // it(`/GET/v2/calendars/${OFFICE_365_CALENDAR}/connect: it should redirect to auth-url for office 365 calendar OAuth with valid access token `, async () => {
// const response = await request(app.getHttpServer()) // const response = await request(app.getHttpServer())
// .get(`/v2/calendars/${OFFICE_365_CALENDAR}/connect`) // .get(`/v2/calendars/${OFFICE_365_CALENDAR}/connect`)
// .set("Authorization", `Bearer ${accessTokenSecret}`) // .set("Authorization", `Bearer ${accessTokenSecret}`)
@@ -123,7 +123,7 @@ describe("Platform Calendars Endpoints", () => {
// expect(data.authUrl).toBeDefined(); // expect(data.authUrl).toBeDefined();
// }); // });
it(`/GET/v2/calendars/${GOOGLE_CALENDAR}/connect: it should redirect to auth-url for google calendar oauth with valid access token `, async () => { it(`/GET/v2/calendars/${GOOGLE_CALENDAR}/connect: it should redirect to auth-url for google calendar OAuth with valid access token `, async () => {
const response = await request(app.getHttpServer()) const response = await request(app.getHttpServer())
.get(`/v2/calendars/${GOOGLE_CALENDAR}/connect`) .get(`/v2/calendars/${GOOGLE_CALENDAR}/connect`)
.set("Authorization", `Bearer ${accessTokenSecret}`) .set("Authorization", `Bearer ${accessTokenSecret}`)
@@ -1,4 +1,4 @@
import { ApiProperty } from "@nestjs/swagger"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { import {
ArrayNotEmpty, ArrayNotEmpty,
IsBoolean, IsBoolean,
@@ -50,11 +50,10 @@ export class CreateIcsFeedInputDto {
urls!: string[]; urls!: string[];
@IsBoolean() @IsBoolean()
@ApiProperty({ @ApiPropertyOptional({
example: false, example: false,
description: "Whether to allowing writing to the calendar or not", description: "Whether to allowing writing to the calendar or not",
type: "boolean", type: "boolean",
required: false,
default: true, default: true,
}) })
@IsOptional() @IsOptional()
@@ -1,4 +1,4 @@
import { ApiProperty } from "@nestjs/swagger"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { IsArray, IsDate, IsEnum, IsOptional, IsString, ValidateNested } from "class-validator"; import { IsArray, IsDate, IsEnum, IsOptional, IsString, ValidateNested } from "class-validator";
@@ -6,13 +6,16 @@ import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
export class BusyTimesOutput { export class BusyTimesOutput {
@IsDate() @IsDate()
@ApiProperty({ type: Date })
start!: Date; start!: Date;
@IsDate() @IsDate()
@ApiProperty({ type: Date })
end!: Date; end!: Date;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional({ type: String, nullable: true })
source?: string | null; source?: string | null;
} }
@@ -24,5 +27,6 @@ export class GetBusyTimesOutput {
@ValidateNested() @ValidateNested()
@Type(() => BusyTimesOutput) @Type(() => BusyTimesOutput)
@IsArray() @IsArray()
@ApiProperty({ type: [BusyTimesOutput] })
data!: BusyTimesOutput[]; data!: BusyTimesOutput[];
} }
@@ -1,4 +1,4 @@
import { ApiProperty } from "@nestjs/swagger"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { import {
IsArray, IsArray,
@@ -18,195 +18,246 @@ import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
class Integration { class Integration {
@IsOptional() @IsOptional()
@IsObject() @IsObject()
@ApiPropertyOptional({ type: Object, nullable: true })
appData?: object | null; appData?: object | null;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
dirName?: string; dirName?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
__template?: string; __template?: string;
@IsString() @IsString()
@ApiProperty()
name!: string; name!: string;
@IsString() @IsString()
@ApiProperty()
description!: string; description!: string;
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiPropertyOptional()
installed?: boolean; installed?: boolean;
@IsString() @IsString()
@ApiProperty()
type!: string; type!: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
title?: string; title?: string;
@IsString() @IsString()
@ApiProperty()
variant!: string; variant!: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
category?: string; category?: string;
@IsArray() @IsArray()
@IsString({ each: true }) @IsString({ each: true })
@ApiProperty({ type: [String] })
categories!: string[]; categories!: string[];
@IsString() @IsString()
@ApiProperty()
logo!: string; logo!: string;
@IsString() @IsString()
@ApiProperty()
publisher!: string; publisher!: string;
@IsString() @IsString()
@ApiProperty()
slug!: string; slug!: string;
@IsUrl() @IsUrl()
@ApiProperty()
url!: string; url!: string;
@IsEmail() @IsEmail()
@ApiProperty()
email!: string; email!: string;
@IsOptional()
@IsObject() @IsObject()
@ApiProperty({ type: Object, nullable: true })
locationOption!: object | null; locationOption!: object | null;
} }
class Primary { class Primary {
@IsEmail() @IsEmail()
@ApiProperty()
externalId!: string; externalId!: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
integration?: string; integration?: string;
@IsOptional() @IsOptional()
@IsEmail() @IsEmail()
@ApiPropertyOptional()
name?: string; name?: string;
@IsBoolean() @IsBoolean()
@ApiProperty({ type: Boolean, nullable: true })
primary!: boolean | null; primary!: boolean | null;
@IsBoolean() @IsBoolean()
@ApiProperty()
readOnly!: boolean; readOnly!: boolean;
@IsEmail() @IsEmail()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
email?: string; email?: string;
@IsBoolean() @IsBoolean()
@ApiProperty()
isSelected!: boolean; isSelected!: boolean;
@IsInt() @IsInt()
@ApiProperty()
credentialId!: number; credentialId!: number;
} }
export class Calendar { export class Calendar {
@IsEmail() @IsEmail()
@ApiProperty()
externalId!: string; externalId!: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
integration?: string; integration?: string;
@IsEmail() @IsEmail()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
name?: string; name?: string;
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiPropertyOptional({ type: Boolean, nullable: true })
primary?: boolean | null; primary?: boolean | null;
@IsBoolean() @IsBoolean()
@ApiProperty()
readOnly!: boolean; readOnly!: boolean;
@IsEmail() @IsEmail()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
email?: string; email?: string;
@IsBoolean() @IsBoolean()
@ApiProperty()
isSelected!: boolean; isSelected!: boolean;
@IsInt() @IsInt()
@ApiProperty()
credentialId!: number; credentialId!: number;
} }
export class ConnectedCalendar { export class ConnectedCalendar {
@ValidateNested() @ValidateNested()
@IsObject() @IsObject()
@ApiProperty({ type: Integration })
integration!: Integration; integration!: Integration;
@IsInt() @IsInt()
@ApiProperty()
credentialId!: number; credentialId!: number;
@ValidateNested() @ValidateNested()
@IsObject() @IsObject()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: Primary })
primary?: Primary; primary?: Primary;
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@IsArray() @IsArray()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: [Calendar] })
calendars?: Calendar[]; calendars?: Calendar[];
} }
class DestinationCalendar { class DestinationCalendar {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsString() @IsString()
@ApiProperty()
integration!: string; integration!: string;
@IsEmail() @IsEmail()
@ApiProperty()
externalId!: string; externalId!: string;
@IsEmail() @IsEmail()
@ApiProperty({ type: String, nullable: true })
primaryEmail!: string | null; primaryEmail!: string | null;
@IsInt() @IsInt()
@ApiProperty({ nullable: true })
userId!: number | null; userId!: number | null;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiProperty({ type: Number, nullable: true })
eventTypeId!: number | null; eventTypeId!: number | null;
@IsInt() @IsInt()
@ApiProperty({ type: Number, nullable: true })
credentialId!: number | null; credentialId!: number | null;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: String, nullable: true })
name?: string | null; name?: string | null;
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
primary?: boolean; primary?: boolean;
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
readOnly?: boolean; readOnly?: boolean;
@IsEmail() @IsEmail()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
email?: string; email?: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
integrationTitle?: string; integrationTitle?: string;
} }
export class ConnectedCalendarsData { export class ConnectedCalendarsData {
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@IsArray() @IsArray()
@ApiProperty({ type: [ConnectedCalendar] })
connectedCalendars!: ConnectedCalendar[]; connectedCalendars!: ConnectedCalendar[];
@ValidateNested() @ValidateNested()
@IsObject() @IsObject()
@ApiProperty({ type: DestinationCalendar })
destinationCalendar!: DestinationCalendar; destinationCalendar!: DestinationCalendar;
} }
export class ConnectedCalendarsOutput { export class ConnectedCalendarsOutput {
@ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] }) @ApiProperty({ example: SUCCESS_STATUS, enum: [SUCCESS_STATUS, ERROR_STATUS] })
@IsEnum([SUCCESS_STATUS, ERROR_STATUS]) @IsEnum([SUCCESS_STATUS, ERROR_STATUS])
@@ -214,5 +265,6 @@ export class ConnectedCalendarsOutput {
@ValidateNested() @ValidateNested()
@Type(() => ConnectedCalendarsData) @Type(() => ConnectedCalendarsData)
@ApiProperty({ type: ConnectedCalendarsData })
data!: ConnectedCalendarsData; data!: ConnectedCalendarsData;
} }
@@ -4,13 +4,14 @@ import { AppsRepository } from "@/modules/apps/apps.repository";
import { CredentialsRepository } from "@/modules/credentials/credentials.repository"; import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository"; import { SelectedCalendarsRepository } from "@/modules/selected-calendars/selected-calendars.repository";
import { TokensRepository } from "@/modules/tokens/tokens.repository"; import { TokensRepository } from "@/modules/tokens/tokens.repository";
import { calendar_v3 } from "@googleapis/calendar";
import { Logger, NotFoundException } from "@nestjs/common"; import { Logger, NotFoundException } from "@nestjs/common";
import { BadRequestException, UnauthorizedException } from "@nestjs/common"; import { BadRequestException, UnauthorizedException } from "@nestjs/common";
import { Injectable } from "@nestjs/common"; import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config"; import { ConfigService } from "@nestjs/config";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import { Request } from "express"; import { Request } from "express";
import { google } from "googleapis"; import { OAuth2Client } from "googleapis-common";
import { z } from "zod"; import { z } from "zod";
import { SUCCESS_STATUS, GOOGLE_CALENDAR_TYPE } from "@calcom/platform-constants"; import { SUCCESS_STATUS, GOOGLE_CALENDAR_TYPE } from "@calcom/platform-constants";
@@ -78,7 +79,7 @@ export class GoogleCalendarService implements OAuthCalendarApp {
const { client_id, client_secret } = this.gcalResponseSchema.parse(app.keys); const { client_id, client_secret } = this.gcalResponseSchema.parse(app.keys);
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirectUri); const oAuth2Client = new OAuth2Client(client_id, client_secret, redirectUri);
return oAuth2Client; return oAuth2Client;
} }
@@ -90,7 +91,7 @@ export class GoogleCalendarService implements OAuthCalendarApp {
} }
if (gcalCredentials.invalid) { if (gcalCredentials.invalid) {
throw new BadRequestException("Invalid google oauth credentials."); throw new BadRequestException("Invalid google OAuth credentials.");
} }
const { connectedCalendars } = await this.calendarsService.getCalendars(userId); const { connectedCalendars } = await this.calendarsService.getCalendars(userId);
@@ -115,7 +116,7 @@ export class GoogleCalendarService implements OAuthCalendarApp {
) { ) {
// User chose not to authorize your app or didn't authorize your app // User chose not to authorize your app or didn't authorize your app
// redirect directly without oauth code // redirect directly without oauth code
if (!code) { if (!code || code === "undefined") {
return { url: redir || origin }; return { url: redir || origin };
} }
@@ -134,8 +135,7 @@ export class GoogleCalendarService implements OAuthCalendarApp {
oAuth2Client.setCredentials(key); oAuth2Client.setCredentials(key);
const calendar = google.calendar({ const calendar = new calendar_v3.Calendar({
version: "v3",
auth: oAuth2Client, auth: oAuth2Client,
}); });
@@ -151,7 +151,7 @@ export class OutlookService implements OAuthCalendarApp {
redir?: string redir?: string
) { ) {
// if code is not defined, user denied to authorize office 365 app, just redirect straight away // if code is not defined, user denied to authorize office 365 app, just redirect straight away
if (!code) { if (!code || code === "undefined") {
return { url: redir || origin }; return { url: redir || origin };
} }
@@ -78,4 +78,11 @@ export class EventTypesRepository_2024_04_15 {
async deleteEventType(eventTypeId: number) { async deleteEventType(eventTypeId: number) {
return this.dbWrite.prisma.eventType.delete({ where: { id: eventTypeId } }); return this.dbWrite.prisma.eventType.delete({ where: { id: eventTypeId } });
} }
async getEventTypeWithDuration(eventTypeId: number) {
return this.dbRead.prisma.eventType.findUnique({
where: { id: eventTypeId },
select: { length: true },
});
}
} }
@@ -1,5 +1,5 @@
import { EventTypeLocation_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/inputs/event-type-location.input"; import { EventTypeLocation_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/inputs/event-type-location.input";
import { ApiProperty as DocsProperty, ApiHideProperty } from "@nestjs/swagger"; import { ApiProperty as DocsProperty, ApiPropertyOptional, ApiHideProperty } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { import {
IsString, IsString,
@@ -36,7 +36,10 @@ export class CreateEventTypeInput_2024_04_15 {
@IsOptional() @IsOptional()
@IsString() @IsString()
@DocsProperty({ example: CREATE_EVENT_DESCRIPTION_EXAMPLE }) @ApiPropertyOptional({
type: String,
example: CREATE_EVENT_DESCRIPTION_EXAMPLE,
})
description?: string; description?: string;
@IsOptional() @IsOptional()
@@ -48,30 +51,38 @@ export class CreateEventTypeInput_2024_04_15 {
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => EventTypeLocation_2024_04_15) @Type(() => EventTypeLocation_2024_04_15)
@IsArray() @IsArray()
@ApiPropertyOptional({
type: [EventTypeLocation_2024_04_15],
})
locations?: EventTypeLocation_2024_04_15[]; locations?: EventTypeLocation_2024_04_15[];
@IsBoolean()
@IsOptional() @IsOptional()
@IsBoolean()
@ApiPropertyOptional()
disableGuests?: boolean; disableGuests?: boolean;
@IsOptional()
@IsInt() @IsInt()
@Min(0) @Min(0)
@IsOptional() @ApiPropertyOptional()
slotInterval?: number; slotInterval?: number;
@IsOptional()
@IsInt() @IsInt()
@Min(0) @Min(0)
@IsOptional() @ApiPropertyOptional()
minimumBookingNotice?: number; minimumBookingNotice?: number;
@IsOptional()
@IsInt() @IsInt()
@Min(0) @Min(0)
@IsOptional() @ApiPropertyOptional()
beforeEventBuffer?: number; beforeEventBuffer?: number;
@IsOptional()
@IsInt() @IsInt()
@Min(0) @Min(0)
@IsOptional() @ApiPropertyOptional()
afterEventBuffer?: number; afterEventBuffer?: number;
// @ApiHideProperty() // @ApiHideProperty()
@@ -1,4 +1,4 @@
import { ApiProperty as DocsProperty, ApiHideProperty } from "@nestjs/swagger"; import { ApiProperty, ApiHideProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { IsString, IsNumber, IsBoolean, IsOptional, IsUrl } from "class-validator"; import { IsString, IsNumber, IsBoolean, IsOptional, IsUrl } from "class-validator";
// note(Lauris): We will gradually expose more properties if any customer needs them. // note(Lauris): We will gradually expose more properties if any customer needs them.
@@ -6,7 +6,7 @@ import { IsString, IsNumber, IsBoolean, IsOptional, IsUrl } from "class-validato
export class EventTypeLocation_2024_04_15 { export class EventTypeLocation_2024_04_15 {
@IsString() @IsString()
@DocsProperty({ example: "link" }) @ApiProperty({ example: "link" })
type!: string; type!: string;
@IsOptional() @IsOptional()
@@ -16,7 +16,7 @@ export class EventTypeLocation_2024_04_15 {
@IsOptional() @IsOptional()
@IsUrl() @IsUrl()
@DocsProperty({ example: "https://masterchief.com/argentina/flan/video/9129412" }) @ApiPropertyOptional({ example: "https://masterchief.com/argentina/flan/video/9129412" })
link?: string; link?: string;
@IsOptional() @IsOptional()
@@ -1,4 +1,4 @@
import { ApiProperty } from "@nestjs/swagger"; import { ApiPropertyOptional } from "@nestjs/swagger";
import { Transform } from "class-transformer"; import { Transform } from "class-transformer";
import { IsBoolean, IsOptional, IsString } from "class-validator"; import { IsBoolean, IsOptional, IsString } from "class-validator";
@@ -6,11 +6,11 @@ export class GetPublicEventTypeQueryParams_2024_04_15 {
@Transform(({ value }: { value: string }) => value === "true") @Transform(({ value }: { value: string }) => value === "true")
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiProperty({ required: false }) @ApiPropertyOptional()
isTeamEvent?: boolean; isTeamEvent?: boolean;
@ApiProperty({ required: false })
@IsString()
@IsOptional() @IsOptional()
@IsString()
@ApiPropertyOptional({ type: String, nullable: true })
org?: string | null; org?: string | null;
} }
@@ -2,6 +2,7 @@ import { Editable } from "@/ee/event-types/event-types_2024_04_15/inputs/enums/e
import { BaseField } from "@/ee/event-types/event-types_2024_04_15/inputs/enums/field-type"; import { BaseField } from "@/ee/event-types/event-types_2024_04_15/inputs/enums/field-type";
import { Frequency } from "@/ee/event-types/event-types_2024_04_15/inputs/enums/frequency"; import { Frequency } from "@/ee/event-types/event-types_2024_04_15/inputs/enums/frequency";
import { EventTypeLocation_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/inputs/event-type-location.input"; import { EventTypeLocation_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/inputs/event-type-location.input";
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { import {
IsString, IsString,
@@ -21,206 +22,259 @@ import {
class Option { class Option {
@IsString() @IsString()
@ApiProperty()
value!: string; value!: string;
@IsString() @IsString()
@ApiProperty()
label!: string; label!: string;
} }
class Source { class Source {
@IsString() @IsString()
@ApiProperty()
id!: string; id!: string;
@IsString() @IsString()
@ApiProperty()
type!: string; type!: string;
@IsString() @IsString()
@ApiProperty()
label!: string; label!: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
editUrl?: string; editUrl?: string;
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiPropertyOptional()
fieldRequired?: boolean; fieldRequired?: boolean;
} }
class View { class View {
@IsString() @IsString()
@ApiProperty()
id!: string; id!: string;
@IsString() @IsString()
@ApiProperty()
label!: string; label!: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
description?: string; description?: string;
} }
class OptionsInput { class OptionsInput {
@IsString() @IsString()
@ApiProperty({
description: 'Type of the field, can be one of "address", "text", or "phone".',
enum: ["address", "text", "phone"],
example: "text",
})
type!: "address" | "text" | "phone"; type!: "address" | "text" | "phone";
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiPropertyOptional()
required?: boolean; required?: boolean;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
placeholder?: string; placeholder?: string;
} }
class VariantField { class VariantField {
@IsString() @IsString()
@ApiProperty()
type!: BaseField; type!: BaseField;
@IsString() @IsString()
@ApiProperty()
name!: string; name!: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
label?: string; label?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
labelAsSafeHtml?: string; labelAsSafeHtml?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
placeholder?: string; placeholder?: string;
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiPropertyOptional()
required?: boolean; required?: boolean;
} }
class Variant { class Variant {
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => VariantField) @Type(() => VariantField)
@ApiProperty({ type: [VariantField] })
fields!: VariantField[]; fields!: VariantField[];
} }
class VariantsConfig { class VariantsConfig {
@ApiProperty({ type: Object })
variants!: Record<string, Variant>; variants!: Record<string, Variant>;
} }
export class BookingField_2024_04_15 { export class BookingField_2024_04_15 {
@IsEnum(BaseField) @IsEnum(BaseField)
@ApiProperty({ enum: BaseField })
type!: BaseField; type!: BaseField;
@IsString() @IsString()
@ApiProperty()
name!: string; name!: string;
@IsOptional() @IsOptional()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => Option) @Type(() => Option)
@ApiPropertyOptional({ type: [Option] })
options?: Option[]; options?: Option[];
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
label?: string; label?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
labelAsSafeHtml?: string; labelAsSafeHtml?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
defaultLabel?: string; defaultLabel?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
placeholder?: string; placeholder?: string;
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiPropertyOptional()
required?: boolean; required?: boolean;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
getOptionsAt?: string; getOptionsAt?: string;
@IsOptional() @IsOptional()
@IsArray() @IsArray()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => OptionsInput) @Type(() => OptionsInput)
@ApiPropertyOptional()
optionsInputs?: Record<string, OptionsInput>; optionsInputs?: Record<string, OptionsInput>;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
variant?: string; variant?: string;
@IsOptional() @IsOptional()
@ValidateNested() @ValidateNested()
@Type(() => VariantsConfig) @Type(() => VariantsConfig)
@ApiPropertyOptional({ type: VariantsConfig })
variantsConfig?: VariantsConfig; variantsConfig?: VariantsConfig;
@IsOptional() @IsOptional()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => View) @Type(() => View)
@ApiPropertyOptional({ type: [View] })
views?: View[]; views?: View[];
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiPropertyOptional()
hideWhenJustOneOption?: boolean; hideWhenJustOneOption?: boolean;
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiPropertyOptional()
hidden?: boolean; hidden?: boolean;
@IsOptional() @IsOptional()
@IsEnum(Editable) @IsEnum(Editable)
@ApiPropertyOptional()
editable?: Editable; editable?: Editable;
@IsOptional() @IsOptional()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => Source) @Type(() => Source)
@ApiPropertyOptional({ type: [Source] })
sources?: Source[]; sources?: Source[];
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiPropertyOptional()
disableOnPrefill?: boolean; disableOnPrefill?: boolean;
} }
export class RecurringEvent_2024_04_15 { export class RecurringEvent_2024_04_15 {
@IsDate() @IsDate()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: Date })
dtstart?: Date; dtstart?: Date;
@IsInt() @IsInt()
@ApiProperty()
interval!: number; interval!: number;
@IsInt() @IsInt()
@ApiProperty()
count!: number; count!: number;
@IsEnum(Frequency) @IsEnum(Frequency)
@ApiProperty({ enum: Frequency })
freq!: Frequency; freq!: Frequency;
@IsDate() @IsDate()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: Date })
until?: Date; until?: Date;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
tzid?: string; tzid?: string;
} }
export class IntervalLimits_2024_04_15 { export class IntervalLimits_2024_04_15 {
@IsNumber() @IsNumber()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
PER_DAY?: number; PER_DAY?: number;
@IsNumber() @IsNumber()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
PER_WEEK?: number; PER_WEEK?: number;
@IsNumber() @IsNumber()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
PER_MONTH?: number; PER_MONTH?: number;
@IsNumber() @IsNumber()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
PER_YEAR?: number; PER_YEAR?: number;
} }
@@ -228,27 +282,33 @@ export class UpdateEventTypeInput_2024_04_15 {
@IsInt() @IsInt()
@Min(1) @Min(1)
@IsOptional() @IsOptional()
@ApiPropertyOptional()
length?: number; length?: number;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
slug?: string; slug?: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
title?: string; title?: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
description?: string; description?: string;
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
hidden?: boolean; hidden?: boolean;
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => EventTypeLocation_2024_04_15) @Type(() => EventTypeLocation_2024_04_15)
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: [EventTypeLocation_2024_04_15] })
locations?: EventTypeLocation_2024_04_15[]; locations?: EventTypeLocation_2024_04_15[];
// @IsInt() // @IsInt()
@@ -283,6 +343,7 @@ export class UpdateEventTypeInput_2024_04_15 {
@IsArray() @IsArray()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => BookingField_2024_04_15) @Type(() => BookingField_2024_04_15)
@ApiPropertyOptional({ type: [BookingField_2024_04_15] })
bookingFields?: BookingField_2024_04_15[]; bookingFields?: BookingField_2024_04_15[];
// @IsString() // @IsString()
@@ -328,6 +389,7 @@ export class UpdateEventTypeInput_2024_04_15 {
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
disableGuests?: boolean; disableGuests?: boolean;
// @IsBoolean() // @IsBoolean()
@@ -337,16 +399,19 @@ export class UpdateEventTypeInput_2024_04_15 {
@IsInt() @IsInt()
@Min(0) @Min(0)
@IsOptional() @IsOptional()
@ApiPropertyOptional()
minimumBookingNotice?: number; minimumBookingNotice?: number;
@IsInt() @IsInt()
@Min(0) @Min(0)
@IsOptional() @IsOptional()
@ApiPropertyOptional()
beforeEventBuffer?: number; beforeEventBuffer?: number;
@IsInt() @IsInt()
@Min(0) @Min(0)
@IsOptional() @IsOptional()
@ApiPropertyOptional()
afterEventBuffer?: number; afterEventBuffer?: number;
// @IsInt() // @IsInt()
@@ -384,6 +449,7 @@ export class UpdateEventTypeInput_2024_04_15 {
@IsInt() @IsInt()
@Min(0) @Min(0)
@IsOptional() @IsOptional()
@ApiPropertyOptional()
slotInterval?: number; slotInterval?: number;
// @IsString() // @IsString()
@@ -12,7 +12,7 @@ import {
IntervalLimits_2024_04_15, IntervalLimits_2024_04_15,
RecurringEvent_2024_04_15, RecurringEvent_2024_04_15,
} from "@/ee/event-types/event-types_2024_04_15/inputs/update-event-type.input"; } from "@/ee/event-types/event-types_2024_04_15/inputs/update-event-type.input";
import { ApiProperty as DocsProperty, ApiHideProperty } from "@nestjs/swagger"; import { ApiHideProperty, ApiProperty } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { import {
IsArray, IsArray,
@@ -29,23 +29,23 @@ import {
export class EventTypeOutput { export class EventTypeOutput {
@IsInt() @IsInt()
@DocsProperty({ example: 1 }) @ApiProperty({ example: 1 })
id!: number; id!: number;
@IsInt() @IsInt()
@DocsProperty({ example: CREATE_EVENT_LENGTH_EXAMPLE }) @ApiProperty({ example: CREATE_EVENT_LENGTH_EXAMPLE })
length!: number; length!: number;
@IsString() @IsString()
@DocsProperty({ example: CREATE_EVENT_SLUG_EXAMPLE }) @ApiProperty({ example: CREATE_EVENT_SLUG_EXAMPLE })
slug!: string; slug!: string;
@IsString() @IsString()
@DocsProperty({ example: CREATE_EVENT_TITLE_EXAMPLE }) @ApiProperty({ example: CREATE_EVENT_TITLE_EXAMPLE })
title!: string; title!: string;
@IsString() @IsString()
@DocsProperty({ example: CREATE_EVENT_DESCRIPTION_EXAMPLE }) @ApiProperty({ example: CREATE_EVENT_DESCRIPTION_EXAMPLE, nullable: true })
description!: string | null; description!: string | null;
@IsBoolean() @IsBoolean()
@@ -55,6 +55,7 @@ export class EventTypeOutput {
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => EventTypeLocation_2024_04_15) @Type(() => EventTypeLocation_2024_04_15)
@IsArray() @IsArray()
@ApiProperty({ type: [EventTypeLocation_2024_04_15], nullable: true })
locations!: EventTypeLocation_2024_04_15[] | null; locations!: EventTypeLocation_2024_04_15[] | null;
@IsInt() @IsInt()
@@ -1,4 +1,4 @@
import { ApiProperty } from "@nestjs/swagger"; import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { import {
IsBoolean, IsBoolean,
@@ -17,330 +17,421 @@ import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
class Location { class Location {
@IsString() @IsString()
@ApiProperty()
type!: string; type!: string;
} }
class Source { class Source {
@IsString() @IsString()
@ApiProperty()
id!: string; id!: string;
@IsString() @IsString()
@ApiProperty()
type!: string; type!: string;
@IsString() @IsString()
@ApiProperty()
label!: string; label!: string;
} }
class OptionInput { class OptionInput {
@IsString() @IsString()
@ApiProperty()
type!: string; type!: string;
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
required?: boolean; required?: boolean;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
placeholder?: string; placeholder?: string;
} }
class BookingField { class BookingField {
@IsString() @IsString()
@ApiProperty()
name!: string; name!: string;
@IsString() @IsString()
@ApiProperty()
type!: string; type!: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
defaultLabel?: string; defaultLabel?: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
label?: string; label?: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
placeholder?: string; placeholder?: string;
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
required?: boolean; required?: boolean;
@IsOptional() @IsOptional()
@ApiPropertyOptional()
getOptionsAt?: string; getOptionsAt?: string;
@IsObject() @IsObject()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
optionsInputs?: { [key: string]: OptionInput }; optionsInputs?: { [key: string]: OptionInput };
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
hideWhenJustOneOption?: boolean; hideWhenJustOneOption?: boolean;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
editable?: string; editable?: string;
@IsArray() @IsArray()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => Source) @Type(() => Source)
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: [Source] })
sources?: Source[]; sources?: Source[];
@IsOptional() @IsOptional()
@IsBoolean() @IsBoolean()
@ApiPropertyOptional()
disableOnPrefill?: boolean; disableOnPrefill?: boolean;
} }
class Organization { class Organization {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: String, nullable: true })
slug?: string | null; slug?: string | null;
@IsString() @IsString()
@ApiProperty()
name!: string; name!: string;
@IsOptional() @IsOptional()
metadata!: Record<string, any>; @ApiPropertyOptional({ type: Object })
metadata?: Record<string, any>;
} }
class Profile { class Profile {
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
username!: string | null; username!: string | null;
@IsInt() @IsInt()
@ApiProperty({ type: Number, nullable: true })
id!: number | null; id!: number | null;
@IsInt() @IsInt()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
userId?: number; userId?: number;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
uid?: string; uid?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
name?: string; name?: string;
@IsInt() @IsInt()
@ApiProperty({ type: Number, nullable: true })
organizationId!: number | null; organizationId!: number | null;
@ValidateNested() @ValidateNested()
@Type(() => Organization) @Type(() => Organization)
@ApiPropertyOptional({ type: Organization, nullable: true })
organization?: Organization | null; organization?: Organization | null;
@IsString() @IsString()
@ApiProperty()
upId!: string; upId!: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
image?: string; image?: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
brandColor?: string; brandColor?: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
darkBrandColor?: string; darkBrandColor?: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional()
theme?: string; theme?: string;
@IsOptional() @IsOptional()
@ApiPropertyOptional()
bookerLayouts?: any; bookerLayouts?: any;
} }
class Owner { class Owner {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: String, nullable: true })
avatarUrl?: string | null; avatarUrl?: string | null;
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
username!: string | null; username!: string | null;
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
name!: string | null; name!: string | null;
@IsString() @IsString()
@ApiProperty()
weekStart!: string; weekStart!: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: String, nullable: true })
brandColor?: string | null; brandColor?: string | null;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: String, nullable: true })
darkBrandColor?: string | null; darkBrandColor?: string | null;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: String, nullable: true })
theme?: string | null; theme?: string | null;
@IsOptional() @IsOptional()
metadata!: any; @ApiPropertyOptional()
metadata?: any;
@IsInt() @IsInt()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: Number, nullable: true })
defaultScheduleId?: number | null; defaultScheduleId?: number | null;
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
nonProfileUsername!: string | null; nonProfileUsername!: string | null;
@ValidateNested() @ValidateNested()
@Type(() => Profile) @Type(() => Profile)
@ApiProperty({ type: Profile })
profile!: Profile; profile!: Profile;
} }
class User { class User {
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
username!: string | null; username!: string | null;
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
name!: string | null; name!: string | null;
@IsString() @IsString()
@ApiProperty()
weekStart!: string; weekStart!: string;
@IsInt() @IsInt()
@IsOptional()
@ApiPropertyOptional()
organizationId?: number; organizationId?: number;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: String, nullable: true })
avatarUrl?: string | null; avatarUrl?: string | null;
@ValidateNested() @ValidateNested()
@ApiProperty({ type: Profile })
profile!: Profile; profile!: Profile;
@IsString() @IsString()
@ApiProperty()
bookerUrl!: string; bookerUrl!: string;
} }
class Schedule { class Schedule {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsString() @IsString()
@ApiProperty({ type: String, nullable: true })
timeZone!: string | null; timeZone!: string | null;
} }
class PublicEventTypeOutput { class PublicEventTypeOutput {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsString() @IsString()
@ApiProperty()
title!: string; title!: string;
@IsString() @IsString()
@ApiProperty()
description!: string; description!: string;
@IsString() @IsString()
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: String, nullable: true })
eventName?: string | null; eventName?: string | null;
@IsString() @IsString()
@ApiProperty()
slug!: string; slug!: string;
@IsBoolean() @IsBoolean()
@ApiProperty()
isInstantEvent!: boolean; isInstantEvent!: boolean;
@IsOptional() @IsOptional()
@ApiPropertyOptional()
aiPhoneCallConfig?: any; aiPhoneCallConfig?: any;
@IsOptional() @IsOptional()
@ApiPropertyOptional()
schedulingType?: any; schedulingType?: any;
@IsInt() @IsInt()
@ApiProperty()
length!: number; length!: number;
@IsArray() @IsArray()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => Location) @Type(() => Location)
@ApiProperty({ type: [Location] })
locations!: Location[]; locations!: Location[];
@IsArray() @IsArray()
@ApiProperty({ type: [Object] })
customInputs!: any[]; customInputs!: any[];
@IsBoolean() @IsBoolean()
@ApiProperty()
disableGuests!: boolean; disableGuests!: boolean;
@IsObject() @IsObject()
@ApiProperty({ type: Object, nullable: true })
metadata!: object | null; metadata!: object | null;
@IsBoolean() @IsBoolean()
@ApiProperty()
lockTimeZoneToggleOnBookingPage!: boolean; lockTimeZoneToggleOnBookingPage!: boolean;
@IsBoolean() @IsBoolean()
@ApiProperty()
requiresConfirmation!: boolean; requiresConfirmation!: boolean;
@IsBoolean() @IsBoolean()
@ApiProperty()
requiresBookerEmailVerification!: boolean; requiresBookerEmailVerification!: boolean;
@IsOptional() @IsOptional()
@ApiPropertyOptional()
recurringEvent?: any; recurringEvent?: any;
@IsNumber() @IsNumber()
@ApiProperty()
price!: number; price!: number;
@IsString() @IsString()
@ApiProperty()
currency!: string; currency!: string;
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: Number, nullable: true })
seatsPerTimeSlot?: number | null; seatsPerTimeSlot?: number | null;
@IsBoolean() @IsBoolean()
@ApiProperty({ type: Boolean, nullable: true })
seatsShowAvailabilityCount!: boolean | null; seatsShowAvailabilityCount!: boolean | null;
@IsArray() @IsArray()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => BookingField) @Type(() => BookingField)
@ApiProperty({ type: [BookingField] })
bookingFields!: BookingField[]; bookingFields!: BookingField[];
@IsOptional() @IsOptional()
@ApiPropertyOptional()
team?: any; team?: any;
@IsOptional() @IsOptional()
@IsUrl() @IsUrl()
@ApiPropertyOptional({ type: String, nullable: true })
successRedirectUrl?: string | null; successRedirectUrl?: string | null;
@IsArray() @IsArray()
@ApiProperty()
workflows!: any[]; workflows!: any[];
@IsArray() @IsArray()
@ApiProperty()
hosts!: any[]; hosts!: any[];
@ValidateNested() @ValidateNested()
@Type(() => Owner) @Type(() => Owner)
@ApiProperty({ type: Owner, nullable: true })
owner!: Owner | null; owner!: Owner | null;
@ValidateNested() @ValidateNested()
@Type(() => Schedule) @Type(() => Schedule)
@ApiProperty({ type: Schedule, nullable: true })
schedule!: Schedule | null; schedule!: Schedule | null;
@IsBoolean() @IsBoolean()
@ApiProperty()
hidden!: boolean; hidden!: boolean;
@IsBoolean() @IsBoolean()
@ApiProperty()
assignAllTeamMembers!: boolean; assignAllTeamMembers!: boolean;
@IsOptional() @IsOptional()
@ApiPropertyOptional()
bookerLayouts?: any; bookerLayouts?: any;
@IsArray() @IsArray()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => User) @Type(() => User)
@ApiProperty({ type: [User] })
users!: User[]; users!: User[];
@IsObject() @IsObject()
@ApiProperty({ type: Object })
entity!: object; entity!: object;
@IsBoolean() @IsBoolean()
@@ -10,10 +10,12 @@ import { UsersModule } from "@/modules/users/users.module";
import { INestApplication } from "@nestjs/common"; import { INestApplication } from "@nestjs/common";
import { NestExpressApplication } from "@nestjs/platform-express"; import { NestExpressApplication } from "@nestjs/platform-express";
import { Test } from "@nestjs/testing"; import { Test } from "@nestjs/testing";
import { PlatformOAuthClient, Team, User, Schedule } from "@prisma/client"; import { PlatformOAuthClient, Team, User, Schedule, EventType } from "@prisma/client";
import * as request from "supertest"; import * as request from "supertest";
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture"; import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture";
import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture"; import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
import { ProfileRepositoryFixture } from "test/fixtures/repository/profiles.repository.fixture";
import { SchedulesRepositoryFixture } from "test/fixtures/repository/schedules.repository.fixture"; import { SchedulesRepositoryFixture } from "test/fixtures/repository/schedules.repository.fixture";
import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture"; import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture"; import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
@@ -75,18 +77,22 @@ describe("Event types Endpoints", () => {
let teamRepositoryFixture: TeamRepositoryFixture; let teamRepositoryFixture: TeamRepositoryFixture;
let eventTypesRepositoryFixture: EventTypesRepositoryFixture; let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
let schedulesRepostoryFixture: SchedulesRepositoryFixture; let schedulesRepostoryFixture: SchedulesRepositoryFixture;
let profileRepositoryFixture: ProfileRepositoryFixture;
let membershipsRepositoryFixture: MembershipRepositoryFixture;
const userEmail = "event-types-test-e2e@api.com"; const userEmail = "event-types-test-e2e@api.com";
const falseTestEmail = "false-event-types@api.com"; const falseTestEmail = "false-event-types@api.com";
const name = "bob-the-builder"; const name = "bob-the-builder";
const username = name; const username = name;
let eventType: EventTypeOutput_2024_06_14; let eventType: EventTypeOutput_2024_06_14;
let user: User; let user: User;
let orgUser: User;
let falseTestUser: User; let falseTestUser: User;
let firstSchedule: Schedule; let firstSchedule: Schedule;
let secondSchedule: Schedule; let secondSchedule: Schedule;
let falseTestSchedule: Schedule; let falseTestSchedule: Schedule;
let orgUserEventType1: EventType;
let orgUserEventType2: EventType;
let orgUserEventType3: EventType;
beforeAll(async () => { beforeAll(async () => {
const moduleRef = await withApiAuth( const moduleRef = await withApiAuth(
userEmail, userEmail,
@@ -109,8 +115,12 @@ describe("Event types Endpoints", () => {
teamRepositoryFixture = new TeamRepositoryFixture(moduleRef); teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef); eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef);
schedulesRepostoryFixture = new SchedulesRepositoryFixture(moduleRef); schedulesRepostoryFixture = new SchedulesRepositoryFixture(moduleRef);
profileRepositoryFixture = new ProfileRepositoryFixture(moduleRef);
organization = await teamRepositoryFixture.create({ name: "organization" }); membershipsRepositoryFixture = new MembershipRepositoryFixture(moduleRef);
organization = await teamRepositoryFixture.create({
name: "organization",
slug: "event-type-2024-06-14-org-slug",
});
oAuthClient = await createOAuthClient(organization.id); oAuthClient = await createOAuthClient(organization.id);
user = await userRepositoryFixture.create({ user = await userRepositoryFixture.create({
email: userEmail, email: userEmail,
@@ -118,6 +128,49 @@ describe("Event types Endpoints", () => {
username, username,
}); });
orgUser = await userRepositoryFixture.create({
email: "event-types-2024-06-14-org-user@example.com",
name: "event-types-2024-06-14-org-user",
username: "event-types-2024-06-14-org-user",
});
profileRepositoryFixture.create({
uid: `usr-${orgUser.id}`,
username: orgUser.username as string,
organization: {
connect: {
id: organization.id,
},
},
user: {
connect: {
id: orgUser.id,
},
},
});
orgUserEventType1 = await eventTypesRepositoryFixture.create(
{ title: "orgUserEventType1", slug: "org-event-type-1", length: 60, locations: [] },
orgUser.id
);
orgUserEventType2 = await eventTypesRepositoryFixture.create(
{ title: "orgUserEventType2", slug: "org-event-type-2", length: 60, locations: [] },
orgUser.id
);
orgUserEventType3 = await eventTypesRepositoryFixture.create(
{ title: "orgUserEventType3", slug: "org-event-type-3", length: 60, locations: [] },
orgUser.id
);
await membershipsRepositoryFixture.create({
role: "MEMBER",
user: { connect: { id: orgUser.id } },
team: { connect: { id: organization.id } },
accepted: true,
});
falseTestUser = await userRepositoryFixture.create({ falseTestUser = await userRepositoryFixture.create({
email: falseTestEmail, email: falseTestEmail,
name: "false-test", name: "false-test",
@@ -342,7 +395,7 @@ describe("Event types Endpoints", () => {
}); });
}); });
it(`/GET/even-types by username`, async () => { it(`/GET/event-types by username`, async () => {
const response = await request(app.getHttpServer()) const response = await request(app.getHttpServer())
.get(`/api/v2/event-types?username=${username}`) .get(`/api/v2/event-types?username=${username}`)
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14) .set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
@@ -386,6 +439,61 @@ describe("Event types Endpoints", () => {
expect(fetchedEventType.color).toEqual(eventType.color); expect(fetchedEventType.color).toEqual(eventType.color);
}); });
it(`/GET/event-types by username and orgSlug`, async () => {
console.log(organization);
const response = await request(app.getHttpServer())
.get(`/api/v2/event-types?username=${orgUser.username}&orgSlug=${organization.slug}`)
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
.set("Authorization", `Bearer whatever`)
.expect(200);
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseBody.data?.length).toEqual(3);
expect(responseBody.data?.find((e) => e.id === orgUserEventType1.id)?.id).toBeDefined();
expect(responseBody.data?.find((e) => e.id === orgUserEventType2.id)?.id).toBeDefined();
expect(responseBody.data?.find((e) => e.id === orgUserEventType3.id)?.id).toBeDefined();
});
it(`/GET/event-types by username and orgSlug and eventSlug`, async () => {
const response = await request(app.getHttpServer())
.get(
`/api/v2/event-types?username=${orgUser.username}&orgSlug=${organization.slug}&eventSlug=${orgUserEventType1.slug}`
)
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
.set("Authorization", `Bearer whatever`)
.expect(200);
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseBody.data?.length).toEqual(1);
expect(responseBody.data?.find((e) => e.id === orgUserEventType1.id)?.id).toBeDefined();
});
it(`/GET/event-types by username and orgId`, async () => {
const response = await request(app.getHttpServer())
.get(`/api/v2/event-types?username=${orgUser.username}&orgId=${organization.id}`)
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
// note: bearer token value mocked using "withAccessTokenAuth" for user which id is used when creating event type above
.set("Authorization", `Bearer whatever`)
.expect(200);
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14[]> = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data).toBeDefined();
expect(responseBody.data?.length).toEqual(3);
expect(responseBody.data?.find((e) => e.id === orgUserEventType1.id)?.id).toBeDefined();
expect(responseBody.data?.find((e) => e.id === orgUserEventType2.id)?.id).toBeDefined();
expect(responseBody.data?.find((e) => e.id === orgUserEventType3.id)?.id).toBeDefined();
});
it("should return an error when creating an event type with seats enabled and multiple locations", async () => { it("should return an error when creating an event type with seats enabled and multiple locations", async () => {
const body: CreateEventTypeInput_2024_06_14 = { const body: CreateEventTypeInput_2024_06_14 = {
title: "Coding class 2", title: "Coding class 2",
@@ -919,6 +1027,12 @@ describe("Event types Endpoints", () => {
} catch (e) { } catch (e) {
// User might have been deleted by the test // User might have been deleted by the test
} }
try {
await userRepositoryFixture.delete(orgUser.id);
} catch (e) {
// User might have been deleted by the test
}
await app.close(); await app.close();
}); });
}); });
@@ -1321,6 +1435,73 @@ describe("Event types Endpoints", () => {
}); });
}); });
it("should return event type with default bookingFields if they are not defined", async () => {
const eventTypeInput = {
title: "undefined booking fields",
description: "undefined booking fields",
length: 40,
hidden: false,
slug: "undefined-booking-fields",
locations: [],
schedulingType: SchedulingType.ROUND_ROBIN,
};
const eventType = await eventTypesRepositoryFixture.create(eventTypeInput, user.id);
return request(app.getHttpServer())
.get(`/api/v2/event-types/${eventType.id}`)
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
.expect(200)
.then(async (response) => {
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14> = response.body;
const fetchedEventType = responseBody.data;
expect(fetchedEventType.bookingFields).toEqual([
{
isDefault: true,
type: "name",
slug: "name",
required: true,
},
{
isDefault: true,
type: "email",
slug: "email",
required: true,
},
{
isDefault: true,
type: "radioInput",
slug: "location",
required: false,
},
{
isDefault: true,
type: "text",
slug: "title",
required: true,
},
{
isDefault: true,
type: "textarea",
slug: "notes",
required: false,
},
{
isDefault: true,
type: "multiemail",
slug: "guests",
required: false,
},
{
isDefault: true,
type: "textarea",
slug: "rescheduleReason",
required: false,
},
]);
});
});
afterAll(async () => { afterAll(async () => {
await oauthClientRepositoryFixture.delete(oAuthClient.id); await oauthClientRepositoryFixture.delete(oAuthClient.id);
await teamRepositoryFixture.delete(organization.id); await teamRepositoryFixture.delete(organization.id);
@@ -1,4 +1,4 @@
import { ApiProperty as DocsProperty } from "@nestjs/swagger"; import { ApiProperty as DocsProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Transform } from "class-transformer"; import { Transform } from "class-transformer";
import { IsString, IsBoolean, IsOptional, IsEnum, Matches } from "class-validator"; import { IsString, IsBoolean, IsOptional, IsEnum, Matches } from "class-validator";
@@ -38,34 +38,34 @@ export class CreatePhoneCallInput {
@IsOptional() @IsOptional()
@IsString() @IsString()
@DocsProperty({ description: "Scheduler name" }) @ApiPropertyOptional({ description: "Scheduler name" })
schedulerName?: string; schedulerName?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@Transform(({ value }) => (value ? value : undefined)) @Transform(({ value }) => (value ? value : undefined))
@DocsProperty({ description: "Guest name" }) @ApiPropertyOptional({ description: "Guest name" })
guestName?: string; guestName?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@Transform(({ value }) => (value ? value : undefined)) @Transform(({ value }) => (value ? value : undefined))
@DocsProperty({ description: "Guest email" }) @ApiPropertyOptional({ description: "Guest email" })
guestEmail?: string; guestEmail?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@Transform(({ value }) => (value ? value : undefined)) @Transform(({ value }) => (value ? value : undefined))
@DocsProperty({ description: "Guest company" }) @ApiPropertyOptional({ description: "Guest company" })
guestCompany?: string; guestCompany?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@DocsProperty({ description: "Begin message" }) @ApiPropertyOptional({ description: "Begin message" })
beginMessage?: string; beginMessage?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@DocsProperty({ description: "General prompt" }) @ApiPropertyOptional({ description: "General prompt" })
generalPrompt?: string; generalPrompt?: string;
} }
@@ -17,7 +17,7 @@ export class EventTypeResponseTransformPipe implements PipeTransform {
private transformEventType(eventType: EventTypeResponse): EventTypeOutput_2024_06_14 { private transformEventType(eventType: EventTypeResponse): EventTypeOutput_2024_06_14 {
return plainToClass( return plainToClass(
EventTypeOutput_2024_06_14, EventTypeOutput_2024_06_14,
this.outputEventTypesService.getResponseEventType(eventType.ownerId, eventType), this.outputEventTypesService.getResponseEventType(eventType.ownerId, eventType, false),
{ strategy: "exposeAll" } { strategy: "exposeAll" }
); );
} }
@@ -74,8 +74,13 @@ export class EventTypesService_2024_06_14 {
await this.checkUserOwnsSchedule(userId, body.scheduleId); await this.checkUserOwnsSchedule(userId, body.scheduleId);
} }
async getEventTypeByUsernameAndSlug(username: string, eventTypeSlug: string) { async getEventTypeByUsernameAndSlug(
const user = await this.usersRepository.findByUsername(username); username: string,
eventTypeSlug: string,
orgSlug?: string,
orgId?: number
) {
const user = await this.usersRepository.findByUsername(username, orgSlug, orgId);
if (!user) { if (!user) {
return null; return null;
} }
@@ -92,8 +97,8 @@ export class EventTypesService_2024_06_14 {
}; };
} }
async getEventTypesByUsername(username: string) { async getEventTypesByUsername(username: string, orgSlug?: string, orgId?: number) {
const user = await this.usersRepository.findByUsername(username); const user = await this.usersRepository.findByUsername(username, orgSlug, orgId);
if (!user) { if (!user) {
return []; return [];
} }
@@ -152,27 +157,26 @@ export class EventTypesService_2024_06_14 {
} }
async getEventTypes(queryParams: GetEventTypesQuery_2024_06_14) { async getEventTypes(queryParams: GetEventTypesQuery_2024_06_14) {
const { username, eventSlug, usernames } = queryParams; const { username, eventSlug, usernames, orgSlug, orgId } = queryParams;
if (username && eventSlug) { if (username && eventSlug) {
const eventType = await this.getEventTypeByUsernameAndSlug(username, eventSlug); const eventType = await this.getEventTypeByUsernameAndSlug(username, eventSlug, orgSlug, orgId);
return eventType ? [eventType] : []; return eventType ? [eventType] : [];
} }
if (username) { if (username) {
return await this.getEventTypesByUsername(username); return await this.getEventTypesByUsername(username, orgSlug, orgId);
} }
if (usernames) { if (usernames) {
const dynamicEventType = await this.getDynamicEventType(usernames); const dynamicEventType = await this.getDynamicEventType(usernames, orgSlug, orgId);
return [dynamicEventType]; return [dynamicEventType];
} }
return []; return [];
} }
async getDynamicEventType(usernames: string[]) { async getDynamicEventType(usernames: string[], orgSlug?: string, orgId?: number) {
const users = await this.usersService.getByUsernames(usernames); const users = await this.usersService.getByUsernames(usernames, orgSlug, orgId);
const usersFiltered: UserWithProfile[] = []; const usersFiltered: UserWithProfile[] = [];
for (const user of users) { for (const user of users) {
if (user) { if (user) {
@@ -21,6 +21,7 @@ import {
transformSeatsInternalToApi, transformSeatsInternalToApi,
InternalLocation, InternalLocation,
BookingFieldSchema, BookingFieldSchema,
getBookingFieldsWithSystemFields,
} from "@calcom/platform-libraries"; } from "@calcom/platform-libraries";
import { import {
TransformFutureBookingsLimitSchema_2024_06_14, TransformFutureBookingsLimitSchema_2024_06_14,
@@ -88,7 +89,11 @@ type Input = Pick<
@Injectable() @Injectable()
export class OutputEventTypesService_2024_06_14 { export class OutputEventTypesService_2024_06_14 {
getResponseEventType(ownerId: number, databaseEventType: Input): EventTypeOutput_2024_06_14 { getResponseEventType(
ownerId: number,
databaseEventType: Input,
isOrgTeamEvent: boolean
): EventTypeOutput_2024_06_14 {
const { const {
id, id,
length, length,
@@ -122,7 +127,7 @@ export class OutputEventTypesService_2024_06_14 {
const customName = databaseEventType?.eventName ?? undefined; const customName = databaseEventType?.eventName ?? undefined;
const bookingFields = databaseEventType.bookingFields const bookingFields = databaseEventType.bookingFields
? this.transformBookingFields(databaseEventType.bookingFields) ? this.transformBookingFields(databaseEventType.bookingFields)
: []; : this.getDefaultBookingFields(isOrgTeamEvent);
const recurrence = this.transformRecurringEvent(databaseEventType.recurringEvent); const recurrence = this.transformRecurringEvent(databaseEventType.recurringEvent);
const metadata = this.transformMetadata(databaseEventType.metadata) || {}; const metadata = this.transformMetadata(databaseEventType.metadata) || {};
const users = this.transformUsers(databaseEventType.users || []); const users = this.transformUsers(databaseEventType.users || []);
@@ -240,6 +245,18 @@ export class OutputEventTypesService_2024_06_14 {
return [...transformBookingFieldsInternalToApi(knownBookingFields), ...unknownBookingFields]; return [...transformBookingFieldsInternalToApi(knownBookingFields), ...unknownBookingFields];
} }
getDefaultBookingFields(isOrgTeamEvent: boolean) {
const defaultBookingFields = getBookingFieldsWithSystemFields({
disableGuests: false,
bookingFields: null,
customInputs: [],
metadata: null,
workflows: [],
isOrgTeamEvent,
});
return this.transformBookingFields(defaultBookingFields);
}
transformRecurringEvent(recurringEvent: any) { transformRecurringEvent(recurringEvent: any) {
if (!recurringEvent) return null; if (!recurringEvent) return null;
const recurringEventParsed = parseRecurringEvent(recurringEvent); const recurringEventParsed = parseRecurringEvent(recurringEvent);
@@ -95,7 +95,7 @@ describe("Platform Gcal Endpoints", () => {
.expect(401); .expect(401);
}); });
it(`/GET/gcal/oauth/auth-url: it should auth-url to google oauth with valid access token `, async () => { it(`/GET/gcal/oauth/auth-url: it should auth-url to google OAuth with valid access token `, async () => {
const response = await request(app.getHttpServer()) const response = await request(app.getHttpServer())
.get(`/v2/gcal/oauth/auth-url`) .get(`/v2/gcal/oauth/auth-url`)
.set("Authorization", `Bearer ${accessTokenSecret}`) .set("Authorization", `Bearer ${accessTokenSecret}`)
@@ -105,7 +105,7 @@ describe("Platform Gcal Endpoints", () => {
expect(data.authUrl).toBeDefined(); expect(data.authUrl).toBeDefined();
}); });
it(`/GET/gcal/oauth/save: without oauth code`, async () => { it(`/GET/gcal/oauth/save: without OAuth code`, async () => {
await request(app.getHttpServer()) await request(app.getHttpServer())
.get( .get(
`/v2/gcal/oauth/save?state=accessToken=${accessTokenSecret}&origin%3D${CLIENT_REDIRECT_URI}&scope=https://www.googleapis.com/auth/calendar.readonly%20https://www.googleapis.com/auth/calendar.events` `/v2/gcal/oauth/save?state=accessToken=${accessTokenSecret}&origin%3D${CLIENT_REDIRECT_URI}&scope=https://www.googleapis.com/auth/calendar.readonly%20https://www.googleapis.com/auth/calendar.events`
+1 -4
View File
@@ -27,10 +27,7 @@ import {
} from "@nestjs/common"; } from "@nestjs/common";
import { ConfigService } from "@nestjs/config"; import { ConfigService } from "@nestjs/config";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger"; import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { Prisma } from "@prisma/client";
import { Request } from "express"; import { Request } from "express";
import { google } from "googleapis";
import { z } from "zod";
import { APPS_READ, GOOGLE_CALENDAR_TYPE, SUCCESS_STATUS } from "@calcom/platform-constants"; import { APPS_READ, GOOGLE_CALENDAR_TYPE, SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -103,7 +100,7 @@ export class GcalController {
} }
if (gcalCredentials.invalid) { if (gcalCredentials.invalid) {
throw new BadRequestException("Invalid google oauth credentials."); throw new BadRequestException("Invalid google OAuth credentials.");
} }
const { connectedCalendars } = await this.calendarsService.getCalendars(userId); const { connectedCalendars } = await this.calendarsService.getCalendars(userId);
+14 -2
View File
@@ -1,39 +1,51 @@
import { ApiProperty as DocsProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { IsInt, IsEmail, IsOptional, IsString, ValidateNested } from "class-validator"; import { IsString, IsOptional, IsInt, IsEmail, ValidateNested } from "class-validator";
export class MeOrgOutput { export class MeOrgOutput {
@DocsProperty()
isPlatform!: boolean; isPlatform!: boolean;
@DocsProperty()
id!: number; id!: number;
} }
export class MeOutput { export class MeOutput {
@IsInt() @IsInt()
@DocsProperty()
id!: number; id!: number;
@IsString() @IsString()
@DocsProperty()
username!: string; username!: string;
@IsEmail() @IsEmail()
@DocsProperty()
email!: string; email!: string;
@IsInt() @IsInt()
@DocsProperty()
timeFormat!: number; timeFormat!: number;
@IsInt() @IsInt()
@IsOptional() @DocsProperty({ type: Number, nullable: true })
defaultScheduleId!: number | null; defaultScheduleId!: number | null;
@IsString() @IsString()
@DocsProperty()
weekStart!: string; weekStart!: string;
@IsString() @IsString()
@DocsProperty()
timeZone!: string; timeZone!: string;
@IsInt() @IsInt()
@DocsProperty({ type: Number, nullable: true })
organizationId!: number | null; organizationId!: number | null;
@IsOptional() @IsOptional()
@ValidateNested() @ValidateNested()
@Type(() => MeOrgOutput) @Type(() => MeOrgOutput)
@ApiPropertyOptional({ type: MeOrgOutput })
organization?: MeOrgOutput; organization?: MeOrgOutput;
} }
@@ -9,6 +9,8 @@ import { ProviderModule } from "@/ee/provider/provider.module";
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module"; import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
import { SchedulesModule_2024_06_11 } from "@/ee/schedules/schedules_2024_06_11/schedules.module"; import { SchedulesModule_2024_06_11 } from "@/ee/schedules/schedules_2024_06_11/schedules.module";
import { SlotsModule } from "@/modules/slots/slots.module"; import { SlotsModule } from "@/modules/slots/slots.module";
import { TeamsModule } from "@/modules/teams/teams/teams.module";
import { TeamsMembershipsModule } from "@/modules/teams/memberships/teams-memberships.module";
import type { MiddlewareConsumer, NestModule } from "@nestjs/common"; import type { MiddlewareConsumer, NestModule } from "@nestjs/common";
import { Module } from "@nestjs/common"; import { Module } from "@nestjs/common";
@@ -24,7 +26,9 @@ import { Module } from "@nestjs/common";
CalendarsModule, CalendarsModule,
BookingsModule_2024_04_15, BookingsModule_2024_04_15,
BookingsModule_2024_08_13, BookingsModule_2024_08_13,
TeamsMembershipsModule,
SlotsModule, SlotsModule,
TeamsModule,
], ],
}) })
export class PlatformEndpointsModule implements NestModule { export class PlatformEndpointsModule implements NestModule {
@@ -1,20 +1,25 @@
import { CreateAvailabilityInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-availability.input"; import { CreateAvailabilityInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-availability.input";
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { IsArray, IsBoolean, IsTimeZone, IsOptional, IsString, ValidateNested } from "class-validator"; import { IsArray, IsBoolean, IsTimeZone, IsOptional, IsString, ValidateNested } from "class-validator";
export class CreateScheduleInput_2024_04_15 { export class CreateScheduleInput_2024_04_15 {
@IsString() @IsString()
@ApiProperty()
name!: string; name!: string;
@IsTimeZone() @IsTimeZone()
@ApiProperty()
timeZone!: string; timeZone!: string;
@IsArray() @IsArray()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => CreateAvailabilityInput_2024_04_15) @Type(() => CreateAvailabilityInput_2024_04_15)
@IsOptional() @IsOptional()
@ApiPropertyOptional({ type: [CreateAvailabilityInput_2024_04_15] })
availabilities?: CreateAvailabilityInput_2024_04_15[]; availabilities?: CreateAvailabilityInput_2024_04_15[];
@IsBoolean() @IsBoolean()
@ApiProperty()
isDefault!: boolean; isDefault!: boolean;
} }
@@ -1,95 +1,117 @@
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { IsBoolean, IsInt, IsOptional, IsString, ValidateNested, IsArray } from "class-validator"; import { IsBoolean, IsInt, IsOptional, IsString, ValidateNested, IsArray } from "class-validator";
class EventTypeModel_2024_04_15 { class EventTypeModel_2024_04_15 {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional({ type: String, nullable: true })
eventName?: string | null; eventName?: string | null;
} }
class AvailabilityModel_2024_04_15 { class AvailabilityModel_2024_04_15 {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiPropertyOptional({ type: Number, nullable: true })
userId?: number | null; userId?: number | null;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiPropertyOptional({ type: Number, nullable: true })
scheduleId?: number | null; scheduleId?: number | null;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiPropertyOptional({ type: Number, nullable: true })
eventTypeId?: number | null; eventTypeId?: number | null;
@IsArray() @IsArray()
@IsInt({ each: true }) @IsInt({ each: true })
@ApiProperty({ type: [Number] })
days!: number[]; days!: number[];
@IsOptional() @IsOptional()
@Type(() => Date) @Type(() => Date)
@IsString() // Assuming date is serialized/deserialized appropriately @IsString()
@ApiPropertyOptional()
startTime?: Date; startTime?: Date;
@IsOptional() @IsOptional()
@Type(() => Date) @Type(() => Date)
@IsString() // Assuming date is serialized/deserialized appropriately @IsString()
@ApiPropertyOptional()
endTime?: Date; endTime?: Date;
@IsOptional() @IsOptional()
@Type(() => Date) @Type(() => Date)
@IsString() // Assuming date is serialized/deserialized appropriately @IsString()
@ApiPropertyOptional({ type: String, nullable: true })
date?: Date | null; date?: Date | null;
} }
class ScheduleModel_2024_04_15 { class ScheduleModel_2024_04_15 {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsInt() @IsInt()
@ApiProperty()
userId!: number; userId!: number;
@IsString() @IsString()
@ApiProperty()
name!: string; name!: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional({ type: String, nullable: true })
timeZone?: string | null; timeZone?: string | null;
@IsOptional() @IsOptional()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => EventTypeModel_2024_04_15) @Type(() => EventTypeModel_2024_04_15)
@IsArray() @IsArray()
@ApiPropertyOptional({ type: [EventTypeModel_2024_04_15] })
eventType?: EventTypeModel_2024_04_15[]; eventType?: EventTypeModel_2024_04_15[];
@IsOptional() @IsOptional()
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => AvailabilityModel_2024_04_15) @Type(() => AvailabilityModel_2024_04_15)
@IsArray() @IsArray()
@ApiPropertyOptional({ type: [AvailabilityModel_2024_04_15] })
availability?: AvailabilityModel_2024_04_15[]; availability?: AvailabilityModel_2024_04_15[];
} }
export class UpdatedScheduleOutput_2024_04_15 { export class UpdatedScheduleOutput_2024_04_15 {
@ValidateNested() @ValidateNested()
@Type(() => ScheduleModel_2024_04_15) @Type(() => ScheduleModel_2024_04_15)
@ApiProperty({ type: ScheduleModel_2024_04_15 })
schedule!: ScheduleModel_2024_04_15; schedule!: ScheduleModel_2024_04_15;
@IsBoolean() @IsBoolean()
@ApiProperty()
isDefault!: boolean; isDefault!: boolean;
@IsOptional() @IsOptional()
@IsString() @IsString()
@ApiPropertyOptional()
timeZone?: string; timeZone?: string;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiPropertyOptional({ type: Number, nullable: true })
prevDefaultId?: number | null; prevDefaultId?: number | null;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiPropertyOptional({ type: Number, nullable: true })
currentDefaultId?: number | null; currentDefaultId?: number | null;
} }
@@ -1,105 +1,132 @@
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer"; import { Type } from "class-transformer";
import { IsDate, IsOptional, IsArray, IsBoolean, IsInt, IsString, ValidateNested } from "class-validator"; import { IsDate, IsOptional, IsArray, IsBoolean, IsInt, IsString, ValidateNested } from "class-validator";
class AvailabilityModel { class AvailabilityModel {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiPropertyOptional({ type: Number, nullable: true })
userId?: number | null; userId?: number | null;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiPropertyOptional({ type: Number, nullable: true })
eventTypeId?: number | null; eventTypeId?: number | null;
@IsArray() @IsArray()
@IsInt({ each: true }) @IsInt({ each: true })
@ApiProperty({ type: [Number] })
days!: number[]; days!: number[];
@IsDate() @IsDate()
@Type(() => Date) @Type(() => Date)
@ApiProperty({ type: Date })
startTime!: Date; startTime!: Date;
@IsDate() @IsDate()
@Type(() => Date) @Type(() => Date)
@ApiProperty({ type: Date })
endTime!: Date; endTime!: Date;
@IsOptional() @IsOptional()
@IsDate() @IsDate()
@Type(() => Date) @Type(() => Date)
@ApiPropertyOptional({ type: Date, nullable: true })
date?: Date | null; date?: Date | null;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiPropertyOptional({ type: Number, nullable: true })
scheduleId?: number | null; scheduleId?: number | null;
} }
class WorkingHours { class WorkingHours {
@IsArray() @IsArray()
@IsInt({ each: true }) @IsInt({ each: true })
@ApiProperty({ type: [Number] })
days!: number[]; days!: number[];
@IsInt() @IsInt()
@ApiProperty()
startTime!: number; startTime!: number;
@IsInt() @IsInt()
@ApiProperty()
endTime!: number; endTime!: number;
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiPropertyOptional({ type: Number, nullable: true })
userId?: number | null; userId?: number | null;
} }
class TimeRange { class TimeRange {
@IsOptional() @IsOptional()
@IsInt() @IsInt()
@ApiPropertyOptional({ type: Number, nullable: true })
userId?: number | null; userId?: number | null;
@IsDate() @IsDate()
@ApiProperty({ type: Date })
start!: Date; start!: Date;
@IsDate() @IsDate()
@ApiProperty({ type: Date })
end!: Date; end!: Date;
} }
export class ScheduleOutput { export class ScheduleOutput {
@IsInt() @IsInt()
@ApiProperty()
id!: number; id!: number;
@IsString() @IsString()
@ApiProperty()
name!: string; name!: string;
@IsBoolean() @IsBoolean()
@ApiProperty()
isManaged!: boolean; isManaged!: boolean;
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => WorkingHours) @Type(() => WorkingHours)
@ApiProperty({ type: [WorkingHours] })
workingHours!: WorkingHours[]; workingHours!: WorkingHours[];
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@Type(() => AvailabilityModel) @Type(() => AvailabilityModel)
@IsArray() @IsArray()
@ApiProperty({ type: [AvailabilityModel] })
schedule!: AvailabilityModel[]; schedule!: AvailabilityModel[];
@ApiProperty({ type: [[TimeRange]] })
availability!: TimeRange[][]; availability!: TimeRange[][];
@IsString() @IsString()
@ApiProperty()
timeZone!: string; timeZone!: string;
@ValidateNested({ each: true }) @ValidateNested({ each: true })
@IsArray() @IsArray()
@ApiPropertyOptional({ type: [Object] })
// note(Lauris) it should be // note(Lauris) it should be
// dateOverrides!: { ranges: TimeRange[] }[]; // dateOverrides!: { ranges: TimeRange[] }[];
// but docs aren't generating correctly it results in array of strings // but docs aren't generating correctly it results in array of strings
dateOverrides!: unknown[]; dateOverrides!: unknown[];
@IsBoolean() @IsBoolean()
@ApiProperty()
isDefault!: boolean; isDefault!: boolean;
@IsBoolean() @IsBoolean()
@ApiProperty()
isLastSchedule!: boolean; isLastSchedule!: boolean;
@IsBoolean() @IsBoolean()
@ApiProperty()
readOnly!: boolean; readOnly!: boolean;
} }
Loaded 100 of 1122 files, more files were not shown because too many files have changed in this diff. Show more