diff --git a/.github/workflows/docs-lint-v2.yml b/.github/workflows/docs-lint-v2.yml index 3e3e27370a3..c27bd4f5641 100644 --- a/.github/workflows/docs-lint-v2.yml +++ b/.github/workflows/docs-lint-v2.yml @@ -45,6 +45,6 @@ jobs: run: | set -o pipefail git diff --name-only origin/$BASE_REF HEAD \ - | { grep -E "^apps/docs/content/guides/(getting-started|ai|api|auth|database|deployment|functions)/" || test $? = 1; } \ + | { grep -E "^apps/docs/content/guides/" || test $? = 1; } \ | xargs -r supa-mdx-lint --format rdf \ | reviewdog -f=rdjsonl -reporter=github-pr-review diff --git a/apps/design-system/content/docs/tailwind-classes.mdx b/apps/design-system/content/docs/tailwind-classes.mdx index 270d4886e58..a90261449b9 100644 --- a/apps/design-system/content/docs/tailwind-classes.mdx +++ b/apps/design-system/content/docs/tailwind-classes.mdx @@ -186,9 +186,3 @@ text-border-control | `500` | | `DEFAULT` | | `600` | - -## Storybook - -The current colors are also auto documented in Storybook - -[Storybook Colors](https://ui-storybook-pre-release.vercel.app/) diff --git a/apps/design-system/package.json b/apps/design-system/package.json index 01cac2a43e7..c94bb23089c 100644 --- a/apps/design-system/package.json +++ b/apps/design-system/package.json @@ -34,7 +34,7 @@ "react-wrap-balancer": "^1.1.0", "recharts": "^2.12.7", "rehype-autolink-headings": "^7.1.0", - "rehype-pretty-code": "^0.6.0", + "rehype-pretty-code": "^0.9.0", "rehype-slug": "^6.0.0", "remark": "^14.0.3", "remark-code-import": "^1.2.0", diff --git a/apps/docs/CONTRIBUTING.md b/apps/docs/CONTRIBUTING.md index fa482554221..843af5ed2e4 100644 --- a/apps/docs/CONTRIBUTING.md +++ b/apps/docs/CONTRIBUTING.md @@ -77,7 +77,7 @@ You can usually identify a federated or reference doc because it uses a Next.js Example spec file import: ```js -import specFile from '~/spec/transforms/analytics_v0_openapi_deparsed.json' assert { type: 'json' } +import specFile from '~/spec/transforms/analytics_v0_openapi_deparsed.json' with { type: 'json' } ``` Example repo definition: diff --git a/apps/docs/app/guides/(with-sidebar)/config/page.tsx b/apps/docs/app/guides/(with-sidebar)/config/page.tsx index 4707208e7e0..9ce99b1da78 100644 --- a/apps/docs/app/guides/(with-sidebar)/config/page.tsx +++ b/apps/docs/app/guides/(with-sidebar)/config/page.tsx @@ -5,7 +5,7 @@ import { type TOCHeader } from '~/components/GuidesTableOfContents' import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' import type { Parameter } from '~/lib/refGenerator/refTypes' -import specFile from '~/spec/cli_v1_config.yaml' assert { type: 'yml' } +import specFile from '~/spec/cli_v1_config.yaml' with { type: 'yml' } const meta = { title: 'Supabase CLI config', diff --git a/apps/docs/app/guides/(with-sidebar)/local-development/cli/config/page.tsx b/apps/docs/app/guides/(with-sidebar)/local-development/cli/config/page.tsx index 4707208e7e0..9ce99b1da78 100644 --- a/apps/docs/app/guides/(with-sidebar)/local-development/cli/config/page.tsx +++ b/apps/docs/app/guides/(with-sidebar)/local-development/cli/config/page.tsx @@ -5,7 +5,7 @@ import { type TOCHeader } from '~/components/GuidesTableOfContents' import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' import type { Parameter } from '~/lib/refGenerator/refTypes' -import specFile from '~/spec/cli_v1_config.yaml' assert { type: 'yml' } +import specFile from '~/spec/cli_v1_config.yaml' with { type: 'yml' } const meta = { title: 'Supabase CLI config', diff --git a/apps/docs/app/guides/(with-sidebar)/self-hosting/analytics/config/page.tsx b/apps/docs/app/guides/(with-sidebar)/self-hosting/analytics/config/page.tsx index 03a88cc171f..d7af68966fd 100644 --- a/apps/docs/app/guides/(with-sidebar)/self-hosting/analytics/config/page.tsx +++ b/apps/docs/app/guides/(with-sidebar)/self-hosting/analytics/config/page.tsx @@ -2,7 +2,7 @@ import Param from '~/components/Params' import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' import { MDXRemoteBase } from '~/features/docs/MdxBase' -import specAnalyticsV0 from '~/spec/analytics_v0_config.yaml' assert { type: 'yml' } +import specAnalyticsV0 from '~/spec/analytics_v0_config.yaml' with { type: 'yml' } const meta = { title: 'Analytics Self-hosting Config', diff --git a/apps/docs/app/guides/(with-sidebar)/self-hosting/auth/config/page.tsx b/apps/docs/app/guides/(with-sidebar)/self-hosting/auth/config/page.tsx index 9d6ff028b51..4a0d480754e 100644 --- a/apps/docs/app/guides/(with-sidebar)/self-hosting/auth/config/page.tsx +++ b/apps/docs/app/guides/(with-sidebar)/self-hosting/auth/config/page.tsx @@ -2,7 +2,7 @@ import Param from '~/components/Params' import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' import { MDXRemoteBase } from '~/features/docs/MdxBase' -import specAuthV1 from '~/spec/gotrue_v1_config.yaml' assert { type: 'yml' } +import specAuthV1 from '~/spec/gotrue_v1_config.yaml' with { type: 'yml' } const meta = { title: 'Auth Self-hosting Config', diff --git a/apps/docs/app/guides/(with-sidebar)/self-hosting/realtime/config/page.tsx b/apps/docs/app/guides/(with-sidebar)/self-hosting/realtime/config/page.tsx index d28ca1077a7..80d76288cd5 100644 --- a/apps/docs/app/guides/(with-sidebar)/self-hosting/realtime/config/page.tsx +++ b/apps/docs/app/guides/(with-sidebar)/self-hosting/realtime/config/page.tsx @@ -2,7 +2,7 @@ import Param from '~/components/Params' import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' import { MDXRemoteBase } from '~/features/docs/MdxBase' -import specRealtimeV0 from '~/spec/realtime_v0_config.yaml' assert { type: 'yml' } +import specRealtimeV0 from '~/spec/realtime_v0_config.yaml' with { type: 'yml' } const meta = { title: 'Realtime Self-hosting Config', diff --git a/apps/docs/app/guides/(with-sidebar)/self-hosting/storage/config/page.tsx b/apps/docs/app/guides/(with-sidebar)/self-hosting/storage/config/page.tsx index 1201543f943..a53adae5ec2 100644 --- a/apps/docs/app/guides/(with-sidebar)/self-hosting/storage/config/page.tsx +++ b/apps/docs/app/guides/(with-sidebar)/self-hosting/storage/config/page.tsx @@ -2,7 +2,7 @@ import Param from '~/components/Params' import { genGuideMeta } from '~/features/docs/GuidesMdx.utils' import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' import { MDXRemoteBase } from '~/features/docs/MdxBase' -import specStorageV0 from '~/spec/storage_v0_config.yaml' assert { type: 'yml' } +import specStorageV0 from '~/spec/storage_v0_config.yaml' with { type: 'yml' } const meta = { title: 'Storage Self-hosting Config', diff --git a/apps/docs/components/MDX/auth_error_codes_table.mdx b/apps/docs/components/MDX/auth_error_codes_table.mdx index 1447eadb689..7fc44588c95 100644 --- a/apps/docs/components/MDX/auth_error_codes_table.mdx +++ b/apps/docs/components/MDX/auth_error_codes_table.mdx @@ -10,6 +10,7 @@ To supplement HTTP status codes, Supabase Auth returns a string error code which | `bad_oauth_state` | OAuth state (data echoed back by the OAuth provider to Supabase Auth) is not in the correct format. Indicates an issue with the OAuth provider integration. | | `captcha_failed` | Captcha challenge could not be verified with the captcha provider. Check your captcha integration. | | `conflict` | General database conflict, such as concurrent requests on resources that should not be modified concurrently. Can often occur when you have too many session refresh requests firing off at the same time for a user. Check your app for concurrency issues, and if detected, back off exponentially. | +| `email_address_invalid` | Example and test domains are currently not supported. Please use a different email address. | | `email_address_not_authorized` | Email sending is not allowed for this address as your project is using the default SMTP service. Emails can only be sent to members in your Supabase organization. If you want to send emails to others, please set up a [custom SMTP provider](/docs/guides/auth/auth-smtp). | | `email_conflict_identity_not_deletable` | Unlinking this identity causes the user's account to change to an email address which is already used by another user account. Indicates an issue where the user has two different accounts using different primary email addresses. You may need to migrate user data to one of their accounts in this case. | | `email_exists` | Email address already exists in the system. | diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 481c0c944a6..9274f8c8ad2 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -1004,6 +1004,10 @@ export const database: NavMenuConstant = { name: 'pg_stat_monitor: Extended Query Performance Monitoring', url: '/guides/database/extensions/pg_stat_monitor', }, + { + name: 'postgres_fdw: query data from an external Postgres server', + url: '/guides/database/extensions/postgres_fdw', + }, { name: 'pgvector: Embeddings and vector similarity', url: '/guides/database/extensions/pgvector', @@ -1553,6 +1557,7 @@ export const realtime: NavMenuConstant = { { name: 'Quotas', url: '/guides/realtime/quotas' }, { name: 'Architecture', url: '/guides/realtime/architecture' }, { name: 'Message Protocol', url: '/guides/realtime/protocol', items: [] }, + { name: 'Benchmarks', url: '/guides/realtime/benchmarks' }, ], }, { @@ -2021,6 +2026,7 @@ export const platform: NavMenuConstant = { { name: 'Compute and Disk', url: '/guides/platform/compute-and-disk' }, { name: 'Database Size', url: '/guides/platform/database-size' }, { name: 'Fly Postgres', url: '/guides/platform/fly-postgres' }, + { name: 'HIPAA Projects', url: '/guides/platform/hipaa-projects' }, { name: 'Network Restrictions', url: '/guides/platform/network-restrictions', diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuCliList.tsx b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuCliList.tsx index 5f148720bb8..f18ba82147d 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenuCliList.tsx +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenuCliList.tsx @@ -1,7 +1,7 @@ import Link from 'next/link' import { BASE_PATH } from '~/lib/constants' -import clientLibsCommon from '~/spec/common-cli.yml' assert { type: 'yml' } +import clientLibsCommon from '~/spec/common-cli.yml' with { type: 'yml' } import * as NavItems from './NavigationMenu.constants' import { ChevronLeft } from 'lucide-react' diff --git a/apps/docs/components/reference/CLICommandSection.tsx b/apps/docs/components/reference/CLICommandSection.tsx index 012b9cdcb63..40b654d5576 100644 --- a/apps/docs/components/reference/CLICommandSection.tsx +++ b/apps/docs/components/reference/CLICommandSection.tsx @@ -5,7 +5,7 @@ import Options from '~/components/Options' import Param from '~/components/Params' import { TabPanel, Tabs } from '~/features/ui/Tabs' import RefSubLayout from '~/layouts/ref/RefSubLayout' -import spec from '~/spec/cli_v1_commands.yaml' assert { type: 'yml' } +import spec from '~/spec/cli_v1_commands.yaml' with { type: 'yml' } import RefDetailCollapse from './RefDetailCollapse' export type Flag = { diff --git a/apps/docs/components/reference/enrichments/cli/CliGlobalFlagsHandler.tsx b/apps/docs/components/reference/enrichments/cli/CliGlobalFlagsHandler.tsx index 71f512f063d..79787faa2c5 100644 --- a/apps/docs/components/reference/enrichments/cli/CliGlobalFlagsHandler.tsx +++ b/apps/docs/components/reference/enrichments/cli/CliGlobalFlagsHandler.tsx @@ -1,6 +1,6 @@ import RefSubLayout from '~/layouts/ref/RefSubLayout' -import spec from '~/spec/cli_v1_commands.yaml' assert { type: 'yaml' } +import spec from '~/spec/cli_v1_commands.yaml' with { type: 'yaml' } import Param from '~/components/Params' const CliGlobalFlagsHandler = () => { diff --git a/apps/docs/content/guides/ai/structured-unstructured.mdx b/apps/docs/content/guides/ai/structured-unstructured.mdx index 7195197a80f..b594ae3c80e 100644 --- a/apps/docs/content/guides/ai/structured-unstructured.mdx +++ b/apps/docs/content/guides/ai/structured-unstructured.mdx @@ -15,7 +15,7 @@ create table docs ( id uuid primary key, embedding vector(3), content text, - url string + url text ); insert into docs diff --git a/apps/docs/content/guides/auth/auth-helpers.mdx b/apps/docs/content/guides/auth/auth-helpers.mdx index 5acfd021e85..909e15797ab 100644 --- a/apps/docs/content/guides/auth/auth-helpers.mdx +++ b/apps/docs/content/guides/auth/auth-helpers.mdx @@ -43,7 +43,7 @@ Working with server-side frameworks is slightly different to client-side framewo ## Status -The Auth Helpers are in `beta`. They are usable in their current state, but it's likely that there will be breaking changes. +The Auth Helpers are `deprecated`. Use the new `@supabase/ssr` package for Server Side Authentication. Use the [migration doc](/docs/guides/auth/server-side/migrating-to-ssr-from-auth-helpers) to learn more. ## Additional links diff --git a/apps/docs/content/guides/auth/auth-identity-linking.mdx b/apps/docs/content/guides/auth/auth-identity-linking.mdx index ef12b12a45b..36c6dffcfd3 100644 --- a/apps/docs/content/guides/auth/auth-identity-linking.mdx +++ b/apps/docs/content/guides/auth/auth-identity-linking.mdx @@ -172,3 +172,13 @@ if google_identity: + +## Frequently asked questions + +### How to add email/password login to an OAuth account? + +Call the `updateUser({ password: 'validpassword'})` to add email with password authentication to an account created with an OAuth provider (Google, GitHub, etc.). + +### Can you sign up with email if already using OAuth? + +If you try to create an email account after previously signing up with OAuth using the same email, you'll receive an obfuscated user response with no verification email sent. This prevents user enumeration attacks. diff --git a/apps/docs/content/guides/auth/server-side/creating-a-client.mdx b/apps/docs/content/guides/auth/server-side/creating-a-client.mdx index 7d8e3f0ecc3..2a3f3e52f9a 100644 --- a/apps/docs/content/guides/auth/server-side/creating-a-client.mdx +++ b/apps/docs/content/guides/auth/server-side/creating-a-client.mdx @@ -150,9 +150,7 @@ The following code samples are for App Router. For help with Pages Router, see t > -```ts utils/supabase/client.tsx -'use client' - +```ts utils/supabase/client.ts import { createBrowserClient } from '@supabase/ssr' export function createClient() { @@ -167,7 +165,7 @@ export function createClient() { -```ts utils/supabase/server.tsx +```ts utils/supabase/server.ts import { createServerClient } from '@supabase/ssr' import { cookies } from 'next/headers' diff --git a/apps/docs/content/guides/auth/social-login/auth-google.mdx b/apps/docs/content/guides/auth/social-login/auth-google.mdx index 11b24c66efd..eaa07a8ecaa 100644 --- a/apps/docs/content/guides/auth/social-login/auth-google.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-google.mdx @@ -166,9 +166,9 @@ To use Google's pre-built signin buttons: ### Configuration [#ios-configuration] - 1. Follow the integration instructions on the [get started with Google Sign-In](https://developers.google.com/identity/sign-in/ios/start-integrating) for iOS guide. + 1. Follow the integration instructions on the [get started with Google Sign-In](https://developers.google.com/identity/sign-in/ios/start-integrating) for the iOS guide. 2. Configure the [OAuth Consent Screen](https://console.cloud.google.com/apis/credentials/consent). This information is shown to the user when giving consent to your app. In particular, make sure you have set up links to your app's privacy policy and terms of service. - 3. Add only the web client ID from step 1 in the [Google provider on the Supabase Dashboard](https://supabase.com/dashboard/project/_/auth/providers), under _Client IDs_. Enable the `Skip nonce check` option. + 3. Add web client ID and iOS client ID from step 1 in the [Google provider on the Supabase Dashboard](https://supabase.com/dashboard/project/_/auth/providers), under _Client IDs_, separated by a comma. Enable the `Skip nonce check` option. diff --git a/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx b/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx index 3e80f7dd1ce..d581605f2ef 100644 --- a/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-linkedin.mdx @@ -8,12 +8,6 @@ To enable LinkedIn Auth for your project, you need to set up a LinkedIn OAuth ap ## Overview - - -We will be replacing the existing _LinkedIn_ provider with a new _LinkedIn (OIDC)_ provider. Developers with LinkedIn OAuth Applications created prior to 1st August 2023 should create a new OAuth application and migrate their credentials from the _LinkedIn_ provider to the _LinkedIn (OIDC)_ provider. Alternatively, you can also add the newly released `Sign In with LinkedIn using OpenID Connect` to your existing OAuth application. [Read this section](/docs/guides/auth/social-login/auth-linkedin#linkedin-open-id-connect-oidc) to find out more. - - - Setting up LinkedIn logins for your application consists of 3 parts: - Create and configure a LinkedIn Project and App on the [LinkedIn Developer Dashboard](https://www.linkedin.com/developers/apps). diff --git a/apps/docs/content/guides/auth/social-login/auth-workos.mdx b/apps/docs/content/guides/auth/social-login/auth-workos.mdx index f09fd573980..5e75b3c3e94 100644 --- a/apps/docs/content/guides/auth/social-login/auth-workos.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-workos.mdx @@ -1,172 +1,116 @@ --- -id: 'auth-workos' -title: 'Login with WorkOS' -description: 'Add WorkOS OAuth to your Supabase project' +title: 'SSO and Social Login with WorkOS' --- -To enable WorkOS Auth for your project, you need to set up WorkOS OAuth application and add the application credentials to your Supabase Dashboard. +## Use Social Login with WorkOS -## Overview +### Step 1. Create a WorkOS organization -In this guide, we will cover how to use Supabase OAuth with WorkOS to implement Single-Sign-On(SSO). - -The procedure consists of five broad steps: - -- Create a new organization from your WorkOS Dashboard. -- Obtain the `Client ID` from the Configuration tab and configure redirect URI. -- Obtain the `WorkOS Secret` from the credentials tab. -- Connect a WorkOS Supported Identity Provider -- Add your WorkOS credentials into your Supabase project - -## Create a WorkOS organization - -Log in to the dashboard and hop over to the Organizations tab to create and organization +Log in to the WorkOS dashboard and visit the Organizations tab to create an organization. ![Create an Organization](/docs/img/guides/auth-workos/workos-create-organization.png) -## Obtain the client ID and configure redirect URI +Alternatively, you can [create an organization via the WorkOS API](https://workos.com/docs/reference/organization/create). -Head over to the Configuration tab and configure the redirect URI.The redirect URI should look like `https://.supabase.co/auth/v1/callback` -Note that this is distinct from the redirect URI referred to in the Supabase dashboard +## Step 2. Obtain your `Client ID` and `WORKOS_API_KEY` values -![Fetch Client ID and configure Redirect URI](/docs/img/guides/auth-workos/workos-clientid-redirect-uri.png) +![Get your Environment's Client ID and Secret](/docs/img/guides/auth-workos/workos-dashboard-get-client-id-and-key.png) -## Obtain the WorkOS secret +Visit the getting started page of the [WorkOS Dashboard](https://dashboard.workos.com/get-started). Copy the following values from the Quickstart panel: -Head over to the API Keys page and obtain the secret key. +- `WORKOS_CLIENT_ID` +- `WORKOS_API_KEY` -![WorkOS Secret Key](/docs/img/guides/auth-workos/workos-secret-key.png) + -## Connect a WorkOS supported identity provider +You must be signed in to see these values. -Set up the identity provider by visiting the setup link. + -![Visiting the setup link](/docs/img/guides/auth-workos/workos-setup-identity-provider.png) +## Step 3. Add your WorkOS credentials to your Supabase project -You can pick between any one of the many identity providers that WorkOS supports. +![Enter your WorkOS application details in your Supabase app's auth provider settings panel](/docs/img/guides/auth-workos/supabase-workos-configuration.png) -## Add your WorkOS credentials into your Supabase project +1. Go to your Supabase Project Dashboard. +1. In the left sidebar, click the Authentication icon (near the top). +1. Click on Providers under the Configuration section. +1. Click on WorkOS from the accordion list to expand. +1. Toggle the `WorkOS Enabled` switch to ON. +1. Enter `https://api.workos.com` in the WorkOS URL field. +1. Enter your WorkOS Client ID and WorkOS Client Secret saved in the previous step. +1. Copy the `Callback URL (for OAuth)` value from the form and save it somewhere handy. +1. Click Save. - +## Step 4. Set your Supabase redirect URI in the WorkOS Dashboard -## Add login code to your client app +Visit the WorkOS dashboard and click the redirects button in the left navigation panel. - - +On the redirects page, enter your Supabase project's `Callback URL (for OAuth)` which you saved in the previous step, as shown below: - +![Set your Supbase project redirect URL in the WorkOS dashboard](/docs/img/guides/auth-workos/workos-set-supabase-redirect.png) -When your user signs in, call [signInWithOAuth()](/docs/reference/javascript/auth-signinwithoauth) with `workos` as the `provider`: +## Step 5. Add login code to your client app -```js +When a user signs in, call signInWithOAuth with `workos` as the provider. + +```javascript async function signInWithWorkOS() { const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'workos', options: { + redirectTo: 'http://example.com/auth/v1/callback', // Make sure your redirect URL is configured in the Supabase Dashboard Auth settings queryParams: { - connection: '', - organization: '', + connection: '', }, }, }) + + if (data.url) { + redirect(data.url) // use the redirect API for your server or framework + } } ``` -Refer to the [WorkOS Documentation](https://workos.com/docs/reference/sso/authorize/) to learn more about the different methods. + - - +You can find your `connection_id` in the WorkOS dashboard under the Organizations tab. Select your organization and then click View connection. -When your user signs in, call [signInWithOAuth()](/docs/reference/dart/auth-signinwithoauth) with `workos` as the `provider`: + -```dart -Future signInWithWorkOS() async { - await supabase.auth.signInWithOAuth( - OAuthProvider.workos, - redirectTo: kIsWeb ? null : 'my.scheme://my-host', // Optionally set the redirect link to bring back the user via deeplink. - authScreenLaunchMode: - kIsWeb ? LaunchMode.platformDefault : LaunchMode.externalApplication, // Launch the auth screen in a new webview on mobile. - queryParams: { - 'connection': '', - 'organization': '', - }, - ); +Within your specified callback URL, you'll exchange the code for a logged-in user profile: + +```javascript auth/v1/callback/route.ts +import { NextResponse } from 'next/server' +import { createClient } from '@/utils/supabase/server' + +export async function GET(request: Request) { + const { searchParams, origin } = new URL(request.url) + const code = searchParams.get('code') + // if "next" is in param, use it as the redirect URL + const next = searchParams.get('next') ?? '/' + + if (code) { + const supabase = await createClient() + const { error } = await supabase.auth.exchangeCodeForSession(code) + if (!error) { + const forwardedHost = request.headers.get('x-forwarded-host') // original origin before load balancer + const isLocalEnv = process.env.NODE_ENV === 'development' + if (isLocalEnv) { + // we can be sure that there is no load balancer in between, so no need to watch for X-Forwarded-Host + return NextResponse.redirect(`${origin}${next}`) + } else if (forwardedHost) { + return NextResponse.redirect(`https://${forwardedHost}${next}`) + } else { + return NextResponse.redirect(`${origin}${next}`) + } + } + } + + // return the user to an error page with instructions + return NextResponse.redirect(`${origin}/auth/auth-code-error`) } + ``` -Refer to the [WorkOS Documentation](https://workos.com/docs/reference/sso/authorize/) to learn more about the different methods. - - - - -When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `WorkOS` as the `Provider`: - -```kotlin -suspend fun signInWithWorkOS() { - supabase.auth.signInWith(WorkOS) { - queryParams["connection"] = "" - queryParams["organization"] = "" - queryParams["workos_provider"] = "" - } -} -``` - -Refer to the [WorkOS Documentation](https://workos.com/docs/reference/sso/authorize/) to learn more about the different methods. - - - - - - - - - -When your user signs out, call [signOut()](/docs/reference/javascript/auth-signout) to remove them from the browser session and any objects from localStorage: - -```js -async function signOut() { - const { error } = await supabase.auth.signOut() -} -``` - - - - -When your user signs out, call [signOut()](/docs/reference/dart/auth-signout) to remove them from the browser session and any objects from localStorage: - -```dart -Future signOut() async { - await supabase.auth.signOut(); -} -``` - - - - -When your user signs out, call [signOut()](/docs/reference/kotlin/auth-signout) to remove them from the browser session and any objects from localStorage: - -```kotlin -suspend fun signOut() { - supabase.auth.signOut() -} -``` - - - - ## Resources - [WorkOS Documentation](https://workos.com/docs/sso/guide) diff --git a/apps/docs/content/guides/cron/quickstart.mdx b/apps/docs/content/guides/cron/quickstart.mdx index 874644001a4..782303b9d57 100644 --- a/apps/docs/content/guides/cron/quickstart.mdx +++ b/apps/docs/content/guides/cron/quickstart.mdx @@ -10,7 +10,7 @@ Attempting to create a second Job with the same name (and case) will overwrite t -## Schedule a Job +## Schedule a job -## Edit a Job +## Edit a job -## Activate/Deactivate a Job +## Activate/Deactivate a job -## Unschedule a Job +## Unschedule a job -## Inspecting Job Runs +## Inspecting job runs -## Caution: Scheduling System Maintenance +## Caution: Scheduling system maintenance Be extremely careful when setting up Jobs for system maintenance tasks as they can have unintended consequences. diff --git a/apps/docs/content/guides/database/connecting-to-postgres.mdx b/apps/docs/content/guides/database/connecting-to-postgres.mdx index a8e103f3451..d866f655bf8 100644 --- a/apps/docs/content/guides/database/connecting-to-postgres.mdx +++ b/apps/docs/content/guides/database/connecting-to-postgres.mdx @@ -84,7 +84,7 @@ Get your project's direct string from the [Database Settings](/dashboard/project ## Supavisor session mode -The session mode connection string connects to your Postgres instance via a proxy. This is ideal for persistent servers when IPv4 is not supported. +The session mode connection string connects to your Postgres instance via a proxy. This is ideal for persistent servers when IPv6 is not supported. The connection string looks like this: diff --git a/apps/docs/content/guides/database/extensions/pg_net.mdx b/apps/docs/content/guides/database/extensions/pg_net.mdx index eefacee9364..8855704fe94 100644 --- a/apps/docs/content/guides/database/extensions/pg_net.mdx +++ b/apps/docs/content/guides/database/extensions/pg_net.mdx @@ -311,7 +311,7 @@ order by "created" desc; -Supabase supports reconfiguring pg_net starting from v0.12.0+. For the latest release, initiate a Postgres upgrade in the [Infrastructure Settings](https://supabase.com/dashboard/project/*/settings/infrastructure). +Supabase supports reconfiguring pg*net starting from v0.12.0+. For the latest release, initiate a Postgres upgrade in the [Infrastructure Settings](https://supabase.com/dashboard/project/*/settings/infrastructure). diff --git a/apps/docs/content/guides/database/extensions/postgis.mdx b/apps/docs/content/guides/database/extensions/postgis.mdx index 72b85b09aff..3f96a5c9fff 100644 --- a/apps/docs/content/guides/database/extensions/postgis.mdx +++ b/apps/docs/content/guides/database/extensions/postgis.mdx @@ -98,9 +98,9 @@ You can insert geographical data through SQL or through our API. insert into public.restaurants (name, location) values - ('Supa Burger', st_point(-73.946823, 40.807416)), - ('Supa Pizza', st_point(-73.94581, 40.807475)), - ('Supa Taco', st_point(-73.945826, 40.80629)); + ('Supa Burger', gis.st_point(-73.946823, 40.807416)), + ('Supa Pizza', gis.st_point(-73.94581, 40.807475)), + ('Supa Taco', gis.st_point(-73.945826, 40.80629)); ``` diff --git a/apps/docs/content/guides/database/extensions/postgres_fdw.mdx b/apps/docs/content/guides/database/extensions/postgres_fdw.mdx new file mode 100644 index 00000000000..2d1e6703e90 --- /dev/null +++ b/apps/docs/content/guides/database/extensions/postgres_fdw.mdx @@ -0,0 +1,148 @@ +--- +id: 'postgres_fdw' +title: 'postgres_fdw' +description: 'Query Postgres server from another' +--- + +The extension enables Postgres to query tables and views on a remote Postgres server. + +## Enable the extension + + + + + 1. Go to the [Database](/dashboard/project/_/database/tables) page in the Dashboard. + 2. Click on **Extensions** in the sidebar. + 3. Search for "postgres_fdw" and enable the extension. + + + + + ```sql + -- Example: enable the "postgres_fdw" extension + create extension if not exists postgres_fdw; + + -- Example: disable the "postgres_fdw" extension + drop extension if exists postgres_fdw; + ``` + + Procedural languages are automatically installed within `pg_catalog`, so you don't need to specify a schema. + + + + + +## Create a connection to another database + + + + + + Define the remote database address + + + + ```sql + create server "" + foreign data wrapper postgres_fdw + options ( + host '', + port '', + dbname '' + ); + ``` + + + + + + Set the user credentials for the remote server + + + ```sql + create user mapping for "" + server "" + options ( + user '', + password '' + ); + ``` + + + + + + Import tables from the foreign database + + + Example: Import all tables from a schema + ```sql + import foreign schema "" + from server "" + into ""; + ``` + + Example: Import specific tables + ```sql + import foreign schema """ + limit to ( + "", + "" + ) + from server "" + into ""; + ``` + + + + + + + ```sql + select * from '' + ``` + + + + + +### Configuring execution options + +#### Fetch_size + +Maximum rows fetched per operation. For example, fetching 200 rows with `fetch_size` set to 100 requires 2 requests. + +```sql +alter server "" +options (fetch_size '10000'); +``` + +#### Batch_size + +Maximum rows inserted per cycle. For example, inserting 200 rows with `batch_size` set to 100 requires 2 requests. + +```sql +alter server "" +options (batch_size '1000'); +``` + +#### Extensions + +Lists shared extensions. Without them, queries involving unlisted extension functions or operators may fail or omit references. + +```sql +alter server "" +options (extensions 'vector, postgis'); +``` + +For more server options, check the extension's [official documentation](https://www.postgresql.org/docs/current/postgres-fdw.html#POSTGRES-FDW) + +## Resources + +- Official [`postgres_fdw` documentation](https://www.postgresql.org/docs/current/postgres-fdw.html#POSTGRES-FDW) diff --git a/apps/docs/content/guides/database/functions.mdx b/apps/docs/content/guides/database/functions.mdx index 8bf1bddc162..eb2b1a6b84e 100644 --- a/apps/docs/content/guides/database/functions.mdx +++ b/apps/docs/content/guides/database/functions.mdx @@ -143,7 +143,7 @@ For example, if we had a database with some Star Wars data inside: | id | name | | --- | -------- | -| 1 | Tattoine | +| 1 | Tatooine | | 2 | Alderaan | | 3 | Kashyyyk | diff --git a/apps/docs/content/guides/integrations/build-a-supabase-integration.mdx b/apps/docs/content/guides/integrations/build-a-supabase-integration.mdx index 6a09e7dbd5b..2cb88272d16 100644 --- a/apps/docs/content/guides/integrations/build-a-supabase-integration.mdx +++ b/apps/docs/content/guides/integrations/build-a-supabase-integration.mdx @@ -13,6 +13,8 @@ Using OAuth2.0 you can retrieve an access and refresh token that grant your appl 2. In the upper-right section of the page, click **Add application**. 3. Fill in the required details and click **Confirm**. +{/* supa-mdx-lint-disable-next-line Rule001HeadingCase */} + ## Show a "Connect Supabase" button In your user interface, add a "Connect Supabase" button to kick off the OAuth flow. Follow the design guidelines outlined in our [brand assets](/brand-assets). @@ -157,7 +159,7 @@ When creating a new project, you can either ask the user to provide a database p You can configure the user's [custom SMTP settings](https://supabase.com/docs/guides/auth/auth-smtp) using the [`/config/auth` endpoint](https://api.supabase.com/api/v1#/projects%20config/updateV1AuthConfig). -### Handling Dynamic Redirect URLs +### Handling dynamic redirect URLs To handle multiple, dynamically generated redirect URLs within the same OAuth app, you can leverage the `state` query parameter. When starting the OAuth process, include the desired, encoded redirect URL in the `state` parameter. Once authorization is complete, we will sends the `state` value back to your app. You can then verify its integrity and extract the correct redirect URL, decoding it and redirecting the user to the correct URL. diff --git a/apps/docs/content/guides/integrations/vercel-marketplace.mdx b/apps/docs/content/guides/integrations/vercel-marketplace.mdx index ec92cffa11c..5821eeccb07 100644 --- a/apps/docs/content/guides/integrations/vercel-marketplace.mdx +++ b/apps/docs/content/guides/integrations/vercel-marketplace.mdx @@ -20,7 +20,7 @@ Vercel Marketplace is currently in Public Alpha. If you encounter any issues or ## Quickstart -### Via Template +### Via template
Deploy a Next.js app with Supabase Vercel Storage now
@@ -34,7 +34,7 @@ Vercel Marketplace is currently in Public Alpha. If you encounter any issues or Details coming soon.. -### Connecting to Supabase Project +### Connecting to Supabase project Supabase Projects created via Vercel Marketplace are automatically synchronized with connected Vercel projects. This synchronization includes setting essential environment variables, such as: diff --git a/apps/docs/content/guides/local-development.mdx b/apps/docs/content/guides/local-development.mdx index 06ac6119d8e..49d0bed8ec0 100644 --- a/apps/docs/content/guides/local-development.mdx +++ b/apps/docs/content/guides/local-development.mdx @@ -97,7 +97,7 @@ Develop locally while running the Supabase stack on your machine. 4. View your local Supabase instance at [http://localhost:54323](http://localhost:54323). -## Local Development +## Local development Local development with Supabase allows you to work on your projects in a self-contained environment on your local machine. Working locally has several advantages: diff --git a/apps/docs/content/guides/monitoring-troubleshooting/advanced-log-filtering.mdx b/apps/docs/content/guides/monitoring-troubleshooting/advanced-log-filtering.mdx index 2097c3d163a..e3e81cad023 100644 --- a/apps/docs/content/guides/monitoring-troubleshooting/advanced-log-filtering.mdx +++ b/apps/docs/content/guides/monitoring-troubleshooting/advanced-log-filtering.mdx @@ -129,7 +129,7 @@ regexp_contains(event_message, 'hello world\.') regexp_contains(event_message, 'started host|authenticated') ``` -### and/or/not statements in SQL: +### `and`/`or`/`not` statements in SQL: `and`, `or`, and `not` are all native terms in SQL and can be used in conjunction with regular expressions to filter results diff --git a/apps/docs/content/guides/monitoring-troubleshooting/exhaust-cpu.mdx b/apps/docs/content/guides/monitoring-troubleshooting/exhaust-cpu.mdx index b056ec150d2..9f3bd379e4d 100644 --- a/apps/docs/content/guides/monitoring-troubleshooting/exhaust-cpu.mdx +++ b/apps/docs/content/guides/monitoring-troubleshooting/exhaust-cpu.mdx @@ -39,4 +39,4 @@ Everything you do with your Supabase project requires compute. Hence, there can There are two ways to solve high CPU: 1. **Optimize performance:** Get more out of your instance's resources by optimizing your usage. Have a look at our [performance tuning guide](https://supabase.com/docs/guides/platform/performance#examining-query-performance) and our [production readiness guide](https://supabase.com/docs/guides/platform/going-into-prod#performance). -2. **Upgrade your compute:** You can get a Compute Add-on for your project. Follow [this link](https://supabase.com/dashboard/project/_/settings/addons?panel=computeInstance) and select your project to see your upgrade options. +2. **Upgrade your compute:** You can get a Compute Add-on for your project. Follow [this link](https://supabase.com/dashboard/project/_/settings/compute-and-disk) and select your project to see your upgrade options. diff --git a/apps/docs/content/guides/monitoring-troubleshooting/exhaust-disk-io.mdx b/apps/docs/content/guides/monitoring-troubleshooting/exhaust-disk-io.mdx index 28e5e98b3d6..8e48f668237 100644 --- a/apps/docs/content/guides/monitoring-troubleshooting/exhaust-disk-io.mdx +++ b/apps/docs/content/guides/monitoring-troubleshooting/exhaust-disk-io.mdx @@ -37,5 +37,5 @@ Most operations on your Supabase project require disk IO in some form. Hence, th ## How to fix -1. **Upgrade your compute:** You can get a Compute Add-on for your project. See your [upgrade options](https://supabase.com/dashboard/project/_/settings/addons?panel=computeInstance) by selecting your project. Do reference the [different baseline performances](https://supabase.com/docs/guides/platform/compute-add-ons#disk-io) that come with larger Compute Add-ons. +1. **Upgrade your compute:** You can get a Compute Add-on for your project. See your [upgrade options](https://supabase.com/dashboard/project/_/settings/compute-and-disk) by selecting your project. Do reference the [different baseline performances](https://supabase.com/docs/guides/platform/compute-add-ons#disk-io) that come with larger Compute Add-ons. 2. **Optimize performance:** Get more out of your instance's resources by optimizing your usage. Have a look at our [performance tuning guide](https://supabase.com/docs/guides/platform/performance#examining-query-performance) and our [production readiness guide](https://supabase.com/docs/guides/platform/going-into-prod#performance). diff --git a/apps/docs/content/guides/monitoring-troubleshooting/exhaust-ram.mdx b/apps/docs/content/guides/monitoring-troubleshooting/exhaust-ram.mdx index 12381f9d492..e82c0f25aae 100644 --- a/apps/docs/content/guides/monitoring-troubleshooting/exhaust-ram.mdx +++ b/apps/docs/content/guides/monitoring-troubleshooting/exhaust-ram.mdx @@ -44,5 +44,5 @@ Everything you do with your Supabase project requires memory in some form. Hence ## How to fix your memory issues -1. **Upgrade your compute:** You can get a Compute Add-on for your project. See your [upgrade options](https://supabase.com/dashboard/project/_/settings/addons?panel=computeInstance) by selecting your project. +1. **Upgrade your compute:** You can get a Compute Add-on for your project. See your [upgrade options](https://supabase.com/dashboard/project/_/settings/compute-and-disk) by selecting your project. 2. **Optimize performance:** Get more out of your instance's resources by optimizing your usage. Have a look at our [performance tuning guide](https://supabase.com/docs/guides/platform/performance#examining-query-performance) and our [production readiness guide](https://supabase.com/docs/guides/platform/going-into-prod#performance). diff --git a/apps/docs/content/guides/monitoring-troubleshooting/exhaust-swap.mdx b/apps/docs/content/guides/monitoring-troubleshooting/exhaust-swap.mdx index 247e9466d36..ae4125cacf7 100644 --- a/apps/docs/content/guides/monitoring-troubleshooting/exhaust-swap.mdx +++ b/apps/docs/content/guides/monitoring-troubleshooting/exhaust-swap.mdx @@ -6,7 +6,7 @@ description: 'Learn what high Swap usage could mean for your Supabase instance a Learn what high Swap usage means, what can cause it, and how to solve it. -## What is Swap for? +## What is swap for? @@ -20,7 +20,7 @@ Swap is a portion of your instance's disk that is reserved for the operating sys Swap can be used even if your instance has plenty of RAM. If this is the case, do not worry. Your instance might try to "preemptively swap" by swapping background processes to make space for your traffic in RAM. -### When is high Swap concerning? +### When is high swap concerning? High Swap is concerning if your instance is using all of the available RAM (i.e. consistently using more than 75%). @@ -30,7 +30,7 @@ High Swap usage can affect your database performance. For example, you might see - **Degraded performance due to swapping regularly between RAM and disk.** - **Higher Disk I/O due to swapping regularly.** -## Monitor your Swap +## Monitor your swap You can check your Swap usage directly on the Supabase Platform. Navigate to the [**Database** page](https://supabase.com/dashboard/project/_/reports/database) of the **Reports** section. @@ -45,7 +45,7 @@ Some useful metrics to monitor are (this is not an exhaustive list): - `node_memory_MemTotal_bytes` and `node_memory_MemFree_bytes` - The total RAM and available RAM. - `node_vmstat_pswpin` and `node_vmstat_pswpout` - The number of pages that have been swapped in or out (monitoring this for spikes means that your instance is swapping). -## Common reasons for high Swap usage +## Common reasons for high swap usage Everything you do with your Supabase project requires compute. Hence, there can be many reasons for high Swap usage. Here are some common ones: @@ -55,10 +55,10 @@ Everything you do with your Supabase project requires compute. Hence, there can - **Workload style:** The usage pattern of your Supabase project might be more read heavy, or involve large amounts of data. - **Extensions:** You might be using extensions that perform intensive operations on large datasets. This increases resource usage. -## Solving high Swap usage +## Solving high swap usage If you find that your RAM and Swap usage are high, you have three options: 1. **Optimize performance:** Get more out of your instance's resources by optimizing your usage. See the [performance tuning guide](https://supabase.com/docs/guides/platform/performance#examining-query-performance) and our [production readiness guide](https://supabase.com/docs/guides/platform/going-into-prod#performance). -2. **Upgrade your compute:** You can get a Compute Add-on for your project. Navigate to [the **Addons** section in project settings](https://supabase.com/dashboard/project/_/settings/addons?panel=computeInstance) to see your upgrade options. +2. **Upgrade your compute:** You can get a Compute Add-on for your project. Follow [this link](https://supabase.com/dashboard/project/_/settings/compute-and-disk) and select your project to see your upgrade options. 3. **Read Replicas:** You can spread the load on your Supabase project by creating a Read Replica. See [the read replicas guide](https://supabase.com/docs/guides/platform/read-replicas) for more information. diff --git a/apps/docs/content/guides/monitoring-troubleshooting/http-status-codes.mdx b/apps/docs/content/guides/monitoring-troubleshooting/http-status-codes.mdx index 5dcfec6e635..d86a6bb6597 100644 --- a/apps/docs/content/guides/monitoring-troubleshooting/http-status-codes.mdx +++ b/apps/docs/content/guides/monitoring-troubleshooting/http-status-codes.mdx @@ -51,6 +51,6 @@ The request is not completed within the configured time limit. The timeout limit is set to prevent long-running queries which can cause performance issues, increase latency, and potentially even crash the project. -#### 546 Edge Functions Resource Limit +#### 546 Edge Functions resource limit Applies only to Edge Functions. Function execution was stopped due to a resource limit (`WORKER_LIMIT`). Edge Function logs should provide which [resource limit](/guides/functions/limits) was exceeded. diff --git a/apps/docs/content/guides/monitoring-troubleshooting/log-drains.mdx b/apps/docs/content/guides/monitoring-troubleshooting/log-drains.mdx index f2f508f581e..0cd85cca823 100644 --- a/apps/docs/content/guides/monitoring-troubleshooting/log-drains.mdx +++ b/apps/docs/content/guides/monitoring-troubleshooting/log-drains.mdx @@ -8,7 +8,7 @@ Log drains will send all logs of the Supabase stack to one or more desired desti You can read about the initial announcement [here](https://supabase.com/blog/log-drains) and vote for your preferred drains in [this discussion](https://github.com/orgs/supabase/discussions/28324?sort=top). -# Supported Destinations +# Supported destinations The following table lists the supported destinations and the required setup configuration: @@ -19,7 +19,7 @@ The following table lists the supported destinations and the required setup conf HTTP requests are batched with a max of 250 logs or 1 second intervals, whichever happens first. Logs are compressed via Gzip if the destination supports it. -## Generic HTTP Endpoint +## Generic HTTP endpoint Logs are sent as a POST request with a JSON body. Both HTTP/1 and HTTP/2 protocols are supported. Custom headers can optionally be configured for all requests. @@ -132,7 +132,7 @@ Deno.serve(async (req) => { -## Datadog Logs +## Datadog logs Logs sent to Datadog have the name of the log source set on the `service` field of the event and the source set to `Supabase`. Logs are gzipped before they are sent to Datadog. diff --git a/apps/docs/content/guides/monitoring-troubleshooting/troubleshooting.mdx b/apps/docs/content/guides/monitoring-troubleshooting/troubleshooting.mdx index 485a2f4efee..bbb71cf897b 100644 --- a/apps/docs/content/guides/monitoring-troubleshooting/troubleshooting.mdx +++ b/apps/docs/content/guides/monitoring-troubleshooting/troubleshooting.mdx @@ -24,7 +24,7 @@ You can view the resource utilization of your Supabase Project using the [report Some common solutions for this issue are: -- [Upgrading](https://supabase.com/dashboard/project/_/settings/addons?panel=computeInstance) to a [larger compute add-on](../platform/compute-add-ons) in order to serve higher volumes of traffic. +- [Upgrading](https://supabase.com/dashboard/project/_/settings/compute-and-disk) to a [larger compute add-on](../platform/compute-add-ons) in order to serve higher volumes of traffic. - [Optimizing the queries](../platform/performance#examining-query-performance) being executed. - [Using fewer Postgres connections](../platform/performance#configuring-clients-to-use-fewer-connections) can reduce the amount of resources needed on the project. - [Restarting](https://supabase.com/dashboard/project/_/settings/general) the project. This only temporarily solves the issue by terminating any ongoing workloads that might be tying up your compute resources. @@ -43,7 +43,7 @@ Errors about too many open connections can be _temporarily_ resolved by [restart - If you're receiving a `No more connections allowed (max_client_conn)` error: - Configure your applications and services to [use fewer connections](../platform/performance#configuring-clients-to-use-fewer-connections). - - [Upgrade](https://supabase.com/dashboard/project/_/settings/addons?panel=computeInstance) to a [larger compute add-on](../platform/compute-add-ons) to increase the number of available connections. + - [Upgrade](https://supabase.com/dashboard/project/_/settings/compute-and-disk) to a [larger compute add-on](../platform/compute-add-ons) to increase the number of available connections. - If you're receiving a `sorry, too many clients already` or `remaining connection slots are reserved for non-replication superuser connections` error message in addition to the above suggestions, switch to using the [connection pooler](/docs/guides/database/connecting-to-postgres#connection-pool) instead. ### Connection refused diff --git a/apps/docs/content/guides/platform/access-control.mdx b/apps/docs/content/guides/platform/access-control.mdx index dcc277294c2..e0ed0f6a5e1 100644 --- a/apps/docs/content/guides/platform/access-control.mdx +++ b/apps/docs/content/guides/platform/access-control.mdx @@ -38,7 +38,7 @@ Each Supabase organization must have at least one owner. If your organization ha Otherwise, you'll need to invite a user as **Owner**, and they need to accept the invitation, or promote an existing organization member to **Owner** before you can leave the organization. -### Organization Scoped Roles vs Project Scoped Roles +### Organization scoped roles vs project scoped roles diff --git a/apps/docs/content/guides/platform/backups.mdx b/apps/docs/content/guides/platform/backups.mdx index c6c413027b6..abd12808510 100644 --- a/apps/docs/content/guides/platform/backups.mdx +++ b/apps/docs/content/guides/platform/backups.mdx @@ -168,13 +168,19 @@ Once the restoration is complete, the new project will be available in your dash New projects are completely independent of their source, and as such can be modified and used as desired. + + +As the entire database is copied to the new project, this will include all extensions that were enabled at the source. If the source project included extensions that are configured to carry out external operations—for example pg_net, pg_cron, wrappers—these should be disabled once the copy process has completed to avoid any unwanted actions from taking place. + + + Restoring to a new project is an excellent way to manage environments more effectively. You can use this feature to create staging environments for testing, experiment with changes without risk to production data, or swiftly recover from unexpected data loss scenarios. ## Troubleshooting ### Logical backups -#### search_path issues +#### `search_path` issues During the `pg_restore` process, the `search_path` is set to an empty string for predictability, and security. Using unqualified references to functions or relations can cause restorations using logical backups to fail, as the database will not be able to locate the function or relation being referenced. This can happen even if the database functions without issues during normal operations, as the `search_path` is usually set to include several schemas during normal operations. Therefore, you should always use schema-qualified names within your SQL code. diff --git a/apps/docs/content/guides/platform/compute-and-disk.mdx b/apps/docs/content/guides/platform/compute-and-disk.mdx index 74aeec43fef..5f8f57bd6c0 100644 --- a/apps/docs/content/guides/platform/compute-and-disk.mdx +++ b/apps/docs/content/guides/platform/compute-and-disk.mdx @@ -94,7 +94,7 @@ Smaller compute instances like Nano, Micro, and Small have baseline performance If you need consistent disk performance, choose the 4XL or larger compute instance. If you're unsure of how much throughput or IOPS your application requires, you can load test your project and inspect these [metrics in the Dashboard](https://supabase.com/dashboard/project/_/reports). If the `Disk IO % consumed` stat is more than 1%, it indicates that your workload has exceeded the baseline IO throughput during the day. If this metric goes to 100%, the workload has used up all available disk IO budget. Projects that use any disk IO budget are good candidates for upgrading to a larger compute instance with higher throughput. -### Provisioned Disk throughput and IOPS +### Provisioned disk throughput and IOPS The default disk type is gp3, which comes with a baseline throughput of 125 MiB/s and a default IOPS of 3,000. You can provision additional IOPS and throughput from the [Database Settings](/dashboard/project/_/settings/database) page, but keep in mind that the effective IOPS and throughput will be limited by the compute instance size. @@ -130,7 +130,7 @@ Compute instance size changes will not change your selected disk type or disk si - General Purpose (gp3) disks come with a baseline of 3,000 IOPS and 125 MiB/s. You can provision additional 500 IOPS for every GB of disk size and additional 0.25 MiB/s throughput per provisioned IOPS. - High Performance (io2) disks can be provisioned with 1,000 IOPS per GB of disk size. -## Limits and Constraints +## Limits and constraints ### Postgres replication slots, WAL senders, and connections diff --git a/apps/docs/content/guides/platform/database-size.mdx b/apps/docs/content/guides/platform/database-size.mdx index a4774aa07a0..adcf8937c30 100644 --- a/apps/docs/content/guides/platform/database-size.mdx +++ b/apps/docs/content/guides/platform/database-size.mdx @@ -139,7 +139,7 @@ Once you have reclaimed space, you can run the following to disable [read-only]( set default_transaction_read_only = 'off'; ``` -### Disk Size Distribution +### Disk size distribution You can check the distribution of your disk size on your [project's compute and disk page](/dashboard/_/settings/compute-and-disk). diff --git a/apps/docs/content/guides/platform/hipaa-projects.mdx b/apps/docs/content/guides/platform/hipaa-projects.mdx new file mode 100644 index 00000000000..b5e9a92470a --- /dev/null +++ b/apps/docs/content/guides/platform/hipaa-projects.mdx @@ -0,0 +1,28 @@ +--- +id: 'hipaa' +title: 'HIPAA Projects' +description: 'Projects that store or process Protected Health Information (PHI) and other sensitive data' +--- + +You can use Supabase to store and process Protected Health Information (PHI). If you want to start developing healthcare apps on Supabase, reach out to the Supabase team [here](https://forms.supabase.com/hipaa2) to sign the Business Associate Agreement (BAA). + + + +Organizations must have a signed BAA with Supabase and have the Health Insurance Portability and Accountability Act (HIPAA) add-on enabled when dealing with PHI. + + + +## Configuring a HIPAA project + +When the HIPAA add-on is enabled on an organization, projects within the organization can be configured as _High Compliance_. This configuration can be found in the [General Project Settings page](https://supabase.com/dashboard/project/_/settings) of the dashboard. +Once enabled, additional security checks will be run against the project to ensure the deployed configuration is compliant. These checks are performed on a continual basis and security warnings will appear in the [Security Advisor](https://supabase.com/dashboard/project/_/advisors/security) if a non-compliant setting is detected. + +The required project configuration is outlined in the [shared responsibility model](https://supabase.com/docs/guides/deployment/shared-responsibility-model#managing-healthcare-data) for managing healthcare data. + +These include: + +- Enabling [Point in Time Recovery](/docs/guides/platform/backups#point-in-time-recovery) which requires at least a [small compute add-on](/docs/guides/platform/compute-add-ons). +- Turning on [SSL Enforcement](/docs/guides/platform/ssl-enforcement). +- Enabling [Network Restrictions](/docs/guides/platform/network-restrictions). + +Additional security checks and controls will be added as the security advisor is extended and additional security controls are made available. diff --git a/apps/docs/content/guides/platform/migrating-to-supabase/firebase-auth.mdx b/apps/docs/content/guides/platform/migrating-to-supabase/firebase-auth.mdx index bf2687b7a10..b29d4396844 100644 --- a/apps/docs/content/guides/platform/migrating-to-supabase/firebase-auth.mdx +++ b/apps/docs/content/guides/platform/migrating-to-supabase/firebase-auth.mdx @@ -69,7 +69,7 @@ hash_config { - `filename.json`: (optional) output filename (defaults to `./users.json`) - `batchSize`: (optional) number of users to fetch in each batch (defaults to 100) -### Import JSON users file to Supabase Auth (Postgres: auth.users) [#import-json-users-file] +### Import JSON users file to Supabase Auth (Postgres: `auth.users`) [#import-json-users-file] `node import_users.js []` diff --git a/apps/docs/content/guides/platform/migrating-to-supabase/neon.mdx b/apps/docs/content/guides/platform/migrating-to-supabase/neon.mdx index 8a983829a5d..b8cfd9fd095 100644 --- a/apps/docs/content/guides/platform/migrating-to-supabase/neon.mdx +++ b/apps/docs/content/guides/platform/migrating-to-supabase/neon.mdx @@ -19,7 +19,7 @@ Example: postgresql://neondb_owner:xxxxxxxxxxxxxxx-random-word-yyyyyyyy.us-west-2.aws.neon.tech/neondb?sslmode=require ``` -## Set your OLD_DB_URL environment variable +## Set your `OLD_DB_URL` environment variable Set the **OLD_DB_URL** environment variable at the command line using your Neon database credentials from the clipboard. @@ -38,7 +38,7 @@ export OLD_DB_URL="postgresql://neondb_owner:xxxxxxxxxxxxxxx-random-word-yyyyyyy 1. Under **Connection string**, select **URI**, make sure **Display connection pooler** is checked, and **Mode: Session** is set. 1. Click the **Copy** button to the right of your connection string to copy it to the clipboard. -## Set your NEW_DB_URL environment variable +## Set your `NEW_DB_URL` environment variable Set the **NEW_DB_URL** environment variable at the command line using your Supabase connection string. You will need to replace `[YOUR-PASSWORD]` with your actual database password. diff --git a/apps/docs/content/guides/platform/migrating-to-supabase/vercel-postgres.mdx b/apps/docs/content/guides/platform/migrating-to-supabase/vercel-postgres.mdx index a94bfaf697f..d3207cbd34f 100644 --- a/apps/docs/content/guides/platform/migrating-to-supabase/vercel-postgres.mdx +++ b/apps/docs/content/guides/platform/migrating-to-supabase/vercel-postgres.mdx @@ -26,7 +26,7 @@ Copy this part to your clipboard: "postgres://default:xxxxxxxxxxxx@yy-yyyyy-yyyyyy-yyyyyyy.us-west-2.aws.neon.tech:5432/verceldb?sslmode=require" ``` -## Set your OLD_DB_URL environment variable +## Set your `OLD_DB_URL` environment variable Set the **OLD_DB_URL** environment variable at the command line using your Vercel Postgres Database credentials. @@ -45,7 +45,7 @@ export OLD_DB_URL="postgres://default:xxxxxxxxxxxx@yy-yyyyy-yyyyyy-yyyyyyy.us-we 1. Under **Connection string**, select **URI**, make sure **Display connection pooler** is checked, and **Mode: Session** is set. 1. Click the **Copy** button to the right of your connection string to copy it to the clipboard. -## Set your NEW_DB_URL environment variable +## Set your `NEW_DB_URL` environment variable Set the **NEW_DB_URL** environment variable at the command line using your Supabase connection string. You will need to replace `[YOUR-PASSWORD]` with your actual database password. diff --git a/apps/docs/content/guides/platform/org-based-billing.mdx b/apps/docs/content/guides/platform/org-based-billing.mdx index 60151a8cbfa..ef394ebe867 100644 --- a/apps/docs/content/guides/platform/org-based-billing.mdx +++ b/apps/docs/content/guides/platform/org-based-billing.mdx @@ -90,7 +90,7 @@ We only count compute hours for instances that are active. Paused projects do no | 12XL | $3.836 | ~$2800 | | 16XL | $5.12 | ~$3730 | -### Compute Credits +### Compute credits Paid plans come with $10 of Compute Credits to cover one Micro instance or parts of any other [Compute Add-On](/docs/guides/platform/compute-add-ons). @@ -153,7 +153,7 @@ You can see a breakdown of the different types of egress on your [organization u />
-## Disk Size +## Disk size We differentiate between database space usage and disk size. Database Space is the actual amount of space used by all your database objects, whereas disk size is the size of the underlying provisioned disk. Each database has a provisioned disk. @@ -178,7 +178,7 @@ Supabase provides two "Free projects". Each project can run a `Nano` instance fo We count your total limit of 2 free projects across all organizations you're either an Owner or Administrator of. You could have two Free Plan organizations with one project each, or one Free Plan organization with two projects. Paused projects do not count towards your free project limit. -## Billing Examples +## Billing examples Here are some examples on how the Billing affects you. diff --git a/apps/docs/content/guides/platform/performance.mdx b/apps/docs/content/guides/platform/performance.mdx index cac8f7005d5..8b2f4a85164 100644 --- a/apps/docs/content/guides/platform/performance.mdx +++ b/apps/docs/content/guides/platform/performance.mdx @@ -150,7 +150,7 @@ from pg_statio_user_tables; This shows the ratio of data blocks fetched from the Postgres [shared_buffers](https://www.postgresql.org/docs/15/runtime-config-resource.html#RUNTIME-CONFIG-RESOURCE-MEMORY) cache against the data blocks that were read from disk/OS cache. -If either of your index or table hit rate are < 99% then this can indicate your compute plan is too small for your current workload and you would benefit from more memory. [Upgrading your compute](./compute-add-ons) is easy and can be done from your [project dashboard](https://supabase.com/dashboard/project/_/settings/addons?panel=computeInstance). +If either of your index or table hit rate are < 99% then this can indicate your compute plan is too small for your current workload and you would benefit from more memory. [Upgrading your compute](./compute-add-ons) is easy and can be done from your [project dashboard](https://supabase.com/dashboard/project/_/settings/compute-and-disk). ### Optimizing poor performing queries @@ -186,7 +186,7 @@ FATAL: remaining connection slots are reserved for non-replication superuser con In such a scenario, you can consider: -- [upgrading to a larger compute add-on](https://supabase.com/dashboard/project/_/settings/addons?panel=computeInstance) +- [upgrading to a larger compute add-on](https://supabase.com/dashboard/project/_/settings/compute-and-disk) - configuring your clients to use fewer connections - manually configuring the database for a higher number of connections diff --git a/apps/docs/content/guides/platform/read-replicas.mdx b/apps/docs/content/guides/platform/read-replicas.mdx index 755574005b8..56540f30af8 100644 --- a/apps/docs/content/guides/platform/read-replicas.mdx +++ b/apps/docs/content/guides/platform/read-replicas.mdx @@ -60,6 +60,8 @@ A Read Replica is deployed by using a physical backup as a starting point, and a Along with the progress of the deployment, the dashboard displays rough estimates for each component. +{/* supa-mdx-lint-disable-next-line Rule001HeadingCase */} + ### What does it mean when "Init failed" is observed? The status `Init failed` indicates that the Read Replica has failed to deploy. Some possible scenarios as to why a Read Replica may have failed to be deployed: diff --git a/apps/docs/content/guides/platform/sso/azure.mdx b/apps/docs/content/guides/platform/sso/azure.mdx index a4f08432c01..5a23c9a9c98 100644 --- a/apps/docs/content/guides/platform/sso/azure.mdx +++ b/apps/docs/content/guides/platform/sso/azure.mdx @@ -25,7 +25,7 @@ Click the _Add_ button then _Enterprise application_. ![Azure AD console: Default Directory Overview](/docs/img/sso-azure-step-01.png) -## Step 2: Choose Create your own application [#create-application] +## Step 2: Choose to create your own application [#create-application] You'll be using the custom enterprise application setup for Supabase. @@ -40,7 +40,7 @@ don't find in the gallery (Non-gallery)_. ![Azure AD console: Create your own application modal](/docs/img/sso-azure-step-03.png) -## Step 4: Choose the Set up single sign-on option [#set-up-single-sign-on] +## Step 4: Set up single sign-on [#set-up-single-sign-on] Before you get to assigning users and groups, which would allow accounts in Azure AD to access Supabase, you need to configure the SAML details that allows Supabase to accept sign in requests from Azure AD. diff --git a/apps/docs/content/guides/platform/sso/gsuite.mdx b/apps/docs/content/guides/platform/sso/gsuite.mdx index e20a1140660..fbeeb299a63 100644 --- a/apps/docs/content/guides/platform/sso/gsuite.mdx +++ b/apps/docs/content/guides/platform/sso/gsuite.mdx @@ -21,7 +21,7 @@ Supabase supports single sign-on (SSO) using Google Workspace (formerly known as ![Google Workspace: Web and mobile apps admin console](/docs/img/sso-gsuite-step-01.png) -## Step 2: Choose Add custom SAML app [#add-custom-saml-app] +## Step 2: Choose to add custom SAML app [#add-custom-saml-app] From the _Add app_ button in the toolbar choose _Add custom SAML app_. diff --git a/apps/docs/content/guides/platform/sso/okta.mdx b/apps/docs/content/guides/platform/sso/okta.mdx index 78cce43d336..116a0995d80 100644 --- a/apps/docs/content/guides/platform/sso/okta.mdx +++ b/apps/docs/content/guides/platform/sso/okta.mdx @@ -17,7 +17,7 @@ Looking for docs on how to add Single Sign-On support in your Supabase project? Supabase supports single sign-on (SSO) using Okta. -## Step 1: Choose Create App Integration in the Applications dashboard [#create-app-integration] +## Step 1: Choose to create an app integration in the applications dashboard [#create-app-integration] Navigate to the Applications dashboard of the Okta admin console. Click _Create App Integration_. @@ -29,13 +29,13 @@ Supabase supports the SAML 2.0 SSO protocol. Choose it from the _Create a new ap ![Okta dashboard: Create new app integration dialog](/docs/img/sso-okta-step-02.png) -## Step 3: Fill out General Settings [#add-general-settings] +## Step 3: Fill out general settings [#add-general-settings] The information you enter here is for visibility into your Okta applications menu. You can choose any values you like. `Supabase` as a name works well for most use cases. ![Okta dashboard: Create SAML Integration wizard](/docs/img/sso-okta-step-03.png) -## Step 4: Fill out SAML Settings [#add-saml-settings] +## Step 4: Fill out SAML settings [#add-saml-settings] These settings let Supabase use SAML 2.0 properly with your Okta application. Make sure you enter this information exactly as shown on in this table and screenshot. @@ -51,7 +51,7 @@ These settings let Supabase use SAML 2.0 properly with your Okta application. Ma ![Okta dashboard: Create SAML Integration wizard, Configure SAML step](/docs/img/sso-okta-step-04.png) -## Step 5: Fill out Attribute Statements [#add-attribute-statements] +## Step 5: Fill out attribute statements [#add-attribute-statements] Attribute Statements allow Supabase to get information about your Okta users on each login. diff --git a/apps/docs/content/guides/platform/upgrading.mdx b/apps/docs/content/guides/platform/upgrading.mdx index 1bdab44de90..b7ab96b76a3 100644 --- a/apps/docs/content/guides/platform/upgrading.mdx +++ b/apps/docs/content/guides/platform/upgrading.mdx @@ -70,7 +70,7 @@ Breaking changes are generally only present in major version upgrades of Postgre If you are upgrading from a significantly older version, you will need to consider the release notes for any intermediary releases as well. -#### Time Limits +#### Time limits Starting from 2024-06-24, when a project is paused, users then have a 90-day window to restore the project on the platform from within Supabase Studio. @@ -100,7 +100,7 @@ If you upgrade to a paid plan while your project is paused, any expired one-clic src="/docs/img/guides/platform/paused-paid-tier.png" /> -#### Restoring a Downloaded Backup Locally +#### Restoring a downloaded backup locally If the 90 day project restore window has expired but you need to access data contained within your project using SQL, you can attempt to restore the project into a local Postgres instance. Supabase publishes tooling that can be used for that purpose. Be aware that this workflow does not produce a complete Supabase environment with REST/Auth/Storage. Instead, it creates a standalone Postgres instance that is maximally compatible with your project's backup file to assist with recovering your data. @@ -157,7 +157,7 @@ When upgrading, the Supabase platform will "right-size" your disk based on the c pg_upgrade does not support upgrading of databases containing reg\* data types referencing system OIDs. If you have created any objects that depend on the following extensions, you will need to recreate them after the upgrade. -#### pg_cron records +#### `pg_cron` records [pg_cron](https://github.com/citusdata/pg_cron#viewing-job-run-details) does not automatically clean up historical records. This can lead to extremely large `cron.job_run_details` tables if the records are not regularly pruned; you should clean unnecessary records from this table prior to an upgrade. diff --git a/apps/docs/content/guides/queues/quickstart.mdx b/apps/docs/content/guides/queues/quickstart.mdx index 426f057ea6c..6743a6cafe6 100644 --- a/apps/docs/content/guides/queues/quickstart.mdx +++ b/apps/docs/content/guides/queues/quickstart.mdx @@ -18,7 +18,7 @@ A pull-based Queue is a Message storage and delivery system where consumers acti A Message in a Queue is a json object that is stored until a consumer explicitly processes and removes it, like a task waiting in a to-do list until someone checks and completes it. -### Queue Types +### Queue types Supabase Queues offers three types of Queues: @@ -141,7 +141,7 @@ The permissions required for each Queue API database function: -### Enqueuing and Dequeuing Messages +### Enqueuing and dequeuing messages Once your Queue has been created, you can begin enqueuing and dequeuing Messages. diff --git a/apps/docs/content/guides/realtime/benchmarks.mdx b/apps/docs/content/guides/realtime/benchmarks.mdx new file mode 100644 index 00000000000..9a12182df42 --- /dev/null +++ b/apps/docs/content/guides/realtime/benchmarks.mdx @@ -0,0 +1,153 @@ +--- +title: 'Benchmarks' +subtitle: 'Benchmark results for Supabase Realtime.' +description: 'Check out reference numbers for Supabase Realtime performance.' +--- + +This guide explores what can be expected from Realtime's Postgres Changes, broadcast and presence performance. A set of load tests demonstrates its scaling capabilities. + +## Methodology + +The benchmarks were conducted using k6, an open-source load testing tool, against a Realtime Cluster deployed on AWS. The cluster configurations used 2-6 nodes, tested in both single-region and multi-region setups, all connected to a single Supabase project. The load generators (k6 servers) were also deployed on AWS to minimize network latency impact on the results. Tests were executed with the full load from the start without warm-up runs. + +The metrics collected include message throughput, latency percentiles, CPU and memory utilization, and connection success rates. It's worth noting that performance in production environments may vary based on factors such as network conditions, hardware specifications, and specific usage patterns. + +## Workloads + +The proposed workloads are designed to demonstrate Supabase Realtime's throughput and scalability capabilities. These benchmarks focus on core functionality and common usage patterns. + +The benchmarking results include the following workloads: + +1. **Broadcast Performance** +2. **Payload Size Impact on Broadcast** +3. **Large-Scale Broadcasting** +4. **Authentication and New Connection Rate** +5. **Database Events** + +## Results + +### Realtime Broadcast Performance + +This workload evaluates the system's capacity to handle multiple concurrent WebSocket connections and message broadcasting. Each virtual user (VU) in the test: + +- Establishes and maintains a WebSocket connection +- Joins two distinct channels: + - An echo channel (1 user per channel) for direct message reflection + - A broadcast channel (6 users per channel) for group communication +- Generates traffic by sending 2 messages per second to each joined channel for 10 minutes + +![Broadcast Performance](/docs/img/guides/realtime/broadcast-performance.png) + +| Metric | Value | +| ------------------- | ------------------------- | +| Concurrent Users | 32_000 | +| Total Channel Joins | 64_000 | +| Message Throughput | 224_000 msgs/sec | +| Median Latency | 6 ms | +| Latency (p95) | 28 ms | +| Latency (p99) | 213 ms | +| Data Received | 47.2 MB/s (29.6 GB total) | +| Data Sent | 15.2 MB/s (9.6 GB total) | +| New Connection Rate | 320 conn/sec | +| Channel Join Rate | 640 joins/sec | + +### Payload Size Impact + +This workload tests the system's performance with different message payload sizes to understand how data volume affects throughput and latency. Each virtual user (VU) follows the same connection pattern as the broadcast test, but with varying message sizes: + +- Establishes and maintains a WebSocket connection +- Joins two distinct channels: + - An echo channel (1 user per channel) for direct message reflection + - A broadcast channel (6 users per channel) for group communication +- Sends messages with payloads of 1KB, 10KB, and 50KB +- Generates traffic by sending 2 messages per second to each joined channel for 5 minutes + +#### 1KB Payload + +![1KB Payload Broadcast Performance](/docs/img/guides/realtime/payload-size-1kb.png) + +#### 10KB Payload + +![10KB Payload Broadcast Performance](/docs/img/guides/realtime/payload-size-10kb.png) + +#### 50KB Payload + +![50KB Payload Broadcast Performance](/docs/img/guides/realtime/payload-size-50kb-small.png) + +| Metric | 1KB Payload | 10KB Payload | 50KB Payload | 50KB Payload (Reduced Load) | +| ------------------ | ------------------- | ----------------- | ------------------ | --------------------------- | +| Concurrent Users | 4_000 | 4_000 | 4_000 | 2_000 | +| Message Throughput | 28_000 msgs/sec | 28_000 msgs/sec | 28_000 msgs/sec | 14_000 msgs/sec | +| Median Latency | 13 ms | 16 ms | 27 ms | 19 ms | +| Latency (p95) | 36 ms | 42 ms | 81 ms | 39 ms | +| Latency (p99) | 85 ms | 93 ms | 146 ms | 82 ms | +| Data Received | 31.2 MB/s (10.4 GB) | 268 MB/s (72 GB) | 1284 MB/s (348 GB) | 644 MB/s (176 GB) | +| Data Sent | 9.2 MB/s (3.1 GB) | 76 MB/s (20.8 GB) | 384 MB/s (104 GB) | 192 MB/s (52 GB) | + +> Note: The final column shows results with reduced load (2,000 users) for the 50KB payload test, demonstrating how the system performs with larger payloads under different concurrency levels. + +### Large-Scale Broadcasting + +This workload demonstrates Realtime's capability to handle high-scale scenarios with a large number of concurrent users and broadcast channels. The test simulates a scenario where each user participates in group communications with periodic message broadcasts: + +- Each virtual user (VU): + - Establishes and maintains a WebSocket connection (30-120 minutes) + - Joins 2 broadcast channels + - Sends 1 message per minute to each joined channel + - Each message is broadcasted to 100 other users + +![Large Broadcast Performance](/docs/img/guides/realtime/broadcast-large.png) + +| Metric | Value | +| ------------------- | ------------------ | +| Concurrent Users | 250_000 | +| Total Channel Joins | 500_000 | +| Users per Channel | 100 | +| Message Throughput | >800_000 msgs/sec | +| Median Latency | 58 ms | +| Latency (p95) | 279 ms | +| Latency (p99) | 508 ms | +| Data Received | 68 MB/s (600 GB) | +| Data Sent | 0.64 MB/s (5.7 GB) | + +### Realtime Auth + +This workload demonstrates Realtime's capability to handle large amounts of new connections per second and channel joins per second with Authentication Row Level Security (RLS) enabled for these channels. The test simulates a scenario where large volumes of users connect to realtime and participate in auth protected communications: + +- Each virtual user (VU): + - Establishes and maintains a WebSocket connection (2.5 minutes) + - Joins 2 broadcast channels + - Sends 1 message per minute to each joined channel + - Each message is broadcasted to 100 other users + +![Broadcast Auth Performance](/docs/img/guides/realtime/broadcast-auth.png) + +| Metric | Value | +| ------------------- | ----------------- | +| Concurrent Users | 50_000 | +| Total Channel Joins | 100_000 | +| Users per Channel | 100 | +| Message Throughput | >150_000 msgs/sec | +| New Connection Rate | 500 conn/sec | +| Channel Join Rate | 1000 joins/sec | +| Median Latency | 19 ms | +| Latency (p95) | 49 ms | +| Latency (p99) | 96 ms | + +### Realtime's Postgres Changes + +Realtime systems usually require forethought because of their scaling dynamics. For the `Postgres Changes` feature, every change event must be checked to see if the subscribed user has access. For instance, if you have 100 users subscribed to a table where you make a single insert, it will then trigger 100 "reads": one for each user. + +There can be a database bottleneck which limits message throughput. If your database cannot authorize the changes rapidly enough, the changes will be delayed until you receive a timeout. + +Database changes are processed on a single thread to maintain the change order. That means compute upgrades don't have a large effect on the performance of Postgres change subscriptions. You can estimate the expected maximum throughput for your database below. + +If you are using Postgres Changes at scale, you should consider using a separate "public" table without RLS and filters. Alternatively, you can use Realtime server-side only and then re-stream the changes to your clients using a Realtime Broadcast. + +Enter your database settings to estimate the maximum throughput for your instance: + + + +Don't forget to run your own benchmarks to make sure that the performance is acceptable for your use case. + +Supabase continues to make improvements to Realtime's Postgres Changes. If you are uncertain about your use case performance, reach out using the [Support Form](https://supabase.com/dashboard/support/new). The support team can advise on the best solution for each use-case. diff --git a/apps/docs/content/guides/realtime/postgres-changes.mdx b/apps/docs/content/guides/realtime/postgres-changes.mdx index 51a50672d38..721d29c6d93 100644 --- a/apps/docs/content/guides/realtime/postgres-changes.mdx +++ b/apps/docs/content/guides/realtime/postgres-changes.mdx @@ -294,7 +294,7 @@ changes = supabase.channel('schema-db-changes').on_postgres_changes( The channel name can be any string except 'realtime'. -### Listening to INSERT events +### Listening to `INSERT` events - If you are using rootless docker, edit `.env` and set `DOCKER_SOCKET_LOCATION` to your docker socket location. For example: `/run/user/1000/docker.sock`. Otherwise, you will see an error like `container supabase-vector exited (0)`. - - - After all the services have started you can see them running in the background: ```sh @@ -93,16 +86,13 @@ docker compose ps All of the services should have a status `running (healthy)`. If you see a status like `created` but not `running`, try starting that service manually with `docker compose start `. - Your app is now running with default credentials. Please [secure your services](#securing-your-services) as soon as possible using the instructions below. - - ### Accessing Supabase Studio You can access Supabase Studio through the API gateway on port `8000`. For example: `http://:8000`, or [localhost:8000](http://localhost:8000) if you are running Docker locally. @@ -167,7 +157,6 @@ You'll want to update the Studio(Dashboard) frequently to get the latest feature While we provided you with some example secrets for getting started, you should NEVER deploy your Supabase setup using the defaults we have provided. Please follow all of the steps in this section to ensure you have a secure setup, and then [restart all services](#restarting-all-services) to pick up the changes. - ### Generate API keys We need to generate secure keys for accessing your services. We'll use the `JWT Secret` to generate `anon` and `service` API keys using the form below. @@ -175,7 +164,6 @@ We need to generate secure keys for accessing your services. We'll use the `JWT 2. **Store Securely**: Save the secret in a secure location on your local machine. Don't share this secret publicly or commit it to version control. 3. **Generate a JWT**: Use the form below to generate a new `JWT` using your secret. - ### Update API keys @@ -422,8 +410,7 @@ docker rm supabase-analytics ## Demo -A minimal setup working on Ubuntu, hosted on Digital Ocean. - +A minimal setup working on Ubuntu, hosted on DigitalOcean.
-### Demo using Digital Ocean -1. A Digital Ocean Droplet with 1 GB memory and 25 GB solid-state drive (SSD) is sufficient to start +### Demo using DigitalOcean +1. A DigitalOcean Droplet with 1 GB memory and 25 GB solid-state drive (SSD) is sufficient to start 2. To access the Dashboard, use the ipv4 IP address of your Droplet. 3. If you're unable to access Dashboard, run `docker compose ps` to see if the Studio service is running and healthy. - - - diff --git a/apps/docs/content/guides/storage/debugging/error-codes.mdx b/apps/docs/content/guides/storage/debugging/error-codes.mdx index 7d76cfe26cb..cce0e9a6878 100644 --- a/apps/docs/content/guides/storage/debugging/error-codes.mdx +++ b/apps/docs/content/guides/storage/debugging/error-codes.mdx @@ -6,7 +6,7 @@ subtitle: 'Learn about the Storage error codes and how to resolve them' sidebar_label: 'Debugging' --- -## Storage Error Codes +## Storage error codes We are transitioning to a new error code system. For backwards compatibility you'll still be able @@ -60,7 +60,7 @@ Here is the full list of error codes and their descriptions: | MissingPart | A part of the entity is missing. | 400 | Ensure all parts of the entity are included in the request before completing the operation. | | SlowDown | The request rate is too high and has been throttled. | 503 | Reduce the request rate or implement exponential backoff and retry mechanisms to handle throttling. | -## Legacy Error Codes +## Legacy error codes As we are transitioning to a new error code system, you might still see the following error format: diff --git a/apps/docs/content/guides/storage/schema/custom-roles.mdx b/apps/docs/content/guides/storage/schema/custom-roles.mdx index f020ccbd696..9af072af00e 100644 --- a/apps/docs/content/guides/storage/schema/custom-roles.mdx +++ b/apps/docs/content/guides/storage/schema/custom-roles.mdx @@ -10,7 +10,7 @@ In this guide, you will learn how to create and use custom roles with Storage to Supabase Storage uses the same role-based access control system as any other Supabase service using RLS (Row Level Security). -## Create a Custom Role +## Create a custom role Let's create a custom role `manager` to provide full read access to a specific bucket. For a more advanced setup, see the [RBAC Guide](/docs/guides/auth/custom-claims-and-role-based-access-control-rbac#create-auth-hook-to-apply-user-role). diff --git a/apps/docs/content/guides/storage/serving/bandwidth.mdx b/apps/docs/content/guides/storage/serving/bandwidth.mdx index b2153fbb91c..740931c242d 100644 --- a/apps/docs/content/guides/storage/serving/bandwidth.mdx +++ b/apps/docs/content/guides/storage/serving/bandwidth.mdx @@ -6,13 +6,13 @@ subtitle: 'Bandwidth & Storage Egress' sidebar_label: 'Bandwidth & Storage Egress' --- -## Bandwidth & Storage Egress +## Bandwidth & Storage egress Free Plan Organizations in Supabase have a limit of 5 GB of bandwidth. This limit is calculated by the sum of all the data transferred from the Supabase servers to the client. This includes all the data transferred from the database, storage, and functions. -### Checking Storage Egress Requests in Log Explorer: +### Checking Storage egress requests in Logs Explorer: -We have a template query that you can use to get the number of requests for each object in [Log Explorer](/dashboard/project/_/logs/explorer/templates). +We have a template query that you can use to get the number of requests for each object in [Logs Explorer](/dashboard/project/_/logs/explorer/templates). ```sql select @@ -45,7 +45,7 @@ Example of the output: ] ``` -### Calculating Egress: +### Calculating egress: If you already know the size of those files, you can calculate the egress by multiplying the number of requests by the size of the file. You can also get the size of the file with the following cURL: @@ -67,6 +67,6 @@ Total Egress = 395.76MB You can see that these values can get quite large, so it's important to keep track of the egress and optimize the files. -### Optimizing Egress: +### Optimizing egress: If you are on the Pro Plan, you can use the [Supabase Image Transformations](/docs/guides/storage/image-transformations) to optimize the images and reduce the egress. diff --git a/apps/docs/features/docs/MdxBase.tsx b/apps/docs/features/docs/MdxBase.tsx index 20413a1cc7d..3077c0a59ae 100644 --- a/apps/docs/features/docs/MdxBase.tsx +++ b/apps/docs/features/docs/MdxBase.tsx @@ -1,4 +1,4 @@ -import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } +import codeHikeTheme from 'config/code-hike.theme.json' with { type: 'json' } import { remarkCodeHike, type CodeHikeConfig } from '@code-hike/mdx' import { type SerializeOptions } from 'next-mdx-remote/dist/types' import { MDXRemote } from 'next-mdx-remote/rsc' diff --git a/apps/docs/features/docs/Reference.generated.script.ts b/apps/docs/features/docs/Reference.generated.script.ts index 4eb584ae6a9..42c5f1b6a5c 100644 --- a/apps/docs/features/docs/Reference.generated.script.ts +++ b/apps/docs/features/docs/Reference.generated.script.ts @@ -1,27 +1,27 @@ -import { keyBy, isPlainObject } from 'lodash' +import { isPlainObject, keyBy } from 'lodash' import { mkdir, readFile, writeFile } from 'node:fs/promises' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' import slugify from 'slugify' import { parse } from 'yaml' -import { REFERENCES, clientSdkIds } from '~/content/navigation.references' +import { clientSdkIds, REFERENCES } from '~/content/navigation.references' import { parseTypeSpec } from '~/features/docs/Reference.typeSpec' import type { AbbrevApiReferenceSection } from '~/features/docs/Reference.utils' import { deepFilterRec } from '~/features/helpers.fn' import type { Json } from '~/features/helpers.types' -import authSpec from '~/spec/auth_v1_openapi.json' assert { type: 'json' } -import apiCommonSections from '~/spec/common-api-sections.json' assert { type: 'json' } -import cliCommonSections from '~/spec/common-cli-sections.json' assert { type: 'json' } -import commonClientLibSections from '~/spec/common-client-libs-sections.json' assert { type: 'json' } -import selfHostingAnalyticsCommonSections from '~/spec/common-self-hosting-analytics-sections.json' assert { type: 'json' } +import authSpec from '~/spec/auth_v1_openapi.json' with { type: 'json' } +import apiCommonSections from '~/spec/common-api-sections.json' with { type: 'json' } +import cliCommonSections from '~/spec/common-cli-sections.json' with { type: 'json' } +import commonClientLibSections from '~/spec/common-client-libs-sections.json' with { type: 'json' } +import selfHostingAnalyticsCommonSections from '~/spec/common-self-hosting-analytics-sections.json' with { type: 'json' } import selfHostingAuthCommonSections from '~/spec/common-self-hosting-auth-sections.json' -import selfHostingFunctionsCommonSections from '~/spec/common-self-hosting-functions-sections.json' assert { type: 'json' } -import selfHostingRealtimeCommonSections from '~/spec/common-self-hosting-realtime-sections.json' assert { type: 'json' } -import selfHostingStorageCommonSections from '~/spec/common-self-hosting-storage-sections.json' assert { type: 'json' } -import storageSpec from '~/spec/storage_v0_openapi.json' assert { type: 'json' } -import analyticsSpec from '~/spec/transforms/analytics_v0_openapi_deparsed.json' assert { type: 'json' } -import openApiSpec from '~/spec/transforms/api_v1_openapi_deparsed.json' assert { type: 'json' } +import selfHostingFunctionsCommonSections from '~/spec/common-self-hosting-functions-sections.json' with { type: 'json' } +import selfHostingRealtimeCommonSections from '~/spec/common-self-hosting-realtime-sections.json' with { type: 'json' } +import selfHostingStorageCommonSections from '~/spec/common-self-hosting-storage-sections.json' with { type: 'json' } +import storageSpec from '~/spec/storage_v0_openapi.json' with { type: 'json' } +import analyticsSpec from '~/spec/transforms/analytics_v0_openapi_deparsed.json' with { type: 'json' } +import openApiSpec from '~/spec/transforms/api_v1_openapi_deparsed.json' with { type: 'json' } import { IApiEndPoint } from './Reference.api.utils' const DOCS_DIRECTORY = join(dirname(fileURLToPath(import.meta.url)), '../..') diff --git a/apps/docs/features/docs/Reference.introduction.tsx b/apps/docs/features/docs/Reference.introduction.tsx index 66e4609c77d..f799668bfe3 100644 --- a/apps/docs/features/docs/Reference.introduction.tsx +++ b/apps/docs/features/docs/Reference.introduction.tsx @@ -5,7 +5,7 @@ import { Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_, cn } from import { getRefMarkdown, MDXRemoteRefs } from '~/features/docs/Reference.mdx' import { ReferenceSectionWrapper } from '~/features/docs/Reference.ui.client' -import commonClientLibSections from '~/spec/common-client-libs-sections.json' assert { type: 'json' } +import commonClientLibSections from '~/spec/common-client-libs-sections.json' with { type: 'json' } function hasIntro(sections: typeof commonClientLibSections, excludeName?: string) { return Boolean( diff --git a/apps/docs/features/docs/Reference.typeSpec.ts b/apps/docs/features/docs/Reference.typeSpec.ts index fe1a41962d7..589ee7702f7 100644 --- a/apps/docs/features/docs/Reference.typeSpec.ts +++ b/apps/docs/features/docs/Reference.typeSpec.ts @@ -6,7 +6,7 @@ * access to a function's type definition, given its name and module. */ -import _typeSpec from '~/spec/enrichments/tsdoc_v2/combined.json' assert { type: 'json' } +import _typeSpec from '~/spec/enrichments/tsdoc_v2/combined.json' with { type: 'json' } // [Charis] 2024-07-10 // Types are more trouble than they're worth here: manually defining the types @@ -303,7 +303,11 @@ function parseMethod( function parseSignature( signature: any, map: Map -): { params: Array; ret: ReturnType; comment?: Comment } { +): { + params: Array + ret: ReturnType + comment?: Comment +} { const params: Array = (signature.parameters ?? []).map((param: any) => { const type = parseType(param.type, map) diff --git a/apps/docs/lib/docs.ts b/apps/docs/lib/docs.ts index 998752e2016..4be5a7dfb01 100644 --- a/apps/docs/lib/docs.ts +++ b/apps/docs/lib/docs.ts @@ -4,12 +4,12 @@ import { serialize } from 'next-mdx-remote/serialize' import type { SerializeOptions } from 'next-mdx-remote/dist/types' import { existsSync } from 'node:fs' import { readdir, readFile } from 'node:fs/promises' -import { join, extname, sep, basename } from 'node:path' +import { basename, extname, join, sep } from 'node:path' import remarkGfm from 'remark-gfm' import rehypeKatex from 'rehype-katex' import remarkMath from 'remark-math' -import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } +import codeHikeTheme from 'config/code-hike.theme.json' with { type: 'json' } // MUST be process.cwd() here, not import.meta.url, or files that are added // with outputFileTracingIncludes (not auto-traced) will not be found at diff --git a/apps/docs/lib/mdx/generateRefMarkdown.tsx b/apps/docs/lib/mdx/generateRefMarkdown.tsx index 165783822ba..1cd3896d1ac 100644 --- a/apps/docs/lib/mdx/generateRefMarkdown.tsx +++ b/apps/docs/lib/mdx/generateRefMarkdown.tsx @@ -1,7 +1,7 @@ import fs from 'fs' import { CodeHikeConfig, remarkCodeHike } from '@code-hike/mdx' -import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } +import codeHikeTheme from 'config/code-hike.theme.json' with { type: 'json' } import matter from 'gray-matter' import { serialize } from 'next-mdx-remote/serialize' import remarkGfm from 'remark-gfm' diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs index 37333dad42d..167677a302d 100644 --- a/apps/docs/next.config.mjs +++ b/apps/docs/next.config.mjs @@ -8,7 +8,7 @@ import remarkGfm from 'remark-gfm' import configureBundleAnalyzer from '@next/bundle-analyzer' import withYaml from 'next-plugin-yaml' -import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } +import codeHikeTheme from 'config/code-hike.theme.json' with { type: 'json' } import remotePatterns from './lib/remotePatterns.js' const withBundleAnalyzer = configureBundleAnalyzer({ diff --git a/apps/docs/public/humans.txt b/apps/docs/public/humans.txt index b631dfe115c..73b70492351 100644 --- a/apps/docs/public/humans.txt +++ b/apps/docs/public/humans.txt @@ -56,6 +56,7 @@ Kevin Brolly Kevin Grüneberg Lakshan Perera Laura C +Laurence Isla Long Hoang Łukasz Niemier Margarita Sandomirskaia diff --git a/apps/docs/public/img/guides/auth-workos/supabase-project-settings-url.png b/apps/docs/public/img/guides/auth-workos/supabase-project-settings-url.png new file mode 100644 index 00000000000..ed1312ac170 Binary files /dev/null and b/apps/docs/public/img/guides/auth-workos/supabase-project-settings-url.png differ diff --git a/apps/docs/public/img/guides/auth-workos/supabase-workos-configuration.png b/apps/docs/public/img/guides/auth-workos/supabase-workos-configuration.png new file mode 100644 index 00000000000..a2fb6b25061 Binary files /dev/null and b/apps/docs/public/img/guides/auth-workos/supabase-workos-configuration.png differ diff --git a/apps/docs/public/img/guides/auth-workos/workos-auth-config-dashboard.png b/apps/docs/public/img/guides/auth-workos/workos-auth-config-dashboard.png new file mode 100644 index 00000000000..814f4d94769 Binary files /dev/null and b/apps/docs/public/img/guides/auth-workos/workos-auth-config-dashboard.png differ diff --git a/apps/docs/public/img/guides/auth-workos/workos-clientid-redirect-uri.png b/apps/docs/public/img/guides/auth-workos/workos-clientid-redirect-uri.png deleted file mode 100644 index 131d0471042..00000000000 Binary files a/apps/docs/public/img/guides/auth-workos/workos-clientid-redirect-uri.png and /dev/null differ diff --git a/apps/docs/public/img/guides/auth-workos/workos-create-organization.png b/apps/docs/public/img/guides/auth-workos/workos-create-organization.png index 4f9d7798de5..f7b6dedc3ce 100644 Binary files a/apps/docs/public/img/guides/auth-workos/workos-create-organization.png and b/apps/docs/public/img/guides/auth-workos/workos-create-organization.png differ diff --git a/apps/docs/public/img/guides/auth-workos/workos-dashboard-get-client-id-and-key.png b/apps/docs/public/img/guides/auth-workos/workos-dashboard-get-client-id-and-key.png new file mode 100644 index 00000000000..dc7e42a8b96 Binary files /dev/null and b/apps/docs/public/img/guides/auth-workos/workos-dashboard-get-client-id-and-key.png differ diff --git a/apps/docs/public/img/guides/auth-workos/workos-secret-key.png b/apps/docs/public/img/guides/auth-workos/workos-secret-key.png deleted file mode 100644 index 9c2df99be53..00000000000 Binary files a/apps/docs/public/img/guides/auth-workos/workos-secret-key.png and /dev/null differ diff --git a/apps/docs/public/img/guides/auth-workos/workos-set-supabase-redirect.png b/apps/docs/public/img/guides/auth-workos/workos-set-supabase-redirect.png new file mode 100644 index 00000000000..0fbb94cc261 Binary files /dev/null and b/apps/docs/public/img/guides/auth-workos/workos-set-supabase-redirect.png differ diff --git a/apps/docs/public/img/guides/auth-workos/workos-setup-identity-provider.png b/apps/docs/public/img/guides/auth-workos/workos-setup-identity-provider.png deleted file mode 100644 index de464f443c2..00000000000 Binary files a/apps/docs/public/img/guides/auth-workos/workos-setup-identity-provider.png and /dev/null differ diff --git a/apps/docs/public/img/guides/realtime/broadcast-auth.png b/apps/docs/public/img/guides/realtime/broadcast-auth.png new file mode 100644 index 00000000000..74a6cbc1a36 Binary files /dev/null and b/apps/docs/public/img/guides/realtime/broadcast-auth.png differ diff --git a/apps/docs/public/img/guides/realtime/broadcast-large.png b/apps/docs/public/img/guides/realtime/broadcast-large.png new file mode 100644 index 00000000000..9b881ca7036 Binary files /dev/null and b/apps/docs/public/img/guides/realtime/broadcast-large.png differ diff --git a/apps/docs/public/img/guides/realtime/broadcast-performance.png b/apps/docs/public/img/guides/realtime/broadcast-performance.png new file mode 100644 index 00000000000..24177d7cbc1 Binary files /dev/null and b/apps/docs/public/img/guides/realtime/broadcast-performance.png differ diff --git a/apps/docs/public/img/guides/realtime/payload-size-10kb.png b/apps/docs/public/img/guides/realtime/payload-size-10kb.png new file mode 100644 index 00000000000..bda28c7a90e Binary files /dev/null and b/apps/docs/public/img/guides/realtime/payload-size-10kb.png differ diff --git a/apps/docs/public/img/guides/realtime/payload-size-1kb.png b/apps/docs/public/img/guides/realtime/payload-size-1kb.png new file mode 100644 index 00000000000..b7492a489a6 Binary files /dev/null and b/apps/docs/public/img/guides/realtime/payload-size-1kb.png differ diff --git a/apps/docs/public/img/guides/realtime/payload-size-50kb-small.png b/apps/docs/public/img/guides/realtime/payload-size-50kb-small.png new file mode 100644 index 00000000000..b9e562b927e Binary files /dev/null and b/apps/docs/public/img/guides/realtime/payload-size-50kb-small.png differ diff --git a/apps/docs/spec/api_v1_openapi.json b/apps/docs/spec/api_v1_openapi.json index 1660b7e6d45..3802f4ea3f4 100644 --- a/apps/docs/spec/api_v1_openapi.json +++ b/apps/docs/spec/api_v1_openapi.json @@ -88,6 +88,35 @@ "security": [{ "bearer": [] }] } }, + "/v1/branches/{branch_id}/push": { + "post": { + "operationId": "v1-push-a-branch", + "summary": "Pushes a database branch", + "description": "Pushes the specified database branch", + "parameters": [ + { + "name": "branch_id", + "required": true, + "in": "path", + "description": "Branch ID", + "schema": { "type": "string" } + } + ], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/BranchUpdateResponse" } + } + } + }, + "500": { "description": "Failed to push database branch" } + }, + "tags": ["Environments"], + "security": [{ "bearer": [] }] + } + }, "/v1/branches/{branch_id}/reset": { "post": { "operationId": "v1-reset-a-branch", @@ -107,7 +136,7 @@ "description": "", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/BranchResetResponse" } + "schema": { "$ref": "#/components/schemas/BranchUpdateResponse" } } } }, @@ -284,6 +313,24 @@ "security": [{ "oauth2": ["write"] }] } }, + "/v1/oauth/revoke": { + "post": { + "operationId": "v1-revoke-token", + "summary": "[Beta] Revoke oauth app authorization and it's corresponding tokens", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/OAuthRevokeTokenBodyDto" } + } + } + }, + "responses": { "204": { "description": "" } }, + "tags": ["OAuth"], + "security": [{ "oauth2": ["write"] }] + } + }, "/v1/snippets": { "get": { "operationId": "v1-list-all-snippets", @@ -1977,6 +2024,24 @@ "security": [{ "bearer": [] }] } }, + "/v1/projects/{ref}/pause": { + "post": { + "operationId": "v1-pause-a-project", + "summary": "Pauses the given project", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + } + ], + "responses": { "200": { "description": "" }, "403": { "description": "" } }, + "tags": ["Projects"], + "security": [{ "bearer": [] }] + } + }, "/v1/projects/{ref}/database/query": { "post": { "operationId": "v1-run-a-query", @@ -2670,9 +2735,13 @@ "UpdateBranchBody": { "type": "object", "properties": { + "reset_on_push": { + "type": "boolean", + "deprecated": true, + "description": "This field is deprecated and will be ignored. Use v1-reset-a-branch endpoint directly instead." + }, "branch_name": { "type": "string" }, "git_branch": { "type": "string" }, - "reset_on_push": { "type": "boolean" }, "persistent": { "type": "boolean" }, "status": { "enum": [ @@ -2691,14 +2760,17 @@ "type": "object", "properties": { "pr_number": { "type": "integer", "format": "int32" }, - "latest_check_run_id": { "type": "integer", "format": "int64" }, + "latest_check_run_id": { + "type": "number", + "deprecated": true, + "description": "This field is deprecated and will not be populated." + }, "id": { "type": "string" }, "name": { "type": "string" }, "project_ref": { "type": "string" }, "parent_project_ref": { "type": "string" }, "is_default": { "type": "boolean" }, "git_branch": { "type": "string" }, - "reset_on_push": { "type": "boolean" }, "persistent": { "type": "boolean" }, "status": { "enum": [ @@ -2720,7 +2792,6 @@ "project_ref", "parent_project_ref", "is_default", - "reset_on_push", "persistent", "status", "created_at", @@ -2732,7 +2803,7 @@ "properties": { "message": { "type": "string" } }, "required": ["message"] }, - "BranchResetResponse": { + "BranchUpdateResponse": { "type": "object", "properties": { "workflow_run_id": { "type": "string" }, "message": { "type": "string" } }, "required": ["workflow_run_id", "message"] @@ -2791,10 +2862,7 @@ "type": "object", "properties": { "db_pass": { "type": "string", "description": "Database password" }, - "name": { - "type": "string", - "description": "Name of your project, should not contain dots" - }, + "name": { "type": "string", "description": "Name of your project" }, "organization_id": { "type": "string", "description": "Slug of your organization" }, "plan": { "type": "string", @@ -2939,6 +3007,16 @@ }, "required": ["expires_in", "token_type", "access_token", "refresh_token"] }, + "OAuthRevokeTokenBodyDto": { + "type": "object", + "properties": { + "client_id": { "type": "string", "format": "uuid" }, + "client_secret": { "type": "string" }, + "refresh_token": { "type": "string" } + }, + "required": ["client_id", "client_secret", "refresh_token"], + "additionalProperties": false + }, "SnippetProject": { "type": "object", "properties": { diff --git a/apps/docs/spec/common-api-sections.json b/apps/docs/spec/common-api-sections.json index acbc5d0da6a..821e282f141 100644 --- a/apps/docs/spec/common-api-sections.json +++ b/apps/docs/spec/common-api-sections.json @@ -297,6 +297,12 @@ "slug": "v1-list-all-branches", "type": "operation" }, + { + "id": "v1-push-a-branch", + "title": "Push a branch", + "slug": "v1-push-a-branch", + "type": "operation" + }, { "id": "v1-reset-a-branch", "title": "Reset a branch", @@ -326,6 +332,12 @@ "title": "Exchange oauth token", "slug": "v1-exchange-oauth-token", "type": "operation" + }, + { + "id": "v1-revoke-token", + "title": "Revoke token", + "slug": "v1-revoke-token", + "type": "operation" } ] }, @@ -423,6 +435,12 @@ "slug": "v1-list-all-projects", "type": "operation" }, + { + "id": "v1-pause-a-project", + "title": "Pause a project", + "slug": "v1-pause-a-project", + "type": "operation" + }, { "id": "v1-update-network-restrictions", "title": "Update network restrictions", diff --git a/apps/docs/spec/transforms/api_v1_openapi_deparsed.json b/apps/docs/spec/transforms/api_v1_openapi_deparsed.json index 6c6e1572af7..f6305737fb3 100644 --- a/apps/docs/spec/transforms/api_v1_openapi_deparsed.json +++ b/apps/docs/spec/transforms/api_v1_openapi_deparsed.json @@ -172,15 +172,17 @@ "schema": { "type": "object", "properties": { + "reset_on_push": { + "type": "boolean", + "deprecated": true, + "description": "This field is deprecated and will be ignored. Use v1-reset-a-branch endpoint directly instead." + }, "branch_name": { "type": "string" }, "git_branch": { "type": "string" }, - "reset_on_push": { - "type": "boolean" - }, "persistent": { "type": "boolean" }, @@ -213,8 +215,9 @@ "format": "int32" }, "latest_check_run_id": { - "type": "integer", - "format": "int64" + "type": "number", + "deprecated": true, + "description": "This field is deprecated and will not be populated." }, "id": { "type": "string" @@ -234,9 +237,6 @@ "git_branch": { "type": "string" }, - "reset_on_push": { - "type": "boolean" - }, "persistent": { "type": "boolean" }, @@ -264,7 +264,6 @@ "project_ref", "parent_project_ref", "is_default", - "reset_on_push", "persistent", "status", "created_at", @@ -329,6 +328,54 @@ ] } }, + "/v1/branches/{branch_id}/push": { + "post": { + "operationId": "v1-push-a-branch", + "summary": "Pushes a database branch", + "description": "Pushes the specified database branch", + "parameters": [ + { + "name": "branch_id", + "required": true, + "in": "path", + "description": "Branch ID", + "schema": { + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "workflow_run_id": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": ["workflow_run_id", "message"] + } + } + } + }, + "500": { + "description": "Failed to push database branch" + } + }, + "tags": ["Environments"], + "security": [ + { + "bearer": [] + } + ] + } + }, "/v1/branches/{branch_id}/reset": { "post": { "operationId": "v1-reset-a-branch", @@ -497,7 +544,7 @@ }, "name": { "type": "string", - "description": "Name of your project, should not contain dots" + "description": "Name of your project" }, "organization_id": { "type": "string", @@ -896,6 +943,48 @@ ] } }, + "/v1/oauth/revoke": { + "post": { + "operationId": "v1-revoke-token", + "summary": "[Beta] Revoke oauth app authorization and it's corresponding tokens", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "client_id": { + "type": "string", + "format": "uuid" + }, + "client_secret": { + "type": "string" + }, + "refresh_token": { + "type": "string" + } + }, + "required": ["client_id", "client_secret", "refresh_token"], + "additionalProperties": false + } + } + } + }, + "responses": { + "204": { + "description": "" + } + }, + "tags": ["OAuth"], + "security": [ + { + "oauth2": ["write"] + } + ] + } + }, "/v1/snippets": { "get": { "operationId": "v1-list-all-snippets", @@ -1753,8 +1842,9 @@ "format": "int32" }, "latest_check_run_id": { - "type": "integer", - "format": "int64" + "type": "number", + "deprecated": true, + "description": "This field is deprecated and will not be populated." }, "id": { "type": "string" @@ -1774,9 +1864,6 @@ "git_branch": { "type": "string" }, - "reset_on_push": { - "type": "boolean" - }, "persistent": { "type": "boolean" }, @@ -1804,7 +1891,6 @@ "project_ref", "parent_project_ref", "is_default", - "reset_on_push", "persistent", "status", "created_at", @@ -1905,8 +1991,9 @@ "format": "int32" }, "latest_check_run_id": { - "type": "integer", - "format": "int64" + "type": "number", + "deprecated": true, + "description": "This field is deprecated and will not be populated." }, "id": { "type": "string" @@ -1926,9 +2013,6 @@ "git_branch": { "type": "string" }, - "reset_on_push": { - "type": "boolean" - }, "persistent": { "type": "boolean" }, @@ -1956,7 +2040,6 @@ "project_ref", "parent_project_ref", "is_default", - "reset_on_push", "persistent", "status", "created_at", @@ -8021,6 +8104,39 @@ ] } }, + "/v1/projects/{ref}/pause": { + "post": { + "operationId": "v1-pause-a-project", + "summary": "Pauses the given project", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "" + }, + "403": { + "description": "" + } + }, + "tags": ["Projects"], + "security": [ + { + "bearer": [] + } + ] + } + }, "/v1/projects/{ref}/database/query": { "post": { "operationId": "v1-run-a-query", @@ -10072,15 +10188,17 @@ "UpdateBranchBody": { "type": "object", "properties": { + "reset_on_push": { + "type": "boolean", + "deprecated": true, + "description": "This field is deprecated and will be ignored. Use v1-reset-a-branch endpoint directly instead." + }, "branch_name": { "type": "string" }, "git_branch": { "type": "string" }, - "reset_on_push": { - "type": "boolean" - }, "persistent": { "type": "boolean" }, @@ -10105,8 +10223,9 @@ "format": "int32" }, "latest_check_run_id": { - "type": "integer", - "format": "int64" + "type": "number", + "deprecated": true, + "description": "This field is deprecated and will not be populated." }, "id": { "type": "string" @@ -10126,9 +10245,6 @@ "git_branch": { "type": "string" }, - "reset_on_push": { - "type": "boolean" - }, "persistent": { "type": "boolean" }, @@ -10156,7 +10272,6 @@ "project_ref", "parent_project_ref", "is_default", - "reset_on_push", "persistent", "status", "created_at", @@ -10172,7 +10287,7 @@ }, "required": ["message"] }, - "BranchResetResponse": { + "BranchUpdateResponse": { "type": "object", "properties": { "workflow_run_id": { @@ -10285,7 +10400,7 @@ }, "name": { "type": "string", - "description": "Name of your project, should not contain dots" + "description": "Name of your project" }, "organization_id": { "type": "string", @@ -10479,6 +10594,23 @@ }, "required": ["expires_in", "token_type", "access_token", "refresh_token"] }, + "OAuthRevokeTokenBodyDto": { + "type": "object", + "properties": { + "client_id": { + "type": "string", + "format": "uuid" + }, + "client_secret": { + "type": "string" + }, + "refresh_token": { + "type": "string" + } + }, + "required": ["client_id", "client_secret", "refresh_token"], + "additionalProperties": false + }, "SnippetProject": { "type": "object", "properties": { diff --git a/apps/studio/.storybook/main.js b/apps/studio/.storybook/main.js deleted file mode 100644 index bc8160546ae..00000000000 --- a/apps/studio/.storybook/main.js +++ /dev/null @@ -1,15 +0,0 @@ -const path = require('path') -module.exports = { - stories: ['./*.stories.mdx', '../components/**/*.stories.@(js|jsx|ts|tsx)'], - addons: [ - '@storybook/addon-links', - '@storybook/addon-essentials', - '@storybook/addon-interactions', - 'storybook-dark-mode', - ], - - webpackFinal: async (config) => { - config.resolve.modules = [...(config.resolve.modules || []), path.resolve(__dirname, '../')] - return config - }, -} diff --git a/apps/studio/.storybook/preview.css b/apps/studio/.storybook/preview.css deleted file mode 100644 index 29b55284e98..00000000000 --- a/apps/studio/.storybook/preview.css +++ /dev/null @@ -1,113 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -.dark, -.light, -.light .docs-story, -.dark .docs-story { - @apply bg-studio; -} - -/* // date picker */ -/* !!!! THIS IS A COPY OF THE styles/date-picker.scss !! */ - -.react-datepicker__input-container input { - @apply block w-full text-base md:text-sm bg-white border border-muted rounded shadow-sm; -} - -.react-datepicker-popper { - @apply z-40 w-72 text-sm bg-surface-100 shadow px-3 py-2 border-2 border-background rounded; -} - -.react-datepicker-left { - @apply absolute left-0 right-auto top-11 transform-none !important; -} - -.react-datepicker-right { - @apply absolute right-0 left-auto top-11 transform-none !important; -} - -.react-datepicker__portal { - @apply absolute z-10 w-72 text-sm transform-none bg-white shadow px-3 py-2 top-12 right-0 border-2 border-background rounded; -} - -.react-datepicker__month-container { - @apply flex flex-col; -} - -.react-datepicker__month { - @apply flex flex-col; - @apply -space-y-px; -} - -.react-datepicker__current-month { - @apply ml-2.5 text-lg font-semibold text-border-stronger; -} - -.react-datepicker__week { - @apply flex -space-x-px; -} - -.react-datepicker__day-names { - @apply flex -space-x-px text-foreground-lighter font-normal text-center text-xs; -} - -.react-datepicker__day-name { - @apply w-10 h-10 flex items-center justify-center py-1 rounded-full; -} - -.react-datepicker__navigation { - @apply absolute top-2; -} - -.react-datepicker__navigation--previous { - @apply right-12 w-10 h-10 rounded transition flex items-center justify-center hover:bg-studio; -} - -.react-datepicker__navigation--next { - @apply right-4 w-10 h-10 rounded transition flex items-center justify-center hover:bg-studio; -} - -.react-datepicker__day { - @apply w-10 h-10 flex items-center font-normal justify-center text-sm leading-loose transition text-foreground-light; - /* @apply mb-1 py-1; */ - @apply -space-y-px; - @apply border border-default; -} - -.react-datepicker__day--disabled { - @apply cursor-not-allowed opacity-40 hover:bg-transparent; -} - -.react-datepicker__day--outside-month { - @apply text-border-stronger; -} - -.react-datepicker__day--in-range { - @apply bg-brand-300 text-scale-fixed-100 border-brand-300; -} - -.react-datepicker__day--in-selecting-range { - @apply bg-brand-400 text-scale-fixed-100 border-brand-400; -} - -.react-datepicker__day--selecting-range-start { - @apply bg-studio; -} - -.react-datepicker__day--selecting-range-end { - @apply bg-studio; -} - -.react-datepicker__day--selected { - @apply bg-brand-300 text-white; -} - -.react-datepicker__day--range-start { - @apply bg-brand text-white hover:text-border-strong hover:bg-white; -} - -.react-datepicker__day--range-end { - @apply bg-brand text-white hover:text-border-strong hover:bg-white; -} diff --git a/apps/studio/.storybook/preview.js b/apps/studio/.storybook/preview.js deleted file mode 100644 index b91e38238f4..00000000000 --- a/apps/studio/.storybook/preview.js +++ /dev/null @@ -1,22 +0,0 @@ -import './preview.css' - -export const parameters = { - actions: { argTypesRegex: '^on[A-Z].*' }, - controls: { - matchers: { - color: /(background|color)$/i, - date: /Date$/, - }, - }, - darkMode: { - darkClass: 'dark', - lightClass: 'light', - stylePreview: true, - }, -} - -export const decorators = [ - (Story) => { - return - }, -] diff --git a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx index c04cdd98f66..f6423222942 100644 --- a/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx +++ b/apps/studio/components/interfaces/App/FeaturePreview/FeaturePreviewModal.tsx @@ -4,7 +4,7 @@ import { useEffect, useState } from 'react' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useFlag } from 'hooks/ui/useFlag' -import { TELEMETRY_EVENTS } from 'lib/constants/telemetry' +import { TelemetryActions } from 'lib/constants/telemetry' import { useAppStateSnapshot } from 'state/app-state' import { Badge, Button, Modal, ScrollArea, cn } from 'ui' import { FEATURE_PREVIEWS, useFeaturePreviewContext } from './FeaturePreviewContext' @@ -37,9 +37,10 @@ const FeaturePreviewModal = () => { const toggleFeature = () => { onUpdateFlag(selectedFeatureKey, !isSelectedFeatureEnabled) sendEvent({ - action: TELEMETRY_EVENTS.FEATURE_PREVIEWS, - label: selectedFeatureKey, - value: isSelectedFeatureEnabled ? 'disabled' : 'enabled', + action: isSelectedFeatureEnabled + ? TelemetryActions.FEATURE_PREVIEW_DISABLED + : TelemetryActions.FEATURE_PREVIEW_ENABLED, + properties: { feature: selectedFeatureKey }, }) } diff --git a/apps/studio/components/interfaces/Auth/EmailRateLimitsAlert/EmailRateLimitsAlert.tsx b/apps/studio/components/interfaces/Auth/EmailRateLimitsAlert/EmailRateLimitsAlert.tsx index 843ce6f9e74..4eb4b01e371 100644 --- a/apps/studio/components/interfaces/Auth/EmailRateLimitsAlert/EmailRateLimitsAlert.tsx +++ b/apps/studio/components/interfaces/Auth/EmailRateLimitsAlert/EmailRateLimitsAlert.tsx @@ -1,11 +1,16 @@ import Link from 'next/link' import { useParams } from 'common' -import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, WarningIcon } from 'ui' +import { + AlertDescription_Shadcn_, + AlertTitle_Shadcn_, + Alert_Shadcn_, + Button, + WarningIcon, +} from 'ui' export function EmailRateLimitsAlert() { - const { ref: projectRef } = useParams() - const after20240926 = Date.now() >= new Date('20240926T00:00:00Z').getTime() + const { ref } = useParams() return ( @@ -14,30 +19,21 @@ export function EmailRateLimitsAlert() { You're using the built-in email service. The service has rate limits and it's not meant to be used for production apps. Check the{' '} + {/* [Refactor] Swap for InlineLink component once https://github.com/supabase/supabase/pull/30494 is in */} documentation {' '} - for an up-to-date information on the current rate limits.{' '} - - Set up a custom SMTP server now. - - - - {after20240926 - ? 'To fight abuse, Auth email messages are restricted ' - : 'On 26th September: To fight abuse, Auth email messages will be restricted '} - to your project's organization members. For example, if your organization has 3 members with - addresses: person-a@example.com, person-b@example.com and{' '} - person-c@example.com, messages will be{' '} - sent to those addresses only. Set up custom SMTP to send to any user. + for an up-to-date information on the current rate limits. + ) diff --git a/apps/studio/components/interfaces/Auth/EmailTemplates/EmailTemplates.tsx b/apps/studio/components/interfaces/Auth/EmailTemplates/EmailTemplates.tsx index 9bced7ff316..80d6ba89f5d 100644 --- a/apps/studio/components/interfaces/Auth/EmailTemplates/EmailTemplates.tsx +++ b/apps/studio/components/interfaces/Auth/EmailTemplates/EmailTemplates.tsx @@ -5,7 +5,7 @@ import { FormHeader } from 'components/ui/Forms/FormHeader' import { FormPanel } from 'components/ui/Forms/FormPanel' import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' import { useAuthConfigQuery } from 'data/auth/auth-config-query' -import { Tabs, Tabs_Shadcn_, TabsContent_Shadcn_, TabsList_Shadcn_, TabsTrigger_Shadcn_ } from 'ui' +import { Tabs_Shadcn_, TabsContent_Shadcn_, TabsList_Shadcn_, TabsTrigger_Shadcn_ } from 'ui' import { TEMPLATES_SCHEMAS } from '../AuthTemplatesValidation' import EmailRateLimitsAlert from '../EmailRateLimitsAlert' import TemplateEditor from './TemplateEditor' @@ -39,7 +39,11 @@ const EmailTemplates = () => { {isError && ( )} - {isLoading && } + {isLoading && ( +
+ +
+ )} {isSuccess && ( diff --git a/apps/studio/components/interfaces/Auth/Users/UserDropdown.tsx b/apps/studio/components/interfaces/Auth/Users/UserDropdown.tsx index 35fb49e6832..b0c912a41a6 100644 --- a/apps/studio/components/interfaces/Auth/Users/UserDropdown.tsx +++ b/apps/studio/components/interfaces/Auth/Users/UserDropdown.tsx @@ -98,7 +98,8 @@ const UserDropdown = ({ async function handleDelete() { await timeout(200) if (!ref) return console.error('Project ref is required') - deleteUser({ projectRef: ref, user }) + if (!user.id) return console.error('User id is required') + deleteUser({ projectRef: ref, userId: user.id }) } async function handleDeleteFactors() { diff --git a/apps/studio/components/interfaces/Auth/Users/UserOverview.tsx b/apps/studio/components/interfaces/Auth/Users/UserOverview.tsx index 886792b3204..958d95fe0c3 100644 --- a/apps/studio/components/interfaces/Auth/Users/UserOverview.tsx +++ b/apps/studio/components/interfaces/Auth/Users/UserOverview.tsx @@ -132,7 +132,10 @@ export const UserOverview = ({ user, onDeleteSuccess }: UserOverviewProps) => { const handleDelete = async () => { await timeout(200) if (!projectRef) return console.error('Project ref is required') - deleteUser({ projectRef, user }) + if (user.id === undefined) { + return toast.error(`Failed to delete user: User ID not found`) + } + deleteUser({ projectRef, userId: user.id }) } const handleDeleteFactors = async () => { @@ -142,7 +145,7 @@ export const UserOverview = ({ user, onDeleteSuccess }: UserOverviewProps) => { } const handleUnban = () => { - if (projectRef === undefined) return console.error('Proejct ref is required') + if (projectRef === undefined) return console.error('Project ref is required') if (user.id === undefined) { return toast.error(`Failed to ban user: User ID not found`) } diff --git a/apps/studio/components/interfaces/Auth/Users/UsersV2.tsx b/apps/studio/components/interfaces/Auth/Users/UsersV2.tsx index 73d5dabc787..a2351632139 100644 --- a/apps/studio/components/interfaces/Auth/Users/UsersV2.tsx +++ b/apps/studio/components/interfaces/Auth/Users/UsersV2.tsx @@ -205,7 +205,7 @@ export const UsersV2 = () => { onKeyDown={(e) => { if (e.code === 'Enter') { setSearch(search.trim()) - setFilterKeywords(search.trim()) + setFilterKeywords(search.trim().toLocaleLowerCase()) } }} actions={[ diff --git a/apps/studio/components/interfaces/Connect/ConnectionParameters.tsx b/apps/studio/components/interfaces/Connect/ConnectionParameters.tsx index 6a5e4ba1687..3bb5f9edf62 100644 --- a/apps/studio/components/interfaces/Connect/ConnectionParameters.tsx +++ b/apps/studio/components/interfaces/Connect/ConnectionParameters.tsx @@ -14,7 +14,6 @@ import { interface Parameter { key: string value: string - description?: string } interface ConnectionParametersProps { diff --git a/apps/studio/components/interfaces/Connect/DatabaseConnectionString.tsx b/apps/studio/components/interfaces/Connect/DatabaseConnectionString.tsx index cd489d0dc0b..910bb0b68fb 100644 --- a/apps/studio/components/interfaces/Connect/DatabaseConnectionString.tsx +++ b/apps/studio/components/interfaces/Connect/DatabaseConnectionString.tsx @@ -10,6 +10,7 @@ import { usePoolingConfigurationQuery } from 'data/database/pooling-configuratio import { useReadReplicasQuery } from 'data/read-replicas/replicas-query' import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' +import { TelemetryActions } from 'lib/constants/telemetry' import { pluckObjectFields } from 'lib/helpers' import { useDatabaseSelectorStateSnapshot } from 'state/database-selector' import { @@ -33,7 +34,7 @@ import { DatabaseConnectionType, } from './Connect.constants' import { CodeBlockFileHeader, ConnectionPanel } from './ConnectionPanel' -import { getConnectionStrings, getPoolerTld } from './DatabaseSettings.utils' +import { getConnectionStrings } from './DatabaseSettings.utils' import examples, { Example } from './DirectConnectionExamples' const StepLabel = ({ @@ -93,11 +94,10 @@ export const DatabaseConnectionString = () => { const connectionInfo = pluckObjectFields(selectedDatabase || emptyState, DB_FIELDS) const handleCopy = (id: string) => { - const labelValue = DATABASE_CONNECTION_TYPES.find((type) => type.id === id)?.label + const connectionType = DATABASE_CONNECTION_TYPES.find((type) => type.id === id)?.label sendEvent({ - category: 'settings', - action: 'copy_connection_string', - label: labelValue ?? '', + action: TelemetryActions.CONNECTION_STRING_COPIED, + properties: { connectionType }, }) } @@ -131,11 +131,6 @@ export const DatabaseConnectionString = () => { }, } - const poolerTld = - isSuccessPoolingInfo && poolingConfiguration !== undefined - ? getPoolerTld(poolingConfiguration?.connectionString) - : 'com' - // @mildtomato - Possible reintroduce later // // const poolerConnStringSyntax = @@ -306,22 +301,14 @@ export const DatabaseConnectionString = () => { }} notice={['Does not support PREPARE statements']} parameters={[ - { - ...CONNECTION_PARAMETERS.host, - value: `${projectRef}.pooler.supabase.${poolerTld}`, - }, + { ...CONNECTION_PARAMETERS.host, value: poolingConfiguration?.db_host ?? '' }, { ...CONNECTION_PARAMETERS.port, value: poolingConfiguration?.db_port.toString() ?? '6543', - description: 'Port number for transaction pooler', - }, - { ...CONNECTION_PARAMETERS.database, value: connectionInfo.db_name }, - { ...CONNECTION_PARAMETERS.user, value: connectionInfo.db_user }, - { - ...CONNECTION_PARAMETERS.pool_mode, - value: 'transaction', - description: 'Each transaction uses a different connection', }, + { ...CONNECTION_PARAMETERS.database, value: poolingConfiguration?.db_name ?? '' }, + { ...CONNECTION_PARAMETERS.user, value: poolingConfiguration?.db_user ?? '' }, + { ...CONNECTION_PARAMETERS.pool_mode, value: 'transaction' }, ]} onCopyCallback={() => handleCopy(selectedTab)} /> @@ -351,22 +338,11 @@ export const DatabaseConnectionString = () => { description: 'Session pooler connections are IPv4 proxied for free.', }} parameters={[ - { - ...CONNECTION_PARAMETERS.host, - value: `${projectRef}.pooler.supabase.${poolerTld}`, - }, - { - ...CONNECTION_PARAMETERS.port, - value: '5432', - description: 'Port number for session pooler', - }, - { ...CONNECTION_PARAMETERS.database, value: connectionInfo.db_name }, - { ...CONNECTION_PARAMETERS.user, value: connectionInfo.db_user }, - { - ...CONNECTION_PARAMETERS.pool_mode, - value: 'session', - description: 'Connection is reserved for the entire session', - }, + { ...CONNECTION_PARAMETERS.host, value: poolingConfiguration?.db_host ?? '' }, + { ...CONNECTION_PARAMETERS.port, value: '5432' }, + { ...CONNECTION_PARAMETERS.database, value: poolingConfiguration?.db_name ?? '' }, + { ...CONNECTION_PARAMETERS.user, value: poolingConfiguration?.db_user ?? '' }, + { ...CONNECTION_PARAMETERS.pool_mode, value: 'session' }, ]} onCopyCallback={() => handleCopy(selectedTab)} /> diff --git a/apps/studio/components/interfaces/Connect/DatabaseSettings.utils.ts b/apps/studio/components/interfaces/Connect/DatabaseSettings.utils.ts index 398789c05d7..23ee85486d5 100644 --- a/apps/studio/components/interfaces/Connect/DatabaseSettings.utils.ts +++ b/apps/studio/components/interfaces/Connect/DatabaseSettings.utils.ts @@ -72,7 +72,7 @@ export const getConnectionStrings = ( // Pooler connection strings const poolerPsqlString = isMd5 ? `psql "postgresql://${poolerUser}:${password}@${poolerHost}:${poolerPort}/${poolerName}?options=reference%3D${projectRef}"` - : `psql -h ${poolerHost} -p ${poolerPort} -d ${poolerName} -U ${poolerUser}.${projectRef}` + : `psql -h ${poolerHost} -p ${poolerPort} -d ${poolerName} -U ${poolerUser}` const poolerUriString = poolingInfo.connectionString @@ -376,13 +376,3 @@ export const constructConnStringSyntax = ( return [] } - -export const getPoolerTld = (connString: string) => { - try { - const segment = connString.split('pooler.supabase.')[1] - const tld = segment.split(':6543')[0] - return tld - } catch { - return 'com' - } -} diff --git a/apps/studio/components/interfaces/HomePageActions.tsx b/apps/studio/components/interfaces/HomePageActions.tsx index 30896319192..2bda86c5e22 100644 --- a/apps/studio/components/interfaces/HomePageActions.tsx +++ b/apps/studio/components/interfaces/HomePageActions.tsx @@ -43,7 +43,7 @@ const HomePageActions = ({ const { isSuccess: orgsLoaded } = useOrganizationsQuery() return ( -
+
)} -
+
} - className="w-64 [&>div>div>div>input]:!pl-7 [&>div>div>div>div]:!pl-2" + className="w-full flex-1 md:w-64 [&>div>div>div>input]:!pl-7 [&>div>div>div>div]:!pl-2" value={search} onChange={(event) => setSearch(event.target.value)} /> diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/CreateCronJobSheet.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/CreateCronJobSheet.tsx index 1088e079d95..3d90ab978d5 100644 --- a/apps/studio/components/interfaces/Integrations/CronJobs/CreateCronJobSheet.tsx +++ b/apps/studio/components/interfaces/Integrations/CronJobs/CreateCronJobSheet.tsx @@ -15,7 +15,7 @@ import { CronJob, useCronJobsQuery } from 'data/database-cron-jobs/database-cron import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' -import { TELEMETRY_EVENTS, TELEMETRY_VALUES } from 'lib/constants/telemetry' +import { TelemetryActions } from 'lib/constants/telemetry' import { Button, Form_Shadcn_, @@ -264,16 +264,13 @@ export const CreateCronJobSheet = ({ toast.success(`Successfully created cron job ${name}`) } - // We should allow sending custom properties with events sendEvent({ - action: TELEMETRY_EVENTS.CRON_JOBS, - value: isEditing - ? TELEMETRY_VALUES.CRON_JOB_UPDATED - : TELEMETRY_VALUES.CRON_JOB_CREATED, - label: isEditing ? 'Cron job was updated' : 'Cron job was created', + action: isEditing + ? TelemetryActions.CRON_JOB_UPDATED + : TelemetryActions.CRON_JOB_CREATED, properties: { - cron_job_type: values.type, - cron_job_schedule: schedule, + type: values.type, + schedule: schedule, }, }) diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/CronJobCard.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/CronJobCard.tsx index d4acb0c9c80..b5f316f2b65 100644 --- a/apps/studio/components/interfaces/Integrations/CronJobs/CronJobCard.tsx +++ b/apps/studio/components/interfaces/Integrations/CronJobs/CronJobCard.tsx @@ -9,7 +9,7 @@ import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectConte import { CronJob } from 'data/database-cron-jobs/database-cron-jobs-query' import { useDatabaseCronJobToggleMutation } from 'data/database-cron-jobs/database-cron-jobs-toggle-mutation' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' -import { TELEMETRY_EVENTS, TELEMETRY_VALUES } from 'lib/constants/telemetry' +import { TelemetryActions } from 'lib/constants/telemetry' import { Button, DropdownMenu, @@ -78,11 +78,7 @@ export const CronJobCard = ({ job, onEditCronJob, onDeleteCronJob }: CronJobCard type="default" icon={} onClick={() => { - sendEvent({ - action: TELEMETRY_EVENTS.CRON_JOBS, - value: TELEMETRY_VALUES.CRON_JOBS_VIEW_PREVIOUS_RUNS, - label: 'User clicked view previous runs button', - }) + sendEvent({ action: TelemetryActions.CRON_JOBS_VIEW_PREVIOUS_RUNS_CLICKED }) }} > History @@ -94,11 +90,7 @@ export const CronJobCard = ({ job, onEditCronJob, onDeleteCronJob }: CronJobCard { - sendEvent({ - action: TELEMETRY_EVENTS.CRON_JOBS, - value: TELEMETRY_VALUES.CRON_JOB_UPDATE_CLICKED, - label: 'User clicked edit cron job button', - }) + sendEvent({ action: TelemetryActions.CRON_JOB_UPDATE_CLICKED }) onEditCronJob(job) }} > @@ -107,11 +99,7 @@ export const CronJobCard = ({ job, onEditCronJob, onDeleteCronJob }: CronJobCard { - sendEvent({ - action: TELEMETRY_EVENTS.CRON_JOBS, - value: TELEMETRY_VALUES.CRON_JOB_DELETE_CLICKED, - label: 'User clicked delete cron job button', - }) + sendEvent({ action: TelemetryActions.CRON_JOB_DELETE_CLICKED }) onDeleteCronJob(job) }} > diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/CronJobsTab.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/CronJobsTab.tsx index 510c8542644..96952fd468e 100644 --- a/apps/studio/components/interfaces/Integrations/CronJobs/CronJobsTab.tsx +++ b/apps/studio/components/interfaces/Integrations/CronJobs/CronJobsTab.tsx @@ -8,7 +8,7 @@ import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' import { CronJob, useCronJobsQuery } from 'data/database-cron-jobs/database-cron-jobs-query' import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' -import { TELEMETRY_EVENTS, TELEMETRY_VALUES } from 'lib/constants/telemetry' +import { TelemetryActions } from 'lib/constants/telemetry' import { Button, Input, Sheet, SheetContent } from 'ui' import { CronJobCard } from './CronJobCard' import { DeleteCronJob } from './DeleteCronJob' @@ -63,11 +63,7 @@ export const CronjobsTab = () => { const filteredCronJobs = (cronJobs ?? []).filter((cj) => cj?.jobname?.includes(searchQuery || '')) const onOpenCreateJobSheet = () => { - sendEvent({ - action: TELEMETRY_EVENTS.CRON_JOBS, - value: TELEMETRY_VALUES.CRON_JOB_CREATE_CLICKED, - label: 'User clicked create job', - }) + sendEvent({ action: TelemetryActions.CRON_JOB_CREATE_CLICKED }) setCreateCronJobSheetShown(true) } diff --git a/apps/studio/components/interfaces/Integrations/CronJobs/DeleteCronJob.tsx b/apps/studio/components/interfaces/Integrations/CronJobs/DeleteCronJob.tsx index 63702a500da..41e333b7dae 100644 --- a/apps/studio/components/interfaces/Integrations/CronJobs/DeleteCronJob.tsx +++ b/apps/studio/components/interfaces/Integrations/CronJobs/DeleteCronJob.tsx @@ -3,9 +3,9 @@ import { toast } from 'sonner' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { useDatabaseCronJobDeleteMutation } from 'data/database-cron-jobs/database-cron-jobs-delete-mutation' import { CronJob } from 'data/database-cron-jobs/database-cron-jobs-query' -import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' -import { TELEMETRY_EVENTS, TELEMETRY_VALUES } from 'lib/constants/telemetry' +import { TelemetryActions } from 'lib/constants/telemetry' +import TextConfirmModal from 'ui-patterns/Dialogs/TextConfirmModal' interface DeleteCronJobProps { cronJob: CronJob @@ -19,11 +19,7 @@ export const DeleteCronJob = ({ cronJob, visible, onClose }: DeleteCronJobProps) const { mutate: sendEvent } = useSendEventMutation() const { mutate: deleteDatabaseCronJob, isLoading } = useDatabaseCronJobDeleteMutation({ onSuccess: () => { - sendEvent({ - action: TELEMETRY_EVENTS.CRON_JOBS, - value: TELEMETRY_VALUES.CRON_JOB_DELETED, - label: 'User deleted cron job', - }) + sendEvent({ action: TelemetryActions.CRON_JOB_DELETED }) toast.success(`Successfully removed cron job ${cronJob.jobname}`) onClose() }, diff --git a/apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover/index.tsx b/apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover/index.tsx index 463f700c670..fd6b45f9ca6 100644 --- a/apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover/index.tsx +++ b/apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover/index.tsx @@ -21,6 +21,7 @@ import { } from 'ui' import { RealtimeConfig } from '../useRealtimeMessages' import { DocsButton } from 'components/ui/DocsButton' +import { TelemetryActions } from 'lib/constants/telemetry' interface ChooseChannelPopoverProps { config: RealtimeConfig @@ -51,11 +52,7 @@ export const ChooseChannelPopover = ({ config, onChangeConfig }: ChooseChannelPo const onSubmit = () => { setOpen(false) - sendEvent({ - category: 'realtime_inspector', - action: 'started_listening_to_channel_in_input_channel_popover', - label: 'realtime_inspector_config', - }) + sendEvent({ action: TelemetryActions.REALTIME_INSPECTOR_LISTEN_CHANNEL_CLICKED }) onChangeConfig({ ...config, channelName: form.getValues('channel'), diff --git a/apps/studio/components/interfaces/Realtime/Inspector/Header.tsx b/apps/studio/components/interfaces/Realtime/Inspector/Header.tsx index e953b6b310e..637bca2ce23 100644 --- a/apps/studio/components/interfaces/Realtime/Inspector/Header.tsx +++ b/apps/studio/components/interfaces/Realtime/Inspector/Header.tsx @@ -3,6 +3,7 @@ import { PlayCircle, StopCircle } from 'lucide-react' import { Dispatch, SetStateAction } from 'react' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' +import { TelemetryActions } from 'lib/constants/telemetry' import { Button } from 'ui' import { ChooseChannelPopover } from './ChooseChannelPopover' import { RealtimeFilterPopover } from './RealtimeFilterPopover' @@ -34,11 +35,7 @@ export const Header = ({ config, onChangeConfig }: HeaderProps) => { onChangeConfig({ ...config, enabled: !config.enabled }) if (!config.enabled) { // the user has clicked to start listening - sendEvent({ - category: 'realtime_inspector', - action: 'started_listening', - label: 'realtime_inspector_config', - }) + sendEvent({ action: TelemetryActions.REALTIME_INSPECTOR_LISTEN_CHANNEL_CLICKED }) } }} > diff --git a/apps/studio/components/interfaces/Realtime/Inspector/MessageSelection.tsx b/apps/studio/components/interfaces/Realtime/Inspector/MessageSelection.tsx index facba5b2015..12b94305a53 100644 --- a/apps/studio/components/interfaces/Realtime/Inspector/MessageSelection.tsx +++ b/apps/studio/components/interfaces/Realtime/Inspector/MessageSelection.tsx @@ -6,6 +6,7 @@ import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { Button, cn } from 'ui' import type { LogData } from './Messages.types' import { SelectedRealtimeMessagePanel } from './SelectedRealtimeMessagePanel' +import { TelemetryActions } from 'lib/constants/telemetry' export interface MessageSelectionProps { log: LogData | null @@ -73,11 +74,7 @@ const MessageSelection = ({ log, onClose }: MessageSelectionProps) => { type="default" title="Copy log to clipboard" onClick={() => { - sendEvent({ - category: 'realtime_inspector', - action: 'copied_message', - label: 'realtime_inspector_results', - }) + sendEvent({ action: TelemetryActions.REALTIME_INSPECTOR_COPY_MESSAGE_CLICKED }) }} />
diff --git a/apps/studio/components/interfaces/Realtime/Inspector/MessagesTable.tsx b/apps/studio/components/interfaces/Realtime/Inspector/MessagesTable.tsx index b70c6d08831..bf2d3378d90 100644 --- a/apps/studio/components/interfaces/Realtime/Inspector/MessagesTable.tsx +++ b/apps/studio/components/interfaces/Realtime/Inspector/MessagesTable.tsx @@ -13,6 +13,7 @@ import type { LogData } from './Messages.types' import NoChannelEmptyState from './NoChannelEmptyState' import { ColumnRenderer } from './RealtimeMessageColumnRenderer' import { DocsButton } from 'components/ui/DocsButton' +import { TelemetryActions } from 'lib/constants/telemetry' export const isErrorLog = (l: LogData) => { return l.message === 'SYSTEM' && l.metadata?.status === 'error' @@ -177,11 +178,7 @@ const MessagesTable = ({ isRowSelected={false} selectedCellIdx={undefined} onClick={() => { - sendEvent({ - category: 'realtime_inspector', - action: 'focused-specific-message', - label: 'realtime_inspector_results', - }) + sendEvent({ action: TelemetryActions.REALTIME_INSPECTOR_MESSAGE_CLICKED }) setFocusedLog(row) }} /> diff --git a/apps/studio/components/interfaces/Realtime/Inspector/RealtimeFilterPopover/index.tsx b/apps/studio/components/interfaces/Realtime/Inspector/RealtimeFilterPopover/index.tsx index d8b7e5e90ef..d78a39de764 100644 --- a/apps/studio/components/interfaces/Realtime/Inspector/RealtimeFilterPopover/index.tsx +++ b/apps/studio/components/interfaces/Realtime/Inspector/RealtimeFilterPopover/index.tsx @@ -20,6 +20,7 @@ import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import { RealtimeConfig } from '../useRealtimeMessages' import { FilterSchema } from './FilterSchema' import { FilterTable } from './FilterTable' +import { TelemetryActions } from 'lib/constants/telemetry' interface RealtimeFilterPopoverProps { config: RealtimeConfig @@ -200,11 +201,7 @@ export const RealtimeFilterPopover = ({ config, onChangeConfig }: RealtimeFilter visible={applyConfigOpen} onCancel={() => setApplyConfigOpen(false)} onConfirm={() => { - sendEvent({ - category: 'realtime_inspector', - action: 'applied_filters', - label: 'realtime_inspector_config', - }) + sendEvent({ action: TelemetryActions.REALTIME_INSPECTOR_FILTERS_APPLIED }) onChangeConfig(tempConfig) setApplyConfigOpen(false) setOpen(false) diff --git a/apps/studio/components/interfaces/Realtime/Inspector/RealtimeTokensPopover/index.tsx b/apps/studio/components/interfaces/Realtime/Inspector/RealtimeTokensPopover/index.tsx index fa395823fd3..637a00827ef 100644 --- a/apps/studio/components/interfaces/Realtime/Inspector/RealtimeTokensPopover/index.tsx +++ b/apps/studio/components/interfaces/Realtime/Inspector/RealtimeTokensPopover/index.tsx @@ -6,6 +6,7 @@ import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-co import { getAPIKeys, useProjectSettingsV2Query } from 'data/config/project-settings-v2-query' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { IS_PLATFORM } from 'lib/constants' +import { TelemetryActions } from 'lib/constants/telemetry' import { getRoleImpersonationJWT } from 'lib/role-impersonation' import { useRoleImpersonationStateSnapshot } from 'state/role-impersonation-state' import { RealtimeConfig } from '../useRealtimeMessages' @@ -34,13 +35,10 @@ export const RealtimeTokensPopover = ({ config, onChangeConfig }: RealtimeTokens useEffect(() => { if (isMounted.current) { - sendEvent({ - category: 'realtime_inspector', - action: 'changed_database_role', - label: 'realtime_inspector_config', - }) + sendEvent({ action: TelemetryActions.REALTIME_INSPECTOR_DATABASE_ROLE_UPDATED }) } isMounted.current = true + // eslint-disable-next-line react-hooks/exhaustive-deps }, [snap.role]) useEffect(() => { @@ -67,6 +65,7 @@ export const RealtimeTokensPopover = ({ config, onChangeConfig }: RealtimeTokens } triggerUpdateTokenBearer() + // eslint-disable-next-line react-hooks/exhaustive-deps }, [snap.role, anonKey, serviceKey]) return diff --git a/apps/studio/components/interfaces/Realtime/Inspector/index.tsx b/apps/studio/components/interfaces/Realtime/Inspector/index.tsx index 5e4eec941ae..ef289600bcf 100644 --- a/apps/studio/components/interfaces/Realtime/Inspector/index.tsx +++ b/apps/studio/components/interfaces/Realtime/Inspector/index.tsx @@ -2,6 +2,7 @@ import { useParams } from 'common' import { useState } from 'react' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' +import { TelemetryActions } from 'lib/constants/telemetry' import { Header } from './Header' import MessagesTable from './MessagesTable' import { SendMessageModal } from './SendMessageModal' @@ -50,11 +51,7 @@ export const RealtimeInspector = () => { visible={sendMessageShown} onSelectCancel={() => setSendMessageShown(false)} onSelectConfirm={(v) => { - sendEvent({ - category: 'realtime_inspector', - action: 'send_broadcast_message', - label: 'realtime_inspector_results', - }) + sendEvent({ action: TelemetryActions.REALTIME_INSPECTOR_BROADCAST_SENT }) sendMessage(v.message, v.payload, () => setSendMessageShown(false)) }} /> diff --git a/apps/studio/components/interfaces/RoleImpersonationSelector/UserImpersonationSelector.tsx b/apps/studio/components/interfaces/RoleImpersonationSelector/UserImpersonationSelector.tsx index 7672529eb08..e834e1422e5 100644 --- a/apps/studio/components/interfaces/RoleImpersonationSelector/UserImpersonationSelector.tsx +++ b/apps/studio/components/interfaces/RoleImpersonationSelector/UserImpersonationSelector.tsx @@ -32,7 +32,7 @@ const UserImpersonationSelector = () => { { projectRef: project?.ref, connectionString: project?.connectionString, - keywords: debouncedSearchText, + keywords: debouncedSearchText.trim().toLocaleLowerCase(), }, { keepPreviousData: true, @@ -95,7 +95,7 @@ const UserImpersonationSelector = () => { ) } placeholder="Search for a user.." - onChange={(e) => setSearchText(e.target.value.trim())} + onChange={(e) => setSearchText(e.target.value)} value={searchText} size="small" actions={ diff --git a/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx b/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx index fd6408d24b0..3fda4b48718 100644 --- a/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx +++ b/apps/studio/components/interfaces/SQLEditor/SQLEditor.tsx @@ -26,6 +26,7 @@ import { useSchemasForAi } from 'hooks/misc/useSchemasForAi' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { useSelectedProject } from 'hooks/misc/useSelectedProject' import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' +import { TelemetryActions } from 'lib/constants/telemetry' import { uuidv4 } from 'lib/helpers' import { useProfile } from 'lib/profile' import { wrapWithRoleImpersonation } from 'lib/role-impersonation' @@ -450,11 +451,7 @@ export const SQLEditor = () => { } } - sendEvent({ - category: 'sql_editor', - action: 'ai_suggestion_accepted', - label: 'edit_snippet', - }) + sendEvent({ action: TelemetryActions.ASSISTANT_SUGGESTION_ACCEPTED }) setSelectedDiffType(DiffType.Modification) setSourceSqlDiff(undefined) @@ -474,12 +471,7 @@ export const SQLEditor = () => { ]) const discardAiHandler = useCallback(() => { - sendEvent({ - category: 'sql_editor', - action: 'ai_suggestion_rejected', - label: 'edit_snippet', - }) - + sendEvent({ action: TelemetryActions.ASSISTANT_SUGGESTION_REJECTED }) setSourceSqlDiff(undefined) setPendingTitle(undefined) }, [router]) diff --git a/apps/studio/components/interfaces/SQLEditor/SQLTemplates/SQLQuickstarts.tsx b/apps/studio/components/interfaces/SQLEditor/SQLTemplates/SQLQuickstarts.tsx index f0fc3ed86ef..1ee8e1d403f 100644 --- a/apps/studio/components/interfaces/SQLEditor/SQLTemplates/SQLQuickstarts.tsx +++ b/apps/studio/components/interfaces/SQLEditor/SQLTemplates/SQLQuickstarts.tsx @@ -8,6 +8,7 @@ import { SQL_TEMPLATES } from 'components/interfaces/SQLEditor/SQLEditor.queries import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useSelectedProject } from 'hooks/misc/useSelectedProject' +import { TelemetryActions } from 'lib/constants/telemetry' import { uuidv4 } from 'lib/helpers' import { useProfile } from 'lib/profile' import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2' @@ -56,7 +57,7 @@ const SQLQuickstarts = () => { } return ( -
+

Quickstarts

@@ -76,9 +77,8 @@ const SQLQuickstarts = () => { onClick={(sql, title) => { handleNewQuery(sql, title) sendEvent({ - category: 'quickstart', - action: 'quickstart_clicked', - label: x.title, + action: TelemetryActions.SQL_EDITOR_QUICKSTART_CLICKED, + properties: { title }, }) }} /> diff --git a/apps/studio/components/interfaces/SQLEditor/SQLTemplates/SQLTemplates.tsx b/apps/studio/components/interfaces/SQLEditor/SQLTemplates/SQLTemplates.tsx index fd3c18223f6..93931d586ed 100644 --- a/apps/studio/components/interfaces/SQLEditor/SQLTemplates/SQLTemplates.tsx +++ b/apps/studio/components/interfaces/SQLEditor/SQLTemplates/SQLTemplates.tsx @@ -13,6 +13,7 @@ import { useProfile } from 'lib/profile' import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2' import { createSqlSnippetSkeletonV2 } from '../SQLEditor.utils' import SQLCard from './SQLCard' +import { TelemetryActions } from 'lib/constants/telemetry' const SQLTemplates = () => { const router = useRouter() @@ -56,7 +57,7 @@ const SQLTemplates = () => { } return ( -
+

Scripts

@@ -76,9 +77,8 @@ const SQLTemplates = () => { onClick={(sql, title) => { handleNewQuery(sql, title) sendEvent({ - category: 'scripts', - action: 'script_clicked', - label: x.title, + action: TelemetryActions.SQL_EDITOR_TEMPLATE_CLICKED, + properties: { title }, }) }} /> diff --git a/apps/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx b/apps/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx index 0e08655db60..3a81ef9f474 100644 --- a/apps/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx +++ b/apps/studio/components/interfaces/SQLEditor/UtilityPanel/ResultsDropdown.tsx @@ -16,6 +16,7 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from 'ui' +import { TelemetryActions } from 'lib/constants/telemetry' export type ResultsDropdownProps = { id: string @@ -66,7 +67,7 @@ const ResultsDropdown = ({ id }: ResultsDropdownProps) => { function onDownloadCSV() { csvRef.current?.link.click() - sendEvent({ category: 'sql_editor', action: 'sql_download_csv', label: '' }) + sendEvent({ action: TelemetryActions.SQL_EDITOR_RESULT_DOWNLOAD_CSV_CLICKED }) } function onCopyAsMarkdown() { @@ -86,7 +87,7 @@ const ResultsDropdown = ({ id }: ResultsDropdownProps) => { copyToClipboard(markdownData, () => { toast.success('Copied results to clipboard') - sendEvent({ category: 'sql_editor', action: 'sql_copy_as_markdown', label: '' }) + sendEvent({ action: TelemetryActions.SQL_EDITOR_RESULT_COPY_MARKDOWN_CLICKED }) }) } } @@ -99,7 +100,7 @@ const ResultsDropdown = ({ id }: ResultsDropdownProps) => { copyToClipboard(JSON.stringify(result.rows, null, 2), () => { toast.success('Copied results to clipboard') - sendEvent({ category: 'sql_editor', action: 'sql_copy_as_json', label: '' }) + sendEvent({ action: TelemetryActions.SQL_EDITOR_RESULT_COPY_JSON_CLICKED }) }) } } diff --git a/apps/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseConnectionString.tsx b/apps/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseConnectionString.tsx index 14793602a19..d980728ce25 100644 --- a/apps/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseConnectionString.tsx +++ b/apps/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseConnectionString.tsx @@ -40,6 +40,7 @@ import { getConnectionStrings, getPoolerTld, } from './DatabaseSettings.utils' +import { TelemetryActions } from 'lib/constants/telemetry' const CONNECTION_TYPES = [ { id: 'uri', label: 'URI' }, @@ -104,12 +105,8 @@ export const DatabaseConnectionString = ({ appearance }: DatabaseConnectionStrin : 'co' const handleCopy = (id: string) => { - const labelValue = CONNECTION_TYPES.find((type) => type.id === id)?.label - sendEvent({ - category: 'settings', - action: 'copy_connection_string', - label: labelValue ? labelValue : '', - }) + const connectionType = CONNECTION_TYPES.find((type) => type.id === id)?.label + sendEvent({ action: TelemetryActions.CONNECTION_STRING_COPIED, properties: { connectionType } }) } const connectionStrings = diff --git a/apps/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseSettings.tsx b/apps/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseSettings.tsx index 9fb82b60a3f..8af5db0eb03 100644 --- a/apps/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseSettings.tsx +++ b/apps/studio/components/interfaces/Settings/Database/DatabaseSettings/DatabaseSettings.tsx @@ -20,8 +20,10 @@ import { IPv4DeprecationNotice, } from '../DatabaseConnectionNotices' import { UsePoolerCheckbox } from '../UsePoolerCheckbox' -import ResetDbPassword from './ResetDbPassword' +/**O + * @deprecated Will be removed once `connectDialogUpdate` flag is persisted + */ const DatabaseSettings = () => { const { ref: projectRef, connectionString } = useParams() const snap = useDatabaseSettingsStateSnapshot() @@ -81,13 +83,6 @@ const DatabaseSettings = () => { } : dbConnectionInfo - const handleCopy = (labelValue?: string) => - sendEvent({ - category: 'settings', - action: 'copy_connection_string', - label: labelValue ? labelValue : '', - }) - useEffect(() => { if (connectionString !== undefined && connectionStringsRef.current !== undefined) { state.setSelectedDatabaseId(connectionString) @@ -166,9 +161,6 @@ const DatabaseSettings = () => { disabled value={connectionInfo.db_host} label="Host" - onCopy={() => { - handleCopy('Host') - }} /> { + const { ref } = useParams() + const { project } = useProjectContext() + const [isSensitive, setIsSensitive] = useState(false) + + const canUpdateComplianceConfig = useCheckPermissions(PermissionAction.UPDATE, 'projects', { + resource: { project_id: project?.id }, + }) + + const { + data: settings, + error, + isError, + isLoading, + isSuccess, + } = useProjectSettingsV2Query({ projectRef: ref }) + const initialIsSensitive = settings?.is_sensitive || false + + const { mutate: updateComplianceConfig, isLoading: isSubmitting } = + useComplianceConfigUpdateMutation({ + onSuccess: () => { + toast.success('Successfully updated project compliance configuration') + }, + onError: (error) => { + setIsSensitive(initialIsSensitive) + toast.error(`Failed to update project compliance configuration: ${error.message}`) + }, + }) + + const toggleIsSensitive = async () => { + if (!ref) return console.error('Project ref is required') + setIsSensitive(!isSensitive) + updateComplianceConfig({ projectRef: ref, isSensitive: !isSensitive }) + } + + useEffect(() => { + if (!isLoading) setIsSensitive(initialIsSensitive) + }, [isLoading]) + + return ( +
+
+ + +
+ + + Enable security warnings in the{' '} + + Security Advisor + {' '} + to enforce requirements for managing sensitive data +

+ } + > + Apply additional compliance controls to project + + } + > + +
+ {(isLoading || isSubmitting) && ( + + )} + {isError && ( + + )} + {isSuccess && ( + + + {/* [Joshen] Added div as tooltip is messing with data state property of toggle */} +
+ +
+
+ {!canUpdateComplianceConfig && ( + + You need additional permissions to update the compliance configuration for + your project + + )} +
+ )} +
+
+
+
+
+ ) +} + +export default ComplianceConfig diff --git a/apps/studio/components/interfaces/Settings/General/TransferProjectPanel/TransferProjectButton.tsx b/apps/studio/components/interfaces/Settings/General/TransferProjectPanel/TransferProjectButton.tsx index 2e2d539eaa6..436949a037f 100644 --- a/apps/studio/components/interfaces/Settings/General/TransferProjectPanel/TransferProjectButton.tsx +++ b/apps/studio/components/interfaces/Settings/General/TransferProjectPanel/TransferProjectButton.tsx @@ -145,7 +145,7 @@ const TransferProjectButton = () => {
  • - +

    Features

    diff --git a/apps/studio/components/interfaces/Settings/General/index.ts b/apps/studio/components/interfaces/Settings/General/index.ts index 904c124b6fd..bfc7871528a 100644 --- a/apps/studio/components/interfaces/Settings/General/index.ts +++ b/apps/studio/components/interfaces/Settings/General/index.ts @@ -4,3 +4,4 @@ export { default as DeleteProjectPanel } from './DeleteProjectPanel/DeleteProjec export { default as DeleteProjectButton } from './DeleteProjectPanel/DeleteProjectButton' export { default as TransferProjectButton } from './TransferProjectPanel/TransferProjectButton' export { default as TransferProjectPanel } from './TransferProjectPanel/TransferProjectPanel' +export { default as ComplianceConfig } from './ComplianceConfig/ProjectComplianceMode' diff --git a/apps/studio/components/interfaces/Settings/Logs/LogTable.stories.tsx b/apps/studio/components/interfaces/Settings/Logs/LogTable.stories.tsx deleted file mode 100644 index 2fb584d1289..00000000000 --- a/apps/studio/components/interfaces/Settings/Logs/LogTable.stories.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react' - -import LogTable from './LogTable' - -export default { - title: 'Logs', -} - -export const Functions = (args: any) => ( -
    - -
    -) diff --git a/apps/studio/components/interfaces/Settings/Logs/LogsErrorRenderers/LogsErrorRenderers.stories.tsx b/apps/studio/components/interfaces/Settings/Logs/LogsErrorRenderers/LogsErrorRenderers.stories.tsx deleted file mode 100644 index 5676760b144..00000000000 --- a/apps/studio/components/interfaces/Settings/Logs/LogsErrorRenderers/LogsErrorRenderers.stories.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useState } from 'react' - -import { DefaultErrorRenderer } from './DefaultErrorRenderer' -import ResourcesExceededErrorRenderer from './ResourcesExceededErrorRenderer' -import { Alert } from 'ui' - -export default { - title: 'Logs', -} - -export const ErrorRenderers = () => ( -
    - {[ - , - , - , - - , - ].map((child, i) => ( - - {child} - - ))} -
    -) diff --git a/apps/studio/components/interfaces/SignIn/SignInMfaForm.tsx b/apps/studio/components/interfaces/SignIn/SignInMfaForm.tsx index 1371592b395..8bba8a7456b 100644 --- a/apps/studio/components/interfaces/SignIn/SignInMfaForm.tsx +++ b/apps/studio/components/interfaces/SignIn/SignInMfaForm.tsx @@ -10,7 +10,6 @@ import AlertError from 'components/ui/AlertError' import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' import { useMfaChallengeAndVerifyMutation } from 'data/profile/mfa-challenge-and-verify-mutation' import { useMfaListFactorsQuery } from 'data/profile/mfa-list-factors-query' -import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useSignOut } from 'lib/auth' import { getReturnToPath } from 'lib/gotrue' import { Button, Form, Input } from 'ui' @@ -25,8 +24,6 @@ const SignInMfaForm = () => { const queryClient = useQueryClient() const [selectedFactor, setSelectedFactor] = useState(null) - const { mutate: sendEvent } = useSendEventMutation() - const { data: factors, error: factorsError, @@ -40,7 +37,6 @@ const SignInMfaForm = () => { isSuccess, } = useMfaChallengeAndVerifyMutation({ onSuccess: async () => { - sendEvent({ category: 'account', action: 'sign_in', label: '' }) await queryClient.resetQueries() router.push(getReturnToPath()) }, diff --git a/apps/studio/components/interfaces/Support/SupportFormV2.tsx b/apps/studio/components/interfaces/Support/SupportFormV2.tsx index 26de59605a7..90f1b94e127 100644 --- a/apps/studio/components/interfaces/Support/SupportFormV2.tsx +++ b/apps/studio/components/interfaces/Support/SupportFormV2.tsx @@ -1,7 +1,7 @@ import { zodResolver } from '@hookform/resolvers/zod' import * as Sentry from '@sentry/nextjs' import { useSupabaseClient } from '@supabase/auth-helpers-react' -import { ChevronDown, ExternalLink, Loader2, Mail, Plus, X } from 'lucide-react' +import { ChevronRight, ExternalLink, Loader2, Mail, Plus, X } from 'lucide-react' import Link from 'next/link' import { ChangeEvent, useEffect, useRef, useState } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' @@ -21,7 +21,6 @@ import { useProfile } from 'lib/profile' import { Badge, Button, - Checkbox_Shadcn_, cn, Collapsible_Shadcn_, CollapsibleContent_Shadcn_, @@ -38,6 +37,7 @@ import { SelectTrigger_Shadcn_, SelectValue_Shadcn_, Separator, + Switch, TextArea_Shadcn_, } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' @@ -642,60 +642,6 @@ export const SupportFormV2 = ({ setSentCategory, setSelectedProject }: SupportFo )} /> - {['Problem', 'Database_unresponsive', 'Performance'].includes(category) && ( - ( - - - - More information about temporary access - - - - By checking this box, you grant permission for our support team to access - your project temporarily and, if applicable, to use AI tools to assist in - diagnosing and resolving issues. This access may involve analyzing database - configurations, query performance, and other relevant data to expedite - troubleshooting and enhance support accuracy. We are committed to - maintaining strict data privacy and security standards in all support - activities.{' '} - - Privacy Policy - - - - - } - > - - field.onChange(value)} - /> - - - )} - /> - )} -

    Attachments

    @@ -746,26 +692,85 @@ export const SupportFormV2 = ({ setSentCategory, setSelectedProject }: SupportFo
    -
    -
    -

    We will contact you at

    -

    {respondToEmail}

    -
    -
    -

    - Please ensure you haven't blocked Hubspot in your emails -

    -
    -
    - + + + {['Problem', 'Database_unresponsive', 'Performance'].includes(category) && ( + <> + ( + +
    + + +

    + More information about temporary access +

    +
    +
    + +

    + By enabling this, you grant permission for our support team to access your + project temporarily and, if applicable, to use AI tools to assist in + diagnosing and resolving issues. This access may involve analyzing + database configurations, query performance, and other relevant data to + expedite troubleshooting and enhance support accuracy. We are committed to + maintaining strict data privacy and security standards in all support + activities.{' '} + + Privacy Policy + +

    +
    + + } + > + +
    + )} + /> + + )} + +
    + +
    +
    + We will contact you at + {respondToEmail} +
    + + Please ensure emails from supabase.io are allowed +
    diff --git a/apps/studio/components/layouts/AccountLayout/WithSidebar.tsx b/apps/studio/components/layouts/AccountLayout/WithSidebar.tsx index b217aeae79c..4e0c983b360 100644 --- a/apps/studio/components/layouts/AccountLayout/WithSidebar.tsx +++ b/apps/studio/components/layouts/AccountLayout/WithSidebar.tsx @@ -1,11 +1,12 @@ import { isUndefined } from 'lodash' import { ArrowUpRight, LogOut } from 'lucide-react' import Link from 'next/link' -import { ReactNode } from 'react' +import { PropsWithChildren, ReactNode, useState } from 'react' -import { Badge, cn, Menu } from 'ui' +import { Badge, cn, Menu, Sheet, SheetContent } from 'ui' import { LayoutHeader } from '../ProjectLayout/LayoutHeader' import type { SidebarLink, SidebarSection } from './AccountLayout.types' +import MobileSheetNav from 'ui-patterns/MobileSheetNav/MobileSheetNav' interface WithSidebarProps { title: string @@ -16,7 +17,6 @@ interface WithSidebarProps { subitemsParentKey?: number hideSidebar?: boolean customSidebarContent?: ReactNode - children: ReactNode } const WithSidebar = ({ @@ -29,63 +29,107 @@ const WithSidebar = ({ subitemsParentKey, hideSidebar = false, customSidebarContent, -}: WithSidebarProps) => { +}: PropsWithChildren) => { const noContent = !sections && !customSidebarContent + const [isSheetOpen, setIsSheetOpen] = useState(false) + + const handleMobileMenu = () => { + setIsSheetOpen(true) + } return ( -
    +
    {!hideSidebar && !noContent && ( -
    - {title && ( -
    -
    -

    - {title} -

    -
    -
    - )} - {header && header} -
    - - {customSidebarContent} - {sections.map((section) => { - return Boolean(section.heading) ? ( - - ) : ( -
    - -
    - ) - })} -
    -
    -
    + )}
    - +
    {children}
    + + +
    ) } export default WithSidebar +export const SidebarContent = ({ + title, + header, + sections, + subitems, + subitemsParentKey, + customSidebarContent, + className, +}: PropsWithChildren> & { className?: string }) => { + return ( + <> +
    + {title && ( +
    +
    +

    + {title} +

    +
    +
    + )} + {header && header} +
    + + {customSidebarContent} + {sections.map((section) => { + return Boolean(section.heading) ? ( + + ) : ( +
    + +
    + ) + })} +
    +
    +
    + + ) +} + interface SectionWithHeadersProps { section: SidebarSection subitems?: any[] diff --git a/apps/studio/components/layouts/OrganizationLayout.tsx b/apps/studio/components/layouts/OrganizationLayout.tsx index 55d32e15e56..802b1235097 100644 --- a/apps/studio/components/layouts/OrganizationLayout.tsx +++ b/apps/studio/components/layouts/OrganizationLayout.tsx @@ -77,7 +77,10 @@ const OrganizationLayout = ({ children }: PropsWithChildren<{}>) => { {selectedOrganization?.name ?? 'Organization'} settings - + {filteredNavMenuItems.map((item) => ( {item.label} diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx index 7f3c4f568cb..8a37500aa87 100644 --- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx +++ b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx @@ -1,5 +1,5 @@ import Link from 'next/link' -import { useMemo } from 'react' +import { ReactNode, useMemo } from 'react' import Connect from 'components/interfaces/Connect/Connect' import { useParams } from 'common' @@ -39,7 +39,22 @@ const LayoutHeaderDivider = () => ( ) -const LayoutHeader = ({ customHeaderComponents, breadcrumbs = [], headerBorder = true }: any) => { +interface LayoutHeaderProps { + customHeaderComponents?: ReactNode + breadcrumbs?: any[] + headerBorder?: boolean + showProductMenu?: boolean + customSidebarContent?: ReactNode + handleMobileMenu: Function +} + +const LayoutHeader = ({ + customHeaderComponents, + breadcrumbs = [], + headerBorder = true, + showProductMenu, + handleMobileMenu, +}: LayoutHeaderProps) => { const { ref: projectRef } = useParams() const selectedProject = useSelectedProject() const selectedOrganization = useSelectedOrganization() @@ -72,51 +87,69 @@ const LayoutHeader = ({ customHeaderComponents, breadcrumbs = [], headerBorder = headerBorder ? 'border-b border-default' : '' )} > -
    -
    - {projectRef && ( - <> -
    - - - - - {exceedingLimits && ( -
    - - Exceeding usage limits - -
    - )} - - {selectedProject && isBranchingEnabled && ( - <> - - - - )} -
    - -
    - {connectDialogUpdate && } - {!isBranchingEnabled && } -
    - - )} - - {/* Additional breadcrumbs are supplied */} - + {showProductMenu && ( +
    +
    -
    - {customHeaderComponents && customHeaderComponents} - {IS_PLATFORM && ( - <> - - - - - )} + )} +
    +
    +
    + {projectRef && ( + <> +
    + + + + + {exceedingLimits && ( +
    + + Exceeding usage limits + +
    + )} + + {selectedProject && isBranchingEnabled && ( + <> + + + + )} +
    + +
    + {connectDialogUpdate && } + {!isBranchingEnabled && } +
    + + )} + + {/* Additional breadcrumbs are supplied */} + +
    +
    + {customHeaderComponents && customHeaderComponents} + {IS_PLATFORM && ( + <> + + + + + )} +
    +
    +
    {!!projectRef && (
    diff --git a/apps/studio/components/layouts/ProjectLayout/NavigationBar/MobileNavigationBar.tsx b/apps/studio/components/layouts/ProjectLayout/NavigationBar/MobileNavigationBar.tsx new file mode 100644 index 00000000000..efd1014ddae --- /dev/null +++ b/apps/studio/components/layouts/ProjectLayout/NavigationBar/MobileNavigationBar.tsx @@ -0,0 +1,90 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' +import { Search, Menu } from 'lucide-react' + +import { useParams } from 'common' +import { IS_PLATFORM } from 'lib/constants' +import { useAppStateSnapshot } from 'state/app-state' + +import { buttonVariants, cn, Sheet, SheetContent } from 'ui' +import { CommandMenuTrigger } from 'ui-patterns' +import { NavContent } from './NavigationBar' +import { useState } from 'react' +import MobileSheetNav from 'ui-patterns/MobileSheetNav/MobileSheetNav' + +export const ICON_SIZE = 20 +export const ICON_STROKE_WIDTH = 1.5 + +const MobileNavigationBar = () => { + const router = useRouter() + const [isSheetOpen, setIsSheetOpen] = useState(false) + const { ref: projectRef } = useParams() + const snap = useAppStateSnapshot() + + const onCloseNavigationIconLink = (event: any) => { + snap.setNavigationPanelOpen( + false, + event.target.id === 'icon-link' || ['svg', 'path'].includes(event.target.localName) + ) + } + + return ( +
    + + + + +
    + ) +} + +export default MobileNavigationBar diff --git a/apps/studio/components/layouts/ProjectLayout/NavigationBar/MobileViewNav.tsx b/apps/studio/components/layouts/ProjectLayout/NavigationBar/MobileViewNav.tsx new file mode 100644 index 00000000000..8cf6394923e --- /dev/null +++ b/apps/studio/components/layouts/ProjectLayout/NavigationBar/MobileViewNav.tsx @@ -0,0 +1,39 @@ +import { PropsWithChildren } from 'react' +import { cn } from 'ui' + +export const ICON_SIZE = 20 +export const ICON_STROKE_WIDTH = 1.5 + +interface Props { + title?: string + handleMobileMenu: Function +} + +const MobileViewNav = ({ title, handleMobileMenu }: PropsWithChildren) => { + return ( + + ) +} + +export default MobileViewNav diff --git a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx index 8d45873c7a9..b7a426be08d 100644 --- a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx +++ b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx @@ -15,7 +15,6 @@ import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { useFlag } from 'hooks/ui/useFlag' import { useSignOut } from 'lib/auth' import { IS_PLATFORM, LOCAL_STORAGE_KEYS } from 'lib/constants' -import { detectOS } from 'lib/helpers' import { useProfile } from 'lib/profile' import { useAppStateSnapshot } from 'state/app-state' import { @@ -57,7 +56,37 @@ export const ICON_SIZE = 20 export const ICON_STROKE_WIDTH = 1.5 const NavigationBar = () => { - const os = detectOS() + const snap = useAppStateSnapshot() + + const [userDropdownOpen, setUserDropdownOpenState] = useState(false) + + const [allowNavPanelToExpand] = useLocalStorageQuery( + LOCAL_STORAGE_KEYS.EXPAND_NAVIGATION_PANEL, + true + ) + + return ( +
    + +
    + ) +} + +export const NavContent = () => { const router = useRouter() const { profile } = useProfile() const { project } = useProjectContext() @@ -169,9 +198,9 @@ const NavigationBar = () => { @@ -196,247 +225,229 @@ const NavigationBar = () => { ) return ( -
    -
    + ) + } else if (route.key === 'logs') { + // TODO: Undo this when warehouse flag is removed + const label = showWarehouse ? 'Logs & Analytics' : route.label + const newRoute = { ...route, label } + return ( + + ) + } else { + return ( + + ) + } + })} + -
      - {settingsRoutes.map((route) => ( - - ))} +
        + {settingsRoutes.map((route) => ( + + ))} - {IS_PLATFORM && ( - <> - {!allowNavPanelToExpand && ( - - {CommandButton} - - Commands - - - )} - {allowNavPanelToExpand && CommandButton} - - )} - - { - setUserDropdownOpenState(open) - if (open === false) snap.setNavigationPanelOpen(false) - }} - > - {allowNavPanelToExpand ? ( - {UserAccountButton} - ) : ( + {IS_PLATFORM && ( + <> + {!allowNavPanelToExpand && ( - - {UserAccountButton} - + {CommandButton} - Account settings + Commands )} + {allowNavPanelToExpand && CommandButton} + + )} - - {IS_PLATFORM && ( - <> -
        - {profile && ( - <> + { + setUserDropdownOpenState(open) + if (open === false) snap.setNavigationPanelOpen(false) + }} + > + {allowNavPanelToExpand ? ( + {UserAccountButton} + ) : ( + + + {UserAccountButton} + + + Account settings + + + )} + + + {IS_PLATFORM && ( + <> +
        + {profile && ( + <> + + {profile.username} + + {profile.primary_email !== profile.username && ( - {profile.username} + {profile.primary_email} - {profile.primary_email !== profile.username && ( - - {profile.primary_email} - - )} - - )} -
        + )} + + )} +
        + + + + + + Account preferences + + + snap.setShowFeaturePreviewModal(true)} + onSelect={() => snap.setShowFeaturePreviewModal(true)} + > + + Feature previews + - - - - - Account preferences - - - snap.setShowFeaturePreviewModal(true)} - onSelect={() => snap.setShowFeaturePreviewModal(true)} - > - - Feature previews - - - - - )} - - Theme - { - setTheme(value) - }} - > - {singleThemes.map((theme: Theme) => ( - - {theme.name} - - ))} - - - {IS_PLATFORM && ( - <> - - - { - await signOut() - await router.push('/sign-in') - }} - > - Log out - - - - )} -
        -
        -
      - -
    + + + )} + + Theme + { + setTheme(value) + }} + > + {singleThemes.map((theme: Theme) => ( + + {theme.name} + + ))} + + + {IS_PLATFORM && ( + <> + + + { + await signOut() + await router.push('/sign-in') + }} + > + Log out + + + + )} + + + + ) } diff --git a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconButton.tsx b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconButton.tsx index eac06187e07..f974fedd9ca 100644 --- a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconButton.tsx +++ b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconButton.tsx @@ -26,8 +26,8 @@ export const NavigationIconButton = forwardRef<
    {icon}
    {rightText} diff --git a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconLink.tsx b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconLink.tsx index d8ff0949a26..10b58a17030 100644 --- a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconLink.tsx +++ b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationIconLink.tsx @@ -36,7 +36,7 @@ const NavigationIconLink = forwardRef) => { return (
    { const router = useRouter() + const [isSheetOpen, setIsSheetOpen] = useState(false) const { ref: projectRef } = useParams() const selectedOrganization = useSelectedOrganization() const selectedProject = useSelectedProject() @@ -115,7 +127,7 @@ const ProjectLayout = forwardRef { const handler = (e: KeyboardEvent) => { - if (e.metaKey && e.code === 'KeyI' && !e.altKey && !e.shiftKey) { + if (e.metaKey && e.key === 'i' && !e.altKey && !e.shiftKey) { setAiAssistantPanel({ open: !open }) e.preventDefault() e.stopPropagation() @@ -126,6 +138,10 @@ const ProjectLayout = forwardRef { + setIsSheetOpen(true) + } + return ( @@ -143,21 +159,32 @@ const ProjectLayout = forwardRef -
    +
    {/* Left-most navigation side bar to access products */} {!hideIconBar && } + {/* Top Nav to access products from mobile */} + {!hideIconBar && } + {showProductMenu && productMenu && !(!hideHeader && IS_PLATFORM) && ( + + )} + {/* Product menu bar */} + {/* Existing desktop menu */} - {!hideHeader && IS_PLATFORM && } + {!hideHeader && IS_PLATFORM && ( + + )} - +
    + + {productMenu} + ) } diff --git a/apps/studio/components/layouts/Scaffold.tsx b/apps/studio/components/layouts/Scaffold.tsx index 2520c7b123c..4c8aa10cb7f 100644 --- a/apps/studio/components/layouts/Scaffold.tsx +++ b/apps/studio/components/layouts/Scaffold.tsx @@ -3,7 +3,7 @@ import { useAppStateSnapshot } from 'state/app-state' import { cn } from 'ui' export const MAX_WIDTH_CLASSES = 'mx-auto w-full max-w-[1200px]' -export const PADDING_CLASSES = 'px-6 lg:px-14 xl:px-24 2xl:px-32' +export const PADDING_CLASSES = 'px-4 md:px-6 lg:px-14 xl:px-24 2xl:px-32' export const MAX_WIDTH_CLASSES_COLUMN = 'min-w-[420px]' const ScaffoldHeader = React.forwardRef>( diff --git a/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx b/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx index 57b63d892f8..23ad874970b 100644 --- a/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx +++ b/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx @@ -1,6 +1,7 @@ import * as Tooltip from '@radix-ui/react-tooltip' import saveAs from 'file-saver' import { + Clipboard, Copy, Download, Edit, @@ -47,6 +48,7 @@ import { DropdownMenuTrigger, } from 'ui' import { useProjectContext } from '../ProjectLayout/ProjectContext' +import { copyToClipboard } from 'lib/helpers' export interface EntityListItemProps { id: number @@ -65,6 +67,7 @@ const EntityListItem: ItemRenderer = ({ const { selectedSchema } = useQuerySchemaState() const isActive = Number(id) === entity.id + const canEdit = isActive && !isLocked const { data: lints = [] } = useProjectLintsQuery({ projectRef: project?.ref, @@ -213,23 +216,21 @@ const EntityListItem: ItemRenderer = ({ switch (entity.type) { case ENTITY_TYPE.TABLE: if (tableHasLints) { - tooltipContent = 'RLS Disabled' + tooltipContent = 'RLS disabled' } break case ENTITY_TYPE.VIEW: if (viewHasLints) { - tooltipContent = 'Security Definer view' + tooltipContent = 'Security definer view' } break case ENTITY_TYPE.MATERIALIZED_VIEW: if (materializedViewHasLints) { - tooltipContent = 'Security Definer view' + tooltipContent = 'Security definer view' } - break case ENTITY_TYPE.FOREIGN_TABLE: tooltipContent = 'RLS is not enforced on foreign tables' - break default: break @@ -355,85 +356,103 @@ const EntityListItem: ItemRenderer = ({
    - {entity.type === ENTITY_TYPE.TABLE && isActive && !isLocked && ( + {canEdit && ( { e.stopPropagation() - snap.onEditTable() + copyToClipboard(entity.name) }} > - - Edit Table - - { - e.stopPropagation() - snap.onDuplicateTable() - }} - > - - Duplicate Table - - - - - View Policies - + + Copy name - - - - Export Data - - + {entity.type === ENTITY_TYPE.TABLE && ( + <> + + { e.stopPropagation() - exportTableAsCSV() + snap.onEditTable() }} > - Export table as CSV + + Edit table { + e.stopPropagation() + snap.onDuplicateTable() + }} + > + + Duplicate table + + + + + View policies + + + + + + + Export data + + + { + e.stopPropagation() + exportTableAsCSV() + }} + > + Export table as CSV + + { + e.stopPropagation() + exportTableAsSQL() + }} + > + Export table as SQL + + + + + + { e.stopPropagation() - exportTableAsSQL() + snap.onDeleteTable() }} > - Export table as SQL + + Delete table - - - - - { - e.stopPropagation() - snap.onDeleteTable() - }} - > - - Delete Table - + + )} )} diff --git a/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx b/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx index cbbf0cdad3e..c233915a21e 100644 --- a/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx +++ b/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx @@ -39,12 +39,14 @@ import { } from 'ui-patterns/InnerSideMenu' import { useProjectContext } from '../ProjectLayout/ProjectContext' import EntityListItem from './EntityListItem' +import { useBreakpoint } from 'common/hooks/useBreakpoint' const TableEditorMenu = () => { const { id: _id } = useParams() const id = _id ? Number(_id) : undefined const snap = useTableEditorStateSnapshot() const { selectedSchema, setSelectedSchema } = useQuerySchemaState() + const isMobile = useBreakpoint() const [showModal, setShowModal] = useState(false) const [searchText, setSearchText] = useState('') @@ -169,7 +171,7 @@ const TableEditorMenu = () => {
    { @@ -201,7 +203,7 @@ const TableEditorMenu = () => { + + )} @@ -567,6 +542,7 @@ export const AIAssistant = ({ .join('\n') || '' const valueWithSnippets = [value, sqlSnippetsString].filter(Boolean).join('\n\n') sendMessageToAssistant(valueWithSnippets) + scrollToEnd() } else { sendMessageToAssistant(value) } diff --git a/apps/studio/components/ui/AIAssistantPanel/Message.tsx b/apps/studio/components/ui/AIAssistantPanel/Message.tsx index bcd29442f04..9cada1b99f5 100644 --- a/apps/studio/components/ui/AIAssistantPanel/Message.tsx +++ b/apps/studio/components/ui/AIAssistantPanel/Message.tsx @@ -107,6 +107,18 @@ export const Message = function Message({ code: (props: any) => { return {props.children} }, + a: (props: any) => { + return ( + + {props.children} + + ) + }, }} > {content} diff --git a/apps/studio/components/ui/AIAssistantPanel/MessagePre.tsx b/apps/studio/components/ui/AIAssistantPanel/MessagePre.tsx deleted file mode 100644 index bd392a584b6..00000000000 --- a/apps/studio/components/ui/AIAssistantPanel/MessagePre.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { InsertCode } from 'icons' -import { Check, Copy, Edit } from 'lucide-react' -import { useEffect, useState } from 'react' -import { format } from 'sql-formatter' -import { - Button, - CodeBlock, - TooltipContent_Shadcn_, - TooltipTrigger_Shadcn_, - Tooltip_Shadcn_, - cn, -} from 'ui' - -import { useSendEventMutation } from 'data/telemetry/send-event-mutation' -import { DiffType } from 'components/interfaces/SQLEditor/SQLEditor.types' - -interface MessagePreProps { - onDiff: (type: DiffType, s: string) => void - children: string[] - className?: string -} - -export const MessagePre = ({ onDiff, children, className }: MessagePreProps) => { - const [copied, setCopied] = useState(false) - - const { mutate: sendEvent } = useSendEventMutation() - - useEffect(() => { - if (!copied) return - const timer = setTimeout(() => setCopied(false), 2000) - return () => clearTimeout(timer) - }, [copied]) - - let formatted = (children || [''])[0] - try { - formatted = format(formatted, { language: 'postgresql', keywordCase: 'upper' }) - } catch {} - - if (formatted.length === 0) { - return null - } - - function handleCopy(formatted: string) { - navigator.clipboard.writeText(formatted).then() - setCopied(true) - } - - return ( -
    -      code]:m-0 [&>code>span]:flex [&>code>span]:flex-wrap [&>code]:block'
    -        )}
    -        hideCopy
    -        hideLineNumbers
    -      />
    -      
    - - - - - - Edit - - - - - - - - - Copy code - - -
    -
    - ) -} diff --git a/apps/studio/components/ui/AIAssistantPanel/SqlSnippet.tsx b/apps/studio/components/ui/AIAssistantPanel/SqlSnippet.tsx index ff9f68ca873..47b3d10a056 100644 --- a/apps/studio/components/ui/AIAssistantPanel/SqlSnippet.tsx +++ b/apps/studio/components/ui/AIAssistantPanel/SqlSnippet.tsx @@ -3,12 +3,17 @@ import { useRouter } from 'next/router' import { useCallback, useEffect, useState } from 'react' import { Bar, BarChart, CartesianGrid, XAxis } from 'recharts' +import { useQueryClient } from '@tanstack/react-query' +import { useParams } from 'common' +import { Markdown } from 'components/interfaces/Markdown' import useNewQuery from 'components/interfaces/SQLEditor/hooks' import { DiffType } from 'components/interfaces/SQLEditor/SQLEditor.types' import { suffixWithLimit } from 'components/interfaces/SQLEditor/SQLEditor.utils' import Results from 'components/interfaces/SQLEditor/UtilityPanel/Results' import { QueryResponseError, useExecuteSqlMutation } from 'data/sql/execute-sql-mutation' +import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useSelectedProject } from 'hooks/misc/useSelectedProject' +import { TelemetryActions } from 'lib/constants/telemetry' import { useAppStateSnapshot } from 'state/app-state' import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2' import { @@ -32,11 +37,6 @@ import { identifyQueryType, isReadOnlySelect, } from './AIAssistant.utils' -import { useParams } from 'common' -import { useQueryClient } from '@tanstack/react-query' -import { Markdown } from 'components/interfaces/Markdown' -import { useSendEventMutation } from 'data/telemetry/send-event-mutation' -import { TELEMETRY_EVENTS, TELEMETRY_VALUES } from 'lib/constants/telemetry' interface SqlSnippetWrapperProps { sql: string @@ -183,6 +183,9 @@ export const SqlCard = ({ : 'This query involves running a function.'}{' '} Are you sure you want to execute it?

    +

    + Make sure you are not accidentally removing something important. +

    )} - {isDismissable && ( - - )}
    ) } - -export default ResourceExhaustionWarningBanner diff --git a/apps/studio/data/auth/user-delete-mutation.ts b/apps/studio/data/auth/user-delete-mutation.ts index 9fe1975b892..5148f59147d 100644 --- a/apps/studio/data/auth/user-delete-mutation.ts +++ b/apps/studio/data/auth/user-delete-mutation.ts @@ -4,17 +4,15 @@ import { toast } from 'sonner' import { del, handleError } from 'data/fetchers' import type { ResponseError } from 'types' import { authKeys } from './keys' -import type { User } from './users-infinite-query' export type UserDeleteVariables = { projectRef: string - user: User + userId: string } -export async function deleteUser({ projectRef, user }: UserDeleteVariables) { - const { data, error } = await del('/platform/auth/{ref}/users', { - params: { path: { ref: projectRef } }, - body: user, +export async function deleteUser({ projectRef, userId }: UserDeleteVariables) { + const { data, error } = await del('/platform/auth/{ref}/users/{id}', { + params: { path: { ref: projectRef, id: userId } }, }) if (error) handleError(error) return data diff --git a/apps/studio/data/auth/users-infinite-query.ts b/apps/studio/data/auth/users-infinite-query.ts index 01f817eea57..dfba22c23a4 100644 --- a/apps/studio/data/auth/users-infinite-query.ts +++ b/apps/studio/data/auth/users-infinite-query.ts @@ -45,7 +45,7 @@ export const getUsersSQL = ({ if (hasValidKeywords) { conditions.push( - `id::text ilike '%${keywords}%' or email ilike '%${keywords}%' or phone ilike '%${keywords}%'` + `id::text like '%${keywords}%' or email like '%${keywords}%' or phone like '%${keywords}%'` ) } diff --git a/apps/studio/data/config/project-compliance-config-mutation.ts b/apps/studio/data/config/project-compliance-config-mutation.ts new file mode 100644 index 00000000000..89317a31cf6 --- /dev/null +++ b/apps/studio/data/config/project-compliance-config-mutation.ts @@ -0,0 +1,58 @@ +import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' +import { toast } from 'sonner' + +import { handleError, patch } from 'data/fetchers' +import type { ResponseError } from 'types' +import { configKeys } from './keys' + +export type ComplianceConfigUpdateVariables = { + projectRef: string + isSensitive: boolean +} + +export async function updateComplianceConfig({ + projectRef, + isSensitive, +}: ComplianceConfigUpdateVariables) { + if (!projectRef) throw new Error('projectRef is required') + + const { data, error } = await patch('/platform/projects/{ref}/settings/sensitivity', { + params: { path: { ref: projectRef } }, + body: { is_sensitive: isSensitive }, + }) + + if (error) handleError(error) + return data +} + +type ComplianceConfigUpdateData = Awaited> + +export const useComplianceConfigUpdateMutation = ({ + onSuccess, + onError, + ...options +}: Omit< + UseMutationOptions, + 'mutationFn' +> = {}) => { + const queryClient = useQueryClient() + + return useMutation( + (vars) => updateComplianceConfig(vars), + { + async onSuccess(data, variables, context) { + const { projectRef } = variables + await queryClient.invalidateQueries(configKeys.settingsV2(projectRef)) + await onSuccess?.(data, variables, context) + }, + async onError(data, variables, context) { + if (onError === undefined) { + toast.error(`Failed to update project compliance configuration: ${data.message}`) + } else { + onError(data, variables, context) + } + }, + ...options, + } + ) +} diff --git a/apps/studio/data/telemetry/send-event-mutation.ts b/apps/studio/data/telemetry/send-event-mutation.ts index 36afd7e929c..4b91d4a0e02 100644 --- a/apps/studio/data/telemetry/send-event-mutation.ts +++ b/apps/studio/data/telemetry/send-event-mutation.ts @@ -4,25 +4,16 @@ import { components } from 'api-types' import { isBrowser, LOCAL_STORAGE_KEYS } from 'common' import { handleError, post } from 'data/fetchers' import { IS_PLATFORM } from 'lib/constants' +import { TelemetryActions } from 'lib/constants/telemetry' import { useRouter } from 'next/router' import type { ResponseError } from 'types' -type SendEvent = components['schemas']['TelemetryEventBodyV2'] - export type SendEventVariables = { - /** Defines the name of the event, refer to TELEMETRY_EVENTS in lib/constants */ - action: string - /** These are all under the event's properties (customizable on the FE) */ - /** value: refer to TELEMETRY_VALUES in lib/constants */ - value?: string - /** label: secondary tag to the event for further identification */ - label?: string - /** To deprecate - seems unnecessary */ - category?: string - properties?: Record + action: TelemetryActions + properties?: Record // Is arbitrary, but always aim to be self-explanatory with custom properties } -type SendEventPayload = any +type SendEventPayload = components['schemas']['TelemetryEventBodyV2'] export async function sendEvent({ body }: { body: SendEventPayload }) { const consent = @@ -34,6 +25,7 @@ export async function sendEvent({ body }: { body: SendEventPayload }) { const headers = { Version: '2' } const { data, error } = await post(`/platform/telemetry/event`, { body, headers }) + if (error) handleError(error) return data } @@ -55,9 +47,9 @@ export const useSendEventMutation = ({ return useMutation( (vars) => { - const { action, ...otherVars } = vars + const { action, properties } = vars - const body: SendEvent = { + const body: SendEventPayload = { action, page_url: window.location.href, page_title: title, @@ -70,8 +62,7 @@ export const useSendEventMutation = ({ viewport_height: isBrowser ? window.innerHeight : 0, viewport_width: isBrowser ? window.innerWidth : 0, }, - // @ts-expect-error - API is returning a wrong type - custom_properties: otherVars, + custom_properties: (properties || {}) as any, } return sendEvent({ body }) diff --git a/apps/studio/lib/constants/index.ts b/apps/studio/lib/constants/index.ts index 874cda2f4b2..b43f1305c8f 100644 --- a/apps/studio/lib/constants/index.ts +++ b/apps/studio/lib/constants/index.ts @@ -73,8 +73,6 @@ export const LOCAL_STORAGE_KEYS = { AUTH_SMTP_CHANGES_WARNING: 'auth-smtp-changes-warning-dismissed', AUTH_USERS_COLUMNS_CONFIGURATION: (ref: string) => `supabase-auth-users-columns-${ref}`, - AUTH_EMAIL_WARNING_BANNER_ACKNOWLEDGE: (ref: string) => - `supabase-auth-email-warning-banner-${ref}`, } export const OPT_IN_TAGS = { diff --git a/apps/studio/lib/constants/telemetry.ts b/apps/studio/lib/constants/telemetry.ts index 2030c342b8d..81ad6fc5257 100644 --- a/apps/studio/lib/constants/telemetry.ts +++ b/apps/studio/lib/constants/telemetry.ts @@ -1,3 +1,43 @@ +// Standardization as per document: https://www.notion.so/supabase/Event-tracking-standardization-1195004b775f80f98ee3fa9e70cf4d05 + +export enum TelemetryActions { + SIGN_UP = 'sign_up', + SIGN_IN = 'sign_in', + + ASSISTANT_PROMPT_SUBMITTED = 'assistant_prompt_submitted', + ASSISTANT_DEBUG_SUBMITTED = 'assistant_debug_submitted', + ASSISTANT_SUGGESTION_RAN = 'assistant_suggestion_ran', + ASSISTANT_SUGGESTION_ACCEPTED = 'assistant_suggestion_accepted', + ASSISTANT_SUGGESTION_REJECTED = 'assistant_suggestion_rejected', + ASSISTANT_SUGGESTION_COPIED = 'assistant_suggestion_copied', + ASSISTANT_EDIT_SQL_CLICKED = 'assistant_edit_sql_clicked', + + CONNECTION_STRING_COPIED = 'connection_string_copied', + CRON_JOB_CREATED = 'cron_job_created', + CRON_JOB_UPDATED = 'cron_job_updated', + CRON_JOB_DELETED = 'cron_job_deleted', + CRON_JOB_DELETE_CLICKED = 'cron_job_delete_clicked', + CRON_JOB_UPDATE_CLICKED = 'cron_job_update_clicked', + CRON_JOB_CREATE_CLICKED = 'cron_job_create_clicked', + CRON_JOBS_VIEW_PREVIOUS_RUNS_CLICKED = 'cron_job_view_previous_runs_clicked', + + FEATURE_PREVIEW_ENABLED = 'feature_preview_enabled', + FEATURE_PREVIEW_DISABLED = 'feature_preview_disabled', + + REALTIME_INSPECTOR_LISTEN_CHANNEL_CLICKED = 'realtime_inspector_listen_channel_clicked', + REALTIME_INSPECTOR_BROADCAST_SENT = 'realtime_inspector_broadcast_sent', + REALTIME_INSPECTOR_MESSAGE_CLICKED = 'realtime_inspector_message_clicked', + REALTIME_INSPECTOR_COPY_MESSAGE_CLICKED = 'realtime_inspector_copy_message_clicked', + REALTIME_INSPECTOR_FILTERS_APPLIED = 'realtime_inspector_filters_applied', + REALTIME_INSPECTOR_DATABASE_ROLE_UPDATED = 'realtime_inspector_database_role_updated', + + SQL_EDITOR_QUICKSTART_CLICKED = 'sql_editor_quickstart_clicked', + SQL_EDITOR_TEMPLATE_CLICKED = 'sql_editor_template_clicked', + SQL_EDITOR_RESULT_DOWNLOAD_CSV_CLICKED = 'sql_editor_result_download_csv_clicked', + SQL_EDITOR_RESULT_COPY_MARKDOWN_CLICKED = 'sql_editor_result_copy_markdown_clicked', + SQL_EDITOR_RESULT_COPY_JSON_CLICKED = 'sql_editor_result_copy_markdown_clicked', +} + // [Joshen] Just adding these to start consolidating our telemetry configs // may change depending on how we choose to standardize across all apps // Events define the name of the event and it'll be used as the primary identification diff --git a/apps/studio/lib/profile.tsx b/apps/studio/lib/profile.tsx index eab9cd5e4ff..dc6149975bb 100644 --- a/apps/studio/lib/profile.tsx +++ b/apps/studio/lib/profile.tsx @@ -6,9 +6,10 @@ import { usePermissionsQuery } from 'data/permissions/permissions-query' import { useProfileCreateMutation } from 'data/profile/profile-create-mutation' import { useProfileQuery } from 'data/profile/profile-query' import type { Profile } from 'data/profile/types' -import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useSendIdentifyMutation } from 'data/telemetry/send-identify-mutation' import type { ResponseError } from 'types' +import { useSendEventMutation } from 'data/telemetry/send-event-mutation' +import { TelemetryActions } from './constants/telemetry' export type ProfileContextType = { profile: Profile | undefined @@ -31,14 +32,11 @@ export const ProfileProvider = ({ children }: PropsWithChildren<{}>) => { const { mutate: sendEvent } = useSendEventMutation() const { mutate: sendIdentify } = useSendIdentifyMutation() - const { mutate: createProfile, isLoading: isCreatingProfile } = useProfileCreateMutation({ - async onSuccess() { - sendEvent({ category: 'conversion', action: 'sign_up', label: '' }) - }, - onError() { - toast.error('Failed to create your profile. Please refresh to try again.') + onSuccess: () => { + sendEvent({ action: TelemetryActions.SIGN_UP, properties: { category: 'conversion' } }) }, + onError: () => toast.error('Failed to create your profile. Please refresh to try again.'), }) // Track telemetry for the current user diff --git a/apps/studio/package.json b/apps/studio/package.json index 0dd1d48a1e0..6466ef888b1 100644 --- a/apps/studio/package.json +++ b/apps/studio/package.json @@ -16,8 +16,6 @@ "test:update:watch": "vitest --watch --update", "deploy:staging": "VERCEL_ORG_ID=team_E6KJ1W561hMTjon1QSwOh0WO VERCEL_PROJECT_ID=QmcmhbiAtCMFTAHCuGgQscNbke4TzgWULECctNcKmxWCoT vercel --prod -A .vercel/staging.json", "typecheck": "tsc --noEmit", - "storybook": "start-storybook -p 6006", - "build-storybook": "build-storybook", "prettier:check": "prettier --check .", "prettier:write": "prettier --write ." }, @@ -135,10 +133,6 @@ }, "devDependencies": { "@aws-sdk/client-secrets-manager": "^3.410.0", - "@storybook/addon-essentials": "^7.5.3", - "@storybook/addon-interactions": "^7.5.3", - "@storybook/addon-links": "^7.5.3", - "@storybook/react": "^7.5.3", "@supabase/postgres-meta": "^0.64.4", "@testing-library/dom": "^9.3.3", "@testing-library/react": "^14.0.0", @@ -178,7 +172,6 @@ "next-router-mock": "^0.9.13", "postcss": "^8.4.31", "prettier": "^4.0.0-alpha.8", - "storybook-dark-mode": "^3.0.1", "tailwindcss": "^3.4.1", "typescript": "~5.5.0" } diff --git a/apps/studio/pages/api/ai/sql/generate-v3.ts b/apps/studio/pages/api/ai/sql/generate-v3.ts index 4218aa062d6..951cd36ea91 100644 --- a/apps/studio/pages/api/ai/sql/generate-v3.ts +++ b/apps/studio/pages/api/ai/sql/generate-v3.ts @@ -74,12 +74,28 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) { - Use vector(384) data type for any embedding/vector related query - When debugging, retrieve sql schema details to ensure sql is correct - In Supabase, the auth schema already has a users table which is used to store users. It is common practice to create a profiles table in the public schema that links to auth.users to store user information instead. You don't need to create a new users table. + - Never suggest creating a view to retrieve information from the users table of the auth schema. This is against our best practices. When generating tables, do the following: + - Ensure that all tables always have a primary key + - Ensure that all tables have RLS enabled. Inform the user that they will need to create RLS policies before being able to read or write to the table over Supabase APIs. - For primary keys, always use "id bigint primary key generated always as identity" (not serial) - Prefer creating foreign key references in the create statement - Prefer 'text' over 'varchar' - Prefer 'timestamp with time zone' over 'date' + - If a foreign key is created, include a statement to create a database index on the columns involved in the foreign key. This is a best practice as it is common to use foreign key columns in join conditions when querying the database, hence the index will improve the performance of those joins and reduce database resource consumption. + - If the table is a foreign table, create it in a new schema called private. If the private schema doesnt exist, create it. + - Explain to the user that we create it outside of the public schema as it can present a security risk as foreign tables views cannot be configured to respect RLS policies. Provide this link for users to view more details about the security risk: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0017_foreign_table_in_api + + When generating views, do the following: + - All views should include 'with (security_invoker=on)' clause in the SQL statement for creating views. + - Place the 'with (security_invoker=on)' immediately after the CREATE VIEW statement, before AS + - If the view is a materialized view, create it in a new schema called private. If the private schema doesnt exist, create it. + - Explain to the user that we create it outside of the public schema as it can present a security risk as materialized views cannot be configured to respect RLS policies of the underlying tables they are built upon, nor can they be secured with RLS directly. Provide this link for users to view more details about the security risk: https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0016_materialized_view_in_api + + When installing database extensions, do the following: + - Never install extensions in the public schema + - Extensions should be installed in the extensions schema, or a dedicated schema Feel free to suggest corrections for suspected typos. @@ -90,6 +106,7 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) { - Then retrieve existing RLS policies and guidelines on how to write policies using the getRlsKnowledge tool . - Then write new policies or update existing policies based on the prompt - When asked to suggest policies, either alter existing policies or add new ones to the public schema. + - When writing policies that use a function from the auth schema, ensure that the calls are wrapped with parentheses e.g select auth.uid() should be written as (select auth.uid()) instead # You write database functions Your purpose is to generate a database function with the constraints given by the user. The output may also include a database trigger @@ -104,7 +121,8 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse) { # For all your abilities, follow these instructions: - First look at the list of provided schemas and if needed, get more information about a schema. You will almost always need to retrieve information about the public schema before answering a question. - - If the question is about users or involves creating a users table, also retrieve the auth schema. + - If the question is about users or involves creating a users table, also retrieve the auth schema. + - If it a query is a destructive query e.g. table drop, ask for confirmation before writing the query. The user will still have to run the query once you create it Here are the existing database schema names you can retrieve: ${schemas} diff --git a/apps/studio/pages/project/[ref]/database/migrations.tsx b/apps/studio/pages/project/[ref]/database/migrations.tsx index ef20245ddbd..d1e4b56ccb5 100644 --- a/apps/studio/pages/project/[ref]/database/migrations.tsx +++ b/apps/studio/pages/project/[ref]/database/migrations.tsx @@ -24,7 +24,7 @@ const MigrationsPage: NextPageWithLayout = () => { description="History of migrations that have been run on your database" /> - + { const connectDialogUpdate = useFlag('connectDialogUpdate') @@ -65,15 +66,9 @@ const Home: NextPageWithLayout = () => { This project is using Postgres with OrioleDB which is currently in preview and not suitable for production workloads. View our{' '} - {/* [Refactor] Make this into a reusable component to use links inline */} - + documentation - {' '} + {' '} for all limitations. diff --git a/apps/studio/pages/project/[ref]/settings/general.tsx b/apps/studio/pages/project/[ref]/settings/general.tsx index c079b53140f..4ad1579cfce 100644 --- a/apps/studio/pages/project/[ref]/settings/general.tsx +++ b/apps/studio/pages/project/[ref]/settings/general.tsx @@ -1,4 +1,6 @@ +import { subscriptionHasHipaaAddon } from 'components/interfaces/Billing/Subscription/Subscription.utils' import { + ComplianceConfig, CustomDomainConfig, DeleteProjectPanel, General, @@ -7,14 +9,21 @@ import { import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import SettingsLayout from 'components/layouts/ProjectSettingsLayout/SettingsLayout' import { ScaffoldContainer, ScaffoldHeader, ScaffoldTitle } from 'components/layouts/Scaffold' +import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' +import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import type { NextPageWithLayout } from 'types' const ProjectSettings: NextPageWithLayout = () => { const { project } = useProjectContext() + const selectedOrganization = useSelectedOrganization() + const isBranch = !!project?.parent_project_ref const { projectsTransfer: projectTransferEnabled } = useIsFeatureEnabled(['projects:transfer']) + const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: selectedOrganization?.slug }) + const hasHipaaAddon = subscriptionHasHipaaAddon(subscription) + return ( <> @@ -26,6 +35,8 @@ const ProjectSettings: NextPageWithLayout = () => { {!isBranch ? ( <> + {/* this is only setable on compliance orgs, currently that means HIPAA orgs */} + {hasHipaaAddon && } {projectTransferEnabled && } diff --git a/apps/studio/pages/sign-in-mfa.tsx b/apps/studio/pages/sign-in-mfa.tsx index 4a37aa30fd3..18fe5f4384a 100644 --- a/apps/studio/pages/sign-in-mfa.tsx +++ b/apps/studio/pages/sign-in-mfa.tsx @@ -6,16 +6,18 @@ import { toast } from 'sonner' import SignInMfaForm from 'components/interfaces/SignIn/SignInMfaForm' import SignInLayout from 'components/layouts/SignInLayout/SignInLayout' import { Loading } from 'components/ui/Loading' -import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { auth, buildPathWithParams, getAccessToken, getReturnToPath } from 'lib/gotrue' import type { NextPageWithLayout } from 'types' +import { useSendEventMutation } from 'data/telemetry/send-event-mutation' +import { TelemetryActions } from 'lib/constants/telemetry' const SignInMfaPage: NextPageWithLayout = () => { const router = useRouter() const queryClient = useQueryClient() - const [loading, setLoading] = useState(true) const { mutate: sendEvent } = useSendEventMutation() + const [loading, setLoading] = useState(true) + // This useEffect redirects the user to MFA if they're already halfway signed in useEffect(() => { auth @@ -41,7 +43,7 @@ const SignInMfaPage: NextPageWithLayout = () => { } if (data.currentLevel === data.nextLevel) { - sendEvent({ category: 'account', action: 'sign_in', label: '' }) + sendEvent({ action: TelemetryActions.SIGN_IN, properties: { category: 'account' } }) await queryClient.resetQueries() router.push(getReturnToPath()) return diff --git a/apps/www/_blog/2023-05-15-building-chatgpt-plugins-template.mdx b/apps/www/_blog/2023-05-15-building-chatgpt-plugins-template.mdx index f14ec6d0a78..b71ea367ec4 100644 --- a/apps/www/_blog/2023-05-15-building-chatgpt-plugins-template.mdx +++ b/apps/www/_blog/2023-05-15-building-chatgpt-plugins-template.mdx @@ -37,7 +37,7 @@ Supabase Edge Runtime does currently not support hosting/serving of static files ```ts // functions/main/index.ts -import aiPlugins from './ai-plugins.json' assert { type: 'json' } +import aiPlugins from './ai-plugins.json' with { type: 'json' } // [...] @@ -89,7 +89,7 @@ Next, we create our `/functions/chatgpt-plugin/index.ts` [file](https://github.c ```ts // /functions/chatgpt-plugin/index.ts import { Application, Router } from 'https://deno.land/x/oak@v11.1.0/mod.ts' -import openapi from './openapi.json' assert { type: 'json' } +import openapi from './openapi.json' with { type: 'json' } console.log('Hello from `chatgpt-plugin` Function!') diff --git a/apps/www/_blog/2024-12-10-durable-workflows-in-postgres-dbos.mdx b/apps/www/_blog/2024-12-10-durable-workflows-in-postgres-dbos.mdx index 21cd5419c9d..4a9e0617293 100644 --- a/apps/www/_blog/2024-12-10-durable-workflows-in-postgres-dbos.mdx +++ b/apps/www/_blog/2024-12-10-durable-workflows-in-postgres-dbos.mdx @@ -39,9 +39,10 @@ Let's start with a common situation where a workflow is useful: you're running a Workflow steps The process is simple, but writing a _robust_ program for this is surprisingly difficult. Some potential problems: @@ -49,7 +50,7 @@ The process is simple, but writing a _robust_ program for this is surprisingly d - You get to step 2, “Check Inventory”, and you're out of stock. You need to wait 24 hours for the new inventory before you can ship it. You need that “step” to sleep for a day. - Your program crashes during step 3, “Ship Order”, and it doesn't record that you've shipped the inventory. You end up sending the same order twice. -A Durable Workflow Engine helps with these problems (and more). There are a few on the market that provide different architectures like [Trigger.dev](https://trigger.dev/), [Inngest](https://www.inngest.com/), [Windmill](https://www.windmill.dev), [Temporal](https://temporal.io/), and [AWS Step Functions](https://aws.amazon.com/step-functions/). +A Durable Workflow Engine helps with these problems (and more). There are a few on the market that provide different architectures like [Oban](https://oban.pro/), [Trigger.dev](https://trigger.dev/), [Inngest](https://www.inngest.com/), [Windmill](https://www.windmill.dev), [Temporal](https://temporal.io/), and [AWS Step Functions](https://aws.amazon.com/step-functions/). DBOS offers a relatively unique approach to Workflows, storing the state in your own Postgres database. Let's explore how DBOS does it. diff --git a/apps/www/_customers/berriai.mdx b/apps/www/_customers/berriai.mdx index b2efa159848..728df55c734 100644 --- a/apps/www/_customers/berriai.mdx +++ b/apps/www/_customers/berriai.mdx @@ -25,6 +25,14 @@ stats: ] misc: [{ label: 'Backed by', text: 'Y Combinator' }] about: Supercharge your products with the power of LLMs like ChatGPT & go from prototype to production in less than a week. +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['ai', 'developer-tools', 'saas'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'North America' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth', 'vector'] --- Berri AI is an API for businesses to programmatically create custom ChatGPT instances on their data. Their customers build production-ready ChatGPT Apps in minutes with features such as prebuilt flows, editable options, authentication, and logging. diff --git a/apps/www/_customers/chatbase.mdx b/apps/www/_customers/chatbase.mdx index c9a01be5d2e..82cb206b451 100644 --- a/apps/www/_customers/chatbase.mdx +++ b/apps/www/_customers/chatbase.mdx @@ -27,9 +27,17 @@ misc: }, ] about: 'Chatbase is an AI chatbot builder. It trains ChatGPT on your data and lets you add a chat widget to your website. Just upload a document or add a link to your website and get a chatbot that can answer any question about their content.' +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['ai', 'saas', 'developer-tools'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'North America' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth', 'storage', 'realtime'] --- -After getting a new grad offer from his “dream company” rescinded with tech layoffs, Yasser Elsaid decided to embark on a different path and bootstrap his own venture. +After getting a new grad offer from his "dream company" rescinded with tech layoffs, Yasser Elsaid decided to embark on a different path and bootstrap his own venture. Two months before the release of the ChatGPT API, Yasser explored the GPT3 API and saw its immense potential. Leveraging Supabase as the backend, he built Chatbase in two weeks and launched it in February. @@ -37,9 +45,9 @@ Just five months after launching, Chatbase reach $1,000,000 annualized revenue, ## The Challenge -Fascinated by the possibilities of GPT3, Yasser envisioned Chatbase as an AI-driven chatbot capable of handling complex customer queries in real time. However, building a cost-effective and reliable solution to ingest and store data and manage large-scale customer interactions by himself wasn’t trivial. +Fascinated by the possibilities of GPT3, Yasser envisioned Chatbase as an AI-driven chatbot capable of handling complex customer queries in real time. However, building a cost-effective and reliable solution to ingest and store data and manage large-scale customer interactions by himself wasn't trivial. -Yasser faced another challenge: he needed to rapidly transform his idea into an actual product to maintain a competitive edge, but he didn’t have a team or funding. +Yasser faced another challenge: he needed to rapidly transform his idea into an actual product to maintain a competitive edge, but he didn't have a team or funding. ## Choosing Supabase @@ -51,7 +59,7 @@ With Supabase, everything seamlessly came together and he was able to go from id

    - Supabase is great because it has everything. I don’t need a different solution for + Supabase is great because it has everything. I don't need a different solution for authentication, a different solution for database, or a different solution for storage.

    diff --git a/apps/www/_customers/e2b.mdx b/apps/www/_customers/e2b.mdx new file mode 100644 index 00000000000..fb631cf159a --- /dev/null +++ b/apps/www/_customers/e2b.mdx @@ -0,0 +1,123 @@ +--- +name: E2B +title: 'E2B: Accelerating AI-Driven Development with Supabase' +# Use meta_title to add a custom meta title. Otherwise it defaults to '{name} | Supabase Customer Stories': +meta_title: 'E2B: Accelerating AI-Driven Development with Supabase' +description: Discover how E2B leveraged Supabase to streamline its platform, allowing secure, scalable execution of AI-generated code in the cloud. +# Use meta_description to add a custom meta description. Otherwise it defaults to {description}: +meta_description: Discover how E2B leveraged Supabase to streamline its platform, allowing secure, scalable execution of AI-generated code in the cloud. +author: paul_copplestone +author_title: Supabase +author_url: https://github.com/kiwicopple +author_image_url: https://avatars2.githubusercontent.com/u/10214025?s=400&u=c6775be2ae667e2acae3ccd347fed62bb3f5b3e7&v=4 +logo: /images/customers/logos/e2b.png +logo_inverse: /images/customers/logos/light/e2b.png +og_image: /images/customers/og/e2b.jpg +tags: + - supabase +date: '2024-12-09' +company_url: 'https://e2b.dev' +stats: [] +misc: + [ + { label: 'Use case', text: 'Secure Code Execution, Developer Productivity, Scalability' }, + { label: 'Solutions', text: 'Supabase Auth, Supabase Database' }, + ] +about: An open-source platform that enables developers to run untrusted AI-generated code in the cloud safely and efficiently. +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['ai', 'developer-tools', 'saas'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'North America' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth', 'realtime', 'functions'] +--- + + + Supabase empowers us to focus on innovation rather than infrastructure. It's the backbone of our + platform, enabling scalability and seamless developer experiences. + + +Discover how E2B, an open-source platform that enables developers to run untrusted AI-generated code in the cloud safely and efficiently, leveraged Supabase to streamline its platform, allowing secure, scalable execution of AI-generated code in the cloud. + +Learn how Supabase's integrated authentication and database tools helped E2B optimize its development process. + +## The Challenge - Simplifying Cloud-Based Code Execution + +E2B aims to simplify how developers interact with AI-generated code by providing secure and scalable cloud-based sandboxes. + +The platform's idea originated from the two founders' experience repeatedly integrating backend tools like authentication, notifications, and payment systems. Inspired by GPT-3.5's capabilities, they envisioned automating this repetitive work, which led to the creation of E2B's sandbox architecture. + +With E2B's sandboxes, developers can quickly spin up lightweight, Ubuntu-based virtual machines with minimal effort, enabling flexible and secure code execution for various use cases. + +Key challenges include: + +- Managing infrastructure complexity while ensuring scalability for growing user needs. +- Safely executing untrusted AI-generated code in the cloud. +- Maintaining a seamless developer experience while handling millions of sandbox requests daily. + +Having raised over $11 million in seed funding and just released SDK version 1.0 and a new landing page, E2B focused on hiring engineers with expertise in AI product development. They wanted to enhance the team's technical capabilities while implementing robust incident reporting and monitoring systems to ensure high reliability and transparency in service performance. + +E2B needed to scale their platform to handle increased user demand while maintaining service quality and performance. + +**Key Requirements** +**Authentication and Database Integration:** E2B needed a platform that seamlessly combined user authentication and database management. Supabase allowed authenticated users to connect automatically to the database without requiring custom integration. + +**Scalability:** The platform needed to support high-scale operations, including logging millions of sandbox instances per month. Supabase's PostgreSQL database effortlessly handled this volume. + +**Developer Productivity:** Supabase's SDKs, with first-party support for frameworks like Next.js and the AI-powered SQL assistant, were essential for streamlining development tasks. This enabled E2B's team to focus on innovation rather than operational overhead. + +**Ease of Use and Familiarity:** Supabase's use of PostgreSQL and high-quality documentation reduced the learning curve for new developers, ensuring rapid onboarding and minimal support requirements. + +**Security and Role-Based Access:** E2B required robust security features, including row-level security, to manage sensitive data and ensure data isolation across users and operations. + +## The Approach - Building on Supabase for Efficiency and Security + +E2B chose Supabase to manage their authentication and database needs due to its: + +**Ease of Use and Integration:** Supabase seamlessly combines authentication and database management, making it easy for E2B to automatically connect users to their database without extensive custom integration. + +**Developer-Friendly Tools:** Supabase provides intuitive SDKs with first-party support for frameworks like Next.js and React, enabling faster development with features like hooks and type safety. + +**Time-Saving Features:** The AI-powered SQL assistant and query-sharing capabilities significantly reduced the time spent on writing and managing SQL queries, boosting productivity and collaboration among team members. + +**Open-Source Community:** E2B's founders were early adopters of Supabase, drawn to its positioning as an open-source alternative to Firebase. Its active community and high-quality documentation made it a trusted choice for their team. + +**Interlinked Systems:** The tight integration between Supabase's authentication system and its PostgreSQL database offered a streamlined solution that was both flexible and easy to manage. + +**Scalability and Reliability:** Supabase's infrastructure seamlessly handled E2B's growing demands, including processing over 4 million monthly sandbox operations, demonstrating its ability to scale reliably. + +E2B leveraged Supabase to store and track logs for sandbox operations, with every session start and end saved as database entries. This enabled detailed analytics while maintaining performance at scale. + +## The Results - Delivering Innovation at Scale + +E2B's adoption of Supabase transformed its operations and enhanced its ability to deliver a reliable and efficient platform for running untrusted AI-generated code in the cloud. + +**Seamless Scalability:** Supabase handled over 4 million monthly sandbox operations, logging each start and close event without disruptions. This seamless scalability ensured that E2B could support its growing user base and complex workloads, from quick code executions to multi-hour processes. + +**Enhanced Developer Productivity:** By leveraging Supabase's SDKs and AI-powered SQL assistant, E2B significantly reduced the time spent on repetitive tasks like writing queries and debugging database issues. Developers could focus on building innovative features rather than managing backend infrastructure, increasing overall velocity. + +**Robust Security Features:** Supabase's row-level security ensured that user data and sandbox operations remained isolated and protected, meeting E2B's stringent security requirements. This was particularly critical for handling untrusted AI-generated code, which demanded high data integrity and user privacy. + +**Cost-Effectiveness:** Supabase's authentication and database tools eliminated the need for additional infrastructure or third-party services, reducing operational costs. The easy-to-use tools also minimized development overhead, saving valuable resources. + +**Improved User Experience:** Supabase's ability to seamlessly connect authentication and database operations ensured a smoother experience for E2B users, who rely on the platform for real-time execution of AI-driven workflows. + +**Streamlined Operations:** Supabase's tools eliminated the need for complex custom infrastructure, freeing up E2B's team to focus on innovation. + +**Enhanced Logging and Analytics:** The comprehensive logging of sandbox operations provided E2B with detailed analytics, enabling better insights into user behavior and system performance. This transparency empowered the team to optimize their platform further and meet customer demands more effectively. + +## Why Supabase? + + + Supabase combines ease of use with powerful features. Its native integration of authentication and + database tools is a game-changer, enabling us to build faster while maintaining security and + scalability. + + +Through its partnership with Supabase, E2B built a foundation for long-term growth. It delivers a platform that meets the increasing demands of AI-driven development while maintaining exceptional reliability and security. + +**Discover how Supabase can simplify your development process and support your growth.** + +[Learn More](https://forms.supabase.com/enterprise) diff --git a/apps/www/_customers/epsilon3.mdx b/apps/www/_customers/epsilon3.mdx index 88fa0008b8c..72d75b7c6b6 100644 --- a/apps/www/_customers/epsilon3.mdx +++ b/apps/www/_customers/epsilon3.mdx @@ -24,7 +24,15 @@ stats: { stat: '00,000', label: Example stat }, ] misc: [{ label: 'Founded', text: Los Angeles, California }] -about: Epsilon3 is the OS for spacecraft and complex operations. Epsilon3’s software platform manages complex operational procedures, saving operators time and reducing errors. +about: Epsilon3 is the OS for spacecraft and complex operations. Epsilon3's software platform manages complex operational procedures, saving operators time and reducing errors. +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['saas', 'developer-tools'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'North America' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'realtime', 'auth'] --- Epsilon3 is a USA-based Y Combinator startup. They digitize paper-based procedures in the space industry using telemetry data, @@ -63,7 +71,7 @@ country. These customer requirements mean Epsilon3 must be able to offer flexibl >

    Billion dollar missions need to run reliably and securely.

    - We didn’t just want something that works, we wanted a solution implementing best practices. We + We didn't just want something that works, we wanted a solution implementing best practices. We also needed a setup that we could self-host to meet compliance needs and allow on-premises deployment options for our customers.

    diff --git a/apps/www/_customers/good-tape.mdx b/apps/www/_customers/good-tape.mdx index 089601da6e0..7e36a98343e 100644 --- a/apps/www/_customers/good-tape.mdx +++ b/apps/www/_customers/good-tape.mdx @@ -24,9 +24,17 @@ misc: { label: 'Solutions', text: 'Database, Auth, Realtime, Edge Functions' }, ] about: 'Good Tape is a secure transcription service that makes it easy for journalists and other professionals to turn audio recordings into text, regardless of sound quality and language.' +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['media', 'ai', 'saas'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'Europe' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth', 'realtime', 'functions'] --- -Good Tape started as a hackathon project by Jakob Steinn. The initial idea was to help journalists at Zetland, the news agency where he worked, transcribe their stories from audio to text. In the past, they had done this by hand since existing tools didn't support smaller language areas like Danish. With the help from his colleagues at Zetland, the prototype went from proof of concept on Google Colab, to proper web service leveraging OpenAI’s Whisper arriving at the foundation that today is Good Tape. +Good Tape started as a hackathon project by Jakob Steinn. The initial idea was to help journalists at Zetland, the news agency where he worked, transcribe their stories from audio to text. In the past, they had done this by hand since existing tools didn't support smaller language areas like Danish. With the help from his colleagues at Zetland, the prototype went from proof of concept on Google Colab, to proper web service leveraging OpenAI's Whisper arriving at the foundation that today is Good Tape. That was one year ago. The initial hackathon product transformed into Good Tape, which allows users to upload their audio files and instantly get them transcribed by AI. @@ -36,9 +44,9 @@ That was one year ago. The initial hackathon product transformed into Good Tape, Good Tape recently crossed the €1M ARR threshold and is growing +25% MoM. Built with security and privacy in mind, Good Tape has customers in more than 130 countries and has helped journalists in countries like Taiwan, Denmark, and The Netherlands - doing roughly 75,000 transcriptions per week in 98 different languages. The product saves them hours upon hours of manual transcription work and allows for new use cases like categorization and classification of previously inaccessible data. But this hyper-growth also came with its own challenges. -One such challenge was authentication. Good Tape started off with Auth0. When they scaled beyond hobbyist levels, the cost to support auth exploded. From Steinn, “their pricing is absolutely wild.” +One such challenge was authentication. Good Tape started off with Auth0. When they scaled beyond hobbyist levels, the cost to support auth exploded. From Steinn, "their pricing is absolutely wild." -More importantly, since Steinn was the only full-time developer, Good Tape needed a database solution that didn't put the burden of database debugging and support on their small team. At first, they went with a Postgres database on Fly, which was “not a managed solution but gave all the tools to feel like it was managed.” But as they scaled, they started noticing limitations with reliability and performance. +More importantly, since Steinn was the only full-time developer, Good Tape needed a database solution that didn't put the burden of database debugging and support on their small team. At first, they went with a Postgres database on Fly, which was "not a managed solution but gave all the tools to feel like it was managed." But as they scaled, they started noticing limitations with reliability and performance. The critical moment for Good Tape came when their primary database went down, and in an event caused by internal network issues, one of their read replicas was promoted to be primary, leaving them with two active leaders. That caused the cluster to go into an irreparable state, from which they could not boot up again. @@ -53,9 +61,9 @@ That prompted Steinn to look for a new solution that would allow them to build w For the database migration, Kevin Brolly, Head of Customer Success, provided invaluable assistance. Additionally, for the authentication migration, Kevin Grüneberg, Billing Lead, provided guidance [based on his own experience](https://kevcodez.medium.com/migrating-125-000-users-from-auth0-to-supabase-81c0568de307). Both migrations proceeded exceptionally smoothly. -Post migration, Steinn appreciated the communication and safety that Supabase offered. Complex database issues that had previously taken them long engineering hours to identify, plan, and fix were now managed and handled by Supabase. One of which is automatically adding more disk space before it’s too late. This allows Steinn and the Good Tape team to focus on their core product. +Post migration, Steinn appreciated the communication and safety that Supabase offered. Complex database issues that had previously taken them long engineering hours to identify, plan, and fix were now managed and handled by Supabase. One of which is automatically adding more disk space before it's too late. This allows Steinn and the Good Tape team to focus on their core product. -Further, while implementing Supabase, he was impressed with “the sheer amount of educational content and incredible documentation. It was amazing. So really, really, really great.” Beyond just the developer resources, once finished with the migration, Steinn ran a small POC with [Supabase Auth](https://supabase.com/docs/guides/auth). It was then that he found that Supabase could solve their authentication cost issues and also add extra benefits that were impossible if he was working with multiple vendors“. The main thing was having our authentication together with our database and seeing the benefits of having those two in the same place. One example is [row-level security](https://supabase.com/docs/guides/auth/row-level-security)… that just sells me immediately.” +Further, while implementing Supabase, he was impressed with "the sheer amount of educational content and incredible documentation. It was amazing. So really, really, really great." Beyond just the developer resources, once finished with the migration, Steinn ran a small POC with [Supabase Auth](https://supabase.com/docs/guides/auth). It was then that he found that Supabase could solve their authentication cost issues and also add extra benefits that were impossible if he was working with multiple vendors". The main thing was having our authentication together with our database and seeing the benefits of having those two in the same place. One example is [row-level security](https://supabase.com/docs/guides/auth/row-level-security)… that just sells me immediately." My biggest regret is not having gone with Supabase from the beginning. @@ -71,11 +79,11 @@ That includes initiatives like the Good Tape archive, where users can upload any In terms of costs, Good Tape has seen a remarkable reduction in backend expenses. Before migrating to Supabase's comprehensive backend solution, they were paying nearly $1,500 per month for authentication through Auth0, and their database-related expenses exceeded $2,600 per month with Fly. With the transition to Supabase, they not only lowered their total costs to $1,600, resulting in a 60% reduction in expenses, but they also saved valuable engineering time that allowed them to focus on their core product. -The best part for Steinn has been the peace of mind with Supabase's managed services. Where in the past, his vacations had always been interrupted - “Every time I was in an airport, our database would go down”. With Supabase now managing the database, Steinn can visit friends and family in Iceland without any concerns. +The best part for Steinn has been the peace of mind with Supabase's managed services. Where in the past, his vacations had always been interrupted - "Every time I was in an airport, our database would go down". With Supabase now managing the database, Steinn can visit friends and family in Iceland without any concerns. Moving to Supabase has saved the company. We wouldn't have worked if we hadn't made the right [backend] decision… So Supabase helped us stay in production and not become an internal tool. -> To learn more about how Supabase can help you manage and scale your database, **[reach out to us](https://forms.supabase.com/enterprise)**. +> To learn more about how Supabase can help you manage and scale your database, **[reach out to us](https://forms.supabase.com/enterprise)**. diff --git a/apps/www/_customers/happyteams.mdx b/apps/www/_customers/happyteams.mdx index d97f2e320d4..f7452d92fa3 100644 --- a/apps/www/_customers/happyteams.mdx +++ b/apps/www/_customers/happyteams.mdx @@ -25,6 +25,14 @@ stats: ] misc: [{ label: 'Founded', text: 'Dublin, Ireland' }] about: HappyTeams is a purpose-built solution that empowers management to unlock valuable team insights. +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['saas', 'analytics'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'Europe' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth'] --- ## The Challenge diff --git a/apps/www/_customers/humata.mdx b/apps/www/_customers/humata.mdx index c32145731e2..39ec69200a1 100644 --- a/apps/www/_customers/humata.mdx +++ b/apps/www/_customers/humata.mdx @@ -27,31 +27,39 @@ misc: }, ] about: Use AI to ask questions across all of your files. +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['ai', 'saas', 'analytics'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'North America' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth', 'realtime', 'vector'] --- ## Introduction -Humata, a platform enabling millions of users to chat with and analyse their all documents, relies on cutting-edge technology to support rapid growth and high user demand. Humata started as PDF AI that allows anyone to chat with their files, initially geared for scientific researchers. Humata has now expanded to serve major enterprises and government institutions by connecting their knowledge base to AI and allowing them to get answers they can trust with best-in-class referencing. With a tech stack that includes Supabase, GCP, OpenAI, Vercel, and NEXT.js, Humata’s platform provides real-time, mission-critical analysis for researchers, enterprises, and government institutions. By partnering with Supabase, Humata achieved a 4X reduction in vector database costs, streamlined their development, and consolidated their data so they could scale seamlessly. +Humata, a platform enabling millions of users to chat with and analyse their all documents, relies on cutting-edge technology to support rapid growth and high user demand. Humata started as PDF AI that allows anyone to chat with their files, initially geared for scientific researchers. Humata has now expanded to serve major enterprises and government institutions by connecting their knowledge base to AI and allowing them to get answers they can trust with best-in-class referencing. With a tech stack that includes Supabase, GCP, OpenAI, Vercel, and NEXT.js, Humata's platform provides real-time, mission-critical analysis for researchers, enterprises, and government institutions. By partnering with Supabase, Humata achieved a 4X reduction in vector database costs, streamlined their development, and consolidated their data so they could scale seamlessly. ## The Challenge -As Humata's user base expanded, the need for scalable, reliable, and cost-effective infrastructure became critical. Initially, Humata used Pinecone for its vector database workloads for chat with doc solution, but managing this infrastructure became complex and expensive, with 20 s2.x8 pods and additional replicas required for latency. Humata needed a solution that would simplify infrastructure management, reduce costs, and enhance the platform’s feature velocity without sacrificing performance. +As Humata's user base expanded, the need for scalable, reliable, and cost-effective infrastructure became critical. Initially, Humata used Pinecone for its vector database workloads for chat with doc solution, but managing this infrastructure became complex and expensive, with 20 s2.x8 pods and additional replicas required for latency. Humata needed a solution that would simplify infrastructure management, reduce costs, and enhance the platform's feature velocity without sacrificing performance. ## The Supabase Solution -Humata chose Supabase for its Postgres database, authentication, and real-time functionality. Supabase’s open-source community and world-class support made it the perfect choice to meet Humata's growing needs. The flexibility of Supabase allowed Humata to consolidate all their data—including semantic searches and business data—into a single database, significantly improving query complexity and feature velocity. Backed by Supabase’s Enterprise plan, Humata gained access to deep technical specialists to optimise the back-end performance ongoing, and leverages this pool of experts to de-risk their critical data infrastructure operations. +Humata chose Supabase for its Postgres database, authentication, and real-time functionality. Supabase's open-source community and world-class support made it the perfect choice to meet Humata's growing needs. The flexibility of Supabase allowed Humata to consolidate all their data—including semantic searches and business data—into a single database, significantly improving query complexity and feature velocity. Backed by Supabase's Enterprise plan, Humata gained access to deep technical specialists to optimise the back-end performance ongoing, and leverages this pool of experts to de-risk their critical data infrastructure operations. ## Migration from Pinecone to Supabase -The migration from Pinecone to Supabase was a game-changer for Humata’s chat with PDF technology. By switching to a single 16XL instance of Supabase’s pgVector, Humata achieved a staggering 4X cost reduction. This transition also simplified infrastructure management, eliminated the need for multiple replicas, and boosted feature velocity by enabling complex queries that combine semantic search with business data. +The migration from Pinecone to Supabase was a game-changer for Humata's chat with PDF technology. By switching to a single 16XL instance of Supabase's pgVector, Humata achieved a staggering 4X cost reduction. This transition also simplified infrastructure management, eliminated the need for multiple replicas, and boosted feature velocity by enabling complex queries that combine semantic search with business data. ## Key Benefits of Using Supabase - **Cost Efficiency**: The move from Pinecone to Supabase resulted in a 75% reduction in database costs, allowing Humata to reallocate resources to further innovation. -- **Scalability**: Supabase’s infrastructure scaled effortlessly, supporting Humata’s growth to millions of users worldwide. -- **Developer Speed**: Supabase’s ease of use and built-in features, such as row-level security and the frontend client, enabled faster development and more efficient workflows. -- **Seamless Real-Time Collaboration**: Supabase’s Real Time feature enhanced collaboration, allowing users to see document changes and updates instantly. -- **Security & Compliance**: With Supabase’s SOC 2 Type II and HIPAA-compliant infrastructure, Humata ensures the highest levels of security, protecting sensitive data as they approach HIPAA certification. +- **Scalability**: Supabase's infrastructure scaled effortlessly, supporting Humata's growth to millions of users worldwide. +- **Developer Speed**: Supabase's ease of use and built-in features, such as row-level security and the frontend client, enabled faster development and more efficient workflows. +- **Seamless Real-Time Collaboration**: Supabase's Real Time feature enhanced collaboration, allowing users to see document changes and updates instantly. +- **Security & Compliance**: With Supabase's SOC 2 Type II and HIPAA-compliant infrastructure, Humata ensures the highest levels of security, protecting sensitive data as they approach HIPAA certification. ## Conclusion diff --git a/apps/www/_customers/maergo.mdx b/apps/www/_customers/maergo.mdx index de8e263f4ca..2be77b447c4 100644 --- a/apps/www/_customers/maergo.mdx +++ b/apps/www/_customers/maergo.mdx @@ -18,6 +18,14 @@ stats: [] misc: [{ label: 'Use case', text: 'Logistics, SaaS' }, { label: 'Solutions', text: 'Database, Auth' }] about: 'Maergo, a nationwide expedited parcel delivery service, chose Supabase to overcome its technology challenges. With Supabase, Maergo was able to reduce its codebase by 90%, decrease deployment times from 12-15 minutes to just seconds, and achieve unprecedented scalability during peak season. Discover how Supabase helped Maergo achieve faster time-to-market, improved performance, and significant cost savings.' +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['ecommerce', 'saas'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'North America' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth'] --- ## Introduction @@ -41,7 +49,7 @@ Maergo chose Supabase due to its lightweight and scalable architecture, which ad Maergo's successful migration to Supabase enabled them to reduce costs, increase scalability, and improve deployment times. The company can now focus on building innovative solutions for its customers while leveraging Supabase's robust features and cost-effective model. **Reduced codebase**\ -Maergo’s codebase went from 730k lines of code to 95k lines of code, while preserving all API components of the previous system and even introducing new functionality with the help of Supabase. +Maergo's codebase went from 730k lines of code to 95k lines of code, while preserving all API components of the previous system and even introducing new functionality with the help of Supabase. **Much faster deployment speed**\ Deployment times decreased from 12-15 minutes to a few seconds. @@ -55,4 +63,4 @@ Supabase's cost model was more appealing, allowing Maergo to scrap their previou **Increased availability**\ Maergo finished their platform migration before peak season, achieving the highest availability in company history. -> To learn more about how Supabase can help you manage and scale your database, **[reach out to us](https://forms.supabase.com/enterprise)**. +> To learn more about how Supabase can help you manage and scale your database, **[reach out to us](https://forms.supabase.com/enterprise)**. diff --git a/apps/www/_customers/markprompt.mdx b/apps/www/_customers/markprompt.mdx index 9e988b9b861..7e440e1215b 100644 --- a/apps/www/_customers/markprompt.mdx +++ b/apps/www/_customers/markprompt.mdx @@ -22,12 +22,20 @@ stats: ] misc: [{ label: 'Built by', text: 'The Motif Team' }] about: Enterprise-grade AI chatbots for your website and docs. +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['ai', 'saas', 'developer-tools'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'Europe' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth', 'vector'] --- Markprompt is an AI-powered chatbot platform that simplifies the process of finding relevant information in large-scale knowledge bases. With its chat-based interface, users can ask questions in natural language and receive instant, accurate answers. Markprompt's advanced analytics help teams identify knowledge gaps and improve their documentation strategies. - We’ve found that having access to the full features of Postgres, colocated with the embeddings, + We've found that having access to the full features of Postgres, colocated with the embeddings, makes it the perfect vector database. diff --git a/apps/www/_customers/mendableai.mdx b/apps/www/_customers/mendableai.mdx index 7be8819dcdf..eaa1d4bf91d 100644 --- a/apps/www/_customers/mendableai.mdx +++ b/apps/www/_customers/mendableai.mdx @@ -25,6 +25,14 @@ stats: ] misc: [{ label: 'Backed by', text: 'Y Combinator' }] about: Mendable is Chat Powered Search for Documentation. +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['ai', 'saas', 'developer-tools'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'North America' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'vector'] --- [Mendable](http://mendable.ai/) provides a chat-powered search engine for technical documentation. Their AI-powered search tool makes it easier for developers and other technical users to find relevant information in complex documentation. Users can simply ask questions in natural language, and the tool returns the most relevant answers. Mendable's search engine also provides detailed analytics, which helps teams identify knowledge gaps and areas for improvement in their documentation. Mendable has integrated with some of the largest open source projects in the space such as LangChain and LlamaIndex. @@ -39,7 +47,7 @@ Mendable lear that Supabase supports [pgvector](https://supabase.com/docs/guides We tried other vector databases - we tried Faiss, we tried Weaviate, we tried Pinecone. We found - them to be incredibly expensive and not very intuitive. If you’re just doing vector search they’re + them to be incredibly expensive and not very intuitive. If you're just doing vector search they're great, but if you need to store a bunch of metadata that becomes a huge pain. @@ -51,10 +59,10 @@ Using [Supabase Vector](https://supabase.com/vector), Mendable was able to build ## The Results -Thanks to Supabase Vector, Mendable was able to significantly improve the efficiency and accuracy of their Chat Powered Search for Documentation. They were able to build faster and more cost-effectively using Supabase’s open source stack. +Thanks to Supabase Vector, Mendable was able to significantly improve the efficiency and accuracy of their Chat Powered Search for Documentation. They were able to build faster and more cost-effectively using Supabase's open source stack. - We looked at the alternatives and chose Supabase because it’s open source, it’s simpler, and, for + We looked at the alternatives and chose Supabase because it's open source, it's simpler, and, for all the ways we need to use it, Supabase has been just as performant - if not more performant - than the other vector databases. diff --git a/apps/www/_customers/mobbin.mdx b/apps/www/_customers/mobbin.mdx index bb15ee4f6da..935fa1c71d3 100644 --- a/apps/www/_customers/mobbin.mdx +++ b/apps/www/_customers/mobbin.mdx @@ -25,6 +25,14 @@ stats: ] misc: [{ label: 'Founded', text: Singapore }] about: Mobbin is a comprehensive, expert-curated library of interfaces from the best-in-class digital products that helps product designers find relevant design inspiration and references with significantly less time and effort. +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['saas', 'developer-tools'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'Asia' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth'] --- Mobbin helps over 200,000 creators globally search and view the latest design patterns from well-known apps. @@ -69,7 +77,7 @@ application sustainable for their business model. The team found Supabase and realized it covered all the bases. Not only that, their team had experience with the tools open source tools that Supabase is built with. -[Authentication](https://supabase.com/auth), built using [GoTrue](https://github.com/supabase/gotrue), instantly solved their users' login issues. +[Authentication](https://supabase.com/auth), built using [GoTrue](https://github.com/supabase/gotrue), instantly solved their users' login issues. Under the hood, Supabase is just Postgres, and Jian Jie (co-founder and CTO of Mobbin) knows this is one of the best battle-tested open source relational databases. @@ -95,6 +103,6 @@ Because Supabase does not charge based on API requests, Mobbin significantly red Migrating to Supabase helped the team at Mobbin instantly improve the end-user experience and save costs. They now add new features with confidence, and they continue to help creators all over the globe find inspiration for their next project. -You can [check out Mobbin on their website](https://mobbin.design/browse/ios/apps). +You can [check out Mobbin on their website](https://mobbin.design/browse/ios/apps). [Sign up](https://supabase.com/dashboard/) for Supabase's Free Plan and set up a scalable backend in less than 2 minutes. diff --git a/apps/www/_customers/next-door-lending.mdx b/apps/www/_customers/next-door-lending.mdx index 5621cfe36a1..a25deac6c26 100644 --- a/apps/www/_customers/next-door-lending.mdx +++ b/apps/www/_customers/next-door-lending.mdx @@ -24,11 +24,19 @@ misc: { label: 'Solutions', text: 'Supabase Database, Supabase Auth, Supabase Storage' }, ] about: "Next Door Lending, a National Mortgage Broker that's licensed in 25 states, needed a solution that could support secure data management, a frictionless user experience, and future AI products." +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['real-estate', 'fintech'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'enterprise' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'North America' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth', 'storage'] --- ## Introduction -Next Door Lending (NDL) is a top 10 mortgage brokerage in the country. They've processed $2 billion dollars of loans in the past three years. They evaluate a borrower’s creditworthiness and ability to repay the loan. Then, they match each borrower with lenders that have the best loan terms. Currently, NDL is licensed and operates in 25 different states. +Next Door Lending (NDL) is a top 10 mortgage brokerage in the country. They've processed $2 billion dollars of loans in the past three years. They evaluate a borrower's creditworthiness and ability to repay the loan. Then, they match each borrower with lenders that have the best loan terms. Currently, NDL is licensed and operates in 25 different states. Partnering with over 50 lending companies, NDL offers hundreds of products for borrowers. These range from standard 30 Year Fixed Rate loans to mortgage refinances, to even USDA or Veteran loans. As a result, technology plays a critical role in NDL's daily operations and has been a differentiation vector against other mortgage brokers. @@ -36,12 +44,12 @@ Partnering with over 50 lending companies, NDL offers hundreds of products for b ## The Challenge -- **Data ownership and management**: Not only is there regulation around financial data, one of NDL's core growth thesis is to leverage their data for future products. To do so, their internal teams needed a manageable data infrastructure. For Howard Haynes, CPO at NDL, “if we didn't have a way to manage data and access it, it would get very overwhelming.” Without the tools to support the growth in data, “this would become a major pain point.” +- **Data ownership and management**: Not only is there regulation around financial data, one of NDL's core growth thesis is to leverage their data for future products. To do so, their internal teams needed a manageable data infrastructure. For Howard Haynes, CPO at NDL, "if we didn't have a way to manage data and access it, it would get very overwhelming." Without the tools to support the growth in data, "this would become a major pain point." - **UX**: Every month, NDL adds thousands of new borrowers. Given the high cost of customer acquisition, any friction within the user experience directly translated to lost revenue. NDL's onboarding process needed to be smooth, fast, and seamless for borrowers. - **Scale**: NDL operates across 25 states with 12 in-person branches to process loans, stay compliant, engage with local markets, and create a boutique experience for borrowers. NDL needed the ability to build future products such as predictive matching and AI tools for their branch brokers and borrowers. - If we didn’t have a way to manage data and access it, it would get very overwhelming. This would + If we didn't have a way to manage data and access it, it would get very overwhelming. This would become a major pain point. @@ -51,7 +59,7 @@ NDL selected Supabase after considering other providers such as Amazon Web Servi ### Data - A single system to rule them all -For NDL, their backend challenges included designing the right APIs, row-level security, authentication, and a serving layer for their brokers. Having a single system that had all the appropriate functionality was important in maintaining its technical momentum. “When you use a lot of different data services, it’s much harder to keep everything as fast as we need it to be”, said Howard. +For NDL, their backend challenges included designing the right APIs, row-level security, authentication, and a serving layer for their brokers. Having a single system that had all the appropriate functionality was important in maintaining its technical momentum. "When you use a lot of different data services, it's much harder to keep everything as fast as we need it to be", said Howard. ### UX - Frictionless authentication @@ -59,7 +67,7 @@ As part of NDL's business model, they work with entrepreneurs who lead local bra ### Scale - To AI and beyond -NDL also deploys a separate website for each individual branch, connected together by a homegrown CMS and a shared Supabase backend server. For Howard, “when you're white labeling, and you're maintaining five or fifteen different deployments, it’s very important that you can manage most of these things from the same system or the same backend. For NDL's future AI products, they can use [Supabase Vector](https://supabase.com/vector) to store, query, and index vector embeddings at scale, without introducing a new platform to their stack. +NDL also deploys a separate website for each individual branch, connected together by a homegrown CMS and a shared Supabase backend server. For Howard, "when you're white labeling, and you're maintaining five or fifteen different deployments, it's very important that you can manage most of these things from the same system or the same backend. For NDL's future AI products, they can use [Supabase Vector](https://supabase.com/vector) to store, query, and index vector embeddings at scale, without introducing a new platform to their stack. ## The Solution @@ -67,16 +75,16 @@ NDL employs multiple instances of Supabase for each of their branch web properti Across all of NDL's branches, they see thousands of new signups each month, and each branch's website sends their signup data to the central Supabase backend via API. All loan details are aggregated in this server, allowing internal teams to access the right data quickly. In the future, NDL plans to add a layer of predictive analytics to match borrowers and lenders. This would require capturing thousands of additional data points per user and building a comprehensive model. With Supabase, they're perfectly positioned to add on the additional capability. -“Supabase allows us to move fast and scale up as needed. There are a lot of tools that can only do one or the other – but Supabase really excels at both.” - Howard Haynes, CPO at NDL +"Supabase allows us to move fast and scale up as needed. There are a lot of tools that can only do one or the other – but Supabase really excels at both." - Howard Haynes, CPO at NDL The Results -After launching their web application in 2020, Supabase helped NDL release new features and scale to become one of the nation's largest mortgage brokers. “Things that would have typically taken longer to set up have been a lot easier.” Howard pointed at how the engineering team quickly onboarded to Supabase, “send[ing] someone to read the docs, they come back, and they’re teaching me about a new feature in Supabase I wasn’t aware of.” +After launching their web application in 2020, Supabase helped NDL release new features and scale to become one of the nation's largest mortgage brokers. "Things that would have typically taken longer to set up have been a lot easier." Howard pointed at how the engineering team quickly onboarded to Supabase, "send[ing] someone to read the docs, they come back, and they're teaching me about a new feature in Supabase I wasn't aware of." The engineering team now had the bandwidth to focus on building a custom CMS better tailored to their business requirements. With Supabase's auth product, NDL has also built custom onboarding flows for new users, asking them for details about their loan before a direct sign-up page, and increasing user happiness. A significant portion of their business relies on SEO, making a fast-loading website crucial. They accomplished an impressive 0.5-second loading time by transitioning to a dynamic system where each page is directly loaded from the database. This transformation underscores the pivotal role Supabase played, as it houses and serves all their pages and data, ensuring their website's exceptional performance. -The success of NDL prompted its owners to expand the business with Neighbourhood Tech, a company that offers “NDL as a service” to other branches. They manage the additional system using Supabase, establishing a backend schema for data management, and utilizing replication for scalable expansion. This approach allows them to handle separate data quickly and efficiently. +The success of NDL prompted its owners to expand the business with Neighbourhood Tech, a company that offers "NDL as a service" to other branches. They manage the additional system using Supabase, establishing a backend schema for data management, and utilizing replication for scalable expansion. This approach allows them to handle separate data quickly and efficiently. An example of this is [Lock It Lending](https://www.lockitlending.com/). This new site uses Supabase to display all dynamic content, store lead information, and display financial literacy terms to their clients. diff --git a/apps/www/_customers/pebblely.mdx b/apps/www/_customers/pebblely.mdx index 12764cb4c22..a534df50c95 100644 --- a/apps/www/_customers/pebblely.mdx +++ b/apps/www/_customers/pebblely.mdx @@ -24,6 +24,14 @@ misc: { label: 'Solutions', text: 'Supabase Database, Supabase Auth, Supabase Realtime' }, ] about: 'Pebblely is an AI tool that turns boring product images into beautiful marketing assets in just seconds. The AI creates backgrounds based on the uploaded images and adds shadows or reflections accordingly.' +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['ai', 'media', 'ecommerce'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'Asia' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth', 'realtime'] --- Alfred and SK wanted to apply the innovations of AI to the advertising industry. At the start of 2023, they began building Pebblely: an AI image generation tool that turns boring images into beautiful marketing assets in just seconds. 7 months later, they had over 1 million users. diff --git a/apps/www/_customers/quivr.mdx b/apps/www/_customers/quivr.mdx index ad5f90e1dac..1a46e5af2b9 100644 --- a/apps/www/_customers/quivr.mdx +++ b/apps/www/_customers/quivr.mdx @@ -24,11 +24,19 @@ misc: { label: 'Solutions', text: 'Supabase Vector, Supabase Auth' }, ] about: "Quivr is an open source 'second brain'. It's like a private ChatGPT, personalized with your own data: you upload your documents and you can then search and ask questions using generative AI." +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['ai', 'saas', 'developer-tools'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'Europe' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth', 'storage', 'vector'] --- ## The challenge: Building a second brain -In May of 2023, [Stan Girard's](https://twitter.com/_StanGirard) started building small prototypes that allowed him to “chat with documents”. After 2 weeks of research, he settled on an idea - build a “second brain” where a user could dump all their digital knowledge (audio, URLs, text, and code) into a vector store and query it with GPT4. +In May of 2023, [Stan Girard's](https://twitter.com/_StanGirard) started building small prototypes that allowed him to "chat with documents". After 2 weeks of research, he settled on an idea - build a "second brain" where a user could dump all their digital knowledge (audio, URLs, text, and code) into a vector store and query it with GPT4. He built the first version in a single afternoon, pushed it to GitHub, and then [tweeted about it](https://twitter.com/_StanGirard/status/1657021618571313155?s=20). One viral tweet later, and [Quivr](https://github.com/StanGirard/quivr) was born. diff --git a/apps/www/_customers/replenysh.mdx b/apps/www/_customers/replenysh.mdx index 55b72b5f574..f6d355f6f52 100644 --- a/apps/www/_customers/replenysh.mdx +++ b/apps/www/_customers/replenysh.mdx @@ -25,6 +25,14 @@ stats: ] misc: [{ label: 'Founded', text: Southern California }] about: Replenysh is a digital platform that connects global brands with local communities to recover materials and eliminate the concept of waste. +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['saas', 'ecommerce'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'North America' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth'] --- Replenysh connects brands who want to recover their products and packaging with communities looking to monetize used materials. @@ -55,7 +63,7 @@ Firebase seemed appealing due to its ability to get started quickly, but the tea When the team discovered Supabase on Hacker News they felt excitement. Supabase has a reputation for a smooth developer experience, providing Postgres and Row Level Security as a core feature. -As a bonus, Supabase provided more than just a database, reducing their DevOps overhead. After reading the [Supabase Auth documentation](https://supabase.com/docs/guides/auth/overview) the team determined they can have the functionality they need, like SMS OTP. When they saw [Mobbin are using Supabase at scale](https://supabase.com/case-studies-mobbin), they decided to migrate to Supabase. +As a bonus, Supabase provided more than just a database, reducing their DevOps overhead. After reading the [Supabase Auth documentation](https://supabase.com/docs/guides/auth/overview) the team determined they can have the functionality they need, like SMS OTP. When they saw [Mobbin are using Supabase at scale](https://supabase.com/case-studies-mobbin), they decided to migrate to Supabase. ## Globally recognized brands need quality at scale diff --git a/apps/www/_customers/shotgun.mdx b/apps/www/_customers/shotgun.mdx index e49c04d562c..46eca13058c 100644 --- a/apps/www/_customers/shotgun.mdx +++ b/apps/www/_customers/shotgun.mdx @@ -24,6 +24,14 @@ misc: { label: 'Solutions', text: 'Database' }, ] about: 'Shotgun is a French ticketing platform with both a B2B and B2C side. It starts with a simple tool for creators to create events, distribute tickets, and build faithful fan bases. Once an event is live, users can purchase tickets on Shotgun and also discover new events.' +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['saas', 'media'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'Europe' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database'] --- ## The challenge @@ -52,7 +60,7 @@ With Supabase, they found instant savings on the instance pricing and then addit img="florent-cailhol-shotgun.jpg" caption="Florent Cailhol - Senior Software Architect at Shotgun" > - It was the cost. We thought it might be too good to be true, but there’s no denying that Supabase + It was the cost. We thought it might be too good to be true, but there's no denying that Supabase was better. @@ -66,9 +74,9 @@ The migration itself also led to further performance gains. Helped by our Head o After moving to Supabase, Shotgun found a dependable database that consistently met their needs, demonstrating reliability even when handling one of the biggest events on their platform ever. Their engineering team now has confidence in their database. -And finally, it’s become much easier for Shotgun to onboard new developers with the Supabase dashboard. With SQL, developers can query specific pieces of data, build performance reports, and even do basic operations. +And finally, it's become much easier for Shotgun to onboard new developers with the Supabase dashboard. With SQL, developers can query specific pieces of data, build performance reports, and even do basic operations. -Shotgun’s experience with Supabase can be best summed up by, it’s cheaper, it’s easier, and it works. +Shotgun's experience with Supabase can be best summed up by, it's cheaper, it's easier, and it works. To learn more about how Supabase can help you manage and scale your database, **[reach out to us](https://forms.supabase.com/enterprise)**. +> To learn more about how Supabase can help you manage and scale your database, **[reach out to us](https://forms.supabase.com/enterprise)**. diff --git a/apps/www/_customers/tinloof.mdx b/apps/www/_customers/tinloof.mdx index b92499c8475..ed8280b5ee1 100644 --- a/apps/www/_customers/tinloof.mdx +++ b/apps/www/_customers/tinloof.mdx @@ -22,6 +22,14 @@ misc: { label: 'Solutions', text: 'Database, Auth' }, ] about: "Tinloof, a full-stack development agency, specializes in building modern web applications for various clients. Their projects range from simple web apps to complex systems requiring robust and scalable backends. Tinloof's team, consisting of developers proficient in both frontend and backend technologies, faced a challenge in managing the infrastructure and backend services for their applications." +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['saas', 'developer-tools'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'Europe' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth'] --- ## The challenge @@ -49,7 +57,7 @@ The simplicity of integrating Supabase into projects and its detailed documentat Using Supabase, Tinloof successfully managed to streamline their development, scale their applications with a secure and reliable backend, and deliver a positive user experience. **Streamline Development**\ -The team could focus on building application logic rather than worrying about backend infrastructure. Supabase’s out-of-the-box features saved significant development time. +The team could focus on building application logic rather than worrying about backend infrastructure. Supabase's out-of-the-box features saved significant development time. **Scale Effortlessly**\ During the launch of Pika, Tinloof handled over a million users without major issues, thanks to Supabase's scalable infrastructure. Supabase upgraded instances proactively based on usage metrics. diff --git a/apps/www/_customers/voypost.mdx b/apps/www/_customers/voypost.mdx index 65d4757795a..1f01b8dc67e 100644 --- a/apps/www/_customers/voypost.mdx +++ b/apps/www/_customers/voypost.mdx @@ -22,6 +22,14 @@ misc: { label: 'Solutions', text: 'Database' }, ] about: 'Voypost, a leading software development company, excels in helping startups and small to medium-sized businesses build scalable and high quality applications. One of their standout projects using Supabase is Inkly, an intelligent contract negotiation platform.' +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['saas', 'developer-tools'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'Europe' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database'] --- ## The challenge diff --git a/apps/www/_customers/xendit.mdx b/apps/www/_customers/xendit.mdx index 950785e8984..8926e16e12a 100644 --- a/apps/www/_customers/xendit.mdx +++ b/apps/www/_customers/xendit.mdx @@ -25,6 +25,14 @@ stats: ] misc: [{ label: 'Founded', text: Jakarta Selatan }] about: Xendit is a financial technology company that provides payment solutions and simplifies the payment process for businesses in Indonesia, the Philippines, and Southeast Asia, from SMEs and e-commerce startups to large enterprises. +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['fintech'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'enterprise' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'Asia' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database'] --- ## Challenge diff --git a/apps/www/components/CustomerStories/CustomersFilters.tsx b/apps/www/components/CustomerStories/CustomersFilters.tsx new file mode 100644 index 00000000000..589bf297bc6 --- /dev/null +++ b/apps/www/components/CustomerStories/CustomersFilters.tsx @@ -0,0 +1,220 @@ +import { useRouter } from 'next/router' +import { AnimatePresence, motion } from 'framer-motion' +import { useEffect, useState } from 'react' +import type PostTypes from '~/types/post' +import { + Button, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + cn, +} from 'ui' +import { ChevronDown, X as CloseIcon } from 'lucide-react' +import startCase from 'lodash/startCase' +import { useBreakpoint } from 'common' + +interface Props { + allCustomers: PostTypes[] + setCustomers: (posts: any) => void + industries: { [key: string]: number } + products: { [key: string]: number } +} + +export const useFilters = (initialIndustry: string, initialProduct: string) => { + const router = useRouter() + const [industry, setIndustry] = useState(initialIndustry) + const [product, setProduct] = useState(initialProduct) + + const updateUrlParams = () => { + const params = new URLSearchParams() + if (industry !== 'all') params.set('industry', industry) + if (product !== 'all') params.set('product', product) + router.replace({ pathname: '/customers', query: params.toString() }, undefined, { + shallow: true, + }) + } + + useEffect(() => { + updateUrlParams() + }, [industry, product]) + + return { industry, setIndustry, product, setProduct } +} + +function CustomerFilters({ allCustomers, setCustomers, industries, products }: Props) { + const isMobile = useBreakpoint('sm') + const router = useRouter() + const { + industry: activeIndustry, + setIndustry, + product: activeProduct, + setProduct, + } = useFilters('all', 'all') + + const handlePosts = () => { + setCustomers( + allCustomers.filter((customer: any) => { + const matchesIndustry = + activeIndustry === 'all' || customer.industry?.includes(activeIndustry) + const matchesProduct = activeProduct === 'all' || customer.products?.includes(activeProduct) + return matchesIndustry && matchesProduct + }) + ) + } + + const resetFilters = () => { + handleIndustryFilter('all') + handleProductFilter('all') + } + + useEffect(() => { + handlePosts() + }, [activeIndustry, activeProduct]) + + const handleIndustryFilter = (selectedIndustry: string) => { + if (selectedIndustry === 'all') { + setIndustry('all') + const { industry, ...rest } = router.query + router.push({ + pathname: router.pathname, + query: rest, + }) + } else { + setIndustry(selectedIndustry) + router.push({ + pathname: router.pathname, + query: { ...router.query, industry: selectedIndustry }, + }) + } + } + + const handleProductFilter = (selectedProduct: string) => { + if (selectedProduct === 'all') { + setProduct('all') + const { product, ...rest } = router.query + router.push({ + pathname: router.pathname, + query: rest, + }) + } else { + setProduct(selectedProduct) + router.push({ + pathname: router.pathname, + query: { ...router.query, product: selectedProduct }, + }) + } + } + + return ( +
    + + + + + + + + {Object.entries(industries).map(([industry, count]) => ( + handleIndustryFilter(industry)} + className={cn( + 'flex gap-0.5 items-center justify-between', + (industry === 'all' && !activeIndustry) || industry === activeIndustry + ? 'text-brand-600' + : '' + )} + > + {getIndustryLabel(industry)}{' '} + {count} + + ))} + + + + + + + + {Object.entries(products).map(([product, count]) => ( + handleProductFilter(product)} + className={cn( + 'flex gap-0.5 items-center justify-between', + (product === 'all' && !activeProduct) || product === activeProduct + ? 'text-brand-600' + : '' + )} + > + {getProductLabel(product)}{' '} + {count} + + ))} + + + + + +
    + ) +} + +const getIndustryLabel = (industry: string) => { + switch (industry) { + case 'all': + return 'All Industries' + case 'ai': + return 'AI' + case 'saas': + return 'SaaS' + default: + return startCase(industry.replaceAll('-', ' ')) + } +} + +const getProductLabel = (product: string) => { + switch (product) { + case 'all': + return 'All Products' + case 'functions': + return 'Edge Functions' + default: + return startCase(product.replaceAll('-', ' ')) + } +} + +export default CustomerFilters diff --git a/apps/www/components/Nav/DevelopersDropdown.tsx b/apps/www/components/Nav/DevelopersDropdown.tsx index e3cffcda2f7..db69f35ebc5 100644 --- a/apps/www/components/Nav/DevelopersDropdown.tsx +++ b/apps/www/components/Nav/DevelopersDropdown.tsx @@ -2,7 +2,7 @@ import { ChevronRight } from 'lucide-react' import Link from 'next/link' import { data as DevelopersData } from 'data/Developers' -import blogPosts from '~/.contentlayer/generated/LatestBlogPost/_index.json' assert { type: 'json' } +import blogPosts from '~/.contentlayer/generated/LatestBlogPost/_index.json' with { type: 'json' } type LinkProps = { text: string diff --git a/apps/www/lib/mdx/mdxSerialize.ts b/apps/www/lib/mdx/mdxSerialize.ts index 281e3c8ef34..ed026ace484 100644 --- a/apps/www/lib/mdx/mdxSerialize.ts +++ b/apps/www/lib/mdx/mdxSerialize.ts @@ -3,7 +3,7 @@ import rehypeSlug from 'rehype-slug' import remarkGfm from 'remark-gfm' import { CodeHikeConfig, remarkCodeHike } from '@code-hike/mdx' -import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } +import codeHikeTheme from 'config/code-hike.theme.json' with { type: 'json' } // mdx2 needs self-closing tags. // dragging an image onto a Github discussion creates an diff --git a/apps/www/next.config.mjs b/apps/www/next.config.mjs index 1a1937d239c..106354d8542 100644 --- a/apps/www/next.config.mjs +++ b/apps/www/next.config.mjs @@ -9,7 +9,7 @@ import remotePatterns from './lib/remotePatterns.js' import rewrites from './lib/rewrites.js' import { remarkCodeHike } from '@code-hike/mdx' -import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } +import codeHikeTheme from 'config/code-hike.theme.json' with { type: 'json' } import { withContentlayer } from 'next-contentlayer2' diff --git a/apps/www/package.json b/apps/www/package.json index d1dc36bf051..c5f2ebf91a4 100644 --- a/apps/www/package.json +++ b/apps/www/package.json @@ -40,7 +40,7 @@ "common": "*", "common-tags": "^1.8.2", "config": "*", - "contentlayer2": "0.4.6", + "contentlayer2": "0.5.3", "dat.gui": "^0.7.9", "dayjs": "^1.11.12", "eslint-config-supabase": "*", @@ -52,7 +52,7 @@ "lucide-react": "*", "markdown-toc": "^1.2.0", "next": "^14.2.10", - "next-contentlayer2": "0.4.6", + "next-contentlayer2": "0.5.3", "next-mdx-remote": "^4.4.1", "next-seo": "^6.5.0", "next-themes": "^0.3.0", diff --git a/apps/www/pages/customers.tsx b/apps/www/pages/customers.tsx index 7722b58fc92..d86c0d8e8c3 100644 --- a/apps/www/pages/customers.tsx +++ b/apps/www/pages/customers.tsx @@ -13,24 +13,71 @@ import { motion } from 'framer-motion' import styles from '~/styles/customers.module.css' import Link from 'next/link' import { GlassPanel } from 'ui-patterns/GlassPanel' +import CustomersFilters from '../components/CustomerStories/CustomersFilters' +import { useState } from 'react' +import { Button, cn } from 'ui' export async function getStaticProps() { - const allPostsData = getSortedPosts({ directory: '_customers' }) + const allPostsData: any[] = getSortedPosts({ directory: '_customers' }) const rss = generateRss(allPostsData) // create a rss feed in public directory // rss feed is added via component in render return fs.writeFileSync('./public/customers-rss.xml', rss) + const industries = allPostsData.reduce<{ [key: string]: number }>( + (acc, customer) => { + // Increment the 'all' counter + acc.all = (acc.all || 0) + 1 + + // Increment the counter for each category + customer.industry?.forEach((industry: string) => { + acc[industry] = (acc[industry] || 0) + 1 + }) + + return acc + }, + { all: 0 } + ) + + const products = allPostsData.reduce<{ [key: string]: number }>( + (acc, customer) => { + // Increment the 'all' counter + acc.all = (acc.all || 0) + 1 + + // Increment the counter for each category + customer.supabase_products?.forEach((product: string) => { + acc[product] = (acc[product] || 0) + 1 + }) + + return acc + }, + { all: 0 } + ) + return { props: { blogs: allPostsData, + industries, + products, }, } } function CustomerStoriesPage(props: any) { const { basePath } = useRouter() + const _allCustomers = props.blogs?.map((blog: PostTypes, idx: number) => { + return { + logo: blog.logo, + logoInverse: blog.logo_inverse, + name: blog.name, + title: blog.title, + link: blog.url, + industry: blog.industry, + products: blog.supabase_products, + } + }) + const [customers, setCustomers] = useState(_allCustomers) const meta = { title: 'Customer Stories | Supabase', @@ -39,15 +86,6 @@ function CustomerStoriesPage(props: any) { 'See how Supabase empowers companies of all sizes to accelerate their growth and streamline their work.', } - const caseStudyThumbs = props.blogs?.map((blog: PostTypes, idx: number) => { - return { - logo: blog.logo, - logoInverse: blog.logo_inverse, - title: blog.title, - link: blog.url, - } - }) - return ( <> @@ -81,7 +119,7 @@ function CustomerStoriesPage(props: any) {
    @@ -91,41 +129,53 @@ function CustomerStoriesPage(props: any) { create outstanding products and set new industry standards. -
    - {caseStudyThumbs.map((caseStudy: any, i: number) => ( - - - +
    + {customers?.length ? ( + customers?.map((caseStudy: any, i: number) => ( + + - {caseStudy.description} - - - - ))} + + {caseStudy.description} + + + + )) + ) : ( +
    +

    No customers found

    +
    + )}
    diff --git a/apps/www/pages/partners/integrations/[slug].tsx b/apps/www/pages/partners/integrations/[slug].tsx index 4305346dc30..35a2b3a0fa0 100644 --- a/apps/www/pages/partners/integrations/[slug].tsx +++ b/apps/www/pages/partners/integrations/[slug].tsx @@ -13,7 +13,7 @@ import 'swiper/css' import { Swiper, SwiperSlide } from 'swiper/react' import { useBreakpoint } from 'common' -import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' } +import codeHikeTheme from 'config/code-hike.theme.json' with { type: 'json' } import { Button } from 'ui' import { Admonition } from 'ui-patterns/admonition' import { ExpandableVideo } from 'ui-patterns/ExpandableVideo' diff --git a/apps/www/public/customers-rss.xml b/apps/www/public/customers-rss.xml index 5a170ffd4c6..e450abbfce2 100644 --- a/apps/www/public/customers-rss.xml +++ b/apps/www/public/customers-rss.xml @@ -5,9 +5,16 @@ https://supabase.com Latest news from Supabase en - Tue, 01 Oct 2024 00:00:00 -0700 + Mon, 09 Dec 2024 00:00:00 -0700 + https://supabase.com/customers/e2b + E2B: Accelerating AI-Driven Development with Supabase + https://supabase.com/customers/e2b + Discover how E2B leveraged Supabase to streamline its platform, allowing secure, scalable execution of AI-generated code in the cloud. + Mon, 09 Dec 2024 00:00:00 -0700 + + https://supabase.com/customers/humata Humata Scales with Supabase: Achieving 4X Cost Savings and Enhanced Performance https://supabase.com/customers/humata diff --git a/apps/www/public/images/blog/avatars/vasek-mlejnsky.jpg b/apps/www/public/images/blog/avatars/vasek-mlejnsky.jpg new file mode 100644 index 00000000000..16d213e147e Binary files /dev/null and b/apps/www/public/images/blog/avatars/vasek-mlejnsky.jpg differ diff --git a/apps/www/public/images/blog/dbos/durable-workflow-engine-dark.png b/apps/www/public/images/blog/dbos/durable-workflow-engine-dark.png new file mode 100644 index 00000000000..9ee9c835f9e Binary files /dev/null and b/apps/www/public/images/blog/dbos/durable-workflow-engine-dark.png differ diff --git a/apps/www/public/images/blog/dbos/durable-workflow-engine-dark.svg b/apps/www/public/images/blog/dbos/durable-workflow-engine-dark.svg deleted file mode 100644 index 0f2c0111d6f..00000000000 --- a/apps/www/public/images/blog/dbos/durable-workflow-engine-dark.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/images/blog/dbos/durable-workflow-engine-light.png b/apps/www/public/images/blog/dbos/durable-workflow-engine-light.png new file mode 100644 index 00000000000..f8e51b5baaa Binary files /dev/null and b/apps/www/public/images/blog/dbos/durable-workflow-engine-light.png differ diff --git a/apps/www/public/images/blog/dbos/durable-workflow-engine-light.svg b/apps/www/public/images/blog/dbos/durable-workflow-engine-light.svg deleted file mode 100644 index 492154cdbea..00000000000 --- a/apps/www/public/images/blog/dbos/durable-workflow-engine-light.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/apps/www/public/rss.xml b/apps/www/public/rss.xml index 00d731b2145..9be5e303f27 100644 --- a/apps/www/public/rss.xml +++ b/apps/www/public/rss.xml @@ -5,9 +5,16 @@ https://supabase.com Latest news from Supabase en - Fri, 06 Dec 2024 00:00:00 -0700 + Tue, 10 Dec 2024 00:00:00 -0700 + https://supabase.com/blog/durable-workflows-in-postgres-dbos + Running Durable Workflows in Postgres using DBOS + https://supabase.com/blog/durable-workflows-in-postgres-dbos + Technical deep dive into the new DBOS integration for Supabase + Tue, 10 Dec 2024 00:00:00 -0700 + + https://supabase.com/blog/database-build-v2 database.build v2: Bring-your-own-LLM https://supabase.com/blog/database-build-v2 @@ -44,7 +51,7 @@ https://supabase.com/blog/high-performance-disks - High Performance Disks + High Performance Disk https://supabase.com/blog/high-performance-disks Store up to 60 TB of data with 100x improved durability and 5x more IOPS Thu, 05 Dec 2024 00:00:00 -0700 diff --git a/apps/www/types/post.ts b/apps/www/types/post.ts index 23c2108f55e..d5ab8ad9aaa 100644 --- a/apps/www/types/post.ts +++ b/apps/www/types/post.ts @@ -2,6 +2,7 @@ type PostTypes = { slug?: string type: 'casestudy' | 'blog' | 'event' title: string + name?: string date: string formattedDate?: string coverImage?: string @@ -19,6 +20,10 @@ type PostTypes = { path: string tags?: string[] categories?: string[] + industry?: string[] + supabase_products?: string[] + company_size?: string + region?: string logo?: string logo_inverse?: string hideAuthor?: boolean diff --git a/babel.config.js b/babel.config.js deleted file mode 100644 index eee873213fe..00000000000 --- a/babel.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - presets: [['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-typescript'], -} diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 262f3e2ffd9..71169b52c92 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -371,7 +371,7 @@ services: # Comment out everything below this point if you are using an external Postgres database db: container_name: supabase-db - image: supabase/postgres:15.6.1.139 + image: supabase/postgres:15.6.1.146 healthcheck: test: pg_isready -U postgres -h localhost interval: 5s diff --git a/package-lock.json b/package-lock.json index c5b27f63ac4..5f5d1904645 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ ], "devDependencies": { "eslint": "^8.57.0", + "nanoid": "^3.3.8", "prettier": "^4.0.0-alpha.8", "prettier-plugin-sql-cst": "^0.11.0", "rimraf": "^6.0.0", @@ -51,7 +52,7 @@ "react-wrap-balancer": "^1.1.0", "recharts": "^2.12.7", "rehype-autolink-headings": "^7.1.0", - "rehype-pretty-code": "^0.6.0", + "rehype-pretty-code": "^0.9.0", "rehype-slug": "^6.0.0", "remark": "^14.0.3", "remark-code-import": "^1.2.0", @@ -265,118 +266,6 @@ "node": ">=8.0.0" } }, - "apps/design-system/node_modules/@opentelemetry/api-logs": { - "version": "0.51.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.51.1.tgz", - "integrity": "sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==", - "dependencies": { - "@opentelemetry/api": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "apps/design-system/node_modules/@opentelemetry/exporter-trace-otlp-grpc": { - "version": "0.51.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.51.1.tgz", - "integrity": "sha512-P9+Hkszih95ITvldGZ+kXvj9HpD1QfS+PwooyHK72GYA+Bgm+yUSAsDkUkDms8+s9HW6poxURv3LcjaMuBBpVQ==", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.24.1", - "@opentelemetry/otlp-grpc-exporter-base": "0.51.1", - "@opentelemetry/otlp-transformer": "0.51.1", - "@opentelemetry/resources": "1.24.1", - "@opentelemetry/sdk-trace-base": "1.24.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "apps/design-system/node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.51.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.51.1.tgz", - "integrity": "sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==", - "dependencies": { - "@opentelemetry/core": "1.24.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "apps/design-system/node_modules/@opentelemetry/otlp-grpc-exporter-base": { - "version": "0.51.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.51.1.tgz", - "integrity": "sha512-ZAS+4pq8o7dsugGTwV9s6JMKSxi+guIHdn0acOv0bqj26e9pWDFx5Ky+bI0aY46uR9Y0JyXqY+KAEYM/SO3DFA==", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.24.1", - "@opentelemetry/otlp-exporter-base": "0.51.1", - "protobufjs": "^7.2.3" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "apps/design-system/node_modules/@opentelemetry/otlp-transformer": { - "version": "0.51.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.51.1.tgz", - "integrity": "sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==", - "dependencies": { - "@opentelemetry/api-logs": "0.51.1", - "@opentelemetry/core": "1.24.1", - "@opentelemetry/resources": "1.24.1", - "@opentelemetry/sdk-logs": "0.51.1", - "@opentelemetry/sdk-metrics": "1.24.1", - "@opentelemetry/sdk-trace-base": "1.24.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.9.0" - } - }, - "apps/design-system/node_modules/@opentelemetry/sdk-logs": { - "version": "0.51.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.51.1.tgz", - "integrity": "sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==", - "dependencies": { - "@opentelemetry/core": "1.24.1", - "@opentelemetry/resources": "1.24.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.9.0", - "@opentelemetry/api-logs": ">=0.39.1" - } - }, - "apps/design-system/node_modules/@opentelemetry/sdk-metrics": { - "version": "1.24.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.24.1.tgz", - "integrity": "sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==", - "dependencies": { - "@opentelemetry/core": "1.24.1", - "@opentelemetry/resources": "1.24.1", - "lodash.merge": "^4.6.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.9.0" - } - }, "apps/design-system/node_modules/@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", @@ -458,24 +347,6 @@ "url": "https://opencollective.com/unified" } }, - "apps/design-system/node_modules/memfs": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.9.3.tgz", - "integrity": "sha512-bsYSSnirtYTWi1+OPMFb0M048evMKyUYe0EbtuGQgq6BVQM1g1W8/KIUJCCvjgI/El0j6Q4WsmMiBwLUBSw8LA==", - "dependencies": { - "@jsonjoy.com/json-pack": "^1.0.3", - "@jsonjoy.com/util": "^1.1.2", - "tree-dump": "^1.0.1", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">= 4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - } - }, "apps/design-system/node_modules/micromark-util-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", @@ -1894,10 +1765,6 @@ }, "devDependencies": { "@aws-sdk/client-secrets-manager": "^3.410.0", - "@storybook/addon-essentials": "^7.5.3", - "@storybook/addon-interactions": "^7.5.3", - "@storybook/addon-links": "^7.5.3", - "@storybook/react": "^7.5.3", "@supabase/postgres-meta": "^0.64.4", "@testing-library/dom": "^9.3.3", "@testing-library/react": "^14.0.0", @@ -1937,7 +1804,6 @@ "next-router-mock": "^0.9.13", "postcss": "^8.4.31", "prettier": "^4.0.0-alpha.8", - "storybook-dark-mode": "^3.0.1", "tailwindcss": "^3.4.1", "typescript": "~5.5.0" } @@ -1950,22 +1816,6 @@ "react": ">= 16" } }, - "apps/studio/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, "apps/studio/node_modules/@opentelemetry/api": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", @@ -1975,757 +1825,6 @@ "node": ">=8.0.0" } }, - "apps/studio/node_modules/@storybook/addon-backgrounds": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-7.6.17.tgz", - "integrity": "sha512-7dize7x8+37PH77kmt69b0xSaeDqOcZ4fpzW6+hk53hIaCVU26eGs4+j+743Xva31eOgZWNLupUhOpUDc6SqZw==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-essentials": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-7.6.17.tgz", - "integrity": "sha512-qlSpamxuYfT2taF953nC9QijGF2pSbg1ewMNpdwLTj16PTZvR/d8NCDMTJujI1bDwM2m18u8Yc43ibh5LEmxCw==", - "dev": true, - "dependencies": { - "@storybook/addon-actions": "7.6.17", - "@storybook/addon-backgrounds": "7.6.17", - "@storybook/addon-controls": "7.6.17", - "@storybook/addon-docs": "7.6.17", - "@storybook/addon-highlight": "7.6.17", - "@storybook/addon-measure": "7.6.17", - "@storybook/addon-outline": "7.6.17", - "@storybook/addon-toolbars": "7.6.17", - "@storybook/addon-viewport": "7.6.17", - "@storybook/core-common": "7.6.17", - "@storybook/manager-api": "7.6.17", - "@storybook/node-logger": "7.6.17", - "@storybook/preview-api": "7.6.17", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "apps/studio/node_modules/@storybook/addon-essentials/node_modules/@storybook/channels": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.17.tgz", - "integrity": "sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-essentials/node_modules/@storybook/client-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", - "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-essentials/node_modules/@storybook/core-common": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.17.tgz", - "integrity": "sha512-me2TP3Q9/qzqCLoDHUSsUF+VS1MHxfHbTVF6vAz0D/COTxzsxLpu9TxTbzJoBCxse6XRb6wWI1RgF1mIcjic7g==", - "dev": true, - "dependencies": { - "@storybook/core-events": "7.6.17", - "@storybook/node-logger": "7.6.17", - "@storybook/types": "7.6.17", - "@types/find-cache-dir": "^3.2.1", - "@types/node": "^18.0.0", - "@types/node-fetch": "^2.6.4", - "@types/pretty-hrtime": "^1.0.0", - "chalk": "^4.1.0", - "esbuild": "^0.18.0", - "esbuild-register": "^3.5.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-essentials/node_modules/@storybook/core-events": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.17.tgz", - "integrity": "sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-essentials/node_modules/@storybook/node-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.17.tgz", - "integrity": "sha512-w59MQuXhhUNrUVmVkXhMwIg2nvFWjdDczLTwYLorhfsE36CWeUOY5QCZWQy0Qf/h+jz8Uo7Evy64qn18v9C4wA==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-essentials/node_modules/@storybook/preview-api": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.17.tgz", - "integrity": "sha512-wLfDdI9RWo1f2zzFe54yRhg+2YWyxLZvqdZnSQ45mTs4/7xXV5Wfbv3QNTtcdw8tT3U5KRTrN1mTfTCiRJc0Kw==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.17", - "@types/qs": "^6.9.5", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "qs": "^6.10.0", - "synchronous-promise": "^2.0.15", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-essentials/node_modules/@storybook/types": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.17.tgz", - "integrity": "sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-highlight": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.6.17.tgz", - "integrity": "sha512-R1yBPUUqGn+60aJakn8q+5Zt34E/gU3n3VmgPdryP0LJUdZ5q1/RZShoVDV+yYQ40htMH6oaCv3OyyPzFAGJ6A==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-interactions": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-7.6.17.tgz", - "integrity": "sha512-6zlX+RDQ1PlA6fp7C+hun8t7h2RXfCGs5dGrhEenp2lqnR/rYuUJRC0tmKpkZBb8kZVcbSChzkB/JYkBjBCzpQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.17", - "jest-mock": "^27.0.6", - "polished": "^4.2.2", - "ts-dedent": "^2.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-interactions/node_modules/@storybook/channels": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.17.tgz", - "integrity": "sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-interactions/node_modules/@storybook/client-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", - "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-interactions/node_modules/@storybook/core-events": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.17.tgz", - "integrity": "sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-interactions/node_modules/@storybook/types": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.17.tgz", - "integrity": "sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-links": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-7.6.17.tgz", - "integrity": "sha512-iFUwKObRn0EKI0zMETsil2p9a/81rCuSMEWECsi+khkCAs1FUnD2cT6Ag5ydcNcBXsdtdfDJdtXQrkw+TSoStQ==", - "dev": true, - "dependencies": { - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - } - } - }, - "apps/studio/node_modules/@storybook/addon-measure": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-7.6.17.tgz", - "integrity": "sha512-O5vnHZNkduvZ95jf1UssbOl6ivIxzl5tv+4EpScPYId7w700bxWsJH+QX7ip6KlrCf2o3iUhmPe8bm05ghG2KA==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-outline": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-7.6.17.tgz", - "integrity": "sha512-9o9JXDsYjNaDgz/cY5+jv694+aik/1aiRGGvsCv68e1p/ob0glkGKav4lnJe2VJqD+gCmaARoD8GOJlhoQl8JQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-toolbars": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.6.17.tgz", - "integrity": "sha512-UMrchbUHiyWrh6WuGnpy34Jqzkx/63B+MSgb3CW7YsQaXz64kE0Rol0TNSznnB+mYXplcqH+ndI4r4kFsmgwDg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/addon-viewport": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-7.6.17.tgz", - "integrity": "sha512-sA0QCcf4QAMixWvn8uvRYPfkKCSl6JajJaAspoPqXSxHEpK7uwOlpg3kqFU5XJJPXD0X957M+ONgNvBzYqSpEw==", - "dev": true, - "dependencies": { - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/channels": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-8.0.4.tgz", - "integrity": "sha512-haKV+8RbiSzLjicowUfc7h2fTClZHX/nz9SRUecf4IEZUEu2T78OgM/TzqZvL7rA3+/fKqp5iI+3PN3OA75Sdg==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "8.0.4", - "@storybook/core-events": "8.0.4", - "@storybook/global": "^5.0.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/client-logger": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-8.0.4.tgz", - "integrity": "sha512-2SeEg3PT/d0l/+EAVtyj9hmMLTyTPp+bRBSzxYouBjtJPM1jrdKpFagj1o3uBRovwWm9SIVX6/ZsoRC33PEV1g==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/core-common": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-8.0.4.tgz", - "integrity": "sha512-dzFRLm5FxUa2EFE6Rx/KLDTJNLBIp1S2/+Q1K+rG8V+CLvewCc2Cd486rStZqSXEKI7vDnsRs/aMla+N0X/++Q==", - "dev": true, - "dependencies": { - "@storybook/core-events": "8.0.4", - "@storybook/csf-tools": "8.0.4", - "@storybook/node-logger": "8.0.4", - "@storybook/types": "8.0.4", - "@yarnpkg/fslib": "2.10.3", - "@yarnpkg/libzip": "2.3.0", - "chalk": "^4.1.0", - "cross-spawn": "^7.0.3", - "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0", - "esbuild-register": "^3.5.0", - "execa": "^5.0.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "semver": "^7.3.7", - "tempy": "^1.0.1", - "tiny-invariant": "^1.3.1", - "ts-dedent": "^2.0.0", - "util": "^0.12.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/core-common/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "apps/studio/node_modules/@storybook/core-events": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-8.0.4.tgz", - "integrity": "sha512-1FgLacIGi9i6/fyxw7ZJDC621RK47IMaA3keH4lc11ASRzCSwJ4YOrXjBFjfPc79EF2BuX72DDJNbhj6ynfF3g==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/csf-tools": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-8.0.4.tgz", - "integrity": "sha512-dMSZxWnXBhmXGOZZOAJ4DKZRCYdA0HaqqZ4/eF9MLLsI+qvW4EklcpjVY6bsIzACgubRWtRZkTpxTnjExi/N1A==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "@storybook/csf": "^0.1.2", - "@storybook/types": "8.0.4", - "fs-extra": "^11.1.0", - "recast": "^0.23.5", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/docs-tools": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-8.0.4.tgz", - "integrity": "sha512-PONfG8j/AOHi79NbEkneFRZIscrShbA0sgA+62zeejH4r9+fuIkIKtLnKcAxvr8Bm6uo9aSQbISJZUcBG42WhQ==", - "dev": true, - "dependencies": { - "@storybook/core-common": "8.0.4", - "@storybook/preview-api": "8.0.4", - "@storybook/types": "8.0.4", - "@types/doctrine": "^0.0.3", - "assert": "^2.1.0", - "doctrine": "^3.0.0", - "lodash": "^4.17.21" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/manager-api": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.6.17.tgz", - "integrity": "sha512-IJIV1Yc6yw1dhCY4tReHCfBnUKDqEBnMyHp3mbXpsaHxnxJZrXO45WjRAZIKlQKhl/Ge1CrnznmHRCmYgqmrWg==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/router": "7.6.17", - "@storybook/theming": "7.6.17", - "@storybook/types": "7.6.17", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "store2": "^2.14.2", - "telejson": "^7.2.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/manager-api/node_modules/@storybook/channels": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.17.tgz", - "integrity": "sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/manager-api/node_modules/@storybook/client-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", - "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/manager-api/node_modules/@storybook/core-events": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.17.tgz", - "integrity": "sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/manager-api/node_modules/@storybook/types": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.17.tgz", - "integrity": "sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/node-logger": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-8.0.4.tgz", - "integrity": "sha512-cALLHuX53vLQsoJamGRlquh2pfhPq9copXou2JTmFT6mrCcipo77SzhBDfeeuhaGv6vUWPfmGjPBEHXWGPe4+g==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/preview-api": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.0.4.tgz", - "integrity": "sha512-uZCgZ/7BZkFTNudCBWx3YPFVdReMQSZJj9EfQVhQaPmfGORHGMvZMRsQXl0ONhPy7zDD4rVQxu5dSKWmIiYoWQ==", - "dev": true, - "dependencies": { - "@storybook/channels": "8.0.4", - "@storybook/client-logger": "8.0.4", - "@storybook/core-events": "8.0.4", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/types": "8.0.4", - "@types/qs": "^6.9.5", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "qs": "^6.10.0", - "tiny-invariant": "^1.3.1", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/react": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@storybook/react/-/react-8.0.4.tgz", - "integrity": "sha512-p4wQSJIhG48UD2fZ6tFDT9zaqrVnvZxjV18+VjSi3dez/pDoEMJ3SWZWcmeDenKwvvk+SPdRH7k5mUHW1Rh0xg==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "8.0.4", - "@storybook/docs-tools": "8.0.4", - "@storybook/global": "^5.0.0", - "@storybook/preview-api": "8.0.4", - "@storybook/react-dom-shim": "8.0.4", - "@storybook/types": "8.0.4", - "@types/escodegen": "^0.0.6", - "@types/estree": "^0.0.51", - "@types/node": "^18.0.0", - "acorn": "^7.4.1", - "acorn-jsx": "^5.3.1", - "acorn-walk": "^7.2.0", - "escodegen": "^2.1.0", - "html-tags": "^3.1.0", - "lodash": "^4.17.21", - "prop-types": "^15.7.2", - "react-element-to-jsx-string": "^15.0.0", - "semver": "^7.3.7", - "ts-dedent": "^2.0.0", - "type-fest": "~2.19", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", - "typescript": ">= 4.2.x" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "apps/studio/node_modules/@storybook/react-dom-shim": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.0.4.tgz", - "integrity": "sha512-H8bci23e+G40WsdYPuPrhAjCeeXypXuAV6mTVvLHGKH+Yb+3wiB1weaXrot/TgzPbkDNybuhTI3Qm48FPLt0bw==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "apps/studio/node_modules/@storybook/react/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "apps/studio/node_modules/@storybook/router": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.6.17.tgz", - "integrity": "sha512-GnyC0j6Wi5hT4qRhSyT8NPtJfGmf82uZw97LQRWeyYu5gWEshUdM7aj40XlNiScd5cZDp0owO1idduVF2k2l2A==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.17", - "memoizerific": "^1.11.3", - "qs": "^6.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/router/node_modules/@storybook/client-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", - "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/theming": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.17.tgz", - "integrity": "sha512-ZbaBt3KAbmBtfjNqgMY7wPMBshhSJlhodyMNQypv+95xLD/R+Az6aBYbpVAOygLaUQaQk4ar7H/Ww6lFIoiFbA==", - "dev": true, - "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.17", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "apps/studio/node_modules/@storybook/theming/node_modules/@storybook/client-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", - "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "apps/studio/node_modules/@storybook/types": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-8.0.4.tgz", - "integrity": "sha512-OO7QY+qZFCYkItDUBACtIV32p75O7sNziAiyS1V2Oxgo7Ln7fwZwr3mJcA1ruBed6ZcrW3c87k7Xs40T2zAWcg==", - "dev": true, - "dependencies": { - "@storybook/channels": "8.0.4", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, "apps/studio/node_modules/@tanstack/query-core": { "version": "4.35.7", "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.35.7.tgz", @@ -2780,51 +1879,6 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "apps/studio/node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true - }, - "apps/studio/node_modules/@types/node": { - "version": "18.19.26", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.26.tgz", - "integrity": "sha512-+wiMJsIwLOYCvUqSdKTrfkS8mpTp+MPINe6+Np4TAGFWWRWiBQ5kSq9nZGCSPkzx9mvT+uEukzpX4MOSCydcvw==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "apps/studio/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "apps/studio/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "apps/studio/node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "apps/studio/node_modules/ai": { "version": "3.4.33", "resolved": "https://registry.npmjs.org/ai/-/ai-3.4.33.tgz", @@ -2895,41 +1949,10 @@ } } }, - "apps/studio/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "apps/studio/node_modules/argparse": { "version": "2.0.1", "license": "Python-2.0" }, - "apps/studio/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "apps/studio/node_modules/eventsource-parser": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-1.1.2.tgz", @@ -2963,40 +1986,6 @@ } } }, - "apps/studio/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "apps/studio/node_modules/jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "dev": true, - "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "apps/studio/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "apps/studio/node_modules/mime-db": { "version": "1.53.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", @@ -3045,24 +2034,6 @@ "svelte": "^4.0.0 || ^5.0.0-next.0" } }, - "apps/studio/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "apps/studio/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "apps/studio/node_modules/zustand": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.1.tgz", @@ -3120,7 +2091,7 @@ "common": "*", "common-tags": "^1.8.2", "config": "*", - "contentlayer2": "0.4.6", + "contentlayer2": "0.5.3", "dat.gui": "^0.7.9", "dayjs": "^1.11.12", "eslint-config-supabase": "*", @@ -3132,7 +2103,7 @@ "lucide-react": "*", "markdown-toc": "^1.2.0", "next": "^14.2.10", - "next-contentlayer2": "0.4.6", + "next-contentlayer2": "0.5.3", "next-mdx-remote": "^4.4.1", "next-seo": "^6.5.0", "next-themes": "^0.3.0", @@ -3184,249 +2155,6 @@ "tsconfig": "*" } }, - "apps/www/node_modules/@contentlayer2/cli": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@contentlayer2/cli/-/cli-0.4.3.tgz", - "integrity": "sha512-ZJ+Iiu2rVI50x60XoqnrsO/Q8eqFX5AlP1L0U/3ygaAas3tnOqTzQZ1UsxYQMpJzcLok24ddlhKfQKbCMUJPiQ==", - "dependencies": { - "@contentlayer2/core": "0.4.3", - "@contentlayer2/utils": "0.4.3", - "clipanion": "^3.2.1", - "typanion": "^3.12.1" - } - }, - "apps/www/node_modules/@contentlayer2/client": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@contentlayer2/client/-/client-0.4.3.tgz", - "integrity": "sha512-Znz6zW0ShVRq3OCQ6SwsfPQ1Wn28dQpiL33r53OjWuiirXhaSdam4MXRjYSFzCq5JK0kY6Hx0CF0fk3oVbsiVQ==", - "dependencies": { - "@contentlayer2/core": "0.4.3" - } - }, - "apps/www/node_modules/@contentlayer2/core": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@contentlayer2/core/-/core-0.4.3.tgz", - "integrity": "sha512-jNagCFHLB8u/qfjoAI+JUuz3jf2hxYFYusgfh+oSq9+jHala4bbLEJOhJmmaY1Dmv2AV6BI7W8Jjy6WIu5g5Yg==", - "dependencies": { - "@contentlayer2/utils": "0.4.3", - "camel-case": "^4.1.2", - "comment-json": "^4.2.3", - "esbuild": "0.17.x || 0.18.x || 0.19.x || 0.20.x", - "gray-matter": "^4.0.3", - "mdx-bundler": "^10.0.2", - "rehype-stringify": "^10.0.0", - "remark-frontmatter": "^5.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.1.0", - "source-map-support": "^0.5.21", - "type-fest": "^4.10.0", - "unified": "^11.0.4" - }, - "peerDependencies": { - "esbuild": "0.17.x || 0.18.x || 0.19.x || 0.20.x", - "markdown-wasm": "1.x" - }, - "peerDependenciesMeta": { - "esbuild": { - "optional": true - }, - "markdown-wasm": { - "optional": true - } - } - }, - "apps/www/node_modules/@contentlayer2/source-files": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@contentlayer2/source-files/-/source-files-0.4.3.tgz", - "integrity": "sha512-NaLkK9iJcjS7xdqbtEJElEXhdEXv47yj4Ju2TCiZU7/8veQQ67QwtTDesyRMSw4mBt5+hxiAcqvf0hKaHh6lxw==", - "dependencies": { - "@contentlayer2/core": "0.4.3", - "@contentlayer2/utils": "0.4.3", - "chokidar": "^3.5.3", - "fast-glob": "^3.2.12", - "gray-matter": "^4.0.3", - "imagescript": "^1.2.16", - "micromatch": "^4.0.5", - "ts-pattern": "^5.0.6", - "unified": "^11.0.4", - "yaml": "^2.3.1", - "zod": "^3.22.4" - } - }, - "apps/www/node_modules/@contentlayer2/source-remote-files": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@contentlayer2/source-remote-files/-/source-remote-files-0.4.3.tgz", - "integrity": "sha512-uow1teWiAJA0WNbqqjEz4FfLPQF6BpdhRfF8JHgX7r8jzqChB5zS8bXX9xJDGhn41oEqR/FDDI1Pn7564H8lsA==", - "dependencies": { - "@contentlayer2/core": "0.4.3", - "@contentlayer2/source-files": "0.4.3", - "@contentlayer2/utils": "0.4.3" - } - }, - "apps/www/node_modules/@contentlayer2/utils": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@contentlayer2/utils/-/utils-0.4.3.tgz", - "integrity": "sha512-E360wNhvGemU1X+uhIwkzA5v+b8U5Q+Gps50uIDAVW6TWV9boUgAGPspSXXcFAIYG+H4z2eV3jTZjFCwyXGMxg==", - "dependencies": { - "@effect-ts/core": "^0.60.5", - "@effect-ts/otel": "^0.15.1", - "@effect-ts/otel-sdk-trace-node": "^0.15.1", - "@js-temporal/polyfill": "^0.4.4", - "@opentelemetry/api": "^1.8.0", - "@opentelemetry/core": "^1.24.0", - "@opentelemetry/exporter-trace-otlp-grpc": "^0.51.0", - "@opentelemetry/resources": "^1.21.0", - "@opentelemetry/sdk-trace-base": "^1.21.0", - "@opentelemetry/sdk-trace-node": "^1.21.0", - "@opentelemetry/semantic-conventions": "^1.21.0", - "chokidar": "^3.5.3", - "hash-wasm": "^4.11.0", - "inflection": "^3.0.0", - "memfs": "^4.8.2", - "oo-ascii-tree": "^1.94.0", - "ts-pattern": "^5.0.6", - "type-fest": "^4.10.0" - }, - "peerDependenciesMeta": { - "@effect-ts/core": { - "optional": true - }, - "@effect-ts/otel": { - "optional": true - }, - "@effect-ts/otel-node": { - "optional": true - } - } - }, - "apps/www/node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "engines": { - "node": ">=8.0.0" - } - }, - "apps/www/node_modules/@opentelemetry/api-logs": { - "version": "0.51.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.51.1.tgz", - "integrity": "sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==", - "dependencies": { - "@opentelemetry/api": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "apps/www/node_modules/@opentelemetry/exporter-trace-otlp-grpc": { - "version": "0.51.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.51.1.tgz", - "integrity": "sha512-P9+Hkszih95ITvldGZ+kXvj9HpD1QfS+PwooyHK72GYA+Bgm+yUSAsDkUkDms8+s9HW6poxURv3LcjaMuBBpVQ==", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.24.1", - "@opentelemetry/otlp-grpc-exporter-base": "0.51.1", - "@opentelemetry/otlp-transformer": "0.51.1", - "@opentelemetry/resources": "1.24.1", - "@opentelemetry/sdk-trace-base": "1.24.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "apps/www/node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.51.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.51.1.tgz", - "integrity": "sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==", - "dependencies": { - "@opentelemetry/core": "1.24.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "apps/www/node_modules/@opentelemetry/otlp-grpc-exporter-base": { - "version": "0.51.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.51.1.tgz", - "integrity": "sha512-ZAS+4pq8o7dsugGTwV9s6JMKSxi+guIHdn0acOv0bqj26e9pWDFx5Ky+bI0aY46uR9Y0JyXqY+KAEYM/SO3DFA==", - "dependencies": { - "@grpc/grpc-js": "^1.7.1", - "@opentelemetry/core": "1.24.1", - "@opentelemetry/otlp-exporter-base": "0.51.1", - "protobufjs": "^7.2.3" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.0.0" - } - }, - "apps/www/node_modules/@opentelemetry/otlp-transformer": { - "version": "0.51.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.51.1.tgz", - "integrity": "sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==", - "dependencies": { - "@opentelemetry/api-logs": "0.51.1", - "@opentelemetry/core": "1.24.1", - "@opentelemetry/resources": "1.24.1", - "@opentelemetry/sdk-logs": "0.51.1", - "@opentelemetry/sdk-metrics": "1.24.1", - "@opentelemetry/sdk-trace-base": "1.24.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.9.0" - } - }, - "apps/www/node_modules/@opentelemetry/sdk-logs": { - "version": "0.51.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.51.1.tgz", - "integrity": "sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==", - "dependencies": { - "@opentelemetry/core": "1.24.1", - "@opentelemetry/resources": "1.24.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.9.0", - "@opentelemetry/api-logs": ">=0.39.1" - } - }, - "apps/www/node_modules/@opentelemetry/sdk-metrics": { - "version": "1.24.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.24.1.tgz", - "integrity": "sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==", - "dependencies": { - "@opentelemetry/core": "1.24.1", - "@opentelemetry/resources": "1.24.1", - "lodash.merge": "^4.6.2" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.9.0" - } - }, - "apps/www/node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "dependencies": { - "@types/unist": "*" - } - }, "apps/www/node_modules/@types/mdx-js__react": { "version": "1.5.6", "dev": true, @@ -3435,31 +2163,6 @@ "@types/react": "*" } }, - "apps/www/node_modules/@types/unist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", - "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" - }, - "apps/www/node_modules/contentlayer2": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/contentlayer2/-/contentlayer2-0.4.6.tgz", - "integrity": "sha512-EhdabpVsn8u3EkoovGrLB/sIxWUlVJGNiYal9rZn0XJRjIyncGrhz9EJ9gn+z3cRHYUdHCuCMLW/ev6isgKXYw==", - "hasInstallScript": true, - "dependencies": { - "@contentlayer2/cli": "0.4.3", - "@contentlayer2/client": "0.4.3", - "@contentlayer2/core": "0.4.3", - "@contentlayer2/source-files": "0.4.3", - "@contentlayer2/source-remote-files": "0.4.3", - "@contentlayer2/utils": "0.4.3" - }, - "bin": { - "contentlayer2": "bin/cli.cjs" - }, - "engines": { - "node": ">=14.18" - } - }, "apps/www/node_modules/framer-motion": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.0.3.tgz", @@ -3500,140 +2203,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "apps/www/node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "apps/www/node_modules/memfs": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.9.3.tgz", - "integrity": "sha512-bsYSSnirtYTWi1+OPMFb0M048evMKyUYe0EbtuGQgq6BVQM1g1W8/KIUJCCvjgI/El0j6Q4WsmMiBwLUBSw8LA==", - "dependencies": { - "@jsonjoy.com/json-pack": "^1.0.3", - "@jsonjoy.com/util": "^1.1.2", - "tree-dump": "^1.0.1", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">= 4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - } - }, - "apps/www/node_modules/micromark-util-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", - "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "apps/www/node_modules/micromark-util-sanitize-uri": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", - "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "apps/www/node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "apps/www/node_modules/next-contentlayer2": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/next-contentlayer2/-/next-contentlayer2-0.4.6.tgz", - "integrity": "sha512-r1g/zCl3dXFfMlMI87n2+V90Jz7inMrfyRzmMSQdDfCBy3uexnsoM43wPbklza2teyd/pdfUT0I0gw/IE+YA4Q==", - "dependencies": { - "@contentlayer2/core": "0.4.3", - "@contentlayer2/utils": "0.4.3" - }, - "peerDependencies": { - "contentlayer2": "0.4.6", - "next": "^12 || ^13 || ^14", - "react": "*", - "react-dom": "*" - } - }, - "apps/www/node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "apps/www/node_modules/remark-rehype": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz", - "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "apps/www/node_modules/slash": { "version": "4.0.0", "license": "MIT", @@ -3644,96 +2213,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "apps/www/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "apps/www/node_modules/type-fest": { - "version": "4.20.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.20.1.tgz", - "integrity": "sha512-R6wDsVsoS9xYOpy8vgeBlqpdOyzJ12HNfQhC/aAKWM3YoCV9TtunJzh/QpkMgeDhkoynDcw5f1y+qF9yc/HHyg==", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "apps/www/node_modules/unified": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", - "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "apps/www/node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "apps/www/node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "apps/www/node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "apps/www/node_modules/vfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", - "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "license": "MIT", @@ -3806,24 +2285,6 @@ "node": ">=14.18" } }, - "node_modules/@ai-sdk/provider-utils/node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/@ai-sdk/react": { "version": "0.0.70", "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-0.0.70.tgz", @@ -5134,20 +3595,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.22.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-import-assertions": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", @@ -5585,21 +4032,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.22.5", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-flow": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-transform-for-of": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", @@ -5956,55 +4388,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.7.tgz", - "integrity": "sha512-H/Snz9PFxKsS1JLI4dJLtnJgCJRoo0AUm3chP6NYr+9En1JMKloheEiLIhlp5MDVznWo+H3AAC1Mc8lmUEpsgg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz", - "integrity": "sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.24.7.tgz", - "integrity": "sha512-QG9EnzoGn+Qar7rxuW+ZOsbWOt56FvvI93xInqsZDC5fsekx1AlIO4KIJ5M+D0p0SqSH156EpmZyXq630B8OlQ==", - "dev": true, - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-transform-react-jsx-self": { "version": "7.24.5", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.24.5.tgz", @@ -6033,22 +4416,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.7.tgz", - "integrity": "sha512-PLgBVk3fzbmEjBJ/u8kFzOqS9tUeDjiaWud/rRym/yjCo/M9cASPlnrd2ZmmZpQT40fOOrvR8jh+n8jikrOhNA==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-transform-regenerator": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", @@ -6156,24 +4523,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.7.tgz", - "integrity": "sha512-iLD3UNkgx2n/HrjBesVbYX6j0yqn/sJktvbtKKgcaLIQ4bTTQ8obAypc1VpyHPD2y4Phh9zHOaAt8e/L14wCpw==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/plugin-syntax-typescript": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", @@ -6332,22 +4681,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-flow": { - "version": "7.22.15", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-transform-flow-strip-types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/preset-modules": { "version": "0.1.6-no-external-plugins", "dev": true, @@ -6361,45 +4694,6 @@ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/preset-react": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.7.tgz", - "integrity": "sha512-AAH4lEkpmzFWrGVlHaxJB7RLH21uPQ9+He+eFLWHmF9IuFQVugz8eAsamaW0DXRrTfco5zj1wWtpdcXJUOfsag==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-transform-react-display-name": "^7.24.7", - "@babel/plugin-transform-react-jsx": "^7.24.7", - "@babel/plugin-transform-react-jsx-development": "^7.24.7", - "@babel/plugin-transform-react-pure-annotations": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.7.tgz", - "integrity": "sha512-SyXRe3OdWwIwalxDg5UtJnJQO+YPcTfwiIY2B0Xlddh9o7jpWLvv8X1RthIeDOxQ+O1ML5BLPCONToObyVQVuQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-option": "^7.24.7", - "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/plugin-transform-modules-commonjs": "^7.24.7", - "@babel/plugin-transform-typescript": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/regjsgen": { "version": "0.8.0", "dev": true, @@ -6462,11 +4756,6 @@ "node": ">=6.9.0" } }, - "node_modules/@base2/pretty-print-object": { - "version": "1.0.1", - "dev": true, - "license": "BSD-2-Clause" - }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", "dev": true, @@ -6550,6 +4839,391 @@ "w3c-keyname": "^2.2.4" } }, + "node_modules/@contentlayer2/cli": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@contentlayer2/cli/-/cli-0.5.3.tgz", + "integrity": "sha512-8xO+piFSNVq5Ad2P3D30nM0BzQh1qQ0Q4kIx2otlLhYe3tdeuf3TCB3nFZTgfOJESnZABxqy6XTcfpmeAfNd/Q==", + "license": "MIT", + "dependencies": { + "@contentlayer2/core": "0.5.3", + "@contentlayer2/utils": "0.5.3", + "clipanion": "^3.2.1", + "typanion": "^3.12.1" + } + }, + "node_modules/@contentlayer2/client": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@contentlayer2/client/-/client-0.5.3.tgz", + "integrity": "sha512-ZlrPpNhK+jNF00f20G6MIlELiEnVjUPuyjXeRHNsq582DzqcdgE/Etqa5O5zJ9/w3fhvyj5N1h7fP0MLFoswPw==", + "license": "MIT", + "dependencies": { + "@contentlayer2/core": "0.5.3" + } + }, + "node_modules/@contentlayer2/core": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@contentlayer2/core/-/core-0.5.3.tgz", + "integrity": "sha512-qGDryuO+6Uw0U8JZ9cdrxzieEJkkwuTuT+soycnYFShuvqofb+wkpktnsaTn4wUXLd5ut/ngPrH/9w/IbOlF2w==", + "license": "MIT", + "dependencies": { + "@contentlayer2/utils": "0.5.3", + "camel-case": "^4.1.2", + "comment-json": "^4.2.3", + "esbuild": ">=0.17", + "gray-matter": "^4.0.3", + "mdx-bundler": "^10.0.2", + "rehype-stringify": "^10.0.0", + "remark-frontmatter": "^5.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.1.0", + "source-map-support": "^0.5.21", + "type-fest": "^4.10.0", + "unified": "^11.0.4" + }, + "peerDependencies": { + "esbuild": ">=0.17", + "markdown-wasm": "1.x" + }, + "peerDependenciesMeta": { + "esbuild": { + "optional": true + }, + "markdown-wasm": { + "optional": true + } + } + }, + "node_modules/@contentlayer2/core/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==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@contentlayer2/core/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@contentlayer2/core/node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@contentlayer2/core/node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/@contentlayer2/core/node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/@contentlayer2/core/node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/@contentlayer2/core/node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@contentlayer2/core/node_modules/remark-rehype": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz", + "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@contentlayer2/core/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/@contentlayer2/core/node_modules/type-fest": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.30.0.tgz", + "integrity": "sha512-G6zXWS1dLj6eagy6sVhOMQiLtJdxQBHIA9Z6HFUNLOlr6MFOgzV8wvmidtPONfPtEUv0uZsy77XJNzTAfwPDaA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@contentlayer2/core/node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@contentlayer2/core/node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@contentlayer2/core/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==", + "license": "MIT", + "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/@contentlayer2/core/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==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@contentlayer2/source-files": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@contentlayer2/source-files/-/source-files-0.5.3.tgz", + "integrity": "sha512-kESg2zAJKDQio5rCILpFPhsWklX5xmMdlJa9KSj5UdXMd7/gY+q0GiVqZ2PAmZi5+hG73pVihg9ieC2TEjLSIQ==", + "license": "MIT", + "dependencies": { + "@contentlayer2/core": "0.5.3", + "@contentlayer2/utils": "0.5.3", + "chokidar": "^3.5.3", + "fast-glob": "^3.2.12", + "gray-matter": "^4.0.3", + "imagescript": "^1.2.16", + "micromatch": "^4.0.5", + "ts-pattern": "^5.0.6", + "unified": "^11.0.4", + "yaml": "^2.3.1", + "zod": "^3.22.4" + } + }, + "node_modules/@contentlayer2/source-files/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@contentlayer2/source-files/node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@contentlayer2/source-files/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==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/@contentlayer2/source-remote-files": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@contentlayer2/source-remote-files/-/source-remote-files-0.5.3.tgz", + "integrity": "sha512-0dxoLfS7EbVR3Xap4MPSmzLvQCGrMwtXVIA4ZmRHLOGzG+vWpaY9BtnCACb/Om4tUQBsCA9oduBjNqRu4Sx+gQ==", + "dependencies": { + "@contentlayer2/core": "0.5.3", + "@contentlayer2/source-files": "0.5.3", + "@contentlayer2/utils": "0.5.3" + } + }, + "node_modules/@contentlayer2/utils": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@contentlayer2/utils/-/utils-0.5.3.tgz", + "integrity": "sha512-2nTFhZ7xnOevoZQnGP6BSIkTCpNos1PkGJ894gMYSmRMaMkyv+9VE9adoa0sywIA1y01iLU8niqcxJiuCQ1OkQ==", + "license": "MIT", + "dependencies": { + "@effect-ts/core": "^0.60.5", + "@effect-ts/otel": "^0.15.1", + "@effect-ts/otel-sdk-trace-node": "^0.15.1", + "@js-temporal/polyfill": "^0.4.4", + "@opentelemetry/api": "^1.8.0", + "@opentelemetry/core": "^1.24.0", + "@opentelemetry/exporter-trace-otlp-grpc": "^0.51.0", + "@opentelemetry/resources": "^1.21.0", + "@opentelemetry/sdk-trace-base": "^1.21.0", + "@opentelemetry/sdk-trace-node": "^1.21.0", + "@opentelemetry/semantic-conventions": "^1.21.0", + "chokidar": "^3.5.3", + "hash-wasm": "^4.11.0", + "inflection": "^3.0.0", + "memfs": "^4.8.2", + "oo-ascii-tree": "^1.94.0", + "ts-pattern": "^5.0.6", + "type-fest": "^4.10.0" + }, + "peerDependenciesMeta": { + "@effect-ts/core": { + "optional": true + }, + "@effect-ts/otel": { + "optional": true + }, + "@effect-ts/otel-node": { + "optional": true + } + } + }, + "node_modules/@contentlayer2/utils/node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@contentlayer2/utils/node_modules/type-fest": { + "version": "4.30.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.30.0.tgz", + "integrity": "sha512-G6zXWS1dLj6eagy6sVhOMQiLtJdxQBHIA9Z6HFUNLOlr6MFOgzV8wvmidtPONfPtEUv0uZsy77XJNzTAfwPDaA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "license": "MIT", @@ -6668,14 +5342,6 @@ "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==", "dev": true }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0" - } - }, "node_modules/@esbuild-plugins/node-resolve": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-resolve/-/node-resolve-0.2.2.tgz", @@ -8463,9 +7129,10 @@ } }, "node_modules/@jsonjoy.com/util": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.1.3.tgz", - "integrity": "sha512-g//kkF4kOwUjemValCtOc/xiYzmwMRmWq3Bn+YnzOzuZLHq2PpMOxxIayN3cKbo7Ko2Np65t6D9H81IvXbXhqg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.5.0.tgz", + "integrity": "sha512-ojoNsrIuPI9g6o8UxhraZQSyF2ByJanAY4cTFbc8Mf2AXEF4aQRGY1dJxyJpuyav8r9FGflEt/Ff3u5Nt6YMPA==", + "license": "Apache-2.0", "engines": { "node": ">=10.0" }, @@ -8477,11 +7144,6 @@ "tslib": "2" } }, - "node_modules/@juggle/resize-observer": { - "version": "3.4.0", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/@kwsites/file-exists": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", @@ -10411,6 +9073,26 @@ "@opentelemetry/api": ">=1.0.0 <1.9.0" } }, + "node_modules/@opentelemetry/exporter-trace-otlp-grpc": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.51.1.tgz", + "integrity": "sha512-P9+Hkszih95ITvldGZ+kXvj9HpD1QfS+PwooyHK72GYA+Bgm+yUSAsDkUkDms8+s9HW6poxURv3LcjaMuBBpVQ==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.24.1", + "@opentelemetry/otlp-grpc-exporter-base": "0.51.1", + "@opentelemetry/otlp-transformer": "0.51.1", + "@opentelemetry/resources": "1.24.1", + "@opentelemetry/sdk-trace-base": "1.24.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, "node_modules/@opentelemetry/instrumentation": { "version": "0.53.0", "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.53.0.tgz", @@ -10923,6 +9605,88 @@ "node": ">=10" } }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.51.1.tgz", + "integrity": "sha512-UYlnOYyDdzo1Gw559EHCzru0RwhvuXCwoH8jGo9J4gO1TE58GjnEmIjomMsKBCym3qWNJfIQXw+9SZCV0DdQNg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.24.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-grpc-exporter-base": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.51.1.tgz", + "integrity": "sha512-ZAS+4pq8o7dsugGTwV9s6JMKSxi+guIHdn0acOv0bqj26e9pWDFx5Ky+bI0aY46uR9Y0JyXqY+KAEYM/SO3DFA==", + "license": "Apache-2.0", + "dependencies": { + "@grpc/grpc-js": "^1.7.1", + "@opentelemetry/core": "1.24.1", + "@opentelemetry/otlp-exporter-base": "0.51.1", + "protobufjs": "^7.2.3" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.51.1.tgz", + "integrity": "sha512-OppYOXwV9LQqqtYUCywqoOqX/JT9LQ5/FMuPZ//eTkvuHdUC4ZMwz2c6uSoT2R90GWvvGnF1iEqTGyTT3xAt2Q==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.51.1", + "@opentelemetry/core": "1.24.1", + "@opentelemetry/resources": "1.24.1", + "@opentelemetry/sdk-logs": "0.51.1", + "@opentelemetry/sdk-metrics": "1.24.1", + "@opentelemetry/sdk-trace-base": "1.24.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.9.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/api-logs": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.51.1.tgz", + "integrity": "sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/sdk-metrics": { + "version": "1.24.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.24.1.tgz", + "integrity": "sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.24.1", + "@opentelemetry/resources": "1.24.1", + "lodash.merge": "^4.6.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.9.0" + } + }, "node_modules/@opentelemetry/propagator-b3": { "version": "1.24.1", "resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.24.1.tgz", @@ -10974,6 +9738,23 @@ "@opentelemetry/api": ">=1.0.0 <1.9.0" } }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.51.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.51.1.tgz", + "integrity": "sha512-ULQQtl82b673PpZc5/0EtH4V+BrwVOgKJZEB7tYZnGTG3I98tQVk89S9/JSixomDr++F4ih+LSJTCqIKBz+MQQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.24.1", + "@opentelemetry/resources": "1.24.1" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.9.0", + "@opentelemetry/api-logs": ">=0.39.1" + } + }, "node_modules/@opentelemetry/sdk-metrics": { "version": "1.26.0", "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.26.0.tgz", @@ -12302,156 +11083,6 @@ } } }, - "node_modules/@radix-ui/react-select": { - "version": "1.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/number": "1.0.1", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.4", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.3", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.2", - "@radix-ui/react-portal": "1.0.3", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-controllable-state": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1", - "@radix-ui/react-use-previous": "1.0.1", - "@radix-ui/react-visually-hidden": "1.0.3", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-focus-scope": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-popper": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1", - "@radix-ui/react-use-rect": "1.0.1", - "@radix-ui/react-use-size": "1.0.1", - "@radix-ui/rect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-portal": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-separator": { "version": "1.0.3", "license": "MIT", @@ -12632,35 +11263,6 @@ } } }, - "node_modules/@radix-ui/react-toolbar": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-roving-focus": "1.0.4", - "@radix-ui/react-separator": "1.0.3", - "@radix-ui/react-toggle-group": "1.0.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-tooltip": { "version": "1.0.7", "license": "MIT", @@ -15076,985 +13678,6 @@ "version": "1.2.8", "license": "MIT" }, - "node_modules/@storybook/addon-actions": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.6.17.tgz", - "integrity": "sha512-TBphs4v6LRfyTpFo/WINF0TkMaE3rrNog7wW5mbz6n0j8o53kDN4o9ZEcygSL5zQX43CAaghQTeDCss7ueG7ZQ==", - "dev": true, - "dependencies": { - "@storybook/core-events": "7.6.17", - "@storybook/global": "^5.0.0", - "@types/uuid": "^9.0.1", - "dequal": "^2.0.2", - "polished": "^4.2.2", - "uuid": "^9.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-actions/node_modules/@storybook/core-events": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.17.tgz", - "integrity": "sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-actions/node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "dev": true - }, - "node_modules/@storybook/addon-controls": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-7.6.17.tgz", - "integrity": "sha512-zR0aLaUF7FtV/nMRyfniFbCls/e0DAAoXACuOAUAwNAv0lbIS8AyZZiHSmKucCvziUQ6WceeCC7+du3C+9y0rQ==", - "dev": true, - "dependencies": { - "@storybook/blocks": "7.6.17", - "lodash": "^4.17.21", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-docs": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-7.6.17.tgz", - "integrity": "sha512-FKa4Mdy7nhgvEVZJHpMkHriDzpVHbohn87zv9NCL+Ctjs1iAmzGwxEm0culszyDS1HN2ToVoY0h8CSi2RSSZqA==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.3.1", - "@mdx-js/react": "^2.1.5", - "@storybook/blocks": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/components": "7.6.17", - "@storybook/csf-plugin": "7.6.17", - "@storybook/csf-tools": "7.6.17", - "@storybook/global": "^5.0.0", - "@storybook/mdx2-csf": "^1.0.0", - "@storybook/node-logger": "7.6.17", - "@storybook/postinstall": "7.6.17", - "@storybook/preview-api": "7.6.17", - "@storybook/react-dom-shim": "7.6.17", - "@storybook/theming": "7.6.17", - "@storybook/types": "7.6.17", - "fs-extra": "^11.1.0", - "remark-external-links": "^8.0.0", - "remark-slug": "^6.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/addon-docs/node_modules/@storybook/channels": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.17.tgz", - "integrity": "sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-docs/node_modules/@storybook/client-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", - "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-docs/node_modules/@storybook/core-events": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.17.tgz", - "integrity": "sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-docs/node_modules/@storybook/node-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.17.tgz", - "integrity": "sha512-w59MQuXhhUNrUVmVkXhMwIg2nvFWjdDczLTwYLorhfsE36CWeUOY5QCZWQy0Qf/h+jz8Uo7Evy64qn18v9C4wA==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-docs/node_modules/@storybook/preview-api": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.17.tgz", - "integrity": "sha512-wLfDdI9RWo1f2zzFe54yRhg+2YWyxLZvqdZnSQ45mTs4/7xXV5Wfbv3QNTtcdw8tT3U5KRTrN1mTfTCiRJc0Kw==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.17", - "@types/qs": "^6.9.5", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "qs": "^6.10.0", - "synchronous-promise": "^2.0.15", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-docs/node_modules/@storybook/react-dom-shim": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-7.6.17.tgz", - "integrity": "sha512-32Sa/G+WnvaPiQ1Wvjjw5UM9rr2c4GDohwCcWVv3/LJuiFPqNS6zglAtmnsrlIBnUwRBMLMh/ekCTdqMiUmfDw==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/addon-docs/node_modules/@storybook/theming": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.17.tgz", - "integrity": "sha512-ZbaBt3KAbmBtfjNqgMY7wPMBshhSJlhodyMNQypv+95xLD/R+Az6aBYbpVAOygLaUQaQk4ar7H/Ww6lFIoiFbA==", - "dev": true, - "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.17", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/addon-docs/node_modules/@storybook/types": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.17.tgz", - "integrity": "sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addons": { - "version": "7.5.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/manager-api": "7.5.3", - "@storybook/preview-api": "7.5.3", - "@storybook/types": "7.5.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/api": { - "version": "7.5.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/client-logger": "7.5.3", - "@storybook/manager-api": "7.5.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, - "node_modules/@storybook/blocks": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.6.17.tgz", - "integrity": "sha512-PsNVoe0bX1mMn4Kk3nbKZ0ItDZZ0YJnYAFJ6toAbsyBAbgzg1sce88sQinzvbn58/RT9MPKeWMPB45ZS7ggiNg==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/components": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/docs-tools": "7.6.17", - "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.6.17", - "@storybook/preview-api": "7.6.17", - "@storybook/theming": "7.6.17", - "@storybook/types": "7.6.17", - "@types/lodash": "^4.14.167", - "color-convert": "^2.0.1", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "markdown-to-jsx": "^7.1.8", - "memoizerific": "^1.11.3", - "polished": "^4.2.2", - "react-colorful": "^5.1.2", - "telejson": "^7.2.0", - "tocbot": "^4.20.1", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/channels": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.17.tgz", - "integrity": "sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/client-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", - "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/core-common": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.17.tgz", - "integrity": "sha512-me2TP3Q9/qzqCLoDHUSsUF+VS1MHxfHbTVF6vAz0D/COTxzsxLpu9TxTbzJoBCxse6XRb6wWI1RgF1mIcjic7g==", - "dev": true, - "dependencies": { - "@storybook/core-events": "7.6.17", - "@storybook/node-logger": "7.6.17", - "@storybook/types": "7.6.17", - "@types/find-cache-dir": "^3.2.1", - "@types/node": "^18.0.0", - "@types/node-fetch": "^2.6.4", - "@types/pretty-hrtime": "^1.0.0", - "chalk": "^4.1.0", - "esbuild": "^0.18.0", - "esbuild-register": "^3.5.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/core-events": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.17.tgz", - "integrity": "sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/docs-tools": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.6.17.tgz", - "integrity": "sha512-bYrLoj06adqklyLkEwD32C0Ww6t+9ZVvrJHiVT42bIhTRpFiFPAetl1a9KPHtFLnfduh4n2IxIr1jv32ThPDTA==", - "dev": true, - "dependencies": { - "@storybook/core-common": "7.6.17", - "@storybook/preview-api": "7.6.17", - "@storybook/types": "7.6.17", - "@types/doctrine": "^0.0.3", - "assert": "^2.1.0", - "doctrine": "^3.0.0", - "lodash": "^4.17.21" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/manager-api": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.6.17.tgz", - "integrity": "sha512-IJIV1Yc6yw1dhCY4tReHCfBnUKDqEBnMyHp3mbXpsaHxnxJZrXO45WjRAZIKlQKhl/Ge1CrnznmHRCmYgqmrWg==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/router": "7.6.17", - "@storybook/theming": "7.6.17", - "@storybook/types": "7.6.17", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "store2": "^2.14.2", - "telejson": "^7.2.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/node-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.17.tgz", - "integrity": "sha512-w59MQuXhhUNrUVmVkXhMwIg2nvFWjdDczLTwYLorhfsE36CWeUOY5QCZWQy0Qf/h+jz8Uo7Evy64qn18v9C4wA==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/preview-api": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.17.tgz", - "integrity": "sha512-wLfDdI9RWo1f2zzFe54yRhg+2YWyxLZvqdZnSQ45mTs4/7xXV5Wfbv3QNTtcdw8tT3U5KRTrN1mTfTCiRJc0Kw==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.17", - "@types/qs": "^6.9.5", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "qs": "^6.10.0", - "synchronous-promise": "^2.0.15", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/router": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.6.17.tgz", - "integrity": "sha512-GnyC0j6Wi5hT4qRhSyT8NPtJfGmf82uZw97LQRWeyYu5gWEshUdM7aj40XlNiScd5cZDp0owO1idduVF2k2l2A==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.17", - "memoizerific": "^1.11.3", - "qs": "^6.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/theming": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.17.tgz", - "integrity": "sha512-ZbaBt3KAbmBtfjNqgMY7wPMBshhSJlhodyMNQypv+95xLD/R+Az6aBYbpVAOygLaUQaQk4ar7H/Ww6lFIoiFbA==", - "dev": true, - "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.17", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/blocks/node_modules/@storybook/types": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.17.tgz", - "integrity": "sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/blocks/node_modules/@types/node": { - "version": "18.19.20", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.20.tgz", - "integrity": "sha512-SKXZvI375jkpvAj8o+5U2518XQv76mAsixqfXiVyWyXZbVWQK25RurFovYpVIxVzul0rZoH58V/3SkEnm7s3qA==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@storybook/blocks/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@storybook/blocks/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@storybook/blocks/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/blocks/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/channels": { - "version": "7.5.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/client-logger": "7.5.3", - "@storybook/core-events": "7.5.3", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/client-logger": { - "version": "7.5.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/components": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.6.17.tgz", - "integrity": "sha512-lbh7GynMidA+CZcJnstVku6Nhs+YkqjYaZ+mKPugvlVhGVWv0DaaeQFVuZ8cJtUGJ/5FFU4Y+n+gylYUHkGBMA==", - "dev": true, - "dependencies": { - "@radix-ui/react-select": "^1.2.2", - "@radix-ui/react-toolbar": "^1.0.4", - "@storybook/client-logger": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/theming": "7.6.17", - "@storybook/types": "7.6.17", - "memoizerific": "^1.11.3", - "use-resize-observer": "^9.1.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/components/node_modules/@storybook/channels": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.17.tgz", - "integrity": "sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/components/node_modules/@storybook/client-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", - "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/components/node_modules/@storybook/core-events": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.17.tgz", - "integrity": "sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/components/node_modules/@storybook/theming": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.17.tgz", - "integrity": "sha512-ZbaBt3KAbmBtfjNqgMY7wPMBshhSJlhodyMNQypv+95xLD/R+Az6aBYbpVAOygLaUQaQk4ar7H/Ww6lFIoiFbA==", - "dev": true, - "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.17", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/components/node_modules/@storybook/types": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.17.tgz", - "integrity": "sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-events": { - "version": "7.5.3", - "dev": true, - "license": "MIT", - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/csf": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.11.tgz", - "integrity": "sha512-dHYFQH3mA+EtnCkHXzicbLgsvzYjcDJ1JWsogbItZogkPHgSJM/Wr71uMkcvw8v9mmCyP4NpXJuu6bPoVsOnzg==", - "dev": true, - "dependencies": { - "type-fest": "^2.19.0" - } - }, - "node_modules/@storybook/csf-plugin": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-7.6.17.tgz", - "integrity": "sha512-xTHv9BUh3bkDVCvcbmdfVF0/e96BdrEgqPJ3G3RmKbSzWLOkQ2U9yiPfHzT0KJWPhVwj12fjfZp0zunu+pcS6Q==", - "dev": true, - "dependencies": { - "@storybook/csf-tools": "7.6.17", - "unplugin": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/csf-tools": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.6.17.tgz", - "integrity": "sha512-dAQtam0EBPeTJYcQPLxXgz4L9JFqD+HWbLFG9CmNIhMMjticrB0mpk1EFIS6vPXk/VsVWpBgMLD7dZlD6YMKcQ==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "@storybook/csf": "^0.1.2", - "@storybook/types": "7.6.17", - "fs-extra": "^11.1.0", - "recast": "^0.23.1", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/csf-tools/node_modules/@storybook/channels": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.17.tgz", - "integrity": "sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/csf-tools/node_modules/@storybook/client-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", - "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/csf-tools/node_modules/@storybook/core-events": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.17.tgz", - "integrity": "sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/csf-tools/node_modules/@storybook/types": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.17.tgz", - "integrity": "sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/global": { - "version": "5.0.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@storybook/manager-api": { - "version": "7.5.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/channels": "7.5.3", - "@storybook/client-logger": "7.5.3", - "@storybook/core-events": "7.5.3", - "@storybook/csf": "^0.1.0", - "@storybook/global": "^5.0.0", - "@storybook/router": "7.5.3", - "@storybook/theming": "7.5.3", - "@storybook/types": "7.5.3", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "semver": "^7.3.7", - "store2": "^2.14.2", - "telejson": "^7.2.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/manager-api/node_modules/lru-cache": { - "version": "6.0.0", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@storybook/manager-api/node_modules/semver": { - "version": "7.5.4", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@storybook/manager-api/node_modules/yallist": { - "version": "4.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/@storybook/mdx2-csf": { - "version": "1.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/@storybook/postinstall": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-7.6.17.tgz", - "integrity": "sha512-WaWqB8o9vUc9aaVls+povQSVirf1Xd1LZcVhUKfAocAF3mzYUsnJsVqvnbjRj/F96UFVihOyDt9Zjl/9OvrCvQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/preview-api": { - "version": "7.5.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/channels": "7.5.3", - "@storybook/client-logger": "7.5.3", - "@storybook/core-events": "7.5.3", - "@storybook/csf": "^0.1.0", - "@storybook/global": "^5.0.0", - "@storybook/types": "7.5.3", - "@types/qs": "^6.9.5", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "qs": "^6.10.0", - "synchronous-promise": "^2.0.15", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/router": { - "version": "7.5.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/client-logger": "7.5.3", - "memoizerific": "^1.11.3", - "qs": "^6.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/theming": { - "version": "7.5.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.5.3", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/types": { - "version": "7.5.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/channels": "7.5.3", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, "node_modules/@stripe/react-stripe-js": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-2.5.0.tgz", @@ -16781,15 +14404,6 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/body-parser": { - "version": "1.19.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, "node_modules/@types/btoa-lite": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.2.tgz", @@ -17062,22 +14676,6 @@ "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", "license": "MIT" }, - "node_modules/@types/doctrine": { - "version": "0.0.3", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/emscripten": { - "version": "1.39.10", - "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.10.tgz", - "integrity": "sha512-TB/6hBkYQJxsZHSqyeuO1Jt0AB/bW6G7rHt9g7lML7SOF6lbgcHvw/Lr+69iqN0qxgXLhWKScAon73JNnptuDw==", - "dev": true - }, - "node_modules/@types/escodegen": { - "version": "0.0.6", - "dev": true, - "license": "MIT" - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -17090,29 +14688,6 @@ "@types/estree": "*" } }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.37", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, "node_modules/@types/extend": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/extend/-/extend-3.0.4.tgz", @@ -17124,11 +14699,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/find-cache-dir": { - "version": "3.2.1", - "dev": true, - "license": "MIT" - }, "node_modules/@types/geojson": { "version": "7946.0.12", "license": "MIT" @@ -17156,11 +14726,6 @@ "hoist-non-react-statics": "^3.3.0" } }, - "node_modules/@types/http-errors": { - "version": "2.0.2", - "dev": true, - "license": "MIT" - }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.4", "dev": true, @@ -17312,11 +14877,6 @@ "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.10.tgz", "integrity": "sha512-Rllzc5KHk0Al5/WANwgSPl1/CwjqCy+AZrGd78zuK+jO9aDM6ffblZ+zIjgPNAaEBmlO0RYDvLNh7wD0zKVgEg==" }, - "node_modules/@types/mime": { - "version": "1.3.3", - "dev": true, - "license": "MIT" - }, "node_modules/@types/mime-db": { "version": "1.43.5", "resolved": "https://registry.npmjs.org/@types/mime-db/-/mime-db-1.43.5.tgz", @@ -17404,11 +14964,6 @@ "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.4.tgz", "integrity": "sha512-B34A7uot1Cv0XtaHRYDATltAdKx0BvVKNgYNqE4WjtPUa4VQJM7kxeXcVKaH+KS+kCmZ+6w+QaUdcljiheiBJA==" }, - "node_modules/@types/pretty-hrtime": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, "node_modules/@types/prismjs": { "version": "1.26.3", "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", @@ -17418,11 +14973,6 @@ "version": "15.7.8", "license": "MIT" }, - "node_modules/@types/qs": { - "version": "6.9.8", - "dev": true, - "license": "MIT" - }, "node_modules/@types/randombytes": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.3.tgz", @@ -17432,11 +14982,6 @@ "@types/node": "*" } }, - "node_modules/@types/range-parser": { - "version": "1.2.5", - "dev": true, - "license": "MIT" - }, "node_modules/@types/react": { "version": "18.3.3", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", @@ -17619,25 +15164,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/send": { - "version": "0.17.2", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, "node_modules/@types/shimmer": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz", @@ -18451,44 +15977,6 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "peer": true }, - "node_modules/@yarnpkg/fslib": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@yarnpkg/fslib/-/fslib-2.10.3.tgz", - "integrity": "sha512-41H+Ga78xT9sHvWLlFOZLIhtU6mTGZ20pZ29EiZa97vnxdohJD2AF42rCoAoWfqUz486xY6fhjMH+DYEM9r14A==", - "dev": true, - "dependencies": { - "@yarnpkg/libzip": "^2.3.0", - "tslib": "^1.13.0" - }, - "engines": { - "node": ">=12 <14 || 14.2 - 14.9 || >14.10.0" - } - }, - "node_modules/@yarnpkg/fslib/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@yarnpkg/libzip": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/libzip/-/libzip-2.3.0.tgz", - "integrity": "sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==", - "dev": true, - "dependencies": { - "@types/emscripten": "^1.39.6", - "tslib": "^1.13.0" - }, - "engines": { - "node": ">=12 <14 || 14.2 - 14.9 || >14.10.0" - } - }, - "node_modules/@yarnpkg/libzip/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/@zip.js/zip.js": { "version": "2.7.30", "license": "BSD-3-Clause", @@ -18789,11 +16277,6 @@ "resolved": "packages/api-types", "link": true }, - "node_modules/app-root-dir": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, "node_modules/aproba": { "version": "2.0.0", "license": "ISC" @@ -19039,18 +16522,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/assert": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "is-nan": "^1.3.2", - "object-is": "^1.1.5", - "object.assign": "^4.1.4", - "util": "^0.12.5" - } - }, "node_modules/assert-plus": { "version": "1.0.0", "license": "MIT", @@ -19066,17 +16537,6 @@ "node": "*" } }, - "node_modules/ast-types": { - "version": "0.16.1", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/ast-types-flow": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", @@ -20419,6 +17879,27 @@ "upper-case": "^2.0.2" } }, + "node_modules/contentlayer2": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/contentlayer2/-/contentlayer2-0.5.3.tgz", + "integrity": "sha512-0vHdTWsuTgY/dMKxhCc8wILoeaCK5ye42i1gRd1jOoKFXQ6q/eMnSp+gSE81V8wbBa2jv7cjP90xpiRfzaXTWw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@contentlayer2/cli": "0.5.3", + "@contentlayer2/client": "0.5.3", + "@contentlayer2/core": "0.5.3", + "@contentlayer2/source-files": "0.5.3", + "@contentlayer2/source-remote-files": "0.5.3", + "@contentlayer2/utils": "0.5.3" + }, + "bin": { + "contentlayer2": "bin/cli.cjs" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "license": "MIT" @@ -20698,15 +18179,6 @@ "node": ">= 8" } }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/css-background-parser": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/css-background-parser/-/css-background-parser-0.1.0.tgz", @@ -21304,65 +18776,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", - "dev": true, - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/del/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/del/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "license": "MIT", @@ -21605,14 +19018,6 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, - "node_modules/dotenv-expand": { - "version": "10.0.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, "node_modules/dotty": { "version": "0.1.2", "dev": true, @@ -21970,17 +19375,6 @@ "@esbuild/win32-x64": "0.18.20" } }, - "node_modules/esbuild-register": { - "version": "3.5.0", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, - "peerDependencies": { - "esbuild": ">=0.12 <1" - } - }, "node_modules/esbuild/node_modules/@esbuild/android-arm": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", @@ -23507,15 +20901,6 @@ "version": "2.0.5", "license": "MIT" }, - "node_modules/file-system-cache": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "fs-extra": "11.1.1", - "ramda": "0.29.0" - } - }, "node_modules/filelist": { "version": "1.0.4", "dev": true, @@ -23554,81 +20939,6 @@ "node": ">=8" } }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-cache-dir/node_modules/find-up": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-cache-dir/node_modules/locate-path": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-cache-dir/node_modules/p-limit": { - "version": "2.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-cache-dir/node_modules/p-locate": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-cache-dir/node_modules/pkg-dir": { - "version": "4.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/find-up": { "version": "5.0.0", "license": "MIT", @@ -23857,19 +21167,6 @@ "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz", "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==" }, - "node_modules/fs-extra": { - "version": "11.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/fs-minipass": { "version": "2.1.0", "license": "ISC", @@ -25527,17 +22824,6 @@ "version": "2.0.2", "license": "MIT" }, - "node_modules/html-tags": { - "version": "3.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/html-to-image": { "version": "1.11.11", "license": "MIT" @@ -25940,14 +23226,6 @@ "resolved": "https://registry.npmjs.org/ip-num/-/ip-num-1.5.1.tgz", "integrity": "sha512-QziFxgxq3mjIf5CuwlzXFYscHxgLqdEdJKRo2UJ5GurL5zrSRMzT/O+nK0ABimoFH8MWF8YwIiwECYsHc1LpUQ==" }, - "node_modules/is-absolute-url": { - "version": "3.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-alphabetical": { "version": "2.0.1", "license": "MIT", @@ -26199,21 +23477,6 @@ "dev": true, "license": "MIT" }, - "node_modules/is-nan": { - "version": "1.3.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-negative-zero": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", @@ -26275,15 +23538,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/is-path-inside": { "version": "3.0.3", "license": "MIT", @@ -28751,19 +26005,6 @@ "node": ">=0.10.0" } }, - "node_modules/lazy-universal-dotenv": { - "version": "4.0.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "app-root-dir": "^1.0.2", - "dotenv": "^16.0.0", - "dotenv-expand": "^10.0.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/leven": { "version": "3.1.0", "dev": true, @@ -29239,11 +26480,6 @@ "tmpl": "1.0.5" } }, - "node_modules/map-or-similar": { - "version": "1.5.0", - "dev": true, - "license": "MIT" - }, "node_modules/mark.js": { "version": "8.11.1", "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", @@ -29373,54 +26609,6 @@ "version": "3.0.0", "license": "MIT" }, - "node_modules/mdast-util-definitions": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-definitions/node_modules/unist-util-is": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-definitions/node_modules/unist-util-visit": { - "version": "2.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-definitions/node_modules/unist-util-visit-parents": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", @@ -31328,18 +28516,29 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/memfs": { + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.14.1.tgz", + "integrity": "sha512-Fq5CMEth+2iprLJ5mNizRcWuiwRZYjNkUD0zKk224jZunE9CRacTRDK8QLALbMBlNX2y3nY6lKZbesCwDwacig==", + "license": "Apache-2.0", + "dependencies": { + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.3.0", + "tree-dump": "^1.0.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">= 4.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + } + }, "node_modules/memoize-one": { "version": "5.2.1", "license": "MIT" }, - "node_modules/memoizerific": { - "version": "1.11.3", - "dev": true, - "license": "MIT", - "dependencies": { - "map-or-similar": "^1.5.0" - } - }, "node_modules/memorystream": { "version": "0.3.1", "dev": true, @@ -33849,14 +31048,15 @@ } }, "node_modules/nanoid": { - "version": "3.3.6", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -33952,6 +31152,22 @@ } } }, + "node_modules/next-contentlayer2": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/next-contentlayer2/-/next-contentlayer2-0.5.3.tgz", + "integrity": "sha512-xNuiAJxeQOABiWsjJ6hFv/Ovb9vRujBgamaEjvYXiVecrBtCIyCDF3WpdDx4krfAd9WJRW8TN6OLpyZsG2ApjA==", + "license": "MIT", + "dependencies": { + "@contentlayer2/core": "0.5.3", + "@contentlayer2/utils": "0.5.3" + }, + "peerDependencies": { + "contentlayer2": "0.5.3", + "next": ">=12.0.0", + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/next-mdx-remote": { "version": "4.4.1", "license": "MPL-2.0", @@ -35799,17 +33015,6 @@ "node": ">= 6" } }, - "node_modules/pkg-dir": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^5.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/pkg-types": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz", @@ -36237,14 +33442,6 @@ "dev": true, "license": "MIT" }, - "node_modules/pretty-hrtime": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/prism-react-renderer": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.3.1.tgz", @@ -36431,21 +33628,6 @@ ], "license": "MIT" }, - "node_modules/qs": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.3.tgz", - "integrity": "sha512-AWJm14H1vVaO/iNZ4/hO+HyaTehuy9nRqVdkTqlJt0HWvBiBIEXFmb4C0DGeYo3Xes9rrEW+TxHsaigCbN5ICQ==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/querystringify": { "version": "2.2.0", "license": "MIT" @@ -36476,15 +33658,6 @@ "version": "1.0.0", "license": "CC0-1.0" }, - "node_modules/ramda": { - "version": "0.29.0", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ramda" - } - }, "node_modules/randexp": { "version": "0.4.6", "license": "MIT", @@ -36562,15 +33735,6 @@ "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" } }, - "node_modules/react-colorful": { - "version": "5.6.1", - "dev": true, - "license": "MIT", - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, "node_modules/react-contexify": { "version": "5.0.0", "license": "MIT", @@ -36744,25 +33908,6 @@ "react-dom": ">= 16.3.0" } }, - "node_modules/react-element-to-jsx-string": { - "version": "15.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@base2/pretty-print-object": "1.0.1", - "is-plain-object": "5.0.0", - "react-is": "18.1.0" - }, - "peerDependencies": { - "react": "^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0", - "react-dom": "^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0" - } - }, - "node_modules/react-element-to-jsx-string/node_modules/react-is": { - "version": "18.1.0", - "dev": true, - "license": "MIT" - }, "node_modules/react-error-boundary": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz", @@ -37351,22 +34496,6 @@ "node": ">=8.10.0" } }, - "node_modules/recast": { - "version": "0.23.6", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.6.tgz", - "integrity": "sha512-9FHoNjX1yjuesMwuthAmPKabxYQdOgihFYmT5ebXfYGBcnqXZf3WOVz+5foEZ8Y83P4ZY6yQD5GMmtV+pgCCAQ==", - "dev": true, - "dependencies": { - "ast-types": "^0.16.1", - "esprima": "~4.0.0", - "source-map": "~0.6.1", - "tiny-invariant": "^1.3.3", - "tslib": "^2.0.1" - }, - "engines": { - "node": ">= 4" - } - }, "node_modules/recharts": { "version": "2.12.7", "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.7.tgz", @@ -37832,38 +34961,24 @@ } }, "node_modules/rehype-pretty-code": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/rehype-pretty-code/-/rehype-pretty-code-0.6.0.tgz", - "integrity": "sha512-VfntYoWYOBVURXYDdB8p/E1sZTm2W5ry89fJyY94WJAo1jUH/5sVhDC7cX5PPnksMyW9PYMxRLNfjkBpSgJrzQ==", + "version": "0.9.11", + "resolved": "https://registry.npmjs.org/rehype-pretty-code/-/rehype-pretty-code-0.9.11.tgz", + "integrity": "sha512-Eq90eCYXQJISktfRZ8PPtwc5SUyH6fJcxS8XOMnHPUQZBtC6RYo67gGlley9X2nR8vlniPj0/7oCDEYHKQa/oA==", + "workspaces": [ + "./word-highlighter-playground" + ], "dependencies": { + "@types/hast": "^2.0.0", "hash-obj": "^4.0.0", - "nanoid": "^4.0.0", "parse-numeric-range": "^1.3.0" }, "engines": { - "node": "^12.16.0 || >=13.2.0" + "node": ">=16" }, "peerDependencies": { "shiki": "*" } }, - "node_modules/rehype-pretty-code/node_modules/nanoid": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-4.0.2.tgz", - "integrity": "sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.js" - }, - "engines": { - "node": "^14 || ^16 || >=18" - } - }, "node_modules/rehype-slug": { "version": "5.1.0", "license": "MIT", @@ -38060,67 +35175,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/remark-external-links": { - "version": "8.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "extend": "^3.0.0", - "is-absolute-url": "^3.0.0", - "mdast-util-definitions": "^4.0.0", - "space-separated-tokens": "^1.0.0", - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-external-links/node_modules/space-separated-tokens": { - "version": "1.1.5", - "dev": true, - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/remark-external-links/node_modules/unist-util-is": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-external-links/node_modules/unist-util-visit": { - "version": "2.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-external-links/node_modules/unist-util-visit-parents": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/remark-frontmatter": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/remark-frontmatter/-/remark-frontmatter-5.0.0.tgz", @@ -38884,70 +35938,6 @@ "@types/unist": "^2" } }, - "node_modules/remark-slug": { - "version": "6.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "github-slugger": "^1.0.0", - "mdast-util-to-string": "^1.0.0", - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-slug/node_modules/github-slugger": { - "version": "1.5.0", - "dev": true, - "license": "ISC" - }, - "node_modules/remark-slug/node_modules/mdast-util-to-string": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-slug/node_modules/unist-util-is": { - "version": "4.1.0", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-slug/node_modules/unist-util-visit": { - "version": "2.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-slug/node_modules/unist-util-visit-parents": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/remark-stringify": { "version": "11.0.0", "license": "MIT", @@ -40401,38 +37391,6 @@ "node": ">= 0.4" } }, - "node_modules/store2": { - "version": "2.14.2", - "dev": true, - "license": "(MIT OR GPL-3.0)" - }, - "node_modules/storybook-dark-mode": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/addons": "^7.0.0", - "@storybook/api": "^7.0.0", - "@storybook/components": "^7.0.0", - "@storybook/core-events": "^7.0.0", - "@storybook/global": "^5.0.0", - "@storybook/theming": "^7.0.0", - "fast-deep-equal": "^3.1.3", - "memoizerific": "^1.11.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, "node_modules/streamsearch": { "version": "1.1.0", "engines": { @@ -41333,11 +38291,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/synchronous-promise": { - "version": "2.0.17", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/tailwind-merge": { "version": "1.14.0", "license": "MIT", @@ -41438,54 +38391,6 @@ "version": "4.0.0", "license": "ISC" }, - "node_modules/telejson": { - "version": "7.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "memoizerific": "^1.11.3" - } - }, - "node_modules/temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/tempy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz", - "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==", - "dev": true, - "dependencies": { - "del": "^6.0.0", - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/type-fest": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/terser": { "version": "5.32.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.32.0.tgz", @@ -42007,11 +38912,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/tocbot": { - "version": "4.22.0", - "dev": true, - "license": "MIT" - }, "node_modules/toggle-selection": { "version": "1.0.6", "license": "MIT" @@ -42126,14 +39026,6 @@ "typescript": ">=4.2.0" } }, - "node_modules/ts-dedent": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.10" - } - }, "node_modules/ts-easing": { "version": "0.2.0", "license": "Unlicense" @@ -42862,18 +39754,6 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/unist-builder": { "version": "3.0.1", "license": "MIT", @@ -43076,18 +39956,6 @@ "node": ">= 10.0.0" } }, - "node_modules/unplugin": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.7.1.tgz", - "integrity": "sha512-JqzORDAPxxs8ErLV4x+LL7bk5pk3YlcWqpSNsIkAZj972KzFZLClc/ekppahKkOczGkwIG6ElFgdOgOlK4tXZw==", - "dev": true, - "dependencies": { - "acorn": "^8.11.3", - "chokidar": "^3.5.3", - "webpack-sources": "^3.2.3", - "webpack-virtual-modules": "^0.6.1" - } - }, "node_modules/untildify": { "version": "4.0.0", "dev": true, @@ -43226,18 +40094,6 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "node_modules/use-resize-observer": { - "version": "9.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@juggle/resize-observer": "^3.3.1" - }, - "peerDependencies": { - "react": "16.8.0 - 18", - "react-dom": "16.8.0 - 18" - } - }, "node_modules/use-sidecar": { "version": "1.1.2", "license": "MIT", @@ -43278,18 +40134,6 @@ "node": ">=6.14.2" } }, - "node_modules/util": { - "version": "0.12.5", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "license": "MIT" @@ -44096,23 +40940,6 @@ "@esbuild/win32-x64": "0.20.2" } }, - "node_modules/vite/node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/vite/node_modules/postcss": { "version": "8.4.38", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", @@ -44554,12 +41381,6 @@ "node": ">=10.13.0" } }, - "node_modules/webpack-virtual-modules": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", - "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", - "dev": true - }, "node_modules/webpack/node_modules/eslint-scope": { "version": "5.1.1", "license": "BSD-2-Clause", @@ -45536,11 +42357,6 @@ "zod": "^3.22.3" }, "devDependencies": { - "@babel/plugin-syntax-flow": "^7.21.4", - "@babel/preset-env": "^7.21.4", - "@babel/preset-flow": "^7.21.4", - "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.21.4", "@ctrl/tinycolor": "^3.4.0", "@testing-library/jest-dom": "^6.1.3", "@testing-library/react": "^14.0.0", diff --git a/packages/api-types/types/api.d.ts b/packages/api-types/types/api.d.ts index 1e34bfb048a..3cb1c88ef2e 100644 --- a/packages/api-types/types/api.d.ts +++ b/packages/api-types/types/api.d.ts @@ -5147,8 +5147,12 @@ export interface components { region: string service_api_keys?: components['schemas']['ProjectServiceApiKeyResponse'][] ssl_enforced: boolean + is_sensitive?: boolean status: string } + ProjectSensitivitySettingResponse: { + is_sensitive: boolean + } /** @enum {string} */ ProjectStatus: | 'ACTIVE_HEALTHY' @@ -6553,6 +6557,9 @@ export interface components { UpdateProjectBody: { name: string } + UpdateProjectSensitivityBody: { + is_sensitive: boolean + } UpdateProviderBody: { attribute_mapping?: components['schemas']['AttributeMapping'] domains?: string[] @@ -13694,29 +13701,30 @@ export interface operations { } } } - /** Updates the given project sensitivity */ - SensitivityController_updateProjectSensitivity: { + /** Update project's sensitivity settings */ + SettingsController_patchProjectSensitivity: { parameters: { path: { /** @description Project ref */ ref: string - } + } } requestBody: { content: { - 'application/json': components['schemas']['MarkSensitiveBody'] + 'application/json': components['schemas']['UpdateProjectSensitivityBody'] } } responses: { 200: { content: { - 'application/json': components['schemas']['ProjectSensitivityResponse'] + 'application/json': components['schemas']['ProjectSensitivitySettingResponse'] } } - 403: { + /** @description Failed to update project's sensitivity setting */ + 404: { content: never } - /** @description Failed to update project */ + /** @description Failed to update project's sensitivity setting */ 500: { content: never } diff --git a/packages/shared-data/extensions.json b/packages/shared-data/extensions.json index 527ea514f6d..c1752b0abea 100644 --- a/packages/shared-data/extensions.json +++ b/packages/shared-data/extensions.json @@ -487,7 +487,7 @@ "name": "postgres_fdw", "comment": "foreign-data wrapper for remote PostgreSQL servers", "tags": ["Admin"], - "link": "https://www.postgresql.org/docs/current/postgres-fdw.html", + "link": "/guides/database/extensions/postgres_fdw", "github_url": null, "product": null, "product_url": null diff --git a/packages/ui-patterns/AssistantChat/AssistantChatCommands.stories.tsx b/packages/ui-patterns/AssistantChat/AssistantChatCommands.stories.tsx deleted file mode 100644 index 05d40164afd..00000000000 --- a/packages/ui-patterns/AssistantChat/AssistantChatCommands.stories.tsx +++ /dev/null @@ -1,90 +0,0 @@ -// import { StoryObj } from '@storybook/react' -// import { AssistantChatForm } from './AssistantChatForm' -// import { AssistantCommandsPopover } from './AssistantCommandsPopover' -// import { Box, User } from 'lucide-react' -// import { createRef, useRef, useState } from 'react' - -// // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export -// export default { -// title: 'AI/Assistant Chat', -// component: AssistantChatForm, -// parameters: { -// // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout -// layout: 'centered', -// }, -// decorators: [ -// (Story: any) => { -// return ( -//
    -// -//
    -// ) -// }, -// ], -// // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs -// tags: ['autodocs'], -// // More on argTypes: https://storybook.js.org/docs/api/argtypes -// // argTypes: {}, -// } - -// // export default meta - -// type Story = StoryObj - -// export const CommandsPopover: Story = { -// render: function Render(args) { -// const [commandsOpen, setCommandsOpen] = useState(false) -// const textAreaRef = createRef() -// const [value, setValueState] = useState('') -// const [loading, setLoading] = useState(false) - -// function handleSubmit(event: React.FormEvent) { -// // set loading for 3 seconds and then reset -// setLoading(true) -// setTimeout(() => { -// setLoading(false) -// }, 1500) -// } - -// const suggestions = [ -// 'Add policy for org Inserted User Access', -// 'Add policy for User-Specific Todo Access', -// 'Add policy for Org Update Restriction', -// ] - -// return ( -// <> -// setValueState(e)} -// suggestions={suggestions} -// > -// setValueState(e.target.value)} -// commandsOpen={commandsOpen} -// setCommandsOpen={setCommandsOpen} -// onSubmit={async (event) => { -// event.preventDefault() -// handleSubmit(event) -// }} -// /> -// -//

    -// Press / to open -// commands -//

    -// -// ) -// }, -// args: { -// // placeholder: 'Ask AI a question...', -// }, -// } diff --git a/packages/ui-patterns/AssistantChat/AssistantChatForm.stories.tsx b/packages/ui-patterns/AssistantChat/AssistantChatForm.stories.tsx deleted file mode 100644 index a59b6fa8143..00000000000 --- a/packages/ui-patterns/AssistantChat/AssistantChatForm.stories.tsx +++ /dev/null @@ -1,66 +0,0 @@ -// import { StoryObj } from '@storybook/react' -// import { AssistantChatForm } from './AssistantChatForm' -// import { Box, User } from 'lucide-react' -// import { createRef, useRef, useState } from 'react' - -// // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export -// export default { -// title: 'AI/Assistant Chat', -// component: AssistantChatForm, -// parameters: { -// // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout -// layout: 'centered', -// }, -// decorators: [ -// (Story: any) => { -// return ( -//
    -// -//
    -// ) -// }, -// ], -// // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs -// tags: ['autodocs'], -// // More on argTypes: https://storybook.js.org/docs/api/argtypes -// // argTypes: {}, -// } - -// // export default meta - -// type Story = StoryObj - -// export const Primary: Story = { -// render: function Render(args) { -// const textAreaRef = createRef() -// const [value, setValueState] = useState('') -// const [loading, setLoading] = useState(false) - -// function handleSubmit(event: React.FormEvent) { -// // set loading for 3 seconds and then reset -// setLoading(true) -// setTimeout(() => { -// setLoading(false) -// }, 1500) -// } - -// return ( -// } -// value={value} -// loading={loading} -// disabled={loading} -// onValueChange={(e) => setValueState(e.target.value)} -// onSubmit={async (event) => { -// event.preventDefault() -// handleSubmit(event) -// }} -// /> -// ) -// }, -// args: { -// // placeholder: 'Ask AI a question...', -// }, -// } diff --git a/packages/ui-patterns/DataInputs/Input.stories.tsx b/packages/ui-patterns/DataInputs/Input.stories.tsx deleted file mode 100644 index 59ad924e26a..00000000000 --- a/packages/ui-patterns/DataInputs/Input.stories.tsx +++ /dev/null @@ -1,69 +0,0 @@ -// import { StoryObj } from '@storybook/react' -// import { Input } from './Input' -// import { User } from 'lucide-react' - -// // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export -// export default { -// title: 'Data Inputs/Input', -// component: Input, -// parameters: { -// // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout -// layout: 'centered', -// }, -// decorators: [ -// (Story: any) => { -// return ( -//
    -// -//
    -// ) -// }, -// ], -// // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs -// tags: ['autodocs'], -// // More on argTypes: https://storybook.js.org/docs/api/argtypes -// // argTypes: {}, -// } - -// // export default meta - -// type Story = StoryObj - -// export const Primary: Story = { -// render: function Render(args) { -// return -// }, -// args: { -// placeholder: 'mildtomato', -// }, -// } - -// export const withIcon: Story = { -// render: function Render(args) { -// return -// }, -// args: { -// placeholder: 'mildtomato', -// icon: , -// }, -// } - -// export const withCopy: Story = { -// render: function Render(args) { -// return -// }, -// args: { -// placeholder: 'mildtomato', -// copy: true, -// }, -// } - -// export const size: Story = { -// render: function Render(args) { -// return -// }, -// args: { -// placeholder: 'mildtomato', -// size: 'small', -// }, -// } diff --git a/packages/ui-patterns/Dialogs/TextConfirmModal.stories.tsx b/packages/ui-patterns/Dialogs/TextConfirmModal.stories.tsx deleted file mode 100644 index 6880be697e5..00000000000 --- a/packages/ui-patterns/Dialogs/TextConfirmModal.stories.tsx +++ /dev/null @@ -1,295 +0,0 @@ -// import { useArgs } from '@storybook/preview-api' -// import { StoryObj } from '@storybook/react' -// import { Button, IconUserPlus } from 'ui' -// import TextConfirmModal from './TextConfirmModal' - -// // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export -// export default { -// title: 'Dialogs/TextConfirmModal', -// component: TextConfirmModal, -// parameters: { -// // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout -// layout: 'centered', -// }, -// // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs -// tags: ['autodocs'], -// // More on argTypes: https://storybook.js.org/docs/api/argtypes -// argTypes: { -// title: { control: 'text' }, -// cancelLabel: { control: 'text' }, -// confirmLabel: { control: 'text' }, -// }, -// } - -// // export default meta - -// // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args -// // export const Primary = { -// // args: { -// // size: 'small', -// // // visible: true, -// // }, -// // } - -// type Story = StoryObj - -// export const Primary: Story = { -// args: { -// confirmString: 'project name', -// visible: false, -// title: 'Are you sure you want to delete?', -// size: 'small', -// // label: 'Try Me!', -// }, -// /** -// * 👇 To avoid linting issues, it is recommended to use a function with a capitalized name. -// * If you are not concerned with linting, you may use an arrow function. -// */ -// render: function Render(args) { -// const [{ visible }, updateArgs] = useArgs() - -// function onVisibleChange() { -// updateArgs({ visible: !visible }) -// } - -// return ( -// <> -// -// onVisibleChange()} -// // onChange={onChange} - -// // isChecked={isChecked} -// /> -// -// ) -// }, -// } - -// export const withInfoAlert: Story = { -// args: { -// confirmString: 'project name', -// visible: false, -// variant: 'default', -// title: 'Are you sure you want to move?', -// alert: { -// title: 'This is a reminder banner', -// description: 'This should not be used for important CRUD events.', -// }, -// }, -// /** -// * 👇 To avoid linting issues, it is recommended to use a function with a capitalized name. -// * If you are not concerned with linting, you may use an arrow function. -// */ -// render: function Render(args) { -// const [{ visible }, updateArgs] = useArgs() -// // const [visible, setVisibleState] = useState(args.visible) - -// function onVisibleChange() { -// updateArgs({ visible: !visible }) -// } - -// return ( -// <> -// -// onVisibleChange()} /> -// -// ) -// }, -// } - -// export const withWarningAlert: Story = { -// args: { -// confirmString: 'project name', -// visible: false, -// variant: 'warning', -// title: 'Are you sure you want to delete?', -// alert: { -// title: 'This is a warning', -// description: 'You are about to delete this item. This action cannot be undone.', -// }, -// }, -// /** -// * 👇 To avoid linting issues, it is recommended to use a function with a capitalized name. -// * If you are not concerned with linting, you may use an arrow function. -// */ -// render: function Render(args) { -// const [{ visible }, updateArgs] = useArgs() -// // const [visible, setVisibleState] = useState(args.visible) - -// function onVisibleChange() { -// updateArgs({ visible: !visible }) -// } - -// return ( -// <> -// -// onVisibleChange()} /> -// -// ) -// }, -// } - -// export const withDestructiveAlert: Story = { -// args: { -// confirmString: 'project name', -// visible: false, -// variant: 'destructive', -// title: 'Are you sure you want to delete?', -// alert: { -// title: 'This is a warning', -// description: 'You are about to delete this item. This action cannot be undone.', -// }, -// // label: 'Try Me!', -// }, -// /** -// * 👇 To avoid linting issues, it is recommended to use a function with a capitalized name. -// * If you are not concerned with linting, you may use an arrow function. -// */ -// render: function Render(args) { -// const [{ visible }, updateArgs] = useArgs() -// // const [visible, setVisibleState] = useState(args.visible) - -// function onVisibleChange() { -// updateArgs({ visible: !visible }) -// } - -// return ( -// <> -// -// onVisibleChange()} /> -// -// ) -// }, -// } - -// export const withCancelButton: Story = { -// args: { -// confirmString: 'project name', -// visible: false, -// variant: 'destructive', -// title: 'Are you sure you want to delete?', -// blockDeleteButton: false, -// // label: 'Try Me!', -// }, -// /** -// * 👇 To avoid linting issues, it is recommended to use a function with a capitalized name. -// * If you are not concerned with linting, you may use an arrow function. -// */ -// render: function Render(args) { -// const [{ visible }, updateArgs] = useArgs() -// // const [visible, setVisibleState] = useState(args.visible) - -// function onVisibleChange() { -// updateArgs({ visible: !visible }) -// } - -// return ( -// <> -// -// onVisibleChange()} /> -// -// ) -// }, -// } - -// export const withChildren: Story = { -// args: { -// confirmString: 'project name', -// visible: false, -// variant: 'destructive', -// title: 'Are you sure you want to delete?', -// }, -// /** -// * 👇 To avoid linting issues, it is recommended to use a function with a capitalized name. -// * If you are not concerned with linting, you may use an arrow function. -// */ -// render: function Render(args) { -// const [{ visible }, updateArgs] = useArgs() - -// function onVisibleChange() { -// updateArgs({ visible: !visible }) -// } - -// return ( -// <> -// -// onVisibleChange()} -// // onChange={onChange} - -// // isChecked={isChecked} -// > -//
    -//
    -// -//

    -// This is a paragraph with some bold text -//

    -//
    -//
    -// -//

    -// This is a paragraph with some bold text -//

    -//
    -//
    -// -//

    -// This is a paragraph with some bold text -//

    -//
    -//
    -//
    -// -// ) -// }, -// } - -// export const withSize: Story = { -// args: { -// confirmString: 'project name', -// visible: false, -// size: 'xlarge', -// variant: 'destructive', -// title: 'Are you sure you want to delete?', -// }, -// /** -// * 👇 To avoid linting issues, it is recommended to use a function with a capitalized name. -// * If you are not concerned with linting, you may use an arrow function. -// */ -// render: function Render(args) { -// const [{ visible }, updateArgs] = useArgs() - -// function onVisibleChange() { -// updateArgs({ visible: !visible }) -// } - -// return ( -// <> -// -// onVisibleChange()} /> -// -// ) -// }, -// } diff --git a/packages/ui-patterns/InnerSideMenu/index.tsx b/packages/ui-patterns/InnerSideMenu/index.tsx index d478cc2ef61..29e310b08de 100644 --- a/packages/ui-patterns/InnerSideMenu/index.tsx +++ b/packages/ui-patterns/InnerSideMenu/index.tsx @@ -145,8 +145,8 @@ const InnerSideBarFilterSearchInput = forwardRef< ref={ref} type="text" className={cn( - 'h-[28px] w-full', - 'text-xs', + 'h-[32px] md:h-[28px] w-full', + 'text-base md:text-xs', 'pl-7', 'pr-7', 'w-full', @@ -179,7 +179,7 @@ const InnerSideBarFilterSortDropdown = forwardRef< = ({ children, open = false, onOpenChange }) => { + const router = useRouter() + const { width } = useWindowSize() + + useEffect(() => { + onOpenChange(false) + }, [router?.asPath]) + + useEffect(() => { + onOpenChange(false) + }, [width]) + + return ( + + + }>{children} + + + ) +} + +export default MobileSheetNav diff --git a/packages/ui-patterns/MobileSheetNav/index.ts b/packages/ui-patterns/MobileSheetNav/index.ts new file mode 100644 index 00000000000..fd58868d481 --- /dev/null +++ b/packages/ui-patterns/MobileSheetNav/index.ts @@ -0,0 +1 @@ +export * from './MobileSheetNav' diff --git a/packages/ui-patterns/form/FormItemLayout/FormItemLayout.stories.tsx b/packages/ui-patterns/form/FormItemLayout/FormItemLayout.stories.tsx deleted file mode 100644 index 8c1cb4c0a7f..00000000000 --- a/packages/ui-patterns/form/FormItemLayout/FormItemLayout.stories.tsx +++ /dev/null @@ -1,283 +0,0 @@ -// import { zodResolver } from '@hookform/resolvers/zod' -// import { StoryContext, StoryObj } from '@storybook/react' -// import { useForm } from 'react-hook-form' -// import { -// Button, -// FormControl_Shadcn_, -// FormField_Shadcn_, -// FormItem_Shadcn_, -// Form_Shadcn_, -// PopoverContent_Shadcn_, -// PopoverTrigger_Shadcn_, -// Popover_Shadcn_, -// SelectContent_Shadcn_, -// SelectItem_Shadcn_, -// SelectTrigger_Shadcn_, -// SelectValue_Shadcn_, -// Select_Shadcn_, -// Calendar, -// cn, -// } from 'ui' -// import { z } from 'zod' -// import { transformSourceForm } from '../../lib/transformSource' -// import { FormItemLayout } from './FormItemLayout' -// import { Input } from '../../DataInputs/Input' -// import { Calendar as CalendarIcon } from 'lucide-react' -// import { format } from 'date-fns' - -// // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export -// export default { -// title: 'Form/FormItemLayout', -// component: FormItemLayout, -// decorators: [ -// (Story: any) => { -// return -// }, -// ], -// parameters: { -// // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout -// layout: 'centered', -// docs: { -// // controls: { exclude: ['style'] }, -// source: { -// language: 'tsx', -// transform: (code: string, StoryContext: StoryContext) => -// transformSourceForm(code, StoryContext).replace('_c', 'FormInput'), -// }, -// }, -// }, -// // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs -// tags: ['autodocs'], -// // More on argTypes: https://storybook.js.org/docs/api/argtypes -// // argTypes: {}, -// } - -// type Story = StoryObj - -// export const Primary: Story = { -// render: function Render(args) { -// const formSchema = z.object({ -// username: z.string().min(2, { -// message: 'Username must be at least 2 characters.', -// }), -// }) - -// // 1. Define your form. -// const form = useForm>({ -// resolver: zodResolver(formSchema), -// defaultValues: { -// username: '', -// }, -// }) - -// // 2. Define a submit handler. -// function onSubmit(values: z.infer) { -// // Do something with the form values. -// // ✅ This will be type-safe and validated. -// console.log(values) -// // action('form form.handleSubmit(onSubmit)')(values) -// } -// return ( -// -//
    -// ( -// -// -// -// -// -// )} -// /> -// -// -//
    -// ) -// }, -// args: { -// label: 'Username', -// description: 'this is the description', -// labelOptional: 'optional', -// }, -// } - -// export const withSelect: Story = { -// render: function Render(args) { -// const FormSchema = z.object({ -// email: z -// .string({ -// required_error: 'Please select an email to display.', -// }) -// .email(), -// }) - -// const form = useForm>({ -// resolver: zodResolver(FormSchema), -// }) - -// // 2. Define a submit handler. -// function onSubmit(values: z.infer) { -// // Do something with the form values. -// // ✅ This will be type-safe and validated. -// console.log(values) -// // action('form form.handleSubmit(onSubmit)')(values) -// } - -// return ( -// -//
    -// ( -// -// -// -// -// -// -// -// -// m@example.com -// m@google.com -// m@support.com -// -// -// -// )} -// /> -// -// -//
    -// ) -// }, -// args: { -// label: 'Username', -// description: 'this is the description', -// labelOptional: 'optional', -// }, -// } - -// export const withLayoutChange: Story = { -// render: function Render(args) { -// const formSchema = z.object({ -// username: z.string().min(2, { -// message: 'Username must be at least 2 characters.', -// }), -// }) - -// // 1. Define your form. -// const form = useForm>({ -// resolver: zodResolver(formSchema), -// defaultValues: { -// username: '', -// }, -// }) - -// // 2. Define a submit handler. -// function onSubmit(values: z.infer) { -// // Do something with the form values. -// // ✅ This will be type-safe and validated. -// console.log(values) -// // action('form form.handleSubmit(onSubmit)')(values) -// } -// return ( -// -//
    -// ( -// -// -// -// -// -// )} -// /> -// -// -//
    -// ) -// }, -// args: { -// layout: 'horizontal', -// label: 'Username', -// description: 'this is the description', -// labelOptional: 'optional', -// }, -// } - -// export const withCalendar: Story = { -// render: function Render(args) { -// const FormSchema = z.object({ -// dob: z.date({ -// required_error: 'A date of birth is required.', -// }), -// }) - -// const form = useForm>({ -// resolver: zodResolver(FormSchema), -// }) -// // 2. Define a submit handler. -// function onSubmit(values: z.infer) { -// // Do something with the form values. -// // ✅ This will be type-safe and validated. -// console.log(values) -// // action('form form.handleSubmit(onSubmit)')(values) -// } -// return ( -// -//
    -// ( -// -// -// -// -// -// -// -// -// date > new Date() || date < new Date('1900-01-01')} -// initialFocus -// /> -// -// -// -// )} -// /> -// -// -//
    -// ) -// }, -// args: { -// layout: 'vertical', -// label: 'Date of birth', -// description: 'Your date of birth is used to calculate your age.', -// labelOptional: 'optional', -// }, -// } diff --git a/packages/ui-patterns/form/FormLayout.tsx b/packages/ui-patterns/form/FormLayout.tsx deleted file mode 100644 index 9c2e10549b9..00000000000 --- a/packages/ui-patterns/form/FormLayout.tsx +++ /dev/null @@ -1,357 +0,0 @@ -import { zodResolver } from '@hookform/resolvers/zod' -import { Box, FileWarning } from 'lucide-react' -import { useForm } from 'react-hook-form' -import { - Badge, - Button, - Checkbox_Shadcn_, - Form_Shadcn_, - FormControl_Shadcn_, - FormField_Shadcn_, - RadioGroup_Shadcn_, - RadioGroupItem_Shadcn_, - Select_Shadcn_, - SelectContent_Shadcn_, - SelectItem_Shadcn_, - SelectTrigger_Shadcn_, - SelectValue_Shadcn_, - Separator, - Switch, -} from 'ui' -import { z } from 'zod' -import { Input } from '../DataInputs/Input' -import { InfoTooltip } from '../info-tooltip' -import { FormItemLayout } from './FormItemLayout/FormItemLayout' - -/** - * This is helper file for the FormLayout stories. - */ -const items = [ - { - id: 'recents', - label: 'Recents', - }, - { - id: 'home', - label: 'Home', - }, - { - id: 'applications', - label: 'Applications', - }, - { - id: 'desktop', - label: 'Desktop', - }, - { - id: 'downloads', - label: 'Downloads', - }, - { - id: 'documents', - label: 'Documents', - }, -] as const - -export const Page = () => { - const FormSchema = z.object({ - username: z.string().min(2, { - message: 'Username must be at least 2 characters.', - }), - kevins_input: z.string().min(6, { - message: 'Username must be at least 6 characters.', - }), - email: z - .string({ - required_error: 'Please select an email to display.', - }) - .email(), - consistent_settings: z.boolean().default(false).optional(), - switch_option: z.boolean().default(false).optional(), - items: z.array(z.string()).refine((value) => value.some((item) => item), { - message: 'You have to select at least one item.', - }), - type: z.enum(['all', 'mentions', 'none'], { - required_error: 'You need to select a notification type.', - }), - }) - - const form = useForm>({ - resolver: zodResolver(FormSchema), - defaultValues: { - username: '', - items: ['recents', 'home'], - }, - }) - - function onSubmit(data: z.infer) { - console.log(data) - } - - const UserIcon = () => { - return ( -
    - -
    - ) - } - - return ( - -
    -
    -

    Welcome

    -

    Please fill in the following

    -
    - - - - ( - You can also rename this later.} - > - - } - placeholder="mildtomato" - {...field} - /> - - - )} - /> - - - - ( - - - - - - )} - /> - - - - ( - - - - - - - - - -
    - - m@example.com -
    -
    - -
    - - UserIconm@google.com -
    -
    - -
    - - m@support.com -
    -
    -
    -
    -
    - )} - /> - - - - ( - - - } - placeholder="Needs to be 6 long" - {...field} - /> - - - )} - /> - - - - ( - - - Danger zone! - - } - label="Use consistent settings" - description="This is your public display name." - layout="flex" - > - - - - - )} - /> - - - - ( - Please give me info} - > - {/*
    - Sidebar - - Select the items you want to display in the sidebar. - -
    */} - {items.map((item) => ( - { - return ( - - - { - return checked - ? field.onChange([...field.value, item.id]) - : field.onChange(field.value?.filter((value) => value !== item.id)) - }} - /> - - - ) - }} - /> - ))} - {/* */} -
    - )} - /> - - ( - - - - - - - - - - - - - - - - - - - - - - )} - /> - - -
    - -
    - -
    - ) -} diff --git a/packages/ui-patterns/form/Page.stories.tsx b/packages/ui-patterns/form/Page.stories.tsx deleted file mode 100644 index 345c32effb3..00000000000 --- a/packages/ui-patterns/form/Page.stories.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// import { within, userEvent, expect } from '@storybook/test' -import { TooltipProvider_Shadcn_ } from 'ui' -import { Page } from './FormLayout' - -export default { - title: 'Form Examples/One', - component: Page, - parameters: { - // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout - layout: 'centered', - }, - decorators: [ - (Story: any) => { - return ( - - - - ) - }, - ], -} - -export const StateOne = {} - -// More on interaction testing: https://storybook.js.org/docs/writing-tests/interaction-testing -// export const LoggedIn = { -// play: async ({ canvasElement }) => { -// const canvas = within(canvasElement) -// const loginButton = canvas.getByRole('button', { name: /Log in/i }) -// await expect(loginButton).toBeInTheDocument() -// await userEvent.click(loginButton) -// await expect(loginButton).not.toBeInTheDocument() - -// const logoutButton = canvas.getByRole('button', { name: /Log out/i }) -// await expect(logoutButton).toBeInTheDocument() -// }, -// } diff --git a/packages/ui-patterns/form/Page2.stories.tsx b/packages/ui-patterns/form/Page2.stories.tsx deleted file mode 100644 index 84956639fcb..00000000000 --- a/packages/ui-patterns/form/Page2.stories.tsx +++ /dev/null @@ -1,37 +0,0 @@ -// import { within, userEvent, expect } from '@storybook/test' -import { TooltipProvider_Shadcn_ } from 'ui' -import { Page } from './FormLayout2' - -export default { - title: 'Form Examples/Side Panel Function Create', - component: Page, - parameters: { - // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout - layout: 'centered', - }, - decorators: [ - (Story: any) => { - return ( - - - - ) - }, - ], -} - -export const StateOne = {} - -// More on interaction testing: https://storybook.js.org/docs/writing-tests/interaction-testing -// export const LoggedIn = { -// play: async ({ canvasElement }) => { -// const canvas = within(canvasElement) -// const loginButton = canvas.getByRole('button', { name: /Log in/i }) -// await expect(loginButton).toBeInTheDocument() -// await userEvent.click(loginButton) -// await expect(loginButton).not.toBeInTheDocument() - -// const logoutButton = canvas.getByRole('button', { name: /Log out/i }) -// await expect(logoutButton).toBeInTheDocument() -// }, -// } diff --git a/packages/ui-patterns/index.tsx b/packages/ui-patterns/index.tsx index c77dbacf424..5efd08f6ce5 100644 --- a/packages/ui-patterns/index.tsx +++ b/packages/ui-patterns/index.tsx @@ -8,6 +8,7 @@ export * from './CountdownWidget' export * from './ExpandableVideo' export * from './GlassPanel' export * from './IconPanel' +export * from './MobileSheetNav' export * from './PrivacySettings' export * from './ThemeToggle' export * from './TweetCard' diff --git a/packages/ui-patterns/lib/transformSource.tsx b/packages/ui-patterns/lib/transformSource.tsx deleted file mode 100644 index 6bd9a4e8783..00000000000 --- a/packages/ui-patterns/lib/transformSource.tsx +++ /dev/null @@ -1,210 +0,0 @@ -// import { StoryContext } from '@storybook/react' - -// export function transformSourceForm(code: string, StoryContext: StoryContext) { -// let _code = code -// // _code = _code.replace(' {}} -// control={{ -// _defaultValues: { -// username: '' -// }, -// _executeSchema: () => {}, -// _fields: {}, -// _formState: { -// dirtyFields: {}, -// errors: {}, -// isDirty: false, -// isLoading: false, -// isSubmitSuccessful: false, -// isSubmitted: false, -// isSubmitting: false, -// isValid: false, -// isValidating: false, -// submitCount: 0, -// touchedFields: {} -// }, -// _formValues: { -// username: '' -// }, -// _getDirty: () => {}, -// _getFieldArray: () => {}, -// _getWatch: () => {}, -// _names: { -// array: {}, -// mount: {}, -// unMount: {}, -// watch: {} -// }, -// _options: { -// defaultValues: { -// username: '' -// }, -// mode: 'onSubmit', -// reValidateMode: 'onChange', -// resolver: () => {}, -// shouldFocusError: true -// }, -// _proxyFormState: { -// dirtyFields: false, -// errors: false, -// isDirty: false, -// isValid: false, -// isValidating: false, -// touchedFields: false -// }, -// _removeUnmounted: () => {}, -// _reset: () => {}, -// _resetDefaultValues: () => {}, -// _state: { -// action: false, -// mount: false, -// watch: false -// }, -// _subjects: { -// array: { -// next: () => {}, -// observers: [], -// subscribe: () => {}, -// unsubscribe: () => {} -// }, -// state: { -// next: () => {}, -// observers: [], -// subscribe: () => {}, -// unsubscribe: () => {} -// }, -// values: { -// next: () => {}, -// observers: [], -// subscribe: () => {}, -// unsubscribe: () => {} -// } -// }, -// _updateDisabledField: () => {}, -// _updateFieldArray: () => {}, -// _updateFormState: () => {}, -// _updateValid: () => {}, -// getFieldState: () => {}, -// handleSubmit: () => {}, -// register: () => {}, -// setError: () => {}, -// unregister: () => {} -// }} -// formState={{ -// defaultValues: { -// username: '' -// } -// }} -// getFieldState={() => {}} -// getValues={() => {}} -// handleSubmit={() => {}} -// register={() => {}} -// reset={() => {}} -// resetField={() => {}} -// setError={() => {}} -// setFocus={() => {}} -// setValue={() => {}} -// trigger={() => {}} -// unregister={() => {}} -// watch={() => {}} -// >`, -// `` -// ) -// _code = _code.replace( -// ` -// control={{ -// _defaultValues: { -// username: '' -// }, -// _executeSchema: () => {}, -// _fields: {}, -// _formState: { -// dirtyFields: {}, -// errors: {}, -// isDirty: false, -// isLoading: false, -// isSubmitSuccessful: false, -// isSubmitted: false, -// isSubmitting: false, -// isValid: false, -// isValidating: false, -// submitCount: 0, -// touchedFields: {} -// }, -// _formValues: { -// username: '' -// }, -// _getDirty: () => {}, -// _getFieldArray: () => {}, -// _getWatch: () => {}, -// _names: { -// array: {}, -// mount: {}, -// unMount: {}, -// watch: {} -// }, -// _options: { -// defaultValues: { -// username: '' -// }, -// mode: 'onSubmit', -// reValidateMode: 'onChange', -// resolver: () => {}, -// shouldFocusError: true -// }, -// _proxyFormState: { -// defaultValues: 'all', -// dirtyFields: false, -// errors: false, -// isDirty: false, -// isValid: false, -// isValidating: false, -// touchedFields: false -// }, -// _removeUnmounted: () => {}, -// _reset: () => {}, -// _resetDefaultValues: () => {}, -// _state: { -// action: false, -// mount: false, -// watch: false -// }, -// _subjects: { -// array: { -// next: () => {}, -// observers: [], -// subscribe: () => {}, -// unsubscribe: () => {} -// }, -// state: { -// next: () => {}, -// observers: [], -// subscribe: () => {}, -// unsubscribe: () => {} -// }, -// values: { -// next: () => {}, -// observers: [], -// subscribe: () => {}, -// unsubscribe: () => {} -// } -// }, -// _updateDisabledField: () => {}, -// _updateFieldArray: () => {}, -// _updateFormState: () => {}, -// _updateValid: () => {}, -// getFieldState: () => {}, -// handleSubmit: () => {}, -// register: () => {}, -// setError: () => {}, -// unregister: () => {} -// }}`, -// ` -// control={form.control}` -// ) - -// _code = _code.replace('', '') -// return _code -// } diff --git a/packages/ui/.babelrc.json b/packages/ui/.babelrc.json deleted file mode 100644 index 104d722287c..00000000000 --- a/packages/ui/.babelrc.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "chrome": "58" - } - } - ], - "@babel/preset-typescript", - "@babel/preset-react" - ], - "plugins": [] -} diff --git a/packages/ui/README.tokens.md b/packages/ui/README.tokens.md deleted file mode 100644 index bcc8a0bd7ec..00000000000 --- a/packages/ui/README.tokens.md +++ /dev/null @@ -1 +0,0 @@ -# tokens diff --git a/packages/ui/components.json b/packages/ui/components.json index 15ad1e04d39..9f1db7ce50e 100644 --- a/packages/ui/components.json +++ b/packages/ui/components.json @@ -4,7 +4,7 @@ "rsc": false, "tailwind": { "config": "./../config/tailwind-shadcn.config.js", - "css": ".storybook/shadcn.css", + "css": "./build/css/source/global.css", "baseColor": "gray", "cssVariables": true }, diff --git a/packages/ui/package.json b/packages/ui/package.json index df7ad43ac4c..366c09cf616 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -94,11 +94,6 @@ "zod": "^3.22.3" }, "devDependencies": { - "@babel/plugin-syntax-flow": "^7.21.4", - "@babel/preset-env": "^7.21.4", - "@babel/preset-flow": "^7.21.4", - "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.21.4", "@ctrl/tinycolor": "^3.4.0", "@testing-library/jest-dom": "^6.1.3", "@testing-library/react": "^14.0.0", diff --git a/packages/ui/postcss.config.js b/packages/ui/postcss.config.js deleted file mode 100644 index 29c447cb54b..00000000000 --- a/packages/ui/postcss.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('config/postcss.config') diff --git a/packages/ui/src/components/Accordion/Accordion.stories.tsx b/packages/ui/src/components/Accordion/Accordion.stories.tsx deleted file mode 100644 index 41eea4d7350..00000000000 --- a/packages/ui/src/components/Accordion/Accordion.stories.tsx +++ /dev/null @@ -1,184 +0,0 @@ -import React from 'react' - -import Accordion, { AccordionProps } from './Accordion' -import { Badge } from '../Badge' - -export default { - title: 'Displays/Accordion', - component: Accordion, -} - -export const OneItem = (args: AccordionProps) => ( - - - - Title of the thing - - Test badge - - } - id="first" - > - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - - - -) - -export const MultipleItems = (args: AccordionProps) => ( - - - Title of the thing - - } - id={'1'} - > - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - - - - Title of the thing - - } - id={'2'} - > - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - - - - Title of the thing - - } - id={'3'} - > - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - - - - Title of the thing - - } - id={'4'} - > - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - - - -) - -export const Bordered = (args: AccordionProps) => ( - - - Title of the thing - - } - id={'1'} - > - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - - - - Title of the thing - - } - id={'2'} - > - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - - - - Title of the thing - - } - id={'3'} - > - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - - - - Title of the thing - - } - id={'4'} - > - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - - - -) - -Bordered.args = { - type: 'bordered', -} - -export const LeftAlignedChevron = (args: AccordionProps) => ( - - - First item - Test badge - - } - id="first" - > - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - - - - Second item - - } - id="second" - > - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - - - -) - -// export const withDefaultActive = Multiple.bind({}) -// withDefaultActive.args = { -// defaultActiveId: [1], -// } - -// export const withIconLeft = Multiple.bind({}) -// withIconLeft.args = { -// iconPosition: 'left', -// } - -// export const withCustomIcon = Multiple.bind({}) -// withCustomIcon.args = { -// icon: , -// } diff --git a/packages/ui/src/components/Alert/Alert.stories.tsx b/packages/ui/src/components/Alert/Alert.stories.tsx deleted file mode 100644 index 42e52242b83..00000000000 --- a/packages/ui/src/components/Alert/Alert.stories.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import React from 'react' - -import { Alert, AlertProps } from './Alert' - -export default { - title: 'Displays/Alerts', - component: Alert, -} - -export const Neutral = (args: AlertProps) => ( - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - -) - -export const Success = (args: AlertProps) => ( - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - -) - -export const SuccessWithIcon = (args: AlertProps) => ( - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - -) - -export const Danger = (args: AlertProps) => ( - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - -) - -export const DangerWithIcon = (args: AlertProps) => ( - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - -) - -export const Info = (args: AlertProps) => ( - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - -) - -export const InfoWithIcon = (args: AlertProps) => ( - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - -) - -export const Warning = (args: AlertProps) => ( - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - -) - -export const WarningWithIcon = (args: AlertProps) => ( - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - -) - -export const Closable = (args: AlertProps) => ( - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - -) - -export const WithAction = (args: AlertProps) => ( - - Lorem ipsum dolor sit amet consectetur adipisicing elit. Consequatur amet labore. - -) - -Neutral.args = { - title: 'Success alert with icon', - withIcon: true, -} - -Success.args = { - title: 'Success alert', - variant: 'success', -} - -SuccessWithIcon.args = { - title: 'Success alert with icon', - variant: 'success', - withIcon: true, -} - -Danger.args = { - title: 'Danger alert', - variant: 'danger', -} - -DangerWithIcon.args = { - title: 'Danger alert with icon', - variant: 'danger', - withIcon: true, -} - -Info.args = { - title: 'Info alert', - variant: 'info', -} - -InfoWithIcon.args = { - title: 'Info alert with icon', - variant: 'info', - withIcon: true, -} - -Warning.args = { - title: 'Warning alert', - variant: 'warning', -} - -WarningWithIcon.args = { - title: 'Warning alert with icon', - variant: 'warning', - withIcon: true, -} - -Closable.args = { - title: 'Closable alert', - closable: true, -} - -WithAction.args = { - title: 'Alert with action', - variant: 'warning', - withIcon: true, - actions:
    Hello
    , -} diff --git a/packages/ui/src/components/Breadcrumb/Breadcrumb.stories.tsx b/packages/ui/src/components/Breadcrumb/Breadcrumb.stories.tsx deleted file mode 100644 index 681539b05b7..00000000000 --- a/packages/ui/src/components/Breadcrumb/Breadcrumb.stories.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react' -import { Breadcrumb } from './../../../' - -export default { - title: 'Navigation/Breadcrumb', - component: Breadcrumb, -} - -export const Default = ({ children, ...args }: any) => {children} - -Default.args = { - children: [ - Brothers, - Grimm, - Hansel, - Gretel, - ], -} diff --git a/packages/ui/src/components/Button/Button.stories.tsx b/packages/ui/src/components/Button/Button.stories.tsx deleted file mode 100644 index 64ef8392507..00000000000 --- a/packages/ui/src/components/Button/Button.stories.tsx +++ /dev/null @@ -1,176 +0,0 @@ -// import React from 'react' -// import { Button } from './../Button' - -// import type { Meta, StoryObj } from '@storybook/react' - -// import { ButtonProps } from './../Button' - -// import { IconActivity } from '../Icon/icons/IconActivity' - -// const meta: Meta = { -// /* 👇 The title prop is optional. -// * See https://storybook.js.org/docs/react/configure/overview#configure-story-loading -// * to learn how to generate automatic titles -// */ -// title: 'Old Primitives/Button', -// component: Button, -// } - -// export default meta - -// type Story = StoryObj - -// /* -// *👇 Render functions are a framework specific feature to allow you control on how the component renders. -// * See https://storybook.js.org/docs/react/api/csf -// * to learn how to use render functions. -// */ - -// export const Primary = { -// args: { -// children: 'Hello world', -// type: 'primary', -// }, -// render: ({ children, type }: ButtonProps) => , -// } -// export const Default = { -// args: { -// children: 'Hello world', -// type: 'default', -// }, -// render: ({ children, type }: ButtonProps) => , -// } - -// export const Alternative = { -// args: { -// children: 'Hello world', -// type: 'alternative', -// }, -// render: ({ children, type }: ButtonProps) => , -// } - -// export const Outline = { -// args: { -// children: 'Hello world', -// type: 'outline', -// }, -// render: ({ children, type }: ButtonProps) => , -// } - -// export const Secondary = { -// args: { -// children: 'Hello world', -// type: 'secondary', -// }, -// render: ({ children, type }: ButtonProps) => , -// } - -// export const Dashed = { -// args: { -// children: 'Hello world', -// type: 'dashed', -// }, -// render: ({ children, type }: ButtonProps) => , -// } - -// export const Link = { -// args: { -// children: 'Hello world', -// type: 'link', -// }, -// render: ({ children, type }: ButtonProps) => , -// } - -// export const Text = { -// args: { -// children: 'Hello world', -// type: 'text', -// }, -// render: ({ children, type }: ButtonProps) => , -// } - -// export const Danger = { -// args: { -// children: 'Hello world', -// type: 'danger', -// }, -// render: ({ children, type }: ButtonProps) => , -// } - -// export const Block = { -// args: { -// children: 'Hello world', -// type: 'primary', -// block: true, -// }, -// render: ({ children, type, block }: ButtonProps) => ( -// -// ), -// } - -// export const Disabled = { -// args: { -// children: 'Hello world', -// type: 'primary', -// disabled: true, -// }, -// render: ({ children, type, disabled }: ButtonProps) => ( -// -// ), -// } - -// export const Sizes = { -// args: { -// children: 'Change the size below', -// type: 'primary', -// size: 'large', -// }, -// render: ({ children, type, size }: ButtonProps) => ( -// -// ), -// } - -// export const LeftIcon = { -// args: { -// children: 'Change the size below', -// type: 'default', -// icon: , -// }, -// render: ({ children, type, icon }: ButtonProps) => ( -// -// ), -// } - -// export const RightIcon = { -// args: { -// children: 'Change the size below', -// type: 'primary', -// iconRight: , -// }, -// render: ({ children, type, iconRight }: ButtonProps) => ( -// -// ), -// } - -// export const Loading = { -// args: { -// children: 'Change the size below', -// type: 'primary', -// loading: true, -// }, -// render: ({ children, type, loading }: ButtonProps) => ( -// -// ), -// } diff --git a/packages/ui/src/components/Card/Card.stories.tsx b/packages/ui/src/components/Card/Card.stories.tsx deleted file mode 100644 index 36fd25b1945..00000000000 --- a/packages/ui/src/components/Card/Card.stories.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react' -import Typography from '../Typography' -// import { AutoForm } from 'uniforms' - -import { Card } from './' - -export default { - title: 'Displays/Card', - component: Card, -} - -export const Default = (args: any) => ( - - Card content - Card content - Card content - -) - -export const withCover = (args: any) => ( - - Sub title here - To Do List with Vue.JS - -) - -export const withMeta = (args: any) => ( - - - -) - -export const withHover = (args: any) => ( - - - -) - -Default.args = { - title: 'I am a title', - titleExtra: Learn more, -} - -withCover.args = { - cover: ( - {'title'} - ), -} - -withMeta.args = { - title: 'title is here' -} - -withHover.args = { - title: 'This card can hover', - hoverable: true -} diff --git a/packages/ui/src/components/Checkbox/Checkbox.stories.tsx b/packages/ui/src/components/Checkbox/Checkbox.stories.tsx deleted file mode 100644 index 55979991380..00000000000 --- a/packages/ui/src/components/Checkbox/Checkbox.stories.tsx +++ /dev/null @@ -1,128 +0,0 @@ -import { Checkbox } from '.' - -export default { - title: 'Data Input/Checkbox', - component: Checkbox, -} - -export const Default = (args: any) => - -export const withGroup = (args: any) => ( - - - - - -) - -export const withGroupHorizontal = (args: any) => ( - - - - - -) - -export const size = (args: any) => ( - - - - - -) - -export const withBeforeAndAfterLabels = (args: any) => - -Default.args = { - label: 'This is the label', - description: 'This is the description', -} - -withGroup.args = { - id: 'checkobox-q', - label: 'This is the label', - description: 'This is the description', - disabled: false, - size: 'medium', - className: 'font-sans', - layout: 'vertical', -} - -withGroupHorizontal.args = { - id: 'checkobox-q', - label: 'This is the label', - description: 'This is the description', - disabled: false, - className: 'font-sans', - layout: 'horizontal', -} - -size.args = { - id: 'checkobox-q', - label: 'Control the size of the checkboxes', - description: 'You can add a size just the Group component and it will affect the children', - disabled: false, - className: 'font-sans', - layout: 'horizontal', - size: 'tiny', -} - -withBeforeAndAfterLabels.args = { - label: 'Label', - beforeLabel: 'Before : ', - afterLabel: ' : After', - options: [ - { - label: 'Label', - beforeLabel: 'Before : ', - afterLabel: ' : After', - description: 'Description', - }, - ], - className: 'font-sans', -} diff --git a/packages/ui/src/components/Collapsible/Collapsible.stories.tsx b/packages/ui/src/components/Collapsible/Collapsible.stories.tsx deleted file mode 100644 index 24f809563a7..00000000000 --- a/packages/ui/src/components/Collapsible/Collapsible.stories.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Collapsible } from './' -import { IconChevronUp } from '../../components/Icon/icons/IconChevronUp' -import { Button } from './../Button' - -export default { - title: 'Displays/Collapsible', - component: Collapsible, -} - -export const Default = (args: any) => { - return ( - <> - - - -
    - - - -
    -

    I am content

    -

    I am content

    -

    I am content

    -

    I am content

    -
    -
    - - - - - - -
    -

    I am content

    -

    I am content

    -

    I am content

    -

    I am content

    -
    -
    -
    - - ) -} - -Default.args = { - placeholder: 'Type text here ...', - disabled: false, - label: 'Name', - layout: 'vertical', -} diff --git a/packages/ui/src/components/ContextMenu/ContextMenu.stories.tsx b/packages/ui/src/components/ContextMenu/ContextMenu.stories.tsx deleted file mode 100644 index 10b4f3ef8eb..00000000000 --- a/packages/ui/src/components/ContextMenu/ContextMenu.stories.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import React, { useState } from 'react' -import { Button } from '../Button' -import { Divider } from '../Divider' -import { IconSettings } from '../Icon/icons/IconSettings' -import { IconLogOut } from '../Icon/icons/IconLogOut' - -import { ContextMenu } from '.' -import { IconLogIn } from '../Icon/icons/IconLogIn' - -export default { - title: 'Navigation/ContextMenu', - component: ContextMenu, -} - -const triggerArea = ( -
    - Right click this area -
    -) - -export const Default = (args: any) => ( -
    - -
    - Signed in as - tom@example.com -
    - , - , - Group label, - console.log('clicked')}>Account, - Settings, - , - }>Log out, - ]} - > - {triggerArea} -
    -
    -) - -Default.args = {} - -export const doNotcloseOverlay = (args: any) => ( -
    - - Signed in as - tom@example.com - , - , - Account, - Settings, - - - , - ]} - > - {triggerArea} - -
    -) - -doNotcloseOverlay.args = {} - -export const withCustomStyles = (args: any) => ( -
    - - Signed in as - tom@example.com - , - , - Account, - Settings, - , - - - , - ]} - > - {triggerArea} - -
    -) - -withCustomStyles.args = {} - -export const Checkbox = (args: any) => { - const [checked, setChecked] = useState(false) - - return ( -
    - }>Account, - Settings, - , - - Show subtitles - , - ]} - > - {triggerArea} - -
    - ) -} - -export const Radio = (args: any) => { - const [value, setValue] = useState('red') - - return ( -
    - - Red - Blue - Green - , - ]} - > - {triggerArea} - -
    - ) -} diff --git a/packages/ui/src/components/Divider/Divider.stories.tsx b/packages/ui/src/components/Divider/Divider.stories.tsx deleted file mode 100644 index eb0c6246ffd..00000000000 --- a/packages/ui/src/components/Divider/Divider.stories.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { Button } from '../Button' -import { Divider } from './' - -export default { - title: 'Utilities/Divider', - component: Divider, -} - -export const Default = (args: any) => - -export const withCenterText = (args: any) => Hello world - -export const withLeftText = (args: any) => Hello world - -export const withRightText = (args: any) => Hello world - -export const lighterColor = (args: any) => - -export const vertical = (args: any) => ( -
    -
    - - - - - - Some text - - -
    -
    -) - -Default.args = {} - -withCenterText.args = {} - -withLeftText.args = { - orientation: 'left', -} - -withRightText.args = { - orientation: 'right', -} - -lighterColor.args = { - light: true, -} - -vertical.args = { - type: 'vertical', -} diff --git a/packages/ui/src/components/Form/Form.stories.tsx b/packages/ui/src/components/Form/Form.stories.tsx deleted file mode 100644 index ad48d26524c..00000000000 --- a/packages/ui/src/components/Form/Form.stories.tsx +++ /dev/null @@ -1,487 +0,0 @@ -import { Form } from '.' -import { Input, Button, InputNumber, Toggle, Checkbox, Radio, Select, Listbox } from '../../index' - -import * as Yup from 'yup' -import { User } from '../Icon/IconImportHandler' -import React from 'react' - -export default { - title: 'Data Input/Form', - component: Form, -} - -interface Values { - email: string - last_name: string - profession: string - number: number | undefined - // remember_me: boolean - checkbox_alone: boolean - favorite_food: string - check_3: boolean - check_2: boolean - check_1: boolean - textarea: string - toggle: boolean - people: number | undefined -} - -const INITIAL_VALUES: Values = { - email: '', - last_name: '', - profession: '', - number: undefined, - checkbox_alone: false, - // remember_me: false, - favorite_food: '', - check_3: false, - check_2: true, - check_1: false, - textarea: '', - toggle: true, - people: undefined, -} - -export const InputLevelValidation = () => { - function validateEmail(value: string) { - console.log('validateEmail running') - let error - if (!value) { - error = 'Required' - } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value)) { - error = 'Invalid email address' - } - return error - } - - return ( -
    { - setTimeout(() => { - alert(JSON.stringify(values, null, 2)) - setSubmitting(false) - }, 400) - }} - > - {({ isSubmitting }: { isSubmitting: boolean }) => ( -
    - - {/* */} - -
    - )} -
    - ) -} - -export const InputLevelValidationYip = () => { - // validation schema - const SignupSchema = Yup.object().shape({ - firstname: Yup.string().min(2, 'Too Short!').max(50, 'Too Long!').required('Required'), - lastname: Yup.string().min(2, 'Too Short!').max(50, 'Too Long!').required('Required'), - email: Yup.string().email('Invalid email').required('Required'), - }) - - return ( -
    { - setTimeout(() => { - alert(JSON.stringify(values, null, 2)) - setSubmitting(false) - }, 400) - }} - > - {({ isSubmitting }: any) => ( -
    - - - - -
    - )} -
    - ) -} - -const people = [ - { - value: 1, - label: 'Wade Cooper', - avatar: - 'https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - { - value: 2, - label: 'Arlene Mccoy', - avatar: - 'https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - { - value: 3, - label: 'Devon Webb', - avatar: - 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.25&w=256&h=256&q=80', - }, - { - value: 4, - label: 'Tom Cook', - avatar: - 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - { - value: 5, - label: 'Tanya Fox', - avatar: - 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - { - value: 6, - label: 'Hellen Schmidt', - avatar: - 'https://images.unsplash.com/photo-1487412720507-e7ab37603c6f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - { - value: 7, - label: 'Caroline Schultz', - avatar: - 'https://images.unsplash.com/photo-1568409938619-12e139227838?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - { - value: 8, - label: 'Mason Heaney', - avatar: - 'https://images.unsplash.com/photo-1531427186611-ecfd6d936c79?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - { - value: 9, - label: 'Claudie Smitham', - avatar: - 'https://images.unsplash.com/photo-1584486520270-19eca1efcce5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - { - value: 10, - label: 'Emil Schaefer', - avatar: - 'https://images.unsplash.com/photo-1561505457-3bcad021f8ee?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, -] - -export const LargerExample = () => { - return ( - <> -
    { - setTimeout(() => { - alert(JSON.stringify(values, null, 2)) - setSubmitting(false) - }, 400) - }} - validate={(values: Values) => { - const errors: any = {} - - console.log('values for validation', values) - - if (!values.email) { - errors.email = 'Required' - } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)) { - errors.email = 'Invalid email address' - } - - if (!values.profession) { - errors.profession = 'Required' - } - - if (!values.textarea) { - errors.textarea = 'Required' - } - - if (!values.number) { - errors.number = 'Required' - } - - if (values.number <= 13) { - errors.number = 'Must be a number above 13' - } - - if (values.number >= 32) { - errors.number = 'Must be a number below 32' - } - - if (!values.favorite_food) { - errors.favorite_food = 'You must select a favourite food' - } - - if (!values.toggle) { - errors.toggle = 'This needs to be turned on' - } - - // if (!values.people) { - // errors.people = 'Please select a person' - // } - - // if (values.remember_me) - - return errors - }} - > - {({ isSubmitting }: any) => ( - <> -
    -

    {isSubmitting ? 'submitting' : 'not submitting'}

    - - - - - - - - - - - - - - - - - - - - {people.map((person) => { - return ( - ( - - )} - children={({ active, selected }: any) => { - // console.log('selected', selected) - // console.log('active', active) - return {person.label} - }} - /> - ) - })} - - - -
    - - )} -
    - - ) -} - -export const CardForm = () => { - // panel - const Panel = ({ - children, - header, - footer, - }: { - children: React.ReactNode - header?: React.ReactNode - footer?: React.ReactNode - }) => ( -
    - {header &&
    {header}
    } -
    {children}
    - {footer} -
    - ) - - const Section = ({ - children, - header, - }: { - children: React.ReactNode - header?: React.ReactNode - }) => ( -
    - {header} - {children} -
    - ) - - return ( - <> -
    { - setTimeout(() => { - alert(JSON.stringify(values, null, 2)) - setSubmitting(false) - }, 400) - }} - validate={(values: Values) => { - const errors: any = {} - - return errors - }} - > - {({ isSubmitting }: any) => ( - -

    Custom Options

    -

    - These settings apply to payment pages you create using Stripe Checkout and Payment - Links. -

    - - } - footer={ - <> -
    -
    - - -
    - - } - > - {/*

    {isSubmitting ? 'submitting' : 'not submitting'}

    */} - {/*
    */} -
    Faster checkout} - > - -
    -
    -
    Faster checkout} - > - - - - -
    -
    -
    Faster checkout} - > -
    - - - This can be found in{' '} - - the developer settings - - - } - /> -
    -
    -
    - )} -
    - - ) -} - -export { default as AuthForm } from './examples/AuthForm' diff --git a/packages/ui/src/components/Icon/Icon.stories.tsx b/packages/ui/src/components/Icon/Icon.stories.tsx deleted file mode 100644 index 8fcc2f8b607..00000000000 --- a/packages/ui/src/components/Icon/Icon.stories.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React from 'react' - -import { IconMail, Typography } from './../../index' - -export default { - title: 'General/Icon', - component: IconMail, -} - -export const Default = (args: any) => ( -
    -
    - - - -
    -
    -) - -Default.args = { - size: 16, - strokeWidth: 2, -} - -// export const IconList = (args: any) => ( -// <> -//
    -// This icons uses react-feather from Feather Icons. For detail you can check -// it here and{' '} -// here -//
    -//
    -// {LIST_ICONS.map((icon) => ( -//
    -// -// {icon} -//
    -// ))} -//
    -// -// ) - -// IconList.args = { -// size: 16, -// strokeWidth: 2, -// } - -// // const SvgMessagesIcon = () => ( -// // -// // -// // -// // ) - -// // export const IconWithSVG = (args: any) => ( -// // } /> -// // ) - -// // IconWithSVG.args = { -// // size: 'xxxlarge', -// // strokeWidth: 4, -// // } diff --git a/packages/ui/src/components/Input/Input.stories.tsx b/packages/ui/src/components/Input/Input.stories.tsx deleted file mode 100644 index 38c0f666042..00000000000 --- a/packages/ui/src/components/Input/Input.stories.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import React from 'react' -// import { AutoForm } from 'uniforms' - -import { Input } from './../Input' -import { Package } from 'lucide-react' -import { Button } from '../Button' - -export default { - title: 'Old primitives/Input', - component: Input, -} - -export const Default = (args: any) => - -export const ErrorState = (args: any) => - -export const withIcon = (args: any) => - -export const withOption = (args: any) => - -export const withDescription = (args: any) => - -export const withCustomStyle = (args: any) => - -export const textArea = (args: any) => - -export const textAreaWithCopy = (args: any) => - -export const textAreaWithLimit = (args: any) => - -export const withRevealAndCopy = (args: any) => - -export const withCustomActions = (args: any) => - -export const withBeforeAndAfterLabel = (args: any) => - -export const size = (args: any) => - -export const borderless = (args: any) => - -export const date = (args: any) => - -const icon: any = - -Default.args = { - placeholder: 'Type text here ...', - disabled: false, - label: 'Name', - layout: 'vertical', -} - -ErrorState.args = { - type: 'text', - placeholder: 'Type text here ...', - disabled: false, - label: 'Input with an error message', - error: 'Your password must be less than 4 characters.', -} - -withIcon.args = { - type: 'text', - placeholder: 'Type text here ...', - disabled: false, - label: 'Input with an icon', - icon: icon, -} - -withOption.args = { - type: 'text', - placeholder: 'Type text here ...', - disabled: false, - label: 'Input with an error message', - labelOptional: 'This is required', -} - -withDescription.args = { - type: 'text', - placeholder: 'Type text here ...', - disabled: false, - label: 'Input with an error message', - descriptionText: 'Make your password short and easy to guess', -} - -withCustomStyle.args = { - type: 'text', - label: 'This has custom styling {width: 50%}', - style: { width: '50%' }, -} - -textArea.args = { - type: 'text', - label: 'This is a text area', -} - -textAreaWithCopy.args = { - copy: true, - rows: 3, - type: 'text', - label: 'This is a text area', -} - -textAreaWithLimit.args = { - type: 'text', - label: 'This is a text area, with 10 rows', - labelOptional: '500 character limit', - rows: 10, - limit: 500, -} - -withRevealAndCopy.args = { - type: 'text', - label: 'Reveal and copy', - labelOptional: 'Reveal the text, then copy it', - value: '12341234HDGRHSGR/adJDJD', - copy: true, - reveal: true, -} - -withCustomActions.args = { - type: 'text', - label: 'Custom actions', - labelOptional: 'Use any react components', - value: 'Value of input', - actions: [ - , - , - ], -} - -size.args = { - type: 'text', - label: 'You can change the size of this Input', - size: 'tiny', -} - -withBeforeAndAfterLabel.args = { - type: 'text', - label: 'This is the label', - beforeLabel: 'Before label : ', - afterLabel: ' : After label', -} - -borderless.args = { - type: 'text', - label: 'This is the label', - borderless: true, - size: 'tiny', -} - -date.args = { - type: 'date', - placeholder: 'Type text here ...', - disabled: false, - label: 'Name', - layout: 'vertical', -} diff --git a/packages/ui/src/components/InputNumber/InputNumber.stories.tsx b/packages/ui/src/components/InputNumber/InputNumber.stories.tsx deleted file mode 100644 index bce31a52703..00000000000 --- a/packages/ui/src/components/InputNumber/InputNumber.stories.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React, { useState, useCallback } from 'react' -// import { AutoForm } from 'uniforms' - -import { InputNumber } from '.' -import { IconPackage } from './../../index' -import { Button } from '../Button' -import { Space } from '../Space' - -export default { - title: 'Data Input/InputNumber', - component: InputNumber, -} - -export const Default = (args: any) => - -export const ErrorState = (args: any) => - -export const WithIcon = (args: any) => - -export const Controlled = (props: any) => { - const [state, setState] = useState(() => undefined) - - const onChange = useCallback((e: React.ChangeEvent) => { - // console.log({ e, value: e.currentTarget.value }) - setState(e.currentTarget.value) - }, []) - - return -} - -Default.args = { - label: 'Max of 100 and min of 0', - disabled: false, - layout: 'vertical', - max: 100, - min: 0, -} - -ErrorState.args = { - disabled: false, - layout: 'vertical', - label: 'Input Number with an error message', - error: 'Input Number must be in range', -} - -WithIcon.args = { - label: 'Max of 100 and min of 0 with a Icon', - disabled: false, - layout: 'vertical', - max: 100, - min: 0, - icon: , -} - -Controlled.args = { - label: 'Max of 100 and min of 0', - disabled: false, - layout: 'vertical', - max: 100, - min: 0, -} diff --git a/packages/ui/src/components/InputOld/Input.stories.js b/packages/ui/src/components/InputOld/Input.stories.js deleted file mode 100644 index 90faec6c033..00000000000 --- a/packages/ui/src/components/InputOld/Input.stories.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react' - -import { Input } from '.' - -export default { - title: 'Archive/Input Old', - component: Input, -} - -export const Default = (args) => -Default.args = { - placeholder: 'Type text here ...', - disabled: false -} diff --git a/packages/ui/src/components/Select/Select.stories.tsx b/packages/ui/src/components/Select/Select.stories.tsx deleted file mode 100644 index b69cad8efd9..00000000000 --- a/packages/ui/src/components/Select/Select.stories.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import React from 'react' - -import Select from '.' -import { IconBook } from '../../index' - -const { Option, OptGroup } = Select - -export default { - title: 'Data Input/Select', - component: Select, -} - -export const Default = (args: any) => ( - -) - -export const withCheckboxes = (args: any) => - - - - -) - -export const withOptionGroup = (args: any) => ( - -) - -export const withIcon = (args: any) => ( - -) - -export const withOptionLabel = (args: any) => ( - -) - -export const withBeforeAndAfterLabel = (args: any) => ( - -) - -export const withDescription = (args: any) => ( - -) - -export const size = (args: any) => ( - -) - -const data = ['England', 'Wales', 'Scotland', 'Ireland'] -const icon = - -Default.args = { - disabled: false, - label: 'Label', - className: 'font-sans', - layout: 'vertical', - children: [ - <> - - - - , - ], -} - -withOptionGroup.args = { - placeholder: 'Type text here ...', - disabled: false, - label: 'Input with an error message', - className: 'font-sans', - value: 'Value of input', - layout: 'vertical', -} - -withCheckboxes.args = { - disabled: false, - checkboxes: data, - allowedValues: data, - className: 'font-sans', - layout: 'vertical', -} - -ErrorState.args = { - placeholder: 'Type text here ...', - disabled: false, - label: 'Input with an error message', - className: 'font-sans', - value: 'Value of input', - error: 'Your password must be less than 4 characters.', - allowedValues: data, - layout: 'vertical', -} - -withIcon.args = { - placeholder: 'Type text here ...', - disabled: false, - label: 'Input with an Icon', - className: 'font-sans', - value: 'Value of input', - icon: icon, - allowedValues: data, - layout: 'vertical', -} - -withOptionLabel.args = { - placeholder: 'Type text here ...', - disabled: false, - label: 'Input with an error message', - className: 'font-sans', - value: 'Value of input', - labelOptional: 'This is required', - allowedValues: data, - layout: 'vertical', -} - -withBeforeAndAfterLabel.args = { - placeholder: 'Type text here ...', - disabled: false, - label: 'Label', - beforeLabel: 'Before : ', - afterLabel: ' : After', - className: 'font-sans', - value: 'Value of input', - labelOptional: 'This is required', - allowedValues: data, - layout: 'vertical', -} - -withDescription.args = { - placeholder: 'Type text here ...', - disabled: false, - label: 'Input with an error message', - className: 'font-sans', - value: 'Value of input', - descriptionText: 'Make your password short and easy to guess', - allowedValues: data, - layout: 'vertical', -} - -size.args = { - placeholder: 'Type text here ...', - disabled: false, - label: 'Input with a size selected', - value: 'Value of input', - descriptionText: 'Choose a different size and font and padding will change', - allowedValues: data, - layout: 'vertical', - size: 'tiny', -} diff --git a/packages/ui/src/components/SidePanel/SidePanel.stories.tsx b/packages/ui/src/components/SidePanel/SidePanel.stories.tsx deleted file mode 100644 index 8dc7e5fcba7..00000000000 --- a/packages/ui/src/components/SidePanel/SidePanel.stories.tsx +++ /dev/null @@ -1,262 +0,0 @@ -import { action } from '@storybook/addon-actions' -import { useState } from 'react' - -import { Button } from '../Button' -import { Space } from '../Space' -import { SidePanel } from './index' - -export default { - title: 'Overlays/SidePanel', - component: SidePanel, -} - -const content = ( - - SidePanel content is inserted here, if you need to insert anything into the SidePanel you can do - so via - children - -) - -export const Default = (args: any) => ( - <> - -

    This is the title

    -

    This is the title

    - - } - > - {content} -
    - -) - -export const withWideLayout = (args: any) => ( - <> - {content} - -) - -export const leftAlignedFooter = (args: any) => ( - <> - {content} - -) - -export const leftAligned = (args: any) => ( - <> - {content} - -) - -export const hideFooter = (args: any) => ( - <> - {content} - -) - -export const customFooter = (args: any) => ( - <> - {content} - -) - -export const triggerElement = (args: any) => ( - <> - - This was opened with a trigger element - - -) - -export const nestedSidepanels = (args: any) => { - const [panel1Visible, setPanel1Visible] = useState(false) - const [panel2Visible, setPanel2Visible] = useState(false) - - return ( - <> -
    -
    -

    Shall we nest some components?

    -

    yea sure, go on then.

    -
    - -
    - setPanel1Visible(false)} - onConfirm={() => setPanel1Visible(false)} - > -
    -

    - This Sidepanel was opened with a trigger element -

    - -

    - You can open a nested panel by clicking the button below -

    - - -
    - setPanel2Visible(false)} - onConfirm={() => setPanel2Visible(false)} - > - - -
    - - ) -} - -export const longContent = (args: any) => ( - <> - -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -

    This is a paragraph

    -
    - -) - -Default.args = { - visible: true, - onCancel: action('onCancel'), - onConfirm: action('onConfirm'), - title: 'This is the title of the SidePanel', - description: 'And i am the description', -} - -withWideLayout.args = { - visible: true, - size: 'large', - onCancel: action('onCancel'), - onConfirm: action('onConfirm'), - title: 'This is the title of the SidePanel', - description: 'And i am the description', -} - -leftAlignedFooter.args = { - visible: true, - alignFooter: 'left', - onCancel: action('onCancel'), - onConfirm: action('onConfirm'), - title: 'This is the title of the SidePanel', - description: 'And i am the description', -} - -leftAligned.args = { - visible: true, - align: 'left', - onCancel: action('onCancel'), - onConfirm: action('onConfirm'), - title: 'This is the title of the SidePanel', - description: 'And i am the description', -} - -hideFooter.args = { - visible: true, - hideFooter: true, - onCancel: action('onCancel'), - onConfirm: action('onConfirm'), - title: 'This is the title of the SidePanel', - description: 'And i am the description', -} - -customFooter.args = { - visible: true, - onCancel: action('onCancel'), - onConfirm: action('onConfirm'), - title: 'This is the title of the SidePanel', - description: 'And i am the description', - customFooter: [ - - - - , - ], -} - -triggerElement.args = { - visible: true, - onCancel: action('onCancel'), - onConfirm: action('onConfirm'), - title: 'This is the title of the SidePanel', - description: 'And i am the description', - triggerElement: ( - - ), -} - -longContent.args = { - visible: true, - header: 'Long content', -} diff --git a/packages/ui/src/components/Tabs/Tabs.stories.tsx b/packages/ui/src/components/Tabs/Tabs.stories.tsx deleted file mode 100644 index 17ed4f3c317..00000000000 --- a/packages/ui/src/components/Tabs/Tabs.stories.tsx +++ /dev/null @@ -1,165 +0,0 @@ -import React from 'react' -import { Button } from '../Button' -import { IconMail } from '../../index' -import Typography from '../Typography' - -import { Tabs } from './' - -export default { - title: 'Displays/Tabs', - component: Tabs, -} - -export const Default = (args: any) => ( - - - Content for the first panel - - - Content for the second panel - - - Content for the third panel - - -) - -export const Underlined = (args: any) => ( - - - Content for the first panel - - - Content for the second panel - - - Content for the third panel - - -) - -export const WithIcons = (args: any) => ( - - }> - Content for the first panel - - }> - Content for the second panel - - }> - Content for the third panel - - -) - -export const LargeButtons = (args: any) => ( - - - Content for the first panel - - - Content for the second panel - - - Content for the third panel - - -) - -export const BlockTabs = (args: any) => ( - - - Content for the first panel - - - Content for the second panel - - - Content for the third panel - - -) - -const limit = 30 -let infitniteSroll: JSX.Element[] = [] - -for (var i = 0; i < limit; i++) { - infitniteSroll.push( - - Content for the panel {i} - - ) -} - -export const Scroll = (args: any) => ( - - {infitniteSroll} - -) - -export const addOnBefore = (args: any) => ( - - - Content for the first panel - - - Content for the second panel - - - Content for the third panel - - -) - -export const addOnAfter = (args: any) => ( - - - Content for the first panel - - - Content for the second panel - - - Content for the third panel - - -) - -export const OneTab = () => ( - - - Content for the first panel - - -) - -Default.args = {} -Underlined.args = { - type: 'underlined', -} -WithIcons.args = { - type: 'underlined', -} -LargeButtons.args = { - type: 'underlined', - size: 'large', -} -BlockTabs.args = { - type: 'underlined', - block: true, -} -Scroll.args = { - type: 'underlined', - block: true, - scrollable: true, -} -addOnBefore.args = { - type: 'underlined', - size: 'large', - addOnBefore: , -} -addOnAfter.args = { - type: 'underlined', - size: 'large', - addOnAfter: , -} diff --git a/packages/ui/src/components/Toggle/Toggle.stories.tsx b/packages/ui/src/components/Toggle/Toggle.stories.tsx deleted file mode 100644 index 42513f7d48c..00000000000 --- a/packages/ui/src/components/Toggle/Toggle.stories.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React from 'react' - -import { Toggle } from '.' -import { Button } from '../Button' -import { Input } from '../Input' -import { Space } from '../Space' - -export default { - title: 'Data Input/Toggle', - component: Toggle, - argTypes: { - label: { control: 'text' }, - }, -} - -export const Primary = (args: any) => -export const Wide = (args: any) => -export const checkedDefault = (args: any) => -export const noLabel = (args: any) => -export const withBeforeAndAfterLabel = (args: any) => -export const size = (args: any) => - -Primary.args = { - descriptionText: 'This is optional description', - disabled: false, - error: '', - label: "Get insights across your organization's repositories", - labelOptional: - 'Star history, issue tracking, and more to come repository.surf organization', - name: 'radiogroup-example', -} - -Wide.args = { - descriptionText: 'This is optional description', - disabled: false, - error: '', - label: "Get insights across your organization's repositories", - labelOptional: - 'Star history, issue tracking, and more to come repository.surf organization', - name: 'radiogroup-example', - layout: 'wide', -} - -checkedDefault.args = { - defaultChecked: true, - descriptionText: 'This is optional description', - label: 'Radio group main label', - labelOptional: 'This is an optional label', -} - -noLabel.args = { - active: true, - disabled: false, - error: '', - name: 'radiogroup-example', - layout: 'horizontal', -} - -withBeforeAndAfterLabel.args = { - label: 'Label', - beforeLabel: 'Before : ', - afterLabel: ' : After', -} - -size.args = { - label: 'Try different sizes', - size: 'tiny', -} diff --git a/packages/ui/src/components/Typography/Typography.stories.tsx b/packages/ui/src/components/Typography/Typography.stories.tsx deleted file mode 100644 index eb3546acb53..00000000000 --- a/packages/ui/src/components/Typography/Typography.stories.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import React from 'react' -// import { AutoForm } from 'uniforms' -// @ts-ignore -import MarkdownExample from './../../lib/MarkdownSample.md' -import ReactMarkdown from 'react-markdown' -const gfm = require('remark-gfm') - -import Typography from '.' -// @ts-ignore -import { Space } from '../../index' - -const { Title, Text, Link } = Typography - -export default { - title: 'General/Typography', - component: Typography, -} - -export const article = () => ( - - - -) - -export const Titles = () => ( - - Hello world - Hello world - Hello world - Hello world - Hello world - -) - -export const Texts = () => ( - <> - Supabase UI (default) -
    - Supabase UI (secondary) -
    - Supabase UI (success) -
    - Supabase UI (warning) -
    - Supabase UI (danger) -
    - Supabase UI (disabled) -
    - Supabase UI (mark) -
    - Supabase UI (code) -
    - Supabase UI (keyboard) -
    - Supabase UI (underline) -
    - Supabase UI (strikethrough) -
    - Supabase UI (strong) -
    - - Supabase (Link) - - -) diff --git a/packages/ui/src/components/Upload/Upload.stories.tsx b/packages/ui/src/components/Upload/Upload.stories.tsx deleted file mode 100644 index 34d884df7ad..00000000000 --- a/packages/ui/src/components/Upload/Upload.stories.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import React, { useState } from 'react' - -import Upload from '.' -import Typography from '../Typography' - -export default { - title: 'Data Input/Upload', - component: Upload, -} - -export const Draggable = () => { - const [files, setFiles] = useState>([]) - - return ( - -
    -
    - -
    - - - Upload a file - - {' or drag and drop'} - -
    - - PNG, JPG, GIF up to 10MB - - {/*

    PNG, JPG, GIF up to 10MB

    */} -
    - - {/* Previews */} - {files.length > 0 && ( -
    - {files.map((file) => ( - {file.name} - ))} -
    - )} -
    -
    - ) -} diff --git a/packages/ui/src/components/shadcn/ui/input.tsx b/packages/ui/src/components/shadcn/ui/input.tsx index fc536af1aca..5f2e8e5ab9b 100644 --- a/packages/ui/src/components/shadcn/ui/input.tsx +++ b/packages/ui/src/components/shadcn/ui/input.tsx @@ -8,7 +8,7 @@ export interface InputProps VariantProps {} export const InputVariants = cva( - 'aria-[] flex h-10 w-full rounded-md border border-control bg-foreground/[.026] px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-foreground-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-background-control focus-visible:ring-offset-2 focus-visible:ring-offset-foreground-muted disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid=true]:bg-destructive-200 aria-[invalid=true]:border-destructive-400 aria-[invalid=true]:focus:border-destructive aria-[invalid=true]:focus-visible:border-destructive', + 'aria-[] flex h-10 w-full rounded-md border border-control bg-foreground/[.026] px-3 py-2 text-base md:text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-foreground-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-background-control focus-visible:ring-offset-2 focus-visible:ring-offset-foreground-muted disabled:cursor-not-allowed disabled:opacity-50 aria-[invalid=true]:bg-destructive-200 aria-[invalid=true]:border-destructive-400 aria-[invalid=true]:focus:border-destructive aria-[invalid=true]:focus-visible:border-destructive', { variants: { size: { diff --git a/packages/ui/src/lib/colors.stories.tsx b/packages/ui/src/lib/colors.stories.tsx deleted file mode 100644 index 611ff440fa8..00000000000 --- a/packages/ui/src/lib/colors.stories.tsx +++ /dev/null @@ -1,98 +0,0 @@ -// import { Meta } from '@storybook/react' -// import { cn } from './utils' -// // import { Card } from '@ui/components/shadcn/ui/card' -// import color from './tailwind-demo-classes' - -// const meta: Meta = { -// title: 'tailwind/Colors', -// } - -// export const Default = () => { -// return ( -//
    -//
    -//
    -//
    Background
    -//
    -// {color.background.map((x: string) => { -// return ( -//
    -//
    -//
    -// {x} -//
    -//
    -// ) -// })} -//
    -//
    -//
    -//
    Border
    -//
    -// {color.border.map((x) => { -// return ( -//
    -//
    -//
    -// {x} -//
    -//
    -// ) -// })} -//
    -//
    -//
    -//
    texts
    -//
    -// {color.text.map((x) => { -// return ( -//
    -// -// ### -// -//
    -// {x} -//
    -//
    -// ) -// })} -//
    -//
    -//
    -//
    -//
    -//
    Colors
    -//
    -// {color.colors.map((x: string) => { -// return ( -//
    -//
    -//
    -// {x} -//
    -//
    -// ) -// })} -//
    -//
    -//
    -//
    Palletes
    -//
    -// {color.palletes.map((x) => { -// return ( -//
    -//
    -//
    -// {x} -//
    -//
    -// ) -// })} -//
    -//
    -//
    -//
    -// ) -// } - -// export default meta diff --git a/packages/ui/tailwind-theming.md b/packages/ui/tailwind-theming.md index d12d000ddd8..38a34bf4639 100644 --- a/packages/ui/tailwind-theming.md +++ b/packages/ui/tailwind-theming.md @@ -137,9 +137,3 @@ text-border-control | `500` | | `DEFAULT` | | `600` | - -## Storybook - -The current colors are also auto documented in Storybook - -[Storybook Colors](https://ui-storybook-pre-release.vercel.app/) diff --git a/packages/ui/tailwind.config.js b/packages/ui/tailwind.config.js deleted file mode 100644 index cc5546ac08d..00000000000 --- a/packages/ui/tailwind.config.js +++ /dev/null @@ -1,90 +0,0 @@ -const config = require('config/tailwind.config') -const { fontFamily } = require('tailwindcss/defaultTheme') - -module.exports = config({ - content: ['../../packages/ui/**/*.{tsx,ts,js}', '../../packages/ui/**/*.*.{tsx,ts,js}'], - theme: { - container: { - center: true, - padding: '2rem', - screens: { - '2xl': '1400px', - }, - }, - extend: { - keyframes: { - 'flash-code': { - '0%': { backgroundColor: 'rgba(63, 207, 142, 0.1)' }, - '100%': { backgroundColor: 'transparent' }, - }, - 'caret-blink': { - '0%,70%,100%': { opacity: '1' }, - '20%,50%': { opacity: '0' }, - }, - }, - animation: { - 'flash-code': 'flash-code 1s forwards', - 'flash-code-slow': 'flash-code 2s forwards', - 'caret-blink': 'caret-blink 1.25s ease-out infinite', - }, - colors: { - border: 'hsl(var(--border-overlay))', - input: 'hsl(var(--border-control))', - ring: 'hsl(var(--ring))', - background: 'hsl(var(--background-default))', - foreground: 'hsl(var(--foreground-default))', - primary: { - DEFAULT: 'hsl(var(--foreground-default))', - foreground: 'hsl(var(--background-default))', - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))', - }, - destructive: { - DEFAULT: 'hsl(var(--destructive-default))', - foreground: 'hsl(var(--foreground-default))', - }, - muted: { - DEFAULT: 'hsl(var(--border-overlay))', - foreground: 'hsl(var(--foreground-lighter))', - }, - accent: { - DEFAULT: 'hsl(var(--background-overlay))', - foreground: 'hsl(var(--foreground-lighter))', - }, - popover: { - DEFAULT: 'hsl(var(--background-overlay))', - foreground: 'hsl(var(--foreground-default))', - }, - card: { - DEFAULT: 'hsl(var(--background-overlay))', - foreground: 'hsl(var(--foreground-default))', - }, - }, - borderRadius: { - lg: `var(--radius)`, - md: `calc(var(--radius) - 2px)`, - sm: 'calc(var(--radius) - 4px)', - }, - fontFamily: { - sans: ['var(--font-sans)', ...fontFamily.sans], - }, - keyframes: { - 'accordion-down': { - from: { height: 0 }, - to: { height: 'var(--radix-accordion-content-height)' }, - }, - 'accordion-up': { - from: { height: 'var(--radix-accordion-content-height)' }, - to: { height: 0 }, - }, - }, - animation: { - 'accordion-down': 'accordion-down 0.2s ease-out', - 'accordion-up': 'accordion-up 0.2s ease-out', - }, - }, - }, - plugins: [require('tailwindcss-animate')], -}) diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index aebbf48962e..c6e40b7c779 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -4,5 +4,5 @@ "resolveJsonModule": true }, "include": ["."], - "exclude": ["dist", "build", "node_modules", "**/*.stories.tsx"] + "exclude": ["dist", "build", "node_modules"] } diff --git a/playwright-tests/tests/snapshot/spec/table-editor.spec.ts b/playwright-tests/tests/snapshot/spec/table-editor.spec.ts index b68e46bc08b..4cf89b421e4 100644 --- a/playwright-tests/tests/snapshot/spec/table-editor.spec.ts +++ b/playwright-tests/tests/snapshot/spec/table-editor.spec.ts @@ -77,11 +77,10 @@ test.describe('Table Editor page', () => { await page.getByTestId('table-editor-pick-column-to-sort-button').click() await page.getByLabel('Pick a column to sort by').getByText('defaultValueColumn').click() await page.getByRole('button', { name: 'Apply sorting' }).click() + // click away to close the sorting dialog - await page - .locator('div') - .filter({ hasText: /^Table Editor$/ }) - .click() + await page.locator('#spec-click-target').click() + // expect the row to be sorted by defaultValueColumn. They're inserted in the order 100, 2 await expect(page.locator('div.rdg-row:nth-child(2)')).toContainText('2') await expect(page.locator('div.rdg-row:nth-child(3)')).toContainText('100') @@ -98,10 +97,7 @@ test.describe('Table Editor page', () => { await page.getByPlaceholder('Enter a value').fill('2') await page.getByRole('button', { name: 'Apply filter' }).click() // click away to close the filter dialog - await page - .locator('div') - .filter({ hasText: /^Table Editor$/ }) - .click() + await page.locator('#spec-click-target').click() await expect(page.getByRole('grid')).toContainText('2') await expect(page.getByRole('grid')).not.toContainText('100') }) diff --git a/supa-mdx-lint/Rule001HeadingCase.toml b/supa-mdx-lint/Rule001HeadingCase.toml index 48d7b7a0193..5bc51f37ec2 100644 --- a/supa-mdx-lint/Rule001HeadingCase.toml +++ b/supa-mdx-lint/Rule001HeadingCase.toml @@ -4,6 +4,7 @@ # Can also specify a regex that is compatible with the [Rust regex crate](https://docs.rs/regex/latest/regex/). may_uppercase = [ "[A-Z0-9]{2,5}s?", + "Add-ons?", "Amazon RDS", "APIs", "Analytics", @@ -30,7 +31,10 @@ may_uppercase = [ "Cloudflare Workers?", "Code Exchange", "Colab", + "Compute", + "Compute Hours", "Content Delivery Network", + "Cron", "Cron Jobs?", "Data API", "DataDog", @@ -45,10 +49,12 @@ may_uppercase = [ "Docker", "Drizzle", "Edge Functions?", + "Enterprise", "Enterprise Plan", "Expo", "Facebook", "Facebook Developers?", + "Fair Use Policy", "Figma", "Figma Developers?", "Firebase", @@ -57,12 +63,14 @@ may_uppercase = [ "Flutter", "Functions?", "Free Plan", + "Frequently Asked Questions", "Git", "GitHub", "GitHub Actions", "GitLab", "GoTrue", "Google", + "Google Workspace", "Grafana", "GraphQL", "Heroku", @@ -98,6 +106,7 @@ may_uppercase = [ "Llamafile", "Logs Explorer", "Magic Link", + "Management API", "Mixpeek", "Mixpeek Embed", "MySQL", @@ -126,10 +135,12 @@ may_uppercase = [ "Presence", "Prometheus", "Python", + "Queues?", "Query Performance", "React", "React Email", "React Native", + "Read Replicas?", "Reciprocal Ranked Fusion", "Redis", "RedwoodJS", @@ -146,7 +157,9 @@ may_uppercase = [ "Single Sign-On", "Slack", "Slack Developers?", + "Social Login", "SolidJS", + "Spend Cap", "Spotify", "Spotify Developers?", "Sqitch", diff --git a/vale/styles/config/vocabularies/Supabase/accept.txt b/vale/styles/config/vocabularies/Supabase/accept.txt index 50cf9cd7072..dbe31562682 100644 --- a/vale/styles/config/vocabularies/Supabase/accept.txt +++ b/vale/styles/config/vocabularies/Supabase/accept.txt @@ -1,6 +1,7 @@ APIs [Aa]uth [Bb]aselining +batch_size Berri [Cc]hatbots? ChatGPT @@ -24,6 +25,7 @@ Deno [Ee]nv [Ee]nums? event_message +fetch_size Firestore Hashicorp [Gg]eospatial @@ -41,6 +43,7 @@ JWTs Lua Markprompt [Mm]iddleware +misexecution [Nn]amespace(d|s)? Next.js npm