From c215375beacb676daf45baf7beeecd827c9fa175 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 17 Feb 2024 01:13:02 +0700 Subject: [PATCH] feat: a bunch of cose --- .eslintrc.json | 3 +- apps/backend/src/api/api.module.ts | 12 +- .../backend/src/api/routes/auth.controller.ts | 1 - .../src/api/routes/integrations.controller.ts | 25 +- .../src/api/routes/posts.controller.ts | 21 + apps/backend/src/main.ts | 2 + .../frontend/public/icons/platforms/devto.png | Bin 0 -> 1171 bytes .../public/icons/platforms/hashnode.png | Bin 0 -> 1706 bytes .../public/icons/platforms/linkedin.png | Bin 0 -> 1071 bytes .../public/icons/platforms/medium.png | Bin 0 -> 983 bytes .../public/icons/platforms/reddit.png | Bin 0 -> 1546 bytes apps/frontend/public/icons/platforms/x.png | Bin 0 -> 1712 bytes .../frontend/src/app/(site)/launches/page.tsx | 6 +- apps/frontend/src/app/(site)/layout.tsx | 1 - apps/frontend/src/app/api/hello/route.ts | 0 apps/frontend/src/app/global.css | 26 +- apps/frontend/src/app/layout.tsx | 2 + .../components/launches/add.edit.model.tsx | 349 +++ .../launches/add.provider.component.tsx | 109 + .../components/launches/calendar.context.tsx | 40 + .../src/components/launches/calendar.tsx | 141 ++ .../src/components/launches/filters.tsx | 9 + .../launches/helpers/use.formatting.ts | 30 + .../launches/helpers/use.hide.top.editor.tsx | 34 + .../launches/helpers/use.integration.ts | 8 + .../helpers/use.move.to.integration.tsx | 30 + .../components/launches/helpers/use.values.ts | 57 + .../launches/launches.component.tsx | 61 + .../launches/providers/devto.provider.tsx | 12 + .../providers/fonts/x/Chirp-Bold.woff2 | Bin 0 -> 45016 bytes .../providers/fonts/x/Chirp-Regular.woff2 | Bin 0 -> 44660 bytes .../providers/high.order.provider.tsx | 144 ++ .../launches/providers/linkedin.provider.tsx | 93 + .../launches/providers/reddit.provider.tsx | 93 + .../launches/providers/show.all.providers.tsx | 28 + .../launches/providers/x.provider.tsx | 97 + .../src/components/layout/layout.settings.tsx | 33 +- .../components/settings/github.component.tsx | 31 +- apps/frontend/tailwind.config.js | 14 +- apps/workers/src/app/app.module.ts | 3 +- apps/workers/src/app/posts.controller.ts | 15 + .../src/bull-mq-transport/bull-mq.module.ts | 4 +- .../client/bull-mq.client.ts | 11 + .../src/database/prisma/database.module.ts | 8 +- .../integrations/integration.repository.ts | 11 +- .../integrations/integration.service.ts | 8 +- .../database/prisma/posts/posts.repository.ts | 71 + .../database/prisma/posts/posts.service.ts | 92 + .../src/database/prisma/schema.prisma | 16 +- .../database/prisma/stars/stars.service.ts | 7 +- .../src/dtos/integrations/api.key.dto.ts | 9 + .../src/dtos/posts/create.post.dto.ts | 47 + .../all.providers.settings.ts | 7 + .../providers-settings/dev.to.settings.dto.ts | 24 + .../article/article.integrations.interface.ts | 4 +- .../integrations/article/dev.to.provider.ts | 14 +- .../integrations/article/hashnode.provider.ts | 19 +- .../integrations/article/medium.provider.ts | 14 +- .../src/integrations/integration.manager.ts | 6 + .../integrations/social/linkedin.provider.ts | 272 ++- .../integrations/social/reddit.provider.ts | 33 +- .../social/social.integrations.interface.ts | 12 +- .../src/integrations/social/x.provider.ts | 162 +- .../src/form/button.tsx | 4 +- .../react-shared-libraries/src/form/input.tsx | 22 + .../src/helpers/delete.dialog.tsx | 14 + .../src/helpers/mantine.wrapper.tsx | 19 + .../src/helpers/use.prevent.window.unload.tsx | 10 + package-lock.json | 2019 ++++++++++++++++- package.json | 13 +- 70 files changed, 4229 insertions(+), 253 deletions(-) create mode 100644 apps/backend/src/api/routes/posts.controller.ts create mode 100644 apps/frontend/public/icons/platforms/devto.png create mode 100644 apps/frontend/public/icons/platforms/hashnode.png create mode 100644 apps/frontend/public/icons/platforms/linkedin.png create mode 100644 apps/frontend/public/icons/platforms/medium.png create mode 100644 apps/frontend/public/icons/platforms/reddit.png create mode 100644 apps/frontend/public/icons/platforms/x.png delete mode 100644 apps/frontend/src/app/api/hello/route.ts create mode 100644 apps/frontend/src/components/launches/add.edit.model.tsx create mode 100644 apps/frontend/src/components/launches/add.provider.component.tsx create mode 100644 apps/frontend/src/components/launches/calendar.context.tsx create mode 100644 apps/frontend/src/components/launches/calendar.tsx create mode 100644 apps/frontend/src/components/launches/filters.tsx create mode 100644 apps/frontend/src/components/launches/helpers/use.formatting.ts create mode 100644 apps/frontend/src/components/launches/helpers/use.hide.top.editor.tsx create mode 100644 apps/frontend/src/components/launches/helpers/use.integration.ts create mode 100644 apps/frontend/src/components/launches/helpers/use.move.to.integration.tsx create mode 100644 apps/frontend/src/components/launches/helpers/use.values.ts create mode 100644 apps/frontend/src/components/launches/launches.component.tsx create mode 100644 apps/frontend/src/components/launches/providers/devto.provider.tsx create mode 100644 apps/frontend/src/components/launches/providers/fonts/x/Chirp-Bold.woff2 create mode 100644 apps/frontend/src/components/launches/providers/fonts/x/Chirp-Regular.woff2 create mode 100644 apps/frontend/src/components/launches/providers/high.order.provider.tsx create mode 100644 apps/frontend/src/components/launches/providers/linkedin.provider.tsx create mode 100644 apps/frontend/src/components/launches/providers/reddit.provider.tsx create mode 100644 apps/frontend/src/components/launches/providers/show.all.providers.tsx create mode 100644 apps/frontend/src/components/launches/providers/x.provider.tsx create mode 100644 apps/workers/src/app/posts.controller.ts create mode 100644 libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts create mode 100644 libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts create mode 100644 libraries/nestjs-libraries/src/dtos/integrations/api.key.dto.ts create mode 100644 libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts create mode 100644 libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts create mode 100644 libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.settings.dto.ts create mode 100644 libraries/react-shared-libraries/src/form/input.tsx create mode 100644 libraries/react-shared-libraries/src/helpers/delete.dialog.tsx create mode 100644 libraries/react-shared-libraries/src/helpers/mantine.wrapper.tsx create mode 100644 libraries/react-shared-libraries/src/helpers/use.prevent.window.unload.tsx diff --git a/.eslintrc.json b/.eslintrc.json index 59107204..974b88a7 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -14,7 +14,8 @@ "rules": { "@typescript-eslint/no-non-null-asserted-optional-chain": "off", "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/ban-ts-comment": "off" + "@typescript-eslint/ban-ts-comment": "off", + "react/display-name": "off" } }, { diff --git a/apps/backend/src/api/api.module.ts b/apps/backend/src/api/api.module.ts index 2fa493db..b613fec1 100644 --- a/apps/backend/src/api/api.module.ts +++ b/apps/backend/src/api/api.module.ts @@ -11,15 +11,21 @@ import {PermissionsService} from "@gitroom/backend/services/auth/permissions/per import {IntegrationsController} from "@gitroom/backend/api/routes/integrations.controller"; import {IntegrationManager} from "@gitroom/nestjs-libraries/integrations/integration.manager"; import {SettingsController} from "@gitroom/backend/api/routes/settings.controller"; +import {BullMqModule} from "@gitroom/nestjs-libraries/bull-mq-transport/bull-mq.module"; +import {ioRedis} from "@gitroom/nestjs-libraries/redis/redis.service"; +import {PostsController} from "@gitroom/backend/api/routes/posts.controller"; const authenticatedController = [ UsersController, AnalyticsController, IntegrationsController, - SettingsController + SettingsController, + PostsController ]; @Module({ - imports: [], + imports: [BullMqModule.forRoot({ + connection: ioRedis + })], controllers: [StripeController, AuthController, ...authenticatedController], providers: [ AuthService, @@ -27,7 +33,7 @@ const authenticatedController = [ AuthMiddleware, PoliciesGuard, PermissionsService, - IntegrationManager + IntegrationManager, ], get exports() { return [...this.imports, ...this.providers]; diff --git a/apps/backend/src/api/routes/auth.controller.ts b/apps/backend/src/api/routes/auth.controller.ts index 6ca2e4b2..579e10d7 100644 --- a/apps/backend/src/api/routes/auth.controller.ts +++ b/apps/backend/src/api/routes/auth.controller.ts @@ -41,7 +41,6 @@ export class AuthController { @Res({ passthrough: true }) response: Response ) { try { - console.log('heghefrgefg'); const jwt = await this._authService.routeAuth(body.provider, body); response.cookie('auth', jwt, { domain: '.' + new URL(process.env.FRONTEND_URL!).hostname, diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index ca9d9d37..9e645b88 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -5,6 +5,7 @@ import {IntegrationManager} from "@gitroom/nestjs-libraries/integrations/integra import {IntegrationService} from "@gitroom/nestjs-libraries/database/prisma/integrations/integration.service"; import {GetOrgFromRequest} from "@gitroom/nestjs-libraries/user/org.from.request"; import {Organization} from "@prisma/client"; +import {ApiKeyDto} from "@gitroom/nestjs-libraries/dtos/integrations/api.key.dto"; @Controller('/integrations') export class IntegrationsController { @@ -13,6 +14,18 @@ export class IntegrationsController { private _integrationService: IntegrationService ) { } + @Get('/') + getIntegration() { + return this._integrationManager.getAllIntegrations(); + } + + @Get('/list') + async getIntegrationList( + @GetOrgFromRequest() org: Organization, + ) { + return {integrations: (await this._integrationService.getIntegrationsList(org.id)).map(p => ({name: p.name, id: p.id, picture: p.picture, identifier: p.providerIdentifier, type: p.type}))}; + } + @Get('/social/:integration') async getIntegrationUrl( @Param('integration') integration: string @@ -25,14 +38,14 @@ export class IntegrationsController { const {codeVerifier, state, url} = await integrationProvider.generateAuthUrl(); await ioRedis.set(`login:${state}`, codeVerifier, 'EX', 300); - return url; + return {url}; } @Post('/article/:integration/connect') async connectArticle( @GetOrgFromRequest() org: Organization, @Param('integration') integration: string, - @Body('code') api: string + @Body() api: ApiKeyDto ) { if (!this._integrationManager.getAllowedArticlesIntegrations().includes(integration)) { throw new Error('Integration not allowed'); @@ -43,13 +56,13 @@ export class IntegrationsController { } const integrationProvider = this._integrationManager.getArticlesIntegration(integration); - const {id, name, token} = await integrationProvider.authenticate(api); + const {id, name, token, picture} = await integrationProvider.authenticate(api.api); if (!id) { throw new Error('Invalid api key'); } - return this._integrationService.createIntegration(org.id, name, 'article', String(id), integration, token); + return this._integrationService.createIntegration(org.id, name, picture,'article', String(id), integration, token); } @Post('/social/:integration/connect') @@ -68,7 +81,7 @@ export class IntegrationsController { } const integrationProvider = this._integrationManager.getSocialIntegration(integration); - const {accessToken, expiresIn, refreshToken, id, name} = await integrationProvider.authenticate({ + const {accessToken, expiresIn, refreshToken, id, name, picture} = await integrationProvider.authenticate({ code: body.code, codeVerifier: getCodeVerifier }); @@ -77,6 +90,6 @@ export class IntegrationsController { throw new Error('Invalid api key'); } - return this._integrationService.createIntegration(org.id, name, 'social', String(id), integration, accessToken, refreshToken, expiresIn); + return this._integrationService.createIntegration(org.id, name, picture, 'social', String(id), integration, accessToken, refreshToken, expiresIn); } } diff --git a/apps/backend/src/api/routes/posts.controller.ts b/apps/backend/src/api/routes/posts.controller.ts new file mode 100644 index 00000000..3e6950ec --- /dev/null +++ b/apps/backend/src/api/routes/posts.controller.ts @@ -0,0 +1,21 @@ +import {Body, Controller, Post} from '@nestjs/common'; +import {PostsService} from "@gitroom/nestjs-libraries/database/prisma/posts/posts.service"; +import {GetOrgFromRequest} from "@gitroom/nestjs-libraries/user/org.from.request"; +import {Organization} from "@prisma/client"; +import {CreatePostDto} from "@gitroom/nestjs-libraries/dtos/posts/create.post.dto"; + +@Controller('/posts') +export class PostsController { + constructor( + private _postsService: PostsService + ) { + } + + @Post('/') + createPost( + @GetOrgFromRequest() org: Organization, + @Body() body: CreatePostDto + ) { + return this._postsService.createPost(org.id, body); + } +} diff --git a/apps/backend/src/main.ts b/apps/backend/src/main.ts index e2402569..f39d7596 100644 --- a/apps/backend/src/main.ts +++ b/apps/backend/src/main.ts @@ -1,3 +1,5 @@ +process.env.TZ='UTC'; + import cookieParser from 'cookie-parser'; import {Logger, ValidationPipe} from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; diff --git a/apps/frontend/public/icons/platforms/devto.png b/apps/frontend/public/icons/platforms/devto.png new file mode 100644 index 0000000000000000000000000000000000000000..37caebcccfb4feb2bb2cbb177f650f0cad7f9618 GIT binary patch literal 1171 zcmV;E1Z?|>P)DMG);=L@k0A5mBxz70v!xX_O@W0W&Bo6U$NO{uY-vZ{APGsK?tp z=Ce9;?wRi#?m2Vr1rQMt5fKp)5fS+>A%qrMT3R|gJO7R&h+r^iwOZ|VJHs&8Syooo z*ViYP%UPD?BBfI4`ue)l=^PsylgVT(%dV`foSdBS#d~{u>+0$Vg1ET2SX^BEM!u7i zlPt@ke-aLdmzI_&iUNS#++2&rf>JRvGow%_+-^5QD3M6a&d&ZJ?(_L@q)<+zP+APb z@b>^ClgS1L2j}MI0HD6UzN)G!wJ1r3LZRd1V{B_Q8ktPStyWf6mX?-cdwY93r9!4q zS{z=l*JLsQKvPqbMxz0Mk&%&wg@r^SK@bE01cSkijg3eof*<8_x!G(6fW5uFo}M0> zrpwFAOG`^HE-tX3R;$g($N+#qAb?Z<2IJ%7@pznNS)`YOFKI| z*wAP+MMXu}$ji$EfXBy2kH;gV`E|>GNpW%U^z<|UM59rI!Ekwb$t_tdmRKxCQPi(r zziMl1@9*!Mnwm(G1c0lntDBn}Ax**ELI`~~KY}1|r8xY4|LN%|w%gj;D2l4CuI4fp zsvxAkEOV#-ShJ^NsGTuu;e)RL(P%Uli*XkO+^roQ9idPNmyh9r5Sp5r;!A0@+LxCX?)9Ui zBW^4UAy`C-M1ltlNs=^8b3$ilXZ`*C0C0VM{r2_-f6WCB0FX+he5r$j1Cz;wdlVrw zKR=(c3;76U?eqC;HXHxsd47JjTCGN-5x20{>(%S^qobozsgyr~9002ovPDHLkV1nddE7Sl0 literal 0 HcmV?d00001 diff --git a/apps/frontend/public/icons/platforms/hashnode.png b/apps/frontend/public/icons/platforms/hashnode.png new file mode 100644 index 0000000000000000000000000000000000000000..7e1679b9b3cb02947468f94dc2ab8d32e2100fbc GIT binary patch literal 1706 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 literal 0 HcmV?d00001 diff --git a/apps/frontend/public/icons/platforms/medium.png b/apps/frontend/public/icons/platforms/medium.png new file mode 100644 index 0000000000000000000000000000000000000000..9c00fd768de327956a997bc6d8d80adb426e9d10 GIT binary patch literal 983 zcmV;|11S87P)L^VwAK_a5xf<=qO_AZ3ZkfkL5mgt4@Lzv$;5Q5s^-USg1o4i=cv+K@^lyEeM4a6eXo7*uB}A&Eqyv9U2zCgChU zK0eeEX_~g#Y~O~|*Vn5hoSvSRmzVz?6R)10o-`M1d3pKU1h3bt*c%80ii(P~FadzU zVDNZ6YFN=|w6L%+O?zc!Wi%R9?9~V)*5&1;8d-mTf0{-d$JH8t0Bd@BS}i>mi=}Bi zJUrBlW%$;CAj%IB0I034wcG8ATNs9QcXxjqo)s(rn4Fw68jbS0+wBH`_xJa#W1XL$ z%Xe`cZ*Fdu6j2m)I-Lx|?Ck7h9cyW6Nxo|`nHClnB*l)7j=H+K$H&LBv$L#Y1%pAw zd)(=CmX(!>E2E>M`T6;KdwV2FW*rLv)IJoC$0MvuN=imXMgV{y2oyzgfi*ijtLSk! z92Sd(U-LKn&CSj8^K)KaUM{d$mL&*6?y0J(ayT5kd2DP909IC3v;k?s0)X}Pb;Xfp zZf*_$hK7bP42#8L$H&Jx#Ue>^e}7+Yv)OEYeSLPj-Dor(9UVPAJ>>#Rcw>BheR;iJ zMd!xGMsIH~0IaR8{R>k@SiD%471p6pC=!XtJr;|lrl#iZ?(X{fI_p^c)e>U?!0_;} znty6)>K~c3V}--vP$(oflO%a^a*{i&&(BYBk!9Jftt|;F0QmiW@vn(2Vign=0D%0# zxV^niQIxpCFwE}m?$1nl>DAECkW41|*=>G)Ub5?UyT!xZ!^4Bb;BvWsemXUq%?Aeu zLKaQaE|*KLpSO%z3O9Jji3&s3$QrIAQP zE%D;wLTvW=e5q8bwY62Kbar;Wy}hMj;`Q?KqDH1XcrXmJxVXsw2cgjO=eKfZX2$RL zUtL`Z6^f$9$Hy6lNpt2L$Dt@{Fl73g(WOh5E?v5^tY3&7K`Ky&8n6HW002ovPDHLk FV1moS#mxW! literal 0 HcmV?d00001 diff --git a/apps/frontend/public/icons/platforms/reddit.png b/apps/frontend/public/icons/platforms/reddit.png new file mode 100644 index 0000000000000000000000000000000000000000..14e73ac797b2a321e1a3e7aa94b6fed065eb2075 GIT binary patch literal 1546 zcmV+l2KD)gP)yh%hsR9J=Wm}zVkRTRg6GxPeEz80Y@c7dR6LLpsPiv$8fSTqeFO#|F-gEB%-gC~qXP%%I@)$z=zX_OQlvX?#r4Q;u%ST5GgYR_y?T4S*sXRnQ8M9ZBy?`k>jL#s!5oPw3WW4<* z3)8@2x3mg^L1JZYaym)ls5(yVWv*UE1gFUOG1hyG65eWZki0whD}hqN}3 zvxMe)eknnf_3JdwZysC5(24As)hb1a;-kniTi4?@WP>M6-KFu6YH2X069vsv*HokZ zsDflCdp>`clc4dk8lE-i(?qig%@%1v%XmKNTp1arfDA*R+DS?0ayi0q`s>d>t-+YE zh%F;aT_y%#2n29RB#*_NLe64^R%lSK~l#&w;~Kwuz?6F_y9Fc1Sf!Z_Flv7`gPsJHlmq7suq?r2*Ul)UZ>?S?de)*@8#%dF}-^2GiaDW>`*tN-I%-$mIcaj7ysJ+IyW9CzoeFElA zw5B!C$JW)cD9H1KmZAP?iq~qQt~<%&R~!0@&Rnn(aymy^zyN3 z6wId=ywD4U>rHfDF2Vy|@POOSqILQKjWwJ;LJ|;f$P>OgjQIQ%N49bE68TGcvDjq5 ztW|vZ8z=VQNThHx>C^NBL~w%tA*xeAg7#Y`z3#DW_}W}Cf`T;^tg-CvZo}^ly~kHU z1h40=R+{S{z~CWpDGisMvG{2Dhz=P6H}PEf>|rcoJC3ot|TNof`A;r@1?JU z=6Y%?s41u6Dgi={v--*ysS{2AoYCg948eNDCE`xTm5jrMAQA8}*o&u+0Y8E8Js}!C zD~t<{#t8m{&^9A3u!U6>g}xqodWQWH{MiYW>T0wd4EzEj3Hr0uf|l+$6Rew&Qo;&? w;i>B~a5B#SxL}l4JQ$@F4@POlgU6KCf8f&sia_GxVE_OC07*qoM6N<$g6Ck`pa1{> literal 0 HcmV?d00001 diff --git a/apps/frontend/public/icons/platforms/x.png b/apps/frontend/public/icons/platforms/x.png new file mode 100644 index 0000000000000000000000000000000000000000..59799be46b5e9d7fb61b4bb19e045ce403e04cb6 GIT binary patch literal 1712 zcmV;h22c5kP)m$uQi+GK9e^Zj3(ze7^@ z)|(gpuC{Z|_jf+edCvKr-#HJZlnMj_vXG4Dc}gk$*BEoy!Wt*9En)>@i&(+f0>u*f zr~}|z98_?b>CLZj?zja&Bod{kr>Cc zlgZ>uOXo6Hm6eq>Iy!2%+btH$Xu3fvjySo7t z7Z*oGMfujwTSo|qii#>NEd^k+*_xV~YHMrj>+2gE8%IV)c%C;JjjUN9OlfIpFJ8O= zaP8VPrBdl%<=f-(<;(Bhy$hhCqJmPYP$=Ybc}`9a0EfeI;=~CbL+*8wl$6xk+6usE zG_G8^(yzon79m8Z(^)K*;o;%T%uH4i9UXo4>{$RKBO_^PX+HWOgv7+eum^kg?Af|? z>jGi=ziZ#s;o;%Oj~@r{jV`t)h1)A{4akDQzwkw_FE zQ=nLc5RFDNHa2FpS`QpJzzQQHBMS=)0hrC^jEsz#e%Nn=!C_DwT?{#>K@oH#Y<5=;(-xiz73_*p3}LMn^{hl$4Z6B$A+*0>xtO zWHQ;+t5*TszI~gO2n2$)YuApCk8>PXUS2K~3K`SJjT>LTehr|yx;i#CHdv-CgUl}gp#-VWg2y?Y9UA_S%&uvk-(NVI3q9u^wb zuV2qt#bWXG>(>GF^z>-8+WYtK19<-Y`Knc`0$+NeW3kp!skE}P62PNJkGvCEsZjN-2IJjxkCU$ZOfhlM#CRnjzMPFav)YR0eQ>U0*AP{6{XS-Z309LCtGc%LT zv(T7A#1aaHdcEG`@!0M5EnBvD$u3;Dz;WD{FJI(x`L8gAg2glviR9e5a{xL!J7qE% zn?o|0th2KdKz)7vk|j(2>S%do2?+`9?d=@LUAlCMOq*7#{rdGQfJ284&9^E2SuBA- zpwVc)ef#Eex%Tee>m@sR@}$evpF?2l}=|6H*VYj(A?a-Y}v9ui^Zl+c6PSM<8eBjES$2Pk?lDxEiK-7k(ijs zf_`CPq2Dt_=vb^}Mn=ZO#Dv@JzIgGXw~$hroSZy1HU^-msECCILdd>-`z#iV$K&xP z+`k!%IaRCGy}i8v8XFsDZ+V9gA9gyO9LH(3TJPplU0n^Jt*wn^X}=STF{xB4mh`o? zwXvlBr#6(*J9qBzJpc0L%f!S)rjkmfPoF*oP+D3V9UUFSC~ z6~OM@yV}6x^$^n zEGC3(*s$U4+qVF6b94Q^N?*@S2-&)I>-hLM&+}DPRe>;hwR*jNVqyZoty{M$Dk>@~ zE6d8t`uqC<^!E0$gga;~#X0PR$ zHt)5YNF)Mq^XAQuA3r{R{Mh61c>D8flu~wK)ZN`Jkw`d>n^`Lq3Y|`8SXh{E5A${w z)cu`r`J5exi1iO`fnMn@Vg+N1SizXD&jr73em^+OXa4~fg%}nP>d>VC0000asd + ); } diff --git a/apps/frontend/src/app/(site)/layout.tsx b/apps/frontend/src/app/(site)/layout.tsx index 350f4d76..12b93072 100644 --- a/apps/frontend/src/app/(site)/layout.tsx +++ b/apps/frontend/src/app/(site)/layout.tsx @@ -1,4 +1,3 @@ -import '../global.css'; import {LayoutSettings} from "@gitroom/frontend/components/layout/layout.settings"; export default async function Layout({ children }: { children: React.ReactNode }) { diff --git a/apps/frontend/src/app/api/hello/route.ts b/apps/frontend/src/app/api/hello/route.ts deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/frontend/src/app/global.css b/apps/frontend/src/app/global.css index 2b0ec37a..3ef18c18 100644 --- a/apps/frontend/src/app/global.css +++ b/apps/frontend/src/app/global.css @@ -2,9 +2,9 @@ @tailwind components; @tailwind utilities; -/*body, html {*/ -/* overflow-x: hidden;*/ -/*}*/ +body, html { + background-color: black; +} .box { position: relative; padding: 8px 24px; @@ -41,7 +41,7 @@ } .table1 thead { - background-color: #111423; + background-color: #0F1524; height: 44px; font-size: 12px; border-bottom: 1px solid #28344F; @@ -55,4 +55,22 @@ padding: 16px 24px; font-family: Inter; font-size: 14px; +} + +.swal2-modal { + background-color: black !important; + border: 2px solid #0B101B; +} + +.swal2-modal * { + color: white !important; +} + +.swal2-icon { + color: white !important; + border-color: white !important; +} + +.swal2-confirm { + background-color: #262373 !important; } \ No newline at end of file diff --git a/apps/frontend/src/app/layout.tsx b/apps/frontend/src/app/layout.tsx index 7668f2c4..7177d176 100644 --- a/apps/frontend/src/app/layout.tsx +++ b/apps/frontend/src/app/layout.tsx @@ -1,3 +1,5 @@ +import './global.css'; + import LayoutContext from "@gitroom/frontend/components/layout/layout.context"; import {ReactNode} from "react"; import {Chakra_Petch} from "next/font/google"; diff --git a/apps/frontend/src/components/launches/add.edit.model.tsx b/apps/frontend/src/components/launches/add.edit.model.tsx new file mode 100644 index 00000000..3b513eef --- /dev/null +++ b/apps/frontend/src/components/launches/add.edit.model.tsx @@ -0,0 +1,349 @@ +'use client'; + +import { FC, useCallback, useEffect, useState } from 'react'; +import dayjs from 'dayjs'; +import { Integrations } from '@gitroom/frontend/components/launches/calendar.context'; +import Image from 'next/image'; +import clsx from 'clsx'; +import MDEditor from '@uiw/react-md-editor'; +import { usePreventWindowUnload } from '@gitroom/react/helpers/use.prevent.window.unload'; +import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; +import { useModals } from '@mantine/modals'; +import { ShowAllProviders } from '@gitroom/frontend/components/launches/providers/show.all.providers'; +import { useHideTopEditor } from '@gitroom/frontend/components/launches/helpers/use.hide.top.editor'; +import { Button } from '@gitroom/react/form/button'; +import { IntegrationContext } from '@gitroom/frontend/components/launches/helpers/use.integration'; +import { + getValues, + resetValues, +} from '@gitroom/frontend/components/launches/helpers/use.values'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import { + useMoveToIntegration, + useMoveToIntegrationListener, +} from '@gitroom/frontend/components/launches/helpers/use.move.to.integration'; + +export const PickPlatforms: FC<{ + integrations: Integrations[]; + selectedIntegrations: Integrations[]; + onChange: (integrations: Integrations[]) => void; + singleSelect: boolean; +}> = (props) => { + const { integrations, selectedIntegrations, onChange } = props; + const [selectedAccounts, setSelectedAccounts] = + useState(selectedIntegrations); + + useEffect(() => { + if ( + props.singleSelect && + selectedAccounts.length && + integrations.indexOf(selectedAccounts?.[0]) === -1 + ) { + addPlatform(integrations[0])(); + } + }, [integrations, selectedAccounts]); + + useMoveToIntegrationListener(props.singleSelect, (identifier) => { + const findIntegration = integrations.find( + (p) => p.identifier === identifier + ); + if (findIntegration) { + addPlatform(findIntegration)(); + } + }); + + const addPlatform = useCallback( + (integration: Integrations) => async () => { + if (props.singleSelect) { + onChange([integration]); + setSelectedAccounts([integration]); + return; + } + if (selectedAccounts.includes(integration)) { + const changedIntegrations = selectedAccounts.filter( + ({ id }) => id !== integration.id + ); + + if ( + !props.singleSelect && + !(await deleteDialog( + 'Are you sure you want to remove this platform?' + )) + ) { + return; + } + onChange(changedIntegrations); + setSelectedAccounts(changedIntegrations); + } else { + const changedIntegrations = [...selectedAccounts, integration]; + onChange(changedIntegrations); + setSelectedAccounts(changedIntegrations); + } + }, + [selectedAccounts] + ); + return ( +
+ {integrations.map((integration) => + !props.singleSelect ? ( +
+
p.id === integration.id) === + -1 + ? 'grayscale opacity-65' + : 'grayscale-0' + )} + > + {integration.identifier} + {integration.identifier} +
+
+ ) : ( +
+
p.id === integration.id) === + -1 + ? 'bg-sixth' + : 'bg-forth' + )} + > +
+
+ {integration.identifier} + {integration.identifier} +
+
{integration.name}
+
+
+
+ ) + )} +
+ ); +}; + +export const PreviewComponent: FC<{ + integrations: Integrations[]; + editorValue: string[]; +}> = (props) => { + const { integrations, editorValue } = props; + const [selectedIntegrations, setSelectedIntegrations] = useState([ + integrations[0], + ]); + + useEffect(() => { + if (integrations.indexOf(selectedIntegrations[0]) === -1) { + setSelectedIntegrations([integrations[0]]); + } + }, [integrations, selectedIntegrations]); + return ( +
+ + + + +
+ ); +}; +export const AddEditModal: FC<{ + date: dayjs.Dayjs; + integrations: Integrations[]; +}> = (props) => { + const { date, integrations } = props; + + // selected integrations to allow edit + const [selectedIntegrations, setSelectedIntegrations] = useState< + Integrations[] + >([]); + + // value of each editor + const [value, setValue] = useState(['']); + + const fetch = useFetch(); + + // prevent the window exit by mistake + usePreventWindowUnload(true); + + // hook to move the settings in the right place to fix missing fields + const moveToIntegration = useMoveToIntegration(); + + // hook to test if the top editor should be hidden + const showHide = useHideTopEditor(); + + // hook to open a new modal + const modal = useModals(); + + // if the user exit the popup we reset the global variable with all the values + useEffect(() => { + return () => { + resetValues(); + }; + }, []); + + // Change the value of the global editor + const changeValue = useCallback( + (index: number) => (newValue: string) => { + return setValue((prev) => { + prev[index] = newValue; + return [...prev]; + }); + }, + [value] + ); + + // Add another editor + const addValue = useCallback( + (index: number) => () => { + setValue((prev) => { + prev.splice(index + 1, 0, ''); + return [...prev]; + }); + }, + [value] + ); + + // override the close modal to ask the user if he is sure to close + const askClose = useCallback(async () => { + if ( + await deleteDialog( + 'Are you sure you want to close this modal? (all data will be lost)', + 'Yes, close it!' + ) + ) { + modal.closeAll(); + } + }, []); + + // function to send to the server and save + const schedule = useCallback(async () => { + const values = getValues(); + const allKeys = Object.keys(values).map((v) => ({ + integration: integrations.find((p) => p.id === v), + value: values[v].posts, + valid: values[v].isValid, + settings: values[v].settings(), + })); + + for (const key of allKeys) { + if (!key.valid) { + moveToIntegration(key?.integration?.identifier!); + return; + } + } + + await fetch('/posts', { + method: 'POST', + body: JSON.stringify({ + date: date.utc().format('YYYY-MM-DDTHH:mm:ss'), + posts: allKeys, + }), + }); + }, []); + + return ( + <> + +
+ + {!showHide.hideTopEditor ? ( + <> + {value.map((p, index) => ( + <> + 1 ? 150 : 500} + value={p} + preview="edit" + // @ts-ignore + onChange={changeValue(index)} + /> +
+ +
+ + ))} + + ) : ( +
+ Global Editor Hidden +
+ )} + {!!selectedIntegrations.length && ( + + )} + +
+ + ); +}; diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx new file mode 100644 index 00000000..b28edd9f --- /dev/null +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -0,0 +1,109 @@ +"use client"; + +import {useModals} from "@mantine/modals"; +import {FC, useCallback} from "react"; +import {useFetch} from "@gitroom/helpers/utils/custom.fetch"; +import {Input} from "@gitroom/react/form/input"; +import {FieldValues, FormProvider, useForm} from "react-hook-form"; +import {Button} from "@gitroom/react/form/button"; +import { classValidatorResolver } from '@hookform/resolvers/class-validator'; +import {ApiKeyDto} from "@gitroom/nestjs-libraries/dtos/integrations/api.key.dto"; +import {useRouter} from "next/navigation"; + +const resolver = classValidatorResolver(ApiKeyDto); + +export const AddProviderButton = () => { + const modal = useModals(); + const fetch = useFetch(); + const openModal = useCallback(async () => { + const data = await (await fetch('/integrations')).json(); + modal.openModal({ + title: 'Add Channel', + children: + }) + }, []); + return ( + + ); +} + +export const ApiModal: FC<{identifier: string, name: string}> = (props) => { + const fetch = useFetch(); + const router = useRouter(); + const modal = useModals(); + const methods = useForm({ + mode: 'onChange', + resolver + }); + + const submit = useCallback(async (data: FieldValues) => { + const add = await fetch(`/integrations/article/${props.identifier}/connect`, { + method: 'POST', + body: JSON.stringify({api: data.api}) + }); + + if (add.ok) { + modal.closeAll(); + router.refresh(); + return ; + } + + methods.setError('api', { + message: 'Invalid API key' + }); + }, []); + + return ( + +
+
+
+
+
+ ) +} +export const AddProviderComponent: FC<{social: Array<{identifier: string, name: string}>, article: Array<{identifier: string, name: string}>}> = (props) => { + const fetch = useFetch(); + const modal = useModals(); + const {social, article} = props; + const getSocialLink = useCallback((identifier: string) => async () => { + const {url} = await (await fetch('/integrations/social/' + identifier)).json(); + window.location.href = url; + }, []); + + const showApiButton = useCallback((identifier: string, name: string) => async () => { + modal.openModal({ + title: `Add ${name}`, + children: + }) + }, []); + return ( +
+
+

Social

+
+ {social.map((item) => ( +
+ {item.name} +
+ ))} +
+
+
+

Articles

+
+ {article.map((item) => ( +
+ {item.name} +
+ ))} +
+
+
+ ) +} \ No newline at end of file diff --git a/apps/frontend/src/components/launches/calendar.context.tsx b/apps/frontend/src/components/launches/calendar.context.tsx new file mode 100644 index 00000000..545e6826 --- /dev/null +++ b/apps/frontend/src/components/launches/calendar.context.tsx @@ -0,0 +1,40 @@ +'use client'; +import weekOfYear from 'dayjs/plugin/weekOfYear'; +import isoWeek from 'dayjs/plugin/isoWeek'; +import utc from 'dayjs/plugin/utc'; + +import {createContext, FC, ReactNode, useContext, useState} from 'react'; +import dayjs from 'dayjs'; + +dayjs.extend(weekOfYear); +dayjs.extend(isoWeek); +dayjs.extend(utc); + +const CalendarContext = createContext({ + currentWeek: dayjs().week(), + integrations: [] as Integrations[], + setFilters: (filters: { currentWeek: number }) => {}, +}); + +export interface Integrations { + name: string; + id: string; + identifier: string; + type: string; + picture: string; +} +export const CalendarWeekProvider: FC<{ children: ReactNode, integrations: Integrations[] }> = ({ + children, + integrations +}) => { + const [filters, setFilters] = useState({ + currentWeek: dayjs().week(), + }); + return ( + + {children} + + ); +}; + +export const useCalendar = () => useContext(CalendarContext); \ No newline at end of file diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx new file mode 100644 index 00000000..35e34cdc --- /dev/null +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -0,0 +1,141 @@ +'use client'; + +import { FC, useCallback, useMemo } from 'react'; +import { + useCalendar, +} from '@gitroom/frontend/components/launches/calendar.context'; +import dayjs from 'dayjs'; +import { useModals } from '@mantine/modals'; +import {AddEditModal} from "@gitroom/frontend/components/launches/add.edit.model"; +import clsx from "clsx"; + +const days = [ + '', + 'Monday', + 'Tuesday', + 'Wednesday', + 'Thursday', + 'Friday', + 'Saturday', + 'Sunday', +]; +const hours = [ + '00:00', + '01:00', + '02:00', + '03:00', + '04:00', + '05:00', + '06:00', + '07:00', + '08:00', + '09:00', + '10:00', + '11:00', + '12:00', + '13:00', + '14:00', + '15:00', + '16:00', + '17:00', + '18:00', + '19:00', + '20:00', + '21:00', + '22:00', + '23:00', +]; + +const CalendarColumn: FC<{ day: number; hour: string }> = (props) => { + const { day, hour } = props; + const week = useCalendar(); + const modal = useModals(); + + const getDate = useMemo(() => { + const date = + dayjs().isoWeek(week.currentWeek).isoWeekday(day).format('YYYY-MM-DD') + + 'T' + + hour + + ':00'; + return dayjs(date); + }, [week.currentWeek]); + + const addModal = useCallback(() => { + modal.openModal({ + closeOnClickOutside: false, + closeOnEscape: false, + withCloseButton: false, + children: ( + + ), + size: '80%', + title: `Adding posts for ${getDate.format('DD/MM/YYYY HH:mm')}`, + }); + }, []); + + const isBeforeNow = useMemo(() => { + return getDate.isBefore(dayjs()); + }, [getDate]); + + return ( +
+
+ {isBeforeNow ? '' : '+ Add'} +
+
+ ); +}; + +export const Calendar = () => { + return ( +
+
+ {days.map((day) => ( +
+ {day} +
+ ))} + {hours.map((hour) => + days.map((day, index) => ( + <> + {index === 0 ? ( +
+ {['00', '10', '20', '30', '40', '50'].map((num) => ( +
+ {hour.split(':')[0] + ':' + num} +
+ ))} +
+ ) : ( +
+ {['00', '10', '20', '30', '40', '50'].map((num) => ( + + ))} +
+ )} + + )) + )} +
+
+ ); +}; diff --git a/apps/frontend/src/components/launches/filters.tsx b/apps/frontend/src/components/launches/filters.tsx new file mode 100644 index 00000000..569de78c --- /dev/null +++ b/apps/frontend/src/components/launches/filters.tsx @@ -0,0 +1,9 @@ +"use client"; +import {useCalendar} from "@gitroom/frontend/components/launches/calendar.context"; +import dayjs from "dayjs"; + +export const Filters = () => { + const week = useCalendar(); + const betweenDates = dayjs().isoWeek(week.currentWeek).startOf('isoWeek').format('DD/MM/YYYY') + ' - ' + dayjs().isoWeek(week.currentWeek).endOf('isoWeek').format('DD/MM/YYYY'); + return
week.setFilters({currentWeek: week.currentWeek + 1})}>Week {week.currentWeek} ({betweenDates})
; +}; diff --git a/apps/frontend/src/components/launches/helpers/use.formatting.ts b/apps/frontend/src/components/launches/helpers/use.formatting.ts new file mode 100644 index 00000000..ad2362d8 --- /dev/null +++ b/apps/frontend/src/components/launches/helpers/use.formatting.ts @@ -0,0 +1,30 @@ +import removeMd from "remove-markdown"; +import {useMemo} from "react"; + +export const useFormatting = (text: string[], params: { + removeMarkdown?: boolean, + saveBreaklines?: boolean, + specialFunc?: (text: string) => string, +}) => { + return useMemo(() => { + return text.map((value) => { + let newText = value; + if (params.saveBreaklines) { + newText = newText.replace('\n', '𝔫𝔢𝔴𝔩𝔦𝔫𝔢'); + } + if (params.removeMarkdown) { + newText = removeMd(value); + } + if (params.saveBreaklines) { + newText = newText.replace('𝔫𝔢𝔴𝔩𝔦𝔫𝔢', '\n'); + } + if (params.specialFunc) { + newText = params.specialFunc(newText); + } + return { + text: newText, + count: params.removeMarkdown && params.saveBreaklines ? newText.replace(/\n/g, ' ').length : newText.length, + } + }); + }, [text]); +} \ No newline at end of file diff --git a/apps/frontend/src/components/launches/helpers/use.hide.top.editor.tsx b/apps/frontend/src/components/launches/helpers/use.hide.top.editor.tsx new file mode 100644 index 00000000..858d4af9 --- /dev/null +++ b/apps/frontend/src/components/launches/helpers/use.hide.top.editor.tsx @@ -0,0 +1,34 @@ +"use client"; + +import EventEmitter from 'events'; +import {useEffect, useState} from "react"; + +const emitter = new EventEmitter(); + +export const useHideTopEditor = () => { + const [hideTopEditor, setHideTopEditor] = useState(false); + useEffect(() => { + const hide = () => { + setHideTopEditor(true); + }; + const show = () => { + setHideTopEditor(false); + }; + emitter.on('hide', hide); + emitter.on('show', show); + return () => { + emitter.off('hide', hide); + emitter.off('show', show); + }; + }, []); + + return { + hideTopEditor, + hide: () => { + emitter.emit('hide'); + }, + show: () => { + emitter.emit('show'); + } + } +} \ No newline at end of file diff --git a/apps/frontend/src/components/launches/helpers/use.integration.ts b/apps/frontend/src/components/launches/helpers/use.integration.ts new file mode 100644 index 00000000..515f146b --- /dev/null +++ b/apps/frontend/src/components/launches/helpers/use.integration.ts @@ -0,0 +1,8 @@ +"use client"; + +import {createContext, useContext} from "react"; +import {Integrations} from "@gitroom/frontend/components/launches/calendar.context"; + +export const IntegrationContext = createContext<{integration: Integrations|undefined, value: string[]}>({integration: undefined, value: []}); + +export const useIntegration = () => useContext(IntegrationContext); \ No newline at end of file diff --git a/apps/frontend/src/components/launches/helpers/use.move.to.integration.tsx b/apps/frontend/src/components/launches/helpers/use.move.to.integration.tsx new file mode 100644 index 00000000..ba4d0f2f --- /dev/null +++ b/apps/frontend/src/components/launches/helpers/use.move.to.integration.tsx @@ -0,0 +1,30 @@ +'use client'; + +import EventEmitter from 'events'; +import {useCallback, useEffect} from 'react'; + +const emitter = new EventEmitter(); +export const useMoveToIntegration = () => { + return useCallback((identifier: string) => { + emitter.emit('moveToIntegration', identifier); + }, []); +}; + +export const useMoveToIntegrationListener = ( + enabled: boolean, + callback: (identifier: string) => void +) => { + useEffect(() => { + if (!enabled) { + return; + } + return load(); + }, []); + + const load = useCallback(() => { + emitter.on('moveToIntegration', callback); + return () => { + emitter.off('moveToIntegration', callback); + }; + }, []); +}; diff --git a/apps/frontend/src/components/launches/helpers/use.values.ts b/apps/frontend/src/components/launches/helpers/use.values.ts new file mode 100644 index 00000000..8732fe5c --- /dev/null +++ b/apps/frontend/src/components/launches/helpers/use.values.ts @@ -0,0 +1,57 @@ +import {useEffect, useMemo} from 'react'; +import { useForm } from 'react-hook-form'; +import { UseFormProps } from 'react-hook-form/dist/types'; +import {allProvidersSettings} from "@gitroom/nestjs-libraries/dtos/posts/providers-settings/all.providers.settings"; +import {classValidatorResolver} from "@hookform/resolvers/class-validator"; + +const finalInformation = {} as { + [key: string]: { posts: string[]; settings: () => object; isValid: boolean }; +}; +export const useValues = (identifier: string, integration: string, value: string[]) => { + const resolver = useMemo(() => { + const findValidator = allProvidersSettings.find((provider) => provider.identifier === identifier)!; + return classValidatorResolver(findValidator?.validator); + }, [integration]); + + const form = useForm({ + resolver + }); + + const getValues = useMemo(() => { + return form.getValues; + }, [form]); + + finalInformation[integration]= finalInformation[integration] || {}; + finalInformation[integration].posts = value; + finalInformation[integration].isValid = form.formState.isValid; + finalInformation[integration].settings = getValues; + + useEffect(() => { + return () => { + delete finalInformation[integration]; + }; + }, []); + + return form; +}; + +export const useSettings = (formProps?: Omit) => { + // const { integration } = useIntegration(); + // const form = useForm({ + // ...formProps, + // mode: 'onChange', + // }); + // + // finalInformation[integration?.identifier!].settings = { + // __type: integration?.identifier!, + // ...form.getValues(), + // }; + // return form; +}; + +export const getValues = () => finalInformation; +export const resetValues = () => { + Object.keys(finalInformation).forEach((key) => { + delete finalInformation[key]; + }); +}; diff --git a/apps/frontend/src/components/launches/launches.component.tsx b/apps/frontend/src/components/launches/launches.component.tsx new file mode 100644 index 00000000..f30e42be --- /dev/null +++ b/apps/frontend/src/components/launches/launches.component.tsx @@ -0,0 +1,61 @@ +import { AddProviderButton } from '@gitroom/frontend/components/launches/add.provider.component'; +import { FC, useMemo } from 'react'; +import Image from 'next/image'; +import { orderBy } from 'lodash'; +import { Calendar } from '@gitroom/frontend/components/launches/calendar'; +import {CalendarWeekProvider, Integrations} from '@gitroom/frontend/components/launches/calendar.context'; +import { Filters } from '@gitroom/frontend/components/launches/filters'; + +export const LaunchesComponent: FC<{ + integrations: Integrations[] +}> = (props) => { + const { integrations } = props; + const sortedIntegrations = useMemo(() => { + return orderBy(integrations, ['type', 'identifier'], ['desc', 'asc']); + }, [integrations]); + return ( + +
+ +
+
+
+

Channels

+
+ {sortedIntegrations.map((integration) => ( +
+
+ {integration.identifier} + {integration.identifier} +
+
{integration.name}
+
3
+
+ ))} +
+ +
+
+ +
+
+
+
+
+ ); +}; diff --git a/apps/frontend/src/components/launches/providers/devto.provider.tsx b/apps/frontend/src/components/launches/providers/devto.provider.tsx new file mode 100644 index 00000000..8c0c338e --- /dev/null +++ b/apps/frontend/src/components/launches/providers/devto.provider.tsx @@ -0,0 +1,12 @@ +import {FC} from "react"; +import {withProvider} from "@gitroom/frontend/components/launches/providers/high.order.provider"; + +const DevtoPreview: FC = () => { + return
asd
+}; + +const DevtoSettings: FC = () => { + return
asdfasd
+}; + +export default withProvider(DevtoSettings, DevtoPreview); \ No newline at end of file diff --git a/apps/frontend/src/components/launches/providers/fonts/x/Chirp-Bold.woff2 b/apps/frontend/src/components/launches/providers/fonts/x/Chirp-Bold.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..ed3a03d90aa33a1811ce8054dfcd7fe9f530e5d9 GIT binary patch literal 45016 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

8A z|2<2;w??n-1zm*;y80IGM{%7~ye1j=|6uVw{LapLx&Ob~sD26k;iaIW*$;8yBj4I@ z$5E#)c4S}wzv9=xDTc8&GVo;TL5}1TnLRnUUUc#*UEY+*{`w7arv@fr1+1bKvcR)L%HCEcD}$BtO6URkk=*qsHiZaXMEA(zs=oU*|Bs&%zFx*!|5Q`q<*DE17|GAb z@$Lrm;N6dq%vc;>2Y!0-hzWgdJ+|6H-r(~%k{REZ>1ohLm6TEWP<8a!=fsVcD7_zv zauCcVf2rJ>XmhIm-20j&5-}I&?trL-ty zRn3l8Se%^qW?YOv*XV@xzM z6PN|@?2n&1_p=-bvhs>IiHE0e3S!j}k1 z#9r-7?q>U$5B5Q=9Bepnd`ND~GS%uiPEI_n6@@Kq#Of&RBj=0vtno_!cB^aj@mztv z8A`!d>L@VU+61}?sMJMu_&Yw4g7OTdAeTCzc#c3xKm{d{+y5jwi@t3He+_c{l$&be z{uzVjq>e`QJ|7=H`r)D)e5{{9_0*tY8;RW`_;P%7fvvpSPlNH}ptUxZ{|bAz`uYkx?8(IrZ|IWV!aH=&&UTO8Q+Mn3-;)53{cN7Tmc=fok>0-k z?sk;W>He#)zSZ>Kl`QcJZ^t!X?iB+R+U>lr#z2N^IOjm-ys6BFv^1pJaJ~DH+s{rT zZe?UABX= McMZR(H|V`S0F|4%2LJ#7 literal 0 HcmV?d00001 diff --git a/apps/frontend/src/components/launches/providers/high.order.provider.tsx b/apps/frontend/src/components/launches/providers/high.order.provider.tsx new file mode 100644 index 00000000..f823d2e8 --- /dev/null +++ b/apps/frontend/src/components/launches/providers/high.order.provider.tsx @@ -0,0 +1,144 @@ +'use client'; + +import { FC, useCallback, useEffect, useState } from 'react'; +import { Button } from '@gitroom/react/form/button'; +import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; +import MDEditor from '@uiw/react-md-editor'; +import { useHideTopEditor } from '@gitroom/frontend/components/launches/helpers/use.hide.top.editor'; +import { useValues } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { FormProvider } from 'react-hook-form'; +import { useMoveToIntegrationListener } from '@gitroom/frontend/components/launches/helpers/use.move.to.integration'; + +// This is a simple function that if we edit in place, we hide the editor on top +export const EditorWrapper: FC = (props) => { + const showHide = useHideTopEditor(); + useEffect(() => { + showHide.hide(); + return () => { + showHide.show(); + }; + }, []); + + return null; +}; + +export const withProvider = (SettingsComponent: FC, PreviewComponent: FC) => { + return (props: { + identifier: string; + id: string; + value: string[]; + show: boolean; + }) => { + const [editInPlace, setEditInPlace] = useState(false); + const [InPlaceValue, setInPlaceValue] = useState(['']); + const [showTab, setShowTab] = useState(0); + + // in case there is an error on submit, we change to the settings tab for the specific provider + useMoveToIntegrationListener(true, (identifier) => { + if (identifier === props.identifier) { + setShowTab(2); + } + }); + + // this is a smart function, it updates the global value without updating the states (too heavy) and set the settings validation + const form = useValues( + props.identifier, + props.id, + editInPlace ? InPlaceValue : props.value + ); + + // change editor value + const changeValue = useCallback( + (index: number) => (newValue: string) => { + return setInPlaceValue((prev) => { + prev[index] = newValue; + return [...prev]; + }); + }, + [InPlaceValue] + ); + + // add another local editor + const addValue = useCallback( + (index: number) => () => { + setInPlaceValue((prev) => { + prev.splice(index + 1, 0, ''); + return [...prev]; + }); + }, + [InPlaceValue] + ); + + // This is a function if we want to switch from the global editor to edit in place + const changeToEditor = useCallback( + (editor: boolean) => async () => { + if ( + editor && + !editInPlace && + !(await deleteDialog( + 'Are you sure you want to edit in place?', + 'Yes, edit in place!' + )) + ) { + return false; + } + setShowTab(editor ? 1 : 0); + if (editor && !editInPlace) { + setEditInPlace(true); + setInPlaceValue(props.value); + } + }, + [props.value, editInPlace] + ); + + if (!props.show) { + return null; + } + + return ( + +

+ {editInPlace && } +
+
+ +
+
+ +
+
+ +
+
+ {showTab === 1 && ( +
+ {InPlaceValue.map((val, index) => ( + <> + 1 ? 200 : 500} + value={val} + preview="edit" + // @ts-ignore + onChange={changeValue(index)} + /> +
+ +
+ + ))} +
+ )} + {showTab === 2 && } + {showTab === 0 && } +
+ + ); + }; +}; diff --git a/apps/frontend/src/components/launches/providers/linkedin.provider.tsx b/apps/frontend/src/components/launches/providers/linkedin.provider.tsx new file mode 100644 index 00000000..122a7e2a --- /dev/null +++ b/apps/frontend/src/components/launches/providers/linkedin.provider.tsx @@ -0,0 +1,93 @@ +import { FC } from 'react'; +import { withProvider } from '@gitroom/frontend/components/launches/providers/high.order.provider'; +import localFont from 'next/font/local'; +import clsx from 'clsx'; +import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; +import { useFormatting } from '@gitroom/frontend/components/launches/helpers/use.formatting'; + +const chirp = localFont({ + src: [ + { + path: './fonts/x/Chirp-Regular.woff2', + weight: '400', + style: 'normal', + }, + { + path: './fonts/x/Chirp-Bold.woff2', + weight: '700', + style: 'normal', + }, + ], +}); + +const LinkedinPreview: FC = (props) => { + const { value: topValue, integration } = useIntegration(); + const newValues = useFormatting(topValue, { + removeMarkdown: true, + saveBreaklines: true, + specialFunc: (text: string) => { + return text.slice(0, 280); + } + }); + + return ( +
+
+ {newValues.map((value, index) => ( +
+
+ x + {index !== topValue.length - 1 && ( +
+ )} +
+
+
+
+ {integration?.name} +
+
+ + + + + +
+
+ @username +
+
+
{value.text}
+
+
+ ))} +
+
+ ); +}; + +const LinkedinSettings: FC = () => { + return
asdfasd
; +}; + +export default withProvider(LinkedinSettings, LinkedinPreview); diff --git a/apps/frontend/src/components/launches/providers/reddit.provider.tsx b/apps/frontend/src/components/launches/providers/reddit.provider.tsx new file mode 100644 index 00000000..1093b14d --- /dev/null +++ b/apps/frontend/src/components/launches/providers/reddit.provider.tsx @@ -0,0 +1,93 @@ +import { FC } from 'react'; +import { withProvider } from '@gitroom/frontend/components/launches/providers/high.order.provider'; +import localFont from 'next/font/local'; +import clsx from 'clsx'; +import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; +import { useFormatting } from '@gitroom/frontend/components/launches/helpers/use.formatting'; + +const chirp = localFont({ + src: [ + { + path: './fonts/x/Chirp-Regular.woff2', + weight: '400', + style: 'normal', + }, + { + path: './fonts/x/Chirp-Bold.woff2', + weight: '700', + style: 'normal', + }, + ], +}); + +const RedditPreview: FC = (props) => { + const { value: topValue, integration } = useIntegration(); + const newValues = useFormatting(topValue, { + removeMarkdown: true, + saveBreaklines: true, + specialFunc: (text: string) => { + return text.slice(0, 280); + } + }); + + return ( +
+
+ {newValues.map((value, index) => ( +
+
+ x + {index !== topValue.length - 1 && ( +
+ )} +
+
+
+
+ {integration?.name} +
+
+ + + + + +
+
+ @username +
+
+
{value.text}
+
+
+ ))} +
+
+ ); +}; + +const RedditSettings: FC = () => { + return
asdfasd
; +}; + +export default withProvider(RedditSettings, RedditPreview); diff --git a/apps/frontend/src/components/launches/providers/show.all.providers.tsx b/apps/frontend/src/components/launches/providers/show.all.providers.tsx new file mode 100644 index 00000000..39e72fb8 --- /dev/null +++ b/apps/frontend/src/components/launches/providers/show.all.providers.tsx @@ -0,0 +1,28 @@ +import {FC} from "react"; +import {Integrations} from "@gitroom/frontend/components/launches/calendar.context"; +import DevtoProvider from "@gitroom/frontend/components/launches/providers/devto.provider"; +import XProvider from "@gitroom/frontend/components/launches/providers/x.provider"; +import LinkedinProvider from "@gitroom/frontend/components/launches/providers/linkedin.provider"; +import RedditProvider from "@gitroom/frontend/components/launches/providers/reddit.provider"; + +const Providers = [ + {identifier: 'devto', component: DevtoProvider}, + {identifier: 'x', component: XProvider}, + {identifier: 'linkedin', component: LinkedinProvider}, + {identifier: 'reddit', component: RedditProvider}, +]; + +export const ShowAllProviders: FC<{integrations: Integrations[], value: string[], selectedProvider?: Integrations}> = (props) => { + const {integrations, value, selectedProvider} = props; + return ( + <> + {integrations.map((integration) => { + const {component: ProviderComponent} = Providers.find(provider => provider.identifier === integration.identifier) || {component: null}; + if (!ProviderComponent || integrations.map(p => p.id).indexOf(selectedProvider?.id!) === -1) { + return null; + } + return ; + })} + + ) +} \ No newline at end of file diff --git a/apps/frontend/src/components/launches/providers/x.provider.tsx b/apps/frontend/src/components/launches/providers/x.provider.tsx new file mode 100644 index 00000000..b61a9003 --- /dev/null +++ b/apps/frontend/src/components/launches/providers/x.provider.tsx @@ -0,0 +1,97 @@ +import { FC } from 'react'; +import { withProvider } from '@gitroom/frontend/components/launches/providers/high.order.provider'; +import localFont from 'next/font/local'; +import clsx from 'clsx'; +import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; +import { useFormatting } from '@gitroom/frontend/components/launches/helpers/use.formatting'; +import {useSettings} from "@gitroom/frontend/components/launches/helpers/use.values"; + +const chirp = localFont({ + src: [ + { + path: './fonts/x/Chirp-Regular.woff2', + weight: '400', + style: 'normal', + }, + { + path: './fonts/x/Chirp-Bold.woff2', + weight: '700', + style: 'normal', + }, + ], +}); + +const XPreview: FC = (props) => { + const { value: topValue, integration } = useIntegration(); + const newValues = useFormatting(topValue, { + removeMarkdown: true, + saveBreaklines: true, + specialFunc: (text: string) => { + return text.slice(0, 280); + } + }); + + return ( +
+
+ {newValues.map((value, index) => ( +
+
+ x + {index !== topValue.length - 1 && ( +
+ )} +
+
+
+
+ {integration?.name} +
+
+ + + + + +
+
+ @username +
+
+
{value.text}
+
+
+ ))} +
+
+ ); +}; + +const XSettings: FC = () => { + const settings = useSettings({ + + }); + return
asdfasd
; +}; + +export default withProvider(XSettings, XPreview); diff --git a/apps/frontend/src/components/layout/layout.settings.tsx b/apps/frontend/src/components/layout/layout.settings.tsx index 55a6f16c..4fda7403 100644 --- a/apps/frontend/src/components/layout/layout.settings.tsx +++ b/apps/frontend/src/components/layout/layout.settings.tsx @@ -4,30 +4,33 @@ import {headers} from "next/headers"; import {ContextWrapper} from "@gitroom/frontend/components/layout/user.context"; import {NotificationComponent} from "@gitroom/frontend/components/notifications/notification.component"; import {TopMenu} from "@gitroom/frontend/components/layout/top.menu"; +import {MantineWrapper} from "@gitroom/react/helpers/mantine.wrapper"; export const LayoutSettings = ({children}: {children: ReactNode}) => { const user = JSON.parse(headers().get('user')!); return ( -
-
-
- Gitroom + +
+
+
+ Gitroom +
+ +
+ +
- -
- -
-
-
-
- - <div className="flex flex-1 flex-col"> - {children} + <div className="flex-1 flex"> + <div className="flex-1 rounded-3xl px-[23px] py-[17px] flex flex-col"> + <Title /> + <div className="flex flex-1 flex-col"> + {children} + </div> </div> </div> </div> - </div> + </MantineWrapper> </ContextWrapper> ); } \ No newline at end of file diff --git a/apps/frontend/src/components/settings/github.component.tsx b/apps/frontend/src/components/settings/github.component.tsx index 8b74ec28..fabd714a 100644 --- a/apps/frontend/src/components/settings/github.component.tsx +++ b/apps/frontend/src/components/settings/github.component.tsx @@ -3,15 +3,26 @@ import Image from "next/image"; import {Button} from "@gitroom/react/form/button"; import {FC, useCallback, useEffect, useState} from "react"; import {useFetch} from "@gitroom/helpers/utils/custom.fetch"; +import {deleteDialog} from "@gitroom/react/helpers/delete.dialog"; -const ConnectedComponent: FC<{id: string, login: string}> = (props) => { - const {id, login} = props; +const ConnectedComponent: FC<{id: string, login: string, deleteRepository: () => void}> = (props) => { + const {id, login, deleteRepository} = props; + const fetch = useFetch(); + const disconnect = useCallback(async () => { + if (!await deleteDialog('Are you sure you want to disconnect this repository?')) { + return ; + } + deleteRepository(); + await fetch(`/settings/repository/${id}`, { + method: 'DELETE' + }); + }, []); return ( <div className="my-[16px] mt-[16px] h-[90px] bg-sixth border-fifth border rounded-[4px] p-[24px]"> <div className="flex items-center gap-[8px] font-[Inter]"> <div><Image src="/icons/github.svg" alt="GitHub" width={40} height={40}/></div> <div className="flex-1"><strong>Connected:</strong> {login}</div> - <Button>Disconnect</Button> + <Button onClick={disconnect}>Disconnect</Button> </div> </div> ); @@ -93,10 +104,18 @@ export const GithubComponent: FC<{ organizations: Array<{ login: string, id: str if (git.id === g.id) { return {id: g.id, login: name}; } - return g; + return git; }) }); - }, []); + }, [githubState]); + + const deleteConnect = useCallback((g: {id: string, login: string}) => () => { + setGithubState((gitlibs) => { + return gitlibs.filter((git, index) => { + return git.id !== g.id; + }) + }); + }, [githubState]); return ( <> @@ -105,7 +124,7 @@ export const GithubComponent: FC<{ organizations: Array<{ login: string, id: str {!g.login ? ( <ConnectComponent setConnected={setConnected(g)} organizations={organizations} {...g} /> ): ( - <ConnectedComponent {...g} /> + <ConnectedComponent deleteRepository={deleteConnect(g)} {...g} /> )} </> ))} diff --git a/apps/frontend/tailwind.config.js b/apps/frontend/tailwind.config.js index 6d4c00c8..84babd43 100644 --- a/apps/frontend/tailwind.config.js +++ b/apps/frontend/tailwind.config.js @@ -20,12 +20,20 @@ module.exports = { primary: '#000', secondary: '#090B13', third: '#080B13', - forth: '#262373', - fifth: '#172034', + forth: '#612AD5', + fifth: '#28344F', sixth: '#0B101B', gray: '#8C8C8C', + input: '#131B2C', + inputText: '#64748B', + tableBorder: '#1F2941' + }, + gridTemplateColumns: { + '13': 'repeat(13, minmax(0, 1fr));' } }, }, - plugins: [], + plugins: [ + require('tailwind-scrollbar') + ], }; \ No newline at end of file diff --git a/apps/workers/src/app/app.module.ts b/apps/workers/src/app/app.module.ts index f0ba9863..99436788 100644 --- a/apps/workers/src/app/app.module.ts +++ b/apps/workers/src/app/app.module.ts @@ -6,12 +6,13 @@ import {DatabaseModule} from "@gitroom/nestjs-libraries/database/prisma/database import {BullMqModule} from "@gitroom/nestjs-libraries/bull-mq-transport/bull-mq.module"; import {ioRedis} from "@gitroom/nestjs-libraries/redis/redis.service"; import {TrendingService} from "@gitroom/nestjs-libraries/services/trending.service"; +import {PostsController} from "@gitroom/workers/app/posts.controller"; @Module({ imports: [RedisModule, DatabaseModule, BullMqModule.forRoot({ connection: ioRedis })], - controllers: [StarsController], + controllers: [StarsController, PostsController], providers: [TrendingService], }) export class AppModule {} diff --git a/apps/workers/src/app/posts.controller.ts b/apps/workers/src/app/posts.controller.ts new file mode 100644 index 00000000..baaa930d --- /dev/null +++ b/apps/workers/src/app/posts.controller.ts @@ -0,0 +1,15 @@ +import {Controller} from '@nestjs/common'; +import {EventPattern, Transport} from '@nestjs/microservices'; +import {PostsService} from "@gitroom/nestjs-libraries/database/prisma/posts/posts.service"; + +@Controller() +export class PostsController { + constructor( + private _postsService: PostsService + ) { + } + @EventPattern('post', Transport.REDIS) + async checkStars(data: {id: string}) { + return this._postsService.post(data.id); + } +} diff --git a/libraries/nestjs-libraries/src/bull-mq-transport/bull-mq.module.ts b/libraries/nestjs-libraries/src/bull-mq-transport/bull-mq.module.ts index be6f4c10..151bfca2 100644 --- a/libraries/nestjs-libraries/src/bull-mq-transport/bull-mq.module.ts +++ b/libraries/nestjs-libraries/src/bull-mq-transport/bull-mq.module.ts @@ -1,9 +1,7 @@ -import { DynamicModule, Module } from '@nestjs/common'; +import { DynamicModule } from '@nestjs/common'; import { BullMqCoreModule } from './bull-mq-core.module'; import { IBullMqModuleOptionsAsync } from './interfaces/bull-mq-module-options-async.interface'; import { IBullMqModuleOptions } from './interfaces/bull-mq-module-options.interface'; - -@Module({}) export class BullMqModule { static forRoot(options: IBullMqModuleOptions): DynamicModule { return { diff --git a/libraries/nestjs-libraries/src/bull-mq-transport/client/bull-mq.client.ts b/libraries/nestjs-libraries/src/bull-mq-transport/client/bull-mq.client.ts index ee428a42..e488ce49 100644 --- a/libraries/nestjs-libraries/src/bull-mq-transport/client/bull-mq.client.ts +++ b/libraries/nestjs-libraries/src/bull-mq-transport/client/bull-mq.client.ts @@ -74,10 +74,21 @@ export class BullMqClient extends ClientProxy { return () => void 0; } + async delay(pattern: string, jobId: string, delay: number) { + const queue = this.getQueue(pattern); + return queue.getJob(jobId).then((job) => job?.changeDelay(delay)); + } + + async delete(pattern: string, jobId: string) { + const queue = this.getQueue(pattern); + return queue.getJob(jobId).then((job) => job?.remove()); + } + protected async dispatchEvent( packet: ReadPacket<IBullMqEvent<any>>, ): Promise<any> { const queue = this.getQueue(packet.pattern); + console.log(packet); await queue.add(packet.pattern, packet.data, { jobId: packet.data.id ?? v4(), ...packet.data.options, diff --git a/libraries/nestjs-libraries/src/database/prisma/database.module.ts b/libraries/nestjs-libraries/src/database/prisma/database.module.ts index 6c8bacf6..549764f1 100644 --- a/libraries/nestjs-libraries/src/database/prisma/database.module.ts +++ b/libraries/nestjs-libraries/src/database/prisma/database.module.ts @@ -11,6 +11,9 @@ import {SubscriptionRepository} from "@gitroom/nestjs-libraries/database/prisma/ import {NotificationService} from "@gitroom/nestjs-libraries/notifications/notification.service"; import {IntegrationService} from "@gitroom/nestjs-libraries/database/prisma/integrations/integration.service"; import {IntegrationRepository} from "@gitroom/nestjs-libraries/database/prisma/integrations/integration.repository"; +import {PostsService} from "@gitroom/nestjs-libraries/database/prisma/posts/posts.service"; +import {PostsRepository} from "@gitroom/nestjs-libraries/database/prisma/posts/posts.repository"; +import {IntegrationManager} from "@gitroom/nestjs-libraries/integrations/integration.manager"; @Global() @Module({ @@ -29,7 +32,10 @@ import {IntegrationRepository} from "@gitroom/nestjs-libraries/database/prisma/i SubscriptionRepository, NotificationService, IntegrationService, - IntegrationRepository + IntegrationRepository, + PostsService, + PostsRepository, + IntegrationManager ], get exports() { return this.providers; diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts index 691627b5..50a58631 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts @@ -8,13 +8,14 @@ export class IntegrationRepository { ) { } - createIntegration(org: string, name: string, type: 'article' | 'social' , internalId: string, provider: string, token: string, refreshToken = '', expiresIn = 999999999) { + createIntegration(org: string, name: string, picture: string, type: 'article' | 'social' , internalId: string, provider: string, token: string, refreshToken = '', expiresIn = 999999999) { return this._integration.model.integration.create({ data: { type: type as any, name, providerIdentifier: provider, token, + picture, refreshToken, ...expiresIn ? {tokenExpiration: new Date(Date.now() + expiresIn * 1000)} :{}, internalId, @@ -22,4 +23,12 @@ export class IntegrationRepository { } }) } + + getIntegrationsList(org: string) { + return this._integration.model.integration.findMany({ + where: { + organizationId: org + } + }); + } } \ No newline at end of file diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts index 140a10ec..692fdfa4 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -7,7 +7,11 @@ export class IntegrationService { private _integrationRepository: IntegrationRepository, ) { } - createIntegration(org: string, name: string, type: 'article' | 'social' , internalId: string, provider: string, token: string, refreshToken = '', expiresIn?: number) { - return this._integrationRepository.createIntegration(org, name, type, internalId, provider, token, refreshToken, expiresIn); + createIntegration(org: string, name: string, picture: string, type: 'article' | 'social' , internalId: string, provider: string, token: string, refreshToken = '', expiresIn?: number) { + return this._integrationRepository.createIntegration(org, name, picture, type, internalId, provider, token, refreshToken, expiresIn); + } + + getIntegrationsList(org: string) { + return this._integrationRepository.getIntegrationsList(org); } } diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts new file mode 100644 index 00000000..d12e152b --- /dev/null +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -0,0 +1,71 @@ +import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/prisma.service'; +import { Injectable } from '@nestjs/common'; +import { Post as PostBody } from '@gitroom/nestjs-libraries/dtos/posts/create.post.dto'; +import dayjs from 'dayjs'; +import { Integration, Post } from '@prisma/client'; + +@Injectable() +export class PostsRepository { + constructor(private _post: PrismaRepository<'post'>) {} + + getPost(id: string, includeIntegration = false) { + return this._post.model.post.findUnique({ + where: { + id, + }, + include: { + ...(includeIntegration ? { integration: true } : {}), + childrenPost: true, + }, + }); + } + + updatePost(id: string, postId: string, releaseURL: string) { + return this._post.model.post.update({ + where: { + id, + }, + data: { + state: 'PUBLISHED', + releaseURL, + releaseId: postId, + }, + }); + } + + async createPost(orgId: string, date: string, body: PostBody) { + const posts: Post[] = []; + for (const value of body.value) { + posts.push( + await this._post.model.post.create({ + data: { + publishDate: dayjs(date).toDate(), + integration: { + connect: { + id: body.integration.id, + organizationId: orgId, + }, + }, + ...(posts.length + ? { + parentPost: { + connect: { + id: posts[posts.length - 1]?.id, + }, + }, + } + : {}), + content: value, + organization: { + connect: { + id: orgId, + }, + }, + }, + }) + ); + } + + return posts; + } +} diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts new file mode 100644 index 00000000..094f9aba --- /dev/null +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -0,0 +1,92 @@ +import { Injectable } from '@nestjs/common'; +import { PostsRepository } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.repository'; +import { CreatePostDto } from '@gitroom/nestjs-libraries/dtos/posts/create.post.dto'; +import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport/client/bull-mq.client'; +import dayjs from 'dayjs'; +import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; +import { Integration, Post } from '@prisma/client'; + +type PostWithConditionals = Post & { + integration?: Integration; + childrenPost: Post[]; +}; + +@Injectable() +export class PostsService { + constructor( + private _postRepository: PostsRepository, + private _workerServiceProducer: BullMqClient, + private _integrationManager: IntegrationManager + ) {} + + async getPostsRecursively( + id: string, + includeIntegration = false + ): Promise<PostWithConditionals[]> { + const post = await this._postRepository.getPost(id, includeIntegration); + return [ + post!, + ...(post?.childrenPost?.length + ? await this.getPostsRecursively(post.childrenPost[0].id) + : []), + ]; + } + + async post(id: string) { + const [firstPost, ...morePosts] = await this.getPostsRecursively(id, true); + if (!firstPost) { + return; + } + + if (firstPost.integration?.type === 'article') { + return this.postArticle(firstPost.integration!, [firstPost, ...morePosts]); + } + + return this.postSocial(firstPost.integration!, [firstPost, ...morePosts]); + } + + private async postSocial(integration: Integration, posts: Post[]) { + const getIntegration = this._integrationManager.getSocialIntegration(integration.providerIdentifier); + if (!getIntegration) { + return; + } + + const publishedPosts = await getIntegration.post(integration.internalId, integration.token, posts.map(p => ({ + id: p.id, + message: p.content, + settings: JSON.parse(p.settings || '{}'), + }))); + + for (const post of publishedPosts) { + await this._postRepository.updatePost(post.id, post.postId, post.releaseURL); + } + } + + private async postArticle(integration: Integration, posts: Post[]) { + const getIntegration = this._integrationManager.getArticlesIntegration(integration.providerIdentifier); + if (!getIntegration) { + return; + } + const {postId, releaseURL} = await getIntegration.post(integration.token, posts.map(p => p.content).join('\n\n'), JSON.parse(posts[0].settings || '{}')); + await this._postRepository.updatePost(posts[0].id, postId, releaseURL); + } + + async createPost(orgId: string, body: CreatePostDto) { + for (const post of body.posts) { + const posts = await this._postRepository.createPost( + orgId, + body.date, + post + ); + this._workerServiceProducer.emit('post', { + id: posts[0].id, + options: { + delay: 0 // dayjs(posts[0].publishDate).diff(dayjs(), 'millisecond'), + }, + payload: { + id: posts[0].id, + }, + }); + } + } +} diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index 69396ff9..49b38347 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -128,6 +128,7 @@ model Integration { organizationId String name String organization Organization @relation(fields: [organizationId], references: [id]) + picture String? providerIdentifier String type String token String @@ -183,21 +184,19 @@ model Slots { model Post { id String @id @default(cuid()) state State @default(QUEUE) - queueId String? publishDate DateTime organizationId String - IntegrationId String + integrationId String + content String organization Organization @relation(fields: [organizationId], references: [id]) - Integration Integration @relation(fields: [IntegrationId], references: [id]) + integration Integration @relation(fields: [integrationId], references: [id]) title String? description String? - canonicalUrl String? - canonicalPostId String? parentPostId String? + releaseId String? releaseURL String? - canonicalPost Post? @relation("canonicalPostId", fields: [canonicalPostId], references: [id]) + settings String? parentPost Post? @relation("parentPostId", fields: [parentPostId], references: [id]) - canonicalChildren Post[] @relation("canonicalPostId") childrenPost Post[] @relation("parentPostId") tags PostTag[] media PostMedia[] @@ -207,7 +206,8 @@ model Post { enum State { QUEUE - SENT + PUBLISHED + ERROR DRAFT } diff --git a/libraries/nestjs-libraries/src/database/prisma/stars/stars.service.ts b/libraries/nestjs-libraries/src/database/prisma/stars/stars.service.ts index 404c7deb..d19aa0d3 100644 --- a/libraries/nestjs-libraries/src/database/prisma/stars/stars.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/stars/stars.service.ts @@ -4,7 +4,7 @@ import {chunk, groupBy} from "lodash"; import dayjs from "dayjs"; import {NotificationService} from "@gitroom/nestjs-libraries/notifications/notification.service"; import {StarsListDto} from "@gitroom/nestjs-libraries/dtos/analytics/stars.list.dto"; -import * as console from "console"; +import {BullMqClient} from "@gitroom/nestjs-libraries/bull-mq-transport/client/bull-mq.client"; enum Inform { Removed, New, @@ -14,7 +14,8 @@ enum Inform { export class StarsService { constructor( private _starsRepository: StarsRepository, - private _notificationsService: NotificationService + private _notificationsService: NotificationService, + private _workerServiceProducer: BullMqClient ){} getGitHubRepositoriesByOrgId(org: string) { @@ -49,7 +50,6 @@ export class StarsService { } async syncProcess(login: string, page = 1) { - console.log('processing', login, page); const starsRequest = await fetch(`https://api.github.com/repos/${login}/stargazers?page=${page}&per_page=100`, { headers: { Accept: 'application/vnd.github.v3.star+json', @@ -221,6 +221,7 @@ export class StarsService { } async updateGitHubLogin(orgId: string, id: string, login: string) { + this._workerServiceProducer.emit('sync_all_stars', {payload: {login}}).subscribe(); return this._starsRepository.updateGitHubLogin(orgId, id, login); } diff --git a/libraries/nestjs-libraries/src/dtos/integrations/api.key.dto.ts b/libraries/nestjs-libraries/src/dtos/integrations/api.key.dto.ts new file mode 100644 index 00000000..005d0dd6 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/integrations/api.key.dto.ts @@ -0,0 +1,9 @@ +import {IsString, MinLength} from "class-validator"; + +export class ApiKeyDto { + @IsString() + @MinLength(4, { + message: 'Must be at least 4 characters' + }) + api: string; +} \ No newline at end of file diff --git a/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts new file mode 100644 index 00000000..b47b48c0 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts @@ -0,0 +1,47 @@ +import {ArrayMinSize, IsArray, IsDateString, IsDefined, IsString, ValidateNested} from "class-validator"; +import {Type} from "class-transformer"; +import {DevToSettingsDto} from "@gitroom/nestjs-libraries/dtos/posts/providers-settings/dev.to.settings.dto"; + +export class EmptySettings {} +export class Integration { + @IsDefined() + @IsString() + id: string +} + +export class Post { + @IsDefined() + @Type(() => Integration) + @ValidateNested() + integration: Integration; + + @IsDefined() + @ArrayMinSize(1) + @IsArray() + @IsString({ each: true }) + value: string[]; + + @Type(() => EmptySettings, { + keepDiscriminatorProperty: true, + discriminator: { + property: '__type', + subTypes: [ + { value: DevToSettingsDto, name: 'devto' }, + ], + }, + }) + settings: DevToSettingsDto +} + +export class CreatePostDto { + @IsDefined() + @IsDateString() + date: string; + + @IsDefined() + @Type(() => Post) + @IsArray() + @ValidateNested({each: true}) + @ArrayMinSize(1) + posts: Post[] +} diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts new file mode 100644 index 00000000..d1e91366 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts @@ -0,0 +1,7 @@ +import {DevToSettingsDto} from "@gitroom/nestjs-libraries/dtos/posts/providers-settings/dev.to.settings.dto"; +export const allProvidersSettings = [{ + identifier: 'devto', + validator: DevToSettingsDto +}]; + +export type AllProvidersSettings = DevToSettingsDto; \ No newline at end of file diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.settings.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.settings.dto.ts new file mode 100644 index 00000000..433a24d5 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/dev.to.settings.dto.ts @@ -0,0 +1,24 @@ +import {IsArray, IsDefined, IsOptional, IsString} from "class-validator"; + +export class DevToSettingsDto { + @IsString() + @IsDefined() + title: string; + + @IsString() + @IsOptional() + main_image?: number; + + @IsString() + canonical: string; + + @IsString({ + each: true + }) + @IsArray() + tags: string[]; + + @IsString() + @IsOptional() + organization?: string; +} \ No newline at end of file diff --git a/libraries/nestjs-libraries/src/integrations/article/article.integrations.interface.ts b/libraries/nestjs-libraries/src/integrations/article/article.integrations.interface.ts index 652522f9..d63d509e 100644 --- a/libraries/nestjs-libraries/src/integrations/article/article.integrations.interface.ts +++ b/libraries/nestjs-libraries/src/integrations/article/article.integrations.interface.ts @@ -1,6 +1,6 @@ export interface ArticleIntegrationsInterface { - authenticate(token: string): Promise<{id: string, name: string, token: string}>; - publishPost(token: string, content: string): Promise<string>; + authenticate(token: string): Promise<{id: string, name: string, token: string, picture: string}>; + post(token: string, content: string, settings: object): Promise<{postId: string, releaseURL: string}>; } export interface ArticleProvider extends ArticleIntegrationsInterface { diff --git a/libraries/nestjs-libraries/src/integrations/article/dev.to.provider.ts b/libraries/nestjs-libraries/src/integrations/article/dev.to.provider.ts index 76ac339e..f13ff12e 100644 --- a/libraries/nestjs-libraries/src/integrations/article/dev.to.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/article/dev.to.provider.ts @@ -3,8 +3,8 @@ import {ArticleProvider} from "@gitroom/nestjs-libraries/integrations/article/ar export class DevToProvider implements ArticleProvider { identifier = 'devto'; name = 'Dev.to'; - async authenticate(token: string): Promise<{ id: string; name: string; token: string; }> { - const {name, id} = await (await fetch('https://dev.to/api/users/me', { + async authenticate(token: string) { + const {name, id, profile_image} = await (await fetch('https://dev.to/api/users/me', { headers: { 'api-key': token } @@ -13,11 +13,15 @@ export class DevToProvider implements ArticleProvider { return { id, name, - token + token, + picture: profile_image } } - async publishPost(token: string, content: string): Promise<string> { - return ''; + async post(token: string, content: string, settings: object) { + return { + postId: '123', + releaseURL: 'https://dev.to' + } } } \ No newline at end of file diff --git a/libraries/nestjs-libraries/src/integrations/article/hashnode.provider.ts b/libraries/nestjs-libraries/src/integrations/article/hashnode.provider.ts index c0b96347..73bc245e 100644 --- a/libraries/nestjs-libraries/src/integrations/article/hashnode.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/article/hashnode.provider.ts @@ -3,9 +3,9 @@ import {ArticleIntegrationsInterface, ArticleProvider} from "@gitroom/nestjs-lib export class HashnodeProvider implements ArticleProvider { identifier = 'hashnode'; name = 'Hashnode'; - async authenticate(token: string): Promise<{ id: string; name: string; token: string; }> { + async authenticate(token: string) { try { - const {data: {me: {name, id}}} = await (await fetch('https://gql.hashnode.com', { + const {data: {me: {name, id, profilePicture}}} = await (await fetch('https://gql.hashnode.com', { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -16,7 +16,8 @@ export class HashnodeProvider implements ArticleProvider { query { me { name, - id + id, + profilePicture } } ` @@ -24,19 +25,23 @@ export class HashnodeProvider implements ArticleProvider { })).json(); return { - id, name, token + id, name, token, picture: profilePicture } } catch (err) { return { id: '', name: '', - token: '' + token: '', + picture: '' } } } - async publishPost(token: string, content: string): Promise<string> { - return ''; + async post(token: string, content: string, settings: object) { + return { + postId: '123', + releaseURL: 'https://dev.to' + } } } \ No newline at end of file diff --git a/libraries/nestjs-libraries/src/integrations/article/medium.provider.ts b/libraries/nestjs-libraries/src/integrations/article/medium.provider.ts index 005534ee..a28d1de0 100644 --- a/libraries/nestjs-libraries/src/integrations/article/medium.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/article/medium.provider.ts @@ -4,8 +4,8 @@ export class MediumProvider implements ArticleProvider { identifier = 'medium'; name = 'Medium'; - async authenticate(token: string): Promise<{ id: string; name: string; token: string; }> { - const {data: {name, id}} = await (await fetch('https://api.medium.com/v1/me', { + async authenticate(token: string) { + const {data: {name, id, imageUrl}} = await (await fetch('https://api.medium.com/v1/me', { headers: { Authorization: `Bearer ${token}` } @@ -14,11 +14,15 @@ export class MediumProvider implements ArticleProvider { return { id, name, - token + token, + picture: imageUrl } } - async publishPost(token: string, content: string): Promise<string> { - return ''; + async post(token: string, content: string, settings: object) { + return { + postId: '123', + releaseURL: 'https://dev.to' + } } } \ No newline at end of file diff --git a/libraries/nestjs-libraries/src/integrations/integration.manager.ts b/libraries/nestjs-libraries/src/integrations/integration.manager.ts index d19a33a1..17adf2ee 100644 --- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts +++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts @@ -22,6 +22,12 @@ const articleIntegrationList = [ @Injectable() export class IntegrationManager { + getAllIntegrations() { + return { + social: socialIntegrationList.map(p => ({name: p.name, identifier: p.identifier})), + article: articleIntegrationList.map(p => ({name: p.name, identifier: p.identifier})), + }; + } getAllowedSocialsIntegrations() { return socialIntegrationList.map(p => p.identifier); } diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index 7c85c686..b745221f 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -1,82 +1,202 @@ -import {AuthTokenDetails, PostDetails, PostResponse, SocialProvider} from "@gitroom/nestjs-libraries/integrations/social/social.integrations.interface"; -import {makeId} from "@gitroom/nestjs-libraries/services/make.is"; +import { + AuthTokenDetails, + PostDetails, + PostResponse, + SocialProvider, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; export class LinkedinProvider implements SocialProvider { - identifier = 'linkedin'; - name = 'LinkedIn'; - async refreshToken(refresh_token: string): Promise<AuthTokenDetails> { - const {access_token: accessToken, refresh_token: refreshToken} = await (await fetch('https://www.linkedin.com/oauth/v2/accessToken', { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' + identifier = 'linkedin'; + name = 'LinkedIn'; + async refreshToken(refresh_token: string): Promise<AuthTokenDetails> { + const { access_token: accessToken, refresh_token: refreshToken } = await ( + await fetch('https://www.linkedin.com/oauth/v2/accessToken', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + grant_type: 'refresh_token', + refresh_token, + client_id: process.env.LINKEDIN_CLIENT_ID!, + client_secret: process.env.LINKEDIN_CLIENT_SECRET!, + }), + }) + ).json(); + + const { + name, + sub: id, + picture, + } = await ( + await fetch('https://api.linkedin.com/v2/userinfo', { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }) + ).json(); + + return { + id, + accessToken, + refreshToken, + name, + picture, + }; + } + + async generateAuthUrl() { + const state = makeId(6); + const codeVerifier = makeId(30); + const url = `https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=${ + process.env.LINKEDIN_CLIENT_ID + }&redirect_uri=${encodeURIComponent( + `${process.env.FRONTEND_URL}/integrations/social/linkedin` + )}&state=${state}&scope=${encodeURIComponent( + 'openid profile w_member_social r_liteprofile' + )}`; + return { + url, + codeVerifier, + state, + }; + } + + async authenticate(params: { code: string; codeVerifier: string }) { + const body = new URLSearchParams(); + body.append('grant_type', 'authorization_code'); + body.append('code', params.code); + body.append( + 'redirect_uri', + `${process.env.FRONTEND_URL}/integrations/social/linkedin` + ); + body.append('client_id', process.env.LINKEDIN_CLIENT_ID!); + body.append('client_secret', process.env.LINKEDIN_CLIENT_SECRET!); + + const { + access_token: accessToken, + expires_in: expiresIn, + refresh_token: refreshToken, + } = await ( + await fetch('https://www.linkedin.com/oauth/v2/accessToken', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body, + }) + ).json(); + + const { + name, + sub: id, + picture, + } = await ( + await fetch('https://api.linkedin.com/v2/userinfo', { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }) + ).json(); + + return { + id, + accessToken, + refreshToken, + expiresIn, + name, + picture, + }; + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[] + ): Promise<PostResponse[]> { + const [firstPost, ...restPosts] = postDetails; + console.log('posting'); + const data = await fetch('https://api.linkedin.com/v2/posts', { + method: 'POST', + headers: { + 'X-Restli-Protocol-Version': '2.0.0', + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ + author: `urn:li:person:${id}`, + commentary: firstPost.message, + visibility: 'PUBLIC', + distribution: { + feedDistribution: 'MAIN_FEED', + targetEntities: [], + thirdPartyDistributionChannels: [], + }, + lifecycleState: 'PUBLISHED', + isReshareDisabledByAuthor: false, + // content: { + // // contentEntities: [ + // // { + // // entityLocation: 'URL_OF_THE_CONTENT_TO_SHARE', + // // thumbnails: [ + // // { + // // resolvedUrl: 'URL_OF_THE_THUMBNAIL_IMAGE', + // // }, + // // ], + // // }, + // // ], + // title: firstPost.message, + // }, + // distribution: { + // linkedInDistributionTarget: {}, + // }, + // owner: `urn:li:person:${id}`, + // subject: firstPost.message, + // text: { + // text: firstPost.message, + // }, + }), + }); + + const topPostId = data.headers.get('x-restli-id')!; + const ids = [ + { + status: 'posted', + postId: topPostId, + id: firstPost.id, + releaseURL: `https://www.linkedin.com/feed/update/${topPostId}`, + }, + ]; + for (const post of restPosts) { + const {object} = await (await fetch( + `https://api.linkedin.com/v2/socialActions/${decodeURIComponent( + topPostId + )}/comments`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ + actor: `urn:li:person:${id}`, + object: topPostId, + message: { + text: post.message, }, - body: new URLSearchParams({ - grant_type: 'refresh_token', - refresh_token, - client_id: process.env.LINKEDIN_CLIENT_ID!, - client_secret: process.env.LINKEDIN_CLIENT_SECRET! - }) - })).json() - - const {id, localizedFirstName, localizedLastName} = await (await fetch('https://api.linkedin.com/v2/me', { - headers: { - Authorization: `Bearer ${accessToken}` - } - })).json(); - - return { - id, - accessToken, - refreshToken, - name: `${localizedFirstName} ${localizedLastName}` + }), } + )).json() + + ids.push({ + status: 'posted', + postId: object, + id: post.id, + releaseURL: `https://www.linkedin.com/embed/feed/update/${object}`, + }); } - async generateAuthUrl() { - const state = makeId(6); - const codeVerifier = makeId(30); - const url = `https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=${process.env.LINKEDIN_CLIENT_ID}&redirect_uri=${encodeURIComponent(`${process.env.FRONTEND_URL}/integrations/social/linkedin`)}&state=${state}&scope=${encodeURIComponent('openid profile w_member_social')}`; - return { - url, - codeVerifier, - state - } - } - - async authenticate(params: {code: string, codeVerifier: string}) { - const body = new URLSearchParams(); - body.append('grant_type', 'authorization_code'); - body.append('code', params.code); - body.append('redirect_uri', `${process.env.FRONTEND_URL}/integrations/social/linkedin`); - body.append('client_id', process.env.LINKEDIN_CLIENT_ID!); - body.append('client_secret', process.env.LINKEDIN_CLIENT_SECRET!); - - const {access_token: accessToken, expires_in: expiresIn, refresh_token: refreshToken, ...data} = await (await fetch('https://www.linkedin.com/oauth/v2/accessToken', { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - }, - body - })).json() - - console.log({accessToken, expiresIn, refreshToken, data}); - - const {name, sub: id} = await (await fetch('https://api.linkedin.com/v2/userinfo', { - headers: { - Authorization: `Bearer ${accessToken}` - } - })).json(); - - return { - id, - accessToken, - refreshToken, - expiresIn, - name - } - } - - async schedulePost(accessToken: string, postDetails: PostDetails[]): Promise<PostResponse[]> { - return []; - } -} \ No newline at end of file + return ids; + } +} diff --git a/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts b/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts index 83fe495b..b04aea39 100644 --- a/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts @@ -17,7 +17,7 @@ export class RedditProvider implements SocialProvider { }) })).json(); - const {name, id} = await (await fetch('https://oauth.reddit.com/api/v1/me', { + const {name, id, icon_img} = await (await fetch('https://oauth.reddit.com/api/v1/me', { headers: { Authorization: `Bearer ${accessToken}` } @@ -28,7 +28,8 @@ export class RedditProvider implements SocialProvider { name, accessToken, refreshToken: newRefreshToken, - expiresIn + expiresIn, + picture: icon_img.split('?')[0] } } @@ -57,7 +58,7 @@ export class RedditProvider implements SocialProvider { }) })).json(); - const {name, id} = await (await fetch('https://oauth.reddit.com/api/v1/me', { + const {name, id, icon_img} = await (await fetch('https://oauth.reddit.com/api/v1/me', { headers: { Authorization: `Bearer ${accessToken}` } @@ -68,14 +69,28 @@ export class RedditProvider implements SocialProvider { name, accessToken, refreshToken, - expiresIn + expiresIn, + picture: icon_img.split('?')[0] } } - async schedulePost(accessToken: string, postDetails: PostDetails[]): Promise<PostResponse[]> { - return [{ - postId: '123', - status: 'scheduled' - }]; + async post(id: string, accessToken: string, postDetails: PostDetails[]): Promise<PostResponse[]> { + const [post, ...rest] = postDetails; + const response = await fetch('https://oauth.reddit.com/api/submit', { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: new URLSearchParams({ + title: 'test', + kind: 'self', + text: post.message, + sr: '/r/gitroom' + }) + }); + + console.log(response); + return []; } } \ No newline at end of file diff --git a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts index 13b62f9c..aa43b38b 100644 --- a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts +++ b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts @@ -16,22 +16,26 @@ export type AuthTokenDetails = { accessToken: string; // The obtained access token refreshToken?: string; // The refresh token, if applicable expiresIn?: number; // The duration in seconds for which the access token is valid + picture?: string; }; export interface ISocialMediaIntegration { - schedulePost(accessToken: string, postDetails: PostDetails[]): Promise<PostResponse[]>; // Schedules a new post + post(id: string, accessToken: string, postDetails: PostDetails[]): Promise<PostResponse[]>; // Schedules a new post } export type PostResponse = { + id: string; // The db internal id of the post postId: string; // The ID of the scheduled post returned by the platform + releaseURL: string; // The URL of the post on the platform status: string; // Status of the operation or initial post status }; export type PostDetails = { + id: string; message: string; - scheduledTime: Date; // The time when the post should be published - media?: MediaContent[]; // Optional array of media content to be attached with the post - poll?: PollDetails; // Optional poll details + settings: object; + media?: MediaContent[]; + poll?: PollDetails; }; export type PollDetails = { diff --git a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts index a7031a18..43da34d3 100644 --- a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts @@ -1,67 +1,105 @@ import { TwitterApi } from 'twitter-api-v2'; -import {AuthTokenDetails, PostDetails, PostResponse, SocialProvider} from "@gitroom/nestjs-libraries/integrations/social/social.integrations.interface"; +import { + AuthTokenDetails, + PostDetails, + PostResponse, + SocialProvider, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; export class XProvider implements SocialProvider { - identifier = 'x'; - name = 'X'; - async refreshToken(refreshToken: string): Promise<AuthTokenDetails> { - const startingClient = new TwitterApi({ clientId: process.env.TWITTER_CLIENT_ID!, clientSecret: process.env.TWITTER_CLIENT_SECRET! }); - const { accessToken, refreshToken: newRefreshToken, expiresIn, client } = await startingClient.refreshOAuth2Token(refreshToken); - const {data: {id, name}} = await client.v2.me(); - return { - id, - name, - accessToken, - refreshToken: newRefreshToken, - expiresIn - } + identifier = 'x'; + name = 'X'; + async refreshToken(refreshToken: string): Promise<AuthTokenDetails> { + const startingClient = new TwitterApi({ + clientId: process.env.TWITTER_CLIENT_ID!, + clientSecret: process.env.TWITTER_CLIENT_SECRET!, + }); + const { + accessToken, + refreshToken: newRefreshToken, + expiresIn, + client, + } = await startingClient.refreshOAuth2Token(refreshToken); + const { + data: { id, name, profile_image_url }, + } = await client.v2.me(); + return { + id, + name, + accessToken, + refreshToken: newRefreshToken, + expiresIn, + picture: profile_image_url, + }; + } + + async generateAuthUrl() { + const client = new TwitterApi({ + clientId: process.env.TWITTER_CLIENT_ID!, + clientSecret: process.env.TWITTER_CLIENT_SECRET!, + }); + const { url, codeVerifier, state } = client.generateOAuth2AuthLink( + process.env.FRONTEND_URL + '/integrations/social/x', + { scope: ['tweet.read', 'users.read', 'tweet.write', 'offline.access'] } + ); + return { + url, + codeVerifier, + state, + }; + } + + async authenticate(params: { code: string; codeVerifier: string }) { + const startingClient = new TwitterApi({ + clientId: process.env.TWITTER_CLIENT_ID!, + clientSecret: process.env.TWITTER_CLIENT_SECRET!, + }); + const { accessToken, refreshToken, expiresIn, client } = + await startingClient.loginWithOAuth2({ + code: params.code, + codeVerifier: params.codeVerifier, + redirectUri: process.env.FRONTEND_URL + '/integrations/social/x', + }); + + const { + data: { id, name, profile_image_url }, + } = await client.v2.me({ + 'user.fields': 'profile_image_url', + }); + + return { + id, + accessToken, + name, + refreshToken, + expiresIn, + picture: profile_image_url, + }; + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[], + ): Promise<PostResponse[]> { + const client = new TwitterApi(accessToken); + const {data: {username}} = await client.v2.me({ + "user.fields": "username" + }); + const ids: Array<{postId: string, id: string, releaseURL: string}> = []; + for (const post of postDetails) { + const { data }: { data: { id: string } } = await client.v2.tweet({ + text: post.message, + ...(ids.length + ? { reply: { in_reply_to_tweet_id: ids[ids.length - 1].postId } } + : {}), + }); + ids.push({postId: data.id, id: post.id, releaseURL: `https://twitter.com/${username}/status/${data.id}`}); } - async generateAuthUrl() { - const client = new TwitterApi({ clientId: process.env.TWITTER_CLIENT_ID!, clientSecret: process.env.TWITTER_CLIENT_SECRET! }); - const {url, codeVerifier, state} = client.generateOAuth2AuthLink( - process.env.FRONTEND_URL + '/integrations/social/x', - { scope: ['tweet.read', 'users.read', 'tweet.write', 'offline.access'] }); - return { - url, - codeVerifier, - state - } - } - - async authenticate(params: {code: string, codeVerifier: string}) { - const startingClient = new TwitterApi({ clientId: process.env.TWITTER_CLIENT_ID!, clientSecret: process.env.TWITTER_CLIENT_SECRET! }); - const {accessToken, refreshToken, expiresIn, client} = await startingClient.loginWithOAuth2({ - code: params.code, - codeVerifier: params.codeVerifier, - redirectUri: process.env.FRONTEND_URL + '/integrations/social/x' - }); - - const {data: {id, name}} = await client.v2.me(); - - return { - id, - accessToken, - name, - refreshToken, - expiresIn - } - } - - async schedulePost(accessToken: string, postDetails: PostDetails[]): Promise<PostResponse[]> { - const client = new TwitterApi(accessToken); - const ids: string[] = []; - for (const post of postDetails) { - const {data}: {data: {id: string}} = await client.v2.tweet({ - text: post.message, - ...ids.length ? { reply: {in_reply_to_tweet_id: ids[ids.length - 1]} } : {}, - }); - ids.push(data.id); - } - - return ids.map(p => ({ - postId: p, - status: 'posted' - })); - } -} \ No newline at end of file + return ids.map((p) => ({ + ...p, + status: 'posted', + })); + } +} diff --git a/libraries/react-shared-libraries/src/form/button.tsx b/libraries/react-shared-libraries/src/form/button.tsx index 03cb734a..38368c8f 100644 --- a/libraries/react-shared-libraries/src/form/button.tsx +++ b/libraries/react-shared-libraries/src/form/button.tsx @@ -1,8 +1,8 @@ import {ButtonHTMLAttributes, DetailedHTMLProps, FC} from "react"; import {clsx} from "clsx"; -export const Button: FC<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>> = (props) => { +export const Button: FC<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> & {secondary?: boolean}> = (props) => { return ( - <button {...props} type={props.type || 'button'} className={clsx('bg-forth px-[24px] h-[40px] cursor-pointer items-center justify-center flex', props?.className)} /> + <button {...props} type={props.type || 'button'} className={clsx(`${props.secondary ? 'bg-sixth' : 'bg-forth'} px-[24px] h-[40px] cursor-pointer items-center justify-center flex`, props?.className)} /> ) } \ No newline at end of file diff --git a/libraries/react-shared-libraries/src/form/input.tsx b/libraries/react-shared-libraries/src/form/input.tsx new file mode 100644 index 00000000..5b3e578c --- /dev/null +++ b/libraries/react-shared-libraries/src/form/input.tsx @@ -0,0 +1,22 @@ +"use client"; + +import {DetailedHTMLProps, FC, InputHTMLAttributes, useMemo} from "react"; +import clsx from "clsx"; +import {useFormContext} from "react-hook-form"; + +export const Input: FC<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> & {label: string, name: string}> = (props) => { + const {label, className, ...rest} = props; + const form = useFormContext(); + const err = useMemo(() => { + if (!form || !form.formState.errors[props?.name!]) return; + return form?.formState?.errors?.[props?.name!]?.message! as string; + }, [form?.formState?.errors?.[props?.name!]?.message]); + + return ( + <div className="flex flex-col gap-[6px]"> + <div className="font-['Inter'] text-[14px]">{label}</div> + <input {...form.register(props.name)} className={clsx("bg-input h-[44px] px-[16px] outline-none border-fifth border rounded-[4px] text-inputText placeholder-inputText", className)} {...rest} /> + <div className="text-red-400 text-[12px]">{err || <> </>}</div> + </div> + ) +} \ No newline at end of file diff --git a/libraries/react-shared-libraries/src/helpers/delete.dialog.tsx b/libraries/react-shared-libraries/src/helpers/delete.dialog.tsx new file mode 100644 index 00000000..26aa59e1 --- /dev/null +++ b/libraries/react-shared-libraries/src/helpers/delete.dialog.tsx @@ -0,0 +1,14 @@ +import Swal from "sweetalert2"; + +export const deleteDialog = async (message: string, confirmButton?: string) => { + const fire = await Swal.fire({ + title: 'Are you sure?', + text: message, + icon: 'warning', + showCancelButton: true, + confirmButtonText: confirmButton || 'Yes, delete it!', + cancelButtonText: 'No, cancel!', + }); + + return fire.isConfirmed; +} diff --git a/libraries/react-shared-libraries/src/helpers/mantine.wrapper.tsx b/libraries/react-shared-libraries/src/helpers/mantine.wrapper.tsx new file mode 100644 index 00000000..5cb14ca0 --- /dev/null +++ b/libraries/react-shared-libraries/src/helpers/mantine.wrapper.tsx @@ -0,0 +1,19 @@ +"use client"; +import {ReactNode} from "react"; +import {MantineProvider} from "@mantine/core"; +import {ModalsProvider} from "@mantine/modals"; + +export const MantineWrapper = (props: { children: ReactNode }) => { + return ( + <MantineProvider> + <ModalsProvider modalProps={{ + classNames: { + modal: 'bg-primary text-white border-fifth border', + close: 'bg-black hover:bg-black cursor-pointer', + } + }}> + {props.children} + </ModalsProvider> + </MantineProvider> + ) +}; diff --git a/libraries/react-shared-libraries/src/helpers/use.prevent.window.unload.tsx b/libraries/react-shared-libraries/src/helpers/use.prevent.window.unload.tsx new file mode 100644 index 00000000..ef4eba61 --- /dev/null +++ b/libraries/react-shared-libraries/src/helpers/use.prevent.window.unload.tsx @@ -0,0 +1,10 @@ +import {useEffect} from "react"; + +export const usePreventWindowUnload = (preventDefault: boolean) => { + useEffect(() => { + if (!preventDefault) return; + const handleBeforeUnload = (event: any) => event.preventDefault(); + window.addEventListener("beforeunload", handleBeforeUnload); + return () => window.removeEventListener("beforeunload", handleBeforeUnload); + }, [preventDefault]); +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8df866c9..829b7d6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,9 @@ ], "dependencies": { "@casl/ability": "^6.5.0", + "@hookform/resolvers": "^3.3.4", + "@mantine/core": "^5.10.5", + "@mantine/modals": "^5.10.5", "@nestjs/common": "^10.0.2", "@nestjs/core": "^10.0.2", "@nestjs/microservices": "^10.3.1", @@ -22,11 +25,14 @@ "@novu/notification-center": "^0.23.0", "@prisma/client": "^5.8.1", "@swc/helpers": "~0.5.2", + "@sweetalert2/theme-dark": "^5.0.16", "@types/bcrypt": "^5.0.2", "@types/jsonwebtoken": "^9.0.5", "@types/lodash": "^4.14.202", "@types/md5": "^2.3.5", + "@types/remove-markdown": "^0.3.4", "@types/stripe": "^8.0.417", + "@uiw/react-md-editor": "^4.0.3", "axios": "^1.0.0", "bcrypt": "^5.1.1", "bullmq": "^5.1.5", @@ -45,14 +51,16 @@ "prisma-paginate": "^5.2.1", "react": "18.2.0", "react-dom": "18.2.0", - "react-hook-form": "^7.49.3", + "react-hook-form": "^7.50.1", "react-query": "^3.39.3", "react-router-dom": "6.11.2", "redis": "^4.6.12", "reflect-metadata": "^0.1.13", + "remove-markdown": "^0.5.0", "rxjs": "^7.8.0", "simple-statistics": "^7.8.3", "stripe": "^14.14.0", + "sweetalert2": "^11.10.5", "tslib": "^2.3.0", "twitter-api-v2": "^1.16.0", "yargs": "^17.7.2" @@ -107,6 +115,7 @@ "prisma": "^5.8.1", "react-refresh": "^0.10.0", "sass": "1.62.1", + "tailwind-scrollbar": "^3.1.0", "tailwindcss": "^3.4.1", "ts-jest": "^29.1.0", "ts-node": "10.9.1", @@ -3077,6 +3086,14 @@ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" }, + "node_modules/@hookform/resolvers": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.3.4.tgz", + "integrity": "sha512-o5cgpGOuJYrd+iMKvkttOclgwRW86EsWJZZRC23prf0uU2i48Htq4PuT73AVb9ionFyZrwYEITuOFGF+BydEtQ==", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -3708,6 +3725,20 @@ "react": ">=16.8.0" } }, + "node_modules/@mantine/modals": { + "version": "5.10.5", + "resolved": "https://registry.npmjs.org/@mantine/modals/-/modals-5.10.5.tgz", + "integrity": "sha512-q3BCqAxulcIZCL48vUrwSaXDhxDng/2daVky8K1mTPYNlcm9iN1mqVTUC4uFWhn4b2UmPu4UdbNePEgLuhK4Mw==", + "dependencies": { + "@mantine/utils": "5.10.5" + }, + "peerDependencies": { + "@mantine/core": "5.10.5", + "@mantine/hooks": "5.10.5", + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@mantine/styles": { "version": "5.10.5", "resolved": "https://registry.npmjs.org/@mantine/styles/-/styles-5.10.5.tgz", @@ -6329,6 +6360,11 @@ "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", "dev": true }, + "node_modules/@sweetalert2/theme-dark": { + "version": "5.0.16", + "resolved": "https://registry.npmjs.org/@sweetalert2/theme-dark/-/theme-dark-5.0.16.tgz", + "integrity": "sha512-SFXEVfp9jmaBLhYEeRilrcpQP/sQimfjtZdvazJ+l5jEdNVKDkPD/CD3T7V8VqUqk/q2zUBSvr0WOMrHoAvfCA==" + }, "node_modules/@szmarczak/http-timer": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", @@ -6622,6 +6658,14 @@ "@types/express": "*" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/eslint": { "version": "8.56.2", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", @@ -6645,8 +6689,15 @@ "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.4.tgz", + "integrity": "sha512-5idy3hvI9lAMqsyilBM+N+boaCf1MgoefbDxN6KEO5aK17TOHwFAYT9sjxzeKAiIWRUBgLxmZ9mPcnzZXtTcRQ==", + "dependencies": { + "@types/estree": "*" + } }, "node_modules/@types/express": { "version": "4.17.21", @@ -6681,6 +6732,14 @@ "@types/node": "*" } }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", @@ -6791,12 +6850,25 @@ "resolved": "https://registry.npmjs.org/@types/md5/-/md5-2.3.5.tgz", "integrity": "sha512-/i42wjYNgE6wf0j2bcTX6kuowmdL/6PE4IVitMpm2eYKBUuYCprdcWVK+xEF0gcV6ufMCRhtxmReGfc6hIK7Jw==" }, + "node_modules/@types/mdast": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", + "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + }, "node_modules/@types/node": { "version": "18.16.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.9.tgz", @@ -6816,11 +6888,15 @@ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, + "node_modules/@types/prismjs": { + "version": "1.26.3", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", + "integrity": "sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==" + }, "node_modules/@types/prop-types": { "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", - "dev": true + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" }, "node_modules/@types/qs": { "version": "6.9.11", @@ -6838,7 +6914,6 @@ "version": "18.2.33", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.33.tgz", "integrity": "sha512-v+I7S+hu3PIBoVkKGpSYYpiBT1ijqEzWpzQD62/jm4K74hPpSP7FF9BnKG6+fg2+62weJYkkBWDJlZt5JO/9hg==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -6854,6 +6929,11 @@ "@types/react": "*" } }, + "node_modules/@types/remove-markdown": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@types/remove-markdown/-/remove-markdown-0.3.4.tgz", + "integrity": "sha512-i753EH/p02bw7bLlpfS/4CV1rdikbGiLabWyVsAvsFid3cA5RNU1frG7JycgY+NSnFwtoGlElvZVceCytecTDA==" + }, "node_modules/@types/responselike": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", @@ -6872,8 +6952,7 @@ "node_modules/@types/scheduler": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", - "dev": true + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" }, "node_modules/@types/semver": { "version": "7.5.6", @@ -6941,6 +7020,11 @@ "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "dev": true }, + "node_modules/@types/unist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", + "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + }, "node_modules/@types/uuid": { "version": "9.0.8", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", @@ -7320,6 +7404,60 @@ "@ucast/mongo": "^2.4.0" } }, + "node_modules/@uiw/copy-to-clipboard": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@uiw/copy-to-clipboard/-/copy-to-clipboard-1.0.16.tgz", + "integrity": "sha512-IXR+N363nLTR3ilklmM+B0nk774jVE/muOrBYt4Rdww/Pf3uP9XHyv2x6YZrbDh29F7w9BkzQyB8QF6WDShmJA==" + }, + "node_modules/@uiw/react-markdown-preview": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@uiw/react-markdown-preview/-/react-markdown-preview-5.0.7.tgz", + "integrity": "sha512-EmNI3LPM5Ff5ikcHJHcoZW268gpeAUPISfIwQaPjjHf/ET4aHNyo8sFBGV0+ycAaS52fXl2cvF+k/JweuMVVeQ==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "@uiw/copy-to-clipboard": "~1.0.12", + "react-markdown": "~9.0.1", + "rehype-attr": "~3.0.1", + "rehype-autolink-headings": "~7.1.0", + "rehype-ignore": "^2.0.0", + "rehype-prism-plus": "2.0.0", + "rehype-raw": "^7.0.0", + "rehype-rewrite": "~4.0.0", + "rehype-slug": "~6.0.0", + "remark-gfm": "~4.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@uiw/react-md-editor": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@uiw/react-md-editor/-/react-md-editor-4.0.3.tgz", + "integrity": "sha512-TvChXxUBUS21Rk0cVC0aeJoWcFZ/G0xN/Hc4Lv9FGFK8wPOHESd7Bcq4jNRHJ6lEzE/+d4Wh00lEVNKj+rQyBw==", + "dependencies": { + "@babel/runtime": "^7.14.6", + "@uiw/react-markdown-preview": "^5.0.6", + "rehype": "~13.0.0", + "rehype-prism-plus": "~2.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, "node_modules/@vitejs/plugin-react": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz", @@ -8564,6 +8702,15 @@ "@babel/core": "^7.0.0" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -8613,6 +8760,15 @@ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, + "node_modules/bcp-47-match": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-2.0.3.tgz", + "integrity": "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/bcrypt": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", @@ -8830,8 +8986,7 @@ "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/brace-expansion": { "version": "1.1.11", @@ -9203,6 +9358,15 @@ } ] }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chai": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", @@ -9245,6 +9409,42 @@ "node": ">=10" } }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", @@ -9525,6 +9725,15 @@ "node": ">= 0.8" } }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -10206,6 +10415,21 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css-selector-parser": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-3.0.4.tgz", + "integrity": "sha512-pnmS1dbKsz6KA4EW4BznyPL2xxkNDRg62hcD0v8g6DEw2W7hxOln5M953jsp9hmw5Dg57S6o/A8GOn37mbAgcQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, "node_modules/css-tree": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", @@ -10421,6 +10645,18 @@ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", "dev": true }, + "node_modules/decode-named-character-reference": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/decompress-response": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", @@ -10659,7 +10895,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, "engines": { "node": ">=6" } @@ -10709,6 +10944,18 @@ "detect-port": "bin/detect-port.js" } }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -10745,6 +10992,18 @@ "node": ">=8" } }, + "node_modules/direction": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/direction/-/direction-2.0.1.tgz", + "integrity": "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA==", + "bin": { + "direction": "cli.js" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -11038,7 +11297,6 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, "engines": { "node": ">=0.12" }, @@ -11940,6 +12198,15 @@ "node": ">=4.0" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -12262,6 +12529,11 @@ "node": ">=4" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -12965,6 +13237,11 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==" + }, "node_modules/glob": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", @@ -13238,6 +13515,248 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-from-html": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.1.tgz", + "integrity": "sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.1.tgz", + "integrity": "sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^8.0.0", + "property-information": "^6.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-has-property": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-has-property/-/hast-util-has-property-3.0.0.tgz", + "integrity": "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-heading-rank": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-3.0.0.tgz", + "integrity": "sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.0.2.tgz", + "integrity": "sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-select": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.2.tgz", + "integrity": "sha512-hT/SD/d/Meu+iobvgkffo1QecV8WeKWxwsNMzcTJsKw1cKTQKSR/7ArJeURLNJF9HDjp9nVoORyNNJxrvBye8Q==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "bcp-47-match": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "css-selector-parser": "^3.0.0", + "devlop": "^1.0.0", + "direction": "^2.0.0", + "hast-util-has-property": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "not": "^0.1.0", + "nth-check": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-html": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.0.tgz", + "integrity": "sha512-IVGhNgg7vANuUA2XKrT6sOIIPgaYZnmLx3l/CCOAK0PtgfoHrZwX7jCSYyFxHTrGmC6S9q8aQQekjp4JPZF+cw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-raw": "^9.0.0", + "hast-util-whitespace": "^3.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "stringify-entities": "^4.0.0", + "zwitch": "^2.0.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", + "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.0.tgz", + "integrity": "sha512-OGkAxX1Ua3cbcW6EJ5pT/tslVb90uViVkcJ4ZZIMW/R33DX/AkcJcRrPebPwJkHYwlDHXz4aIwvAAaAdtrACFA==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-8.0.0.tgz", + "integrity": "sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -13363,6 +13882,24 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, + "node_modules/html-url-attributes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.0.tgz", + "integrity": "sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -13726,6 +14263,11 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "node_modules/inline-style-parser": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz", + "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==" + }, "node_modules/internal-slot": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", @@ -13771,6 +14313,28 @@ "node": ">= 0.10" } }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -13889,6 +14453,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -13942,6 +14515,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -15772,6 +16354,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -15878,6 +16469,15 @@ "tmpl": "1.0.5" } }, + "node_modules/markdown-table": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", + "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/match-sorter": { "version": "6.3.3", "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.3.tgz", @@ -15897,6 +16497,272 @@ "is-buffer": "~1.1.6" } }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", + "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", + "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", + "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz", + "integrity": "sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", + "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", + "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.0.tgz", + "integrity": "sha512-A8AJHlR7/wPQ3+Jre1+1rq040fX9A4Q1jG8JxmSNp/PLPHg80A6475wxTp3KzHpApFH6yWxFotHrJQA3dXP6/w==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-remove-position": "^5.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.1.0.tgz", + "integrity": "sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", + "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdn-data": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", @@ -15951,6 +16817,541 @@ "node": ">= 0.6" } }, + "node_modules/micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", + "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.0.0.tgz", + "integrity": "sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.0.0.tgz", + "integrity": "sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz", + "integrity": "sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.0.1.tgz", + "integrity": "sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", + "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", + "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", + "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", + "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", + "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", + "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", + "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", + "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", + "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", + "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", + "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", + "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", + "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", + "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", + "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -16555,6 +17956,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/not": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/not/-/not-0.1.0.tgz", + "integrity": "sha512-5PDmaAsVfnWUgTUbJ3ERwn7u79Z0dYxN9ErxCpVJJqe2RK0PJ3z+iFUxuqjwtlDDegXvtWoxD/3Fzxox7tFGWA==" + }, "node_modules/npm-package-arg": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", @@ -16597,7 +18003,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -17023,6 +18428,30 @@ "node": ">=6" } }, + "node_modules/parse-entities": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", + "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -17054,11 +18483,15 @@ "node": ">= 0.10" } }, + "node_modules/parse-numeric-range": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/parse-numeric-range/-/parse-numeric-range-1.3.0.tgz", + "integrity": "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==" + }, "node_modules/parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, "dependencies": { "entities": "^4.4.0" }, @@ -18123,6 +19556,15 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/property-information": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz", + "integrity": "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -18312,12 +19754,11 @@ } }, "node_modules/react-hook-form": { - "version": "7.49.3", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.49.3.tgz", - "integrity": "sha512-foD6r3juidAT1cOZzpmD/gOKt7fRsDhXXZ0y28+Al1CHgX+AY1qIN9VSIIItXRq1dN68QrRwl1ORFlwjBaAqeQ==", + "version": "7.50.1", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.50.1.tgz", + "integrity": "sha512-3PCY82oE0WgeOgUtIr3nYNNtNvqtJ7BZjsbxh6TnYNbXButaD5WpjOmTjdxZfheuHKR68qfeFnEDVYoSSFPMTQ==", "engines": { - "node": ">=18", - "pnpm": "8" + "node": ">=12.22.0" }, "funding": { "type": "opencollective", @@ -18344,6 +19785,31 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/react-markdown": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", + "integrity": "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, "node_modules/react-query": { "version": "3.39.3", "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", @@ -18511,6 +19977,62 @@ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==" }, + "node_modules/refractor": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/refractor/-/refractor-4.8.1.tgz", + "integrity": "sha512-/fk5sI0iTgFYlmVGYVew90AoYnNMP6pooClx/XKqyeeCQXrL0Kvgn8V0VEht5ccdljbzzF1i3Q213gcntkRExg==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/prismjs": "^1.0.0", + "hastscript": "^7.0.0", + "parse-entities": "^4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/refractor/node_modules/@types/hast": { + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", + "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", + "dependencies": { + "@types/unist": "^2" + } + }, + "node_modules/refractor/node_modules/@types/unist": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", + "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" + }, + "node_modules/refractor/node_modules/hast-util-parse-selector": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", + "integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==", + "dependencies": { + "@types/hast": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/refractor/node_modules/hastscript": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", + "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^3.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -18598,11 +20120,228 @@ "jsesc": "bin/jsesc" } }, + "node_modules/rehype": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.1.tgz", + "integrity": "sha512-AcSLS2mItY+0fYu9xKxOu1LhUZeBZZBx8//5HKzF+0XP+eP8+6a5MXn2+DW2kfXR6Dtp1FEXMVrjyKAcvcU8vg==", + "dependencies": { + "@types/hast": "^3.0.0", + "rehype-parse": "^9.0.0", + "rehype-stringify": "^10.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-attr": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/rehype-attr/-/rehype-attr-3.0.3.tgz", + "integrity": "sha512-Up50Xfra8tyxnkJdCzLBIBtxOcB2M1xdeKe1324U06RAvSjYm7ULSeoM+b/nYPQPVd7jsXJ9+39IG1WAJPXONw==", + "dependencies": { + "unified": "~11.0.0", + "unist-util-visit": "~5.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/rehype-autolink-headings": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-7.1.0.tgz", + "integrity": "sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-heading-rank": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-ignore": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/rehype-ignore/-/rehype-ignore-2.0.2.tgz", + "integrity": "sha512-BpAT/3lU9DMJ2siYVD/dSR0A/zQgD6Fb+fxkJd4j+wDVy6TYbYpK+FZqu8eM9EuNKGvi4BJR7XTZ/+zF02Dq8w==", + "dependencies": { + "hast-util-select": "^6.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/rehype-parse": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.0.tgz", + "integrity": "sha512-WG7nfvmWWkCR++KEkZevZb/uw41E8TsH4DsY9UxsTbIXCVGbAs4S+r8FrQ+OtH5EEQAs+5UxKC42VinkmpA1Yw==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-html": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-prism-plus": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/rehype-prism-plus/-/rehype-prism-plus-2.0.0.tgz", + "integrity": "sha512-FeM/9V2N7EvDZVdR2dqhAzlw5YI49m9Tgn7ZrYJeYHIahM6gcXpH0K1y2gNnKanZCydOMluJvX2cB9z3lhY8XQ==", + "dependencies": { + "hast-util-to-string": "^3.0.0", + "parse-numeric-range": "^1.3.0", + "refractor": "^4.8.0", + "rehype-parse": "^9.0.0", + "unist-util-filter": "^5.0.0", + "unist-util-visit": "^5.0.0" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-rewrite": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/rehype-rewrite/-/rehype-rewrite-4.0.2.tgz", + "integrity": "sha512-rjLJ3z6fIV11phwCqHp/KRo8xuUCO8o9bFJCNw5o6O2wlLk6g8r323aRswdGBQwfXPFYeSuZdAjp4tzo6RGqEg==", + "dependencies": { + "hast-util-select": "^6.0.0", + "unified": "^11.0.3", + "unist-util-visit": "^5.0.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/rehype-slug": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-6.0.0.tgz", + "integrity": "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==", + "dependencies": { + "@types/hast": "^3.0.0", + "github-slugger": "^2.0.0", + "hast-util-heading-rank": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-stringify": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.0.tgz", + "integrity": "sha512-1TX1i048LooI9QoecrXy7nGFFbFSufxVRAfc6Y9YMRAi56l+oB0zP51mLSV312uRuvVLPV1opSlJmslozR1XHQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-html": "^9.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", + "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz", + "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remove-accents": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" }, + "node_modules/remove-markdown": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-markdown/-/remove-markdown-0.5.0.tgz", + "integrity": "sha512-x917M80K97K5IN1L8lUvFehsfhR8cYjGQ/yAMRI9E7JIKivtl5Emo5iD13DhMr+VojzMCiYk8V2byNPwT/oapg==" + }, "node_modules/repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", @@ -19474,6 +21213,15 @@ "source-map": "^0.6.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -19696,6 +21444,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", + "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -19845,6 +21606,14 @@ "webpack": "^5.0.0" } }, + "node_modules/style-to-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.5.tgz", + "integrity": "sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==", + "dependencies": { + "inline-style-parser": "0.2.2" + } + }, "node_modules/styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", @@ -20130,6 +21899,15 @@ "url": "https://opencollective.com/svgo" } }, + "node_modules/sweetalert2": { + "version": "11.10.5", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.10.5.tgz", + "integrity": "sha512-q9eE3EKhMcpIDU/Xcz7z5lk8axCGkgxwK47gXGrrfncnBJWxHPPHnBVAjfsVXcTt8Yi8U6HNEcBRSu+qGeyFdA==", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/limonte" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -20141,6 +21919,18 @@ "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" }, + "node_modules/tailwind-scrollbar": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tailwind-scrollbar/-/tailwind-scrollbar-3.1.0.tgz", + "integrity": "sha512-pmrtDIZeHyu2idTejfV59SbaJyvp1VRjYxAjZBH0jnyrPRo6HL1kD5Glz8VPagasqr6oAx6M05+Tuw429Z8jxg==", + "dev": true, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "tailwindcss": "3.x" + } + }, "node_modules/tailwindcss": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", @@ -20597,6 +22387,15 @@ "node": ">=14" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/trim-repeated": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-2.0.0.tgz", @@ -20621,6 +22420,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/ts-api-utils": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", @@ -21031,6 +22839,35 @@ "node": ">=4" } }, + "node_modules/unified": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", + "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/union": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", @@ -21043,6 +22880,92 @@ "node": ">= 0.8.0" } }, + "node_modules/unist-util-filter": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/unist-util-filter/-/unist-util-filter-5.0.1.tgz", + "integrity": "sha512-pHx7D4Zt6+TsfwylH9+lYhBhzyhEnCXs/lbq/Hstxno5z4gVdyc2WEW0asfjGKPyG4pEKrnBv5hdkO6+aRnQJw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", + "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", + "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -21266,6 +23189,46 @@ "node": ">= 0.8" } }, + "node_modules/vfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", + "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.2.tgz", + "integrity": "sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", + "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vite": { "version": "5.0.12", "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", @@ -21487,6 +23450,15 @@ "defaults": "^1.0.3" } }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/webfontloader": { "version": "1.6.28", "resolved": "https://registry.npmjs.org/webfontloader/-/webfontloader-1.6.28.tgz", @@ -22156,6 +24128,15 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index e9f9a010..cb56d008 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "license": "MIT", "scripts": { - "dev": "concurrently \"stripe listen --forward-to localhost:3000/payment\" \"nx run-many --target=serve --projects=frontend,backend --parallel=4\"", + "dev": "concurrently \"stripe listen --forward-to localhost:3000/payment\" \"nx run-many --target=serve --projects=frontend,backend,workers --parallel=4\"", "workers": "nx run workers:serve:development", "cron": "nx run cron:serve:development", "command": "nx run commands:build && nx run commands:command", @@ -13,6 +13,9 @@ "private": true, "dependencies": { "@casl/ability": "^6.5.0", + "@hookform/resolvers": "^3.3.4", + "@mantine/core": "^5.10.5", + "@mantine/modals": "^5.10.5", "@nestjs/common": "^10.0.2", "@nestjs/core": "^10.0.2", "@nestjs/microservices": "^10.3.1", @@ -22,11 +25,14 @@ "@novu/notification-center": "^0.23.0", "@prisma/client": "^5.8.1", "@swc/helpers": "~0.5.2", + "@sweetalert2/theme-dark": "^5.0.16", "@types/bcrypt": "^5.0.2", "@types/jsonwebtoken": "^9.0.5", "@types/lodash": "^4.14.202", "@types/md5": "^2.3.5", + "@types/remove-markdown": "^0.3.4", "@types/stripe": "^8.0.417", + "@uiw/react-md-editor": "^4.0.3", "axios": "^1.0.0", "bcrypt": "^5.1.1", "bullmq": "^5.1.5", @@ -45,14 +51,16 @@ "prisma-paginate": "^5.2.1", "react": "18.2.0", "react-dom": "18.2.0", - "react-hook-form": "^7.49.3", + "react-hook-form": "^7.50.1", "react-query": "^3.39.3", "react-router-dom": "6.11.2", "redis": "^4.6.12", "reflect-metadata": "^0.1.13", + "remove-markdown": "^0.5.0", "rxjs": "^7.8.0", "simple-statistics": "^7.8.3", "stripe": "^14.14.0", + "sweetalert2": "^11.10.5", "tslib": "^2.3.0", "twitter-api-v2": "^1.16.0", "yargs": "^17.7.2" @@ -107,6 +115,7 @@ "prisma": "^5.8.1", "react-refresh": "^0.10.0", "sass": "1.62.1", + "tailwind-scrollbar": "^3.1.0", "tailwindcss": "^3.4.1", "ts-jest": "^29.1.0", "ts-node": "10.9.1",