From d13c71d1f679a644d428fdcd344344c18fa5eb73 Mon Sep 17 00:00:00 2001 From: jamesread Date: Fri, 6 Sep 2024 23:34:26 +0100 Subject: [PATCH 01/59] feat: Configuration checking on backend startup, and via cli. --- apps/backend/src/main.ts | 20 +++ apps/commands/src/command.module.ts | 3 +- apps/commands/src/tasks/configuration.ts | 30 +++++ .../configuration/configuration.checker.ts | 116 ++++++++++++++++++ 4 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 apps/commands/src/tasks/configuration.ts create mode 100644 libraries/helpers/src/configuration/configuration.checker.ts diff --git a/apps/backend/src/main.ts b/apps/backend/src/main.ts index 6b0bd131..24d5051d 100644 --- a/apps/backend/src/main.ts +++ b/apps/backend/src/main.ts @@ -8,6 +8,7 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { SubscriptionExceptionFilter } from '@gitroom/backend/services/auth/permissions/subscription.exception'; import { HttpExceptionFilter } from '@gitroom/nestjs-libraries/services/exception.filter'; +import { ConfigurationChecker } from '@gitroom/helpers/configuration/configuration.checker'; async function bootstrap() { const app = await NestFactory.create(AppModule, { @@ -36,7 +37,26 @@ async function bootstrap() { const port = process.env.PORT || 3000; await app.listen(port); + + checkConfiguration() // Do this last, so that users will see obvious issues at the end of the startup log without having to scroll up. + Logger.log(`🚀 Application is running on: http://localhost:${port}`); } +function checkConfiguration() { + const checker = new ConfigurationChecker(); + checker.readEnvFromProcess() + checker.check() + + if (checker.hasIssues()) { + for (const issue of checker.getIssues()) { + Logger.warn(issue, 'Configuration issue') + } + + Logger.warn("Configuration issues found: " + checker.getIssuesCount() + ". You run run `npm run command config:check` to quickly check again.") + } else { + Logger.log("Configuration check completed without any issues.") + } +} + bootstrap(); diff --git a/apps/commands/src/command.module.ts b/apps/commands/src/command.module.ts index dfa6acaa..9430ef56 100644 --- a/apps/commands/src/command.module.ts +++ b/apps/commands/src/command.module.ts @@ -4,11 +4,12 @@ import { CheckStars } from './tasks/check.stars'; import { DatabaseModule } from '@gitroom/nestjs-libraries/database/prisma/database.module'; import { RefreshTokens } from './tasks/refresh.tokens'; import { BullMqModule } from '@gitroom/nestjs-libraries/bull-mq-transport-new/bull.mq.module'; +import { ConfigurationTask } from './tasks/configuration'; @Module({ imports: [ExternalCommandModule, DatabaseModule, BullMqModule], controllers: [], - providers: [CheckStars, RefreshTokens], + providers: [CheckStars, RefreshTokens, ConfigurationTask], get exports() { return [...this.imports, ...this.providers]; }, diff --git a/apps/commands/src/tasks/configuration.ts b/apps/commands/src/tasks/configuration.ts new file mode 100644 index 00000000..7fd6f287 --- /dev/null +++ b/apps/commands/src/tasks/configuration.ts @@ -0,0 +1,30 @@ +import { Command } from 'nestjs-command'; +import { Injectable } from '@nestjs/common'; +import { ConfigurationChecker } from '@gitroom/helpers/configuration/configuration.checker'; + +@Injectable() +export class ConfigurationTask { + @Command({ + command: 'config:check', + describe: 'Checks your configuration (.env) file for issues.', + }) + create() { + const checker = new ConfigurationChecker(); + checker.readEnvFromProcess() + checker.check() + + if (checker.hasIssues()) { + for (const issue of checker.getIssues()) { + console.warn("Configuration issue:", issue) + } + + console.error("Configuration check complete, issues: ", checker.getIssuesCount()) + } else { + console.log("Configuration check complete, no issues found.") + } + + console.log("Press Ctrl+C to exit."); + return true + } +} + diff --git a/libraries/helpers/src/configuration/configuration.checker.ts b/libraries/helpers/src/configuration/configuration.checker.ts new file mode 100644 index 00000000..b6a1ca6d --- /dev/null +++ b/libraries/helpers/src/configuration/configuration.checker.ts @@ -0,0 +1,116 @@ +import { readFileSync, existsSync } from 'fs' +import * as dotenv from 'dotenv' +import { resolve } from 'path' + +export class ConfigurationChecker { + cfg: dotenv.DotenvParseOutput + issues: string[] = [] + + readEnvFromFile () { + const envFile = resolve(__dirname, '../../../.env') + + if (!existsSync(envFile)) { + console.error('Env file not found!: ', envFile) + return + } + + const handle = readFileSync(envFile, 'utf-8') + + this.cfg = dotenv.parse(handle) + } + + readEnvFromProcess () { + this.cfg = process.env + } + + check () { + this.checkDatabaseServers() + this.checkNonEmpty('JWT_SECRET') + this.checkIsValidUrl('MAIN_URL') + this.checkIsValidUrl('FRONTEND_URL') + this.checkIsValidUrl('NEXT_PUBLIC_BACKEND_URL') + this.checkIsValidUrl('BACKEND_INTERNAL_URL') + this.checkNonEmpty('RESEND_API_KEY', 'Needed to send user activation emails.') + this.checkNonEmpty('CLOUDFLARE_ACCOUNT_ID', 'Needed to setup providers.') + this.checkNonEmpty('CLOUDFLARE_ACCESS_KEY', 'Needed to setup providers.') + this.checkNonEmpty('CLOUDFLARE_SECRET_ACCESS_KEY', 'Needed to setup providers.') + this.checkNonEmpty('CLOUDFLARE_BUCKETNAME', 'Needed to setup providers.') + this.checkNonEmpty('CLOUDFLARE_BUCKET_URL', 'Needed to setup providers.') + this.checkNonEmpty('CLOUDFLARE_REGION', 'Needed to setup providers.') + } + + checkNonEmpty (key: string, description?: string): boolean { + const v = this.get(key) + + if (!description) { + description = '' + } + + if (!v) { + this.issues.push(key + ' not set. ' + description) + return false + } + + if (v.length === 0) { + this.issues.push(key + ' is empty.' + description) + return false + } + + return true + } + + get(key: string): string | undefined { + return this.cfg[key as keyof typeof this.cfg] + } + + checkDatabaseServers () { + this.checkRedis() + this.checkIsValidUrl('DATABASE_URL') + } + + checkRedis () { + if (!this.cfg.REDIS_URL) { + this.issues.push('REDIS_URL not set') + } + + try { + const redisUrl = new URL(this.cfg.REDIS_URL) + + if (redisUrl.protocol !== 'redis:') { + this.issues.push('REDIS_URL must start with redis://') + } + } catch (error) { + this.issues.push('REDIS_URL is not a valid URL') + } + } + + checkIsValidUrl (key: string) { + if (!this.checkNonEmpty(key)) { + return + } + + const urlString = this.get(key) + + try { + new URL(urlString) + } catch (error) { + this.issues.push(key + ' is not a valid URL') + } + + if (urlString.endsWith('/')) { + this.issues.push(key + ' should not end with /') + } + } + + hasIssues() { + return this.issues.length > 0 + } + + getIssues() { + return this.issues + } + + getIssuesCount() { + return this.issues.length + } +} From db472df76427ca4049d23de5e810c22365d8a7ba Mon Sep 17 00:00:00 2001 From: jamesread Date: Fri, 6 Sep 2024 23:50:25 +0100 Subject: [PATCH 02/59] bug: typo --- apps/backend/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend/src/main.ts b/apps/backend/src/main.ts index 24d5051d..d7b8d464 100644 --- a/apps/backend/src/main.ts +++ b/apps/backend/src/main.ts @@ -53,7 +53,7 @@ function checkConfiguration() { Logger.warn(issue, 'Configuration issue') } - Logger.warn("Configuration issues found: " + checker.getIssuesCount() + ". You run run `npm run command config:check` to quickly check again.") + Logger.warn("Configuration issues found: " + checker.getIssuesCount() + ". You can run `npm run command config:check` to quickly check again.") } else { Logger.log("Configuration check completed without any issues.") } From 423ac3857300dcecfc444c6b37f11bce3c889fb5 Mon Sep 17 00:00:00 2001 From: jamesread Date: Mon, 16 Sep 2024 22:21:59 +0100 Subject: [PATCH 03/59] doc: Add contribitors guide --- CONTRIBUTING.adoc | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 CONTRIBUTING.adoc diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc new file mode 100644 index 00000000..be4a1fa5 --- /dev/null +++ b/CONTRIBUTING.adoc @@ -0,0 +1,33 @@ +# Contributing + +Contributions are welcome - code, docs, whatever it might be! If this is your first contribution to an Open Source project or you're a core maintainer of multiple projects, your time and interest in contributing to this project is most welcome. + +## Write code with others + +This is an open source project, with an open and welcoming community that is always keen to welcome new contributors. We recommend the two best ways to interact with the community are: + +- **GitHub issues**: To discuss more slowly, or longer-written messages. +- **[Discord chat](https://discord.postiz.com)**: To chat with people [Discord chat](https://discord.postiz.com/) and a quicker feedback. + +As a general rule; + +- **If a change is less than 3 lines**: You're probably safe just to submit the change without a discussion. This includes typos, dependency changes, and quick fixes, etc. +- **If a change is more than 3 lines**: It's probably best to discuss the change in an issue or on discord first. This is simply because you might not be aware of the roadmap for the project, or understand the impact this change might have. We're just trying to save you time here, and importantly, avoid you being disappointed if your change isn't accepted. + +## How to contribute + +This project follows a Fork/Feature Brabch/Pull Request model. If you're not familiar with this, here's how it works: + +1. Fork the project to your GitHub account. +2. Clone your fork to your local machine. +3. Create a new branch for your changes. +4. Make your changes in this new branch. +5. Push your changes to your fork. +6. Create a pull request from your fork to this project. +7. We'll review your changes and approve or comment on them. + +## Setting up your development environment + +Documentation on how to setup a development environment is available here; https://docs.postiz.com/installation/development + + From 0c6e3b10675b45ed57f84372403392fc588d5045 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 23 Sep 2024 22:29:31 +0700 Subject: [PATCH 04/59] feat: app is running --- apps/backend/src/api/api.module.ts | 3 ++- apps/backend/src/api/routes/root.controller.ts | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 apps/backend/src/api/routes/root.controller.ts diff --git a/apps/backend/src/api/api.module.ts b/apps/backend/src/api/api.module.ts index 01156270..d60fabc3 100644 --- a/apps/backend/src/api/api.module.ts +++ b/apps/backend/src/api/api.module.ts @@ -11,7 +11,6 @@ import { PermissionsService } from '@gitroom/backend/services/auth/permissions/p 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 { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; import { PostsController } from '@gitroom/backend/api/routes/posts.controller'; import { MediaController } from '@gitroom/backend/api/routes/media.controller'; import { UploadModule } from '@gitroom/nestjs-libraries/upload/upload.module'; @@ -27,6 +26,7 @@ import { CodesService } from '@gitroom/nestjs-libraries/services/codes.service'; import { CopilotController } from '@gitroom/backend/api/routes/copilot.controller'; import { AgenciesController } from '@gitroom/backend/api/routes/agencies.controller'; import { PublicController } from '@gitroom/backend/api/routes/public.controller'; +import { RootController } from '@gitroom/backend/api/routes/root.controller'; const authenticatedController = [ UsersController, @@ -60,6 +60,7 @@ const authenticatedController = [ : []), ], controllers: [ + RootController, StripeController, AuthController, PublicController, diff --git a/apps/backend/src/api/routes/root.controller.ts b/apps/backend/src/api/routes/root.controller.ts new file mode 100644 index 00000000..c79d9080 --- /dev/null +++ b/apps/backend/src/api/routes/root.controller.ts @@ -0,0 +1,8 @@ +import { Controller, Get } from '@nestjs/common'; +@Controller('/') +export class RootController { + @Get('/') + getRoot(): string { + return 'App is running!'; + } +} From ec0259d23eafcdf577c0405f111f480eab49381e Mon Sep 17 00:00:00 2001 From: Nevo David Date: Tue, 24 Sep 2024 01:04:55 +0700 Subject: [PATCH 05/59] feat: fix login for any url --- .../src/api/routes/users.controller.ts | 17 +- apps/frontend/src/middleware.ts | 33 +- .../src/subdomain/subdomain.management.ts | 37 +- package-lock.json | 2929 +---------------- package.json | 1 + 5 files changed, 94 insertions(+), 2923 deletions(-) diff --git a/apps/backend/src/api/routes/users.controller.ts b/apps/backend/src/api/routes/users.controller.ts index e8d52049..85311535 100644 --- a/apps/backend/src/api/routes/users.controller.ts +++ b/apps/backend/src/api/routes/users.controller.ts @@ -21,7 +21,7 @@ import { AuthorizationActions, Sections, } from '@gitroom/backend/services/auth/permissions/permissions.service'; -import { removeSubdomain } from '@gitroom/helpers/subdomain/subdomain.management'; +import { getCookieUrlFromDomain } from '@gitroom/helpers/subdomain/subdomain.management'; import { pricing } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/pricing'; import { ApiTags } from '@nestjs/swagger'; import { UsersService } from '@gitroom/nestjs-libraries/database/prisma/users/users.service'; @@ -92,8 +92,7 @@ export class UsersController { } response.cookie('impersonate', id, { - domain: - '.' + new URL(removeSubdomain(process.env.FRONTEND_URL!)).hostname, + domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!), secure: true, httpOnly: true, sameSite: 'none', @@ -163,8 +162,7 @@ export class UsersController { @Res({ passthrough: true }) response: Response ) { response.cookie('showorg', id, { - domain: - '.' + new URL(removeSubdomain(process.env.FRONTEND_URL!)).hostname, + domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!), secure: true, httpOnly: true, sameSite: 'none', @@ -177,8 +175,7 @@ export class UsersController { @Post('/logout') logout(@Res({ passthrough: true }) response: Response) { response.cookie('auth', '', { - domain: - '.' + new URL(removeSubdomain(process.env.FRONTEND_URL!)).hostname, + domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!), secure: true, httpOnly: true, maxAge: -1, @@ -187,8 +184,7 @@ export class UsersController { }); response.cookie('showorg', '', { - domain: - '.' + new URL(removeSubdomain(process.env.FRONTEND_URL!)).hostname, + domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!), secure: true, httpOnly: true, maxAge: -1, @@ -197,8 +193,7 @@ export class UsersController { }); response.cookie('impersonate', '', { - domain: - '.' + new URL(removeSubdomain(process.env.FRONTEND_URL!)).hostname, + domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!), secure: true, httpOnly: true, maxAge: -1, diff --git a/apps/frontend/src/middleware.ts b/apps/frontend/src/middleware.ts index 72c27f7a..3f7fe8ac 100644 --- a/apps/frontend/src/middleware.ts +++ b/apps/frontend/src/middleware.ts @@ -1,7 +1,7 @@ import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; import { fetchBackend } from '@gitroom/helpers/utils/custom.fetch.func'; -import { removeSubdomain } from '@gitroom/helpers/subdomain/subdomain.management'; +import { getCookieUrlFromDomain } from '@gitroom/helpers/subdomain/subdomain.management'; // This function can be marked `async` if using `await` inside export async function middleware(request: NextRequest) { @@ -19,8 +19,7 @@ export async function middleware(request: NextRequest) { httpOnly: true, secure: true, maxAge: -1, - domain: - '.' + new URL(removeSubdomain(process.env.FRONTEND_URL!)).hostname, + domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!), }); return response; } @@ -30,9 +29,17 @@ export async function middleware(request: NextRequest) { if (nextUrl.href.indexOf('/auth') === -1 && !authCookie) { const providers = ['google', 'settings']; - const findIndex = providers.find(p => nextUrl.href.indexOf(p) > -1); - const additional = !findIndex ? '' : (url.indexOf('?') > -1 ? '&' : '?') + `provider=${(findIndex === 'settings' ? 'github' : findIndex).toUpperCase()}`; - return NextResponse.redirect(new URL(`/auth${url}${additional}`, nextUrl.href)); + const findIndex = providers.find((p) => nextUrl.href.indexOf(p) > -1); + const additional = !findIndex + ? '' + : (url.indexOf('?') > -1 ? '&' : '?') + + `provider=${(findIndex === 'settings' + ? 'github' + : findIndex + ).toUpperCase()}`; + return NextResponse.redirect( + new URL(`/auth${url}${additional}`, nextUrl.href) + ); } // If the url is /auth and the cookie exists, redirect to / @@ -49,8 +56,7 @@ export async function middleware(request: NextRequest) { httpOnly: true, secure: true, expires: new Date(Date.now() + 15 * 60 * 1000), - domain: - '.' + new URL(removeSubdomain(process.env.FRONTEND_URL!)).hostname, + domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!), }); return redirect; } @@ -81,8 +87,7 @@ export async function middleware(request: NextRequest) { httpOnly: true, secure: true, expires: new Date(Date.now() + 15 * 60 * 1000), - domain: - '.' + new URL(removeSubdomain(process.env.FRONTEND_URL!)).hostname, + domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!), }); } @@ -91,7 +96,10 @@ export async function middleware(request: NextRequest) { if (nextUrl.pathname === '/') { return NextResponse.redirect( - new URL(!!process.env.IS_GENERAL ? '/launches' : `/analytics`, nextUrl.href) + new URL( + !!process.env.IS_GENERAL ? '/launches' : `/analytics`, + nextUrl.href + ) ); } @@ -109,8 +117,7 @@ export async function middleware(request: NextRequest) { httpOnly: true, secure: true, expires: new Date(Date.now() + 15 * 60 * 1000), - domain: - '.' + new URL(removeSubdomain(process.env.FRONTEND_URL!)).hostname, + domain: getCookieUrlFromDomain(process.env.FRONTEND_URL!), }); } diff --git a/libraries/helpers/src/subdomain/subdomain.management.ts b/libraries/helpers/src/subdomain/subdomain.management.ts index 7a8aa931..ec54bd38 100644 --- a/libraries/helpers/src/subdomain/subdomain.management.ts +++ b/libraries/helpers/src/subdomain/subdomain.management.ts @@ -1,37 +1,6 @@ -import {allTwoLevelSubdomain} from "./all.two.level.subdomain"; -const ipRegex = /^(https?:\/\/)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(:\d+)?$/; - -export function removeSubdomain(domain: string) { - // Check if the domain is an IP address with optional port - if (ipRegex.test(domain)) { - return domain; // Return the original domain if it's an IP address - } - // Split the domain into its parts - const parts = domain.split('.'); - - // Check if there are at least two parts (e.g., 'example.com') - if (parts.length < 2) { - return domain; // Return the original domain if it's too short to have a subdomain - } - - if (parts.length > 2) { - const lastTwo = parts.slice(-2).join('.'); - if (allTwoLevelSubdomain.includes(lastTwo)) { - return 'https://' + parts.slice(-3).join('.'); // Return the last three parts for known second-level domains - } - } - - // Return the last two parts for standard domains - return 'https://' + parts.slice(-2).join('.'); -} - +import { parse } from 'tldts'; export function getCookieUrlFromDomain(domain: string) { - const url = removeSubdomain(domain); - const urlObj = new URL(url); - if (!ipRegex.test(domain)) { - return '.' + urlObj.hostname - } - - return urlObj.hostname; + const url = parse(domain); + return url.domain! ? "." + url.domain! : url.hostname!; } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 8bf65143..b410290d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -116,6 +116,7 @@ "swr": "^2.2.5", "tailwind-scrollbar": "^3.1.0", "tailwindcss": "3.4.3", + "tldts": "^6.1.47", "tslib": "^2.3.0", "twitter-api-v2": "^1.16.0", "use-debounce": "^10.0.0", @@ -125,7 +126,6 @@ "yup": "^1.4.0" }, "devDependencies": { - "@mintlify/scraping": "^3.0.90", "@nestjs/schematics": "^10.0.1", "@nestjs/testing": "^10.0.2", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.7", @@ -461,50 +461,6 @@ "web-streams-polyfill": "^3.2.1" } }, - "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz", - "integrity": "sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==", - "dev": true, - "dependencies": { - "@jsdevtools/ono": "^7.1.3", - "call-me-maybe": "^1.0.1", - "js-yaml": "^3.13.1" - } - }, - "node_modules/@apidevtools/openapi-schemas": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", - "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@apidevtools/swagger-methods": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", - "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", - "dev": true - }, - "node_modules/@apidevtools/swagger-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz", - "integrity": "sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==", - "dev": true, - "dependencies": { - "@apidevtools/json-schema-ref-parser": "9.0.6", - "@apidevtools/openapi-schemas": "^2.1.0", - "@apidevtools/swagger-methods": "^3.0.2", - "@jsdevtools/ono": "^7.1.3", - "ajv": "^8.6.3", - "ajv-draft-04": "^1.0.0", - "call-me-maybe": "^1.0.1" - }, - "peerDependencies": { - "openapi-types": ">=7" - } - }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", @@ -6862,12 +6818,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", - "dev": true - }, "node_modules/@jsonjoy.com/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", @@ -7302,95 +7252,6 @@ "node": ">=10" } }, - "node_modules/@mdx-js/mdx": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@mdx-js/mdx/-/mdx-2.3.0.tgz", - "integrity": "sha512-jLuwRlz8DQfQNiUCJR50Y09CGPq3fLtmtUQfVrj79E0JWu3dvsVcxVIcfhR5h0iXu+/z++zDrYeiJqifRynJkA==", - "dev": true, - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/mdx": "^2.0.0", - "estree-util-build-jsx": "^2.0.0", - "estree-util-is-identifier-name": "^2.0.0", - "estree-util-to-js": "^1.1.0", - "estree-walker": "^3.0.0", - "hast-util-to-estree": "^2.0.0", - "markdown-extensions": "^1.0.0", - "periscopic": "^3.0.0", - "remark-mdx": "^2.0.0", - "remark-parse": "^10.0.0", - "remark-rehype": "^10.0.0", - "unified": "^10.0.0", - "unist-util-position-from-estree": "^1.0.0", - "unist-util-stringify-position": "^3.0.0", - "unist-util-visit": "^4.0.0", - "vfile": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/mdx/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, - "node_modules/@mdx-js/mdx/node_modules/estree-util-to-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-1.2.0.tgz", - "integrity": "sha512-IzU74r1PK5IMMGZXUVZbmiu4A1uhiPgW5hm1GjcOfr4ZzHaMPpLNJjR7HjXiIOzi25nZDrgFTobHTkV5Q6ITjA==", - "dev": true, - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/mdx/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@mdx-js/mdx/node_modules/unist-util-position-from-estree": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-1.1.2.tgz", - "integrity": "sha512-poZa0eXpS+/XpoQwGwl79UUdea4ol2ZuCYguVaJS4qzIOMDzbqz8a3erUCOmubSZkaOuGamb3tX790iwOIROww==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/@mdx-js/react": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", - "integrity": "sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==", - "dev": true, - "dependencies": { - "@types/mdx": "^2.0.0", - "@types/react": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "react": ">=16" - } - }, "node_modules/@meronex/icons": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@meronex/icons/-/icons-4.0.0.tgz", @@ -7416,119 +7277,6 @@ "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.0.tgz", "integrity": "sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==" }, - "node_modules/@mintlify/common": { - "version": "1.0.141", - "resolved": "https://registry.npmjs.org/@mintlify/common/-/common-1.0.141.tgz", - "integrity": "sha512-W55hktZ8jrf5z7bI1r6O84GtXMhwBicHOQWUrozXDXoQz4eDu1HPCjOp0/IktasEDpAlmde1lYErq88IZXDhHA==", - "dev": true, - "dependencies": { - "@apidevtools/swagger-parser": "^10.1.0", - "@mintlify/mdx": "0.0.46", - "@mintlify/models": "0.0.120", - "@mintlify/validation": "0.1.186", - "@sindresorhus/slugify": "^2.1.1", - "acorn": "^8.11.2", - "acorn-jsx": "^5.3.2", - "esast-util-from-js": "^2.0.0", - "estree-util-to-js": "^2.0.0", - "estree-walker": "^3.0.3", - "gray-matter": "^4.0.3", - "hast-util-from-html": "^1.0.0", - "hast-util-from-html-isomorphic": "^2.0.0", - "hast-util-to-html": "^8.0.3", - "hast-util-to-text": "^4.0.0", - "is-absolute-url": "^4.0.1", - "lodash": "^4.17.21", - "mdast": "^3.0.0", - "mdast-util-from-markdown": "^1.2.0", - "mdast-util-gfm": "^2.0.1", - "mdast-util-mdx": "^2.0.0", - "mdast-util-mdx-jsx": "^2.1.2", - "mdast-util-mdxjs-esm": "^1.3.0", - "micromark-extension-mdx-jsx": "^1.0.3", - "micromark-extension-mdxjs": "^1.0.0", - "micromark-extension-mdxjs-esm": "^1.0.0", - "openapi-types": "^12.0.0", - "remark": "^14.0.2", - "remark-frontmatter": "^4.0.1", - "remark-gfm": "^3.0.1", - "remark-math": "^5.1.1", - "remark-mdx": "^2.0.0", - "unist-builder": "^3.0.1", - "unist-util-map": "^3.1.2", - "unist-util-remove": "^3.1.0", - "unist-util-remove-position": "^4.0.0", - "unist-util-visit": "^4.1.1", - "unist-util-visit-parents": "^5.0.0", - "vfile": "^5.3.6" - } - }, - "node_modules/@mintlify/mdx": { - "version": "0.0.46", - "resolved": "https://registry.npmjs.org/@mintlify/mdx/-/mdx-0.0.46.tgz", - "integrity": "sha512-8BZsTvFhek4Z6//+kWKvlgnKy+dOjbtHqPnhiYaNlvZvGFU5BkLjOtFFP2jG+7vluvfI2yWOS8g3mtPwntWrVg==", - "dev": true, - "dependencies": { - "hast-util-to-string": "^2.0.0", - "next-mdx-remote": "^4.4.1", - "refractor": "^4.8.0", - "rehype-katex": "^6.0.3", - "remark-gfm": "^3.0.1", - "remark-math": "^5.1.1", - "remark-smartypants": "^2.0.0", - "unist-util-visit": "^4.1.1" - } - }, - "node_modules/@mintlify/models": { - "version": "0.0.120", - "resolved": "https://registry.npmjs.org/@mintlify/models/-/models-0.0.120.tgz", - "integrity": "sha512-H6bb2StjBiLAySPoPJbx2k3o6W/WOoGPkJudyzHSUzGPt8HN5hXUR6gnyRjVt0Ba7LKy8HxiG24G4s6wVwDgsA==", - "dev": true, - "dependencies": { - "axios": "^1.4.0", - "openapi-types": "12.x" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@mintlify/scraping": { - "version": "3.0.164", - "resolved": "https://registry.npmjs.org/@mintlify/scraping/-/scraping-3.0.164.tgz", - "integrity": "sha512-MjtXFr8kRjbJiPB9n2KCds3EdJ5XlrmsZq7TeeT5JCp0xpe4LnY12csWyNuNNVb017hqOpcxYPCAvsxqTWMB2g==", - "dev": true, - "dependencies": { - "@apidevtools/swagger-parser": "^10.1.0", - "@mintlify/common": "1.0.141", - "axios": "^1.2.2", - "cheerio": "^1.0.0-rc.12", - "fs-extra": "^11.1.1", - "node-html-markdown": "^1.3.0", - "ora": "^6.1.2", - "puppeteer": "^22.14.0", - "yargs": "^17.6.0" - }, - "bin": { - "mintlify-scrape": "bin/cli.js" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@mintlify/validation": { - "version": "0.1.186", - "resolved": "https://registry.npmjs.org/@mintlify/validation/-/validation-0.1.186.tgz", - "integrity": "sha512-gOrp9N2N5rg+P7gD6OSpr4hjYe26LKli/4Dk1SetLAd9oDSHbB/z0vQ6IkiXxMew+kHlRk3fwlnesLLf/A/3yg==", - "dev": true, - "dependencies": { - "@mintlify/models": "0.0.120", - "lcm": "^0.0.3", - "lodash": "^4.17.21", - "openapi-types": "12.x", - "zod": "^3.20.6", - "zod-to-json-schema": "^3.20.3" - } - }, "node_modules/@module-federation/bridge-react-webpack-plugin": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.6.3.tgz", @@ -10473,40 +10221,6 @@ "@prisma/debug": "5.19.1" } }, - "node_modules/@puppeteer/browsers": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.3.0.tgz", - "integrity": "sha512-ioXoq9gPxkss4MYhD+SFaU9p1IHFUX0ILAWFPyjGaBdjLsYAlZw6j1iLA0N/m12uVHLFDfSYNF7EQccjinIMDA==", - "dev": true, - "dependencies": { - "debug": "^4.3.5", - "extract-zip": "^2.0.1", - "progress": "^2.0.3", - "proxy-agent": "^6.4.0", - "semver": "^7.6.3", - "tar-fs": "^3.0.6", - "unbzip2-stream": "^1.4.3", - "yargs": "^17.7.2" - }, - "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@puppeteer/browsers/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@radix-ui/number": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.0.tgz", @@ -11425,61 +11139,6 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@sindresorhus/slugify": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/slugify/-/slugify-2.2.1.tgz", - "integrity": "sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==", - "dev": true, - "dependencies": { - "@sindresorhus/transliterate": "^1.0.0", - "escape-string-regexp": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@sindresorhus/slugify/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==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@sindresorhus/transliterate": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/transliterate/-/transliterate-1.6.0.tgz", - "integrity": "sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@sindresorhus/transliterate/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==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -12886,12 +12545,6 @@ "node": ">= 10" } }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true - }, "node_modules/@transloadit/prettier-bytes": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.3.4.tgz", @@ -12933,15 +12586,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@types/acorn": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@types/acorn/-/acorn-4.0.6.tgz", - "integrity": "sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==", - "dev": true, - "dependencies": { - "@types/estree": "*" - } - }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -13205,12 +12849,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@types/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "dev": true - }, "node_modules/@types/jsdom": { "version": "20.0.1", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", @@ -13253,12 +12891,6 @@ "@types/node": "*" } }, - "node_modules/@types/katex": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.14.0.tgz", - "integrity": "sha512-+2FW2CcT0K3P+JMR8YG846bmDwplKUTsWgT2ENwdQ1UdVfRk3GQrh6Mi4sTopy30gI8Uau5CEqHTDZ6YvWIUPA==", - "dev": true - }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -13296,12 +12928,6 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" }, - "node_modules/@types/mdx": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.13.tgz", - "integrity": "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==", - "dev": true - }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -13325,21 +12951,6 @@ "@types/express": "*" } }, - "node_modules/@types/nlcst": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-1.0.4.tgz", - "integrity": "sha512-ABoYdNQ/kBSsLvZAekMhIPMQ3YUZvavStpKYs7BjLLuKVmIMA0LUgZ7b54zzuWJRbHF80v1cNf4r90Vd6eMQDg==", - "dev": true, - "dependencies": { - "@types/unist": "^2" - } - }, - "node_modules/@types/nlcst/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, "node_modules/@types/node": { "version": "18.16.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.16.9.tgz", @@ -13367,12 +12978,6 @@ "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, - "node_modules/@types/parse5": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", - "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==", - "dev": true - }, "node_modules/@types/prismjs": { "version": "1.26.4", "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.4.tgz", @@ -13570,8 +13175,8 @@ "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, "optional": true, + "peer": true, "dependencies": { "@types/node": "*" } @@ -15689,20 +15294,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "dev": true, - "peerDependencies": { - "ajv": "^8.5.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/ajv-formats": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", @@ -15939,16 +15530,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-iterate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", - "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/array-move": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/array-move/-/array-move-4.0.0.tgz", @@ -16106,33 +15687,12 @@ "node": "*" } }, - "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "dev": true, - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", "dev": true }, - "node_modules/astring": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/astring/-/astring-1.9.0.tgz", - "integrity": "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==", - "dev": true, - "bin": { - "astring": "bin/astring" - } - }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -16238,12 +15798,6 @@ "node": ">= 0.4" } }, - "node_modules/b4a": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.6.tgz", - "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==", - "dev": true - }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -16520,53 +16074,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/bare-events": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", - "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", - "dev": true, - "optional": true - }, - "node_modules/bare-fs": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.4.tgz", - "integrity": "sha512-7YyxitZEq0ey5loOF5gdo1fZQFF7290GziT+VbAJ+JbYTJYaPZwuEz2r/Nq23sm4fjyTgUf2uJI2gkT3xAuSYA==", - "dev": true, - "optional": true, - "dependencies": { - "bare-events": "^2.0.0", - "bare-path": "^2.0.0", - "bare-stream": "^2.0.0" - } - }, - "node_modules/bare-os": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.2.tgz", - "integrity": "sha512-HZoJwzC+rZ9lqEemTMiO0luOePoGYNBgsLLgegKR/cljiJvcDNhDZQkzC+NC5Oh0aHbdBNSOHpghwMuB5tqhjg==", - "dev": true, - "optional": true - }, - "node_modules/bare-path": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", - "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", - "dev": true, - "optional": true, - "dependencies": { - "bare-os": "^2.1.0" - } - }, - "node_modules/bare-stream": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.3.0.tgz", - "integrity": "sha512-pVRWciewGUeCyKEuRxwv06M079r+fRjAQjBEK2P6OYGrO43O+Z0LrPZZEjlc4mB6C2RpZ9AxJ1s7NLEtOHO6eA==", - "dev": true, - "optional": true, - "dependencies": { - "b4a": "^1.6.6", - "streamx": "^2.20.0" - } - }, "node_modules/base-64": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/base-64/-/base-64-0.1.0.tgz", @@ -16607,15 +16114,6 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "node_modules/basic-ftp": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", - "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -16780,17 +16278,6 @@ "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==" }, - "node_modules/bl": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", - "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", - "dev": true, - "dependencies": { - "buffer": "^6.0.3", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", @@ -16954,7 +16441,8 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "devOptional": true, + "optional": true, + "peer": true, "engines": { "node": "*" } @@ -17154,12 +16642,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/call-me-maybe": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", - "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", - "dev": true - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -17426,7 +16908,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", @@ -17451,7 +16934,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", @@ -17468,7 +16952,8 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "entities": "^4.4.0" }, @@ -17526,20 +17011,6 @@ "node": ">=6.0" } }, - "node_modules/chromium-bidi": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.3.tgz", - "integrity": "sha512-qXlsCmpCZJAnoTYI83Iu6EdYQpMYdVkCfq08KDh2pmlVqK5t5IA9mGs4/LwCwp4fqisSOMXZxP3HIh8w8aRn0A==", - "dev": true, - "dependencies": { - "mitt": "3.0.1", - "urlpattern-polyfill": "10.0.0", - "zod": "3.23.8" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -18937,15 +18408,6 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", - "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", - "dev": true, - "engines": { - "node": ">= 14" - } - }, "node_modules/data-urls": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", @@ -19247,20 +18709,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "dev": true, - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -19361,12 +18809,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/devtools-protocol": { - "version": "0.0.1312386", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1312386.tgz", - "integrity": "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA==", - "dev": true - }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -19706,7 +19148,8 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" @@ -19719,7 +19162,8 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -19769,15 +19213,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", @@ -19968,38 +19403,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esast-util-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/esast-util-from-estree/-/esast-util-from-estree-2.0.0.tgz", - "integrity": "sha512-4CyanoAudUSBAn5K13H4JhsMH6L9ZP7XbLVe/dKybkxMO7eDyLsT8UHl9TRNrU2Gr9nz+FovfSIjuXWJ81uVwQ==", - "dev": true, - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "devlop": "^1.0.0", - "estree-util-visit": "^2.0.0", - "unist-util-position-from-estree": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esast-util-from-js": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esast-util-from-js/-/esast-util-from-js-2.0.1.tgz", - "integrity": "sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==", - "dev": true, - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "acorn": "^8.0.0", - "esast-util-from-estree": "^2.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", @@ -21082,82 +20485,6 @@ "node": ">=4.0" } }, - "node_modules/estree-util-attach-comments": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/estree-util-attach-comments/-/estree-util-attach-comments-2.1.1.tgz", - "integrity": "sha512-+5Ba/xGGS6mnwFbXIuQiDPTbuTxuMCooq3arVv7gPZtYpjp+VXH/NkHAP35OOefPhNG/UGqU3vt/LTABwcHX0w==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-build-jsx": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/estree-util-build-jsx/-/estree-util-build-jsx-2.2.2.tgz", - "integrity": "sha512-m56vOXcOBuaF+Igpb9OPAy7f9w9OIkb5yhjsZuaPm7HoGi4oTOQi0h2+yZ+AtKklYFZ+rPC4n0wYCJCEU1ONqg==", - "dev": true, - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "estree-util-is-identifier-name": "^2.0.0", - "estree-walker": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-2.1.0.tgz", - "integrity": "sha512-bEN9VHRyXAUOjkKVQVvArFym08BTWB0aJPppZZr0UNyAqWsLaVfAqP7hbaTJjzHifmB5ebnR8Wm7r7yGN/HonQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-to-js/-/estree-util-to-js-2.0.0.tgz", - "integrity": "sha512-WDF+xj5rRWmD5tj6bIqRi6CkLIXbbNQUcxQHzGysQzvHmdYG2G7p/Tf0J0gpxGgkeMZNTIjT/AoSvC9Xehcgdg==", - "dev": true, - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "astring": "^1.8.0", - "source-map": "^0.7.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/estree-util-to-js/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/estree-util-visit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-2.0.0.tgz", - "integrity": "sha512-m5KgiH85xAhhW8Wta0vShLcUvOsh3LLPI2YVwcbio1l7E09NTLL1EyMZFM1OyWowoH0skScNbhOPl4kcBgzTww==", - "dev": true, - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -21492,23 +20819,12 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", @@ -21528,7 +20844,8 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "pump": "^3.0.0" }, @@ -21559,12 +20876,6 @@ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==" }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true - }, "node_modules/fast-glob": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", @@ -21703,7 +21014,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "pend": "~1.2.0" } @@ -22532,12 +21844,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/gcd": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/gcd/-/gcd-0.0.1.tgz", - "integrity": "sha512-VNx3UEGr+ILJTiMs1+xc5SX1cMgJCrXezKPa003APUWNqQqaF6n25W8VcR7nHN6yRWbvvUTwCpZCFJeWC2kXlw==", - "dev": true - }, "node_modules/gcp-metadata": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", @@ -22654,21 +21960,6 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/get-uri": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.3.tgz", - "integrity": "sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==", - "dev": true, - "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.2", - "debug": "^4.3.4", - "fs-extra": "^11.2.0" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/github-slugger": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", @@ -23027,21 +22318,6 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==" }, - "node_modules/gray-matter": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", - "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", - "dev": true, - "dependencies": { - "js-yaml": "^3.13.1", - "kind-of": "^6.0.2", - "section-matter": "^1.0.0", - "strip-bom-string": "^1.0.0" - }, - "engines": { - "node": ">=6.0" - } - }, "node_modules/groq-sdk": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/groq-sdk/-/groq-sdk-0.5.0.tgz", @@ -23177,293 +22453,6 @@ "node": ">= 0.4" } }, - "node_modules/hast-util-from-dom": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.0.tgz", - "integrity": "sha512-d6235voAp/XR3Hh5uy7aGLbM3S4KamdW0WEgOaU1YoewnuYw4HXb5eRtv9g65m/RFGEfUY1Mw4UqCc5Y8L4Stg==", - "dev": true, - "dependencies": { - "@types/hast": "^3.0.0", - "hastscript": "^8.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-dom/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==", - "dev": true, - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/hast-util-from-html": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-1.0.2.tgz", - "integrity": "sha512-LhrTA2gfCbLOGJq2u/asp4kwuG0y6NhWTXiPKP+n0qNukKy7hc10whqqCFfyvIA1Q5U5d0sp9HhNim9gglEH4A==", - "dev": true, - "dependencies": { - "@types/hast": "^2.0.0", - "hast-util-from-parse5": "^7.0.0", - "parse5": "^7.0.0", - "vfile": "^5.0.0", - "vfile-message": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-html-isomorphic": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", - "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", - "dev": true, - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-from-dom": "^5.0.0", - "hast-util-from-html": "^2.0.0", - "unist-util-remove-position": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-html-isomorphic/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==", - "dev": true, - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/hast-util-from-html-isomorphic/node_modules/hast-util-from-html": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.2.tgz", - "integrity": "sha512-HwOHwxdt2zC5KQ/CNoybBntRook2zJvfZE/u5/Ap7aLPe22bDqen7KwGkOqOyzL5zIqKwiYX/OTtE0FWgr6XXA==", - "dev": true, - "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-html-isomorphic/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==", - "dev": true, - "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-from-html-isomorphic/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" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/hast-util-from-html-isomorphic/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==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-html-isomorphic/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==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-html-isomorphic/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==", - "dev": true, - "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/hast-util-from-html-isomorphic/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==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-html-isomorphic/node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-html-isomorphic/node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-html/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, - "node_modules/hast-util-from-html/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" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/hast-util-from-html/node_modules/vfile-message": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", - "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz", - "integrity": "sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==", - "dev": true, - "dependencies": { - "@types/hast": "^2.0.0", - "@types/unist": "^2.0.0", - "hastscript": "^7.0.0", - "property-information": "^6.0.0", - "vfile": "^5.0.0", - "vfile-location": "^4.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, - "node_modules/hast-util-from-parse5/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==", - "dev": true, - "dependencies": { - "@types/hast": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/hastscript": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", - "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", - "dev": true, - "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/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", @@ -23544,35 +22533,6 @@ "@types/unist": "*" } }, - "node_modules/hast-util-raw": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-7.2.3.tgz", - "integrity": "sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==", - "dev": true, - "dependencies": { - "@types/hast": "^2.0.0", - "@types/parse5": "^6.0.0", - "hast-util-from-parse5": "^7.0.0", - "hast-util-to-parse5": "^7.0.0", - "html-void-elements": "^2.0.0", - "parse5": "^6.0.0", - "unist-util-position": "^4.0.0", - "unist-util-visit": "^4.0.0", - "vfile": "^5.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, "node_modules/hast-util-select": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/hast-util-select/-/hast-util-select-6.0.2.tgz", @@ -23671,68 +22631,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-to-estree": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/hast-util-to-estree/-/hast-util-to-estree-2.3.3.tgz", - "integrity": "sha512-ihhPIUPxN0v0w6M5+IiAZZrn0LH2uZomeWwhn7uP7avZC6TE7lIiEh2yBMPr5+zi1aUCXq6VoYRgs2Bw9xmycQ==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0", - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^2.0.0", - "@types/unist": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "estree-util-attach-comments": "^2.0.0", - "estree-util-is-identifier-name": "^2.0.0", - "hast-util-whitespace": "^2.0.0", - "mdast-util-mdx-expression": "^1.0.0", - "mdast-util-mdxjs-esm": "^1.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-object": "^0.4.1", - "unist-util-position": "^4.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-estree/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, - "node_modules/hast-util-to-html": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-8.0.4.tgz", - "integrity": "sha512-4tpQTUOr9BMjtYyNlt0P50mH7xj0Ks2xpo8M943Vykljf99HW6EzulIoJP1N3eKOSScEHzyzi9dm7/cn0RfGwA==", - "dev": true, - "dependencies": { - "@types/hast": "^2.0.0", - "@types/unist": "^2.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-raw": "^7.0.0", - "hast-util-whitespace": "^2.0.0", - "html-void-elements": "^2.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-html/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, "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", @@ -24417,62 +23315,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/hast-util-to-parse5": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-7.1.0.tgz", - "integrity": "sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw==", - "dev": true, - "dependencies": { - "@types/hast": "^2.0.0", - "comma-separated-tokens": "^2.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": "2.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-2.0.0.tgz", - "integrity": "sha512-02AQ3vLhuH3FisaMM+i/9sm4OXGSq1UhOOCpTLLQtHdL3tZt7qil69r8M8iDkZYyC0HCFylcYoP+8IO7ddta1A==", - "dev": true, - "dependencies": { - "@types/hast": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-text": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", - "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", - "dev": true, - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "unist-util-find-after": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-text/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==", - "dev": true, - "dependencies": { - "@types/unist": "*" - } - }, "node_modules/hast-util-whitespace": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", @@ -24714,21 +23556,10 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/html-void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz", - "integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/htmlparser2": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", - "devOptional": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -24736,6 +23567,8 @@ "url": "https://github.com/sponsors/fb55" } ], + "optional": true, + "peer": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", @@ -25216,25 +24049,6 @@ "url": "https://opencollective.com/ioredis" } }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "dev": true, - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -25243,18 +24057,6 @@ "node": ">= 0.10" } }, - "node_modules/is-absolute-url": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-4.0.1.tgz", - "integrity": "sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", @@ -25462,15 +24264,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -25577,18 +24370,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-interactive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -25678,15 +24459,6 @@ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", "devOptional": true }, - "node_modules/is-reference": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", - "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", - "dev": true, - "dependencies": { - "@types/estree": "*" - } - }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -25789,18 +24561,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-unicode-supported": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-weakmap": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", @@ -27894,12 +26654,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true - }, "node_modules/jsdom": { "version": "22.1.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz", @@ -28376,15 +27130,6 @@ "shell-quote": "^1.8.1" } }, - "node_modules/lcm": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/lcm/-/lcm-0.0.3.tgz", - "integrity": "sha512-TB+ZjoillV6B26Vspf9l2L/vKaRY/4ep3hahcyVkCGFgsTNRUQdc24bQeNFiZeoxH0vr5+7SfNRMQuPHv/1IrQ==", - "dev": true, - "dependencies": { - "gcd": "^0.0.1" - } - }, "node_modules/leac": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", @@ -28686,34 +27431,6 @@ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" }, - "node_modules/log-symbols": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-5.1.0.tgz", - "integrity": "sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==", - "dev": true, - "dependencies": { - "chalk": "^5.0.0", - "is-unicode-supported": "^1.1.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/log4js": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", @@ -28866,15 +27583,6 @@ "tmpl": "1.0.5" } }, - "node_modules/markdown-extensions": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/markdown-extensions/-/markdown-extensions-1.1.1.tgz", - "integrity": "sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/markdown-table": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", @@ -28904,13 +27612,6 @@ "is-buffer": "~1.1.6" } }, - "node_modules/mdast": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast/-/mdast-3.0.0.tgz", - "integrity": "sha512-xySmf8g4fPKMeC07jXGz971EkLbWAJ83s4US2Tj9lEdnZ142UP5grN73H1Xd3HzrdbU5o9GYYP/y8F9ZSwLE9g==", - "deprecated": "`mdast` was renamed to `remark`", - "dev": true - }, "node_modules/mdast-util-definitions": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-5.1.2.tgz", @@ -28984,21 +27685,6 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" }, - "node_modules/mdast-util-frontmatter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-frontmatter/-/mdast-util-frontmatter-1.0.1.tgz", - "integrity": "sha512-JjA2OjxRqAa8wEG8hloD0uTU0kdn8kbtOWpPP94NBkfAlbxn4S8gCGf/9DwFtEeGPXrDcNXdiDjVaRdUFqYokw==", - "dev": true, - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-to-markdown": "^1.3.0", - "micromark-extension-frontmatter": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-gfm": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-2.0.2.tgz", @@ -29101,101 +27787,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdast-util-mdx": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-2.0.1.tgz", - "integrity": "sha512-38w5y+r8nyKlGvNjSEqWrhG0w5PmnRA+wnBvm+ulYCct7nsGYhFVb0lljS9bQav4psDAS1eGkP2LMVcZBi/aqw==", - "dev": true, - "dependencies": { - "mdast-util-from-markdown": "^1.0.0", - "mdast-util-mdx-expression": "^1.0.0", - "mdast-util-mdx-jsx": "^2.0.0", - "mdast-util-mdxjs-esm": "^1.0.0", - "mdast-util-to-markdown": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-1.3.2.tgz", - "integrity": "sha512-xIPmR5ReJDu/DHH1OoIT1HkuybIfRGYRywC+gJtI7qHjCJp/M9jrmBEJW22O8lskDWm562BX2W8TiAwRTb0rKA==", - "dev": true, - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^2.0.0", - "@types/mdast": "^3.0.0", - "mdast-util-from-markdown": "^1.0.0", - "mdast-util-to-markdown": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-2.1.4.tgz", - "integrity": "sha512-DtMn9CmVhVzZx3f+optVDF8yFgQVt7FghCRNdlIaS3X5Bnym3hZwPbg/XW86vdpKjlc1PVj26SpnLGeJBXD3JA==", - "dev": true, - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^2.0.0", - "@types/mdast": "^3.0.0", - "@types/unist": "^2.0.0", - "ccount": "^2.0.0", - "mdast-util-from-markdown": "^1.1.0", - "mdast-util-to-markdown": "^1.3.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-remove-position": "^4.0.0", - "unist-util-stringify-position": "^3.0.0", - "vfile-message": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, - "node_modules/mdast-util-mdx-jsx/node_modules/vfile-message": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", - "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-1.3.1.tgz", - "integrity": "sha512-SXqglS0HrEvSdUEfoXFtcg7DRl7S2cwOXc7jkuusG472Mmjag34DUDeOJUZtl+BVnyeO1frIgVpHlNRWc2gk/w==", - "dev": true, - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^2.0.0", - "@types/mdast": "^3.0.0", - "mdast-util-from-markdown": "^1.0.0", - "mdast-util-to-markdown": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-phrasing": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz", @@ -29394,35 +27985,6 @@ "uvu": "^0.5.0" } }, - "node_modules/micromark-extension-frontmatter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-frontmatter/-/micromark-extension-frontmatter-1.1.1.tgz", - "integrity": "sha512-m2UH9a7n3W8VAH9JO9y01APpPKmNNNs71P0RbknEmYSaZU5Ghogv38BYO94AI5Xw6OYfxZRdHZZ2nYjs/Z+SZQ==", - "dev": true, - "dependencies": { - "fault": "^2.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-frontmatter/node_modules/fault": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz", - "integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==", - "dev": true, - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/micromark-extension-gfm": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-2.0.3.tgz", @@ -29560,161 +28122,6 @@ "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==" }, - "node_modules/micromark-extension-mdx-expression": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-1.0.8.tgz", - "integrity": "sha512-zZpeQtc5wfWKdzDsHRBY003H2Smg+PUi2REhqgIhdzAa5xonhP03FcXxqFSerFiNUr5AWmHpaNPQTBVOS4lrXw==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/estree": "^1.0.0", - "micromark-factory-mdx-expression": "^1.0.0", - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-events-to-acorn": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0" - } - }, - "node_modules/micromark-extension-mdx-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-1.0.5.tgz", - "integrity": "sha512-gPH+9ZdmDflbu19Xkb8+gheqEDqkSpdCEubQyxuz/Hn8DOXiXvrXeikOoBA71+e8Pfi0/UYmU3wW3H58kr7akA==", - "dev": true, - "dependencies": { - "@types/acorn": "^4.0.0", - "@types/estree": "^1.0.0", - "estree-util-is-identifier-name": "^2.0.0", - "micromark-factory-mdx-expression": "^1.0.0", - "micromark-factory-space": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0", - "vfile-message": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, - "node_modules/micromark-extension-mdx-jsx/node_modules/vfile-message": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", - "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdx-md": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-mdx-md/-/micromark-extension-mdx-md-1.0.1.tgz", - "integrity": "sha512-7MSuj2S7xjOQXAjjkbjBsHkMtb+mDGVW6uI2dBL9snOBCbZmoNgDAeZ0nSn9j3T42UE/g2xVNMn18PJxZvkBEA==", - "dev": true, - "dependencies": { - "micromark-util-types": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs/-/micromark-extension-mdxjs-1.0.1.tgz", - "integrity": "sha512-7YA7hF6i5eKOfFUzZ+0z6avRG52GpWR8DL+kN47y3f2KhxbBZMhmxe7auOeaTBrW2DenbbZTf1ea9tA2hDpC2Q==", - "dev": true, - "dependencies": { - "acorn": "^8.0.0", - "acorn-jsx": "^5.0.0", - "micromark-extension-mdx-expression": "^1.0.0", - "micromark-extension-mdx-jsx": "^1.0.0", - "micromark-extension-mdx-md": "^1.0.0", - "micromark-extension-mdxjs-esm": "^1.0.0", - "micromark-util-combine-extensions": "^1.0.0", - "micromark-util-types": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-1.0.5.tgz", - "integrity": "sha512-xNRBw4aoURcyz/S69B19WnZAkWJMxHMT5hE36GtDAyhoyn/8TuAeqjFJQlwk+MKQsUD7b3l7kFX+vlfVWgcX1w==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0", - "micromark-core-commonmark": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-events-to-acorn": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "unist-util-position-from-estree": "^1.1.0", - "uvu": "^0.5.0", - "vfile-message": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, - "node_modules/micromark-extension-mdxjs-esm/node_modules/unist-util-position-from-estree": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-1.1.2.tgz", - "integrity": "sha512-poZa0eXpS+/XpoQwGwl79UUdea4ol2ZuCYguVaJS4qzIOMDzbqz8a3erUCOmubSZkaOuGamb3tX790iwOIROww==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-mdxjs-esm/node_modules/vfile-message": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", - "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/micromark-factory-destination": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-1.1.0.tgz", @@ -29756,65 +28163,6 @@ "uvu": "^0.5.0" } }, - "node_modules/micromark-factory-mdx-expression": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-1.0.9.tgz", - "integrity": "sha512-jGIWzSmNfdnkJq05c7b0+Wv0Kfz3NJ3N4cBjnbO4zjXIlxJr+f8lk+5ZmwFvqdAbUy2q6B5rCY//g0QAAaXDWA==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/estree": "^1.0.0", - "micromark-util-character": "^1.0.0", - "micromark-util-events-to-acorn": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "unist-util-position-from-estree": "^1.0.0", - "uvu": "^0.5.0", - "vfile-message": "^3.0.0" - } - }, - "node_modules/micromark-factory-mdx-expression/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, - "node_modules/micromark-factory-mdx-expression/node_modules/unist-util-position-from-estree": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-1.1.2.tgz", - "integrity": "sha512-poZa0eXpS+/XpoQwGwl79UUdea4ol2ZuCYguVaJS4qzIOMDzbqz8a3erUCOmubSZkaOuGamb3tX790iwOIROww==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-mdx-expression/node_modules/vfile-message": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", - "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/micromark-factory-space": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-1.1.0.tgz", @@ -30006,66 +28354,6 @@ } ] }, - "node_modules/micromark-util-events-to-acorn": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-1.2.3.tgz", - "integrity": "sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==", - "dev": true, - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/acorn": "^4.0.0", - "@types/estree": "^1.0.0", - "@types/unist": "^2.0.0", - "estree-util-visit": "^1.0.0", - "micromark-util-symbol": "^1.0.0", - "micromark-util-types": "^1.0.0", - "uvu": "^0.5.0", - "vfile-message": "^3.0.0" - } - }, - "node_modules/micromark-util-events-to-acorn/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, - "node_modules/micromark-util-events-to-acorn/node_modules/estree-util-visit": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/estree-util-visit/-/estree-util-visit-1.2.1.tgz", - "integrity": "sha512-xbgqcrkIVbIG+lI/gzbvd9SGTJL4zqJKBFttUl5pP27KhAjtMKbX/mQXJ7qgyXpMgVy/zvpm0xoQQaGL8OloOw==", - "dev": true, - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-util-events-to-acorn/node_modules/vfile-message": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.4.tgz", - "integrity": "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-stringify-position": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/micromark-util-html-tag-name": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.2.0.tgz", @@ -30355,12 +28643,6 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "node_modules/mitt": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", - "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", - "dev": true - }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -30713,15 +28995,6 @@ "yargs": "^16.0.0 || ^17.0.0" } }, - "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/next": { "version": "14.2.3", "resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz", @@ -30771,26 +29044,6 @@ } } }, - "node_modules/next-mdx-remote": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/next-mdx-remote/-/next-mdx-remote-4.4.1.tgz", - "integrity": "sha512-1BvyXaIou6xy3XoNF4yaMZUCb6vD2GTAa5ciOa6WoO+gAUTYsb1K4rI/HSC2ogAWLrb/7VSV52skz07vOzmqIQ==", - "dev": true, - "dependencies": { - "@mdx-js/mdx": "^2.2.1", - "@mdx-js/react": "^2.2.1", - "vfile": "^5.3.0", - "vfile-matter": "^3.0.1" - }, - "engines": { - "node": ">=14", - "npm": ">=7" - }, - "peerDependencies": { - "react": ">=16.x <=18.x", - "react-dom": ">=16.x <=18.x" - } - }, "node_modules/next-plausible": { "version": "3.12.2", "resolved": "https://registry.npmjs.org/next-plausible/-/next-plausible-3.12.2.tgz", @@ -30879,19 +29132,6 @@ "dev": true, "optional": true }, - "node_modules/nlcst-to-string": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-3.1.1.tgz", - "integrity": "sha512-63mVyqaqt0cmn2VcI2aH6kxe1rLAmSROqHMA0i4qqg1tidkfExgpb0FGMikMCn86mw5dFtBtEANfmSSK7TjNHw==", - "dev": true, - "dependencies": { - "@types/nlcst": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -30999,28 +29239,6 @@ "node-gyp-build-optional-packages-test": "build-test.js" } }, - "node_modules/node-html-markdown": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/node-html-markdown/-/node-html-markdown-1.3.0.tgz", - "integrity": "sha512-OeFi3QwC/cPjvVKZ114tzzu+YoR+v9UXW5RwSXGUqGb0qCl0DvP406tzdL7SFn8pZrMyzXoisfG2zcuF9+zw4g==", - "dev": true, - "dependencies": { - "node-html-parser": "^6.1.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/node-html-parser": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.13.tgz", - "integrity": "sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==", - "dev": true, - "dependencies": { - "css-select": "^5.1.0", - "he": "1.2.0" - } - }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -31675,99 +29893,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-6.3.1.tgz", - "integrity": "sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==", - "dev": true, - "dependencies": { - "chalk": "^5.0.0", - "cli-cursor": "^4.0.0", - "cli-spinners": "^2.6.1", - "is-interactive": "^2.0.0", - "is-unicode-supported": "^1.1.0", - "log-symbols": "^5.1.0", - "stdin-discarder": "^0.1.0", - "strip-ansi": "^7.0.1", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dev": true, - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, "node_modules/os-filter-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/os-filter-obj/-/os-filter-obj-2.0.0.tgz", @@ -31887,76 +30012,6 @@ "node": ">=6" } }, - "node_modules/pac-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", - "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", - "dev": true, - "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.5", - "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dev": true, - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-resolver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", - "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", - "dev": true, - "dependencies": { - "degenerator": "^5.0.0", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/package-json-from-dist": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", @@ -32033,21 +30088,6 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, - "node_modules/parse-latin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-5.0.1.tgz", - "integrity": "sha512-b/K8ExXaWC9t34kKeDV8kGXBkXZ1HCSAZRYE7HR14eA1GlXX5L8iWhs8USJNhQU9q5ci413jCKF0gOyovvyRBg==", - "dev": true, - "dependencies": { - "nlcst-to-string": "^3.0.0", - "unist-util-modify-children": "^3.0.0", - "unist-util-visit-children": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/parse-node-version": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", @@ -32078,7 +30118,8 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "domhandler": "^5.0.2", "parse5": "^7.0.0" @@ -32091,7 +30132,8 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "entities": "^4.4.0" }, @@ -32103,7 +30145,8 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "parse5": "^7.0.0" }, @@ -32115,7 +30158,8 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "entities": "^4.4.0" }, @@ -32261,18 +30305,8 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "devOptional": true - }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } + "optional": true, + "peer": true }, "node_modules/picocolors": { "version": "1.1.0", @@ -33341,7 +31375,8 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "devOptional": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.4.0" } @@ -33415,72 +31450,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-agent": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", - "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", - "dev": true, - "dependencies": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.3", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dev": true, - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -33521,106 +31490,6 @@ "node": ">=6" } }, - "node_modules/puppeteer": { - "version": "22.15.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-22.15.0.tgz", - "integrity": "sha512-XjCY1SiSEi1T7iSYuxS82ft85kwDJUS7wj1Z0eGVXKdtr5g4xnVcbjwxhq5xBnpK/E7x1VZZoJDxpjAOasHT4Q==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@puppeteer/browsers": "2.3.0", - "cosmiconfig": "^9.0.0", - "devtools-protocol": "0.0.1312386", - "puppeteer-core": "22.15.0" - }, - "bin": { - "puppeteer": "lib/esm/puppeteer/node/cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer-core": { - "version": "22.15.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.15.0.tgz", - "integrity": "sha512-cHArnywCiAAVXa3t4GGL2vttNxh7GqXtIYGym99egkNJ3oG//wL9LkvO4WE8W1TJe95t1F1ocu9X4xWaGsOKOA==", - "dev": true, - "dependencies": { - "@puppeteer/browsers": "2.3.0", - "chromium-bidi": "0.6.3", - "debug": "^4.3.6", - "devtools-protocol": "0.0.1312386", - "ws": "^8.18.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/puppeteer-core/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/puppeteer/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/puppeteer/node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dev": true, - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/puppeteer/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -33675,12 +31544,6 @@ } ] }, - "node_modules/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "dev": true - }, "node_modules/quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", @@ -34975,134 +32838,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/rehype-katex": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-6.0.3.tgz", - "integrity": "sha512-ByZlRwRUcWegNbF70CVRm2h/7xy7jQ3R9LaY4VVSvjnoVWwWVhNL60DiZsBpC5tSzYQOCvDbzncIpIjPZWodZA==", - "dev": true, - "dependencies": { - "@types/hast": "^2.0.0", - "@types/katex": "^0.14.0", - "hast-util-from-html-isomorphic": "^1.0.0", - "hast-util-to-text": "^3.1.0", - "katex": "^0.16.0", - "unist-util-visit": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-katex/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, - "node_modules/rehype-katex/node_modules/hast-util-from-dom": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-4.2.0.tgz", - "integrity": "sha512-t1RJW/OpJbCAJQeKi3Qrj1cAOLA0+av/iPFori112+0X7R3wng+jxLA+kXec8K4szqPRGI8vPxbbpEYvvpwaeQ==", - "dev": true, - "dependencies": { - "hastscript": "^7.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-katex/node_modules/hast-util-from-html-isomorphic": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-1.0.0.tgz", - "integrity": "sha512-Yu480AKeOEN/+l5LA674a+7BmIvtDj24GvOt7MtQWuhzUwlaaRWdEPXAh3Qm5vhuthpAipFb2vTetKXWOjmTvw==", - "dev": true, - "dependencies": { - "@types/hast": "^2.0.0", - "hast-util-from-dom": "^4.0.0", - "hast-util-from-html": "^1.0.0", - "unist-util-remove-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-katex/node_modules/hast-util-is-element": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-2.1.3.tgz", - "integrity": "sha512-O1bKah6mhgEq2WtVMk+Ta5K7pPMqsBBlmzysLdcwKVrqzZQ0CHqUPiIVspNhAG1rvxpvJjtGee17XfauZYKqVA==", - "dev": true, - "dependencies": { - "@types/hast": "^2.0.0", - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-katex/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==", - "dev": true, - "dependencies": { - "@types/hast": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-katex/node_modules/hast-util-to-text": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-3.1.2.tgz", - "integrity": "sha512-tcllLfp23dJJ+ju5wCCZHVpzsQQ43+moJbqVX3jNWPB7z/KFC4FyZD6R7y94cHL6MQ33YtMZL8Z0aIXXI4XFTw==", - "dev": true, - "dependencies": { - "@types/hast": "^2.0.0", - "@types/unist": "^2.0.0", - "hast-util-is-element": "^2.0.0", - "unist-util-find-after": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-katex/node_modules/hastscript": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz", - "integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==", - "dev": true, - "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/rehype-katex/node_modules/unist-util-find-after": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-4.0.1.tgz", - "integrity": "sha512-QO/PuPMm2ERxC6vFXEPtmAutOopy5PknD+Oq64gGwxKtk4xwo9Z97t9Av1obPmGU0IyTa6EKYUfTrK2QJS3Ozw==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/rehype-parse": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.0.tgz", @@ -36083,38 +33818,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/remark": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/remark/-/remark-14.0.3.tgz", - "integrity": "sha512-bfmJW1dmR2LvaMJuAnE88pZP9DktIFYXazkTfOIKZzi3Knk9lT0roItIA24ydOucI3bV/g/tXBA6hzqq3FV9Ew==", - "dev": true, - "dependencies": { - "@types/mdast": "^3.0.0", - "remark-parse": "^10.0.0", - "remark-stringify": "^10.0.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-frontmatter": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-4.0.1.tgz", - "integrity": "sha512-38fJrB0KnmD3E33a5jZC/5+gGAC2WKNiPw1/fdXJvijBlhA7RCsvJklrYJakS0HedninvaCYW8lQGf9C918GfA==", - "dev": true, - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-frontmatter": "^1.0.0", - "micromark-extension-frontmatter": "^1.0.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/remark-gfm": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz", @@ -36198,20 +33901,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/remark-mdx": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-2.3.0.tgz", - "integrity": "sha512-g53hMkpM0I98MU266IzDFMrTD980gNF3BJnkyFcmN+dD873mQeD5rdMO3Y2X+x8umQfbSE0PcoEDl7ledSA+2g==", - "dev": true, - "dependencies": { - "mdast-util-mdx": "^2.0.0", - "micromark-extension-mdxjs": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/remark-parse": { "version": "10.0.2", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.2.tgz", @@ -36241,77 +33930,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/remark-smartypants": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-2.1.0.tgz", - "integrity": "sha512-qoF6Vz3BjU2tP6OfZqHOvCU0ACmu/6jhGaINSQRI9mM7wCxNQTKB3JUAN4SVoN2ybElEDTxBIABRep7e569iJw==", - "dev": true, - "dependencies": { - "retext": "^8.1.0", - "retext-smartypants": "^5.2.0", - "unist-util-visit": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/remark-smartypants/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==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-smartypants/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==", - "dev": true, - "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/remark-smartypants/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==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-10.0.3.tgz", - "integrity": "sha512-koyOzCMYoUHudypbj4XpnAKFbkddRMYZHwghnxd7ue5210WzGw6kOBwauJTRUMq16jsovXx8dYNvSSWP89kZ3A==", - "dev": true, - "dependencies": { - "@types/mdast": "^3.0.0", - "mdast-util-to-markdown": "^1.0.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/remove-markdown": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/remove-markdown/-/remove-markdown-0.5.5.tgz", @@ -36453,69 +34071,6 @@ "node": ">=8" } }, - "node_modules/retext": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/retext/-/retext-8.1.0.tgz", - "integrity": "sha512-N9/Kq7YTn6ZpzfiGW45WfEGJqFf1IM1q8OsRa1CGzIebCJBNCANDRmOrholiDRGKo/We7ofKR4SEvcGAWEMD3Q==", - "dev": true, - "dependencies": { - "@types/nlcst": "^1.0.0", - "retext-latin": "^3.0.0", - "retext-stringify": "^3.0.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-latin": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-3.1.0.tgz", - "integrity": "sha512-5MrD1tuebzO8ppsja5eEu+ZbBeUNCjoEarn70tkXOS7Bdsdf6tNahsv2bY0Z8VooFF6cw7/6S+d3yI/TMlMVVQ==", - "dev": true, - "dependencies": { - "@types/nlcst": "^1.0.0", - "parse-latin": "^5.0.0", - "unherit": "^3.0.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-smartypants": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-5.2.0.tgz", - "integrity": "sha512-Do8oM+SsjrbzT2UNIKgheP0hgUQTDDQYyZaIY3kfq0pdFzoPk+ZClYJ+OERNXveog4xf1pZL4PfRxNoVL7a/jw==", - "dev": true, - "dependencies": { - "@types/nlcst": "^1.0.0", - "nlcst-to-string": "^3.0.0", - "unified": "^10.0.0", - "unist-util-visit": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-stringify": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-3.1.0.tgz", - "integrity": "sha512-767TLOaoXFXyOnjx/EggXlb37ZD2u4P1n0GJqVdpipqACsQP+20W+BNpMYrlJkq7hxffnFk+jc6mAK9qrbuB8w==", - "dev": true, - "dependencies": { - "@types/nlcst": "^1.0.0", - "nlcst-to-string": "^3.0.0", - "unified": "^10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -36822,19 +34377,6 @@ "compute-scroll-into-view": "^1.0.20" } }, - "node_modules/section-matter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", - "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/secure-compare": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", @@ -37433,16 +34975,6 @@ "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.0.6.tgz", "integrity": "sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==" }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, "node_modules/snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", @@ -37470,46 +35002,6 @@ "uuid": "dist/bin/uuid" } }, - "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", - "dev": true, - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", - "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", - "dev": true, - "dependencies": { - "agent-base": "^7.1.1", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/sonic-boom": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.1.0.tgz", @@ -37713,21 +35205,6 @@ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==" }, - "node_modules/stdin-discarder": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.1.0.tgz", - "integrity": "sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==", - "dev": true, - "dependencies": { - "bl": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/streamroller": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", @@ -37778,20 +35255,6 @@ "node": ">=10.0.0" } }, - "node_modules/streamx": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.0.tgz", - "integrity": "sha512-ZGd1LhDeGFucr1CUCTBOS58ZhEendd0ttpGT3usTvosS4ntIwKN9LJFp+OeCSprsCPL14BXVRZlHGRY1V9PVzQ==", - "dev": true, - "dependencies": { - "fast-fifo": "^1.3.2", - "queue-tick": "^1.0.1", - "text-decoder": "^1.1.0" - }, - "optionalDependencies": { - "bare-events": "^2.2.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -37978,15 +35441,6 @@ "node": ">=8" } }, - "node_modules/strip-bom-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", - "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/strip-eof": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", @@ -38559,31 +36013,6 @@ "node": ">=10" } }, - "node_modules/tar-fs": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", - "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", - "dev": true, - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" - } - }, - "node_modules/tar-fs/node_modules/tar-stream": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", - "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", - "dev": true, - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, "node_modules/tar-stream": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", @@ -38833,15 +36262,6 @@ "node": "*" } }, - "node_modules/text-decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", - "integrity": "sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==", - "dev": true, - "dependencies": { - "b4a": "^1.6.4" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -38936,6 +36356,22 @@ "node": ">=14.0.0" } }, + "node_modules/tldts": { + "version": "6.1.47", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.47.tgz", + "integrity": "sha512-R/K2tZ5MiY+mVrnSkNJkwqYT2vUv1lcT6wJvd2emGaMJ7PHUGRY4e3tUsdFCXgqxi2QgbHjL3yJgXCo40v9Hxw==", + "dependencies": { + "tldts-core": "^6.1.47" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.47", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.47.tgz", + "integrity": "sha512-6SWyFMnlst1fEt7GQVAAu16EGgFK0cLouH/2Mk6Ftlwhv3Ol40L0dlpGMcnnNiiOMyD2EV/aF3S+U2nKvvLvrA==" + }, "node_modules/tmp": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", @@ -39633,7 +37069,8 @@ "version": "1.4.3", "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "buffer": "^5.2.1", "through": "^2.3.8" @@ -39643,7 +37080,6 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "devOptional": true, "funding": [ { "type": "github", @@ -39658,6 +37094,8 @@ "url": "https://feross.org/support" } ], + "optional": true, + "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -39667,21 +37105,12 @@ "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz", "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==", - "devOptional": true, + "optional": true, + "peer": true, "engines": { "node": ">=18.17" } }, - "node_modules/unherit": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/unherit/-/unherit-3.0.1.tgz", - "integrity": "sha512-akOOQ/Yln8a2sgcLj4U0Jmx0R5jpIg2IUyRrWOzmEbjBtGzBdHtSeFKgoEcoH4KYIG/Pb8GQ/BwtYm0GCq1Sqg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -39785,25 +37214,6 @@ "node": ">= 0.8.0" } }, - "node_modules/unist-builder": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-3.0.1.tgz", - "integrity": "sha512-gnpOw7DIpCA0vpr6NqdPvTWnlPTApCTRzr+38E6hCWx3rz/cjo83SsKIlS1Z+L5ttScQ2AwutNnb8+tAvpb6qQ==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-builder/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, "node_modules/unist-util-filter": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/unist-util-filter/-/unist-util-filter-5.0.1.tgz", @@ -39839,33 +37249,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-find-after": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", - "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-find-after/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==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/unist-util-generated": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", @@ -39892,45 +37275,6 @@ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" }, - "node_modules/unist-util-map": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/unist-util-map/-/unist-util-map-3.1.3.tgz", - "integrity": "sha512-4/mDauoxqZ6geK97lJ6n2kDk6JK88Vh+hWMSJqyaaP/7eqN1dDhjcjnNxKNm3YU6Sw7PVJtcFMUbnmHvYzb6Vg==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-map/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, - "node_modules/unist-util-modify-children": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-3.1.1.tgz", - "integrity": "sha512-yXi4Lm+TG5VG+qvokP6tpnk+r1EPwyYL04JWDxLvgvPV40jANh7nm3udk65OOWquvbMDe+PL9+LmkxDpTv/7BA==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "array-iterate": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-modify-children/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, "node_modules/unist-util-position": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-4.0.4.tgz", @@ -39943,65 +37287,11 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-position-from-estree": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position-from-estree/-/unist-util-position-from-estree-2.0.0.tgz", - "integrity": "sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==", - "dev": true, - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/unist-util-position/node_modules/@types/unist": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" }, - "node_modules/unist-util-remove": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/unist-util-remove/-/unist-util-remove-3.1.1.tgz", - "integrity": "sha512-kfCqZK5YVY5yEa89tvpl7KnBBHu2c6CzMkqHUrlOqaRgGOMp0sMvwWOVrbAtj03KhovQB7i96Gda72v/EFE0vw==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^5.0.0", - "unist-util-visit-parents": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-4.0.2.tgz", - "integrity": "sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-visit": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, - "node_modules/unist-util-remove/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, "node_modules/unist-util-stringify-position": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", @@ -40033,25 +37323,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/unist-util-visit-children": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-2.0.2.tgz", - "integrity": "sha512-+LWpMFqyUwLGpsQxpumsQ9o9DG2VGLFrpz+rpVXYIEdPy57GSy5HioC0g3bg/8WP9oCLlapQtklOzQ8uLS496Q==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-children/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, "node_modules/unist-util-visit-parents": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-5.1.3.tgz", @@ -40468,82 +37739,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/vfile-location": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-4.1.0.tgz", - "integrity": "sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "vfile": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true - }, - "node_modules/vfile-matter": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/vfile-matter/-/vfile-matter-3.0.1.tgz", - "integrity": "sha512-CAAIDwnh6ZdtrqAuxdElUqQRQDQgbbIrYtDYI8gCjXS1qQ+1XdLoK8FIZWxJwn0/I+BkSSZpar3SOgjemQz4fg==", - "dev": true, - "dependencies": { - "@types/js-yaml": "^4.0.0", - "is-buffer": "^2.0.0", - "js-yaml": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-matter/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/vfile-matter/node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, - "node_modules/vfile-matter/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/vfile-message": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", @@ -41418,7 +38613,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "iconv-lite": "0.6.3" }, @@ -41430,7 +38626,8 @@ "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -41442,7 +38639,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "devOptional": true, + "optional": true, + "peer": true, "engines": { "node": ">=18" } @@ -41773,7 +38971,8 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" diff --git a/package.json b/package.json index 8eac8ece..187bffbd 100644 --- a/package.json +++ b/package.json @@ -136,6 +136,7 @@ "swr": "^2.2.5", "tailwind-scrollbar": "^3.1.0", "tailwindcss": "3.4.3", + "tldts": "^6.1.47", "tslib": "^2.3.0", "twitter-api-v2": "^1.16.0", "use-debounce": "^10.0.0", From 3e32e24748ec43e070262dd604e3f0263faabcae Mon Sep 17 00:00:00 2001 From: jamesread Date: Tue, 24 Sep 2024 00:40:01 +0000 Subject: [PATCH 06/59] feat: Better registration errors --- .../frontend/src/components/auth/register.tsx | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/apps/frontend/src/components/auth/register.tsx b/apps/frontend/src/components/auth/register.tsx index ff678025..0f3d3cb4 100644 --- a/apps/frontend/src/components/auth/register.tsx +++ b/apps/frontend/src/components/auth/register.tsx @@ -65,6 +65,17 @@ export function Register() { ); } +function getHelpfulReasonForRegistrationFailure(httpCode) { + switch (httpCode) { + case 400: + return 'Email already exists'; + case 404: + return 'Your browser got a 404 when trying to contact the API, the most likely reasons for this are the NEXT_PUBLIC_BACKEND_URL is set incorrectly, or the backend is not running.'; + } + + return 'Unhandled error: ' + httpCode; +} + export function RegisterAfter({ token, provider, @@ -97,23 +108,31 @@ export function RegisterAfter({ const onSubmit: SubmitHandler = async (data) => { setLoading(true); - const register = await fetchData('/auth/register', { + + await fetchData('/auth/register', { method: 'POST', body: JSON.stringify({ ...data }), - }); - if (register.status === 400) { - form.setError('email', { - message: 'Email already exists', - }); - + }).then((response) => { setLoading(false); - } - fireEvents('register'); + if (response.status === 200) { + fireEvents('register') - if (register.headers.get('activate')) { - router.push('/auth/activate'); - } + if (response.headers.get('activate') === "true") { + router.push('/auth/activate'); + } else { + router.push('/auth/login'); + } + } else { + form.setError('email', { + message: getHelpfulReasonForRegistrationFailure(response.status), + }); + } + }).catch(e => { + form.setError("email", { + message: 'General error: ' + e.toString() + '. Please check your browser console.', + }); + }) }; return ( From dbf58a9f09de070c12ba8edb8c34be6bc955fca0 Mon Sep 17 00:00:00 2001 From: jamesread Date: Tue, 24 Sep 2024 00:48:22 +0000 Subject: [PATCH 07/59] Add type to registration failure func --- apps/frontend/src/components/auth/register.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/auth/register.tsx b/apps/frontend/src/components/auth/register.tsx index 0f3d3cb4..cd10c7a8 100644 --- a/apps/frontend/src/components/auth/register.tsx +++ b/apps/frontend/src/components/auth/register.tsx @@ -65,7 +65,7 @@ export function Register() { ); } -function getHelpfulReasonForRegistrationFailure(httpCode) { +function getHelpfulReasonForRegistrationFailure(httpCode: int) { switch (httpCode) { case 400: return 'Email already exists'; From 2e24c78c5d89d96af2d7c1191a86c9c5fcf05d9c Mon Sep 17 00:00:00 2001 From: jamesread Date: Tue, 24 Sep 2024 00:55:34 +0000 Subject: [PATCH 08/59] Add number type to registration failure func --- apps/frontend/src/components/auth/register.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/auth/register.tsx b/apps/frontend/src/components/auth/register.tsx index cd10c7a8..67fefbdf 100644 --- a/apps/frontend/src/components/auth/register.tsx +++ b/apps/frontend/src/components/auth/register.tsx @@ -65,7 +65,7 @@ export function Register() { ); } -function getHelpfulReasonForRegistrationFailure(httpCode: int) { +function getHelpfulReasonForRegistrationFailure(httpCode: number) { switch (httpCode) { case 400: return 'Email already exists'; From 6ded2719c5602cde3af79534433f39e53104bb3d Mon Sep 17 00:00:00 2001 From: jamesread Date: Tue, 24 Sep 2024 11:47:22 +0100 Subject: [PATCH 09/59] rename .adoc to .md --- CONTRIBUTING.adoc => CONTRIBUTING.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CONTRIBUTING.adoc => CONTRIBUTING.md (100%) diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.adoc rename to CONTRIBUTING.md From ef9b58fde5c5e1ab2bc978d17bb9d7639aa691a4 Mon Sep 17 00:00:00 2001 From: jamesread Date: Tue, 24 Sep 2024 12:46:50 +0100 Subject: [PATCH 10/59] ci: GitHub workflow for ARM64 --- .github/workflows/build-containers.yml | 62 ++++++++++++++++++++------ 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-containers.yml b/.github/workflows/build-containers.yml index a3dfadc1..56d38ad4 100644 --- a/.github/workflows/build-containers.yml +++ b/.github/workflows/build-containers.yml @@ -8,8 +8,27 @@ on: - '*' jobs: - build-containers: + build-containers-common: runs-on: ubuntu-latest + outputs: + containerver: ${{ steps.getcontainerver.outputs.containerver }} + steps: + - name: Get Container Version + id: getcontainerver + run: | + echo "containerver=$(date +'%s')" >> "$GITHUB_OUTPUT" + + build-containers: + needs: build-containers-common + strategy: + matrix: + include: + - os: ubuntu-latest + arch: [amd64] + - os: ubuntu-22.04-arm + arch: [arm64] + + runs-on: ${{ matrix.os }} steps: - name: Checkout uses: actions/checkout@v4 @@ -26,24 +45,41 @@ jobs: - name: docker build run: ./var/docker/docker-build.sh - - name: Get date - run: | - echo "DATE=$(date +'%s')" >> "$GITHUB_ENV" - - name: Print post-build debug info run: | docker images - name: docker tag + env: + CONTAINERVER: ${{ needs.build-containers-common.outputs.containerver }} run: | - docker tag localhost/postiz ghcr.io/gitroomhq/postiz-app:${{ env.DATE }} - docker push ghcr.io/gitroomhq/postiz-app:${{ env.DATE }} + docker tag localhost/postiz ghcr.io/gitroomhq/postiz-app:${{ matrix.arch }}-${{ env.CONTAINERVER }} + docker push ghcr.io/gitroomhq/postiz-app:${{ matrix.arch }}-${{ env.CONTAINERVER }} - docker tag ghcr.io/gitroomhq/postiz-app:${{ env.DATE }} ghcr.io/gitroomhq/postiz-app:latest - docker push ghcr.io/gitroomhq/postiz-app:latest + docker tag localhost/postiz-devcontainer ghcr.io/gitroomhq/postiz-devcontainer:${{ env.CONTAINERVER }} + docker push ghcr.io/gitroomhq/postiz-devcontainer:${{ env.CONTAINERVER }} - docker tag localhost/postiz-devcontainer ghcr.io/gitroomhq/postiz-devcontainer:${{ env.DATE }} - docker push ghcr.io/gitroomhq/postiz-devcontainer:${{ env.DATE }} - - docker tag ghcr.io/gitroomhq/postiz-devcontainer:${{ env.DATE }} ghcr.io/gitroomhq/postiz-devcontainer:latest + docker tag ghcr.io/gitroomhq/postiz-devcontainer:${{ env.CONTAINERVER }} ghcr.io/gitroomhq/postiz-devcontainer:latest docker push ghcr.io/gitroomhq/postiz-devcontainer:latest + + build-container-manifest: + needs: build-containers + runs-on: ubuntu-latest + steps: + - name: Login to ghcr + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + + - name: Create Docker Manifest + env: + CONTAINERVER: ${{ needs.build-containers-common.outputs.containerver }} + run: | + docker manifest create \ + ghcr.io/gitroomhq/postiz-app:latest \ + ghcr.io/gitroomhq/postiz-app:amd64-${{ needs.build-containers.outputs.containerver }} \ + ghcr.io/gitroomhq/postiz-app:arm64-${{ needs.build-containers.outputs.containerver }} + + docker manifest push ghcr.io/gitroomhq/postiz-app:latest From c6ef1a81c37c47366b6cfe6cfeb5f3413ee47ca8 Mon Sep 17 00:00:00 2001 From: jamesread Date: Tue, 24 Sep 2024 12:51:36 +0100 Subject: [PATCH 11/59] ci: bugfix ARM64 runner --- .github/workflows/build-containers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-containers.yml b/.github/workflows/build-containers.yml index 56d38ad4..fde2e0c7 100644 --- a/.github/workflows/build-containers.yml +++ b/.github/workflows/build-containers.yml @@ -25,7 +25,7 @@ jobs: include: - os: ubuntu-latest arch: [amd64] - - os: ubuntu-22.04-arm + - os: [self-hosted, ARM64] arch: [arm64] runs-on: ${{ matrix.os }} From abeff59cf6af600da4c6cc5455ace8ae526e247d Mon Sep 17 00:00:00 2001 From: jamesread Date: Tue, 24 Sep 2024 13:16:22 +0100 Subject: [PATCH 12/59] ci: Fix container matrix job --- .github/workflows/build-containers.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-containers.yml b/.github/workflows/build-containers.yml index fde2e0c7..f6276a26 100644 --- a/.github/workflows/build-containers.yml +++ b/.github/workflows/build-containers.yml @@ -23,12 +23,12 @@ jobs: strategy: matrix: include: - - os: ubuntu-latest - arch: [amd64] - - os: [self-hosted, ARM64] - arch: [arm64] + - runnertags: ubuntu-latest + arch: amd64 + - runnertags: [self-hosted, ARM64] + arch: arm64 - runs-on: ${{ matrix.os }} + runs-on: ${{ matrix.runnertags }} steps: - name: Checkout uses: actions/checkout@v4 From f4341a218bcc786f790d45846b2df8fdee684b32 Mon Sep 17 00:00:00 2001 From: jamesread Date: Tue, 24 Sep 2024 13:25:14 +0100 Subject: [PATCH 13/59] ci: Fix container matrix job --- .github/workflows/build-containers.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-containers.yml b/.github/workflows/build-containers.yml index f6276a26..2c34863a 100644 --- a/.github/workflows/build-containers.yml +++ b/.github/workflows/build-containers.yml @@ -79,7 +79,7 @@ jobs: run: | docker manifest create \ ghcr.io/gitroomhq/postiz-app:latest \ - ghcr.io/gitroomhq/postiz-app:amd64-${{ needs.build-containers.outputs.containerver }} \ - ghcr.io/gitroomhq/postiz-app:arm64-${{ needs.build-containers.outputs.containerver }} + ghcr.io/gitroomhq/postiz-app:amd64-${{ env.CONTAINERVER }} \ + ghcr.io/gitroomhq/postiz-app:arm64-${{ env.CONTAINERVER }} docker manifest push ghcr.io/gitroomhq/postiz-app:latest From e3764a1f18383bd26dc83466c566707598d983d2 Mon Sep 17 00:00:00 2001 From: James Read Date: Tue, 24 Sep 2024 17:16:19 +0200 Subject: [PATCH 14/59] Update build-containers.yml --- .github/workflows/build-containers.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-containers.yml b/.github/workflows/build-containers.yml index 2c34863a..effc4454 100644 --- a/.github/workflows/build-containers.yml +++ b/.github/workflows/build-containers.yml @@ -75,7 +75,7 @@ jobs: - name: Create Docker Manifest env: - CONTAINERVER: ${{ needs.build-containers-common.outputs.containerver }} + CONTAINERVER: ${{ jobs.build-containers-common.outputs.containerver }} run: | docker manifest create \ ghcr.io/gitroomhq/postiz-app:latest \ From e3b1966be56ab9d2a40e30863c05e54d2f9084ac Mon Sep 17 00:00:00 2001 From: James Read Date: Tue, 24 Sep 2024 17:25:55 +0200 Subject: [PATCH 15/59] Update build-containers.yml --- .github/workflows/build-containers.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-containers.yml b/.github/workflows/build-containers.yml index effc4454..a0d83a75 100644 --- a/.github/workflows/build-containers.yml +++ b/.github/workflows/build-containers.yml @@ -63,7 +63,7 @@ jobs: docker push ghcr.io/gitroomhq/postiz-devcontainer:latest build-container-manifest: - needs: build-containers + needs: [build-containers, build-containers-common] runs-on: ubuntu-latest steps: - name: Login to ghcr @@ -75,7 +75,7 @@ jobs: - name: Create Docker Manifest env: - CONTAINERVER: ${{ jobs.build-containers-common.outputs.containerver }} + CONTAINERVER: ${{ needs.build-containers-common.outputs.containerver }} run: | docker manifest create \ ghcr.io/gitroomhq/postiz-app:latest \ From 3db361367be9ddf9cf5bfaed7bbac6bd8e5ef1c6 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Wed, 25 Sep 2024 11:12:59 +0700 Subject: [PATCH 16/59] feat: remove cron add logs --- apps/cron/src/cron.module.ts | 2 +- apps/workers/src/app/app.module.ts | 2 +- .../src/database/prisma/posts/posts.service.ts | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/cron/src/cron.module.ts b/apps/cron/src/cron.module.ts index a44744b9..4db31e81 100644 --- a/apps/cron/src/cron.module.ts +++ b/apps/cron/src/cron.module.ts @@ -8,6 +8,6 @@ import { BullMqModule } from '@gitroom/nestjs-libraries/bull-mq-transport-new/bu @Module({ imports: [DatabaseModule, ScheduleModule.forRoot(), BullMqModule], controllers: [], - providers: [CheckStars, SyncTrending], + providers: [...(!process.env.IS_GENERAL ? [CheckStars, SyncTrending] : [])], }) export class CronModule {} diff --git a/apps/workers/src/app/app.module.ts b/apps/workers/src/app/app.module.ts index efaf1029..20d2e381 100644 --- a/apps/workers/src/app/app.module.ts +++ b/apps/workers/src/app/app.module.ts @@ -8,7 +8,7 @@ import { BullMqModule } from '@gitroom/nestjs-libraries/bull-mq-transport-new/bu @Module({ imports: [DatabaseModule, BullMqModule], - controllers: [StarsController, PostsController], + controllers: [...!process.env.IS_GENERAL ? [StarsController] : [], PostsController], providers: [TrendingService], }) export class AppModule {} diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index 1ec0b844..63487f51 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -135,6 +135,7 @@ export class PostsService { `An error occurred while posting on ${firstPost.integration?.providerIdentifier}`, true ); + return; } @@ -160,6 +161,8 @@ export class PostsService { }`, true ); + + console.error('[Error] posting on', firstPost.integration?.providerIdentifier, err); } } From c84d8af915aa467301ff5be15bb45e3497f88060 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Wed, 25 Sep 2024 11:56:25 +0700 Subject: [PATCH 17/59] feat: console error --- .../database/prisma/posts/posts.service.ts | 11 -------- .../src/integrations/social.abstract.ts | 28 +++++++++++-------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index 63487f51..fbd051f2 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -290,17 +290,6 @@ export class PostsService { return this.postSocial(integration, posts, true); } - if ( - err instanceof BadBody && - process.env.EMAIL_FROM_ADDRESS === 'nevo@postiz.com' - ) { - await this._notificationService.sendEmail( - 'nevo@positz.com', - 'Bad body', - JSON.stringify(err.body) - ); - } - throw err; } } diff --git a/libraries/nestjs-libraries/src/integrations/social.abstract.ts b/libraries/nestjs-libraries/src/integrations/social.abstract.ts index 39034f55..eee510db 100644 --- a/libraries/nestjs-libraries/src/integrations/social.abstract.ts +++ b/libraries/nestjs-libraries/src/integrations/social.abstract.ts @@ -1,7 +1,8 @@ -export class RefreshToken {} +export class RefreshToken { + constructor(public json: string, public body: BodyInit) {} +} export class BadBody { - constructor(public body: BodyInit) { - } + constructor(public json: string, public body: BodyInit) {} } export class NotEnoughScopes {} @@ -10,20 +11,23 @@ export abstract class SocialAbstract { async fetch(url: string, options: RequestInit = {}) { const request = await fetch(url, options); - if (request.status !== 200 && request.status !== 201) { - try { - console.log(await request.json()); - } - catch (err) { - console.log('skip'); - } + if (request.status === 200 || request.status === 201) { + return request; } + + let json = '{}'; + try { + json = await request.json(); + } catch (err) { + json = '{}'; + } + if (request.status === 401) { - throw new RefreshToken(); + throw new RefreshToken(json, options.body!); } if (request.status === 400) { - throw new BadBody(options.body!); + throw new BadBody(json, options.body!); } return request; From 5fcc365efbd50fee9fea42d00e0e3d5d3cbdf92a Mon Sep 17 00:00:00 2001 From: jamesread Date: Wed, 25 Sep 2024 23:56:50 +0100 Subject: [PATCH 18/59] feat: Dont send activate=true if activation isnt required --- apps/backend/src/api/routes/auth.controller.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/backend/src/api/routes/auth.controller.ts b/apps/backend/src/api/routes/auth.controller.ts index bd14422e..1a0a8af2 100644 --- a/apps/backend/src/api/routes/auth.controller.ts +++ b/apps/backend/src/api/routes/auth.controller.ts @@ -30,7 +30,9 @@ export class AuthController { getOrgFromCookie ); - if (body.provider === 'LOCAL') { + const activationRequired = body.provider === 'LOCAL' && !!process.env.RESEND_API_KEY; + + if (activationRequired) { response.header('activate', 'true'); response.status(200).json({ activate: true }); return; From 69da6489ee6ae2ac3438e9742e09b752f84bb5fe Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 26 Sep 2024 10:28:48 +0700 Subject: [PATCH 19/59] feat: find linkedin error --- .../integrations/social/linkedin.provider.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index 6bacf92c..177b5994 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -9,7 +9,10 @@ import sharp from 'sharp'; import { lookup } from 'mime-types'; import { readOrFetch } from '@gitroom/helpers/utils/read.or.fetch'; import { removeMarkdown } from '@gitroom/helpers/utils/remove.markdown'; -import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import { + BadBody, + SocialAbstract, +} from '@gitroom/nestjs-libraries/integrations/social.abstract'; export class LinkedinProvider extends SocialAbstract implements SocialProvider { identifier = 'linkedin'; @@ -19,7 +22,11 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { refreshWait = true; async refreshToken(refresh_token: string): Promise { - const { access_token: accessToken, refresh_token: refreshToken, expires_in } = await ( + const { + access_token: accessToken, + refresh_token: refreshToken, + expires_in, + } = await ( await this.fetch('https://www.linkedin.com/oauth/v2/accessToken', { method: 'POST', headers: { @@ -262,7 +269,13 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { return finalOutput; } catch (err: any) { - throw 'eerr'; + throw new BadBody(JSON.stringify(err), { + // @ts-ignore + fileName, + personId, + picture, + type, + }); } } From 129b21b46beb28a435ff7d021aeb234d4ca97e29 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 26 Sep 2024 16:49:45 +0700 Subject: [PATCH 20/59] feat: check tiktok error --- .../src/integrations/social/tiktok.provider.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts index 15d3b766..92659c4b 100644 --- a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts @@ -5,7 +5,10 @@ import { SocialProvider, } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; import dayjs from 'dayjs'; -import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import { + BadBody, + SocialAbstract, +} from '@gitroom/nestjs-libraries/integrations/social.abstract'; import { TikTokDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/tiktok.dto'; export class TiktokProvider extends SocialAbstract implements SocialProvider { @@ -183,7 +186,10 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { }, ]; } catch (err) { - return []; + throw new BadBody(JSON.stringify(err), { + // @ts-ignore + postDetails + }); } } } From 940fc7e58318dc9326c2b6b78a4d98a460bf2f8a Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 27 Sep 2024 13:36:42 +0700 Subject: [PATCH 21/59] feat: choosable email --- .../backend/src/api/routes/auth.controller.ts | 8 ++- .../configuration/configuration.checker.ts | 1 - .../notifications/notification.service.ts | 4 ++ .../organizations/organization.repository.ts | 5 +- .../organizations/organization.service.ts | 2 +- .../src/emails/email.interface.ts | 5 ++ .../src/emails/empty.provider.ts | 9 ++++ .../src/emails/node.mailer.provider.ts | 40 +++++++++++++++ .../src/emails/resend.provider.ts | 19 +++++++ .../src/services/email.service.ts | 51 ++++++++++++++----- package-lock.json | 18 +++++++ package.json | 2 + 12 files changed, 144 insertions(+), 20 deletions(-) create mode 100644 libraries/nestjs-libraries/src/emails/email.interface.ts create mode 100644 libraries/nestjs-libraries/src/emails/empty.provider.ts create mode 100644 libraries/nestjs-libraries/src/emails/node.mailer.provider.ts create mode 100644 libraries/nestjs-libraries/src/emails/resend.provider.ts diff --git a/apps/backend/src/api/routes/auth.controller.ts b/apps/backend/src/api/routes/auth.controller.ts index 1a0a8af2..ba394cc4 100644 --- a/apps/backend/src/api/routes/auth.controller.ts +++ b/apps/backend/src/api/routes/auth.controller.ts @@ -8,11 +8,15 @@ import { ForgotReturnPasswordDto } from '@gitroom/nestjs-libraries/dtos/auth/for import { ForgotPasswordDto } from '@gitroom/nestjs-libraries/dtos/auth/forgot.password.dto'; import { ApiTags } from '@nestjs/swagger'; import { getCookieUrlFromDomain } from '@gitroom/helpers/subdomain/subdomain.management'; +import { EmailService } from '@gitroom/nestjs-libraries/services/email.service'; @ApiTags('Auth') @Controller('/auth') export class AuthController { - constructor(private _authService: AuthService) {} + constructor( + private _authService: AuthService, + private _emailService: EmailService + ) {} @Post('/register') async register( @Req() req: Request, @@ -30,7 +34,7 @@ export class AuthController { getOrgFromCookie ); - const activationRequired = body.provider === 'LOCAL' && !!process.env.RESEND_API_KEY; + const activationRequired = body.provider === 'LOCAL' && this._emailService.hasProvider(); if (activationRequired) { response.header('activate', 'true'); diff --git a/libraries/helpers/src/configuration/configuration.checker.ts b/libraries/helpers/src/configuration/configuration.checker.ts index b6a1ca6d..425de8ad 100644 --- a/libraries/helpers/src/configuration/configuration.checker.ts +++ b/libraries/helpers/src/configuration/configuration.checker.ts @@ -30,7 +30,6 @@ export class ConfigurationChecker { this.checkIsValidUrl('FRONTEND_URL') this.checkIsValidUrl('NEXT_PUBLIC_BACKEND_URL') this.checkIsValidUrl('BACKEND_INTERNAL_URL') - this.checkNonEmpty('RESEND_API_KEY', 'Needed to send user activation emails.') this.checkNonEmpty('CLOUDFLARE_ACCOUNT_ID', 'Needed to setup providers.') this.checkNonEmpty('CLOUDFLARE_ACCESS_KEY', 'Needed to setup providers.') this.checkNonEmpty('CLOUDFLARE_SECRET_ACCESS_KEY', 'Needed to setup providers.') diff --git a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts index bbe8d5b7..52f9ce3c 100644 --- a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts @@ -40,4 +40,8 @@ export class NotificationService { async sendEmail(to: string, subject: string, html: string) { await this._emailService.sendEmail(to, subject, html); } + + hasEmailProvider() { + return this._emailService.hasProvider(); + } } diff --git a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts index fd62013a..85084600 100644 --- a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts @@ -177,7 +177,8 @@ export class OrganizationRepository { } async createOrgAndUser( - body: Omit & { providerId?: string } + body: Omit & { providerId?: string }, + hasEmail: boolean ) { return this._organization.model.organization.create({ data: { @@ -187,7 +188,7 @@ export class OrganizationRepository { role: Role.SUPERADMIN, user: { create: { - activated: body.provider !== 'LOCAL' || !process.env.RESEND_API_KEY, + activated: body.provider !== 'LOCAL' || !hasEmail, email: body.email, password: body.password ? AuthService.hashPassword(body.password) diff --git a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts index 18c947fe..db239ac3 100644 --- a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts @@ -17,7 +17,7 @@ export class OrganizationService { async createOrgAndUser( body: Omit & { providerId?: string } ) { - return this._organizationRepository.createOrgAndUser(body); + return this._organizationRepository.createOrgAndUser(body, this._notificationsService.hasEmailProvider()); } addUserToOrg( diff --git a/libraries/nestjs-libraries/src/emails/email.interface.ts b/libraries/nestjs-libraries/src/emails/email.interface.ts new file mode 100644 index 00000000..7e6acbc0 --- /dev/null +++ b/libraries/nestjs-libraries/src/emails/email.interface.ts @@ -0,0 +1,5 @@ +export interface EmailInterface { + name: string; + validateEnvKeys: string[]; + sendEmail(to: string, subject: string, html: string, emailFromName: string, emailFromAddress: string): Promise; +} \ No newline at end of file diff --git a/libraries/nestjs-libraries/src/emails/empty.provider.ts b/libraries/nestjs-libraries/src/emails/empty.provider.ts new file mode 100644 index 00000000..c146c7ba --- /dev/null +++ b/libraries/nestjs-libraries/src/emails/empty.provider.ts @@ -0,0 +1,9 @@ +import { EmailInterface } from "./email.interface"; + +export class EmptyProvider implements EmailInterface { + name = 'no provider'; + validateEnvKeys = []; + async sendEmail(to: string, subject: string, html: string) { + return `No email provider found, email was supposed to be sent to ${to} with subject: ${subject} and ${html}, html`; + } +} \ No newline at end of file diff --git a/libraries/nestjs-libraries/src/emails/node.mailer.provider.ts b/libraries/nestjs-libraries/src/emails/node.mailer.provider.ts new file mode 100644 index 00000000..b0b8ca65 --- /dev/null +++ b/libraries/nestjs-libraries/src/emails/node.mailer.provider.ts @@ -0,0 +1,40 @@ +import nodemailer from 'nodemailer'; +import { EmailInterface } from '@gitroom/nestjs-libraries/emails/email.interface'; + +const transporter = nodemailer.createTransport({ + host: process.env.EMAIL_HOST, + port: +process.env.EMAIL_PORT!, + secure: process.env.EMAIL_SECURE === 'true', + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASS, + }, +}); + +export class NodeMailerProvider implements EmailInterface { + name = 'nodemailer'; + validateEnvKeys = [ + 'EMAIL_HOST', + 'EMAIL_PORT', + 'EMAIL_SECURE', + 'EMAIL_USER', + 'EMAIL_PASS', + ]; + async sendEmail( + to: string, + subject: string, + html: string, + emailFromName: string, + emailFromAddress: string + ) { + const sends = await transporter.sendMail({ + from:`${emailFromName} <${emailFromAddress}>`, // sender address + to: to, // list of receivers + subject: subject, // Subject line + text: html, // plain text body + html: html, // html body + }); + + return sends; + } +} diff --git a/libraries/nestjs-libraries/src/emails/resend.provider.ts b/libraries/nestjs-libraries/src/emails/resend.provider.ts new file mode 100644 index 00000000..15e34cf5 --- /dev/null +++ b/libraries/nestjs-libraries/src/emails/resend.provider.ts @@ -0,0 +1,19 @@ +import { Resend } from 'resend'; +import { EmailInterface } from '@gitroom/nestjs-libraries/emails/email.interface'; + +const resend = new Resend(process.env.RESEND_API_KEY || 're_132'); + +export class ResendProvider implements EmailInterface { + name = 'resend'; + validateEnvKeys = ['RESEND_API_KEY']; + async sendEmail(to: string, subject: string, html: string, emailFromName: string, emailFromAddress: string) { + const sends = await resend.emails.send({ + from: `${emailFromName} <${emailFromAddress}>`, + to, + subject, + html, + }); + + return sends; + } +} diff --git a/libraries/nestjs-libraries/src/services/email.service.ts b/libraries/nestjs-libraries/src/services/email.service.ts index 037a72fd..41f588c4 100644 --- a/libraries/nestjs-libraries/src/services/email.service.ts +++ b/libraries/nestjs-libraries/src/services/email.service.ts @@ -1,29 +1,52 @@ import { Injectable } from '@nestjs/common'; -import { Resend } from 'resend'; - -const resend = new Resend(process.env.RESEND_API_KEY || 're_132'); +import { EmailInterface } from '@gitroom/nestjs-libraries/emails/email.interface'; +import { ResendProvider } from '@gitroom/nestjs-libraries/emails/resend.provider'; +import { EmptyProvider } from '@gitroom/nestjs-libraries/emails/empty.provider'; +import { NodeMailerProvider } from '@gitroom/nestjs-libraries/emails/node.mailer.provider'; @Injectable() export class EmailService { + emailService: EmailInterface; + constructor() { + this.emailService = this.selectProvider(process.env.EMAIL_PROVIDER!); + console.log('Email service provider:', this.emailService.name); + for (const key of this.emailService.validateEnvKeys) { + if (!process.env[key]) { + console.error(`Missing environment variable: ${key}`); + } + } + } + + hasProvider() { + return !(this.emailService instanceof EmptyProvider); + } + + selectProvider(provider: string) { + switch (provider) { + case 'resend': + return new ResendProvider(); + case 'nodemailer': + return new NodeMailerProvider(); + default: + return new EmptyProvider(); + } + } + async sendEmail(to: string, subject: string, html: string) { - if (!process.env.RESEND_API_KEY) { - console.log('No Resend API Key found, skipping email sending'); - return; - } - if (!process.env.EMAIL_FROM_ADDRESS || !process.env.EMAIL_FROM_NAME) { - console.log('Email sender information not found in environment variables'); + console.log( + 'Email sender information not found in environment variables' + ); return; } - console.log('Sending email to', to); - const sends = await resend.emails.send({ - from: `${process.env.EMAIL_FROM_NAME} <${process.env.EMAIL_FROM_ADDRESS}>`, + const sends = await this.emailService.sendEmail( to, subject, html, - }); - + process.env.EMAIL_FROM_NAME, + process.env.EMAIL_FROM_ADDRESS + ); console.log(sends); } } diff --git a/package-lock.json b/package-lock.json index b410290d..d4a91654 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,6 +50,7 @@ "@types/md5": "^2.3.5", "@types/mime-types": "^2.1.4", "@types/multer": "^1.4.11", + "@types/nodemailer": "^6.4.16", "@types/remove-markdown": "^0.3.4", "@types/sha256": "^0.2.2", "@types/stripe": "^8.0.417", @@ -90,6 +91,7 @@ "nestjs-command": "^3.1.4", "next": "14.2.3", "next-plausible": "^3.12.0", + "nodemailer": "^6.9.15", "nx": "19.7.2", "openai": "^4.47.1", "polotno": "^2.10.5", @@ -12973,6 +12975,14 @@ "@types/node": "*" } }, + "node_modules/@types/nodemailer": { + "version": "6.4.16", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.16.tgz", + "integrity": "sha512-uz6hN6Pp0upXMcilM61CoKyjT7sskBoOWpptkjjJp8jIMlTdc3xG01U7proKkXzruMS4hS0zqtHNkNPFB20rKQ==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", @@ -29267,6 +29277,14 @@ "node": ">=6" } }, + "node_modules/nodemailer": { + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.15.tgz", + "integrity": "sha512-AHf04ySLC6CIfuRtRiEYtGEXgRfa6INgWGluDhnxTZhHSKvrBu7lc1VVchQ0d8nPc4cFaZoPq8vkyNoZr0TpGQ==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", diff --git a/package.json b/package.json index 187bffbd..0019d3f5 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ "@types/md5": "^2.3.5", "@types/mime-types": "^2.1.4", "@types/multer": "^1.4.11", + "@types/nodemailer": "^6.4.16", "@types/remove-markdown": "^0.3.4", "@types/sha256": "^0.2.2", "@types/stripe": "^8.0.417", @@ -110,6 +111,7 @@ "nestjs-command": "^3.1.4", "next": "14.2.3", "next-plausible": "^3.12.0", + "nodemailer": "^6.9.15", "nx": "19.7.2", "openai": "^4.47.1", "polotno": "^2.10.5", From 9235a8de1c9cf629296fd6c0c4230a5116d56005 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 27 Sep 2024 22:02:06 +0700 Subject: [PATCH 22/59] feat: fix linkedin chunks upload --- libraries/helpers/src/utils/read.or.fetch.ts | 7 +++- .../integrations/social/linkedin.provider.ts | 32 +++++++++++-------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/libraries/helpers/src/utils/read.or.fetch.ts b/libraries/helpers/src/utils/read.or.fetch.ts index 10c93904..f5b2338b 100644 --- a/libraries/helpers/src/utils/read.or.fetch.ts +++ b/libraries/helpers/src/utils/read.or.fetch.ts @@ -1,8 +1,13 @@ import {readFileSync} from "fs"; +import axios from 'axios'; export const readOrFetch = async (path: string) => { if (path.indexOf('http') === 0) { - return (await fetch(path)).arrayBuffer(); + return (await axios({ + url: path, + method: 'GET', + responseType: 'arraybuffer', + })).data; } return readFileSync(path); diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index 177b5994..1422505b 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -231,21 +231,25 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { const sendUrlRequest = uploadInstructions?.[0]?.uploadUrl || uploadUrl; const finalOutput = video || image; - const upload = await this.fetch(sendUrlRequest, { - method: 'PUT', - headers: { - 'X-Restli-Protocol-Version': '2.0.0', - 'LinkedIn-Version': '202402', - Authorization: `Bearer ${accessToken}`, - ...(fileName.indexOf('mp4') > -1 - ? { 'Content-Type': 'application/octet-stream' } - : {}), - }, - body: picture, - }); + const etags = []; + for (let i = 0; i < picture.length; i += 1024 * 1024 * 2) { + const upload = await this.fetch(sendUrlRequest, { + method: 'PUT', + headers: { + 'X-Restli-Protocol-Version': '2.0.0', + 'LinkedIn-Version': '202402', + Authorization: `Bearer ${accessToken}`, + ...(fileName.indexOf('mp4') > -1 + ? { 'Content-Type': 'application/octet-stream' } + : {}), + }, + body: picture.slice(i, i + 1024 * 1024 * 2), + }); + + etags.push(upload.headers.get('etag')); + } if (fileName.indexOf('mp4') > -1) { - const etag = upload.headers.get('etag'); const a = await this.fetch( 'https://api.linkedin.com/rest/videos?action=finalizeUpload', { @@ -254,7 +258,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { finalizeUploadRequest: { video, uploadToken: '', - uploadedPartIds: [etag], + uploadedPartIds: etags, }, }), headers: { From 2ce05febcdbaea885ac24c0be2cc6adcefeea50c Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 29 Sep 2024 19:17:15 +0700 Subject: [PATCH 23/59] feat: redirectmeto --- .../src/integrations/social/threads.provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts index 2a701987..2aa64e95 100644 --- a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts @@ -42,7 +42,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { `?client_id=${process.env.THREADS_APP_ID}` + `&redirect_uri=${encodeURIComponent( process.env.NODE_ENV === 'development' || !process.env.NODE_ENV - ? `https://integration.git.sn/integrations/social/threads` + ? `https://redirectmeto.com/${process.env.FRONTEND_URL}/integrations/social/threads` : `${process.env.FRONTEND_URL}/integrations/social/threads${ refresh ? `?refresh=${refresh}` : '' }` From 94157b10aeb84e6bfbba87ef3ccd4c425bd5d310 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 29 Sep 2024 19:25:14 +0700 Subject: [PATCH 24/59] feat: redirectmeto --- .../integrations/social/threads.provider.ts | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts index 2aa64e95..5355dc5d 100644 --- a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts @@ -41,11 +41,11 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { 'https://threads.net/oauth/authorize' + `?client_id=${process.env.THREADS_APP_ID}` + `&redirect_uri=${encodeURIComponent( - process.env.NODE_ENV === 'development' || !process.env.NODE_ENV - ? `https://redirectmeto.com/${process.env.FRONTEND_URL}/integrations/social/threads` - : `${process.env.FRONTEND_URL}/integrations/social/threads${ - refresh ? `?refresh=${refresh}` : '' - }` + `${ + process?.env.FRONTEND_URL?.indexOf('https') == -1 + ? `https://redirectmeto.com/${process?.env.FRONTEND_URL}` + : `${process?.env.FRONTEND_URL}` + }/integrations/social/threads` )}` + `&state=${state}` + `&scope=${encodeURIComponent(this.scopes.join(','))}`, @@ -104,8 +104,15 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { }; } - private async checkLoaded(mediaContainerId: string, accessToken: string): Promise { - const {status, id, error_message} = await (await this.fetch(`https://graph.threads.net/v1.0/${mediaContainerId}?fields=status,error_message&access_token=${accessToken}`)).json(); + private async checkLoaded( + mediaContainerId: string, + accessToken: string + ): Promise { + const { status, id, error_message } = await ( + await this.fetch( + `https://graph.threads.net/v1.0/${mediaContainerId}?fields=status,error_message&access_token=${accessToken}` + ) + ).json(); console.log(status, error_message); if (status === 'ERROR') { throw new Error(id); @@ -205,12 +212,8 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { mediaLoad.path.indexOf('.mp4') > -1 ? 'video_url' : 'image_url'; const media = new URLSearchParams({ - ...(type === 'video_url' - ? { video_url: mediaLoad.path } - : {}), - ...(type === 'image_url' - ? { image_url: mediaLoad.path } - : {}), + ...(type === 'video_url' ? { video_url: mediaLoad.path } : {}), + ...(type === 'image_url' ? { image_url: mediaLoad.path } : {}), is_carousel_item: 'true', media_type: type === 'video_url' @@ -234,7 +237,9 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { medias.push(mediaId); } - await Promise.all(medias.map((p: string) => this.checkLoaded(p, accessToken))); + await Promise.all( + medias.map((p: string) => this.checkLoaded(p, accessToken)) + ); const { id: containerId } = await ( await this.fetch( From 98384105b55eadf451bd00067cb943452a1e5b33 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 29 Sep 2024 19:26:14 +0700 Subject: [PATCH 25/59] feat: threads --- .../src/integrations/social/threads.provider.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts index 5355dc5d..756c43b6 100644 --- a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts @@ -64,11 +64,11 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { 'https://graph.threads.net/oauth/access_token' + `?client_id=${process.env.THREADS_APP_ID}` + `&redirect_uri=${encodeURIComponent( - process.env.NODE_ENV === 'development' || !process.env.NODE_ENV - ? `https://integration.git.sn/integrations/social/threads` - : `${process.env.FRONTEND_URL}/integrations/social/threads${ - params.refresh ? `?refresh=${params.refresh}` : '' - }` + `${ + process?.env.FRONTEND_URL?.indexOf('https') == -1 + ? `https://redirectmeto.com/${process?.env.FRONTEND_URL}` + : `${process?.env.FRONTEND_URL}` + }/integrations/social/threads` )}` + `&grant_type=authorization_code` + `&client_secret=${process.env.THREADS_APP_SECRET}` + From 44688ee77a56f4586028ddd344bc4c803f0e609c Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 29 Sep 2024 19:50:42 +0700 Subject: [PATCH 26/59] feat: better errors --- .../database/prisma/posts/posts.service.ts | 19 ++++++++++++++- .../src/integrations/social.abstract.ts | 24 +++++++++++-------- .../integrations/social/facebook.provider.ts | 12 ++++++---- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index fbd051f2..70308a97 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -162,7 +162,24 @@ export class PostsService { true ); - console.error('[Error] posting on', firstPost.integration?.providerIdentifier, err); + if (err instanceof BadBody) { + console.error( + '[Error] posting on', + firstPost.integration?.providerIdentifier, + err.identifier, + err.json, + err.body, + err + ); + + return ; + } + + console.error( + '[Error] posting on', + firstPost.integration?.providerIdentifier, + err + ); } } diff --git a/libraries/nestjs-libraries/src/integrations/social.abstract.ts b/libraries/nestjs-libraries/src/integrations/social.abstract.ts index eee510db..9d06d017 100644 --- a/libraries/nestjs-libraries/src/integrations/social.abstract.ts +++ b/libraries/nestjs-libraries/src/integrations/social.abstract.ts @@ -1,14 +1,22 @@ export class RefreshToken { - constructor(public json: string, public body: BodyInit) {} + constructor( + public identifier: string, + public json: string, + public body: BodyInit + ) {} } export class BadBody { - constructor(public json: string, public body: BodyInit) {} + constructor( + public identifier: string, + public json: string, + public body: BodyInit + ) {} } export class NotEnoughScopes {} export abstract class SocialAbstract { - async fetch(url: string, options: RequestInit = {}) { + async fetch(url: string, options: RequestInit = {}, identifier = '') { const request = await fetch(url, options); if (request.status === 200 || request.status === 201) { @@ -17,20 +25,16 @@ export abstract class SocialAbstract { let json = '{}'; try { - json = await request.json(); + json = await request.text(); } catch (err) { json = '{}'; } if (request.status === 401) { - throw new RefreshToken(json, options.body!); + throw new RefreshToken(identifier, json, options.body!); } - if (request.status === 400) { - throw new BadBody(json, options.body!); - } - - return request; + throw new BadBody(identifier, json, options.body!); } checkScopes(required: string[], got: string | string[]) { diff --git a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts index 7332848a..13c24f74 100644 --- a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts @@ -187,7 +187,8 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { description: firstPost.message, published: true, }), - } + }, + 'upload mp4' ) ).json(); @@ -210,7 +211,8 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { url: media.url, published: false, }), - } + }, + 'upload images slides' ) ).json(); @@ -235,7 +237,8 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { message: firstPost.message, published: true, }), - } + }, + 'finalize upload' ) ).json(); @@ -259,7 +262,8 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { : {}), message: comment.message, }), - } + }, + 'add comment' ) ).json(); From c40039423f9633594414cb60bbc35b7a0cb63f20 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 29 Sep 2024 19:55:17 +0700 Subject: [PATCH 27/59] feat: fix bad body --- .../src/integrations/social/linkedin.provider.ts | 2 +- .../nestjs-libraries/src/integrations/social/tiktok.provider.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index 1422505b..b76db213 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -273,7 +273,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { return finalOutput; } catch (err: any) { - throw new BadBody(JSON.stringify(err), { + throw new BadBody('error-posting-to-linkedin', JSON.stringify(err), { // @ts-ignore fileName, personId, diff --git a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts index 92659c4b..6bc13da5 100644 --- a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts @@ -186,7 +186,7 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { }, ]; } catch (err) { - throw new BadBody(JSON.stringify(err), { + throw new BadBody('titok-error', JSON.stringify(err), { // @ts-ignore postDetails }); From fde1efa15424882818220bdd312789532537312e Mon Sep 17 00:00:00 2001 From: Nevo David <100117126+nevo-david@users.noreply.github.com> Date: Mon, 30 Sep 2024 14:28:56 +0700 Subject: [PATCH 28/59] Update README.md --- README.md | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 88678384..df62e7aa 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,3 @@ - -

- - Follow me - -
- - - DevFest - - -

-

-

@@ -68,6 +54,22 @@
+ +

+


+

We participate in Hacktoberfest 2024! 🎉🎊

+

We are sending a t-shirt for every merged PR! (max 1 per person)

+

Rules:

+
    +
  • You must create an issue before making a pull request.
  • +
  • You can also ask to be assigned to an issue. During Hacktoberfest, each issue can have multiple assignees.
  • +
  • We have to approve the issue and add a "hacktoberfest" tag.
  • +
  • We encourage everybody to contribute to all types of issues. We will only send swag for issues with features and bug fixes (no typos, sorry).
  • +
+

+


+

+

From 566d13f015b34e9ef96b9828a85289b2677397b6 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Tue, 1 Oct 2024 01:07:49 +0900 Subject: [PATCH 29/59] docs: update CONTRIBUTING.md Brabch -> Branch --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index be4a1fa5..619dee9c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ As a general rule; ## How to contribute -This project follows a Fork/Feature Brabch/Pull Request model. If you're not familiar with this, here's how it works: +This project follows a Fork/Feature Branch/Pull Request model. If you're not familiar with this, here's how it works: 1. Fork the project to your GitHub account. 2. Clone your fork to your local machine. From 57a3a9094118c1477d60729652910d9252493a8f Mon Sep 17 00:00:00 2001 From: JeevaRamanathan Date: Tue, 1 Oct 2024 17:18:00 +0530 Subject: [PATCH 30/59] fix: Checkbox tick visible on both modes Signed-off-by: JeevaRamanathan --- .../src/form/checkbox.tsx | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/libraries/react-shared-libraries/src/form/checkbox.tsx b/libraries/react-shared-libraries/src/form/checkbox.tsx index a16521b7..4e54b560 100644 --- a/libraries/react-shared-libraries/src/form/checkbox.tsx +++ b/libraries/react-shared-libraries/src/form/checkbox.tsx @@ -4,21 +4,26 @@ import clsx from 'clsx'; import Image from 'next/image'; import { useFormContext, useWatch } from 'react-hook-form'; -export const Checkbox = forwardRef void; - variant?: 'default' | 'hollow'; -}>((props, ref: any) => { +export const Checkbox = forwardRef< + null, + { + checked?: boolean; + disableForm?: boolean; + name?: string; + className?: string; + onChange?: (event: { target: { name?: string; value: boolean } }) => void; + variant?: 'default' | 'hollow'; + } +>((props, ref: any) => { const { checked, className, disableForm, variant } = props; const form = useFormContext(); const register = disableForm ? {} : form.register(props.name!); - const watch = disableForm ? undefined : useWatch({ - name: props.name!, - }); + const watch = disableForm + ? undefined + : useWatch({ + name: props.name!, + }); const [currentStatus, setCurrentStatus] = useState(watch || checked); const changeStatus = useCallback(() => { @@ -26,7 +31,9 @@ export const Checkbox = forwardRef {currentStatus && ( - Checked +
+ + + +
)} ); From f21ea604cefc4bb483237a367788531f2981bb8b Mon Sep 17 00:00:00 2001 From: JeevaRamanathan Date: Tue, 1 Oct 2024 17:33:34 +0530 Subject: [PATCH 31/59] removed formatting --- .../src/form/checkbox.tsx | 67 ++++++++----------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/libraries/react-shared-libraries/src/form/checkbox.tsx b/libraries/react-shared-libraries/src/form/checkbox.tsx index 4e54b560..8090d000 100644 --- a/libraries/react-shared-libraries/src/form/checkbox.tsx +++ b/libraries/react-shared-libraries/src/form/checkbox.tsx @@ -4,26 +4,21 @@ import clsx from 'clsx'; import Image from 'next/image'; import { useFormContext, useWatch } from 'react-hook-form'; -export const Checkbox = forwardRef< - null, - { - checked?: boolean; - disableForm?: boolean; - name?: string; - className?: string; - onChange?: (event: { target: { name?: string; value: boolean } }) => void; - variant?: 'default' | 'hollow'; - } ->((props, ref: any) => { +export const Checkbox = forwardRef void; + variant?: 'default' | 'hollow'; +}>((props, ref: any) => { const { checked, className, disableForm, variant } = props; const form = useFormContext(); const register = disableForm ? {} : form.register(props.name!); - const watch = disableForm - ? undefined - : useWatch({ - name: props.name!, - }); + const watch = disableForm ? undefined : useWatch({ + name: props.name!, + }); const [currentStatus, setCurrentStatus] = useState(watch || checked); const changeStatus = useCallback(() => { @@ -31,9 +26,7 @@ export const Checkbox = forwardRef< props?.onChange?.({ target: { name: props.name!, value: !currentStatus } }); if (!disableForm) { // @ts-ignore - register?.onChange?.({ - target: { name: props.name!, value: !currentStatus }, - }); + register?.onChange?.({ target: { name: props.name!, value: !currentStatus } }); } }, [currentStatus]); @@ -44,29 +37,27 @@ export const Checkbox = forwardRef< onClick={changeStatus} className={clsx( 'cursor-pointer rounded-[4px] select-none w-[24px] h-[24px] justify-center items-center flex', - variant === 'default' || !variant - ? 'bg-forth' - : 'border-customColor1 border-2 bg-customColor2', + variant === 'default' || !variant ? ('bg-forth') : ('border-customColor1 border-2 bg-customColor2'), className )} > {currentStatus && ( -
- - - -
- )} +
+ + + +
+ )} ); }); From e2e06f6f4cd240485a92eb89a57c09ada37f7eef Mon Sep 17 00:00:00 2001 From: Zor0000 Date: Wed, 2 Oct 2024 01:19:24 +0530 Subject: [PATCH 32/59] Updated button style to rounded edges --- apps/frontend/src/components/auth/login.tsx | 2 +- apps/frontend/src/components/auth/register.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/components/auth/login.tsx b/apps/frontend/src/components/auth/login.tsx index 9810e5d6..13a458ec 100644 --- a/apps/frontend/src/components/auth/login.tsx +++ b/apps/frontend/src/components/auth/login.tsx @@ -89,7 +89,7 @@ export function Login() {
-
diff --git a/apps/frontend/src/components/auth/register.tsx b/apps/frontend/src/components/auth/register.tsx index 67fefbdf..e22b3653 100644 --- a/apps/frontend/src/components/auth/register.tsx +++ b/apps/frontend/src/components/auth/register.tsx @@ -198,7 +198,7 @@ export function RegisterAfter({
-
From 9c9c7329fcaf36aa283eb7a2878b4989113ddba9 Mon Sep 17 00:00:00 2001 From: Akash Jana <103350981+AkashJana18@users.noreply.github.com> Date: Wed, 2 Oct 2024 14:16:54 +0530 Subject: [PATCH 33/59] doc: Update CONTRIBUTING.md --- CONTRIBUTING.md | 69 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 619dee9c..590ba55b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,20 +14,73 @@ As a general rule; - **If a change is less than 3 lines**: You're probably safe just to submit the change without a discussion. This includes typos, dependency changes, and quick fixes, etc. - **If a change is more than 3 lines**: It's probably best to discuss the change in an issue or on discord first. This is simply because you might not be aware of the roadmap for the project, or understand the impact this change might have. We're just trying to save you time here, and importantly, avoid you being disappointed if your change isn't accepted. +## Types of Contributions + +Contributions can include: +- **Code improvements:** Fixing bugs or adding new features. +- **Documentation updates:** Enhancing clarity or adding missing information. +- **Feature requests:** Suggesting new capabilities or integrations. +- **Bug reports:** Identifying and reporting issues. + ## How to contribute This project follows a Fork/Feature Branch/Pull Request model. If you're not familiar with this, here's how it works: -1. Fork the project to your GitHub account. -2. Clone your fork to your local machine. -3. Create a new branch for your changes. -4. Make your changes in this new branch. -5. Push your changes to your fork. -6. Create a pull request from your fork to this project. -7. We'll review your changes and approve or comment on them. +1. **Fork the project:** Create a personal copy of the repository on your GitHub account. +2. **Clone your fork:** Bring a copy of your fork to your local machine. + ```bash + git clone https://github.com/YOUR_USERNAME/postiz.git + ``` +3. **Create a new branch**: Start a new branch for your changes + ```bash + git checkout -b feature/your-feature-name + ``` +6. **Make your changes**: Implement the changes you wish to contribute. +7. **Push your changes**: Upload your changes to your fork. + ```bash + git push origin feature/your-feature-name + ``` +9. **Create a pull request**: Propose your changes to the main project. + ## Setting up your development environment +To contribute effectively, you’ll need to set up your development environment. Follow the instructions below: -Documentation on how to setup a development environment is available here; https://docs.postiz.com/installation/development +### Prerequisites +1. **Node.js** (version 18+): Install [Node.js](https://nodejs.org/en/download/package-manager) +2. **PostgreSQL**: Install PostgreSQL for your database. You can run it in Docker: +```bash +docker run -e POSTGRES_USER=root -e POSTGRES_PASSWORD=your_password --name postgres -p 5432:5432 -d postgres +``` +3. **Redis**: Also install Redis, which can be done using Docker: +```bash +docker run --name redis -p 6379:6379 -d redis +``` +4. **Cloud Services** + - Cloudflare R2: For image uploads and storage (optional). + - Social Media API Keys: Obtain various API keys and secrets for services you wish to integrate (e.g., Reddit, X, Instagram). + +### Setting Up the Project +1. **Clone the repository**: +```bash +git clone https://github.com/gitroomhq/gitroom +``` +2. **Set environment variables**: Copy `.env.example` to `.env` and fill in the required values. +3. **Install dependencies**: +```bash +npm install +``` +4. **Generate the Prisma client and run migrations**: +```bash +npm run prisma-db-push +``` +5. **Run the project**: +```bash +npm run dev +``` +If everything runs successfully, open http://localhost:4200 in your browser! + +Need Help? +If you encounter any issues, please visit our support page or check the community forums. Your contributions help make Postiz better! From c274e26b1778555961b41bd4317a37cab15ba094 Mon Sep 17 00:00:00 2001 From: Akash Jana <103350981+AkashJana18@users.noreply.github.com> Date: Wed, 2 Oct 2024 16:42:24 +0530 Subject: [PATCH 34/59] doc: Changes according to review --- CONTRIBUTING.md | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 590ba55b..f04e4d88 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,49 +38,17 @@ This project follows a Fork/Feature Branch/Pull Request model. If you're not fam 6. **Make your changes**: Implement the changes you wish to contribute. 7. **Push your changes**: Upload your changes to your fork. ```bash - git push origin feature/your-feature-name + git push -u origin feature/your-feature-name ``` 9. **Create a pull request**: Propose your changes to the main project. ## Setting up your development environment -To contribute effectively, you’ll need to set up your development environment. Follow the instructions below: +To contribute effectively, you’ll need to set up your development environment. Follow the instructions on this page: https://docs.postiz.com/installation/development -### Prerequisites -1. **Node.js** (version 18+): Install [Node.js](https://nodejs.org/en/download/package-manager) -2. **PostgreSQL**: Install PostgreSQL for your database. You can run it in Docker: -```bash -docker run -e POSTGRES_USER=root -e POSTGRES_PASSWORD=your_password --name postgres -p 5432:5432 -d postgres -``` -3. **Redis**: Also install Redis, which can be done using Docker: -```bash -docker run --name redis -p 6379:6379 -d redis -``` -4. **Cloud Services** - - Cloudflare R2: For image uploads and storage (optional). - - Social Media API Keys: Obtain various API keys and secrets for services you wish to integrate (e.g., Reddit, X, Instagram). - -### Setting Up the Project -1. **Clone the repository**: -```bash -git clone https://github.com/gitroomhq/gitroom -``` -2. **Set environment variables**: Copy `.env.example` to `.env` and fill in the required values. -3. **Install dependencies**: -```bash -npm install -``` -4. **Generate the Prisma client and run migrations**: -```bash -npm run prisma-db-push -``` -5. **Run the project**: -```bash -npm run dev -``` -If everything runs successfully, open http://localhost:4200 in your browser! Need Help? -If you encounter any issues, please visit our support page or check the community forums. Your contributions help make Postiz better! +If you encounter any issues, please visit our [support page](https://docs.postiz.com/support) or check the community forums. Your contributions help make Postiz better! + From b2be32cad745acfbf57e88eba9581a658d6282db Mon Sep 17 00:00:00 2001 From: JeevaRamanathan Date: Wed, 2 Oct 2024 19:25:06 +0530 Subject: [PATCH 35/59] fix:Improve Icon and Toast Text Visibility in Profile Settings Screen for Light Mode --- apps/frontend/src/components/layout/settings.component.tsx | 4 ++-- libraries/react-shared-libraries/src/toaster/toaster.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/frontend/src/components/layout/settings.component.tsx b/apps/frontend/src/components/layout/settings.component.tsx index a0d8e817..c229216b 100644 --- a/apps/frontend/src/components/layout/settings.component.tsx +++ b/apps/frontend/src/components/layout/settings.component.tsx @@ -155,7 +155,7 @@ export const SettingsPopup: FC<{ getRef?: Ref }> = (props) => { />
-
+
Upload image
@@ -173,7 +173,7 @@ export const SettingsPopup: FC<{ getRef?: Ref }> = (props) => { >
diff --git a/libraries/react-shared-libraries/src/toaster/toaster.tsx b/libraries/react-shared-libraries/src/toaster/toaster.tsx index 88eb1c93..5768ca7b 100644 --- a/libraries/react-shared-libraries/src/toaster/toaster.tsx +++ b/libraries/react-shared-libraries/src/toaster/toaster.tsx @@ -68,7 +68,7 @@ export const Toaster = () => { )} -
{toasterText}
+
{toasterText}
Date: Thu, 3 Oct 2024 10:38:29 +0530 Subject: [PATCH 36/59] Create SECURITY.md --- SECURITY.md | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..712082c0 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,44 @@ +# Security Policy + +## Introduction + +The Postiz app is committed to ensuring the security and integrity of our users' data. This security policy outlines our procedures for handling security vulnerabilities and our disclosure policy. + +## Reporting Security Vulnerabilities + +If you discover a security vulnerability in the Postiz app, please report it to us privately via email to one of the maintainers: + +* @nevo-david ([nevo.david@postiz.app](mailto:nevo.david@postiz.app)) +* @jamesread ([james.read@postiz.app](mailto:james.read@postiz.app)) +* @jonathan-irvin ([jonathan.irvin@postiz.app](mailto:jonathan.irvin@postiz.app)) + +When reporting a security vulnerability, please provide as much detail as possible, including: + +* A clear description of the vulnerability +* Steps to reproduce the vulnerability +* Any relevant code or configuration files + +## Supported Versions + +This project currently only supports the latest release. We recommend that users always use the latest version of the Postiz app to ensure they have the latest security patches. + +## Disclosure Guidelines + +We follow a private disclosure policy. If you discover a security vulnerability, please report it to us privately via email to one of the maintainers listed above. We will respond promptly to reports of vulnerabilities and work to resolve them as quickly as possible. + +We will not publicly disclose security vulnerabilities until a patch or fix is available to prevent malicious actors from exploiting the vulnerability before a fix is released. + +## Security Vulnerability Response Process + +We take security vulnerabilities seriously and will respond promptly to reports of vulnerabilities. Our response process includes: + +* Investigating the report and verifying the vulnerability. +* Developing a patch or fix for the vulnerability. +* Releasing the patch or fix as soon as possible. +* Notifying users of the vulnerability and the patch or fix. + +## Template Attribution + +This SECURITY.md file is based on the [GitHub Security Policy Template](https://github.com/github/security/blob/master/SECURITY.md). + +Thank you for helping to keep the `postiz-app` secure! From 8d4d450feef081563e35f9b592731f09e237389b Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 3 Oct 2024 13:33:29 +0700 Subject: [PATCH 37/59] feat: slack and discord --- .../src/api/routes/integrations.controller.ts | 83 +++++-- .../public/icons/platforms/discord.png | Bin 0 -> 14896 bytes .../frontend/public/icons/platforms/slack.png | Bin 0 -> 16549 bytes .../src/components/launches/bot.picture.tsx | 101 ++++++++ .../components/launches/calendar.context.tsx | 2 + .../launches/launches.component.tsx | 6 +- .../src/components/launches/menu/menu.tsx | 69 +++++- .../discord/discord.channel.select.tsx | 44 ++++ .../providers/discord/discord.provider.tsx | 25 ++ .../launches/providers/show.all.providers.tsx | 4 + .../providers/slack/slack.channel.select.tsx | 44 ++++ .../providers/slack/slack.provider.tsx | 25 ++ .../onboarding/connect.channels.tsx | 2 + .../integrations/integration.repository.ts | 13 +- .../integrations/integration.service.ts | 4 + .../database/prisma/posts/posts.service.ts | 5 +- .../src/dtos/posts/create.post.dto.ts | 4 + .../posts/providers-settings/discord.dto.ts | 8 + .../posts/providers-settings/slack.dto.ts | 8 + .../src/integrations/integration.manager.ts | 4 + .../integrations/social/discord.provider.ts | 227 ++++++++++++++++++ .../social/linkedin.page.provider.ts | 6 +- .../integrations/social/linkedin.provider.ts | 2 + .../src/integrations/social/slack.provider.ts | 207 ++++++++++++++++ .../social/social.integrations.interface.ts | 21 +- .../src/helpers/image.with.fallback.tsx | 7 +- 26 files changed, 894 insertions(+), 27 deletions(-) create mode 100644 apps/frontend/public/icons/platforms/discord.png create mode 100644 apps/frontend/public/icons/platforms/slack.png create mode 100644 apps/frontend/src/components/launches/bot.picture.tsx create mode 100644 apps/frontend/src/components/launches/providers/discord/discord.channel.select.tsx create mode 100644 apps/frontend/src/components/launches/providers/discord/discord.provider.tsx create mode 100644 apps/frontend/src/components/launches/providers/slack/slack.channel.select.tsx create mode 100644 apps/frontend/src/components/launches/providers/slack/slack.provider.tsx create mode 100644 libraries/nestjs-libraries/src/dtos/posts/providers-settings/discord.dto.ts create mode 100644 libraries/nestjs-libraries/src/dtos/posts/providers-settings/slack.dto.ts create mode 100644 libraries/nestjs-libraries/src/integrations/social/discord.provider.ts create mode 100644 libraries/nestjs-libraries/src/integrations/social/slack.provider.ts diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index a9a92b2d..b0e01043 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -46,21 +46,68 @@ export class IntegrationsController { return { integrations: ( await this._integrationService.getIntegrationsList(org.id) - ).map((p) => ({ - name: p.name, - id: p.id, - internalId: p.internalId, - disabled: p.disabled, - picture: p.picture, - identifier: p.providerIdentifier, - inBetweenSteps: p.inBetweenSteps, - refreshNeeded: p.refreshNeeded, - type: p.type, - time: JSON.parse(p.postingTimes) - })), + ).map((p) => { + const findIntegration = this._integrationManager.getSocialIntegration( + p.providerIdentifier + ); + return { + name: p.name, + id: p.id, + internalId: p.internalId, + disabled: p.disabled, + picture: p.picture, + identifier: p.providerIdentifier, + inBetweenSteps: p.inBetweenSteps, + refreshNeeded: p.refreshNeeded, + type: p.type, + time: JSON.parse(p.postingTimes), + changeProfilePicture: !!findIntegration.changeProfilePicture, + changeNickName: !!findIntegration.changeNickname, + }; + }), }; } + @Post('/:id/nickname') + async setNickname( + @GetOrgFromRequest() org: Organization, + @Param('id') id: string, + @Body() body: { name: string; picture: string } + ) { + const integration = await this._integrationService.getIntegrationById( + org.id, + id + ); + if (!integration) { + throw new Error('Invalid integration'); + } + + const manager = this._integrationManager.getSocialIntegration( + integration.providerIdentifier + ); + if (!manager.changeProfilePicture && !manager.changeNickname) { + throw new Error('Invalid integration'); + } + + const { url } = manager.changeProfilePicture + ? await manager.changeProfilePicture( + integration.internalId, + integration.token, + body.picture + ) + : { url: '' }; + + const { name } = manager.changeNickname + ? await manager.changeNickname( + integration.internalId, + integration.token, + body.name + ) + : { name: '' }; + + return this._integrationService.updateNameAndUrl(id, name, url); + } + @Get('/:id') getSingleIntegration( @Param('id') id: string, @@ -129,7 +176,11 @@ export class IntegrationsController { } if (integrationProvider[body.name]) { - return integrationProvider[body.name](getIntegration.token, body.data); + return integrationProvider[body.name]( + getIntegration.token, + body.data, + getIntegration.internalId + ); } throw new Error('Function not found'); } @@ -144,7 +195,11 @@ export class IntegrationsController { } if (integrationProvider[body.name]) { - return integrationProvider[body.name](getIntegration.token, body.data); + return integrationProvider[body.name]( + getIntegration.token, + body.data, + getIntegration.internalId + ); } throw new Error('Function not found'); } diff --git a/apps/frontend/public/icons/platforms/discord.png b/apps/frontend/public/icons/platforms/discord.png new file mode 100644 index 0000000000000000000000000000000000000000..90af762aabcb6d8a18ead0d012441e831611b3db GIT binary patch literal 14896 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 literal 0 HcmV?d00001 diff --git a/apps/frontend/src/components/launches/bot.picture.tsx b/apps/frontend/src/components/launches/bot.picture.tsx new file mode 100644 index 00000000..fadac522 --- /dev/null +++ b/apps/frontend/src/components/launches/bot.picture.tsx @@ -0,0 +1,101 @@ +import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; +import React, { FC, FormEventHandler, useCallback, useState } from 'react'; +import { Integrations } from '@gitroom/frontend/components/launches/calendar.context'; +import { useModals } from '@mantine/modals'; +import { Input } from '@gitroom/react/form/input'; +import { Button } from '@gitroom/react/form/button'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import { useToaster } from '@gitroom/react/toaster/toaster'; +import { + MediaComponent, + showMediaBox, +} from '@gitroom/frontend/components/media/media.component'; + +export const BotPicture: FC<{ + integration: Integrations; + canChangeProfilePicture: boolean; + canChangeNickName: boolean; + mutate: () => void; +}> = (props) => { + const modal = useModals(); + const toast = useToaster(); + const [nick, setNickname] = useState(props.integration.name); + const [picture, setPicture] = useState(props.integration.picture); + + const fetch = useFetch(); + const submitForm: FormEventHandler = useCallback( + async (e) => { + e.preventDefault(); + await fetch(`/integrations/${props.integration.id}/nickname`, { + method: 'POST', + body: JSON.stringify({ name: nick, picture }), + }); + + props.mutate(); + toast.show('Updated', 'success'); + modal.closeAll(); + }, + [nick, picture, props.mutate] + ); + + const openMedia = useCallback(() => { + showMediaBox((values) => { + setPicture(values.path); + }); + }, []); + + return ( +
+ + + +
+
+ {props.canChangeProfilePicture && ( +
+ Bot Picture + +
+ )} + {props.canChangeNickName && ( + setNickname(e.target.value)} + name="Nickname" + label="Nickname" + placeholder="" + disableForm={true} + /> + )} + +
+ +
+
+
+
+ ); +}; diff --git a/apps/frontend/src/components/launches/calendar.context.tsx b/apps/frontend/src/components/launches/calendar.context.tsx index 901b02f5..2c508e66 100644 --- a/apps/frontend/src/components/launches/calendar.context.tsx +++ b/apps/frontend/src/components/launches/calendar.context.tsx @@ -57,6 +57,8 @@ export interface Integrations { identifier: string; type: string; picture: string; + changeProfilePicture: boolean; + changeNickName: boolean; time: { time: number }[]; } diff --git a/apps/frontend/src/components/launches/launches.component.tsx b/apps/frontend/src/components/launches/launches.component.tsx index d0a5ef52..27cf01dc 100644 --- a/apps/frontend/src/components/launches/launches.component.tsx +++ b/apps/frontend/src/components/launches/launches.component.tsx @@ -94,7 +94,7 @@ export const LaunchesComponent = () => { useEffect(() => { if (typeof window === 'undefined') { - return ; + return; } if (search.get('scope') === 'missing') { toast.show('You have to approve all the channel permissions', 'warning'); @@ -117,7 +117,7 @@ export const LaunchesComponent = () => {
-
+

Channels

{sortedIntegrations.length === 0 && ( @@ -196,6 +196,8 @@ export const LaunchesComponent = () => { {integration.name}
void, + mutate: () => void; onChange: (shouldReload: boolean) => void; }> = (props) => { - const { canEnable, canDisable, id, onChange, mutate } = props; + const { + canEnable, + canDisable, + id, + onChange, + mutate, + canChangeProfilePicture, + canChangeNickName, + } = props; const fetch = useFetch(); const { integrations } = useCalendar(); const toast = useToaster(); @@ -98,8 +109,30 @@ export const Menu: FC<{ withCloseButton: false, closeOnEscape: false, closeOnClickOutside: false, + children: , + }); + setShow(false); + }, [integrations]); + + const changeBotPicture = useCallback(() => { + const findIntegration = integrations.find( + (integration) => integration.id === id + ); + modal.openModal({ + classNames: { + modal: 'w-[100%] max-w-[600px] bg-transparent text-textColor', + }, + size: '100%', + withCloseButton: false, + closeOnEscape: true, + closeOnClickOutside: true, children: ( - + ), }); setShow(false); @@ -128,6 +161,36 @@ export const Menu: FC<{ onClick={(e) => e.stopPropagation()} className={`absolute top-[100%] left-0 p-[8px] px-[20px] bg-fifth flex flex-col gap-[16px] z-[100] rounded-[8px] border border-tableBorder ${interClass} text-nowrap`} > + {(canChangeProfilePicture || canChangeNickName) && ( +
+
+ + + +
+
+ Change Bot{' '} + {[ + canChangeProfilePicture && 'Picture', + canChangeNickName && 'Nickname', + ] + .filter((f) => f) + .join(' / ')} +
+
+ )}
void; +}> = (props) => { + const { onChange, name } = props; + const customFunc = useCustomProviderFunction(); + const [publications, setOrgs] = useState([]); + const { getValues } = useSettings(); + const [currentMedia, setCurrentMedia] = useState(); + + const onChangeInner = (event: { target: { value: string, name: string } }) => { + setCurrentMedia(event.target.value); + onChange(event); + }; + + useEffect(() => { + customFunc.get('channels').then((data) => setOrgs(data)); + const settings = getValues()[props.name]; + if (settings) { + setCurrentMedia(settings); + } + }, []); + + + if (!publications.length) { + return null; + } + + return ( + + ); +}; diff --git a/apps/frontend/src/components/launches/providers/discord/discord.provider.tsx b/apps/frontend/src/components/launches/providers/discord/discord.provider.tsx new file mode 100644 index 00000000..86f00438 --- /dev/null +++ b/apps/frontend/src/components/launches/providers/discord/discord.provider.tsx @@ -0,0 +1,25 @@ +import { withProvider } from '@gitroom/frontend/components/launches/providers/high.order.provider'; +import { FC } from 'react'; +import { DiscordDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/discord.dto'; +import { DiscordChannelSelect } from '@gitroom/frontend/components/launches/providers/discord/discord.channel.select'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; + +const Empty: FC = () => { + return null; +}; + +const DiscordComponent: FC = () => { + const form = useSettings(); + return ( +
+ +
+ ); +}; +export default withProvider( + DiscordComponent, + Empty, + DiscordDto, + undefined, + 280 +); diff --git a/apps/frontend/src/components/launches/providers/show.all.providers.tsx b/apps/frontend/src/components/launches/providers/show.all.providers.tsx index e215d71e..50d9392a 100644 --- a/apps/frontend/src/components/launches/providers/show.all.providers.tsx +++ b/apps/frontend/src/components/launches/providers/show.all.providers.tsx @@ -13,6 +13,8 @@ import TiktokProvider from '@gitroom/frontend/components/launches/providers/tikt import PinterestProvider from '@gitroom/frontend/components/launches/providers/pinterest/pinterest.provider'; import DribbbleProvider from '@gitroom/frontend/components/launches/providers/dribbble/dribbble.provider'; import ThreadsProvider from '@gitroom/frontend/components/launches/providers/threads/threads.provider'; +import DiscordProvider from '@gitroom/frontend/components/launches/providers/discord/discord.provider'; +import SlackProvider from '@gitroom/frontend/components/launches/providers/slack/slack.provider'; export const Providers = [ {identifier: 'devto', component: DevtoProvider}, @@ -29,6 +31,8 @@ export const Providers = [ {identifier: 'pinterest', component: PinterestProvider}, {identifier: 'dribbble', component: DribbbleProvider}, {identifier: 'threads', component: ThreadsProvider}, + {identifier: 'discord', component: DiscordProvider}, + {identifier: 'slack', component: SlackProvider}, ]; diff --git a/apps/frontend/src/components/launches/providers/slack/slack.channel.select.tsx b/apps/frontend/src/components/launches/providers/slack/slack.channel.select.tsx new file mode 100644 index 00000000..d07f4260 --- /dev/null +++ b/apps/frontend/src/components/launches/providers/slack/slack.channel.select.tsx @@ -0,0 +1,44 @@ +import { FC, useEffect, useState } from 'react'; +import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { Select } from '@gitroom/react/form/select'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; + +export const SlackChannelSelect: FC<{ + name: string; + onChange: (event: { target: { value: string; name: string } }) => void; +}> = (props) => { + const { onChange, name } = props; + const customFunc = useCustomProviderFunction(); + const [publications, setOrgs] = useState([]); + const { getValues } = useSettings(); + const [currentMedia, setCurrentMedia] = useState(); + + const onChangeInner = (event: { target: { value: string, name: string } }) => { + setCurrentMedia(event.target.value); + onChange(event); + }; + + useEffect(() => { + customFunc.get('channels').then((data) => setOrgs(data)); + const settings = getValues()[props.name]; + if (settings) { + setCurrentMedia(settings); + } + }, []); + + + if (!publications.length) { + return null; + } + + return ( + + ); +}; diff --git a/apps/frontend/src/components/launches/providers/slack/slack.provider.tsx b/apps/frontend/src/components/launches/providers/slack/slack.provider.tsx new file mode 100644 index 00000000..90857a87 --- /dev/null +++ b/apps/frontend/src/components/launches/providers/slack/slack.provider.tsx @@ -0,0 +1,25 @@ +import { withProvider } from '@gitroom/frontend/components/launches/providers/high.order.provider'; +import { FC } from 'react'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { SlackChannelSelect } from '@gitroom/frontend/components/launches/providers/slack/slack.channel.select'; +import { SlackDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/slack.dto'; + +const Empty: FC = () => { + return null; +}; + +const SlackComponent: FC = () => { + const form = useSettings(); + return ( +
+ +
+ ); +}; +export default withProvider( + SlackComponent, + Empty, + SlackDto, + undefined, + 280 +); diff --git a/apps/frontend/src/components/onboarding/connect.channels.tsx b/apps/frontend/src/components/onboarding/connect.channels.tsx index 02987db8..65e5c7f7 100644 --- a/apps/frontend/src/components/onboarding/connect.channels.tsx +++ b/apps/frontend/src/components/onboarding/connect.channels.tsx @@ -242,6 +242,8 @@ export const ConnectChannels: FC = () => { {integration.name}
{ + const { access_token, expires_in, refresh_token } = await ( + await this.fetch('https://discord.com/api/oauth2/token', { + method: 'POST', + body: new URLSearchParams({ + refresh_token: refreshToken, + grant_type: 'refresh_token', + }), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Authorization: `Basic ${Buffer.from( + process.env.DISCORD_CLIENT_ID + + ':' + + process.env.DISCORD_CLIENT_SECRET + ).toString('base64')}`, + }, + }) + ).json(); + + const { application } = await ( + await fetch('https://discord.com/api/oauth2/@me', { + headers: { + Authorization: `Bearer ${access_token}`, + }, + }) + ).json(); + + return { + refreshToken: refresh_token, + expiresIn: expires_in, + accessToken: access_token, + id: '', + name: application.name, + picture: '', + username: '', + }; + } + async generateAuthUrl(refresh?: string) { + const state = makeId(6); + return { + url: `https://discord.com/oauth2/authorize?client_id=${ + process.env.DISCORD_CLIENT_ID + }&permissions=377957124096&response_type=code&redirect_uri=${encodeURIComponent( + `${process.env.FRONTEND_URL}/integrations/social/discord${ + refresh ? `?refresh=${refresh}` : '' + }` + )}&integration_type=0&scope=bot+identify+guilds&state=${state}`, + codeVerifier: makeId(10), + state, + }; + } + + async authenticate(params: { + code: string; + codeVerifier: string; + refresh?: string; + }) { + const [newCode, guild] = params.code.split(':'); + const { access_token, expires_in, refresh_token, scope } = await ( + await this.fetch('https://discord.com/api/oauth2/token', { + method: 'POST', + body: new URLSearchParams({ + code: newCode, + grant_type: 'authorization_code', + redirect_uri: `${process.env.FRONTEND_URL}/integrations/social/discord`, + }), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Authorization: `Basic ${Buffer.from( + process.env.DISCORD_CLIENT_ID + + ':' + + process.env.DISCORD_CLIENT_SECRET + ).toString('base64')}`, + }, + }) + ).json(); + + this.checkScopes(this.scopes, scope.split(' ')); + + const { application } = await ( + await fetch('https://discord.com/api/oauth2/@me', { + headers: { + Authorization: `Bearer ${access_token}`, + }, + }) + ).json(); + + return { + id: guild, + name: application.name, + accessToken: access_token, + refreshToken: refresh_token, + expiresIn: expires_in, + picture: `https://cdn.discordapp.com/avatars/${application.bot.id}/${application.bot.avatar}.png`, + username: application.bot.username, + }; + } + + async channels(accessToken: string, params: any, id: string) { + const list = await ( + await fetch(`https://discord.com/api/guilds/${id}/channels`, { + headers: { + Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN_ID}`, + }, + }) + ).json(); + + console.log(list); + + return list + .filter((p: any) => p.type === 0 || p.type === 15) + .map((p: any) => ({ + id: String(p.id), + name: p.name, + })); + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[] + ): Promise { + let channel = postDetails[0].settings.channel; + if (postDetails.length > 1) { + const { id: threadId } = await ( + await fetch( + `https://discord.com/api/channels/${postDetails[0].settings.channel}/threads`, + { + method: 'POST', + headers: { + Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN_ID}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: postDetails[0].message, + auto_archive_duration: 1440, + type: 11, // Public thread type + }), + } + ) + ).json(); + channel = threadId; + } + + const finalData = []; + for (const post of postDetails) { + const form = new FormData(); + form.append( + 'payload_json', + JSON.stringify({ + content: post.message, + attachments: post.media?.map((p, index) => ({ + id: index, + description: `Picture ${index}`, + filename: p.url.split('/').pop(), + })), + }) + ); + + let index = 0; + for (const media of post.media || []) { + const loadMedia = await fetch(media.url); + + form.append( + `files[${index}]`, + await loadMedia.blob(), + media.url.split('/').pop() + ); + index++; + } + + const data = await ( + await fetch(`https://discord.com/api/channels/${channel}/messages`, { + method: 'POST', + headers: { + Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN_ID}`, + }, + body: form, + }) + ).json(); + + finalData.push({ + id: post.id, + releaseURL: `https://discord.com/channels/${id}/${channel}/${data.id}`, + postId: data.id, + status: 'success', + }); + } + + return finalData; + } + + async changeNickname( + id: string, + accessToken: string, + name: string, + ) { + await (await fetch(`https://discord.com/api/guilds/${id}/members/@me`, { + method: 'PATCH', + headers: { + Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN_ID}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + nick: name, + }) + })).json(); + + return { + name, + } + } +} diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts index 452b9123..831dfc38 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts @@ -8,6 +8,7 @@ import { import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; import { LinkedinProvider } from '@gitroom/nestjs-libraries/integrations/social/linkedin.provider'; import dayjs from 'dayjs'; +import { Integration } from '@prisma/client'; export class LinkedinPageProvider extends LinkedinProvider @@ -206,9 +207,10 @@ export class LinkedinPageProvider override async post( id: string, accessToken: string, - postDetails: PostDetails[] + postDetails: PostDetails[], + integration: Integration ): Promise { - return super.post(id, accessToken, postDetails, 'company'); + return super.post(id, accessToken, postDetails, integration, 'company'); } async analytics( diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index b76db213..ef80d46a 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -13,6 +13,7 @@ import { BadBody, SocialAbstract, } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import { Integration } from '@prisma/client'; export class LinkedinProvider extends SocialAbstract implements SocialProvider { identifier = 'linkedin'; @@ -287,6 +288,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { id: string, accessToken: string, postDetails: PostDetails[], + integration: Integration, type = 'personal' as 'company' | 'personal' ): Promise { const [firstPost, ...restPosts] = postDetails; diff --git a/libraries/nestjs-libraries/src/integrations/social/slack.provider.ts b/libraries/nestjs-libraries/src/integrations/social/slack.provider.ts new file mode 100644 index 00000000..da34b898 --- /dev/null +++ b/libraries/nestjs-libraries/src/integrations/social/slack.provider.ts @@ -0,0 +1,207 @@ +import { + AuthTokenDetails, + PostDetails, + PostResponse, + SocialProvider, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import dayjs from 'dayjs'; +import { Integration } from '@prisma/client'; + +export class SlackProvider extends SocialAbstract implements SocialProvider { + identifier = 'slack'; + name = 'Slack'; + isBetweenSteps = false; + scopes = ['identify', 'guilds']; + async refreshToken(refreshToken: string): Promise { + const { access_token, expires_in, refresh_token } = await ( + await this.fetch('https://discord.com/api/oauth2/token', { + method: 'POST', + body: new URLSearchParams({ + refresh_token: refreshToken, + grant_type: 'refresh_token', + }), + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Authorization: `Basic ${Buffer.from( + process.env.DISCORD_CLIENT_ID + + ':' + + process.env.DISCORD_CLIENT_SECRET + ).toString('base64')}`, + }, + }) + ).json(); + + const { application } = await ( + await fetch('https://discord.com/api/oauth2/@me', { + headers: { + Authorization: `Bearer ${access_token}`, + }, + }) + ).json(); + + return { + refreshToken: refresh_token, + expiresIn: expires_in, + accessToken: access_token, + id: '', + name: application.name, + picture: '', + username: '', + }; + } + async generateAuthUrl(refresh?: string) { + const state = makeId(6); + + return { + url: `https://slack.com/oauth/v2/authorize?client_id=${ + process.env.SLACK_ID + }&redirect_uri=${encodeURIComponent( + `${ + process?.env?.FRONTEND_URL?.indexOf('https') === -1 + ? 'https://redirectmeto.com/' + : '' + }${process?.env?.FRONTEND_URL}/integrations/social/slack${ + refresh ? `?refresh=${refresh}` : '' + }` + )}&scope=channels:read,chat:write,users:read,groups:read,channels:join,chat:write.customize&state=${state}`, + codeVerifier: makeId(10), + state, + }; + } + + async authenticate(params: { + code: string; + codeVerifier: string; + refresh?: string; + }) { + const { access_token, team, bot_user_id, authed_user, ...all } = await ( + await this.fetch(`https://slack.com/api/oauth.v2.access`, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + client_id: process.env.SLACK_ID!, + client_secret: process.env.SLACK_SECRET!, + code: params.code, + redirect_uri: `${ + process?.env?.FRONTEND_URL?.indexOf('https') === -1 + ? 'https://redirectmeto.com/' + : '' + }${process?.env?.FRONTEND_URL}/integrations/social/slack${ + params.refresh ? `?refresh=${params.refresh}` : '' + }`, + }), + }) + ).json(); + + const { user } = await ( + await fetch(`https://slack.com/api/users.info?user=${bot_user_id}`, { + method: 'GET', + headers: { + Authorization: `Bearer ${access_token}`, + }, + }) + ).json(); + + return { + id: team.id, + name: user.real_name, + accessToken: access_token, + refreshToken: 'null', + expiresIn: dayjs().add(100, 'years').unix() - dayjs().unix(), + picture: user.profile.image_48, + username: user.name, + }; + } + + async channels(accessToken: string, params: any, id: string) { + const list = await ( + await fetch( + `https://slack.com/api/conversations.list?types=public_channel,private_channel`, + { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ) + ).json(); + + return list.channels.map((p: any) => ({ + id: p.id, + name: p.name, + })); + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise { + await fetch(`https://slack.com/api/conversations.join`, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + channel: postDetails[0].settings.channel, + }), + }); + + let lastId = ''; + for (const post of postDetails) { + const { ts } = await ( + await fetch(`https://slack.com/api/chat.postMessage`, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + channel: postDetails[0].settings.channel, + username: integration.name, + icon_url: integration.picture, + ...(lastId ? { thread_ts: lastId } : {}), + blocks: [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: post.message, + }, + }, + ...(post.media?.length + ? post.media.map((m) => ({ + type: 'image', + image_url: m.url, + alt_text: '', + })) + : []), + ], + }), + }) + ).json(); + + lastId = ts; + } + + return []; + } + + async changeProfilePicture(id: string, accessToken: string, url: string) { + return { + url, + }; + } + + async changeNickname(id: string, accessToken: string, name: string) { + return { + name, + }; + } +} 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 35a9c299..e3b232bb 100644 --- a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts +++ b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts @@ -1,3 +1,5 @@ +import { Integration } from '@prisma/client'; + export interface IAuthenticator { authenticate(params: { code: string; @@ -6,7 +8,21 @@ export interface IAuthenticator { }): Promise; refreshToken(refreshToken: string): Promise; generateAuthUrl(refresh?: string): Promise; - analytics?(id: string, accessToken: string, date: number): Promise; + analytics?( + id: string, + accessToken: string, + date: number + ): Promise; + changeNickname?( + id: string, + accessToken: string, + name: string + ): Promise<{ name: string }>; + changeProfilePicture?( + id: string, + accessToken: string, + url: string + ): Promise<{ url: string }>; } export interface AnalyticsData { @@ -35,7 +51,8 @@ export interface ISocialMediaIntegration { post( id: string, accessToken: string, - postDetails: PostDetails[] + postDetails: PostDetails[], + integration: Integration ): Promise; // Schedules a new post } diff --git a/libraries/react-shared-libraries/src/helpers/image.with.fallback.tsx b/libraries/react-shared-libraries/src/helpers/image.with.fallback.tsx index 430d9b9a..c4ddc1fb 100644 --- a/libraries/react-shared-libraries/src/helpers/image.with.fallback.tsx +++ b/libraries/react-shared-libraries/src/helpers/image.with.fallback.tsx @@ -1,4 +1,4 @@ -import { FC, useState } from 'react'; +import { FC, useEffect, useState } from 'react'; import Image from 'next/image'; interface ImageSrc { @@ -12,6 +12,11 @@ interface ImageSrc { const ImageWithFallback: FC = (props) => { const { src, fallbackSrc, ...rest } = props; const [imgSrc, setImgSrc] = useState(src); + useEffect(() => { + if (src !== imgSrc) { + setImgSrc(src); + } + }, [src]); return ( Date: Thu, 3 Oct 2024 13:36:44 +0700 Subject: [PATCH 38/59] feat: frontend changes --- apps/frontend/src/components/layout/continue.provider.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/frontend/src/components/layout/continue.provider.tsx b/apps/frontend/src/components/layout/continue.provider.tsx index bc1e814e..9f12971c 100644 --- a/apps/frontend/src/components/layout/continue.provider.tsx +++ b/apps/frontend/src/components/layout/continue.provider.tsx @@ -88,6 +88,8 @@ export const ContinueProvider: FC = () => { name: '', picture: '', inBetweenSteps: true, + changeNickName: false, + changeProfilePicture: false, identifier: added, }, }} From cf9a2e69904e231b8e504fcbdc253c64ab8c2616 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 3 Oct 2024 15:45:33 +0700 Subject: [PATCH 39/59] feat: fixes --- .../integrations/social/discord.provider.ts | 7 ++- .../src/integrations/social/slack.provider.ts | 49 ++++++------------- 2 files changed, 19 insertions(+), 37 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/discord.provider.ts b/libraries/nestjs-libraries/src/integrations/social/discord.provider.ts index f5667a52..d03ea417 100644 --- a/libraries/nestjs-libraries/src/integrations/social/discord.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/discord.provider.ts @@ -69,12 +69,11 @@ export class DiscordProvider extends SocialAbstract implements SocialProvider { codeVerifier: string; refresh?: string; }) { - const [newCode, guild] = params.code.split(':'); - const { access_token, expires_in, refresh_token, scope } = await ( + const { access_token, expires_in, refresh_token, scope, guild } = await ( await this.fetch('https://discord.com/api/oauth2/token', { method: 'POST', body: new URLSearchParams({ - code: newCode, + code: params.code, grant_type: 'authorization_code', redirect_uri: `${process.env.FRONTEND_URL}/integrations/social/discord`, }), @@ -100,7 +99,7 @@ export class DiscordProvider extends SocialAbstract implements SocialProvider { ).json(); return { - id: guild, + id: guild.id, name: application.name, accessToken: access_token, refreshToken: refresh_token, diff --git a/libraries/nestjs-libraries/src/integrations/social/slack.provider.ts b/libraries/nestjs-libraries/src/integrations/social/slack.provider.ts index da34b898..675bee99 100644 --- a/libraries/nestjs-libraries/src/integrations/social/slack.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/slack.provider.ts @@ -13,40 +13,21 @@ export class SlackProvider extends SocialAbstract implements SocialProvider { identifier = 'slack'; name = 'Slack'; isBetweenSteps = false; - scopes = ['identify', 'guilds']; + scopes = [ + 'channels:read', + 'chat:write', + 'users:read', + 'groups:read', + 'channels:join', + 'chat:write.customize', + ]; async refreshToken(refreshToken: string): Promise { - const { access_token, expires_in, refresh_token } = await ( - await this.fetch('https://discord.com/api/oauth2/token', { - method: 'POST', - body: new URLSearchParams({ - refresh_token: refreshToken, - grant_type: 'refresh_token', - }), - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: `Basic ${Buffer.from( - process.env.DISCORD_CLIENT_ID + - ':' + - process.env.DISCORD_CLIENT_SECRET - ).toString('base64')}`, - }, - }) - ).json(); - - const { application } = await ( - await fetch('https://discord.com/api/oauth2/@me', { - headers: { - Authorization: `Bearer ${access_token}`, - }, - }) - ).json(); - return { - refreshToken: refresh_token, - expiresIn: expires_in, - accessToken: access_token, + refreshToken: '', + expiresIn: 1000000, + accessToken: '', id: '', - name: application.name, + name: '', picture: '', username: '', }; @@ -76,7 +57,7 @@ export class SlackProvider extends SocialAbstract implements SocialProvider { codeVerifier: string; refresh?: string; }) { - const { access_token, team, bot_user_id, authed_user, ...all } = await ( + const { access_token, team, bot_user_id, scope } = await ( await this.fetch(`https://slack.com/api/oauth.v2.access`, { method: 'POST', headers: { @@ -97,6 +78,8 @@ export class SlackProvider extends SocialAbstract implements SocialProvider { }) ).json(); + this.checkScopes(this.scopes, scope.split(',')); + const { user } = await ( await fetch(`https://slack.com/api/users.info?user=${bot_user_id}`, { method: 'GET', @@ -112,7 +95,7 @@ export class SlackProvider extends SocialAbstract implements SocialProvider { accessToken: access_token, refreshToken: 'null', expiresIn: dayjs().add(100, 'years').unix() - dayjs().unix(), - picture: user.profile.image_48, + picture: user.profile.image_original, username: user.name, }; } From 70cdff4c7d31e6fcbb594bb6e08c46110da27a2b Mon Sep 17 00:00:00 2001 From: GURSAL PRATHAMESH APPASAHEB <135983263+prathamesh424@users.noreply.github.com> Date: Thu, 3 Oct 2024 14:46:41 +0530 Subject: [PATCH 40/59] Updated the save button and add hover effect to the remove button --- apps/frontend/src/components/layout/settings.component.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/frontend/src/components/layout/settings.component.tsx b/apps/frontend/src/components/layout/settings.component.tsx index c229216b..436f7fe9 100644 --- a/apps/frontend/src/components/layout/settings.component.tsx +++ b/apps/frontend/src/components/layout/settings.component.tsx @@ -160,7 +160,7 @@ export const SettingsPopup: FC<{ getRef?: Ref }> = (props) => {
@@ -191,7 +191,7 @@ export const SettingsPopup: FC<{ getRef?: Ref }> = (props) => {
{!getRef && (
- +
)} {!!user?.tier?.team_members && isGeneral && } From c081584ce8073282102a14738711cd601ec2c0e9 Mon Sep 17 00:00:00 2001 From: GURSAL PRATHAMESH APPASAHEB <135983263+prathamesh424@users.noreply.github.com> Date: Thu, 3 Oct 2024 14:48:44 +0530 Subject: [PATCH 41/59] change the percentage color in the progress bar --- apps/frontend/src/app/global.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/app/global.scss b/apps/frontend/src/app/global.scss index 64dec72f..da791348 100644 --- a/apps/frontend/src/app/global.scss +++ b/apps/frontend/src/app/global.scss @@ -361,6 +361,7 @@ html { .uppy-ProgressBar-percentage { position: absolute; + color: red; top: 50%; left: 50%; transform: translate(-50%, -50%); @@ -383,4 +384,4 @@ div div .set-font-family { font-stretch: 100% !important; font-style: normal !important; font-weight: 400 !important; -} \ No newline at end of file +} From 79b81893416dce8ecd7a37283c0d5e9bda56517f Mon Sep 17 00:00:00 2001 From: JeevaRamanathan Date: Fri, 4 Oct 2024 03:22:14 +0530 Subject: [PATCH 42/59] updated faq descriptions --- apps/frontend/src/components/billing/faq.component.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/components/billing/faq.component.tsx b/apps/frontend/src/components/billing/faq.component.tsx index 97231547..89b21ca7 100644 --- a/apps/frontend/src/components/billing/faq.component.tsx +++ b/apps/frontend/src/components/billing/faq.component.tsx @@ -10,7 +10,7 @@ const useFaqList = () => { return [ { title: `Can I trust ${isGeneral ? 'Postiz' : 'Gitroom'}?`, - description: `${isGeneral ? 'Postiz' : 'Gitroom'} is proudly open-source! We believe in an ethical and transparent culture, meaning Postiz will live forever. You can check the entire code / or use it for your personal use. You can check the open-source repository click here.`, + description: `${isGeneral ? 'Postiz' : 'Gitroom'} is proudly open-source! We believe in an ethical and transparent culture, meaning that ${isGeneral ? 'Postiz' : 'Gitroom'} will forever. You can check out the entire code or use it for personal projects. To view the open-source repository,
click here.`, }, { title: 'What are channels?', @@ -18,7 +18,7 @@ const useFaqList = () => { isGeneral ? 'Postiz' : 'Gitroom' } allows you to schedule your posts between different channels. A channel is a publishing platform where you can schedule your posts. -For example, you can schedule your posts on Twitter, Linkedin, DEV and Hashnode`, +For example, you can schedule your posts on X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads and Pinterest.`, }, { title: 'What are team members?', From 8d48b071ad90a6960df61a6bbfabee365c36ca31 Mon Sep 17 00:00:00 2001 From: JeevaRamanathan Date: Fri, 4 Oct 2024 03:40:15 +0530 Subject: [PATCH 43/59] updated faq description --- apps/frontend/src/components/billing/billing.component.tsx | 2 +- apps/frontend/src/components/billing/faq.component.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/components/billing/billing.component.tsx b/apps/frontend/src/components/billing/billing.component.tsx index b726bb4b..97a5c54b 100644 --- a/apps/frontend/src/components/billing/billing.component.tsx +++ b/apps/frontend/src/components/billing/billing.component.tsx @@ -20,7 +20,7 @@ export const BillingComponent = () => { }, []); const load = useCallback(async (path: string) => { - return await (await fetch(path)).json(); + // return await (await fetch(path)).json(); }, []); const { isLoading: isLoadingTier, data: tiers } = useSWR( diff --git a/apps/frontend/src/components/billing/faq.component.tsx b/apps/frontend/src/components/billing/faq.component.tsx index 89b21ca7..fff38e76 100644 --- a/apps/frontend/src/components/billing/faq.component.tsx +++ b/apps/frontend/src/components/billing/faq.component.tsx @@ -10,7 +10,7 @@ const useFaqList = () => { return [ { title: `Can I trust ${isGeneral ? 'Postiz' : 'Gitroom'}?`, - description: `${isGeneral ? 'Postiz' : 'Gitroom'} is proudly open-source! We believe in an ethical and transparent culture, meaning that ${isGeneral ? 'Postiz' : 'Gitroom'} will forever. You can check out the entire code or use it for personal projects. To view the open-source repository, click here.`, + description: `${isGeneral ? 'Postiz' : 'Gitroom'} is proudly open-source! We believe in an ethical and transparent culture, meaning that ${isGeneral ? 'Postiz' : 'Gitroom'} will live forever. You can check out the entire code or use it for personal projects. To view the open-source repository, click here.`, }, { title: 'What are channels?', From 205ae034c3710822967c840dbef87492da6f2cb5 Mon Sep 17 00:00:00 2001 From: JeevaRamanathan Date: Fri, 4 Oct 2024 03:42:37 +0530 Subject: [PATCH 44/59] fix --- apps/frontend/src/components/billing/billing.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/billing/billing.component.tsx b/apps/frontend/src/components/billing/billing.component.tsx index 97a5c54b..b726bb4b 100644 --- a/apps/frontend/src/components/billing/billing.component.tsx +++ b/apps/frontend/src/components/billing/billing.component.tsx @@ -20,7 +20,7 @@ export const BillingComponent = () => { }, []); const load = useCallback(async (path: string) => { - // return await (await fetch(path)).json(); + return await (await fetch(path)).json(); }, []); const { isLoading: isLoadingTier, data: tiers } = useSWR( From 3b35ca3ac87b78a3bed503a8973d9d55a726a325 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 4 Oct 2024 12:23:01 +0700 Subject: [PATCH 45/59] feat: external social media --- .../src/api/routes/integrations.controller.ts | 59 +++++- .../icons/platforms/mastodon-custom.png | Bin 0 -> 15209 bytes .../public/icons/platforms/mastodon.png | Bin 0 -> 15209 bytes .../launches/add.provider.component.tsx | 101 ++++++++- .../providers/mastodon/mastodon.provider.tsx | 8 + .../launches/providers/show.all.providers.tsx | 2 + .../integrations/integration.repository.ts | 4 +- .../integrations/integration.service.ts | 6 +- .../src/database/prisma/schema.prisma | 43 ++-- .../src/integrations/integration.manager.ts | 7 +- .../social/mastodon.custom.provider.ts | 81 ++++++++ .../integrations/social/mastodon.provider.ts | 193 ++++++++++++++++++ .../social/social.integrations.interface.ts | 26 ++- 13 files changed, 480 insertions(+), 50 deletions(-) create mode 100644 apps/frontend/public/icons/platforms/mastodon-custom.png create mode 100644 apps/frontend/public/icons/platforms/mastodon.png create mode 100644 apps/frontend/src/components/launches/providers/mastodon/mastodon.provider.tsx create mode 100644 libraries/nestjs-libraries/src/integrations/social/mastodon.custom.provider.ts create mode 100644 libraries/nestjs-libraries/src/integrations/social/mastodon.provider.ts diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index b0e01043..bfdada6e 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -27,6 +27,7 @@ import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.req import { NotEnoughScopesFilter } from '@gitroom/nestjs-libraries/integrations/integration.missing.scopes'; import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service'; import { IntegrationTimeDto } from '@gitroom/nestjs-libraries/dtos/integrations/integration.time.dto'; +import { AuthService } from '@gitroom/helpers/auth/auth.service'; @ApiTags('Integrations') @Controller('/integrations') @@ -127,7 +128,8 @@ export class IntegrationsController { @CheckPolicies([AuthorizationActions.Create, Sections.CHANNEL]) async getIntegrationUrl( @Param('integration') integration: string, - @Query('refresh') refresh: string + @Query('refresh') refresh: string, + @Query('externalUrl') externalUrl: string ) { if ( !this._integrationManager @@ -139,11 +141,33 @@ export class IntegrationsController { const integrationProvider = this._integrationManager.getSocialIntegration(integration); - const { codeVerifier, state, url } = - await integrationProvider.generateAuthUrl(refresh); - await ioRedis.set(`login:${state}`, codeVerifier, 'EX', 300); - return { url }; + if (integrationProvider.externalUrl && !externalUrl) { + throw new Error('Missing external url'); + } + + try { + const getExternalUrl = integrationProvider.externalUrl + ? { + ...(await integrationProvider.externalUrl(externalUrl)), + instanceUrl: externalUrl, + } + : undefined; + + const { codeVerifier, state, url } = + await integrationProvider.generateAuthUrl(refresh, getExternalUrl); + await ioRedis.set(`login:${state}`, codeVerifier, 'EX', 300); + await ioRedis.set( + `external:${state}`, + JSON.stringify(getExternalUrl), + 'EX', + 300 + ); + + return { url }; + } catch (err) { + return { err: true }; + } } @Post('/:id/time') @@ -273,6 +297,15 @@ export class IntegrationsController { const integrationProvider = this._integrationManager.getSocialIntegration(integration); + + const details = integrationProvider.externalUrl + ? await ioRedis.get(`external:${body.state}`) + : undefined; + + if (details) { + await ioRedis.del(`external:${body.state}`); + } + const { accessToken, expiresIn, @@ -281,11 +314,14 @@ export class IntegrationsController { name, picture, username, - } = await integrationProvider.authenticate({ - code: body.code, - codeVerifier: getCodeVerifier, - refresh: body.refresh, - }); + } = await integrationProvider.authenticate( + { + code: body.code, + codeVerifier: getCodeVerifier, + refresh: body.refresh, + }, + details ? JSON.parse(details) : undefined + ); if (!id) { throw new Error('Invalid api key'); @@ -304,7 +340,8 @@ export class IntegrationsController { username, integrationProvider.isBetweenSteps, body.refresh, - +body.timezone + +body.timezone, + AuthService.fixedEncryption(details) ); } diff --git a/apps/frontend/public/icons/platforms/mastodon-custom.png b/apps/frontend/public/icons/platforms/mastodon-custom.png new file mode 100644 index 0000000000000000000000000000000000000000..79670abcf0f80a8eaff7bcc590a72f51e7c61dd4 GIT binary patch literal 15209 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 CR@RgN literal 0 HcmV?d00001 diff --git a/apps/frontend/public/icons/platforms/mastodon.png b/apps/frontend/public/icons/platforms/mastodon.png new file mode 100644 index 0000000000000000000000000000000000000000..79670abcf0f80a8eaff7bcc590a72f51e7c61dd4 GIT binary patch literal 15209 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 CR@RgN literal 0 HcmV?d00001 diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx index 7d0f6a8b..9f354049 100644 --- a/apps/frontend/src/components/launches/add.provider.component.tsx +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -11,6 +11,7 @@ import { ApiKeyDto } from '@gitroom/nestjs-libraries/dtos/integrations/api.key.d import { useRouter } from 'next/navigation'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { useVariables } from '@gitroom/react/helpers/variable.context'; +import { useToaster } from '@gitroom/react/toaster/toaster'; const resolver = classValidatorResolver(ApiKeyDto); @@ -127,23 +128,107 @@ export const ApiModal: FC<{
); }; + +export const UrlModal: FC<{ + gotoUrl(url: string): void; +}> = (props) => { + const { gotoUrl } = props; + const methods = useForm({ + mode: 'onChange', + }); + + const submit = useCallback(async (data: FieldValues) => { + gotoUrl(data.url); + }, []); + + return ( +
+ + + +
+
+ +
+
+ +
+
+
+
+ ); +}; + export const AddProviderComponent: FC<{ - social: Array<{ identifier: string; name: string }>; + social: Array<{ identifier: string; name: string; isExternal: boolean }>; article: Array<{ identifier: string; name: string }>; update?: () => void; }> = (props) => { const { update } = props; - const {isGeneral} = useVariables(); + const { isGeneral } = useVariables(); + const toaster = useToaster(); 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; + (identifier: string, isExternal: boolean) => async () => { + const gotoIntegration = async (externalUrl?: string) => { + const { url, err } = await ( + await fetch( + `/integrations/social/${identifier}${ + externalUrl ? `?externalUrl=${externalUrl}` : `` + }` + ) + ).json(); + + if (err) { + toaster.show('Could not connect to the platform', 'warning'); + return ; + } + window.location.href = url; + }; + + if (isExternal) { + modal.closeAll(); + + modal.openModal({ + title: '', + withCloseButton: false, + classNames: { + modal: 'bg-transparent text-textColor', + }, + children: ( + + ), + }); + + return; + } + + await gotoIntegration(); }, [] ); @@ -196,7 +281,7 @@ export const AddProviderComponent: FC<{ {social.map((item) => (
{ + return null; +}; + +export default withProvider(null, Empty, undefined, undefined); diff --git a/apps/frontend/src/components/launches/providers/show.all.providers.tsx b/apps/frontend/src/components/launches/providers/show.all.providers.tsx index 50d9392a..def7733c 100644 --- a/apps/frontend/src/components/launches/providers/show.all.providers.tsx +++ b/apps/frontend/src/components/launches/providers/show.all.providers.tsx @@ -15,6 +15,7 @@ import DribbbleProvider from '@gitroom/frontend/components/launches/providers/dr import ThreadsProvider from '@gitroom/frontend/components/launches/providers/threads/threads.provider'; import DiscordProvider from '@gitroom/frontend/components/launches/providers/discord/discord.provider'; import SlackProvider from '@gitroom/frontend/components/launches/providers/slack/slack.provider'; +import MastodonProvider from '@gitroom/frontend/components/launches/providers/mastodon/mastodon.provider'; export const Providers = [ {identifier: 'devto', component: DevtoProvider}, @@ -33,6 +34,7 @@ export const Providers = [ {identifier: 'threads', component: ThreadsProvider}, {identifier: 'discord', component: DiscordProvider}, {identifier: 'slack', component: SlackProvider}, + {identifier: 'mastodon', component: MastodonProvider}, ]; 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 f618cdfc..145c5ce3 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts @@ -79,7 +79,8 @@ export class IntegrationRepository { username?: string, isBetweenSteps = false, refresh?: string, - timezone?: number + timezone?: number, + customInstanceDetails?: string ) { const postTimes = timezone ? { @@ -113,6 +114,7 @@ export class IntegrationRepository { ...postTimes, organizationId: org, refreshNeeded: false, + ...(customInstanceDetails ? { customInstanceDetails } : {}), }, update: { type: type as any, 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 53f2cad6..0de38c66 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -47,7 +47,8 @@ export class IntegrationService { username?: string, isBetweenSteps = false, refresh?: string, - timezone?: number + timezone?: number, + customInstanceDetails?: string ) { const loadImage = await axios.get(picture, { responseType: 'arraybuffer' }); const uploadedPicture = await simpleUpload( @@ -69,7 +70,8 @@ export class IntegrationService { username, isBetweenSteps, refresh, - timezone + timezone, + customInstanceDetails ); } diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index 3944ec4d..a2858330 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -242,27 +242,28 @@ model Subscription { } model Integration { - id String @id @default(cuid()) - internalId String - organizationId String - name String - organization Organization @relation(fields: [organizationId], references: [id]) - picture String? - providerIdentifier String - type String - token String - disabled Boolean @default(false) - tokenExpiration DateTime? - refreshToken String? - posts Post[] - profile String? - deletedAt DateTime? - createdAt DateTime @default(now()) - updatedAt DateTime? @updatedAt - orderItems OrderItems[] - inBetweenSteps Boolean @default(false) - refreshNeeded Boolean @default(false) - postingTimes String @default("[{\"time\":120}, {\"time\":400}, {\"time\":700}]") + id String @id @default(cuid()) + internalId String + organizationId String + name String + organization Organization @relation(fields: [organizationId], references: [id]) + picture String? + providerIdentifier String + type String + token String + disabled Boolean @default(false) + tokenExpiration DateTime? + refreshToken String? + posts Post[] + profile String? + deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime? @updatedAt + orderItems OrderItems[] + inBetweenSteps Boolean @default(false) + refreshNeeded Boolean @default(false) + postingTimes String @default("[{\"time\":120}, {\"time\":400}, {\"time\":700}]") + customInstanceDetails String? @@index([updatedAt]) @@index([deletedAt]) diff --git a/libraries/nestjs-libraries/src/integrations/integration.manager.ts b/libraries/nestjs-libraries/src/integrations/integration.manager.ts index 00ef821d..2133c05e 100644 --- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts +++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts @@ -17,8 +17,10 @@ import { LinkedinPageProvider } from '@gitroom/nestjs-libraries/integrations/soc import { ThreadsProvider } from '@gitroom/nestjs-libraries/integrations/social/threads.provider'; import { DiscordProvider } from '@gitroom/nestjs-libraries/integrations/social/discord.provider'; import { SlackProvider } from '@gitroom/nestjs-libraries/integrations/social/slack.provider'; +import { MastodonProvider } from '@gitroom/nestjs-libraries/integrations/social/mastodon.provider'; +import { MastodonCustomProvider } from '@gitroom/nestjs-libraries/integrations/social/mastodon.custom.provider'; -const socialIntegrationList = [ +const socialIntegrationList: SocialProvider[] = [ new XProvider(), new LinkedinProvider(), new LinkedinPageProvider(), @@ -32,6 +34,8 @@ const socialIntegrationList = [ new DribbbleProvider(), new DiscordProvider(), new SlackProvider(), + new MastodonProvider(), + new MastodonCustomProvider(), ]; const articleIntegrationList = [ @@ -47,6 +51,7 @@ export class IntegrationManager { social: socialIntegrationList.map((p) => ({ name: p.name, identifier: p.identifier, + isExternal: !!p.externalUrl })), article: articleIntegrationList.map((p) => ({ name: p.name, diff --git a/libraries/nestjs-libraries/src/integrations/social/mastodon.custom.provider.ts b/libraries/nestjs-libraries/src/integrations/social/mastodon.custom.provider.ts new file mode 100644 index 00000000..d3b11e44 --- /dev/null +++ b/libraries/nestjs-libraries/src/integrations/social/mastodon.custom.provider.ts @@ -0,0 +1,81 @@ +import { + ClientInformation, + PostDetails, + PostResponse, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { MastodonProvider } from '@gitroom/nestjs-libraries/integrations/social/mastodon.provider'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; + +export class MastodonCustomProvider extends MastodonProvider { + override identifier = 'mastodon-custom'; + override name = 'M. Instance'; + async externalUrl(url: string) { + const form = new FormData(); + form.append('client_name', 'Postiz'); + form.append( + 'redirect_uris', + `${process.env.FRONTEND_URL}/integrations/social/mastodon` + ); + form.append('scopes', this.scopes.join(' ')); + form.append('website', process.env.FRONTEND_URL!); + const { client_id, client_secret } = await ( + await fetch(url + '/api/v1/apps', { + method: 'POST', + body: form, + }) + ).json(); + + return { + client_id, + client_secret, + }; + } + override async generateAuthUrl( + refresh?: string, + external?: ClientInformation + ) { + const state = makeId(6); + const url = this.generateUrlDynamic( + external?.instanceUrl!, + state, + external?.client_id!, + process.env.FRONTEND_URL!, + refresh + ); + + return { + url, + codeVerifier: makeId(10), + state, + }; + } + + override async authenticate( + params: { + code: string; + codeVerifier: string; + refresh?: string; + }, + clientInformation?: ClientInformation + ) { + return this.dynamicAuthenticate( + clientInformation?.client_id!, + clientInformation?.client_secret!, + clientInformation?.instanceUrl!, + params.code + ); + } + + override async post( + id: string, + accessToken: string, + postDetails: PostDetails[] + ): Promise { + return this.dynamicPost( + id, + accessToken, + 'https://mastodon.social', + postDetails + ); + } +} diff --git a/libraries/nestjs-libraries/src/integrations/social/mastodon.provider.ts b/libraries/nestjs-libraries/src/integrations/social/mastodon.provider.ts new file mode 100644 index 00000000..74198db5 --- /dev/null +++ b/libraries/nestjs-libraries/src/integrations/social/mastodon.provider.ts @@ -0,0 +1,193 @@ +import { + AuthTokenDetails, + ClientInformation, + PostDetails, + PostResponse, + SocialProvider, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import dayjs from 'dayjs'; + +export class MastodonProvider extends SocialAbstract implements SocialProvider { + identifier = 'mastodon'; + name = 'Mastodon'; + isBetweenSteps = false; + scopes = ['write:statuses', 'profile', 'write:media']; + + async refreshToken(refreshToken: string): Promise { + return { + refreshToken: '', + expiresIn: 0, + accessToken: '', + id: '', + name: '', + picture: '', + username: '', + }; + } + protected generateUrlDynamic( + customUrl: string, + state: string, + clientId: string, + url: string, + refresh?: string + ) { + return `${customUrl}/oauth/authorize?client_id=${clientId}&response_type=code&redirect_uri=${encodeURIComponent( + `${url}/integrations/social/mastodon${ + refresh ? `?refresh=${refresh}` : '' + }` + )}&scope=${this.scopes.join('+')}&state=${state}`; + } + + async generateAuthUrl(refresh?: string) { + const state = makeId(6); + const url = this.generateUrlDynamic( + 'https://mastodon.social', + state, + process.env.MASTODON_CLIENT_ID!, + process.env.FRONTEND_URL!, + refresh + ); + return { + url, + codeVerifier: makeId(10), + state, + }; + } + + protected async dynamicAuthenticate( + clientId: string, + clientSecret: string, + url: string, + code: string + ) { + const form = new FormData(); + form.append('client_id', clientId); + form.append('client_secret', clientSecret); + form.append('code', code); + form.append('grant_type', 'authorization_code'); + form.append( + 'redirect_uri', + `${process.env.FRONTEND_URL}/integrations/social/mastodon` + ); + form.append('scope', this.scopes.join(' ')); + + const tokenInformation = await ( + await this.fetch(`${url}/oauth/token`, { + method: 'POST', + body: form, + }) + ).json(); + + const personalInformation = await ( + await this.fetch(`${url}/api/v1/accounts/verify_credentials`, { + headers: { + Authorization: `Bearer ${tokenInformation.access_token}`, + }, + }) + ).json(); + + return { + id: personalInformation.id, + name: personalInformation.display_name || personalInformation.acct, + accessToken: tokenInformation.access_token, + refreshToken: 'null', + expiresIn: dayjs().add(100, 'years').unix() - dayjs().unix(), + picture: personalInformation.avatar, + username: personalInformation.username, + }; + } + + async authenticate( + params: { + code: string; + codeVerifier: string; + refresh?: string; + } + ) { + return this.dynamicAuthenticate( + process.env.MASTODON_CLIENT_ID!, + process.env.MASTODON_CLIENT_SECRET!, + 'https://mastodon.social', + params.code + ); + } + + async uploadFile(instanceUrl: string, fileUrl: string, accessToken: string) { + const form = new FormData(); + form.append('file', await fetch(fileUrl).then((r) => r.blob())); + const media = await ( + await this.fetch(`${instanceUrl}/api/v1/media`, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + body: form, + }) + ).json(); + return media.id; + } + + async dynamicPost( + id: string, + accessToken: string, + url: string, + postDetails: PostDetails[] + ): Promise { + let loadId = ''; + const ids = [] as string[]; + for (const getPost of postDetails) { + const uploadFiles = await Promise.all( + getPost?.media?.map((media) => + this.uploadFile(url, media.url, accessToken) + ) || [] + ); + + const form = new FormData(); + form.append('status', getPost.message); + form.append('visibility', 'public'); + if (loadId) { + form.append('in_reply_to_id', loadId); + } + if (uploadFiles.length) { + for (const file of uploadFiles) { + form.append('media_ids[]', file); + } + } + + const post = await ( + await this.fetch(`${url}/api/v1/statuses`, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + body: form, + }) + ).json(); + + ids.push(post.id); + loadId = loadId || post.id; + } + + return postDetails.map((p, i) => ({ + id: p.id, + postId: ids[i], + releaseURL: `${url}/statuses/${ids[i]}`, + status: 'completed', + })); + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[] + ): Promise { + return this.dynamicPost( + id, + accessToken, + 'https://mastodon.social', + postDetails + ); + } +} 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 e3b232bb..b36ac6cb 100644 --- a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts +++ b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts @@ -1,13 +1,24 @@ import { Integration } from '@prisma/client'; +export interface ClientInformation { + client_id: string; + client_secret: string; + instanceUrl: string; +} export interface IAuthenticator { - authenticate(params: { - code: string; - codeVerifier: string; - refresh?: string; - }): Promise; + authenticate( + params: { + code: string; + codeVerifier: string; + refresh?: string; + }, + clientInformation?: ClientInformation + ): Promise; refreshToken(refreshToken: string): Promise; - generateAuthUrl(refresh?: string): Promise; + generateAuthUrl( + refresh?: string, + clientInformation?: ClientInformation + ): Promise; analytics?( id: string, accessToken: string, @@ -90,4 +101,7 @@ export interface SocialProvider name: string; isBetweenSteps: boolean; scopes: string[]; + externalUrl?: ( + url: string + ) => Promise<{ client_id: string; client_secret: string }>; } From 220cad308625ba887f873761a11ef0eec03450ce Mon Sep 17 00:00:00 2001 From: Mujtaba <126389386+Mujtaba500@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:07:25 +0500 Subject: [PATCH 46/59] fix:Change button cursor to pointer on hover --- apps/frontend/src/components/launches/filters.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/frontend/src/components/launches/filters.tsx b/apps/frontend/src/components/launches/filters.tsx index f399ecbd..5dcc29a8 100644 --- a/apps/frontend/src/components/launches/filters.tsx +++ b/apps/frontend/src/components/launches/filters.tsx @@ -188,7 +188,7 @@ export const Filters = () => {
{betweenDates}
{
{
Date: Fri, 4 Oct 2024 16:53:07 +0700 Subject: [PATCH 47/59] feat: fix instagram --- apps/backend/src/api/routes/integrations.controller.ts | 4 ++-- .../src/integrations/social/instagram.provider.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index b0e01043..f061ab5d 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -61,8 +61,8 @@ export class IntegrationsController { refreshNeeded: p.refreshNeeded, type: p.type, time: JSON.parse(p.postingTimes), - changeProfilePicture: !!findIntegration.changeProfilePicture, - changeNickName: !!findIntegration.changeNickname, + changeProfilePicture: !!findIntegration?.changeProfilePicture, + changeNickName: !!findIntegration?.changeNickname, }; }), }; diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts index 8ae19c73..c9cce36f 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts @@ -9,7 +9,6 @@ import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; import { timer } from '@gitroom/helpers/utils/timer'; import dayjs from 'dayjs'; import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; -import { chunk } from 'lodash'; export class InstagramProvider extends SocialAbstract @@ -207,7 +206,7 @@ export class InstagramProvider const medias = await Promise.all( firstPost?.media?.map(async (m) => { const caption = - firstPost.media?.length === 1 ? `&caption=${firstPost.message}` : ``; + firstPost.media?.length === 1 ? `&caption=${encodeURIComponent(firstPost.message)}` : ``; const isCarousel = (firstPost?.media?.length || 0) > 1 ? `&is_carousel_item=true` : ``; const mediaType = @@ -216,9 +215,10 @@ export class InstagramProvider ? `video_url=${m.url}&media_type=REELS` : `video_url=${m.url}&media_type=VIDEO` : `image_url=${m.url}`; + const { id: photoId } = await ( await this.fetch( - `https://graph.facebook.com/v20.0/${id}/media?${mediaType}${caption}${isCarousel}&access_token=${accessToken}`, + `https://graph.facebook.com/v20.0/${id}/media?${mediaType}${isCarousel}&access_token=${accessToken}${caption}`, { method: 'POST', } From 7fdad8d0b4917f2696a766ec8d044af2f1a52c1c Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 4 Oct 2024 17:05:40 +0700 Subject: [PATCH 48/59] feat: x can have maximum 4 pictures --- .../src/components/launches/providers/x/x.provider.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/components/launches/providers/x/x.provider.tsx b/apps/frontend/src/components/launches/providers/x/x.provider.tsx index b099db4e..3e0cec02 100644 --- a/apps/frontend/src/components/launches/providers/x/x.provider.tsx +++ b/apps/frontend/src/components/launches/providers/x/x.provider.tsx @@ -102,4 +102,10 @@ const XPreview: FC = (props) => { ); }; -export default withProvider(null, XPreview, undefined, undefined, 280); +export default withProvider(null, XPreview, undefined, async (posts) => { + if (posts.some(p => p.length > 4)) { + return 'There can be maximum 4 pictures in a post.'; + } + + return true; +}, 280); From 7f72ccf31fdbe952d0ff4b7dbb7a473d729cea50 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 4 Oct 2024 22:06:49 +0700 Subject: [PATCH 49/59] feat: fix facebook video upload --- .../src/integrations/social/facebook.provider.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts index 13c24f74..3e2d84b8 100644 --- a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts @@ -174,7 +174,8 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { let finalId = ''; let finalUrl = ''; if ((firstPost?.media?.[0]?.path?.indexOf('mp4') || -2) > -1) { - const { id: videoId, permalink_url } = await ( + console.log('mp4'); + const { id: videoId, permalink_url, ...all } = await ( await this.fetch( `https://graph.facebook.com/v20.0/${id}/videos?access_token=${accessToken}&fields=id,permalink_url`, { @@ -183,7 +184,7 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { 'Content-Type': 'application/json', }, body: JSON.stringify({ - file_url: firstPost?.media?.[0]?.path!, + file_url: firstPost?.media?.[0]?.url!, description: firstPost.message, published: true, }), @@ -192,7 +193,7 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { ) ).json(); - finalUrl = permalink_url; + finalUrl = 'https://www.facebook.com/reel/' + videoId; finalId = videoId; } else { const uploadPhotos = !firstPost?.media?.length From efe22d8e292ead03e6538d5de2d4c7c792662c02 Mon Sep 17 00:00:00 2001 From: James Read Date: Fri, 4 Oct 2024 21:19:16 +0100 Subject: [PATCH 50/59] Fix email addresses. --- SECURITY.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index 712082c0..c0fafb4f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -8,9 +8,9 @@ The Postiz app is committed to ensuring the security and integrity of our users' If you discover a security vulnerability in the Postiz app, please report it to us privately via email to one of the maintainers: -* @nevo-david ([nevo.david@postiz.app](mailto:nevo.david@postiz.app)) -* @jamesread ([james.read@postiz.app](mailto:james.read@postiz.app)) -* @jonathan-irvin ([jonathan.irvin@postiz.app](mailto:jonathan.irvin@postiz.app)) +* @nevo-david +* @jamesread ([email](mailto:contact@jread.com)) +* @jonathan-irvin ([email](mailto:offendingcommit@gmail.com)) When reporting a security vulnerability, please provide as much detail as possible, including: From 635b9b396fa2e130faca845f110502a76af4202a Mon Sep 17 00:00:00 2001 From: jamesread Date: Fri, 4 Oct 2024 22:27:08 +0100 Subject: [PATCH 51/59] doc: The pull request template needs a checklist to avoid common issues. --- .github/PULL_REQUEST_TEMPLATE.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b0e80b68..15092e71 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,8 +4,16 @@ eg: Bug fix, feature, docs update, ... # Why was this change needed? -Please link to related issues when possible. +Please link to related issues when possible, and explain WHY you changed things, not WHAT you changed. # Other information: -eg: Did you discuss this change with anybody before working on it (not required, but can be a good idea for bigger changes). Any plans for the future, etc? +eg: Did you discuss this change with anybody before working on it (not required, but can be a good idea for bigger changes). Any plans for the future, etc? + +# Checklist: + +Put a "X" in the boxes below to indicate you have followed the checklist; + +- [ ] I have read the [CONTRIBUTING](CONTRIBUTING.md) guide. +- [ ] I checked that there were not similar issues or PRs already open for this. +- [ ] This PR fixes just ONE issue (do not include multiple issues or types of change in the same PR) For example, don't try and fix a UI issue and include new dependencies in the same PR. From 71f85414637bf9428f9beca105b9fbd957723bcf Mon Sep 17 00:00:00 2001 From: jamesread Date: Fri, 4 Oct 2024 23:01:03 +0100 Subject: [PATCH 52/59] ci: Add /uploads to Docker containers [skip ci] --- Dockerfile.dev | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile.dev b/Dockerfile.dev index 0162b4ef..71fe30fc 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -31,6 +31,7 @@ COPY var/docker/Caddyfile /app/Caddyfile COPY .env.example /config/postiz.env VOLUME /config +VOLUME /uploads LABEL org.opencontainers.image.source=https://github.com/gitroomhq/postiz-app @@ -55,6 +56,7 @@ COPY libraries /app/libraries/ RUN npm ci --no-fund && npx nx run-many --target=build --projects=frontend,backend,workers,cron VOLUME /config +VOLUME /uploads LABEL org.opencontainers.image.title="Postiz App (DevContainer)" @@ -70,6 +72,7 @@ COPY --from=devcontainer /app/libraries/ /app/libraries/ COPY package.json nx.json /app/ VOLUME /config +VOLUME /uploads ## Labels at the bottom, because CI will eventually add dates, commit hashes, etc. LABEL org.opencontainers.image.title="Postiz App (Production)" From 022af75434cd47bcdb07c584fffaa4ba228b5bcb Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 5 Oct 2024 12:11:06 +0700 Subject: [PATCH 53/59] feat: mastodon --- .../nestjs-libraries/src/integrations/integration.manager.ts | 4 ++-- .../src/integrations/social/mastodon.custom.provider.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/integration.manager.ts b/libraries/nestjs-libraries/src/integrations/integration.manager.ts index 2133c05e..520804fe 100644 --- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts +++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts @@ -18,7 +18,7 @@ import { ThreadsProvider } from '@gitroom/nestjs-libraries/integrations/social/t import { DiscordProvider } from '@gitroom/nestjs-libraries/integrations/social/discord.provider'; import { SlackProvider } from '@gitroom/nestjs-libraries/integrations/social/slack.provider'; import { MastodonProvider } from '@gitroom/nestjs-libraries/integrations/social/mastodon.provider'; -import { MastodonCustomProvider } from '@gitroom/nestjs-libraries/integrations/social/mastodon.custom.provider'; +// import { MastodonCustomProvider } from '@gitroom/nestjs-libraries/integrations/social/mastodon.custom.provider'; const socialIntegrationList: SocialProvider[] = [ new XProvider(), @@ -35,7 +35,7 @@ const socialIntegrationList: SocialProvider[] = [ new DiscordProvider(), new SlackProvider(), new MastodonProvider(), - new MastodonCustomProvider(), + // new MastodonCustomProvider(), ]; const articleIntegrationList = [ diff --git a/libraries/nestjs-libraries/src/integrations/social/mastodon.custom.provider.ts b/libraries/nestjs-libraries/src/integrations/social/mastodon.custom.provider.ts index d3b11e44..1e48f2da 100644 --- a/libraries/nestjs-libraries/src/integrations/social/mastodon.custom.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/mastodon.custom.provider.ts @@ -18,7 +18,7 @@ export class MastodonCustomProvider extends MastodonProvider { ); form.append('scopes', this.scopes.join(' ')); form.append('website', process.env.FRONTEND_URL!); - const { client_id, client_secret } = await ( + const { client_id, client_secret, ...all } = await ( await fetch(url + '/api/v1/apps', { method: 'POST', body: form, From 934296955aa620ea0994ab38e451c27d1b9a25d0 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 5 Oct 2024 15:11:26 +0700 Subject: [PATCH 54/59] feat: integrations --- apps/backend/src/api/routes/integrations.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index c5a41fc6..f0665778 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -341,7 +341,7 @@ export class IntegrationsController { integrationProvider.isBetweenSteps, body.refresh, +body.timezone, - AuthService.fixedEncryption(details) + details ? AuthService.fixedEncryption(details) : undefined ); } From 3bfac7a38d616e05d31a1196a1448bd1ad1ffe20 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 5 Oct 2024 21:54:52 +0700 Subject: [PATCH 55/59] feat: bluesky --- .../src/api/routes/integrations.controller.ts | 22 +- .../public/icons/platforms/bluesky.png | Bin 0 -> 14926 bytes .../social/[provider]/continue/page.tsx | 17 ++ .../launches/add.provider.component.tsx | 218 +++++++++++++++--- .../providers/bluesky/bluesky.provider.tsx | 14 ++ .../launches/providers/show.all.providers.tsx | 2 + .../src/components/layout/redirect.tsx | 15 ++ .../src/integrations/integration.manager.ts | 17 +- .../integrations/social/bluesky.provider.ts | 169 ++++++++++++++ .../integrations/social/mastodon.provider.ts | 1 - .../social/social.integrations.interface.ts | 9 + package-lock.json | 84 +++++++ package.json | 1 + 13 files changed, 519 insertions(+), 50 deletions(-) create mode 100644 apps/frontend/public/icons/platforms/bluesky.png create mode 100644 apps/frontend/src/components/launches/providers/bluesky/bluesky.provider.tsx create mode 100644 apps/frontend/src/components/layout/redirect.tsx create mode 100644 libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index f0665778..6d89b46e 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -288,15 +288,19 @@ export class IntegrationsController { throw new Error('Integration not allowed'); } - const getCodeVerifier = await ioRedis.get(`login:${body.state}`); + const integrationProvider = + this._integrationManager.getSocialIntegration(integration); + + const getCodeVerifier = integrationProvider.customFields + ? 'none' + : await ioRedis.get(`login:${body.state}`); if (!getCodeVerifier) { throw new Error('Invalid state'); } - await ioRedis.del(`login:${body.state}`); - - const integrationProvider = - this._integrationManager.getSocialIntegration(integration); + if (!integrationProvider.customFields) { + await ioRedis.del(`login:${body.state}`); + } const details = integrationProvider.externalUrl ? await ioRedis.get(`external:${body.state}`) @@ -341,7 +345,13 @@ export class IntegrationsController { integrationProvider.isBetweenSteps, body.refresh, +body.timezone, - details ? AuthService.fixedEncryption(details) : undefined + details + ? AuthService.fixedEncryption(details) + : integrationProvider.customFields + ? AuthService.fixedEncryption( + Buffer.from(body.code, 'base64').toString() + ) + : undefined ); } diff --git a/apps/frontend/public/icons/platforms/bluesky.png b/apps/frontend/public/icons/platforms/bluesky.png new file mode 100644 index 0000000000000000000000000000000000000000..492b23a6336cb1ea68621f795ae75ff1a96cf103 GIT binary patch literal 14926 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 T4&Ksap{AqlCYy6sfyw^`6q_Fl literal 0 HcmV?d00001 diff --git a/apps/frontend/src/app/(site)/integrations/social/[provider]/continue/page.tsx b/apps/frontend/src/app/(site)/integrations/social/[provider]/continue/page.tsx index bead77f8..8a06e542 100644 --- a/apps/frontend/src/app/(site)/integrations/social/[provider]/continue/page.tsx +++ b/apps/frontend/src/app/(site)/integrations/social/[provider]/continue/page.tsx @@ -4,6 +4,7 @@ export const dynamic = 'force-dynamic'; import { internalFetch } from '@gitroom/helpers/utils/internal.fetch'; import { redirect } from 'next/navigation'; +import { Redirect } from '@gitroom/frontend/components/layout/redirect'; export default async function Page({ params: { provider }, @@ -30,6 +31,22 @@ export default async function Page({ return redirect(`/launches?scope=missing`); } + if ( + data.status !== HttpStatusCode.Ok && + data.status !== HttpStatusCode.Created + ) { + return ( + <> +
+ Could not add provider. +
+ You are being redirected back +
+ + + ); + } + const { inBetweenSteps, id } = await data.json(); if (inBetweenSteps && !searchParams.refresh) { diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx index 9f354049..dc5e9049 100644 --- a/apps/frontend/src/components/launches/add.provider.component.tsx +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -1,7 +1,7 @@ 'use client'; import { useModals } from '@mantine/modals'; -import React, { FC, useCallback } from 'react'; +import React, { FC, useCallback, useMemo } 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'; @@ -12,6 +12,8 @@ import { useRouter } from 'next/navigation'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { useVariables } from '@gitroom/react/helpers/variable.context'; import { useToaster } from '@gitroom/react/toaster/toaster'; +import { object, string } from 'yup'; +import { yupResolver } from '@hookform/resolvers/yup'; const resolver = classValidatorResolver(ApiKeyDto); @@ -181,55 +183,193 @@ export const UrlModal: FC<{ ); }; +export const CustomVariables: FC<{ + variables: Array<{ + key: string; + label: string; + defaultValue?: string; + validation: string; + type: 'text' | 'password'; + }>; + identifier: string; + gotoUrl(url: string): void; +}> = (props) => { + const { gotoUrl, identifier, variables } = props; + const modals = useModals(); + const schema = useMemo(() => { + return object({ + ...variables.reduce((aIcc, item) => { + const splitter = item.validation.split('/'); + const regex = new RegExp( + splitter.slice(1, -1).join('/'), + splitter.pop() + ); + return { + ...aIcc, + [item.key]: string() + .matches(regex, `${item.label} is invalid`) + .required(), + }; + }, {}), + }); + }, [variables]); + + const methods = useForm({ + mode: 'onChange', + resolver: yupResolver(schema), + values: variables.reduce( + (acc, item) => ({ + ...acc, + ...(item.defaultValue ? { [item.key]: item.defaultValue } : {}), + }), + {} + ), + }); + + const submit = useCallback( + async (data: FieldValues) => { + gotoUrl( + `/integrations/social/${identifier}?state=nostate&code=${Buffer.from( + JSON.stringify(data) + ).toString('base64')}` + ); + }, + [variables] + ); + + return ( +
+ + + +
+ {variables.map((variable) => ( +
+ +
+ ))} +
+ +
+
+
+
+ ); +}; + export const AddProviderComponent: FC<{ - social: Array<{ identifier: string; name: string; isExternal: boolean }>; + social: Array<{ + identifier: string; + name: string; + isExternal: boolean; + customFields?: Array<{ + key: string; + label: string; + validation: string; + type: 'text' | 'password'; + }>; + }>; article: Array<{ identifier: string; name: string }>; update?: () => void; }> = (props) => { - const { update } = props; + const { update, social, article } = props; const { isGeneral } = useVariables(); const toaster = useToaster(); - + const router = useRouter(); const fetch = useFetch(); const modal = useModals(); - const { social, article } = props; const getSocialLink = useCallback( - (identifier: string, isExternal: boolean) => async () => { - const gotoIntegration = async (externalUrl?: string) => { - const { url, err } = await ( - await fetch( - `/integrations/social/${identifier}${ - externalUrl ? `?externalUrl=${externalUrl}` : `` - }` - ) - ).json(); + ( + identifier: string, + isExternal: boolean, + customFields?: Array<{ + key: string; + label: string; + validation: string; + defaultValue?: string; + type: 'text' | 'password'; + }> + ) => + async () => { + const gotoIntegration = async (externalUrl?: string) => { + const { url, err } = await ( + await fetch( + `/integrations/social/${identifier}${ + externalUrl ? `?externalUrl=${externalUrl}` : `` + }` + ) + ).json(); - if (err) { - toaster.show('Could not connect to the platform', 'warning'); - return ; + if (err) { + toaster.show('Could not connect to the platform', 'warning'); + return; + } + window.location.href = url; + }; + + if (isExternal) { + modal.closeAll(); + + modal.openModal({ + title: '', + withCloseButton: false, + classNames: { + modal: 'bg-transparent text-textColor', + }, + children: , + }); + + return; } - window.location.href = url; - }; - if (isExternal) { - modal.closeAll(); + if (customFields) { + modal.closeAll(); - modal.openModal({ - title: '', - withCloseButton: false, - classNames: { - modal: 'bg-transparent text-textColor', - }, - children: ( - - ), - }); + modal.openModal({ + title: '', + withCloseButton: false, + classNames: { + modal: 'bg-transparent text-textColor', + }, + children: ( + router.push(url)} + variables={customFields} + /> + ), + }); + return; + } - return; - } - - await gotoIntegration(); - }, + await gotoIntegration(); + }, [] ); @@ -281,7 +421,11 @@ export const AddProviderComponent: FC<{ {social.map((item) => (
{ + return null; +}; + +export default withProvider(null, Empty, undefined, async (posts) => { + if (posts.some((p) => p.length > 4)) { + return 'There can be maximum 4 pictures in a post.'; + } + + return true; +}); diff --git a/apps/frontend/src/components/launches/providers/show.all.providers.tsx b/apps/frontend/src/components/launches/providers/show.all.providers.tsx index def7733c..0fece0eb 100644 --- a/apps/frontend/src/components/launches/providers/show.all.providers.tsx +++ b/apps/frontend/src/components/launches/providers/show.all.providers.tsx @@ -16,6 +16,7 @@ import ThreadsProvider from '@gitroom/frontend/components/launches/providers/thr import DiscordProvider from '@gitroom/frontend/components/launches/providers/discord/discord.provider'; import SlackProvider from '@gitroom/frontend/components/launches/providers/slack/slack.provider'; import MastodonProvider from '@gitroom/frontend/components/launches/providers/mastodon/mastodon.provider'; +import BlueskyProvider from '@gitroom/frontend/components/launches/providers/bluesky/bluesky.provider'; export const Providers = [ {identifier: 'devto', component: DevtoProvider}, @@ -35,6 +36,7 @@ export const Providers = [ {identifier: 'discord', component: DiscordProvider}, {identifier: 'slack', component: SlackProvider}, {identifier: 'mastodon', component: MastodonProvider}, + {identifier: 'bluesky', component: BlueskyProvider}, ]; diff --git a/apps/frontend/src/components/layout/redirect.tsx b/apps/frontend/src/components/layout/redirect.tsx new file mode 100644 index 00000000..6a7dca4a --- /dev/null +++ b/apps/frontend/src/components/layout/redirect.tsx @@ -0,0 +1,15 @@ +'use client'; + +import { FC, useEffect } from 'react'; +import { useRouter } from 'next/navigation'; + +export const Redirect: FC<{url: string, delay: number}> = (props) => { + const { url, delay } = props; + const router = useRouter(); + useEffect(() => { + setTimeout(() => { + router.push(url); + }, delay); + }, []); + return null; +} \ 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 520804fe..e4e951d8 100644 --- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts +++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts @@ -18,6 +18,7 @@ import { ThreadsProvider } from '@gitroom/nestjs-libraries/integrations/social/t import { DiscordProvider } from '@gitroom/nestjs-libraries/integrations/social/discord.provider'; import { SlackProvider } from '@gitroom/nestjs-libraries/integrations/social/slack.provider'; import { MastodonProvider } from '@gitroom/nestjs-libraries/integrations/social/mastodon.provider'; +import { BlueskyProvider } from '@gitroom/nestjs-libraries/integrations/social/bluesky.provider'; // import { MastodonCustomProvider } from '@gitroom/nestjs-libraries/integrations/social/mastodon.custom.provider'; const socialIntegrationList: SocialProvider[] = [ @@ -35,6 +36,7 @@ const socialIntegrationList: SocialProvider[] = [ new DiscordProvider(), new SlackProvider(), new MastodonProvider(), + new BlueskyProvider(), // new MastodonCustomProvider(), ]; @@ -46,13 +48,16 @@ const articleIntegrationList = [ @Injectable() export class IntegrationManager { - getAllIntegrations() { + async getAllIntegrations() { return { - social: socialIntegrationList.map((p) => ({ - name: p.name, - identifier: p.identifier, - isExternal: !!p.externalUrl - })), + social: await Promise.all( + socialIntegrationList.map(async (p) => ({ + name: p.name, + identifier: p.identifier, + isExternal: !!p.externalUrl, + ...(p.customFields ? { customFields: await p.customFields() } : {}), + })) + ), article: articleIntegrationList.map((p) => ({ name: p.name, identifier: p.identifier, diff --git a/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts b/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts new file mode 100644 index 00000000..f6556b05 --- /dev/null +++ b/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts @@ -0,0 +1,169 @@ +import { + AuthTokenDetails, + PostDetails, + PostResponse, + SocialProvider, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import { BskyAgent } from '@atproto/api'; +import dayjs from 'dayjs'; +import { Integration } from '@prisma/client'; +import { AuthService } from '@gitroom/helpers/auth/auth.service'; +import sharp from 'sharp'; + +export class BlueskyProvider extends SocialAbstract implements SocialProvider { + identifier = 'bluesky'; + name = 'Bluesky'; + isBetweenSteps = false; + scopes = ['write:statuses', 'profile', 'write:media']; + + async customFields() { + return [ + { + key: 'service', + label: 'Service', + defaultValue: 'https://bsky.social', + validation: `/^(https?:\\/\\/)?((([a-zA-Z0-9\\-_]{1,256}\\.[a-zA-Z]{2,6})|(([0-9]{1,3}\\.){3}[0-9]{1,3}))(:[0-9]{1,5})?)(\\/[^\\s]*)?$/`, + type: 'text' as const, + }, + { + key: 'identifier', + label: 'Identifier', + validation: `/^.{3,}$/`, + type: 'text' as const, + }, + { + key: 'password', + label: 'Password', + validation: `/^.{3,}$/`, + type: 'password' as const, + }, + ]; + } + + async refreshToken(refreshToken: string): Promise { + return { + refreshToken: '', + expiresIn: 0, + accessToken: '', + id: '', + name: '', + picture: '', + username: '', + }; + } + + async generateAuthUrl(refresh?: string) { + const state = makeId(6); + return { + url: '', + codeVerifier: makeId(10), + state, + }; + } + + async authenticate(params: { + code: string; + codeVerifier: string; + refresh?: string; + }) { + const body = JSON.parse(Buffer.from(params.code, 'base64').toString()); + + const agent = new BskyAgent({ + service: body.service, + }); + + const { + data: { accessJwt, refreshJwt, handle, did }, + } = await agent.login({ + identifier: body.identifier, + password: body.password, + }); + + const profile = await agent.getProfile({ + actor: did, + }); + + return { + refreshToken: refreshJwt, + expiresIn: dayjs().add(100, 'years').unix() - dayjs().unix(), + accessToken: accessJwt, + id: did, + name: profile.data.displayName!, + picture: profile.data.avatar!, + username: profile.data.handle!, + }; + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise { + const body = JSON.parse( + AuthService.fixedDecryption(integration.customInstanceDetails!) + ); + const agent = new BskyAgent({ + service: body.service, + }); + + await agent.login({ + identifier: body.identifier, + password: body.password, + }); + + let loadCid = ''; + let loadUri = ''; + for (const post of postDetails) { + const images = await Promise.all( + post.media?.map(async (p) => { + return await agent.uploadBlob( + new Blob([ + await sharp(await (await fetch(p.url)).arrayBuffer()) + .resize({ width: 400 }) + .toBuffer(), + ]) + ); + }) || [] + ); + + const { cid, uri } = await agent.post({ + text: post.message, + createdAt: new Date().toISOString(), + ...(images.length + ? { + embed: { + $type: 'app.bsky.embed.images', + images: images.map((p) => ({ + // can be an array up to 4 values + alt: 'image', // the alt text + image: p.data.blob, + })), + }, + } + : {}), + ...(loadCid + ? { + reply: { + root: { + uri: loadUri, + cid: loadCid, + }, + parent: { + uri: loadUri, + cid: loadCid, + }, + }, + } + : {}), + }); + + loadCid = loadCid || cid; + loadUri = loadUri || uri; + } + + return []; + } +} diff --git a/libraries/nestjs-libraries/src/integrations/social/mastodon.provider.ts b/libraries/nestjs-libraries/src/integrations/social/mastodon.provider.ts index 74198db5..1587dcec 100644 --- a/libraries/nestjs-libraries/src/integrations/social/mastodon.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/mastodon.provider.ts @@ -1,6 +1,5 @@ import { AuthTokenDetails, - ClientInformation, PostDetails, PostResponse, SocialProvider, 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 b36ac6cb..0b4fa59e 100644 --- a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts +++ b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts @@ -98,6 +98,15 @@ export interface SocialProvider ISocialMediaIntegration { identifier: string; refreshWait?: boolean; + customFields?: () => Promise< + { + key: string; + label: string; + defaultValue?: string; + validation: string; + type: 'text' | 'password'; + }[] + >; name: string; isBetweenSteps: boolean; scopes: string[]; diff --git a/package-lock.json b/package-lock.json index d4a91654..0b166b2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { + "@atproto/api": "^0.13.11", "@aws-sdk/client-s3": "^3.410.0", "@aws-sdk/s3-request-presigner": "^3.410.0", "@casl/ability": "^6.5.0", @@ -463,6 +464,58 @@ "web-streams-polyfill": "^3.2.1" } }, + "node_modules/@atproto/api": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/@atproto/api/-/api-0.13.11.tgz", + "integrity": "sha512-YW+4WzZEGGj/SDYo9w+S2PkSaeSS+8Dosk21GFm4EFYq1eq7G0cxuMgvdcq6fov7f9zqsaTFQL2fA6cAgMA0ow==", + "dependencies": { + "@atproto/common-web": "^0.3.1", + "@atproto/lexicon": "^0.4.2", + "@atproto/syntax": "^0.3.0", + "@atproto/xrpc": "^0.6.3", + "await-lock": "^2.2.2", + "multiformats": "^9.9.0", + "tlds": "^1.234.0", + "zod": "^3.23.8" + } + }, + "node_modules/@atproto/common-web": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.3.1.tgz", + "integrity": "sha512-N7wiTnus5vAr+lT//0y8m/FaHHLJ9LpGuEwkwDAeV3LCiPif4m/FS8x/QOYrx1PdZQwKso95RAPzCGWQBH5j6Q==", + "dependencies": { + "graphemer": "^1.4.0", + "multiformats": "^9.9.0", + "uint8arrays": "3.0.0", + "zod": "^3.23.8" + } + }, + "node_modules/@atproto/lexicon": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@atproto/lexicon/-/lexicon-0.4.2.tgz", + "integrity": "sha512-CXoOkhcdF3XVUnR2oNgCs2ljWfo/8zUjxL5RIhJW/UNLp/FSl+KpF8Jm5fbk8Y/XXVPGRAsv9OYfxyU/14N/pw==", + "dependencies": { + "@atproto/common-web": "^0.3.1", + "@atproto/syntax": "^0.3.0", + "iso-datestring-validator": "^2.2.2", + "multiformats": "^9.9.0", + "zod": "^3.23.8" + } + }, + "node_modules/@atproto/syntax": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.3.0.tgz", + "integrity": "sha512-Weq0ZBxffGHDXHl9U7BQc2BFJi/e23AL+k+i5+D9hUq/bzT4yjGsrCejkjq0xt82xXDjmhhvQSZ0LqxyZ5woxA==" + }, + "node_modules/@atproto/xrpc": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@atproto/xrpc/-/xrpc-0.6.3.tgz", + "integrity": "sha512-S3tRvOdA9amPkKLll3rc4vphlDitLrkN5TwWh5Tu/jzk7mnobVVE3akYgICV9XCNHKjWM+IAPxFFI2qi+VW6nQ==", + "dependencies": { + "@atproto/lexicon": "^0.4.2", + "zod": "^3.23.8" + } + }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", @@ -15780,6 +15833,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/await-lock": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/await-lock/-/await-lock-2.2.2.tgz", + "integrity": "sha512-aDczADvlvTGajTDjcjpJMqRkOF6Qdz3YbPZm/PyW6tKPkx2hlYBzxMhEywM/tU72HrVZjgl5VCdRuMlA7pZ8Gw==" + }, "node_modules/axe-core": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", @@ -24646,6 +24704,11 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/iso-datestring-validator": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/iso-datestring-validator/-/iso-datestring-validator-2.2.2.tgz", + "integrity": "sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==" + }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", @@ -28889,6 +28952,11 @@ "multicast-dns": "cli.js" } }, + "node_modules/multiformats": { + "version": "9.9.0", + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==" + }, "node_modules/mustache": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", @@ -36374,6 +36442,14 @@ "node": ">=14.0.0" } }, + "node_modules/tlds": { + "version": "1.255.0", + "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.255.0.tgz", + "integrity": "sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw==", + "bin": { + "tlds": "bin.js" + } + }, "node_modules/tldts": { "version": "6.1.47", "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.47.tgz", @@ -37068,6 +37144,14 @@ "node": ">=8" } }, + "node_modules/uint8arrays": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", + "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==", + "dependencies": { + "multiformats": "^9.4.2" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", diff --git a/package.json b/package.json index 0019d3f5..744288a1 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ }, "private": true, "dependencies": { + "@atproto/api": "^0.13.11", "@aws-sdk/client-s3": "^3.410.0", "@aws-sdk/s3-request-presigner": "^3.410.0", "@casl/ability": "^6.5.0", From c25132ca3e55150b50a186a54c1442e60d9af3c0 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 5 Oct 2024 22:45:04 +0700 Subject: [PATCH 56/59] feat: save post --- .../src/integrations/social/bluesky.provider.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts b/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts index f6556b05..e8147b7a 100644 --- a/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts @@ -116,6 +116,7 @@ export class BlueskyProvider extends SocialAbstract implements SocialProvider { let loadCid = ''; let loadUri = ''; + const cidUrl = [] as { cid: string; url: string, rev: string }[]; for (const post of postDetails) { const images = await Promise.all( post.media?.map(async (p) => { @@ -129,7 +130,8 @@ export class BlueskyProvider extends SocialAbstract implements SocialProvider { }) || [] ); - const { cid, uri } = await agent.post({ + // @ts-ignore + const { cid, uri, commit } = await agent.post({ text: post.message, createdAt: new Date().toISOString(), ...(images.length @@ -162,8 +164,15 @@ export class BlueskyProvider extends SocialAbstract implements SocialProvider { loadCid = loadCid || cid; loadUri = loadUri || uri; + + cidUrl.push({ cid, url: uri, rev: commit.rev }); } - return []; + return postDetails.map((p, index) => ({ + id: p.id, + postId: cidUrl[index].cid, + status: 'completed', + releaseURL: `https://bsky.app/profile/${id}/post/${cidUrl[index].url.split('/').pop()}`, + })); } } From 9c2f9806f188ccb69c238eab4923eac966b1fd0c Mon Sep 17 00:00:00 2001 From: JeevaRamanathan <64531160+JeevaRamanathan@users.noreply.github.com> Date: Sat, 5 Oct 2024 21:17:58 +0530 Subject: [PATCH 57/59] fix: path of contributing guide in PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 15092e71..0c5d671d 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -14,6 +14,6 @@ eg: Did you discuss this change with anybody before working on it (not required, Put a "X" in the boxes below to indicate you have followed the checklist; -- [ ] I have read the [CONTRIBUTING](CONTRIBUTING.md) guide. +- [ ] I have read the [CONTRIBUTING](https://github.com/gitroomhq/postiz-app/blob/main/CONTRIBUTING.md) guide. - [ ] I checked that there were not similar issues or PRs already open for this. - [ ] This PR fixes just ONE issue (do not include multiple issues or types of change in the same PR) For example, don't try and fix a UI issue and include new dependencies in the same PR. From 25a0260a8f4ecee8755b9c8677f848fd9fd9d35d Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 5 Oct 2024 23:00:25 +0700 Subject: [PATCH 58/59] feat: env for new social networks --- .env.example | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.env.example b/.env.example index 5b70c78c..04bac460 100644 --- a/.env.example +++ b/.env.example @@ -61,6 +61,14 @@ PINTEREST_CLIENT_ID="" PINTEREST_CLIENT_SECRET="" DRIBBBLE_CLIENT_ID="" DRIBBBLE_CLIENT_SECRET="" +DISCORD_CLIENT_ID="" +DISCORD_CLIENT_SECRET="" +DISCORD_BOT_TOKEN_ID="" +SLACK_ID="" +SLACK_SECRET="" +SLACK_SIGNING_SECRET="" +MASTODON_CLIENT_ID="" +MASTODON_CLIENT_SECRET="" # Misc Settings OPENAI_API_KEY="" From 300ccc729714d35f5ff769b0e5b21ab4a5ad6b3f Mon Sep 17 00:00:00 2001 From: Nevo David <100117126+nevo-david@users.noreply.github.com> Date: Sat, 5 Oct 2024 23:06:31 +0700 Subject: [PATCH 59/59] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index df62e7aa..37a882c3 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,10 @@ Pinterest Threads X + X + X + X + X