diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3feaae032a1..043d489e617 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,7 +1,7 @@ /packages/ui/ @supabase/design /packages/shared-data/pricing.ts @roryw10 @supabase/billing /packages/shared-data/plans.ts @roryw10 @supabase/billing -/packages/common/telemetry-constants.ts @4L3k51 @supabase/growth-eng +/packages/common/telemetry-constants.ts @supabase/growth-eng /apps/studio/ @supabase/Dashboard diff --git a/.github/workflows/docs-mgmt-api-update.yml b/.github/workflows/docs-mgmt-api-update.yml index 7272f722f36..0b8d542d59a 100644 --- a/.github/workflows/docs-mgmt-api-update.yml +++ b/.github/workflows/docs-mgmt-api-update.yml @@ -8,7 +8,7 @@ on: permissions: pull-requests: write - contents: read + contents: write jobs: update-docs: diff --git a/.vale.ini b/.vale.ini deleted file mode 100644 index 1ff9438668a..00000000000 --- a/.vale.ini +++ /dev/null @@ -1,14 +0,0 @@ -StylesPath = './vale/styles' -MinAlertLevel = 'suggestion' - -Vocab = Supabase - -[formats] -mdx = md - -[*] -BasedOnStyles = Vale, Custom -BlockIgnores = (?s) *(.*?), \ -(?s) *(export\s+const\s+\S+\s*=\s*\[.*?\]), \ -(?s) *() -TokenIgnores = (href\s*([=:])\s*['"].*?\1), \ diff --git a/DEVELOPERS.md b/DEVELOPERS.md index ae379939f1d..c41044ceb71 100644 --- a/DEVELOPERS.md +++ b/DEVELOPERS.md @@ -65,7 +65,7 @@ To contribute code to [Supabase](https://supabase.com), you must fork the [Supab ### Install dependencies -1. Install the dependencies in the root of the repo. +1. Install the dependencies in the root of the repo: ```sh pnpm install # install dependencies @@ -77,7 +77,7 @@ To contribute code to [Supabase](https://supabase.com), you must fork the [Supab cp apps/www/.env.local.example apps/www/.env.local ``` -3. After that you can run the apps simultaneously with the following. +3. After that you can run the apps simultaneously with the following: ```sh pnpm dev # start all the applications ``` @@ -117,8 +117,8 @@ Installing a package in a specific workspace requires you to move to the workspa For example: -1. `cd apps/studio`: move to the `studio` workspace -2. `pnpm add react`: installs `react` into `studio` workspace +1. `cd apps/studio`: move to the `studio` workspace. +2. `pnpm add react`: installs `react` into `studio` workspace. --- @@ -145,7 +145,7 @@ First, make sure you have the Docker installed on your device. You can download cp .env.example .env ``` -3. Run docker +3. Run docker: ```sh docker compose up @@ -186,9 +186,9 @@ Create a new entry in the [`redirects.js`](https://github.com/supabase/supabase/ We support "federating" docs, meaning doc content can come directly from external repos other than [`supabase/supabase`](https://github.com/supabase/supabase). - It's great for things like client libs who have their own set of docs that we don't want to duplicate on the official Supabase docs (eg. [`supabase/vecs`](https://github.com/supabase/vecs)). -- No duplication or manual steps required - fetches and generates automatically as part of the docs build pipeline -- It's flexible - you can "embed" external docs nearly anywhere at any level in Supabase docs, but they will feel native -- If you are maintaining a repo containing docs that you think could also live in Supabase docs, feel free to create an issue and we can work together to integrate +- No duplication or manual steps required - fetches and generates automatically as part of the docs build pipeline. +- It's flexible - you can "embed" external docs nearly anywhere at any level in Supabase docs, but they will feel native. +- If you are maintaining a repo containing docs that you think could also live in Supabase docs, feel free to create an issue and we can work together to integrate. Federated docs work using Next.js's build pipeline. We use `getStaticProps()` to fetch remote documentation (ie. markdown) at build time which is processed and passed to the respective page within the docs. diff --git a/README.md b/README.md index e74a9fd2814..ee13ffbc323 100644 --- a/README.md +++ b/README.md @@ -244,6 +244,7 @@ Our approach for client libraries is modular. Each sub-library is a standalone i - [Bangla / বাংলা](/i18n/README.bn.md) - [Bulgarian / Български](/i18n/README.bg.md) - [Catalan / Català](/i18n/README.ca.md) +- [Croatian / Hrvatski](/i18n/README.hr.md) - [Czech / čeština](/i18n/README.cs.md) - [Danish / Dansk](/i18n/README.da.md) - [Dutch / Nederlands](/i18n/README.nl.md) diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index e507e711b14..6cba85388af 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -1443,7 +1443,7 @@ export const functions: NavMenuConstant = { url: undefined, items: [ { name: 'Background Tasks', url: '/guides/functions/background-tasks' }, - { name: 'Ephemeral Storage', url: '/guides/functions/ephemeral-storage' }, + { name: 'File Storage', url: '/guides/functions/ephemeral-storage' }, { name: 'WebSockets', url: '/guides/functions/websockets' }, { name: 'Custom Routing', url: '/guides/functions/routing' }, { name: 'Wasm Modules', url: '/guides/functions/wasm' }, diff --git a/apps/docs/content/_partials/project_setup.mdx b/apps/docs/content/_partials/project_setup.mdx index 3d0d9d43288..9b0f3b19adb 100644 --- a/apps/docs/content/_partials/project_setup.mdx +++ b/apps/docs/content/_partials/project_setup.mdx @@ -1,16 +1,16 @@ ## Project setup -Before we start building we're going to set up our Database and API. This is as simple as starting a new Project in Supabase and then creating a "schema" inside the database. +Before you start building you need to set up the Database and API. You can do this by starting a new Project in Supabase and then creating a "schema" inside the database. ### Create a project 1. [Create a new project](https://supabase.com/dashboard) in the Supabase Dashboard. -1. Enter your project details. -1. Wait for the new database to launch. +2. Enter your project details. +3. Wait for the new database to launch. ### Set up the database schema -Now we are going to set up the database schema. We can use the "User Management Starter" quickstart in the SQL Editor, or you can just copy/paste the SQL from below and run it yourself. +Now set up the database schema. You can use the "User Management Starter" quickstart in the SQL Editor, or you can copy/paste the SQL from below and run it. +{/* TODO: discuss */} ```bash supabase link --project-ref @@ -37,6 +37,8 @@ supabase link --project-ref supabase db pull ``` + + @@ -58,7 +60,8 @@ supabase migration new user_management_starter ### Get the API keys Now that you've created some database tables, you are ready to insert data using the auto-generated API. -We just need to get the Project URL and `anon` key from the API settings. + +To do this, you need to get the Project URL and `anon` key from the API settings. 1. Go to the [API Settings](https://supabase.com/dashboard/project/_/settings/api) page in the Dashboard. -1. Find your Project `URL`, `anon`, and `service_role` keys on this page. +2. Find your Project `URL`, `anon`, and `service_role` keys on this page. diff --git a/apps/docs/content/guides/auth/auth-anonymous.mdx b/apps/docs/content/guides/auth/auth-anonymous.mdx index 7c363423ab3..0a4f0b43c2e 100644 --- a/apps/docs/content/guides/auth/auth-anonymous.mdx +++ b/apps/docs/content/guides/auth/auth-anonymous.mdx @@ -269,6 +269,7 @@ using ( true ); RLS policies are permissive by default, which means that they are combined using an "OR" operator when multiple policies are applied. It is important to construct restrictive policies to ensure that the checks for an anonymous user are always enforced when combined with other policies. +Be aware that a single 'restrictive' RLS policy alone will fail unless combined with another policy that returns true, ensuring the combined condition is met. diff --git a/apps/docs/content/guides/auth/passwords.mdx b/apps/docs/content/guides/auth/passwords.mdx index afdfa5b0c0b..961f4cc4c73 100644 --- a/apps/docs/content/guides/auth/passwords.mdx +++ b/apps/docs/content/guides/auth/passwords.mdx @@ -629,7 +629,7 @@ The PKCE flow allows for server-side authentication. Unlike the implicit flow, w Update your reset password email template to send the token hash. See [Email Templates](/docs/guides/auth/auth-email-templates) for how to configure your email templates. -Your signup email template should contain the following HTML: +Your reset password email template should contain the following HTML: ```html

Reset Password

@@ -960,11 +960,11 @@ Consider configuring a custom SMTP server for production. See the [Custom SMTP guide](/docs/guides/auth/auth-smtp) for instructions. -#### Local development with Inbucket +#### Local development with Mailpit -You can test email flows on your local machine. The Supabase CLI automatically captures emails sent locally by using [Inbucket](https://github.com/inbucket/inbucket). +You can test email flows on your local machine. The Supabase CLI automatically captures emails sent locally by using [Mailpit](https://github.com/axllent/mailpit). -In your terminal, run `supabase status` to get the Inbucket URL. Go to this URL in your browser, and follow the instructions to find your emails. +In your terminal, run `supabase status` to get the Mailpit URL. Go to this URL in your browser, and follow the instructions to find your emails. ## With phone diff --git a/apps/docs/content/guides/auth/quickstarts/react-native.mdx b/apps/docs/content/guides/auth/quickstarts/react-native.mdx index ac779e0e5fc..45d125f9074 100644 --- a/apps/docs/content/guides/auth/quickstarts/react-native.mdx +++ b/apps/docs/content/guides/auth/quickstarts/react-native.mdx @@ -71,7 +71,7 @@ hideToc: true ```ts name=lib/supabase.ts - import { AppState } from 'react-native' + import { AppState, Platform } from 'react-native' import 'react-native-url-polyfill/auto' import AsyncStorage from '@react-native-async-storage/async-storage' import { createClient, processLock } from '@supabase/supabase-js' @@ -81,7 +81,7 @@ hideToc: true export const supabase = createClient(supabaseUrl, supabaseAnonKey, { auth: { - storage: AsyncStorage, + ...(Platform.OS !== "web" ? { storage: AsyncStorage } : {}), autoRefreshToken: true, persistSession: true, detectSessionInUrl: false, @@ -94,13 +94,15 @@ hideToc: true // to receive `onAuthStateChange` events with the `TOKEN_REFRESHED` or // `SIGNED_OUT` event if the user's session is terminated. This should // only be registered once. - AppState.addEventListener('change', (state) => { - if (state === 'active') { - supabase.auth.startAutoRefresh() - } else { - supabase.auth.stopAutoRefresh() - } - }) + if (Platform.OS !== "web") { + AppState.addEventListener('change', (state) => { + if (state === 'active') { + supabase.auth.startAutoRefresh() + } else { + supabase.auth.stopAutoRefresh() + } + }) + } ``` diff --git a/apps/docs/content/guides/auth/server-side/nextjs.mdx b/apps/docs/content/guides/auth/server-side/nextjs.mdx index 1b3390e20a6..2cae8e90d46 100644 --- a/apps/docs/content/guides/auth/server-side/nextjs.mdx +++ b/apps/docs/content/guides/auth/server-side/nextjs.mdx @@ -67,7 +67,7 @@ To access Supabase from your Next.js app, you need 2 types of Supabase clients: 1. **Client Component client** - To access Supabase from Client Components, which run in the browser. 1. **Server Component client** - To access Supabase from Server Components, Server Actions, and Route Handlers, which run only on the server. -Create a `utils/supabase` folder with a file for each type of client. Then copy the utility functions for each client type. +Create a `utils/supabase` folder at the root of your project, or inside the `./src` folder if you are using one, with a file for each type of client. Then copy the utility functions for each client type. { --- -## Limitations +## Using synchronous file APIs -Currently, the synchronous APIs for creating or writing files are not supported: +You can safely use the following synchronous Deno APIs (and their Node counterparts) _during initial script evaluation_: + +- Deno.statSync +- Deno.removeSync +- Deno.writeFileSync +- Deno.writeTextFileSync +- Deno.readFileSync +- Deno.readTextFileSync +- Deno.mkdirSync +- Deno.makeTempDirSync +- Deno.readDirSync + +**Keep in mind** that the sync APIs are available only during initial script evaluation and aren’t supported in callbacks like HTTP handlers or `setTimeout`. ```tsx -// ❌ Not supported -Deno.writeFileSync('/tmp/file.txt', data) -Deno.mkdirSync('/tmp/directory') +Deno.statSync('...') // ✅ -// ✅ Supported -await Deno.writeFile('/tmp/file.txt', data) -await Deno.mkdir('/tmp/directory') -``` +setTimeout(() => { + Deno.statSync('...') // 💣 ERROR! Deno.statSync is blocklisted on the current context +}) -You can use sync variations of read APIs: - -```tsx -// ✅ Supported -const data = Deno.readFileSync('/tmp/file.txt') +Deno.serve(() => { + Deno.statSync('...') // 💣 ERROR! Deno.statSync is blocklisted on the current context +}) ``` --- ## Limits +There are no limits on S3 buckets you mount for Persistent storage. + +Ephemeral Storage: + - Free projects: Up to 256MB of ephemeral storage - Paid projects: Up to 512MB of ephemeral storage diff --git a/apps/docs/content/guides/getting-started/tutorials/with-angular.mdx b/apps/docs/content/guides/getting-started/tutorials/with-angular.mdx index 384950af5b9..04eb888e4d3 100644 --- a/apps/docs/content/guides/getting-started/tutorials/with-angular.mdx +++ b/apps/docs/content/guides/getting-started/tutorials/with-angular.mdx @@ -17,27 +17,27 @@ If you get stuck while working through this guide, refer to the [full example on ## Building the app -Let's start building the Angular app from scratch. +Start with building the Angular app from scratch. ### Initialize an Angular app -We can use the [Angular CLI](https://angular.io/cli) to initialize -an app called `supabase-angular`: +You can use the [Angular CLI](https://angular.io/cli) to initialize +an app called `supabase-angular`. The command sets some defaults, that you change to suit your needs: ```bash -npx ng new supabase-angular --routing false --style css --standalone false +npx ng new supabase-angular --routing false --style css --standalone false --zoneless true --ssr false cd supabase-angular ``` -Then let's install the only additional dependency: [supabase-js](https://github.com/supabase/supabase-js) +Then, install the only additional dependency: [supabase-js](https://github.com/supabase/supabase-js) ```bash npm install @supabase/supabase-js ``` -And finally we want to save the environment variables in the `src/environments/environment.ts` file. -All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys). -These variables will be exposed on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database. +Finally, save the environment variables in the `src/environments/environment.ts` file. +All you need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys). +The application exposes these variables in the browser, and that's fine as you have [Row Level Security](/docs/guides/auth#row-level-security) enabled on the Database. <$CodeTabs> @@ -51,7 +51,7 @@ export const environment = { -Now that we have the API credentials in place, let's create a `SupabaseService` with `ng g s supabase` to initialize the Supabase client and implement functions to communicate with the Supabase API. +Now you have the API credentials in place, create a `SupabaseService` with `ng g s supabase` and add the following code to initialize the Supabase client and implement functions to communicate with the Supabase API. <$CodeTabs> @@ -133,37 +133,41 @@ export class SupabaseService { -Optionally, update [src/styles.css](https://raw.githubusercontent.com/supabase/supabase/master/examples/user-management/angular-user-management/src/styles.css) to style the app. +Optionally, update `src/styles.css` [with the following styles](https://raw.githubusercontent.com/supabase/supabase/master/examples/user-management/angular-user-management/src/styles.css) to style the app. ### Set up a login component -Let's set up an Angular component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords. -Create an `AuthComponent` with `ng g c auth` Angular CLI command. +Next, set up an Angular component to manage logins and sign ups. The component uses [Magic Links](/docs/guides/auth/auth-email-passwordless#with-magic-link), so users can sign in with their email without using passwords. + +Create an `AuthComponent` with the `ng g c auth` Angular CLI command and add the following code. <$CodeTabs> -```ts name=src/app/auth/auth.component.ts +```ts name=src/app/auth/auth.ts import { Component } from '@angular/core' -import { FormBuilder } from '@angular/forms' +import { FormBuilder, FormGroup } from '@angular/forms' import { SupabaseService } from '../supabase.service' @Component({ selector: 'app-auth', - templateUrl: './auth.component.html', - styleUrls: ['./auth.component.css'], + templateUrl: './auth.html', + styleUrls: ['./auth.css'], + standalone: false, }) export class AuthComponent { - loading = false - - signInForm = this.formBuilder.group({ - email: '', - }) - + signInForm!: FormGroup constructor( private readonly supabase: SupabaseService, private readonly formBuilder: FormBuilder ) {} + loading = false + ngOnInit() { + this.signInForm = this.formBuilder.group({ + email: '', + }) + } + async onSubmit(): Promise { try { this.loading = true @@ -183,7 +187,7 @@ export class AuthComponent { } ``` -```html name=src/app/auth/auth.component.html +```html name=src/app/auth/auth.html

Supabase + Angular

@@ -201,7 +205,7 @@ export class AuthComponent {
@@ -214,38 +218,50 @@ export class AuthComponent { ### Account page Users also need a way to edit their profile details and manage their accounts after signing in. -Create an `AccountComponent` with the `ng g c account` Angular CLI command. +Create an `AccountComponent` with the `ng g c account` Angular CLI command and add the following code. <$CodeTabs> -```ts name=src/app/account/account.component.ts +```ts name=src/app/account/account.ts import { Component, Input, OnInit } from '@angular/core' -import { FormBuilder } from '@angular/forms' +import { FormBuilder, FormGroup } from '@angular/forms' import { AuthSession } from '@supabase/supabase-js' import { Profile, SupabaseService } from '../supabase.service' @Component({ selector: 'app-account', - templateUrl: './account.component.html', - styleUrls: ['./account.component.css'], + templateUrl: './account.html', + styleUrls: ['./account.css'], + standalone: false, }) export class AccountComponent implements OnInit { loading = false profile!: Profile + updateProfileForm!: FormGroup + + get avatarUrl() { + return this.updateProfileForm.value.avatar_url as string + } + async updateAvatar(event: string): Promise { + this.updateProfileForm.patchValue({ + avatar_url: event, + }) + await this.updateProfile() + } @Input() session!: AuthSession - updateProfileForm = this.formBuilder.group({ - username: '', - website: '', - avatar_url: '', - }) - constructor( private readonly supabase: SupabaseService, private formBuilder: FormBuilder - ) {} + ) { + this.updateProfileForm = this.formBuilder.group({ + username: '', + website: '', + avatar_url: '', + }) + } async ngOnInit(): Promise { await this.getProfile() @@ -311,8 +327,9 @@ export class AccountComponent implements OnInit { } ``` -```html name=src/app/account/account.component.html +```html name=src/app/account/account.html
+
@@ -328,7 +345,7 @@ export class AccountComponent implements OnInit {
@@ -342,33 +359,34 @@ export class AccountComponent implements OnInit { ### Launch! -Now that we have all the components in place, let's update `AppComponent`: +Now you have all the components in place, update `AppComponent`: <$CodeTabs> -```ts name=src/app/app.component.ts +```ts name=src/app/app.ts import { Component, OnInit } from '@angular/core' import { SupabaseService } from './supabase.service' @Component({ selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.css'], + templateUrl: './app.html', + styleUrls: ['./app.css'], + standalone: false, }) export class AppComponent implements OnInit { - title = 'angular-user-management' - - session = this.supabase.session - constructor(private readonly supabase: SupabaseService) {} + title = 'angular-user-management' + session: any + ngOnInit() { + this.session = this.supabase.session this.supabase.authChanges((_, session) => (this.session = session)) } } ``` -```html name=src/app/app.component.html +```html name=src/app/app.html
@@ -379,7 +397,7 @@ export class AppComponent implements OnInit { -`app.module.ts` also needs to be modified to include the `ReactiveFormsModule` from the `@angular/forms` package. +You also need to change `app.module.ts` to include the `ReactiveFormsModule` from the `@angular/forms` package. <$CodeTabs> @@ -387,32 +405,33 @@ export class AppComponent implements OnInit { import { NgModule } from '@angular/core' import { BrowserModule } from '@angular/platform-browser' -import { AppComponent } from './app.component' -import { AuthComponent } from './auth/auth.component' -import { AccountComponent } from './account/account.component' +import { AppComponent } from './app' +import { AuthComponent } from './auth/auth' +import { AccountComponent } from './account/account' import { ReactiveFormsModule } from '@angular/forms' -import { AvatarComponent } from './avatar/avatar.component' +import { AvatarComponent } from './avatar/avatar' @NgModule({ declarations: [AppComponent, AuthComponent, AccountComponent, AvatarComponent], imports: [BrowserModule, ReactiveFormsModule], providers: [], bootstrap: [AppComponent], + exports: [AppComponent, AuthComponent, AccountComponent, AvatarComponent], }) export class AppModule {} ``` -Once that's done, run this in a terminal window: +Once that's done, run the application in a terminal: ```bash npm run start ``` -And then open the browser to [localhost:4200](http://localhost:4200) and you should see the completed app. +Open the browser to [localhost:4200](http://localhost:4200) and you should see the completed app. -![Supabase Angular](/docs/img/supabase-angular-demo.png) +![Screenshot of the Supabase Angular application running in a browser](/docs/img/supabase-angular-demo.png) ## Bonus: Profile photos @@ -420,20 +439,21 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma ### Create an upload widget -Let's create an avatar for the user so that they can upload a profile photo. -Create an `AvatarComponent` with `ng g c avatar` Angular CLI command. +Create an avatar for the user so that they can upload a profile photo. +Create an `AvatarComponent` with `ng g c avatar` Angular CLI command and add the following code. <$CodeTabs> -```ts name=src/app/avatar/avatar.component.ts +```ts name=src/app/avatar/avatar.ts import { Component, EventEmitter, Input, Output } from '@angular/core' import { SafeResourceUrl, DomSanitizer } from '@angular/platform-browser' import { SupabaseService } from '../supabase.service' @Component({ selector: 'app-avatar', - templateUrl: './avatar.component.html', - styleUrls: ['./avatar.component.css'], + templateUrl: './avatar.html', + styleUrls: ['./avatar.css'], + standalone: false, }) export class AvatarComponent { _avatarUrl: SafeResourceUrl | undefined @@ -490,7 +510,7 @@ export class AvatarComponent { } ``` -```html name=src/app/avatar/avatar.component.html +```html name=src/app/avatar/avatar.html
-```html name=src/app/account.component.html +```html name=src/app/account.html - + ``` @@ -537,11 +557,11 @@ And add an `updateAvatar` function along with an `avatarUrl` getter to the `Acco <$CodeTabs> -```ts name=src/app/account.component.ts +```ts name=src/app/account.ts @Component({ selector: 'app-account', - templateUrl: './account.component.html', - styleUrls: ['./account.component.css'], + templateUrl: './account.html', + styleUrls: ['./account.css'], }) export class AccountComponent implements OnInit { // ... diff --git a/apps/docs/content/guides/local-development.mdx b/apps/docs/content/guides/local-development.mdx index e8ba56d4654..8545ceb1768 100644 --- a/apps/docs/content/guides/local-development.mdx +++ b/apps/docs/content/guides/local-development.mdx @@ -5,6 +5,17 @@ subtitle: Learn how to develop locally and use the Supabase CLI Develop locally while running the Supabase stack on your machine. + + +As a prerequisite, you must install a container runtime compatible with Docker APIs. + +- [Docker Desktop](https://docs.docker.com/desktop/) (macOS, Windows, Linux) +- [Rancher Desktop](https://rancherdesktop.io/) (macOS, Windows, Linux) +- [Podman](https://podman.io/) (macOS, Windows, Linux) +- [OrbStack](https://orbstack.dev/) (macOS) + + + ## Quickstart 1. Install the Supabase CLI: @@ -101,17 +112,6 @@ Develop locally while running the Supabase stack on your machine. - - - As a prerequisite, you must install a container runtime compatible with Docker APIs. - - - [Docker Desktop](https://docs.docker.com/desktop/) (macOS, Windows, Linux) - - [Rancher Desktop](https://rancherdesktop.io/) (macOS, Windows, Linux) - - [Podman](https://podman.io/) (macOS, Windows, Linux) - - [OrbStack](https://orbstack.dev/) (macOS) - - - 4. View your local Supabase instance at [http://localhost:54323](http://localhost:54323). ## Local development diff --git a/apps/docs/content/guides/local-development/cli/getting-started.mdx b/apps/docs/content/guides/local-development/cli/getting-started.mdx index 4240378957e..a65e54431ea 100644 --- a/apps/docs/content/guides/local-development/cli/getting-started.mdx +++ b/apps/docs/content/guides/local-development/cli/getting-started.mdx @@ -217,7 +217,7 @@ Started supabase local development setup. API URL: http://localhost:54321 DB URL: postgresql://postgres:postgres@localhost:54322/postgres Studio URL: http://localhost:54323 - Inbucket URL: http://localhost:54324 + Mailpit URL: http://localhost:54324 anon key: eyJh...... service_role key: eyJh...... diff --git a/apps/docs/content/guides/local-development/cli/testing-and-linting.mdx b/apps/docs/content/guides/local-development/cli/testing-and-linting.mdx index c3c8d2eb9a3..59e8bb4e0d1 100644 --- a/apps/docs/content/guides/local-development/cli/testing-and-linting.mdx +++ b/apps/docs/content/guides/local-development/cli/testing-and-linting.mdx @@ -36,11 +36,11 @@ Edge Functions are powered by Deno, which provides a [native set of testing tool ## Testing Auth emails -The Supabase CLI uses [Inbucket](https://github.com/inbucket/inbucket) to capture emails sent from your local machine. This is useful for testing emails sent from Supabase Auth. +The Supabase CLI uses [Mailpit](https://github.com/axllent/mailpit) to capture emails sent from your local machine. This is useful for testing emails sent from Supabase Auth. -### Accessing Inbucket +### Accessing Mailpit -By default, Inbucket is available at [localhost:54324](http://localhost:54324) when you run `supabase start`. Open this URL in your browser to view the emails. +By default, Mailpit is available at [localhost:54324](http://localhost:54324) when you run `supabase start`. Open this URL in your browser to view the emails. ### Going into production diff --git a/apps/docs/content/guides/platform/access-control.mdx b/apps/docs/content/guides/platform/access-control.mdx index 37b7b85dd52..2483a8f1ddb 100644 --- a/apps/docs/content/guides/platform/access-control.mdx +++ b/apps/docs/content/guides/platform/access-control.mdx @@ -260,20 +260,13 @@ The table below shows the actions each role can take on the resources belonging | | View | | | | | | | List | | | | | | | Run | | | | | -| Events Collections | Create | | | | | -| | Update | | | | | -| | Delete | | | | | -| | View | | | | | -| | List | | | | | -| Warehouse Access Tokens | Create | | | | | -| | Revoke | | | | | -| | List | | | | | | **Branching** | | | | | | -| Enable branching | - | | | | | -| Disable branching | - | | | | | +| Production Branch | Read | | | | | +| | Write | | | | | +| Development Branches | List | | | | | | | Create | | | | | +| | Update | | | | | | | Delete | | | | | -| | List | | | | | [^1]: Available on the Team and Enterprise Plans. [^2]: Sending anonymous data to OpenAI is opt in and can improve Studio AI Assistant's responses. diff --git a/apps/docs/content/guides/platform/credits.mdx b/apps/docs/content/guides/platform/credits.mdx index a71d1574de0..625cfc8bd79 100644 --- a/apps/docs/content/guides/platform/credits.mdx +++ b/apps/docs/content/guides/platform/credits.mdx @@ -27,7 +27,7 @@ As an example, if you start a Pro Plan subscription on January 1 and downgrade t ## Credit top-ups -You can top up credits at any time, with a maximum of per top-up. These credits do not expire and are non-refundable. +You can top up credits at any time, with a maximum of per top-up. These credits do not expire and are non-refundable. If you have any outstanding invoices, we’ll automatically use your credits to pay them off. Any remaining credits will be applied to future invoices. diff --git a/apps/docs/content/guides/platform/migrating-to-supabase/firestore-data.mdx b/apps/docs/content/guides/platform/migrating-to-supabase/firestore-data.mdx index 809d4e38b26..063eecdbad5 100644 --- a/apps/docs/content/guides/platform/migrating-to-supabase/firestore-data.mdx +++ b/apps/docs/content/guides/platform/migrating-to-supabase/firestore-data.mdx @@ -1,6 +1,6 @@ --- id: 'firestore-data' -title: 'Migrated from Firebase Firestore to Supabase' +title: 'Migrate from Firebase Firestore to Supabase' description: 'Migrate your Firebase Firestore database to a Supabase Postgres database.' subtitle: 'Migrate your Firebase Firestore database to a Supabase Postgres database.' --- diff --git a/apps/docs/content/guides/platform/migrating-within-supabase/backup-restore.mdx b/apps/docs/content/guides/platform/migrating-within-supabase/backup-restore.mdx index fd3644ed209..f26c2f72814 100644 --- a/apps/docs/content/guides/platform/migrating-within-supabase/backup-restore.mdx +++ b/apps/docs/content/guides/platform/migrating-within-supabase/backup-restore.mdx @@ -227,7 +227,7 @@ supabase db diff --linked --schema auth,storage > changes.sql The new project has the old project's Storage buckets, but the Storage objects need to be migrated manually. Use this script to move storage objects from one project to another. ```js -// npm install @supabase/supabase-js@1 +// npm install @supabase/supabase-js@2 const { createClient } = require('@supabase/supabase-js') const OLD_PROJECT_URL = 'https://xxx.supabase.co' @@ -238,7 +238,9 @@ const NEW_PROJECT_SERVICE_KEY = 'new-project-service-key-yyy' ;(async () => { const oldSupabaseRestClient = createClient(OLD_PROJECT_URL, OLD_PROJECT_SERVICE_KEY, { - schema: 'storage', + db: { + schema: 'storage', + }, }) const oldSupabaseClient = createClient(OLD_PROJECT_URL, OLD_PROJECT_SERVICE_KEY) const newSupabaseClient = createClient(NEW_PROJECT_URL, NEW_PROJECT_SERVICE_KEY) diff --git a/apps/docs/content/guides/queues/pgmq.mdx b/apps/docs/content/guides/queues/pgmq.mdx index fa4f840a37c..d5c01326e8f 100644 --- a/apps/docs/content/guides/queues/pgmq.mdx +++ b/apps/docs/content/guides/queues/pgmq.mdx @@ -207,7 +207,7 @@ select * from pgmq.send_batch( #### `read` -Read 1 or more messages from a queue. The VT specifies the delay in seconds between reading and the message becoming invisible to other consumers. +Read 1 or more messages from a queue. The VT specifies the duration of time in seconds that the message is invisible to other consumers. At the end of that duration, the message is visible again and could be read by other consumers. {/* prettier-ignore */} ```sql diff --git a/apps/docs/content/guides/storage/s3/authentication.mdx b/apps/docs/content/guides/storage/s3/authentication.mdx index b6a09be5555..55c5102fcce 100644 --- a/apps/docs/content/guides/storage/s3/authentication.mdx +++ b/apps/docs/content/guides/storage/s3/authentication.mdx @@ -25,6 +25,14 @@ This is all the information you need to connect to Supabase Storage using any S3 Storage S3 Access keys + + +For optimal performance when uploading large files you should always use the direct storage hostname. This provides several performance enhancements that will greatly improve performance when uploading large files. + +Instead of `https://project-id.supabase.co` use `https://project-id.storage.supabase.co` + + + @@ -86,7 +94,7 @@ const { const client = new S3Client({ forcePathStyle: true, region: 'project_region', - endpoint: 'https://project_ref.supabase.co/storage/v1/s3', + endpoint: 'https://project_ref.storage.supabase.co/storage/v1/s3', credentials: { accessKeyId: 'project_ref', secretAccessKey: 'anonKey', diff --git a/apps/docs/content/guides/storage/uploads/file-limits.mdx b/apps/docs/content/guides/storage/uploads/file-limits.mdx index af1628c5bc9..d890c477687 100644 --- a/apps/docs/content/guides/storage/uploads/file-limits.mdx +++ b/apps/docs/content/guides/storage/uploads/file-limits.mdx @@ -8,13 +8,13 @@ sidebar_label: 'Limits' ## Global file size -You can set the max file size across all your buckets by setting this global value in the dashboard [here](https://supabase.com/dashboard/project/_/settings/storage). For Free projects, the limit can't exceed 50 MB. On the Pro Plan and up, you can set this value to up to 50 GB. If you need more than 50 GB, [contact us](https://supabase.com/dashboard/support/new). +You can set the max file size across all your buckets by setting this global value in the dashboard [here](https://supabase.com/dashboard/project/_/settings/storage). For Free projects, the limit can't exceed 50 MB. On the Pro Plan and up, you can set this value to up to 500 GB. If you need more than 500 GB, [contact us](https://supabase.com/dashboard/support/new). | Plan | Max File Size Limit | | ---------- | ------------------- | | Free | 50 MB | -| Pro | 50 GB | -| Team | 50 GB | +| Pro | 500 GB | +| Team | 500 GB | | Enterprise | Custom | diff --git a/apps/docs/content/guides/storage/uploads/resumable-uploads.mdx b/apps/docs/content/guides/storage/uploads/resumable-uploads.mdx index d12d1cc6924..a7ac47b3031 100644 --- a/apps/docs/content/guides/storage/uploads/resumable-uploads.mdx +++ b/apps/docs/content/guides/storage/uploads/resumable-uploads.mdx @@ -14,6 +14,14 @@ The resumable upload method is recommended when: Supabase Storage implements the [TUS protocol](https://tus.io/) to enable resumable uploads. TUS stands for The Upload Server and is an open protocol for supporting resumable uploads. The protocol allows the upload process to be resumed from where it left off in case of interruptions. This method can be implemented using the [`tus-js-client`](https://github.com/tus/tus-js-client) library, or other client-side libraries like [Uppy](https://uppy.io/docs/tus/) that support the TUS protocol. + + +For optimal performance when uploading large files you should always use the direct storage hostname. This provides several performance enhancements that will greatly improve performance when uploading large files. + +Instead of `https://project-id.supabase.co` use `https://project-id.storage.supabase.co` + + + { var upload = new tus.Upload(file, { - endpoint: `https://${projectId}.supabase.co/storage/v1/upload/resumable`, + // Supabase TUS endpoint (with direct storage hostname) + endpoint: `https://${projectId}.storage.supabase.co/storage/v1/upload/resumable`, retryDelays: [0, 3000, 5000, 10000, 20000], headers: { authorization: `Bearer ${session.access_token}`, @@ -125,7 +134,7 @@ Supabase Storage implements the [TUS protocol](https://tus.io/) to enable resuma // Initialize Uppy instance only once const [uppy] = useState(() => new Uppy()); // Initialize Supabase client with project URL and anon key - const supabase = createClient(projectURL, anonKey); + const supabase = createClient(`https://${projectId}.supabase.co`, anonKey); useEffect(() => { const initializeUppy = async () => { @@ -135,7 +144,8 @@ Supabase Storage implements the [TUS protocol](https://tus.io/) to enable resuma } = await supabase.auth.getSession(); uppy.use(Tus, { - endpoint: `${projectURL}/storage/v1/upload/resumable`, // Supabase TUS endpoint + // Supabase TUS endpoint (with direct storage hostname) + endpoint: `https://${projectId}.storage.supabase.co/storage/v1/upload/resumable`, retryDelays: [0, 3000, 5000, 10000, 20000], // Retry delays for resumable uploads headers: { authorization: `Bearer ${session?.access_token}`, // User session access token diff --git a/apps/docs/content/guides/storage/uploads/s3-uploads.mdx b/apps/docs/content/guides/storage/uploads/s3-uploads.mdx index 13f888e2fb5..93c3f4f2be9 100644 --- a/apps/docs/content/guides/storage/uploads/s3-uploads.mdx +++ b/apps/docs/content/guides/storage/uploads/s3-uploads.mdx @@ -17,7 +17,7 @@ The S3 protocol supports file upload using: The `PutObject` action uploads the file in a single request. This matches the behavior of the Supabase SDK [Standard Upload](/docs/guides/storage/uploads/standard-uploads). -Use `PutObject` to upload smaller files, where retrying the entire upload won't be an issue. The maximum file size on paid plans is 50 GB. +Use `PutObject` to upload smaller files, where retrying the entire upload won't be an issue. The maximum file size on paid plans is 500 GB. For example, using JavaScript and the `aws-sdk` client: @@ -42,7 +42,7 @@ await s3Client.send(uploadCommand) Multipart Uploads split the file into smaller parts and upload them in parallel, maximizing the upload speed on a fast network. When uploading large files, this allows you to retry the upload of individual parts in case of network issues. -This method is preferable over [Resumable Upload](/docs/guides/storage/uploads/resumable-uploads) for server-side uploads, when you want to maximize upload speed at the cost of resumability. The maximum file size on paid plans is 50 GB. +This method is preferable over [Resumable Upload](/docs/guides/storage/uploads/resumable-uploads) for server-side uploads, when you want to maximize upload speed at the cost of resumability. The maximum file size on paid plans is 500 GB. ### Upload a file in parts diff --git a/apps/docs/package.json b/apps/docs/package.json index 1dd7f1f831c..96c1cdb065a 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -9,7 +9,7 @@ "build:llms": "tsx ./scripts/llms.ts", "build:sitemap": "tsx ./internals/generate-sitemap.ts", "clean": "rimraf .next .turbo node_modules features/docs/generated examples __generated__", - "codegen:examples": "cp -r ../../examples ./examples", + "codegen:examples": "shx cp -r ../../examples ./examples", "codegen:graphql": "tsx --conditions=react-server ./scripts/graphqlSchema.ts && graphql-codegen --config codegen.ts", "codegen:references": "tsx features/docs/Reference.generated.script.ts", "codemod:frontmatter": "node ./scripts/codemod/mdx-meta.mjs && prettier --cache --write \"content/**/*.mdx\"", @@ -149,6 +149,7 @@ "openapi-types": "^12.1.3", "postcss": "^8.5.3", "shiki": "^3.2.1", + "shx": "^0.4.0", "simple-git": "^3.24.0", "slugify": "^1.6.6", "smol-toml": "^1.3.1", diff --git a/apps/docs/public/humans.txt b/apps/docs/public/humans.txt index 4aa7b3c1469..58db9f6a50c 100644 --- a/apps/docs/public/humans.txt +++ b/apps/docs/public/humans.txt @@ -26,7 +26,9 @@ Chris Caruso Chris Chandler Chris Copplestone Chris Gwilliams +Chris Martin Chris Stockton +Chris Ward Craig Cannon Danny White Darren Cunningham @@ -39,6 +41,7 @@ Divit D Eduardo Gurgel Egor Romanov Eleftheria Trivyzaki +Emmett Folger Etienne Stalmans Fabrizio Fenoglio Felipe Stival @@ -54,6 +57,7 @@ Inian P Ivan Vasilov Jenny Kibiri Jess Shears +Jim Chanco Jr John Pena Jon M Jonny Summers-Muir @@ -81,6 +85,7 @@ Manan Gupta Margarita Sandomirskaia Mark Burggraf Monica Khoury +Mykhailo Mischa Lieibenson Nyannyacha Oli R Pamela Chia @@ -104,6 +109,7 @@ Stanislav M Stephen Morgan Steve Chavez Stojan Dimitrovski +Sugu Sougoumarane Supun Sudaraka Kalidasa Taryn King Terry Sutton diff --git a/apps/docs/spec/api_v1_openapi.json b/apps/docs/spec/api_v1_openapi.json index 370f302ea3d..81dd92764b4 100644 --- a/apps/docs/spec/api_v1_openapi.json +++ b/apps/docs/spec/api_v1_openapi.json @@ -7,7 +7,13 @@ "summary": "Get database branch config", "description": "Fetches configurations of the specified database branch", "parameters": [ - { "name": "branch_id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "branch_id", + "required": true, + "in": "path", + "description": "Branch ID", + "schema": { "format": "uuid", "type": "string" } + } ], "responses": { "200": { @@ -28,7 +34,13 @@ "summary": "Update database branch config", "description": "Updates the configuration of the specified database branch", "parameters": [ - { "name": "branch_id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "branch_id", + "required": true, + "in": "path", + "description": "Branch ID", + "schema": { "format": "uuid", "type": "string" } + } ], "requestBody": { "required": true, @@ -53,7 +65,13 @@ "summary": "Delete a database branch", "description": "Deletes the specified database branch", "parameters": [ - { "name": "branch_id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "branch_id", + "required": true, + "in": "path", + "description": "Branch ID", + "schema": { "format": "uuid", "type": "string" } + } ], "responses": { "200": { @@ -76,8 +94,20 @@ "summary": "Pushes a database branch", "description": "Pushes the specified database branch", "parameters": [ - { "name": "branch_id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "branch_id", + "required": true, + "in": "path", + "description": "Branch ID", + "schema": { "format": "uuid", "type": "string" } + } ], + "requestBody": { + "required": true, + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/BranchActionBody" } } + } + }, "responses": { "201": { "description": "", @@ -99,8 +129,20 @@ "summary": "Merges a database branch", "description": "Merges the specified database branch", "parameters": [ - { "name": "branch_id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "branch_id", + "required": true, + "in": "path", + "description": "Branch ID", + "schema": { "format": "uuid", "type": "string" } + } ], + "requestBody": { + "required": true, + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/BranchActionBody" } } + } + }, "responses": { "201": { "description": "", @@ -122,8 +164,20 @@ "summary": "Resets a database branch", "description": "Resets the specified database branch", "parameters": [ - { "name": "branch_id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "branch_id", + "required": true, + "in": "path", + "description": "Branch ID", + "schema": { "format": "uuid", "type": "string" } + } ], + "requestBody": { + "required": true, + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/BranchActionBody" } } + } + }, "responses": { "201": { "description": "", @@ -139,6 +193,37 @@ "security": [{ "bearer": [] }] } }, + "/v1/branches/{branch_id}/diff": { + "get": { + "operationId": "v1-diff-a-branch", + "summary": "[Beta] Diffs a database branch", + "description": "Diffs the specified database branch", + "parameters": [ + { + "name": "branch_id", + "required": true, + "in": "path", + "description": "Branch ID", + "schema": { "format": "uuid", "type": "string" } + }, + { + "name": "included_schemas", + "required": false, + "in": "query", + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "content": { "text/plain": { "schema": { "type": "string" } } }, + "description": "" + }, + "500": { "description": "Failed to diff database branch" } + }, + "tags": ["Environments"], + "security": [{ "bearer": [] }] + } + }, "/v1/projects": { "get": { "operationId": "v1-list-all-projects", @@ -168,9 +253,7 @@ "requestBody": { "required": true, "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/V1CreateProjectBodyDto" } - } + "application/json": { "schema": { "$ref": "#/components/schemas/V1CreateProjectBody" } } } }, "responses": { @@ -216,7 +299,7 @@ "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CreateOrganizationV1Dto" } + "schema": { "$ref": "#/components/schemas/CreateOrganizationV1" } } } }, @@ -241,23 +324,16 @@ "summary": "[Beta] Authorize user through oauth", "parameters": [ { - "name": "code_challenge_method", - "required": false, + "name": "client_id", + "required": true, "in": "query", - "schema": { "enum": ["plain", "sha256", "S256"], "type": "string" } + "schema": { "format": "uuid", "type": "string" } }, - { - "name": "code_challenge", - "required": false, - "in": "query", - "schema": { "type": "string" } - }, - { "name": "state", "required": false, "in": "query", "schema": { "type": "string" } }, { "name": "response_type", "required": true, "in": "query", - "schema": { "type": "string" } + "schema": { "enum": ["code", "token", "id_token token"], "type": "string" } }, { "name": "redirect_uri", @@ -265,9 +341,35 @@ "in": "query", "schema": { "type": "string" } }, - { "name": "client_id", "required": true, "in": "query", "schema": { "type": "string" } } + { "name": "scope", "required": false, "in": "query", "schema": { "type": "string" } }, + { "name": "state", "required": false, "in": "query", "schema": { "type": "string" } }, + { + "name": "response_mode", + "required": false, + "in": "query", + "schema": { "type": "string" } + }, + { + "name": "code_challenge", + "required": false, + "in": "query", + "schema": { "type": "string" } + }, + { + "name": "code_challenge_method", + "required": false, + "in": "query", + "schema": { "enum": ["plain", "sha256", "S256"], "type": "string" } + }, + { + "name": "organization_slug", + "required": false, + "in": "query", + "description": "Organization slug", + "schema": { "pattern": "^[\\w-]+$", "type": "string" } + } ], - "responses": { "303": { "description": "" } }, + "responses": { "204": { "description": "" } }, "tags": ["OAuth"], "security": [{ "oauth2": ["read"] }] } @@ -308,7 +410,7 @@ "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/OAuthRevokeTokenBodyDto" } + "schema": { "$ref": "#/components/schemas/OAuthRevokeTokenBody" } } } }, @@ -317,11 +419,75 @@ "security": [{ "oauth2": ["write"] }] } }, + "/v1/oauth/authorize/project-claim": { + "get": { + "operationId": "v1-oauth-authorize-project-claim", + "x-internal": true, + "summary": "Authorize user through oauth and claim a project", + "description": "Initiates the OAuth authorization flow for the specified provider. After successful authentication, the user can claim ownership of the specified project.", + "parameters": [ + { + "name": "project_ref", + "required": true, + "in": "query", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "client_id", + "required": true, + "in": "query", + "schema": { "format": "uuid", "type": "string" } + }, + { + "name": "response_type", + "required": true, + "in": "query", + "schema": { "enum": ["code", "token", "id_token token"], "type": "string" } + }, + { + "name": "redirect_uri", + "required": true, + "in": "query", + "schema": { "type": "string" } + }, + { "name": "state", "required": false, "in": "query", "schema": { "type": "string" } }, + { + "name": "response_mode", + "required": false, + "in": "query", + "schema": { "type": "string" } + }, + { + "name": "code_challenge", + "required": false, + "in": "query", + "schema": { "type": "string" } + }, + { + "name": "code_challenge_method", + "required": false, + "in": "query", + "schema": { "enum": ["plain", "sha256", "S256"], "type": "string" } + } + ], + "responses": { "204": { "description": "" } }, + "tags": ["OAuth"], + "security": [{ "bearer": [] }] + } + }, "/v1/snippets": { "get": { "operationId": "v1-list-all-snippets", "summary": "Lists SQL snippets for the logged in user", "parameters": [ + { + "name": "project_ref", + "required": false, + "in": "query", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, { "name": "cursor", "required": false, "in": "query", "schema": { "type": "string" } }, { "name": "limit", @@ -340,12 +506,6 @@ "required": false, "in": "query", "schema": { "enum": ["asc", "desc"], "type": "string" } - }, - { - "name": "project_ref", - "required": false, - "in": "query", - "schema": { "type": "string" } } ], "responses": { @@ -391,8 +551,20 @@ "operationId": "v1-get-project-api-keys", "summary": "Get project api keys", "parameters": [ - { "name": "reveal", "required": false, "in": "query", "schema": { "type": "boolean" } }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "reveal", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { "type": "boolean" } + } ], "responses": { "200": { @@ -412,11 +584,23 @@ "security": [{ "bearer": [] }] }, "post": { - "operationId": "createApiKey", - "summary": "[Alpha] Creates a new API key for the project", + "operationId": "v1-create-project-api-key", + "summary": "Creates a new API key for the project", "parameters": [ - { "name": "reveal", "required": false, "in": "query", "schema": { "type": "boolean" } }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "reveal", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { "type": "boolean" } + } ], "requestBody": { "required": true, @@ -437,14 +621,92 @@ "security": [{ "bearer": [] }] } }, + "/v1/projects/{ref}/api-keys/legacy": { + "get": { + "operationId": "v1-get-project-legacy-api-keys", + "summary": "Check whether JWT based legacy (anon, service_role) API keys are enabled. This API endpoint will be removed in the future, check for HTTP 404 Not Found.", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/LegacyApiKeysResponse" } + } + } + }, + "403": { "description": "" } + }, + "tags": ["Secrets"], + "security": [{ "bearer": [] }] + }, + "put": { + "operationId": "v1-update-project-legacy-api-keys", + "summary": "Disable or re-enable JWT based legacy (anon, service_role) API keys. This API endpoint will be removed in the future, check for HTTP 404 Not Found.", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "enabled", + "required": true, + "in": "query", + "description": "Boolean string, true or false", + "schema": { "type": "boolean" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/LegacyApiKeysResponse" } + } + } + }, + "403": { "description": "" } + }, + "tags": ["Secrets"], + "security": [{ "bearer": [] }] + } + }, "/v1/projects/{ref}/api-keys/{id}": { "patch": { - "operationId": "updateApiKey", - "summary": "[Alpha] Updates an API key for the project", + "operationId": "v1-update-project-api-key", + "summary": "Updates an API key for the project", "parameters": [ - { "name": "reveal", "required": false, "in": "query", "schema": { "type": "boolean" } }, - { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "id", + "required": true, + "in": "path", + "schema": { "format": "uuid", "type": "string" } + }, + { + "name": "reveal", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { "type": "boolean" } + } ], "requestBody": { "required": true, @@ -465,12 +727,29 @@ "security": [{ "bearer": [] }] }, "get": { - "operationId": "getApiKey", - "summary": "[Alpha] Get API key", + "operationId": "v1-get-project-api-key", + "summary": "Get API key", "parameters": [ - { "name": "reveal", "required": false, "in": "query", "schema": { "type": "boolean" } }, - { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "id", + "required": true, + "in": "path", + "schema": { "format": "uuid", "type": "string" } + }, + { + "name": "reveal", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { "type": "boolean" } + } ], "responses": { "200": { @@ -485,12 +764,37 @@ "security": [{ "bearer": [] }] }, "delete": { - "operationId": "deleteApiKey", - "summary": "[Alpha] Deletes an API key for the project", + "operationId": "v1-delete-project-api-key", + "summary": "Deletes an API key for the project", "parameters": [ - { "name": "reveal", "required": false, "in": "query", "schema": { "type": "boolean" } }, - { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "id", + "required": true, + "in": "path", + "schema": { "format": "uuid", "type": "string" } + }, + { + "name": "reveal", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { "type": "boolean" } + }, + { + "name": "was_compromised", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { "type": "boolean" } + }, + { "name": "reason", "required": false, "in": "query", "schema": { "type": "string" } } ], "responses": { "200": { @@ -511,7 +815,13 @@ "summary": "List all database branches", "description": "Returns all database branches of the specified project.", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -536,7 +846,13 @@ "summary": "Create a database branch", "description": "Creates a database branch from the specified project.", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -562,7 +878,13 @@ "summary": "Disables preview branching", "description": "Disables preview branching for the specified project", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { "description": "" }, @@ -573,6 +895,35 @@ "security": [{ "bearer": [] }] } }, + "/v1/projects/{ref}/branches/{name}": { + "get": { + "operationId": "v1-get-a-branch", + "summary": "Get a database branch", + "description": "Fetches the specified database branch by its name.", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { "name": "name", "required": true, "in": "path", "schema": { "type": "string" } } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/BranchResponse" } } + } + }, + "403": { "description": "" }, + "500": { "description": "Failed to fetch database branch" } + }, + "tags": ["Environments"], + "security": [{ "bearer": [] }] + } + }, "/v1/projects/{ref}/custom-hostname": { "get": { "operationId": "v1-get-hostname-config", @@ -583,7 +934,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -610,7 +961,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -632,7 +983,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "requestBody": { @@ -669,7 +1020,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -698,7 +1049,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -722,7 +1073,13 @@ "operationId": "v1-list-all-network-bans", "summary": "[Beta] Gets project's network bans", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "201": { @@ -740,12 +1097,47 @@ "security": [{ "bearer": [] }] } }, + "/v1/projects/{ref}/network-bans/retrieve/enriched": { + "post": { + "operationId": "v1-list-all-network-bans-enriched", + "summary": "[Beta] Gets project's network bans with additional information about which databases they affect", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } + ], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/NetworkBanResponseEnriched" } + } + } + }, + "403": { "description": "" }, + "500": { "description": "Failed to retrieve project's enriched network bans" } + }, + "tags": ["Projects"], + "security": [{ "bearer": [] }] + } + }, "/v1/projects/{ref}/network-bans": { "delete": { "operationId": "v1-delete-network-bans", "summary": "[Beta] Remove network bans.", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -769,7 +1161,13 @@ "operationId": "v1-get-network-restrictions", "summary": "[Beta] Gets project's network restrictions", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -792,7 +1190,13 @@ "operationId": "v1-update-network-restrictions", "summary": "[Beta] Updates project's network restrictions", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -823,7 +1227,13 @@ "operationId": "v1-get-pgsodium-config", "summary": "[Beta] Gets project's pgsodium config", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -844,7 +1254,13 @@ "operationId": "v1-update-pgsodium-config", "summary": "[Beta] Updates project's pgsodium config. Updating the root_key can cause all data encrypted with the older key to become inaccessible.", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -880,7 +1296,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -907,7 +1323,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "requestBody": { @@ -944,7 +1360,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -971,7 +1387,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -995,7 +1411,13 @@ "summary": "List all secrets", "description": "Returns all secrets you've previously added to the specified project.", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -1020,17 +1442,18 @@ "summary": "Bulk create secrets", "description": "Creates multiple secrets and adds them to the specified project.", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, "content": { - "application/json": { - "schema": { - "type": "array", - "items": { "$ref": "#/components/schemas/CreateSecretBody" } - } - } + "application/json": { "schema": { "$ref": "#/components/schemas/CreateSecretBody" } } } }, "responses": { @@ -1046,7 +1469,13 @@ "summary": "Bulk delete secrets", "description": "Deletes all secrets with the given names from the specified project", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -1055,10 +1484,7 @@ } }, "responses": { - "200": { - "description": "", - "content": { "application/json": { "schema": { "type": "object" } } } - }, + "200": { "description": "" }, "403": { "description": "" }, "500": { "description": "Failed to delete secrets with given names" } }, @@ -1071,7 +1497,13 @@ "operationId": "v1-get-ssl-enforcement-config", "summary": "[Beta] Get project's SSL enforcement configuration.", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -1092,7 +1524,13 @@ "operationId": "v1-update-ssl-enforcement-config", "summary": "[Beta] Update project's SSL enforcement configuration.", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -1130,7 +1568,13 @@ "in": "query", "schema": { "default": "public", "type": "string" } }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -1153,7 +1597,13 @@ "operationId": "v1-get-vanity-subdomain-config", "summary": "[Beta] Gets current vanity subdomain config", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -1174,7 +1624,13 @@ "operationId": "v1-deactivate-vanity-subdomain-config", "summary": "[Beta] Deletes a project's vanity subdomain configuration", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { "description": "" }, @@ -1190,7 +1646,13 @@ "operationId": "v1-check-vanity-subdomain-availability", "summary": "[Beta] Checks vanity subdomain availability", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -1219,7 +1681,13 @@ "operationId": "v1-activate-vanity-subdomain-config", "summary": "[Beta] Activates a vanity subdomain for a project.", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -1248,7 +1716,13 @@ "operationId": "v1-upgrade-postgres-version", "summary": "[Beta] Upgrades the project's Postgres version", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -1277,7 +1751,13 @@ "operationId": "v1-get-postgres-upgrade-eligibility", "summary": "[Beta] Returns the project's eligibility for upgrades", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -1300,13 +1780,19 @@ "operationId": "v1-get-postgres-upgrade-status", "summary": "[Beta] Gets the latest status of the project's upgrade", "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, { "name": "tracking_id", "required": false, "in": "query", "schema": { "type": "string" } - }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + } ], "responses": { "200": { @@ -1334,7 +1820,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -1363,7 +1849,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -1380,7 +1866,13 @@ "operationId": "v1-setup-a-read-replica", "summary": "[Beta] Set up a read replica", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -1404,7 +1896,13 @@ "operationId": "v1-remove-a-read-replica", "summary": "[Beta] Remove a read replica", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -1433,13 +1931,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } - }, - { - "name": "timeout_ms", - "required": false, - "in": "query", - "schema": { "minimum": 0, "maximum": 10000, "type": "integer" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } }, { "name": "services", @@ -1452,6 +1944,12 @@ "enum": ["auth", "db", "pooler", "realtime", "rest", "storage"] } } + }, + { + "name": "timeout_ms", + "required": false, + "in": "query", + "schema": { "minimum": 0, "maximum": 10000, "type": "integer" } } ], "responses": { @@ -1473,12 +1971,72 @@ "security": [{ "bearer": [] }] } }, + "/v1/projects/{ref}/config/auth/signing-keys/legacy": { + "post": { + "operationId": "v1-create-legacy-signing-key", + "summary": "Set up the project's existing JWT secret as an in_use JWT signing key. This endpoint will be removed in the future always check for HTTP 404 Not Found.", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } + ], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/SigningKeyResponse" } + } + } + }, + "403": { "description": "" } + }, + "tags": ["Auth"], + "security": [{ "bearer": [] }] + }, + "get": { + "operationId": "v1-get-legacy-signing-key", + "summary": "Get the signing key information for the JWT secret imported as signing key for this project. This endpoint will be removed in the future, check for HTTP 404 Not Found.", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/SigningKeyResponse" } + } + } + }, + "403": { "description": "" } + }, + "tags": ["Auth"], + "security": [{ "bearer": [] }] + } + }, "/v1/projects/{ref}/config/auth/signing-keys": { "post": { - "operationId": "createSigningKeyForProject", - "summary": "[Alpha] Create a new signing key for the project in standby status", + "operationId": "v1-create-project-signing-key", + "summary": "Create a new signing key for the project in standby status", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -1490,10 +2048,10 @@ }, "responses": { "201": { - "description": "[Alpha] Create a new signing key for the project in standby status", + "description": "", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CreateSigningKeyBody" } + "schema": { "$ref": "#/components/schemas/SigningKeyResponse" } } } }, @@ -1503,10 +2061,16 @@ "security": [{ "bearer": [] }] }, "get": { - "operationId": "listSigningKeysForProject", - "summary": "[Alpha] List all signing keys for the project", + "operationId": "v1-get-project-signing-keys", + "summary": "List all signing keys for the project", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -1525,11 +2089,22 @@ }, "/v1/projects/{ref}/config/auth/signing-keys/{id}": { "get": { - "operationId": "getSigningKeyForProject", - "summary": "[Alpha] Get information about a signing key", + "operationId": "v1-get-project-signing-key", + "summary": "Get information about a signing key", "parameters": [ - { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "id", + "required": true, + "in": "path", + "schema": { "format": "uuid", "type": "string" } + }, + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -1546,11 +2121,22 @@ "security": [{ "bearer": [] }] }, "delete": { - "operationId": "deleteSigningKey", - "summary": "[Alpha] Remove a signing key from a project, where the status is previously_used", + "operationId": "v1-remove-project-signing-key", + "summary": "Remove a signing key from a project. Only possible if the key has been in revoked status for a while.", "parameters": [ - { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "id", + "required": true, + "in": "path", + "schema": { "format": "uuid", "type": "string" } + }, + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -1567,11 +2153,22 @@ "security": [{ "bearer": [] }] }, "patch": { - "operationId": "patchSigningKey", - "summary": "[Alpha] Update a signing key, mainly its status", + "operationId": "v1-update-project-signing-key", + "summary": "Update a signing key, mainly its status", "parameters": [ - { "name": "id", "required": true, "in": "path", "schema": { "type": "string" } }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "id", + "required": true, + "in": "path", + "schema": { "format": "uuid", "type": "string" } + }, + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -1606,7 +2203,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -1633,7 +2230,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "requestBody": { @@ -1663,7 +2260,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -1690,7 +2287,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "requestBody": { @@ -1727,7 +2324,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -1747,7 +2344,7 @@ }, "/v1/projects/{ref}/config/database/pooler": { "get": { - "operationId": "getSupavisorConfig", + "operationId": "v1-get-pooler-config", "summary": "Gets project's supavisor config", "parameters": [ { @@ -1755,7 +2352,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -1776,7 +2373,7 @@ "security": [{ "bearer": [] }] }, "patch": { - "operationId": "updateSupavisorConfig", + "operationId": "v1-update-pooler-config", "summary": "Updates project's supavisor config", "parameters": [ { @@ -1784,7 +2381,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "requestBody": { @@ -1821,7 +2418,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -1848,7 +2445,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "requestBody": { @@ -1877,7 +2474,7 @@ }, "/v1/projects/{ref}/config/auth/third-party-auth": { "post": { - "operationId": "createTPAForProject", + "operationId": "v1-create-project-tpa-integration", "summary": "Creates a new third-party auth integration", "parameters": [ { @@ -1885,7 +2482,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "requestBody": { @@ -1909,7 +2506,7 @@ "security": [{ "bearer": [] }] }, "get": { - "operationId": "listTPAForProject", + "operationId": "v1-list-project-tpa-integrations", "summary": "Lists all third-party auth integrations", "parameters": [ { @@ -1917,7 +2514,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -1940,7 +2537,7 @@ }, "/v1/projects/{ref}/config/auth/third-party-auth/{tpa_id}": { "delete": { - "operationId": "deleteTPAForProject", + "operationId": "v1-delete-project-tpa-integration", "summary": "Removes a third-party auth integration", "parameters": [ { @@ -1948,9 +2545,14 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } }, - { "name": "tpa_id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "tpa_id", + "required": true, + "in": "path", + "schema": { "format": "uuid", "type": "string" } + } ], "responses": { "200": { @@ -1965,7 +2567,7 @@ "security": [{ "bearer": [] }] }, "get": { - "operationId": "getTPAForProject", + "operationId": "v1-get-project-tpa-integration", "summary": "Get a third-party integration", "parameters": [ { @@ -1973,9 +2575,14 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } }, - { "name": "tpa_id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "tpa_id", + "required": true, + "in": "path", + "schema": { "format": "uuid", "type": "string" } + } ], "responses": { "200": { @@ -2000,7 +2607,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { "200": { "description": "" }, "403": { "description": "" } }, @@ -2013,7 +2620,13 @@ "operationId": "v1-list-available-restore-versions", "summary": "Lists available restore versions for the given project", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -2035,16 +2648,14 @@ "operationId": "v1-restore-a-project", "summary": "Restores the given project", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/RestoreProjectBodyDto" } - } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } - }, + ], "responses": { "200": { "description": "" }, "403": { "description": "" } }, "tags": ["Projects"], "security": [{ "bearer": [] }] @@ -2055,7 +2666,13 @@ "operationId": "v1-cancel-a-project-restoration", "summary": "Cancels the given project restoration", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { "description": "" }, "403": { "description": "" } }, "tags": ["Projects"], @@ -2067,33 +2684,46 @@ "operationId": "v1-list-project-addons", "summary": "Lists project addons", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { "description": "", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ListProjectAddonsResponseDto" } + "schema": { "$ref": "#/components/schemas/ListProjectAddonsResponse" } } } }, "403": { "description": "" }, "500": { "description": "Failed to list project addons" } }, + "tags": ["Billing"], "security": [{ "bearer": [] }] }, "patch": { "operationId": "v1-apply-project-addon", "summary": "Applies project addon", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ApplyProjectAddonBodyDto" } + "schema": { "$ref": "#/components/schemas/ApplyProjectAddonBody" } } } }, @@ -2102,6 +2732,7 @@ "403": { "description": "" }, "500": { "description": "Failed to apply project addon" } }, + "tags": ["Billing"], "security": [{ "bearer": [] }] } }, @@ -2110,83 +2741,311 @@ "operationId": "v1-remove-project-addon", "summary": "Removes project addon", "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, { "name": "addon_variant", "required": true, "in": "path", - "schema": { - "enum": [ - "ci_micro", - "ci_small", - "ci_medium", - "ci_large", - "ci_xlarge", - "ci_2xlarge", - "ci_4xlarge", - "ci_8xlarge", - "ci_12xlarge", - "ci_16xlarge", - "cd_default", - "pitr_7", - "pitr_14", - "pitr_28", - "ipv4_default" - ], - "type": "string" - } - }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + "oneOf": [ + { + "type": "string", + "enum": [ + "ci_micro", + "ci_small", + "ci_medium", + "ci_large", + "ci_xlarge", + "ci_2xlarge", + "ci_4xlarge", + "ci_8xlarge", + "ci_12xlarge", + "ci_16xlarge", + "ci_24xlarge", + "ci_24xlarge_optimized_cpu", + "ci_24xlarge_optimized_memory", + "ci_24xlarge_high_memory", + "ci_48xlarge", + "ci_48xlarge_optimized_cpu", + "ci_48xlarge_optimized_memory", + "ci_48xlarge_high_memory" + ] + }, + { "type": "string", "enum": ["cd_default"] }, + { "type": "string", "enum": ["pitr_7", "pitr_14", "pitr_28"] }, + { "type": "string", "enum": ["ipv4_default"] } + ], + "schema": {} + } ], "responses": { "200": { "description": "" }, "403": { "description": "" }, "500": { "description": "Failed to remove project addon" } }, + "tags": ["Billing"], "security": [{ "bearer": [] }] } }, - "/v1/projects/{ref}/analytics/endpoints/logs.all": { + "/v1/projects/{ref}/claim-token": { "get": { - "operationId": "getLogs", - "summary": "Gets project's logs", + "operationId": "v1-get-project-claim-token", + "x-internal": true, + "summary": "Gets project claim token", "parameters": [ { - "name": "iso_timestamp_end", - "required": false, - "in": "query", - "schema": { "type": "string" } - }, - { - "name": "iso_timestamp_start", - "required": false, - "in": "query", - "schema": { "type": "string" } - }, - { "name": "sql", "required": false, "in": "query", "schema": { "type": "string" } }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { "description": "", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/V1AnalyticsResponse" } + "schema": { "$ref": "#/components/schemas/ProjectClaimTokenResponse" } } } }, "403": { "description": "" } }, + "tags": ["Projects"], + "security": [{ "bearer": [] }] + }, + "post": { + "operationId": "v1-create-project-claim-token", + "x-internal": true, + "summary": "Creates project claim token", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/CreateProjectClaimTokenResponse" } + } + } + }, + "403": { "description": "" } + }, + "tags": ["Projects"], + "security": [{ "bearer": [] }] + }, + "delete": { + "operationId": "v1-delete-project-claim-token", + "x-internal": true, + "summary": "Revokes project claim token", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } + ], + "responses": { "204": { "description": "" }, "403": { "description": "" } }, + "tags": ["Projects"], + "security": [{ "bearer": [] }] + } + }, + "/v1/projects/{ref}/advisors/performance": { + "get": { + "operationId": "v1-get-performance-advisors", + "summary": "Gets project performance advisors.", + "description": "This is an **experimental** endpoint. It is subject to change or removal in future versions. Use it with caution, as it may not remain supported or stable.", + "deprecated": true, + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/V1ProjectAdvisorsResponse" } + } + } + }, + "403": { "description": "" } + }, + "tags": ["Advisors"], + "security": [{ "bearer": [] }] + } + }, + "/v1/projects/{ref}/advisors/security": { + "get": { + "operationId": "v1-get-security-advisors", + "summary": "Gets project security advisors.", + "description": "This is an **experimental** endpoint. It is subject to change or removal in future versions. Use it with caution, as it may not remain supported or stable.", + "deprecated": true, + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "lint_type", + "required": false, + "in": "query", + "schema": { "enum": ["sql"], "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/V1ProjectAdvisorsResponse" } + } + } + }, + "403": { "description": "" } + }, + "tags": ["Advisors"], + "security": [{ "bearer": [] }] + } + }, + "/v1/projects/{ref}/analytics/endpoints/logs.all": { + "get": { + "operationId": "v1-get-project-logs", + "summary": "Gets project's logs", + "description": "Executes a SQL query on the project's logs.\n\nEither the 'iso_timestamp_start' and 'iso_timestamp_end' parameters must be provided.\nIf both are not provided, only the last 1 minute of logs will be queried.\nThe timestamp range must be no more than 24 hours and is rounded to the nearest minute. If the range is more than 24 hours, a validation error will be thrown.\n", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { "name": "sql", "required": false, "in": "query", "schema": { "type": "string" } }, + { + "name": "iso_timestamp_start", + "required": false, + "in": "query", + "schema": { "format": "date-time", "type": "string" } + }, + { + "name": "iso_timestamp_end", + "required": false, + "in": "query", + "schema": { "format": "date-time", "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/AnalyticsResponse" } } + } + }, + "403": { "description": "" } + }, + "tags": ["Analytics"], + "security": [{ "bearer": [] }] + } + }, + "/v1/projects/{ref}/analytics/endpoints/usage.api-counts": { + "get": { + "operationId": "v1-get-project-usage-api-count", + "summary": "Gets project's usage api counts", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "interval", + "required": false, + "in": "query", + "schema": { + "enum": ["15min", "30min", "1hr", "3hr", "1day", "3day", "7day"], + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/AnalyticsResponse" } } + } + }, + "403": { "description": "" }, + "500": { "description": "Failed to get project's usage api counts" } + }, + "tags": ["Analytics"], + "security": [{ "bearer": [] }] + } + }, + "/v1/projects/{ref}/analytics/endpoints/usage.api-requests-count": { + "get": { + "operationId": "v1-get-project-usage-request-count", + "summary": "Gets project's usage api requests count", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/AnalyticsResponse" } } + } + }, + "500": { "description": "Failed to get project's usage api requests count" } + }, "tags": ["Analytics"], "security": [{ "bearer": [] }] } }, "/v1/projects/{ref}/database/migrations": { "get": { - "operationId": "v1-list-migrations", + "operationId": "v1-list-migration-history", "summary": "[Beta] List applied migration versions", "description": "Only available to selected partner OAuth apps", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -2208,18 +3067,72 @@ "summary": "[Beta] Apply a database migration", "description": "Only available to selected partner OAuth apps", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } - ], - "responses": { - "200": { - "description": "", - "content": { "application/json": { "schema": { "type": "object" } } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } }, + { + "name": "Idempotency-Key", + "required": false, + "in": "header", + "description": "A unique key to ensure the same migration is tracked only once.", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/V1CreateMigrationBody" } + } + } + }, + "responses": { + "200": { "description": "" }, "403": { "description": "" }, "500": { "description": "Failed to apply database migration" } }, "tags": ["Database"], "security": [{ "bearer": [] }] + }, + "put": { + "operationId": "v1-upsert-a-migration", + "summary": "[Beta] Upsert a database migration without applying", + "description": "Only available to selected partner OAuth apps", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "Idempotency-Key", + "required": false, + "in": "header", + "description": "A unique key to ensure the same migration is tracked only once.", + "schema": { "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/V1UpsertMigrationBody" } + } + } + }, + "responses": { + "200": { "description": "" }, + "403": { "description": "" }, + "500": { "description": "Failed to upsert database migration" } + }, + "tags": ["Database"], + "security": [{ "bearer": [] }] } }, "/v1/projects/{ref}/database/query": { @@ -2227,7 +3140,13 @@ "operationId": "v1-run-a-query", "summary": "[Beta] Run sql query", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -2236,10 +3155,7 @@ } }, "responses": { - "201": { - "description": "", - "content": { "application/json": { "schema": { "type": "object" } } } - }, + "201": { "description": "" }, "403": { "description": "" }, "500": { "description": "Failed to run sql query" } }, @@ -2257,7 +3173,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -2271,19 +3187,25 @@ }, "/v1/projects/{ref}/database/context": { "get": { - "operationId": "getDatabaseMetadata", + "operationId": "v1-get-database-metadata", "summary": "Gets database metadata for the given project.", "description": "This is an **experimental** endpoint. It is subject to change or removal in future versions. Use it with caution, as it may not remain supported or stable.", "deprecated": true, "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { "description": "", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/GetProjectDbMetadataResponseDto" } + "schema": { "$ref": "#/components/schemas/GetProjectDbMetadataResponse" } } } }, @@ -2299,7 +3221,13 @@ "summary": "List all functions", "description": "Returns all functions you've previously added to the specified project.", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "responses": { "200": { @@ -2324,12 +3252,45 @@ "summary": "Create a function", "description": "This endpoint is deprecated - use the deploy endpoint. Creates a function and adds it to the specified project.", "deprecated": true, + "requestBody": { + "required": true, + "content": { + "application/vnd.denoland.eszip": { + "schema": { "type": "string", "format": "binary" } + }, + "application/json": { + "schema": { "$ref": "#/components/schemas/V1CreateFunctionBody" } + } + } + }, "parameters": [ { - "name": "import_map_path", + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "slug", "required": false, "in": "query", - "schema": { "type": "string" } + "schema": { "pattern": "^[A-Za-z0-9_-]+$", "type": "string" } + }, + { "name": "name", "required": false, "in": "query", "schema": { "type": "string" } }, + { + "name": "verify_jwt", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { "type": "boolean" } + }, + { + "name": "import_map", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { "type": "boolean" } }, { "name": "entrypoint_path", @@ -2338,32 +3299,18 @@ "schema": { "type": "string" } }, { - "name": "import_map", + "name": "import_map_path", "required": false, "in": "query", - "schema": { "type": "boolean" } + "schema": { "type": "string" } }, { - "name": "verify_jwt", + "name": "ezbr_sha256", "required": false, "in": "query", - "schema": { "type": "boolean" } - }, - { "name": "name", "required": false, "in": "query", "schema": { "type": "string" } }, - { "name": "slug", "required": false, "in": "query", "schema": { "type": "string" } }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/V1CreateFunctionBody" } - }, - "application/vnd.denoland.eszip": { - "schema": { "$ref": "#/components/schemas/V1CreateFunctionBody" } - } + "schema": { "type": "string" } } - }, + ], "responses": { "201": { "description": "", @@ -2382,16 +3329,19 @@ "summary": "Bulk update functions", "description": "Bulk update functions. It will create a new function or replace existing. The operation is idempotent. NOTE: You will need to manually bump the version.", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, "content": { "application/json": { - "schema": { - "type": "array", - "items": { "$ref": "#/components/schemas/BulkUpdateFunctionBody" } - } + "schema": { "$ref": "#/components/schemas/BulkUpdateFunctionBody" } } } }, @@ -2417,14 +3367,26 @@ "summary": "Deploy a function", "description": "A new endpoint to deploy functions. It will create if function does not exist.", "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { "pattern": "^[A-Za-z0-9_-]+$", "type": "string" } + }, { "name": "bundleOnly", "required": false, "in": "query", + "description": "Boolean string, true or false", "schema": { "type": "boolean" } - }, - { "name": "slug", "required": false, "in": "query", "schema": { "type": "string" } }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + } ], "requestBody": { "required": true, @@ -2456,13 +3418,20 @@ "summary": "Retrieve a function", "description": "Retrieves a function with the specified slug and project.", "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, { "name": "function_slug", "required": true, "in": "path", + "description": "Function slug", "schema": { "pattern": "^[A-Za-z0-9_-]+$", "type": "string" } - }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + } ], "responses": { "200": { @@ -2483,12 +3452,52 @@ "operationId": "v1-update-a-function", "summary": "Update a function", "description": "Updates a function with the specified slug and project.", + "requestBody": { + "required": true, + "content": { + "application/vnd.denoland.eszip": { + "schema": { "type": "string", "format": "binary" } + }, + "application/json": { + "schema": { "$ref": "#/components/schemas/V1UpdateFunctionBody" } + } + } + }, "parameters": [ { - "name": "import_map_path", + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "function_slug", + "required": true, + "in": "path", + "description": "Function slug", + "schema": { "pattern": "^[A-Za-z0-9_-]+$", "type": "string" } + }, + { + "name": "slug", "required": false, "in": "query", - "schema": { "type": "string" } + "schema": { "pattern": "^[A-Za-z0-9_-]+$", "type": "string" } + }, + { "name": "name", "required": false, "in": "query", "schema": { "type": "string" } }, + { + "name": "verify_jwt", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { "type": "boolean" } + }, + { + "name": "import_map", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { "type": "boolean" } }, { "name": "entrypoint_path", @@ -2497,37 +3506,18 @@ "schema": { "type": "string" } }, { - "name": "import_map", + "name": "import_map_path", "required": false, "in": "query", - "schema": { "type": "boolean" } + "schema": { "type": "string" } }, { - "name": "verify_jwt", + "name": "ezbr_sha256", "required": false, "in": "query", - "schema": { "type": "boolean" } - }, - { "name": "name", "required": false, "in": "query", "schema": { "type": "string" } }, - { - "name": "function_slug", - "required": true, - "in": "path", - "schema": { "pattern": "^[A-Za-z0-9_-]+$", "type": "string" } - }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/V1UpdateFunctionBody" } - }, - "application/vnd.denoland.eszip": { - "schema": { "$ref": "#/components/schemas/V1UpdateFunctionBody" } - } + "schema": { "type": "string" } } - }, + ], "responses": { "200": { "description": "", @@ -2546,13 +3536,20 @@ "summary": "Delete a function", "description": "Deletes a function with the specified slug from the specified project.", "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, { "name": "function_slug", "required": true, "in": "path", + "description": "Function slug", "schema": { "pattern": "^[A-Za-z0-9_-]+$", "type": "string" } - }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + } ], "responses": { "200": { "description": "" }, @@ -2569,16 +3566,28 @@ "summary": "Retrieve a function body", "description": "Retrieves a function body for the specified slug and project.", "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, { "name": "function_slug", "required": true, "in": "path", + "description": "Function slug", "schema": { "pattern": "^[A-Za-z0-9_-]+$", "type": "string" } - }, - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + } ], "responses": { - "200": { "description": "" }, + "200": { + "description": "", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/StreamableFile" } } + } + }, "403": { "description": "" }, "500": { "description": "Failed to retrieve function body with given slug" } }, @@ -2596,7 +3605,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -2628,7 +3637,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "requestBody": { @@ -2661,7 +3670,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -2690,9 +3699,14 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } }, - { "name": "provider_id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "provider_id", + "required": true, + "in": "path", + "schema": { "format": "uuid", "type": "string" } + } ], "responses": { "200": { @@ -2720,9 +3734,14 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } }, - { "name": "provider_id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "provider_id", + "required": true, + "in": "path", + "schema": { "format": "uuid", "type": "string" } + } ], "requestBody": { "required": true, @@ -2756,9 +3775,14 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } }, - { "name": "provider_id", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "provider_id", + "required": true, + "in": "path", + "schema": { "format": "uuid", "type": "string" } + } ], "responses": { "200": { @@ -2788,7 +3812,7 @@ "required": true, "in": "path", "description": "Project ref", - "schema": { "minLength": 20, "maxLength": 20, "type": "string" } + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } } ], "responses": { @@ -2810,7 +3834,13 @@ "operationId": "v1-restore-pitr-backup", "summary": "Restores a PITR backup for a database", "parameters": [ - { "name": "ref", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } ], "requestBody": { "required": true, @@ -2823,12 +3853,113 @@ "security": [{ "bearer": [] }] } }, + "/v1/projects/{ref}/database/backups/restore-point": { + "post": { + "operationId": "v1-create-restore-point", + "x-internal": true, + "summary": "Initiates a creation of a restore point for a database", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/V1RestorePointPostBody" } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/V1RestorePointResponse" } + } + } + } + }, + "tags": ["Database"], + "security": [{ "bearer": [] }] + }, + "get": { + "operationId": "v1-get-restore-point", + "x-internal": true, + "summary": "Get restore points for project", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "name", + "required": false, + "in": "query", + "schema": { "maxLength": 20, "type": "string" } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/V1RestorePointResponse" } + } + } + }, + "403": { "description": "" }, + "500": { "description": "Failed to get requested restore points" } + }, + "tags": ["Database"], + "security": [{ "bearer": [] }] + } + }, + "/v1/projects/{ref}/database/backups/undo": { + "post": { + "operationId": "v1-undo", + "x-internal": true, + "summary": "Initiates an undo to a given restore point", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { "minLength": 20, "maxLength": 20, "pattern": "^[a-z]+$", "type": "string" } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/V1UndoBody" } } + } + }, + "responses": { "201": { "description": "" }, "403": { "description": "" } }, + "tags": ["Database"], + "security": [{ "bearer": [] }] + } + }, "/v1/organizations/{slug}/members": { "get": { "operationId": "v1-list-organization-members", "summary": "List members of an organization", "parameters": [ - { "name": "slug", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "slug", + "required": true, + "in": "path", + "description": "Organization slug", + "schema": { "pattern": "^[\\w-]+$", "type": "string" } + } ], "responses": { "200": { @@ -2853,7 +3984,13 @@ "operationId": "v1-get-an-organization", "summary": "Gets information about the organization", "parameters": [ - { "name": "slug", "required": true, "in": "path", "schema": { "type": "string" } } + { + "name": "slug", + "required": true, + "in": "path", + "description": "Organization slug", + "schema": { "pattern": "^[\\w-]+$", "type": "string" } + } ], "responses": { "200": { @@ -2869,6 +4006,54 @@ "tags": ["Organizations"], "security": [{ "bearer": [] }] } + }, + "/v1/organizations/{slug}/project-claim/{token}": { + "get": { + "operationId": "v1-get-organization-project-claim", + "x-internal": true, + "summary": "Gets project details for the specified organization and claim token", + "parameters": [ + { + "name": "slug", + "required": true, + "in": "path", + "description": "Organization slug", + "schema": { "pattern": "^[\\w-]+$", "type": "string" } + }, + { "name": "token", "required": true, "in": "path", "schema": { "type": "string" } } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/OrganizationProjectClaimResponse" } + } + } + }, + "403": { "description": "" } + }, + "tags": ["Organizations"], + "security": [{ "bearer": [] }] + }, + "post": { + "operationId": "v1-claim-project-for-organization", + "x-internal": true, + "summary": "Claims project for the specified organization", + "parameters": [ + { + "name": "slug", + "required": true, + "in": "path", + "description": "Organization slug", + "schema": { "pattern": "^[\\w-]+$", "type": "string" } + }, + { "name": "token", "required": true, "in": "path", "schema": { "type": "string" } } + ], + "responses": { "204": { "description": "" }, "403": { "description": "" } }, + "tags": ["Organizations"], + "security": [{ "bearer": [] }] + } } }, "info": { @@ -2878,7 +4063,10 @@ "contact": {} }, "tags": [ + { "name": "Advisors", "description": "Advisors related endpoints" }, + { "name": "Analytics", "description": "Analytics related endpoints" }, { "name": "Auth", "description": "Auth related endpoints" }, + { "name": "Billing", "description": "Billing related endpoints" }, { "name": "Database", "description": "Database related endpoints" }, { "name": "Domains", "description": "Domains related endpoints" }, { "name": "Edge Functions", "description": "Edge related endpoints" }, @@ -2922,7 +4110,7 @@ ] }, "db_host": { "type": "string" }, - "db_port": { "type": "number" }, + "db_port": { "type": "integer", "minimum": 0, "exclusiveMinimum": true }, "db_user": { "type": "string" }, "db_pass": { "type": "string" }, "jwt_secret": { "type": "string" } @@ -2958,13 +4146,14 @@ "FUNCTIONS_DEPLOYED", "FUNCTIONS_FAILED" ] - } + }, + "request_review": { "type": "boolean" } } }, "BranchResponse": { "type": "object", "properties": { - "id": { "type": "string" }, + "id": { "type": "string", "format": "uuid" }, "name": { "type": "string" }, "project_ref": { "type": "string" }, "parent_project_ref": { "type": "string" }, @@ -2988,8 +4177,9 @@ "FUNCTIONS_FAILED" ] }, - "created_at": { "type": "string" }, - "updated_at": { "type": "string" } + "created_at": { "type": "string", "format": "date-time" }, + "updated_at": { "type": "string", "format": "date-time" }, + "review_requested_at": { "type": "string", "format": "date-time" } }, "required": [ "id", @@ -3008,6 +4198,10 @@ "properties": { "message": { "type": "string", "enum": ["ok"] } }, "required": ["message"] }, + "BranchActionBody": { + "type": "object", + "properties": { "migration_version": { "type": "string" } } + }, "BranchUpdateResponse": { "type": "object", "properties": { @@ -3016,16 +4210,6 @@ }, "required": ["workflow_run_id", "message"] }, - "V1DatabaseResponse": { - "type": "object", - "properties": { - "host": { "type": "string", "description": "Database host" }, - "version": { "type": "string", "description": "Database version" }, - "postgres_engine": { "type": "string", "description": "Database engine" }, - "release_channel": { "type": "string", "description": "Release channel" } - }, - "required": ["host", "version", "postgres_engine", "release_channel"] - }, "V1ProjectWithDatabaseResponse": { "type": "object", "properties": { @@ -3062,15 +4246,24 @@ "RESIZING" ] }, - "database": { "$ref": "#/components/schemas/V1DatabaseResponse" } + "database": { + "type": "object", + "properties": { + "host": { "type": "string", "description": "Database host" }, + "version": { "type": "string", "description": "Database version" }, + "postgres_engine": { "type": "string", "description": "Database engine" }, + "release_channel": { "type": "string", "description": "Release channel" } + }, + "required": ["host", "version", "postgres_engine", "release_channel"] + } }, "required": ["id", "organization_id", "name", "region", "created_at", "status", "database"] }, - "V1CreateProjectBodyDto": { + "V1CreateProjectBody": { "type": "object", "properties": { "db_pass": { "type": "string", "description": "Database password" }, - "name": { "type": "string", "description": "Name of your project" }, + "name": { "type": "string", "maxLength": 256, "description": "Name of your project" }, "organization_id": { "type": "string", "description": "Slug of your organization" }, "plan": { "type": "string", @@ -3110,6 +4303,8 @@ "desired_instance_size": { "type": "string", "enum": [ + "pico", + "nano", "micro", "small", "medium", @@ -3119,7 +4314,15 @@ "4xlarge", "8xlarge", "12xlarge", - "16xlarge" + "16xlarge", + "24xlarge", + "24xlarge_optimized_memory", + "24xlarge_optimized_cpu", + "24xlarge_high_memory", + "48xlarge", + "48xlarge_optimized_memory", + "48xlarge_optimized_cpu", + "48xlarge_high_memory" ] }, "template_url": { @@ -3177,7 +4380,7 @@ "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": ["id", "name"] }, - "CreateOrganizationV1Dto": { + "CreateOrganizationV1": { "type": "object", "properties": { "name": { "type": "string" } }, "required": ["name"], @@ -3207,7 +4410,7 @@ "required": ["access_token", "refresh_token", "expires_in", "token_type"], "additionalProperties": false }, - "OAuthRevokeTokenBodyDto": { + "OAuthRevokeTokenBody": { "type": "object", "properties": { "client_id": { "type": "string", "format": "uuid" }, @@ -3217,23 +4420,56 @@ "required": ["client_id", "client_secret", "refresh_token"], "additionalProperties": false }, - "SnippetProject": { + "SnippetList": { "type": "object", "properties": { - "id": { "type": "integer", "format": "int64" }, - "name": { "type": "string" } + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "inserted_at": { "type": "string" }, + "updated_at": { "type": "string" }, + "type": { "type": "string", "enum": ["sql"] }, + "visibility": { "type": "string", "enum": ["user", "project", "org", "public"] }, + "name": { "type": "string" }, + "description": { "type": "string", "nullable": true }, + "project": { + "type": "object", + "properties": { "id": { "type": "number" }, "name": { "type": "string" } }, + "required": ["id", "name"] + }, + "owner": { + "type": "object", + "properties": { "id": { "type": "number" }, "username": { "type": "string" } }, + "required": ["id", "username"] + }, + "updated_by": { + "type": "object", + "properties": { "id": { "type": "number" }, "username": { "type": "string" } }, + "required": ["id", "username"] + } + }, + "required": [ + "id", + "inserted_at", + "updated_at", + "type", + "visibility", + "name", + "description", + "project", + "owner", + "updated_by" + ] + } + }, + "cursor": { "type": "string" } }, - "required": ["id", "name"] + "required": ["data"] }, - "SnippetUser": { - "type": "object", - "properties": { - "id": { "type": "integer", "format": "int64" }, - "username": { "type": "string" } - }, - "required": ["id", "username"] - }, - "SnippetMeta": { + "SnippetResponse": { "type": "object", "properties": { "id": { "type": "string" }, @@ -3243,54 +4479,30 @@ "visibility": { "type": "string", "enum": ["user", "project", "org", "public"] }, "name": { "type": "string" }, "description": { "type": "string", "nullable": true }, - "project": { "$ref": "#/components/schemas/SnippetProject" }, - "owner": { "$ref": "#/components/schemas/SnippetUser" }, - "updated_by": { "$ref": "#/components/schemas/SnippetUser" } - }, - "required": [ - "id", - "inserted_at", - "updated_at", - "type", - "visibility", - "name", - "description", - "project", - "owner", - "updated_by" - ] - }, - "SnippetList": { - "type": "object", - "properties": { - "data": { "type": "array", "items": { "$ref": "#/components/schemas/SnippetMeta" } }, - "cursor": { "type": "string" } - }, - "required": ["data"] - }, - "SnippetContent": { - "type": "object", - "properties": { - "favorite": { "type": "boolean" }, - "schema_version": { "type": "string" }, - "sql": { "type": "string" } - }, - "required": ["favorite", "schema_version", "sql"] - }, - "SnippetResponse": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "inserted_at": { "type": "string" }, - "updated_at": { "type": "string" }, - "type": { "type": "string", "enum": ["sql"] }, - "visibility": { "enum": ["user", "project", "org", "public"], "type": "string" }, - "name": { "type": "string" }, - "description": { "type": "string", "nullable": true }, - "project": { "$ref": "#/components/schemas/SnippetProject" }, - "owner": { "$ref": "#/components/schemas/SnippetUser" }, - "updated_by": { "$ref": "#/components/schemas/SnippetUser" }, - "content": { "$ref": "#/components/schemas/SnippetContent" } + "project": { + "type": "object", + "properties": { "id": { "type": "number" }, "name": { "type": "string" } }, + "required": ["id", "name"] + }, + "owner": { + "type": "object", + "properties": { "id": { "type": "number" }, "username": { "type": "string" } }, + "required": ["id", "username"] + }, + "updated_by": { + "type": "object", + "properties": { "id": { "type": "number" }, "username": { "type": "string" } }, + "required": ["id", "username"] + }, + "content": { + "type": "object", + "properties": { + "favorite": { "type": "boolean" }, + "schema_version": { "type": "string" }, + "sql": { "type": "string" } + }, + "required": ["favorite", "schema_version", "sql"] + } }, "required": [ "id", @@ -3309,15 +4521,15 @@ "ApiKeyResponse": { "type": "object", "properties": { - "name": { "type": "string" }, - "api_key": { "type": "string" }, + "api_key": { "type": "string", "nullable": true }, "id": { "type": "string", "nullable": true }, "type": { "type": "string", - "enum": ["publishable", "secret", "legacy"], + "enum": ["legacy", "publishable", "secret"], "nullable": true }, "prefix": { "type": "string", "nullable": true }, + "name": { "type": "string" }, "description": { "type": "string", "nullable": true }, "hash": { "type": "string", "nullable": true }, "secret_jwt_template": { @@ -3329,12 +4541,23 @@ "inserted_at": { "type": "string", "format": "date-time", "nullable": true }, "updated_at": { "type": "string", "format": "date-time", "nullable": true } }, - "required": ["name", "api_key"] + "required": ["name"] + }, + "LegacyApiKeysResponse": { + "type": "object", + "properties": { "enabled": { "type": "boolean" } }, + "required": ["enabled"] }, "CreateApiKeyBody": { "type": "object", "properties": { "type": { "type": "string", "enum": ["publishable", "secret"] }, + "name": { + "type": "string", + "minLength": 4, + "maxLength": 64, + "pattern": "^[a-z_][a-z0-9_]+$" + }, "description": { "type": "string", "nullable": true }, "secret_jwt_template": { "type": "object", @@ -3343,11 +4566,17 @@ "nullable": true } }, - "required": ["type"] + "required": ["type", "name"] }, "UpdateApiKeyBody": { "type": "object", "properties": { + "name": { + "type": "string", + "minLength": 4, + "maxLength": 64, + "pattern": "^[a-z_][a-z0-9_]+$" + }, "description": { "type": "string", "nullable": true }, "secret_jwt_template": { "type": "object", @@ -3362,6 +4591,7 @@ "properties": { "branch_name": { "type": "string", "minLength": 1 }, "git_branch": { "type": "string" }, + "is_default": { "type": "boolean" }, "persistent": { "type": "boolean" }, "region": { "type": "string" }, "desired_instance_size": { @@ -3378,7 +4608,15 @@ "4xlarge", "8xlarge", "12xlarge", - "16xlarge" + "16xlarge", + "24xlarge", + "24xlarge_optimized_memory", + "24xlarge_optimized_cpu", + "24xlarge_high_memory", + "48xlarge", + "48xlarge_optimized_memory", + "48xlarge_optimized_cpu", + "48xlarge_high_memory" ] }, "release_channel": { @@ -3388,92 +4626,96 @@ }, "postgres_engine": { "type": "string", - "enum": ["15", "17-oriole"], + "enum": ["15", "17", "17-oriole"], "description": "Postgres engine version. If not provided, the latest version will be used." }, - "secrets": { "type": "object", "additionalProperties": { "type": "string" } } + "secrets": { "type": "object", "additionalProperties": { "type": "string" } }, + "with_data": { "type": "boolean" } }, "required": ["branch_name"] }, - "ValidationRecord": { - "type": "object", - "properties": { "txt_name": { "type": "string" }, "txt_value": { "type": "string" } }, - "required": ["txt_name", "txt_value"] - }, - "ValidationError": { - "type": "object", - "properties": { "message": { "type": "string" } }, - "required": ["message"] - }, - "SslValidation": { - "type": "object", - "properties": { - "status": { "type": "string" }, - "validation_records": { - "type": "array", - "items": { "$ref": "#/components/schemas/ValidationRecord" } - }, - "validation_errors": { - "type": "array", - "items": { "$ref": "#/components/schemas/ValidationError" } - } - }, - "required": ["status", "validation_records"] - }, - "OwnershipVerification": { - "type": "object", - "properties": { - "type": { "type": "string" }, - "name": { "type": "string" }, - "value": { "type": "string" } - }, - "required": ["type", "name", "value"] - }, - "CustomHostnameDetails": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "hostname": { "type": "string" }, - "ssl": { "$ref": "#/components/schemas/SslValidation" }, - "ownership_verification": { "$ref": "#/components/schemas/OwnershipVerification" }, - "custom_origin_server": { "type": "string" }, - "verification_errors": { "type": "array", "items": { "type": "string" } }, - "status": { "type": "string" } - }, - "required": [ - "id", - "hostname", - "ssl", - "ownership_verification", - "custom_origin_server", - "status" - ] - }, - "CfResponse": { - "type": "object", - "properties": { - "success": { "type": "boolean" }, - "errors": { "type": "array", "items": { "type": "object" } }, - "messages": { "type": "array", "items": { "type": "object" } }, - "result": { "$ref": "#/components/schemas/CustomHostnameDetails" } - }, - "required": ["success", "errors", "messages", "result"] - }, "UpdateCustomHostnameResponse": { "type": "object", "properties": { "status": { + "type": "string", "enum": [ "1_not_started", "2_initiated", "3_challenge_verified", "4_origin_setup_completed", "5_services_reconfigured" - ], - "type": "string" + ] }, "custom_hostname": { "type": "string" }, - "data": { "$ref": "#/components/schemas/CfResponse" } + "data": { + "type": "object", + "properties": { + "success": { "type": "boolean" }, + "errors": { + "type": "array", + "items": { "description": "Any JSON-serializable value" } + }, + "messages": { + "type": "array", + "items": { "description": "Any JSON-serializable value" } + }, + "result": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "hostname": { "type": "string" }, + "ssl": { + "type": "object", + "properties": { + "status": { "type": "string" }, + "validation_records": { + "type": "array", + "items": { + "type": "object", + "properties": { + "txt_name": { "type": "string" }, + "txt_value": { "type": "string" } + }, + "required": ["txt_name", "txt_value"] + } + }, + "validation_errors": { + "type": "array", + "items": { + "type": "object", + "properties": { "message": { "type": "string" } }, + "required": ["message"] + } + } + }, + "required": ["status", "validation_records"] + }, + "ownership_verification": { + "type": "object", + "properties": { + "type": { "type": "string" }, + "name": { "type": "string" }, + "value": { "type": "string" } + }, + "required": ["type", "name", "value"] + }, + "custom_origin_server": { "type": "string" }, + "verification_errors": { "type": "array", "items": { "type": "string" } }, + "status": { "type": "string" } + }, + "required": [ + "id", + "hostname", + "ssl", + "ownership_verification", + "custom_origin_server", + "status" + ] + } + }, + "required": ["success", "errors", "messages", "result"] + } }, "required": ["status", "custom_hostname", "data"] }, @@ -3489,9 +4731,30 @@ }, "required": ["banned_ipv4_addresses"] }, + "NetworkBanResponseEnriched": { + "type": "object", + "properties": { + "banned_ipv4_addresses": { + "type": "array", + "items": { + "type": "object", + "properties": { + "banned_address": { "type": "string" }, + "identifier": { "type": "string" }, + "type": { "type": "string" } + }, + "required": ["banned_address", "identifier", "type"] + } + } + }, + "required": ["banned_ipv4_addresses"] + }, "RemoveNetworkBanRequest": { "type": "object", - "properties": { "ipv4_addresses": { "type": "array", "items": { "type": "string" } } }, + "properties": { + "ipv4_addresses": { "type": "array", "items": { "type": "string" } }, + "identifier": { "type": "string" } + }, "required": ["ipv4_addresses"] }, "NetworkRestrictionsResponse": { @@ -3576,7 +4839,7 @@ "V1ProjectRefResponse": { "type": "object", "properties": { - "id": { "type": "integer", "format": "int64" }, + "id": { "type": "integer" }, "ref": { "type": "string" }, "name": { "type": "string" } }, @@ -3592,18 +4855,21 @@ "required": ["name", "value"] }, "CreateSecretBody": { - "type": "object", - "properties": { - "name": { - "type": "string", - "maxLength": 256, - "pattern": "^(?!SUPABASE_).*", - "description": "Secret name must not start with the SUPABASE_ prefix.", - "example": "string" + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 256, + "pattern": "^(?!SUPABASE_).*", + "description": "Secret name must not start with the SUPABASE_ prefix.", + "example": "string" + }, + "value": { "type": "string", "maxLength": 24576 } }, - "value": { "type": "string", "maxLength": 24576 } - }, - "required": ["name", "value"] + "required": ["name", "value"] + } }, "SslEnforcementResponse": { "type": "object", @@ -3687,7 +4953,7 @@ "items": { "type": "object", "properties": { - "postgres_version": { "type": "string", "enum": ["15", "17-oriole"] }, + "postgres_version": { "type": "string", "enum": ["15", "17", "17-oriole"] }, "release_channel": { "type": "string", "enum": ["internal", "alpha", "beta", "ga", "withdrawn", "preview"] @@ -3697,10 +4963,14 @@ "required": ["postgres_version", "release_channel", "app_version"] } }, - "potential_breaking_changes": { "type": "array", "items": { "type": "string" } }, "duration_estimate_hours": { "type": "number" }, "legacy_auth_custom_roles": { "type": "array", "items": { "type": "string" } }, - "extension_dependent_objects": { "type": "array", "items": { "type": "string" } } + "objects_to_be_dropped": { "type": "array", "items": { "type": "string" } }, + "unsupported_extensions": { "type": "array", "items": { "type": "string" } }, + "user_defined_objects_in_internal_schemas": { + "type": "array", + "items": { "type": "string" } + } }, "required": [ "eligible", @@ -3708,10 +4978,11 @@ "current_app_version_release_channel", "latest_app_version", "target_upgrade_versions", - "potential_breaking_changes", "duration_estimate_hours", "legacy_auth_custom_roles", - "extension_dependent_objects" + "objects_to_be_dropped", + "unsupported_extensions", + "user_defined_objects_in_internal_schemas" ] }, "DatabaseUpgradeStatusResponse": { @@ -3806,40 +5077,115 @@ "properties": { "database_identifier": { "type": "string" } }, "required": ["database_identifier"] }, - "AuthHealthResponse": { - "type": "object", - "properties": { "name": { "type": "string", "enum": ["GoTrue"] } }, - "required": ["name"] - }, - "RealtimeHealthResponse": { - "type": "object", - "properties": { "connected_cluster": { "type": "integer" } }, - "required": ["connected_cluster"] - }, "V1ServiceHealthResponse": { "type": "object", "properties": { - "info": { - "oneOf": [ - { "$ref": "#/components/schemas/AuthHealthResponse" }, - { "$ref": "#/components/schemas/RealtimeHealthResponse" } - ] - }, "name": { - "enum": ["auth", "db", "pooler", "realtime", "rest", "storage"], - "type": "string" + "type": "string", + "enum": ["auth", "db", "pooler", "realtime", "rest", "storage"] }, "healthy": { "type": "boolean" }, - "status": { "enum": ["COMING_UP", "ACTIVE_HEALTHY", "UNHEALTHY"], "type": "string" }, + "status": { "type": "string", "enum": ["COMING_UP", "ACTIVE_HEALTHY", "UNHEALTHY"] }, + "info": { + "oneOf": [ + { + "type": "object", + "properties": { + "name": { "type": "string", "enum": ["GoTrue"] }, + "version": { "type": "string" }, + "description": { "type": "string" } + }, + "required": ["name", "version", "description"] + }, + { + "type": "object", + "properties": { + "healthy": { "type": "boolean" }, + "db_connected": { "type": "boolean" }, + "connected_cluster": { "type": "integer" } + }, + "required": ["healthy", "db_connected", "connected_cluster"] + } + ] + }, "error": { "type": "string" } }, "required": ["name", "healthy", "status"] }, + "SigningKeyResponse": { + "type": "object", + "properties": { + "id": { "type": "string", "format": "uuid" }, + "algorithm": { "type": "string", "enum": ["EdDSA", "ES256", "RS256", "HS256"] }, + "status": { + "type": "string", + "enum": ["in_use", "previously_used", "revoked", "standby"] + }, + "public_jwk": { "nullable": true }, + "created_at": { "type": "string", "format": "date-time" }, + "updated_at": { "type": "string", "format": "date-time" } + }, + "required": ["id", "algorithm", "status", "created_at", "updated_at"], + "additionalProperties": false + }, "CreateSigningKeyBody": { "type": "object", "properties": { "algorithm": { "type": "string", "enum": ["EdDSA", "ES256", "RS256", "HS256"] }, - "status": { "type": "string", "enum": ["in_use", "standby"] } + "status": { "type": "string", "enum": ["in_use", "standby"] }, + "private_jwk": { + "discriminator": { "propertyName": "kty" }, + "oneOf": [ + { + "type": "object", + "properties": { + "kty": { "type": "string", "enum": ["RSA"] }, + "n": { "type": "string" }, + "e": { "type": "string", "enum": ["AQAB"] }, + "d": { "type": "string" }, + "p": { "type": "string" }, + "q": { "type": "string" }, + "dp": { "type": "string" }, + "dq": { "type": "string" }, + "qi": { "type": "string" } + }, + "required": ["kty", "n", "e", "d", "p", "q", "dp", "dq", "qi"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "kty": { "type": "string", "enum": ["EC"] }, + "crv": { "type": "string", "enum": ["P-256"] }, + "x": { "type": "string" }, + "y": { "type": "string" }, + "d": { "type": "string" } + }, + "required": ["kty", "crv", "x", "y", "d"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "kty": { "type": "string", "enum": ["OKP"] }, + "crv": { "type": "string", "enum": ["Ed25519"] }, + "x": { "type": "string" }, + "d": { "type": "string" } + }, + "required": ["kty", "crv", "x", "d"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "kty": { "type": "string", "enum": ["oct"] }, + "k": { "type": "string", "minLength": 16 } + }, + "required": ["kty", "k"], + "additionalProperties": false + } + ] + } }, "required": ["algorithm"], "additionalProperties": false @@ -3870,22 +5216,6 @@ "required": ["keys"], "additionalProperties": false }, - "SigningKeyResponse": { - "type": "object", - "properties": { - "id": { "type": "string", "format": "uuid" }, - "algorithm": { "type": "string", "enum": ["EdDSA", "ES256", "RS256", "HS256"] }, - "status": { - "type": "string", - "enum": ["in_use", "previously_used", "revoked", "standby"] - }, - "public_jwk": { "nullable": true }, - "created_at": { "type": "string", "format": "date-time" }, - "updated_at": { "type": "string", "format": "date-time" } - }, - "required": ["id", "algorithm", "status", "created_at", "updated_at"], - "additionalProperties": false - }, "UpdateSigningKeyBody": { "type": "object", "properties": { @@ -3897,31 +5227,31 @@ "required": ["status"], "additionalProperties": false }, - "StorageFeatureImageTransformation": { - "type": "object", - "properties": { "enabled": { "type": "boolean" } }, - "required": ["enabled"] - }, - "StorageFeatureS3Protocol": { - "type": "object", - "properties": { "enabled": { "type": "boolean" } }, - "required": ["enabled"] - }, - "StorageFeatures": { - "type": "object", - "properties": { - "imageTransformation": { - "$ref": "#/components/schemas/StorageFeatureImageTransformation" - }, - "s3Protocol": { "$ref": "#/components/schemas/StorageFeatureS3Protocol" } - }, - "required": ["imageTransformation", "s3Protocol"] - }, "StorageConfigResponse": { "type": "object", "properties": { "fileSizeLimit": { "type": "integer", "format": "int64" }, - "features": { "$ref": "#/components/schemas/StorageFeatures" } + "features": { + "type": "object", + "properties": { + "imageTransformation": { + "type": "object", + "properties": { "enabled": { "type": "boolean" } }, + "required": ["enabled"] + }, + "s3Protocol": { + "type": "object", + "properties": { "enabled": { "type": "boolean" } }, + "required": ["enabled"] + }, + "icebergCatalog": { + "type": "object", + "properties": { "enabled": { "type": "boolean" } }, + "required": ["enabled"] + } + }, + "required": ["imageTransformation", "s3Protocol"] + } }, "required": ["fileSizeLimit", "features"] }, @@ -3931,10 +5261,30 @@ "fileSizeLimit": { "type": "integer", "minimum": 0, - "maximum": 53687091200, + "maximum": 536870912000, "format": "int64" }, - "features": { "$ref": "#/components/schemas/StorageFeatures" } + "features": { + "type": "object", + "properties": { + "imageTransformation": { + "type": "object", + "properties": { "enabled": { "type": "boolean" } }, + "required": ["enabled"] + }, + "s3Protocol": { + "type": "object", + "properties": { "enabled": { "type": "boolean" } }, + "required": ["enabled"] + }, + "icebergCatalog": { + "type": "object", + "properties": { "enabled": { "type": "boolean" } }, + "required": ["enabled"] + } + }, + "required": ["imageTransformation", "s3Protocol"] + } } }, "PostgresConfigResponse": { @@ -3956,13 +5306,13 @@ "max_wal_size": { "type": "string" }, "max_wal_senders": { "type": "integer" }, "max_worker_processes": { "type": "integer", "minimum": 0, "maximum": 262143 }, + "session_replication_role": { "type": "string", "enum": ["origin", "replica", "local"] }, "shared_buffers": { "type": "string" }, "statement_timeout": { "type": "string" }, "track_commit_timestamp": { "type": "boolean" }, "wal_keep_size": { "type": "string" }, "wal_sender_timeout": { "type": "string" }, - "work_mem": { "type": "string" }, - "session_replication_role": { "enum": ["origin", "replica", "local"], "type": "string" } + "work_mem": { "type": "string" } } }, "UpdatePostgresConfigBody": { @@ -3984,59 +5334,59 @@ "max_wal_size": { "type": "string" }, "max_wal_senders": { "type": "integer" }, "max_worker_processes": { "type": "integer", "minimum": 0, "maximum": 262143 }, + "session_replication_role": { "type": "string", "enum": ["origin", "replica", "local"] }, "shared_buffers": { "type": "string" }, "statement_timeout": { "type": "string" }, "track_commit_timestamp": { "type": "boolean" }, "wal_keep_size": { "type": "string" }, "wal_sender_timeout": { "type": "string" }, "work_mem": { "type": "string" }, - "restart_database": { "type": "boolean" }, - "session_replication_role": { "enum": ["origin", "replica", "local"], "type": "string" } + "restart_database": { "type": "boolean" } } }, "V1PgbouncerConfigResponse": { "type": "object", "properties": { - "pool_mode": { "type": "string", "enum": ["transaction", "session", "statement"] }, - "default_pool_size": { "type": "number" }, + "default_pool_size": { "type": "integer" }, "ignore_startup_parameters": { "type": "string" }, - "max_client_conn": { "type": "number" }, - "connection_string": { "type": "string" } + "max_client_conn": { "type": "integer" }, + "pool_mode": { "type": "string", "enum": ["transaction", "session", "statement"] }, + "connection_string": { "type": "string" }, + "server_idle_timeout": { "type": "integer" }, + "server_lifetime": { "type": "integer" }, + "query_wait_timeout": { "type": "integer" }, + "reserve_pool_size": { "type": "integer" } } }, "SupavisorConfigResponse": { "type": "object", "properties": { - "database_type": { "type": "string", "enum": ["PRIMARY", "READ_REPLICA"] }, - "db_port": { "type": "integer" }, - "connectionString": { - "type": "string", - "deprecated": true, - "description": "Use connection_string instead" - }, - "default_pool_size": { "type": "integer", "nullable": true }, - "max_client_conn": { "type": "integer", "nullable": true }, - "pool_mode": { "enum": ["transaction", "session"], "type": "string" }, "identifier": { "type": "string" }, + "database_type": { "type": "string", "enum": ["PRIMARY", "READ_REPLICA"] }, "is_using_scram_auth": { "type": "boolean" }, "db_user": { "type": "string" }, "db_host": { "type": "string" }, + "db_port": { "type": "integer" }, "db_name": { "type": "string" }, - "connection_string": { "type": "string" } + "connection_string": { "type": "string" }, + "connectionString": { "type": "string", "description": "Use connection_string instead" }, + "default_pool_size": { "type": "integer", "nullable": true }, + "max_client_conn": { "type": "integer", "nullable": true }, + "pool_mode": { "type": "string", "enum": ["transaction", "session"] } }, "required": [ - "database_type", - "db_port", - "connectionString", - "default_pool_size", - "max_client_conn", - "pool_mode", "identifier", + "database_type", "is_using_scram_auth", "db_user", "db_host", + "db_port", "db_name", - "connection_string" + "connection_string", + "connectionString", + "default_pool_size", + "max_client_conn", + "pool_mode" ] }, "UpdateSupavisorConfigBody": { @@ -4044,15 +5394,14 @@ "properties": { "default_pool_size": { "type": "integer", - "nullable": true, "minimum": 0, - "maximum": 1000 + "maximum": 3000, + "nullable": true }, "pool_mode": { - "enum": ["transaction", "session"], "type": "string", - "description": "Dedicated pooler mode for the project", - "deprecated": true + "enum": ["transaction", "session"], + "description": "Dedicated pooler mode for the project" } } }, @@ -4069,26 +5418,6 @@ "properties": { "api_max_request_duration": { "type": "integer", "nullable": true }, "db_max_pool_size": { "type": "integer", "nullable": true }, - "jwt_exp": { "type": "integer", "nullable": true }, - "mailer_otp_exp": { "type": "integer" }, - "mailer_otp_length": { "type": "integer", "nullable": true }, - "mfa_max_enrolled_factors": { "type": "integer", "nullable": true }, - "mfa_phone_otp_length": { "type": "integer" }, - "mfa_phone_max_frequency": { "type": "integer", "nullable": true }, - "password_min_length": { "type": "integer", "nullable": true }, - "rate_limit_anonymous_users": { "type": "integer", "nullable": true }, - "rate_limit_email_sent": { "type": "integer", "nullable": true }, - "rate_limit_sms_sent": { "type": "integer", "nullable": true }, - "rate_limit_token_refresh": { "type": "integer", "nullable": true }, - "rate_limit_verify": { "type": "integer", "nullable": true }, - "rate_limit_otp": { "type": "integer", "nullable": true }, - "security_refresh_token_reuse_interval": { "type": "integer", "nullable": true }, - "sessions_inactivity_timeout": { "type": "integer", "nullable": true }, - "sessions_timebox": { "type": "integer", "nullable": true }, - "sms_max_frequency": { "type": "integer", "nullable": true }, - "sms_otp_exp": { "type": "integer", "nullable": true }, - "sms_otp_length": { "type": "integer" }, - "smtp_max_frequency": { "type": "integer", "nullable": true }, "disable_signup": { "type": "boolean", "nullable": true }, "external_anonymous_users_enabled": { "type": "boolean", "nullable": true }, "external_apple_additional_client_ids": { "type": "string", "nullable": true }, @@ -4157,6 +5486,7 @@ "external_workos_enabled": { "type": "boolean", "nullable": true }, "external_workos_secret": { "type": "string", "nullable": true }, "external_workos_url": { "type": "string", "nullable": true }, + "external_web3_solana_enabled": { "type": "boolean", "nullable": true }, "external_zoom_client_id": { "type": "string", "nullable": true }, "external_zoom_enabled": { "type": "boolean", "nullable": true }, "external_zoom_secret": { "type": "string", "nullable": true }, @@ -4175,8 +5505,14 @@ "hook_send_email_enabled": { "type": "boolean", "nullable": true }, "hook_send_email_uri": { "type": "string", "nullable": true }, "hook_send_email_secrets": { "type": "string", "nullable": true }, + "hook_before_user_created_enabled": { "type": "boolean", "nullable": true }, + "hook_before_user_created_uri": { "type": "string", "nullable": true }, + "hook_before_user_created_secrets": { "type": "string", "nullable": true }, + "jwt_exp": { "type": "integer", "nullable": true }, "mailer_allow_unverified_email_sign_ins": { "type": "boolean", "nullable": true }, "mailer_autoconfirm": { "type": "boolean", "nullable": true }, + "mailer_otp_exp": { "type": "integer" }, + "mailer_otp_length": { "type": "integer", "nullable": true }, "mailer_secure_email_change_enabled": { "type": "boolean", "nullable": true }, "mailer_subjects_confirmation": { "type": "string", "nullable": true }, "mailer_subjects_email_change": { "type": "string", "nullable": true }, @@ -4190,37 +5526,71 @@ "mailer_templates_magic_link_content": { "type": "string", "nullable": true }, "mailer_templates_reauthentication_content": { "type": "string", "nullable": true }, "mailer_templates_recovery_content": { "type": "string", "nullable": true }, + "mfa_max_enrolled_factors": { "type": "integer", "nullable": true }, "mfa_totp_enroll_enabled": { "type": "boolean", "nullable": true }, "mfa_totp_verify_enabled": { "type": "boolean", "nullable": true }, "mfa_phone_enroll_enabled": { "type": "boolean", "nullable": true }, "mfa_phone_verify_enabled": { "type": "boolean", "nullable": true }, "mfa_web_authn_enroll_enabled": { "type": "boolean", "nullable": true }, "mfa_web_authn_verify_enabled": { "type": "boolean", "nullable": true }, + "mfa_phone_otp_length": { "type": "integer" }, "mfa_phone_template": { "type": "string", "nullable": true }, + "mfa_phone_max_frequency": { "type": "integer", "nullable": true }, "password_hibp_enabled": { "type": "boolean", "nullable": true }, - "password_required_characters": { "type": "string", "nullable": true }, + "password_min_length": { "type": "integer", "nullable": true }, + "password_required_characters": { + "type": "string", + "enum": [ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", + "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", + "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\\\:\"|<>?,./`~", + "" + ], + "nullable": true + }, + "rate_limit_anonymous_users": { "type": "integer", "nullable": true }, + "rate_limit_email_sent": { "type": "integer", "nullable": true }, + "rate_limit_sms_sent": { "type": "integer", "nullable": true }, + "rate_limit_token_refresh": { "type": "integer", "nullable": true }, + "rate_limit_verify": { "type": "integer", "nullable": true }, + "rate_limit_otp": { "type": "integer", "nullable": true }, + "rate_limit_web3": { "type": "integer", "nullable": true }, "refresh_token_rotation_enabled": { "type": "boolean", "nullable": true }, "saml_enabled": { "type": "boolean", "nullable": true }, "saml_external_url": { "type": "string", "nullable": true }, "saml_allow_encrypted_assertions": { "type": "boolean", "nullable": true }, "security_captcha_enabled": { "type": "boolean", "nullable": true }, - "security_captcha_provider": { "type": "string", "nullable": true }, + "security_captcha_provider": { + "type": "string", + "enum": ["turnstile", "hcaptcha"], + "nullable": true + }, "security_captcha_secret": { "type": "string", "nullable": true }, "security_manual_linking_enabled": { "type": "boolean", "nullable": true }, + "security_refresh_token_reuse_interval": { "type": "integer", "nullable": true }, "security_update_password_require_reauthentication": { "type": "boolean", "nullable": true }, + "sessions_inactivity_timeout": { "type": "integer", "nullable": true }, "sessions_single_per_user": { "type": "boolean", "nullable": true }, "sessions_tags": { "type": "string", "nullable": true }, + "sessions_timebox": { "type": "integer", "nullable": true }, "site_url": { "type": "string", "nullable": true }, "sms_autoconfirm": { "type": "boolean", "nullable": true }, + "sms_max_frequency": { "type": "integer", "nullable": true }, "sms_messagebird_access_key": { "type": "string", "nullable": true }, "sms_messagebird_originator": { "type": "string", "nullable": true }, - "sms_provider": { "type": "string", "nullable": true }, + "sms_otp_exp": { "type": "integer", "nullable": true }, + "sms_otp_length": { "type": "integer" }, + "sms_provider": { + "type": "string", + "enum": ["messagebird", "textlocal", "twilio", "twilio_verify", "vonage"], + "nullable": true + }, "sms_template": { "type": "string", "nullable": true }, "sms_test_otp": { "type": "string", "nullable": true }, - "sms_test_otp_valid_until": { "type": "string", "nullable": true }, + "sms_test_otp_valid_until": { "type": "string", "format": "date-time", "nullable": true }, "sms_textlocal_api_key": { "type": "string", "nullable": true }, "sms_textlocal_sender": { "type": "string", "nullable": true }, "sms_twilio_account_sid": { "type": "string", "nullable": true }, @@ -4233,8 +5603,9 @@ "sms_vonage_api_key": { "type": "string", "nullable": true }, "sms_vonage_api_secret": { "type": "string", "nullable": true }, "sms_vonage_from": { "type": "string", "nullable": true }, - "smtp_admin_email": { "type": "string", "nullable": true }, + "smtp_admin_email": { "type": "string", "format": "email", "nullable": true }, "smtp_host": { "type": "string", "nullable": true }, + "smtp_max_frequency": { "type": "integer", "nullable": true }, "smtp_pass": { "type": "string", "nullable": true }, "smtp_port": { "type": "string", "nullable": true }, "smtp_sender_name": { "type": "string", "nullable": true }, @@ -4244,26 +5615,6 @@ "required": [ "api_max_request_duration", "db_max_pool_size", - "jwt_exp", - "mailer_otp_exp", - "mailer_otp_length", - "mfa_max_enrolled_factors", - "mfa_phone_otp_length", - "mfa_phone_max_frequency", - "password_min_length", - "rate_limit_anonymous_users", - "rate_limit_email_sent", - "rate_limit_sms_sent", - "rate_limit_token_refresh", - "rate_limit_verify", - "rate_limit_otp", - "security_refresh_token_reuse_interval", - "sessions_inactivity_timeout", - "sessions_timebox", - "sms_max_frequency", - "sms_otp_exp", - "sms_otp_length", - "smtp_max_frequency", "disable_signup", "external_anonymous_users_enabled", "external_apple_additional_client_ids", @@ -4332,6 +5683,7 @@ "external_workos_enabled", "external_workos_secret", "external_workos_url", + "external_web3_solana_enabled", "external_zoom_client_id", "external_zoom_enabled", "external_zoom_secret", @@ -4350,8 +5702,14 @@ "hook_send_email_enabled", "hook_send_email_uri", "hook_send_email_secrets", + "hook_before_user_created_enabled", + "hook_before_user_created_uri", + "hook_before_user_created_secrets", + "jwt_exp", "mailer_allow_unverified_email_sign_ins", "mailer_autoconfirm", + "mailer_otp_exp", + "mailer_otp_length", "mailer_secure_email_change_enabled", "mailer_subjects_confirmation", "mailer_subjects_email_change", @@ -4365,15 +5723,26 @@ "mailer_templates_magic_link_content", "mailer_templates_reauthentication_content", "mailer_templates_recovery_content", + "mfa_max_enrolled_factors", "mfa_totp_enroll_enabled", "mfa_totp_verify_enabled", "mfa_phone_enroll_enabled", "mfa_phone_verify_enabled", "mfa_web_authn_enroll_enabled", "mfa_web_authn_verify_enabled", + "mfa_phone_otp_length", "mfa_phone_template", + "mfa_phone_max_frequency", "password_hibp_enabled", + "password_min_length", "password_required_characters", + "rate_limit_anonymous_users", + "rate_limit_email_sent", + "rate_limit_sms_sent", + "rate_limit_token_refresh", + "rate_limit_verify", + "rate_limit_otp", + "rate_limit_web3", "refresh_token_rotation_enabled", "saml_enabled", "saml_external_url", @@ -4382,13 +5751,19 @@ "security_captcha_provider", "security_captcha_secret", "security_manual_linking_enabled", + "security_refresh_token_reuse_interval", "security_update_password_require_reauthentication", + "sessions_inactivity_timeout", "sessions_single_per_user", "sessions_tags", + "sessions_timebox", "site_url", "sms_autoconfirm", + "sms_max_frequency", "sms_messagebird_access_key", "sms_messagebird_originator", + "sms_otp_exp", + "sms_otp_length", "sms_provider", "sms_template", "sms_test_otp", @@ -4407,6 +5782,7 @@ "sms_vonage_from", "smtp_admin_email", "smtp_host", + "smtp_max_frequency", "smtp_pass", "smtp_port", "smtp_sender_name", @@ -4417,71 +5793,113 @@ "UpdateAuthConfigBody": { "type": "object", "properties": { - "jwt_exp": { "type": "integer", "minimum": 0, "maximum": 604800 }, - "smtp_max_frequency": { "type": "integer", "minimum": 0, "maximum": 32767 }, - "mfa_max_enrolled_factors": { "type": "integer", "minimum": 0, "maximum": 2147483647 }, - "sessions_timebox": { "type": "integer", "minimum": 0 }, - "sessions_inactivity_timeout": { "type": "integer", "minimum": 0 }, - "rate_limit_anonymous_users": { "type": "integer", "minimum": 1, "maximum": 2147483647 }, - "rate_limit_email_sent": { "type": "integer", "minimum": 1, "maximum": 2147483647 }, - "rate_limit_sms_sent": { "type": "integer", "minimum": 1, "maximum": 2147483647 }, - "rate_limit_verify": { "type": "integer", "minimum": 1, "maximum": 2147483647 }, - "rate_limit_token_refresh": { "type": "integer", "minimum": 1, "maximum": 2147483647 }, - "rate_limit_otp": { "type": "integer", "minimum": 1, "maximum": 2147483647 }, - "password_min_length": { "type": "integer", "minimum": 6, "maximum": 32767 }, - "security_refresh_token_reuse_interval": { + "site_url": { "type": "string", "pattern": "^[^,]+$", "nullable": true }, + "disable_signup": { "type": "boolean", "nullable": true }, + "jwt_exp": { "type": "integer", "minimum": 0, "maximum": 604800, "nullable": true }, + "smtp_admin_email": { "type": "string", "format": "email", "nullable": true }, + "smtp_host": { "type": "string", "nullable": true }, + "smtp_port": { "type": "string", "nullable": true }, + "smtp_user": { "type": "string", "nullable": true }, + "smtp_pass": { "type": "string", "nullable": true }, + "smtp_max_frequency": { "type": "integer", "minimum": 0, - "maximum": 2147483647 + "maximum": 32767, + "nullable": true }, - "mailer_otp_exp": { "type": "integer", "minimum": 0, "maximum": 2147483647 }, - "mailer_otp_length": { "type": "integer", "minimum": 6, "maximum": 10 }, - "sms_max_frequency": { "type": "integer", "minimum": 0, "maximum": 32767 }, - "sms_otp_exp": { "type": "integer", "minimum": 0, "maximum": 2147483647 }, - "sms_otp_length": { "type": "integer", "minimum": 0, "maximum": 32767 }, - "db_max_pool_size": { "type": "integer" }, - "api_max_request_duration": { "type": "integer" }, - "mfa_phone_max_frequency": { "type": "integer", "minimum": 0, "maximum": 32767 }, - "mfa_phone_otp_length": { "type": "integer", "minimum": 0, "maximum": 32767 }, - "site_url": { "type": "string", "pattern": "/^[^,]+$/" }, - "disable_signup": { "type": "boolean" }, - "smtp_admin_email": { "type": "string" }, - "smtp_host": { "type": "string" }, - "smtp_port": { "type": "string" }, - "smtp_user": { "type": "string" }, - "smtp_pass": { "type": "string" }, - "smtp_sender_name": { "type": "string" }, - "mailer_allow_unverified_email_sign_ins": { "type": "boolean" }, - "mailer_autoconfirm": { "type": "boolean" }, - "mailer_subjects_invite": { "type": "string" }, - "mailer_subjects_confirmation": { "type": "string" }, - "mailer_subjects_recovery": { "type": "string" }, - "mailer_subjects_email_change": { "type": "string" }, - "mailer_subjects_magic_link": { "type": "string" }, - "mailer_subjects_reauthentication": { "type": "string" }, - "mailer_templates_invite_content": { "type": "string" }, - "mailer_templates_confirmation_content": { "type": "string" }, - "mailer_templates_recovery_content": { "type": "string" }, - "mailer_templates_email_change_content": { "type": "string" }, - "mailer_templates_magic_link_content": { "type": "string" }, - "mailer_templates_reauthentication_content": { "type": "string" }, - "uri_allow_list": { "type": "string" }, - "external_anonymous_users_enabled": { "type": "boolean" }, - "external_email_enabled": { "type": "boolean" }, - "external_phone_enabled": { "type": "boolean" }, - "saml_enabled": { "type": "boolean" }, - "saml_external_url": { "type": "string", "pattern": "/^[^,]+$/" }, - "security_captcha_enabled": { "type": "boolean" }, - "security_captcha_provider": { "type": "string" }, - "security_captcha_secret": { "type": "string" }, - "sessions_single_per_user": { "type": "boolean" }, + "smtp_sender_name": { "type": "string", "nullable": true }, + "mailer_allow_unverified_email_sign_ins": { "type": "boolean", "nullable": true }, + "mailer_autoconfirm": { "type": "boolean", "nullable": true }, + "mailer_subjects_invite": { "type": "string", "nullable": true }, + "mailer_subjects_confirmation": { "type": "string", "nullable": true }, + "mailer_subjects_recovery": { "type": "string", "nullable": true }, + "mailer_subjects_email_change": { "type": "string", "nullable": true }, + "mailer_subjects_magic_link": { "type": "string", "nullable": true }, + "mailer_subjects_reauthentication": { "type": "string", "nullable": true }, + "mailer_templates_invite_content": { "type": "string", "nullable": true }, + "mailer_templates_confirmation_content": { "type": "string", "nullable": true }, + "mailer_templates_recovery_content": { "type": "string", "nullable": true }, + "mailer_templates_email_change_content": { "type": "string", "nullable": true }, + "mailer_templates_magic_link_content": { "type": "string", "nullable": true }, + "mailer_templates_reauthentication_content": { "type": "string", "nullable": true }, + "mfa_max_enrolled_factors": { + "type": "integer", + "minimum": 0, + "maximum": 2147483647, + "nullable": true + }, + "uri_allow_list": { "type": "string", "nullable": true }, + "external_anonymous_users_enabled": { "type": "boolean", "nullable": true }, + "external_email_enabled": { "type": "boolean", "nullable": true }, + "external_phone_enabled": { "type": "boolean", "nullable": true }, + "saml_enabled": { "type": "boolean", "nullable": true }, + "saml_external_url": { "type": "string", "pattern": "^[^,]+$", "nullable": true }, + "security_captcha_enabled": { "type": "boolean", "nullable": true }, + "security_captcha_provider": { + "type": "string", + "enum": ["turnstile", "hcaptcha"], + "nullable": true + }, + "security_captcha_secret": { "type": "string", "nullable": true }, + "sessions_timebox": { "type": "integer", "minimum": 0, "nullable": true }, + "sessions_inactivity_timeout": { "type": "integer", "minimum": 0, "nullable": true }, + "sessions_single_per_user": { "type": "boolean", "nullable": true }, "sessions_tags": { "type": "string", - "pattern": "/^\\s*([a-z0-9_-]+(\\s*,+\\s*)?)*\\s*$/i" + "pattern": "^\\s*([a-zA-Z0-9_-]+(\\s*,+\\s*)?)*\\s*$", + "nullable": true + }, + "rate_limit_anonymous_users": { + "type": "integer", + "minimum": 1, + "maximum": 2147483647, + "nullable": true + }, + "rate_limit_email_sent": { + "type": "integer", + "minimum": 1, + "maximum": 2147483647, + "nullable": true + }, + "rate_limit_sms_sent": { + "type": "integer", + "minimum": 1, + "maximum": 2147483647, + "nullable": true + }, + "rate_limit_verify": { + "type": "integer", + "minimum": 1, + "maximum": 2147483647, + "nullable": true + }, + "rate_limit_token_refresh": { + "type": "integer", + "minimum": 1, + "maximum": 2147483647, + "nullable": true + }, + "rate_limit_otp": { + "type": "integer", + "minimum": 1, + "maximum": 2147483647, + "nullable": true + }, + "rate_limit_web3": { + "type": "integer", + "minimum": 1, + "maximum": 2147483647, + "nullable": true + }, + "mailer_secure_email_change_enabled": { "type": "boolean", "nullable": true }, + "refresh_token_rotation_enabled": { "type": "boolean", "nullable": true }, + "password_hibp_enabled": { "type": "boolean", "nullable": true }, + "password_min_length": { + "type": "integer", + "minimum": 6, + "maximum": 32767, + "nullable": true }, - "mailer_secure_email_change_enabled": { "type": "boolean" }, - "refresh_token_rotation_enabled": { "type": "boolean" }, - "password_hibp_enabled": { "type": "boolean" }, "password_required_characters": { "type": "string", "enum": [ @@ -4489,118 +5907,169 @@ "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\\\:\"|<>?,./`~", "" - ] + ], + "nullable": true }, - "security_manual_linking_enabled": { "type": "boolean" }, - "security_update_password_require_reauthentication": { "type": "boolean" }, - "sms_autoconfirm": { "type": "boolean" }, - "sms_provider": { "type": "string" }, - "sms_messagebird_access_key": { "type": "string" }, - "sms_messagebird_originator": { "type": "string" }, - "sms_test_otp": { "type": "string", "pattern": "/^([0-9]{1,15}=[0-9]+,?)*$/" }, - "sms_test_otp_valid_until": { "type": "string" }, - "sms_textlocal_api_key": { "type": "string" }, - "sms_textlocal_sender": { "type": "string" }, - "sms_twilio_account_sid": { "type": "string" }, - "sms_twilio_auth_token": { "type": "string" }, - "sms_twilio_content_sid": { "type": "string" }, - "sms_twilio_message_service_sid": { "type": "string" }, - "sms_twilio_verify_account_sid": { "type": "string" }, - "sms_twilio_verify_auth_token": { "type": "string" }, - "sms_twilio_verify_message_service_sid": { "type": "string" }, - "sms_vonage_api_key": { "type": "string" }, - "sms_vonage_api_secret": { "type": "string" }, - "sms_vonage_from": { "type": "string" }, - "sms_template": { "type": "string" }, - "hook_mfa_verification_attempt_enabled": { "type": "boolean" }, - "hook_mfa_verification_attempt_uri": { "type": "string" }, - "hook_mfa_verification_attempt_secrets": { "type": "string" }, - "hook_password_verification_attempt_enabled": { "type": "boolean" }, - "hook_password_verification_attempt_uri": { "type": "string" }, - "hook_password_verification_attempt_secrets": { "type": "string" }, - "hook_custom_access_token_enabled": { "type": "boolean" }, - "hook_custom_access_token_uri": { "type": "string" }, - "hook_custom_access_token_secrets": { "type": "string" }, - "hook_send_sms_enabled": { "type": "boolean" }, - "hook_send_sms_uri": { "type": "string" }, - "hook_send_sms_secrets": { "type": "string" }, - "hook_send_email_enabled": { "type": "boolean" }, - "hook_send_email_uri": { "type": "string" }, - "hook_send_email_secrets": { "type": "string" }, - "external_apple_enabled": { "type": "boolean" }, - "external_apple_client_id": { "type": "string" }, - "external_apple_secret": { "type": "string" }, - "external_apple_additional_client_ids": { "type": "string" }, - "external_azure_enabled": { "type": "boolean" }, - "external_azure_client_id": { "type": "string" }, - "external_azure_secret": { "type": "string" }, - "external_azure_url": { "type": "string" }, - "external_bitbucket_enabled": { "type": "boolean" }, - "external_bitbucket_client_id": { "type": "string" }, - "external_bitbucket_secret": { "type": "string" }, - "external_discord_enabled": { "type": "boolean" }, - "external_discord_client_id": { "type": "string" }, - "external_discord_secret": { "type": "string" }, - "external_facebook_enabled": { "type": "boolean" }, - "external_facebook_client_id": { "type": "string" }, - "external_facebook_secret": { "type": "string" }, - "external_figma_enabled": { "type": "boolean" }, - "external_figma_client_id": { "type": "string" }, - "external_figma_secret": { "type": "string" }, - "external_github_enabled": { "type": "boolean" }, - "external_github_client_id": { "type": "string" }, - "external_github_secret": { "type": "string" }, - "external_gitlab_enabled": { "type": "boolean" }, - "external_gitlab_client_id": { "type": "string" }, - "external_gitlab_secret": { "type": "string" }, - "external_gitlab_url": { "type": "string" }, - "external_google_enabled": { "type": "boolean" }, - "external_google_client_id": { "type": "string" }, - "external_google_secret": { "type": "string" }, - "external_google_additional_client_ids": { "type": "string" }, - "external_google_skip_nonce_check": { "type": "boolean" }, - "external_kakao_enabled": { "type": "boolean" }, - "external_kakao_client_id": { "type": "string" }, - "external_kakao_secret": { "type": "string" }, - "external_keycloak_enabled": { "type": "boolean" }, - "external_keycloak_client_id": { "type": "string" }, - "external_keycloak_secret": { "type": "string" }, - "external_keycloak_url": { "type": "string" }, - "external_linkedin_oidc_enabled": { "type": "boolean" }, - "external_linkedin_oidc_client_id": { "type": "string" }, - "external_linkedin_oidc_secret": { "type": "string" }, - "external_slack_oidc_enabled": { "type": "boolean" }, - "external_slack_oidc_client_id": { "type": "string" }, - "external_slack_oidc_secret": { "type": "string" }, - "external_notion_enabled": { "type": "boolean" }, - "external_notion_client_id": { "type": "string" }, - "external_notion_secret": { "type": "string" }, - "external_slack_enabled": { "type": "boolean" }, - "external_slack_client_id": { "type": "string" }, - "external_slack_secret": { "type": "string" }, - "external_spotify_enabled": { "type": "boolean" }, - "external_spotify_client_id": { "type": "string" }, - "external_spotify_secret": { "type": "string" }, - "external_twitch_enabled": { "type": "boolean" }, - "external_twitch_client_id": { "type": "string" }, - "external_twitch_secret": { "type": "string" }, - "external_twitter_enabled": { "type": "boolean" }, - "external_twitter_client_id": { "type": "string" }, - "external_twitter_secret": { "type": "string" }, - "external_workos_enabled": { "type": "boolean" }, - "external_workos_client_id": { "type": "string" }, - "external_workos_secret": { "type": "string" }, - "external_workos_url": { "type": "string" }, - "external_zoom_enabled": { "type": "boolean" }, - "external_zoom_client_id": { "type": "string" }, - "external_zoom_secret": { "type": "string" }, - "mfa_totp_enroll_enabled": { "type": "boolean" }, - "mfa_totp_verify_enabled": { "type": "boolean" }, - "mfa_web_authn_enroll_enabled": { "type": "boolean" }, - "mfa_web_authn_verify_enabled": { "type": "boolean" }, - "mfa_phone_enroll_enabled": { "type": "boolean" }, - "mfa_phone_verify_enabled": { "type": "boolean" }, - "mfa_phone_template": { "type": "string" } + "security_manual_linking_enabled": { "type": "boolean", "nullable": true }, + "security_update_password_require_reauthentication": { + "type": "boolean", + "nullable": true + }, + "security_refresh_token_reuse_interval": { + "type": "integer", + "minimum": 0, + "maximum": 2147483647, + "nullable": true + }, + "mailer_otp_exp": { "type": "integer", "minimum": 0, "maximum": 2147483647 }, + "mailer_otp_length": { "type": "integer", "minimum": 6, "maximum": 10, "nullable": true }, + "sms_autoconfirm": { "type": "boolean", "nullable": true }, + "sms_max_frequency": { + "type": "integer", + "minimum": 0, + "maximum": 32767, + "nullable": true + }, + "sms_otp_exp": { + "type": "integer", + "minimum": 0, + "maximum": 2147483647, + "nullable": true + }, + "sms_otp_length": { "type": "integer", "minimum": 0, "maximum": 32767 }, + "sms_provider": { + "type": "string", + "enum": ["messagebird", "textlocal", "twilio", "twilio_verify", "vonage"], + "nullable": true + }, + "sms_messagebird_access_key": { "type": "string", "nullable": true }, + "sms_messagebird_originator": { "type": "string", "nullable": true }, + "sms_test_otp": { + "type": "string", + "pattern": "^([0-9]{1,15}=[0-9]+,?)*$", + "nullable": true + }, + "sms_test_otp_valid_until": { "type": "string", "format": "date-time", "nullable": true }, + "sms_textlocal_api_key": { "type": "string", "nullable": true }, + "sms_textlocal_sender": { "type": "string", "nullable": true }, + "sms_twilio_account_sid": { "type": "string", "nullable": true }, + "sms_twilio_auth_token": { "type": "string", "nullable": true }, + "sms_twilio_content_sid": { "type": "string", "nullable": true }, + "sms_twilio_message_service_sid": { "type": "string", "nullable": true }, + "sms_twilio_verify_account_sid": { "type": "string", "nullable": true }, + "sms_twilio_verify_auth_token": { "type": "string", "nullable": true }, + "sms_twilio_verify_message_service_sid": { "type": "string", "nullable": true }, + "sms_vonage_api_key": { "type": "string", "nullable": true }, + "sms_vonage_api_secret": { "type": "string", "nullable": true }, + "sms_vonage_from": { "type": "string", "nullable": true }, + "sms_template": { "type": "string", "nullable": true }, + "hook_mfa_verification_attempt_enabled": { "type": "boolean", "nullable": true }, + "hook_mfa_verification_attempt_uri": { "type": "string", "nullable": true }, + "hook_mfa_verification_attempt_secrets": { "type": "string", "nullable": true }, + "hook_password_verification_attempt_enabled": { "type": "boolean", "nullable": true }, + "hook_password_verification_attempt_uri": { "type": "string", "nullable": true }, + "hook_password_verification_attempt_secrets": { "type": "string", "nullable": true }, + "hook_custom_access_token_enabled": { "type": "boolean", "nullable": true }, + "hook_custom_access_token_uri": { "type": "string", "nullable": true }, + "hook_custom_access_token_secrets": { "type": "string", "nullable": true }, + "hook_send_sms_enabled": { "type": "boolean", "nullable": true }, + "hook_send_sms_uri": { "type": "string", "nullable": true }, + "hook_send_sms_secrets": { "type": "string", "nullable": true }, + "hook_send_email_enabled": { "type": "boolean", "nullable": true }, + "hook_send_email_uri": { "type": "string", "nullable": true }, + "hook_send_email_secrets": { "type": "string", "nullable": true }, + "hook_before_user_created_enabled": { "type": "boolean", "nullable": true }, + "hook_before_user_created_uri": { "type": "string", "nullable": true }, + "hook_before_user_created_secrets": { "type": "string", "nullable": true }, + "external_apple_enabled": { "type": "boolean", "nullable": true }, + "external_apple_client_id": { "type": "string", "nullable": true }, + "external_apple_secret": { "type": "string", "nullable": true }, + "external_apple_additional_client_ids": { "type": "string", "nullable": true }, + "external_azure_enabled": { "type": "boolean", "nullable": true }, + "external_azure_client_id": { "type": "string", "nullable": true }, + "external_azure_secret": { "type": "string", "nullable": true }, + "external_azure_url": { "type": "string", "nullable": true }, + "external_bitbucket_enabled": { "type": "boolean", "nullable": true }, + "external_bitbucket_client_id": { "type": "string", "nullable": true }, + "external_bitbucket_secret": { "type": "string", "nullable": true }, + "external_discord_enabled": { "type": "boolean", "nullable": true }, + "external_discord_client_id": { "type": "string", "nullable": true }, + "external_discord_secret": { "type": "string", "nullable": true }, + "external_facebook_enabled": { "type": "boolean", "nullable": true }, + "external_facebook_client_id": { "type": "string", "nullable": true }, + "external_facebook_secret": { "type": "string", "nullable": true }, + "external_figma_enabled": { "type": "boolean", "nullable": true }, + "external_figma_client_id": { "type": "string", "nullable": true }, + "external_figma_secret": { "type": "string", "nullable": true }, + "external_github_enabled": { "type": "boolean", "nullable": true }, + "external_github_client_id": { "type": "string", "nullable": true }, + "external_github_secret": { "type": "string", "nullable": true }, + "external_gitlab_enabled": { "type": "boolean", "nullable": true }, + "external_gitlab_client_id": { "type": "string", "nullable": true }, + "external_gitlab_secret": { "type": "string", "nullable": true }, + "external_gitlab_url": { "type": "string", "nullable": true }, + "external_google_enabled": { "type": "boolean", "nullable": true }, + "external_google_client_id": { "type": "string", "nullable": true }, + "external_google_secret": { "type": "string", "nullable": true }, + "external_google_additional_client_ids": { "type": "string", "nullable": true }, + "external_google_skip_nonce_check": { "type": "boolean", "nullable": true }, + "external_kakao_enabled": { "type": "boolean", "nullable": true }, + "external_kakao_client_id": { "type": "string", "nullable": true }, + "external_kakao_secret": { "type": "string", "nullable": true }, + "external_keycloak_enabled": { "type": "boolean", "nullable": true }, + "external_keycloak_client_id": { "type": "string", "nullable": true }, + "external_keycloak_secret": { "type": "string", "nullable": true }, + "external_keycloak_url": { "type": "string", "nullable": true }, + "external_linkedin_oidc_enabled": { "type": "boolean", "nullable": true }, + "external_linkedin_oidc_client_id": { "type": "string", "nullable": true }, + "external_linkedin_oidc_secret": { "type": "string", "nullable": true }, + "external_slack_oidc_enabled": { "type": "boolean", "nullable": true }, + "external_slack_oidc_client_id": { "type": "string", "nullable": true }, + "external_slack_oidc_secret": { "type": "string", "nullable": true }, + "external_notion_enabled": { "type": "boolean", "nullable": true }, + "external_notion_client_id": { "type": "string", "nullable": true }, + "external_notion_secret": { "type": "string", "nullable": true }, + "external_slack_enabled": { "type": "boolean", "nullable": true }, + "external_slack_client_id": { "type": "string", "nullable": true }, + "external_slack_secret": { "type": "string", "nullable": true }, + "external_spotify_enabled": { "type": "boolean", "nullable": true }, + "external_spotify_client_id": { "type": "string", "nullable": true }, + "external_spotify_secret": { "type": "string", "nullable": true }, + "external_twitch_enabled": { "type": "boolean", "nullable": true }, + "external_twitch_client_id": { "type": "string", "nullable": true }, + "external_twitch_secret": { "type": "string", "nullable": true }, + "external_twitter_enabled": { "type": "boolean", "nullable": true }, + "external_twitter_client_id": { "type": "string", "nullable": true }, + "external_twitter_secret": { "type": "string", "nullable": true }, + "external_workos_enabled": { "type": "boolean", "nullable": true }, + "external_workos_client_id": { "type": "string", "nullable": true }, + "external_workos_secret": { "type": "string", "nullable": true }, + "external_workos_url": { "type": "string", "nullable": true }, + "external_web3_solana_enabled": { "type": "boolean", "nullable": true }, + "external_zoom_enabled": { "type": "boolean", "nullable": true }, + "external_zoom_client_id": { "type": "string", "nullable": true }, + "external_zoom_secret": { "type": "string", "nullable": true }, + "db_max_pool_size": { "type": "integer", "nullable": true }, + "api_max_request_duration": { "type": "integer", "nullable": true }, + "mfa_totp_enroll_enabled": { "type": "boolean", "nullable": true }, + "mfa_totp_verify_enabled": { "type": "boolean", "nullable": true }, + "mfa_web_authn_enroll_enabled": { "type": "boolean", "nullable": true }, + "mfa_web_authn_verify_enabled": { "type": "boolean", "nullable": true }, + "mfa_phone_enroll_enabled": { "type": "boolean", "nullable": true }, + "mfa_phone_verify_enabled": { "type": "boolean", "nullable": true }, + "mfa_phone_max_frequency": { + "type": "integer", + "minimum": 0, + "maximum": 32767, + "nullable": true + }, + "mfa_phone_otp_length": { + "type": "integer", + "minimum": 0, + "maximum": 32767, + "nullable": true + }, + "mfa_phone_template": { "type": "string", "nullable": true } } }, "CreateThirdPartyAuthBody": { @@ -4608,52 +6077,49 @@ "properties": { "oidc_issuer_url": { "type": "string" }, "jwks_url": { "type": "string" }, - "custom_jwks": { "type": "object" } + "custom_jwks": {} } }, "ThirdPartyAuth": { "type": "object", "properties": { - "id": { "type": "string" }, + "id": { "type": "string", "format": "uuid" }, "type": { "type": "string" }, "oidc_issuer_url": { "type": "string", "nullable": true }, "jwks_url": { "type": "string", "nullable": true }, - "custom_jwks": { "type": "object", "nullable": true }, - "resolved_jwks": { "type": "object", "nullable": true }, + "custom_jwks": { "nullable": true }, + "resolved_jwks": { "nullable": true }, "inserted_at": { "type": "string" }, "updated_at": { "type": "string" }, "resolved_at": { "type": "string", "nullable": true } }, "required": ["id", "type", "inserted_at", "updated_at"] }, - "ProjectAvailableRestoreVersion": { - "type": "object", - "properties": { - "version": { "type": "string" }, - "release_channel": { - "type": "string", - "enum": ["internal", "alpha", "beta", "ga", "withdrawn", "preview"] - }, - "postgres_engine": { "type": "string", "enum": ["13", "14", "15", "17", "17-oriole"] } - }, - "required": ["version", "release_channel", "postgres_engine"] - }, "GetProjectAvailableRestoreVersionsResponse": { "type": "object", "properties": { "available_versions": { "type": "array", - "items": { "$ref": "#/components/schemas/ProjectAvailableRestoreVersion" } + "items": { + "type": "object", + "properties": { + "version": { "type": "string" }, + "release_channel": { + "type": "string", + "enum": ["internal", "alpha", "beta", "ga", "withdrawn", "preview"] + }, + "postgres_engine": { + "type": "string", + "enum": ["13", "14", "15", "17", "17-oriole"] + } + }, + "required": ["version", "release_channel", "postgres_engine"] + } } }, "required": ["available_versions"] }, - "RestoreProjectBodyDto": { - "type": "object", - "properties": {}, - "hideDefinitions": ["release_channel", "postgres_engine"] - }, - "ListProjectAddonsResponseDto": { + "ListProjectAddonsResponse": { "type": "object", "properties": { "selected_addons": { @@ -4690,7 +6156,15 @@ "ci_4xlarge", "ci_8xlarge", "ci_12xlarge", - "ci_16xlarge" + "ci_16xlarge", + "ci_24xlarge", + "ci_24xlarge_optimized_cpu", + "ci_24xlarge_optimized_memory", + "ci_24xlarge_high_memory", + "ci_48xlarge", + "ci_48xlarge_optimized_cpu", + "ci_48xlarge_optimized_memory", + "ci_48xlarge_high_memory" ] }, { "type": "string", "enum": ["cd_default"] }, @@ -4712,17 +6186,7 @@ }, "required": ["description", "type", "interval", "amount"] }, - "meta": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { "type": "number" }, - { "type": "boolean" }, - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - } - } + "meta": { "description": "Any JSON-serializable value" } }, "required": ["id", "name", "price"] } @@ -4767,7 +6231,15 @@ "ci_4xlarge", "ci_8xlarge", "ci_12xlarge", - "ci_16xlarge" + "ci_16xlarge", + "ci_24xlarge", + "ci_24xlarge_optimized_cpu", + "ci_24xlarge_optimized_memory", + "ci_24xlarge_high_memory", + "ci_48xlarge", + "ci_48xlarge_optimized_cpu", + "ci_48xlarge_optimized_memory", + "ci_48xlarge_high_memory" ] }, { "type": "string", "enum": ["cd_default"] }, @@ -4789,17 +6261,7 @@ }, "required": ["description", "type", "interval", "amount"] }, - "meta": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { "type": "number" }, - { "type": "boolean" }, - { "type": "string" }, - { "type": "array", "items": { "type": "string" } } - ] - } - } + "meta": { "description": "Any JSON-serializable value" } }, "required": ["id", "name", "price"] } @@ -4811,7 +6273,7 @@ }, "required": ["selected_addons", "available_addons"] }, - "ApplyProjectAddonBodyDto": { + "ApplyProjectAddonBody": { "type": "object", "properties": { "addon_variant": { @@ -4828,7 +6290,15 @@ "ci_4xlarge", "ci_8xlarge", "ci_12xlarge", - "ci_16xlarge" + "ci_16xlarge", + "ci_24xlarge", + "ci_24xlarge_optimized_cpu", + "ci_24xlarge_optimized_memory", + "ci_24xlarge_high_memory", + "ci_48xlarge", + "ci_48xlarge_optimized_cpu", + "ci_48xlarge_optimized_memory", + "ci_48xlarge_high_memory" ] }, { "type": "string", "enum": ["cd_default"] }, @@ -4851,34 +6321,142 @@ }, "required": ["addon_variant", "addon_type"] }, - "V1AnalyticsResponse": { + "ProjectClaimTokenResponse": { "type": "object", "properties": { + "token_alias": { "type": "string" }, + "expires_at": { "type": "string" }, + "created_at": { "type": "string" }, + "created_by": { "type": "string", "format": "uuid" } + }, + "required": ["token_alias", "expires_at", "created_at", "created_by"] + }, + "CreateProjectClaimTokenResponse": { + "type": "object", + "properties": { + "token": { "type": "string" }, + "token_alias": { "type": "string" }, + "expires_at": { "type": "string" }, + "created_at": { "type": "string" }, + "created_by": { "type": "string", "format": "uuid" } + }, + "required": ["token", "token_alias", "expires_at", "created_at", "created_by"] + }, + "V1ProjectAdvisorsResponse": { + "type": "object", + "properties": { + "lints": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "unindexed_foreign_keys", + "auth_users_exposed", + "auth_rls_initplan", + "no_primary_key", + "unused_index", + "multiple_permissive_policies", + "policy_exists_rls_disabled", + "rls_enabled_no_policy", + "duplicate_index", + "security_definer_view", + "function_search_path_mutable", + "rls_disabled_in_public", + "extension_in_public", + "rls_references_user_metadata", + "materialized_view_in_api", + "foreign_table_in_api", + "unsupported_reg_types", + "auth_otp_long_expiry", + "auth_otp_short_length", + "ssl_not_enforced", + "network_restrictions_not_set", + "password_requirements_min_length", + "pitr_not_enabled", + "auth_leaked_password_protection", + "auth_insufficient_mfa_options", + "auth_password_policy_missing", + "leaked_service_key", + "no_backup_admin" + ] + }, + "title": { "type": "string" }, + "level": { "type": "string", "enum": ["ERROR", "WARN", "INFO"] }, + "facing": { "type": "string", "enum": ["EXTERNAL"] }, + "categories": { + "type": "array", + "items": { "type": "string", "enum": ["PERFORMANCE", "SECURITY"] } + }, + "description": { "type": "string" }, + "detail": { "type": "string" }, + "remediation": { "type": "string" }, + "metadata": { + "type": "object", + "properties": { + "schema": { "type": "string" }, + "name": { "type": "string" }, + "entity": { "type": "string" }, + "type": { + "type": "string", + "enum": ["table", "view", "auth", "function", "extension", "compliance"] + }, + "fkey_name": { "type": "string" }, + "fkey_columns": { "type": "array", "items": { "type": "number" } } + } + }, + "cache_key": { "type": "string" } + }, + "required": [ + "name", + "title", + "level", + "facing", + "categories", + "description", + "detail", + "remediation", + "cache_key" + ] + } + } + }, + "required": ["lints"] + }, + "AnalyticsResponse": { + "type": "object", + "properties": { + "result": { "type": "array", "items": {} }, "error": { "oneOf": [ + { "type": "string" }, { + "type": "object", "properties": { "code": { "type": "number" }, "errors": { "type": "array", "items": { + "type": "object", "properties": { "domain": { "type": "string" }, "location": { "type": "string" }, "locationType": { "type": "string" }, "message": { "type": "string" }, "reason": { "type": "string" } - } + }, + "required": ["domain", "location", "locationType", "message", "reason"] } }, "message": { "type": "string" }, "status": { "type": "string" } - } - }, - { "type": "string" } + }, + "required": ["code", "errors", "message", "status"] + } ] - }, - "result": { "type": "array", "items": { "type": "object" } } + } } }, "V1ListMigrationsResponse": { @@ -4892,12 +6470,31 @@ "required": ["version"] } }, - "V1RunQueryBody": { + "V1CreateMigrationBody": { "type": "object", - "properties": { "query": { "type": "string", "minLength": 1 } }, + "properties": { + "query": { "type": "string", "minLength": 1 }, + "name": { "type": "string" } + }, "required": ["query"] }, - "GetProjectDbMetadataResponseDto": { + "V1UpsertMigrationBody": { + "type": "object", + "properties": { + "query": { "type": "string", "minLength": 1 }, + "name": { "type": "string" } + }, + "required": ["query"] + }, + "V1RunQueryBody": { + "type": "object", + "properties": { + "query": { "type": "string", "minLength": 1 }, + "read_only": { "type": "boolean" } + }, + "required": ["query"] + }, + "GetProjectDbMetadataResponse": { "type": "object", "properties": { "databases": { @@ -4931,12 +6528,13 @@ "name": { "type": "string" }, "status": { "type": "string", "enum": ["ACTIVE", "REMOVED", "THROTTLED"] }, "version": { "type": "integer" }, - "created_at": { "type": "integer" }, - "updated_at": { "type": "integer" }, + "created_at": { "type": "integer", "format": "int64" }, + "updated_at": { "type": "integer", "format": "int64" }, "verify_jwt": { "type": "boolean" }, "import_map": { "type": "boolean" }, "entrypoint_path": { "type": "string" }, - "import_map_path": { "type": "string" } + "import_map_path": { "type": "string" }, + "ezbr_sha256": { "type": "string" } }, "required": ["id", "slug", "name", "status", "version", "created_at", "updated_at"] }, @@ -4951,20 +6549,24 @@ "required": ["slug", "name", "body"] }, "BulkUpdateFunctionBody": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "slug": { "type": "string", "pattern": "^[A-Za-z0-9_-]+$" }, - "name": { "type": "string" }, - "status": { "type": "string", "enum": ["ACTIVE", "REMOVED", "THROTTLED"] }, - "version": { "type": "integer" }, - "created_at": { "type": "integer" }, - "verify_jwt": { "type": "boolean" }, - "import_map": { "type": "boolean" }, - "entrypoint_path": { "type": "string" }, - "import_map_path": { "type": "string" } - }, - "required": ["id", "slug", "name", "status", "version"] + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "slug": { "type": "string", "pattern": "^[A-Za-z0-9_-]+$" }, + "name": { "type": "string" }, + "status": { "type": "string", "enum": ["ACTIVE", "REMOVED", "THROTTLED"] }, + "version": { "type": "integer" }, + "created_at": { "type": "integer", "format": "int64" }, + "verify_jwt": { "type": "boolean" }, + "import_map": { "type": "boolean" }, + "entrypoint_path": { "type": "string" }, + "import_map_path": { "type": "string" }, + "ezbr_sha256": { "type": "string" } + }, + "required": ["id", "slug", "name", "status", "version"] + } }, "BulkUpdateFunctionResponse": { "type": "object", @@ -4979,12 +6581,13 @@ "name": { "type": "string" }, "status": { "type": "string", "enum": ["ACTIVE", "REMOVED", "THROTTLED"] }, "version": { "type": "integer" }, - "created_at": { "type": "integer" }, - "updated_at": { "type": "integer" }, + "created_at": { "type": "integer", "format": "int64" }, + "updated_at": { "type": "integer", "format": "int64" }, "verify_jwt": { "type": "boolean" }, "import_map": { "type": "boolean" }, "entrypoint_path": { "type": "string" }, - "import_map_path": { "type": "string" } + "import_map_path": { "type": "string" }, + "ezbr_sha256": { "type": "string" } }, "required": ["id", "slug", "name", "status", "version", "created_at", "updated_at"] } @@ -5018,12 +6621,13 @@ "name": { "type": "string" }, "status": { "type": "string", "enum": ["ACTIVE", "REMOVED", "THROTTLED"] }, "version": { "type": "integer" }, - "created_at": { "type": "integer" }, - "updated_at": { "type": "integer" }, + "created_at": { "type": "integer", "format": "int64" }, + "updated_at": { "type": "integer", "format": "int64" }, "verify_jwt": { "type": "boolean" }, "import_map": { "type": "boolean" }, "entrypoint_path": { "type": "string" }, - "import_map_path": { "type": "string" } + "import_map_path": { "type": "string" }, + "ezbr_sha256": { "type": "string" } }, "required": ["id", "slug", "name", "status", "version"] }, @@ -5035,15 +6639,17 @@ "name": { "type": "string" }, "status": { "type": "string", "enum": ["ACTIVE", "REMOVED", "THROTTLED"] }, "version": { "type": "integer" }, - "created_at": { "type": "integer" }, - "updated_at": { "type": "integer" }, + "created_at": { "type": "integer", "format": "int64" }, + "updated_at": { "type": "integer", "format": "int64" }, "verify_jwt": { "type": "boolean" }, "import_map": { "type": "boolean" }, "entrypoint_path": { "type": "string" }, - "import_map_path": { "type": "string" } + "import_map_path": { "type": "string" }, + "ezbr_sha256": { "type": "string" } }, "required": ["id", "slug", "name", "status", "version", "created_at", "updated_at"] }, + "StreamableFile": { "type": "object", "properties": {} }, "V1UpdateFunctionBody": { "type": "object", "properties": { @@ -5064,32 +6670,6 @@ }, "required": ["id", "name", "owner", "created_at", "updated_at", "public"] }, - "AttributeValue": { - "type": "object", - "properties": { - "default": { - "oneOf": [ - { "type": "object" }, - { "type": "number" }, - { "type": "string" }, - { "type": "boolean" } - ] - }, - "name": { "type": "string" }, - "names": { "type": "array", "items": { "type": "string" } }, - "array": { "type": "boolean" } - } - }, - "AttributeMapping": { - "type": "object", - "properties": { - "keys": { - "type": "object", - "additionalProperties": { "$ref": "#/components/schemas/AttributeValue" } - } - }, - "required": ["keys"] - }, "CreateProviderBody": { "type": "object", "properties": { @@ -5101,48 +6681,86 @@ "metadata_xml": { "type": "string" }, "metadata_url": { "type": "string" }, "domains": { "type": "array", "items": { "type": "string" } }, - "attribute_mapping": { "$ref": "#/components/schemas/AttributeMapping" } + "attribute_mapping": { + "type": "object", + "properties": { + "keys": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "names": { "type": "array", "items": { "type": "string" } }, + "default": { + "oneOf": [ + { "type": "object", "properties": {} }, + { "type": "number" }, + { "type": "string" }, + { "type": "boolean" } + ] + }, + "array": { "type": "boolean" } + } + } + } + }, + "required": ["keys"] + } }, "required": ["type"] }, - "SamlDescriptor": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "entity_id": { "type": "string" }, - "metadata_url": { "type": "string" }, - "metadata_xml": { "type": "string" }, - "attribute_mapping": { "$ref": "#/components/schemas/AttributeMapping" } - }, - "required": ["id", "entity_id"] - }, - "Domain": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "domain": { "type": "string" }, - "created_at": { "type": "string" }, - "updated_at": { "type": "string" } - }, - "required": ["id"] - }, "CreateProviderResponse": { "type": "object", "properties": { "id": { "type": "string" }, - "saml": { "$ref": "#/components/schemas/SamlDescriptor" }, - "domains": { "type": "array", "items": { "$ref": "#/components/schemas/Domain" } }, - "created_at": { "type": "string" }, - "updated_at": { "type": "string" } - }, - "required": ["id"] - }, - "Provider": { - "type": "object", - "properties": { - "id": { "type": "string" }, - "saml": { "$ref": "#/components/schemas/SamlDescriptor" }, - "domains": { "type": "array", "items": { "$ref": "#/components/schemas/Domain" } }, + "saml": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "entity_id": { "type": "string" }, + "metadata_url": { "type": "string" }, + "metadata_xml": { "type": "string" }, + "attribute_mapping": { + "type": "object", + "properties": { + "keys": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "names": { "type": "array", "items": { "type": "string" } }, + "default": { + "oneOf": [ + { "type": "object", "properties": {} }, + { "type": "number" }, + { "type": "string" }, + { "type": "boolean" } + ] + }, + "array": { "type": "boolean" } + } + } + } + }, + "required": ["keys"] + } + }, + "required": ["id", "entity_id"] + }, + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "domain": { "type": "string" }, + "created_at": { "type": "string" }, + "updated_at": { "type": "string" } + }, + "required": ["id"] + } + }, "created_at": { "type": "string" }, "updated_at": { "type": "string" } }, @@ -5151,7 +6769,66 @@ "ListProvidersResponse": { "type": "object", "properties": { - "items": { "type": "array", "items": { "$ref": "#/components/schemas/Provider" } } + "items": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "saml": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "entity_id": { "type": "string" }, + "metadata_url": { "type": "string" }, + "metadata_xml": { "type": "string" }, + "attribute_mapping": { + "type": "object", + "properties": { + "keys": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "names": { "type": "array", "items": { "type": "string" } }, + "default": { + "oneOf": [ + { "type": "object", "properties": {} }, + { "type": "number" }, + { "type": "string" }, + { "type": "boolean" } + ] + }, + "array": { "type": "boolean" } + } + } + } + }, + "required": ["keys"] + } + }, + "required": ["id", "entity_id"] + }, + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "domain": { "type": "string" }, + "created_at": { "type": "string" }, + "updated_at": { "type": "string" } + }, + "required": ["id"] + } + }, + "created_at": { "type": "string" }, + "updated_at": { "type": "string" } + }, + "required": ["id"] + } + } }, "required": ["items"] }, @@ -5159,8 +6836,54 @@ "type": "object", "properties": { "id": { "type": "string" }, - "saml": { "$ref": "#/components/schemas/SamlDescriptor" }, - "domains": { "type": "array", "items": { "$ref": "#/components/schemas/Domain" } }, + "saml": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "entity_id": { "type": "string" }, + "metadata_url": { "type": "string" }, + "metadata_xml": { "type": "string" }, + "attribute_mapping": { + "type": "object", + "properties": { + "keys": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "names": { "type": "array", "items": { "type": "string" } }, + "default": { + "oneOf": [ + { "type": "object", "properties": {} }, + { "type": "number" }, + { "type": "string" }, + { "type": "boolean" } + ] + }, + "array": { "type": "boolean" } + } + } + } + }, + "required": ["keys"] + } + }, + "required": ["id", "entity_id"] + }, + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "domain": { "type": "string" }, + "created_at": { "type": "string" }, + "updated_at": { "type": "string" } + }, + "required": ["id"] + } + }, "created_at": { "type": "string" }, "updated_at": { "type": "string" } }, @@ -5172,15 +6895,85 @@ "metadata_xml": { "type": "string" }, "metadata_url": { "type": "string" }, "domains": { "type": "array", "items": { "type": "string" } }, - "attribute_mapping": { "$ref": "#/components/schemas/AttributeMapping" } + "attribute_mapping": { + "type": "object", + "properties": { + "keys": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "names": { "type": "array", "items": { "type": "string" } }, + "default": { + "oneOf": [ + { "type": "object", "properties": {} }, + { "type": "number" }, + { "type": "string" }, + { "type": "boolean" } + ] + }, + "array": { "type": "boolean" } + } + } + } + }, + "required": ["keys"] + } } }, "UpdateProviderResponse": { "type": "object", "properties": { "id": { "type": "string" }, - "saml": { "$ref": "#/components/schemas/SamlDescriptor" }, - "domains": { "type": "array", "items": { "$ref": "#/components/schemas/Domain" } }, + "saml": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "entity_id": { "type": "string" }, + "metadata_url": { "type": "string" }, + "metadata_xml": { "type": "string" }, + "attribute_mapping": { + "type": "object", + "properties": { + "keys": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "names": { "type": "array", "items": { "type": "string" } }, + "default": { + "oneOf": [ + { "type": "object", "properties": {} }, + { "type": "number" }, + { "type": "string" }, + { "type": "boolean" } + ] + }, + "array": { "type": "boolean" } + } + } + } + }, + "required": ["keys"] + } + }, + "required": ["id", "entity_id"] + }, + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "domain": { "type": "string" }, + "created_at": { "type": "string" }, + "updated_at": { "type": "string" } + }, + "required": ["id"] + } + }, "created_at": { "type": "string" }, "updated_at": { "type": "string" } }, @@ -5190,40 +6983,87 @@ "type": "object", "properties": { "id": { "type": "string" }, - "saml": { "$ref": "#/components/schemas/SamlDescriptor" }, - "domains": { "type": "array", "items": { "$ref": "#/components/schemas/Domain" } }, + "saml": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "entity_id": { "type": "string" }, + "metadata_url": { "type": "string" }, + "metadata_xml": { "type": "string" }, + "attribute_mapping": { + "type": "object", + "properties": { + "keys": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "names": { "type": "array", "items": { "type": "string" } }, + "default": { + "oneOf": [ + { "type": "object", "properties": {} }, + { "type": "number" }, + { "type": "string" }, + { "type": "boolean" } + ] + }, + "array": { "type": "boolean" } + } + } + } + }, + "required": ["keys"] + } + }, + "required": ["id", "entity_id"] + }, + "domains": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "domain": { "type": "string" }, + "created_at": { "type": "string" }, + "updated_at": { "type": "string" } + }, + "required": ["id"] + } + }, "created_at": { "type": "string" }, "updated_at": { "type": "string" } }, "required": ["id"] }, - "V1Backup": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": ["COMPLETED", "FAILED", "PENDING", "REMOVED", "ARCHIVED", "CANCELLED"] - }, - "is_physical_backup": { "type": "boolean" }, - "inserted_at": { "type": "string" } - }, - "required": ["status", "is_physical_backup", "inserted_at"] - }, - "V1PhysicalBackup": { - "type": "object", - "properties": { - "earliest_physical_backup_date_unix": { "type": "integer", "format": "int64" }, - "latest_physical_backup_date_unix": { "type": "integer", "format": "int64" } - } - }, "V1BackupsResponse": { "type": "object", "properties": { "region": { "type": "string" }, "walg_enabled": { "type": "boolean" }, "pitr_enabled": { "type": "boolean" }, - "backups": { "type": "array", "items": { "$ref": "#/components/schemas/V1Backup" } }, - "physical_backup_data": { "$ref": "#/components/schemas/V1PhysicalBackup" } + "backups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "is_physical_backup": { "type": "boolean" }, + "status": { + "type": "string", + "enum": ["COMPLETED", "FAILED", "PENDING", "REMOVED", "ARCHIVED", "CANCELLED"] + }, + "inserted_at": { "type": "string" } + }, + "required": ["is_physical_backup", "status", "inserted_at"] + } + }, + "physical_backup_data": { + "type": "object", + "properties": { + "earliest_physical_backup_date_unix": { "type": "integer" }, + "latest_physical_backup_date_unix": { "type": "integer" } + } + } }, "required": ["region", "walg_enabled", "pitr_enabled", "backups", "physical_backup_data"] }, @@ -5234,6 +7074,24 @@ }, "required": ["recovery_time_target_unix"] }, + "V1RestorePointPostBody": { + "type": "object", + "properties": { "name": { "type": "string", "maxLength": 20 } }, + "required": ["name"] + }, + "V1RestorePointResponse": { + "type": "object", + "properties": { + "name": { "type": "string" }, + "status": { "type": "string", "enum": ["AVAILABLE", "PENDING", "REMOVED"] } + }, + "required": ["name", "status"] + }, + "V1UndoBody": { + "type": "object", + "properties": { "name": { "type": "string", "maxLength": 20 } }, + "required": ["name"] + }, "V1OrganizationMemberResponse": { "type": "object", "properties": { @@ -5245,27 +7103,106 @@ }, "required": ["user_id", "user_name", "role_name", "mfa_enabled"] }, - "BillingPlanId": { "type": "string", "enum": ["free", "pro", "team", "enterprise"] }, - "ReleaseChannel": { - "type": "string", - "enum": ["internal", "alpha", "beta", "ga", "withdrawn", "preview"] - }, "V1OrganizationSlugResponse": { "type": "object", "properties": { - "plan": { "$ref": "#/components/schemas/BillingPlanId" }, + "id": { "type": "string" }, + "name": { "type": "string" }, + "plan": { "type": "string", "enum": ["free", "pro", "team", "enterprise"] }, "opt_in_tags": { "type": "array", - "items": { "type": "string", "enum": ["AI_SQL_GENERATOR_OPT_IN"] } + "items": { + "type": "string", + "enum": [ + "AI_SQL_GENERATOR_OPT_IN", + "AI_DATA_GENERATOR_OPT_IN", + "AI_LOG_GENERATOR_OPT_IN" + ] + } }, "allowed_release_channels": { "type": "array", - "items": { "$ref": "#/components/schemas/ReleaseChannel" } - }, - "id": { "type": "string" }, - "name": { "type": "string" } + "items": { + "type": "string", + "enum": ["internal", "alpha", "beta", "ga", "withdrawn", "preview"] + } + } }, - "required": ["opt_in_tags", "allowed_release_channels", "id", "name"] + "required": ["id", "name", "opt_in_tags", "allowed_release_channels"] + }, + "OrganizationProjectClaimResponse": { + "type": "object", + "properties": { + "project": { + "type": "object", + "properties": { "ref": { "type": "string" }, "name": { "type": "string" } }, + "required": ["ref", "name"] + }, + "preview": { + "type": "object", + "properties": { + "valid": { "type": "boolean" }, + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { "key": { "type": "string" }, "message": { "type": "string" } }, + "required": ["key", "message"] + } + }, + "errors": { + "type": "array", + "items": { + "type": "object", + "properties": { "key": { "type": "string" }, "message": { "type": "string" } }, + "required": ["key", "message"] + } + }, + "info": { + "type": "array", + "items": { + "type": "object", + "properties": { "key": { "type": "string" }, "message": { "type": "string" } }, + "required": ["key", "message"] + } + }, + "members_exceeding_free_project_limit": { + "type": "array", + "items": { + "type": "object", + "properties": { "name": { "type": "string" }, "limit": { "type": "number" } }, + "required": ["name", "limit"] + } + }, + "target_organization_eligible": { "type": "boolean", "nullable": true }, + "target_organization_has_free_project_slots": { "type": "boolean", "nullable": true }, + "source_subscription_plan": { + "type": "string", + "enum": ["free", "pro", "team", "enterprise"] + }, + "target_subscription_plan": { + "type": "string", + "enum": ["free", "pro", "team", "enterprise"], + "nullable": true + } + }, + "required": [ + "valid", + "warnings", + "errors", + "info", + "members_exceeding_free_project_limit", + "target_organization_eligible", + "target_organization_has_free_project_slots", + "source_subscription_plan", + "target_subscription_plan" + ] + }, + "expires_at": { "type": "string" }, + "created_at": { "type": "string" }, + "created_by": { "type": "string", "format": "uuid" } + }, + "required": ["project", "preview", "expires_at", "created_at", "created_by"] } } } diff --git a/apps/docs/spec/common-api-sections.json b/apps/docs/spec/common-api-sections.json index c2549bc3b09..733b6ffb814 100644 --- a/apps/docs/spec/common-api-sections.json +++ b/apps/docs/spec/common-api-sections.json @@ -6,6 +6,48 @@ "type": "markdown", "items": [] }, + { + "type": "category", + "title": "Advisors", + "items": [ + { + "id": "v1-get-performance-advisors", + "title": "Get performance advisors", + "slug": "v1-get-performance-advisors", + "type": "operation" + }, + { + "id": "v1-get-security-advisors", + "title": "Get security advisors", + "slug": "v1-get-security-advisors", + "type": "operation" + } + ] + }, + { + "type": "category", + "title": "Analytics", + "items": [ + { + "id": "v1-get-project-logs", + "title": "Get project logs", + "slug": "v1-get-project-logs", + "type": "operation" + }, + { + "id": "v1-get-project-usage-api-count", + "title": "Get project usage api count", + "slug": "v1-get-project-usage-api-count", + "type": "operation" + }, + { + "id": "v1-get-project-usage-request-count", + "title": "Get project usage request count", + "slug": "v1-get-project-usage-request-count", + "type": "operation" + } + ] + }, { "type": "category", "title": "Auth", @@ -16,12 +58,36 @@ "slug": "v1-create-a-sso-provider", "type": "operation" }, + { + "id": "v1-create-legacy-signing-key", + "title": "Create legacy signing key", + "slug": "v1-create-legacy-signing-key", + "type": "operation" + }, + { + "id": "v1-create-project-signing-key", + "title": "Create project signing key", + "slug": "v1-create-project-signing-key", + "type": "operation" + }, + { + "id": "v1-create-project-tpa-integration", + "title": "Create project tpa integration", + "slug": "v1-create-project-tpa-integration", + "type": "operation" + }, { "id": "v1-delete-a-sso-provider", "title": "Delete a sso provider", "slug": "v1-delete-a-sso-provider", "type": "operation" }, + { + "id": "v1-delete-project-tpa-integration", + "title": "Delete project tpa integration", + "slug": "v1-delete-project-tpa-integration", + "type": "operation" + }, { "id": "v1-get-a-sso-provider", "title": "Get a sso provider", @@ -34,12 +100,48 @@ "slug": "v1-get-auth-service-config", "type": "operation" }, + { + "id": "v1-get-legacy-signing-key", + "title": "Get legacy signing key", + "slug": "v1-get-legacy-signing-key", + "type": "operation" + }, + { + "id": "v1-get-project-signing-key", + "title": "Get project signing key", + "slug": "v1-get-project-signing-key", + "type": "operation" + }, + { + "id": "v1-get-project-signing-keys", + "title": "Get project signing keys", + "slug": "v1-get-project-signing-keys", + "type": "operation" + }, + { + "id": "v1-get-project-tpa-integration", + "title": "Get project tpa integration", + "slug": "v1-get-project-tpa-integration", + "type": "operation" + }, { "id": "v1-list-all-sso-provider", "title": "List all sso provider", "slug": "v1-list-all-sso-provider", "type": "operation" }, + { + "id": "v1-list-project-tpa-integrations", + "title": "List project tpa integrations", + "slug": "v1-list-project-tpa-integrations", + "type": "operation" + }, + { + "id": "v1-remove-project-signing-key", + "title": "Remove project signing key", + "slug": "v1-remove-project-signing-key", + "type": "operation" + }, { "id": "v1-update-a-sso-provider", "title": "Update a sso provider", @@ -51,6 +153,36 @@ "title": "Update auth service config", "slug": "v1-update-auth-service-config", "type": "operation" + }, + { + "id": "v1-update-project-signing-key", + "title": "Update project signing key", + "slug": "v1-update-project-signing-key", + "type": "operation" + } + ] + }, + { + "type": "category", + "title": "Billing", + "items": [ + { + "id": "v1-apply-project-addon", + "title": "Apply project addon", + "slug": "v1-apply-project-addon", + "type": "operation" + }, + { + "id": "v1-list-project-addons", + "title": "List project addons", + "slug": "v1-list-project-addons", + "type": "operation" + }, + { + "id": "v1-remove-project-addon", + "title": "Remove project addon", + "slug": "v1-remove-project-addon", + "type": "operation" } ] }, @@ -64,6 +196,12 @@ "slug": "v1-apply-a-migration", "type": "operation" }, + { + "id": "v1-create-restore-point", + "title": "Create restore point", + "slug": "v1-create-restore-point", + "type": "operation" + }, { "id": "v1-disable-readonly-mode-temporarily", "title": "Disable readonly mode temporarily", @@ -88,6 +226,18 @@ "slug": "v1-get-a-snippet", "type": "operation" }, + { + "id": "v1-get-database-metadata", + "title": "Get database metadata", + "slug": "v1-get-database-metadata", + "type": "operation" + }, + { + "id": "v1-get-pooler-config", + "title": "Get pooler config", + "slug": "v1-get-pooler-config", + "type": "operation" + }, { "id": "v1-get-postgres-config", "title": "Get postgres config", @@ -106,6 +256,12 @@ "slug": "v1-get-readonly-mode-status", "type": "operation" }, + { + "id": "v1-get-restore-point", + "title": "Get restore point", + "slug": "v1-get-restore-point", + "type": "operation" + }, { "id": "v1-get-ssl-enforcement-config", "title": "Get ssl enforcement config", @@ -125,9 +281,9 @@ "type": "operation" }, { - "id": "v1-list-migrations", - "title": "List migrations", - "slug": "v1-list-migrations", + "id": "v1-list-migration-history", + "title": "List migration history", + "slug": "v1-list-migration-history", "type": "operation" }, { @@ -154,6 +310,18 @@ "slug": "v1-setup-a-read-replica", "type": "operation" }, + { + "id": "v1-undo", + "title": "Undo", + "slug": "v1-undo", + "type": "operation" + }, + { + "id": "v1-update-pooler-config", + "title": "Update pooler config", + "slug": "v1-update-pooler-config", + "type": "operation" + }, { "id": "v1-update-postgres-config", "title": "Update postgres config", @@ -165,6 +333,12 @@ "title": "Update ssl enforcement config", "slug": "v1-update-ssl-enforcement-config", "type": "operation" + }, + { + "id": "v1-upsert-a-migration", + "title": "Upsert a migration", + "slug": "v1-upsert-a-migration", + "type": "operation" } ] }, @@ -292,12 +466,24 @@ "slug": "v1-delete-a-branch", "type": "operation" }, + { + "id": "v1-diff-a-branch", + "title": "Diff a branch", + "slug": "v1-diff-a-branch", + "type": "operation" + }, { "id": "v1-disable-preview-branching", "title": "Disable preview branching", "slug": "v1-disable-preview-branching", "type": "operation" }, + { + "id": "v1-get-a-branch", + "title": "Get a branch", + "slug": "v1-get-a-branch", + "type": "operation" + }, { "id": "v1-get-a-branch-config", "title": "Get a branch config", @@ -352,6 +538,12 @@ "slug": "v1-exchange-oauth-token", "type": "operation" }, + { + "id": "v1-oauth-authorize-project-claim", + "title": "Oauth authorize project claim", + "slug": "v1-oauth-authorize-project-claim", + "type": "operation" + }, { "id": "v1-revoke-token", "title": "Revoke token", @@ -364,6 +556,12 @@ "type": "category", "title": "Organizations", "items": [ + { + "id": "v1-claim-project-for-organization", + "title": "Claim project for organization", + "slug": "v1-claim-project-for-organization", + "type": "operation" + }, { "id": "v1-create-an-organization", "title": "Create an organization", @@ -376,6 +574,12 @@ "slug": "v1-get-an-organization", "type": "operation" }, + { + "id": "v1-get-organization-project-claim", + "title": "Get organization project claim", + "slug": "v1-get-organization-project-claim", + "type": "operation" + }, { "id": "v1-list-all-organizations", "title": "List all organizations", @@ -406,6 +610,12 @@ "slug": "v1-create-a-project", "type": "operation" }, + { + "id": "v1-create-project-claim-token", + "title": "Create project claim token", + "slug": "v1-create-project-claim-token", + "type": "operation" + }, { "id": "v1-delete-a-project", "title": "Delete a project", @@ -418,6 +628,12 @@ "slug": "v1-delete-network-bans", "type": "operation" }, + { + "id": "v1-delete-project-claim-token", + "title": "Delete project claim token", + "slug": "v1-delete-project-claim-token", + "type": "operation" + }, { "id": "v1-get-network-restrictions", "title": "Get network restrictions", @@ -442,6 +658,12 @@ "slug": "v1-get-project", "type": "operation" }, + { + "id": "v1-get-project-claim-token", + "title": "Get project claim token", + "slug": "v1-get-project-claim-token", + "type": "operation" + }, { "id": "v1-get-services-health", "title": "Get services health", @@ -454,6 +676,12 @@ "slug": "v1-list-all-network-bans", "type": "operation" }, + { + "id": "v1-list-all-network-bans-enriched", + "title": "List all network bans enriched", + "slug": "v1-list-all-network-bans-enriched", + "type": "operation" + }, { "id": "v1-list-all-projects", "title": "List all projects", @@ -526,18 +754,42 @@ "slug": "v1-bulk-delete-secrets", "type": "operation" }, + { + "id": "v1-create-project-api-key", + "title": "Create project api key", + "slug": "v1-create-project-api-key", + "type": "operation" + }, + { + "id": "v1-delete-project-api-key", + "title": "Delete project api key", + "slug": "v1-delete-project-api-key", + "type": "operation" + }, { "id": "v1-get-pgsodium-config", "title": "Get pgsodium config", "slug": "v1-get-pgsodium-config", "type": "operation" }, + { + "id": "v1-get-project-api-key", + "title": "Get project api key", + "slug": "v1-get-project-api-key", + "type": "operation" + }, { "id": "v1-get-project-api-keys", "title": "Get project api keys", "slug": "v1-get-project-api-keys", "type": "operation" }, + { + "id": "v1-get-project-legacy-api-keys", + "title": "Get project legacy api keys", + "slug": "v1-get-project-legacy-api-keys", + "type": "operation" + }, { "id": "v1-list-all-secrets", "title": "List all secrets", @@ -549,6 +801,18 @@ "title": "Update pgsodium config", "slug": "v1-update-pgsodium-config", "type": "operation" + }, + { + "id": "v1-update-project-api-key", + "title": "Update project api key", + "slug": "v1-update-project-api-key", + "type": "operation" + }, + { + "id": "v1-update-project-legacy-api-keys", + "title": "Update project legacy api keys", + "slug": "v1-update-project-legacy-api-keys", + "type": "operation" } ] }, diff --git a/apps/docs/spec/transforms/api_v1_openapi_deparsed.json b/apps/docs/spec/transforms/api_v1_openapi_deparsed.json index 9815cec79d5..3231de60004 100644 --- a/apps/docs/spec/transforms/api_v1_openapi_deparsed.json +++ b/apps/docs/spec/transforms/api_v1_openapi_deparsed.json @@ -8,10 +8,22 @@ }, "servers": [], "tags": [ + { + "name": "Advisors", + "description": "Advisors related endpoints" + }, + { + "name": "Analytics", + "description": "Analytics related endpoints" + }, { "name": "Auth", "description": "Auth related endpoints" }, + { + "name": "Billing", + "description": "Billing related endpoints" + }, { "name": "Database", "description": "Database related endpoints" @@ -64,7 +76,9 @@ "name": "branch_id", "required": true, "in": "path", + "description": "Branch ID", "schema": { + "format": "uuid", "type": "string" } } @@ -113,7 +127,9 @@ "type": "string" }, "db_port": { - "type": "number" + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true }, "db_user": { "type": "string" @@ -158,7 +174,9 @@ "name": "branch_id", "required": true, "in": "path", + "description": "Branch ID", "schema": { + "format": "uuid", "type": "string" } } @@ -194,6 +212,9 @@ "FUNCTIONS_DEPLOYED", "FUNCTIONS_FAILED" ] + }, + "request_review": { + "type": "boolean" } } } @@ -209,7 +230,8 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "name": { "type": "string" @@ -250,10 +272,16 @@ ] }, "created_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "updated_at": { - "type": "string" + "type": "string", + "format": "date-time" + }, + "review_requested_at": { + "type": "string", + "format": "date-time" } }, "required": [ @@ -291,7 +319,9 @@ "name": "branch_id", "required": true, "in": "path", + "description": "Branch ID", "schema": { + "format": "uuid", "type": "string" } } @@ -336,11 +366,28 @@ "name": "branch_id", "required": true, "in": "path", + "description": "Branch ID", "schema": { + "format": "uuid", "type": "string" } } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "migration_version": { + "type": "string" + } + } + } + } + } + }, "responses": { "201": { "description": "", @@ -384,11 +431,28 @@ "name": "branch_id", "required": true, "in": "path", + "description": "Branch ID", "schema": { + "format": "uuid", "type": "string" } } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "migration_version": { + "type": "string" + } + } + } + } + } + }, "responses": { "201": { "description": "", @@ -432,11 +496,28 @@ "name": "branch_id", "required": true, "in": "path", + "description": "Branch ID", "schema": { + "format": "uuid", "type": "string" } } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "migration_version": { + "type": "string" + } + } + } + } + } + }, "responses": { "201": { "description": "", @@ -470,6 +551,54 @@ ] } }, + "/v1/branches/{branch_id}/diff": { + "get": { + "operationId": "v1-diff-a-branch", + "summary": "[Beta] Diffs a database branch", + "description": "Diffs the specified database branch", + "parameters": [ + { + "name": "branch_id", + "required": true, + "in": "path", + "description": "Branch ID", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "included_schemas", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "text/plain": { + "schema": { + "type": "string" + } + } + }, + "description": "" + }, + "500": { + "description": "Failed to diff database branch" + } + }, + "tags": ["Environments"], + "security": [ + { + "bearer": [] + } + ] + } + }, "/v1/projects": { "get": { "operationId": "v1-list-all-projects", @@ -590,6 +719,7 @@ }, "name": { "type": "string", + "maxLength": 256, "description": "Name of your project" }, "organization_id": { @@ -634,6 +764,8 @@ "desired_instance_size": { "type": "string", "enum": [ + "pico", + "nano", "micro", "small", "medium", @@ -643,7 +775,15 @@ "4xlarge", "8xlarge", "12xlarge", - "16xlarge" + "16xlarge", + "24xlarge", + "24xlarge_optimized_memory", + "24xlarge_optimized_cpu", + "24xlarge_high_memory", + "48xlarge", + "48xlarge_optimized_memory", + "48xlarge_optimized_cpu", + "48xlarge_high_memory" ] }, "template_url": { @@ -824,16 +964,33 @@ "summary": "[Beta] Authorize user through oauth", "parameters": [ { - "name": "code_challenge_method", - "required": false, + "name": "client_id", + "required": true, "in": "query", "schema": { - "enum": ["plain", "sha256", "S256"], + "format": "uuid", "type": "string" } }, { - "name": "code_challenge", + "name": "response_type", + "required": true, + "in": "query", + "schema": { + "enum": ["code", "token", "id_token token"], + "type": "string" + } + }, + { + "name": "redirect_uri", + "required": true, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "scope", "required": false, "in": "query", "schema": { @@ -849,32 +1006,43 @@ } }, { - "name": "response_type", - "required": true, + "name": "response_mode", + "required": false, "in": "query", "schema": { "type": "string" } }, { - "name": "redirect_uri", - "required": true, + "name": "code_challenge", + "required": false, "in": "query", "schema": { "type": "string" } }, { - "name": "client_id", - "required": true, + "name": "code_challenge_method", + "required": false, "in": "query", "schema": { + "enum": ["plain", "sha256", "S256"], + "type": "string" + } + }, + { + "name": "organization_slug", + "required": false, + "in": "query", + "description": "Organization slug", + "schema": { + "pattern": "^[\\w-]+$", "type": "string" } } ], "responses": { - "303": { + "204": { "description": "" } }, @@ -1006,11 +1174,115 @@ ] } }, + "/v1/oauth/authorize/project-claim": { + "get": { + "operationId": "v1-oauth-authorize-project-claim", + "x-internal": true, + "summary": "Authorize user through oauth and claim a project", + "description": "Initiates the OAuth authorization flow for the specified provider. After successful authentication, the user can claim ownership of the specified project.", + "parameters": [ + { + "name": "project_ref", + "required": true, + "in": "query", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + }, + { + "name": "client_id", + "required": true, + "in": "query", + "schema": { + "format": "uuid", + "type": "string" + } + }, + { + "name": "response_type", + "required": true, + "in": "query", + "schema": { + "enum": ["code", "token", "id_token token"], + "type": "string" + } + }, + { + "name": "redirect_uri", + "required": true, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "state", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "response_mode", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "code_challenge", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "code_challenge_method", + "required": false, + "in": "query", + "schema": { + "enum": ["plain", "sha256", "S256"], + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "" + } + }, + "tags": ["OAuth"], + "security": [ + { + "bearer": [] + } + ] + } + }, "/v1/snippets": { "get": { "operationId": "v1-list-all-snippets", "summary": "Lists SQL snippets for the logged in user", "parameters": [ + { + "name": "project_ref", + "required": false, + "in": "query", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + }, { "name": "cursor", "required": false, @@ -1046,14 +1318,6 @@ "enum": ["asc", "desc"], "type": "string" } - }, - { - "name": "project_ref", - "required": false, - "in": "query", - "schema": { - "type": "string" - } } ], "responses": { @@ -1097,8 +1361,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "number" }, "name": { "type": "string" @@ -1110,8 +1373,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "number" }, "username": { "type": "string" @@ -1123,8 +1385,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "number" }, "username": { "type": "string" @@ -1205,8 +1466,8 @@ "enum": ["sql"] }, "visibility": { - "enum": ["user", "project", "org", "public"], - "type": "string" + "type": "string", + "enum": ["user", "project", "org", "public"] }, "name": { "type": "string" @@ -1219,8 +1480,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "number" }, "name": { "type": "string" @@ -1232,8 +1492,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "number" }, "username": { "type": "string" @@ -1245,8 +1504,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "number" }, "username": { "type": "string" @@ -1304,21 +1562,26 @@ "operationId": "v1-get-project-api-keys", "summary": "Get project api keys", "parameters": [ - { - "name": "reveal", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, { "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "reveal", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { + "type": "boolean" + } } ], "responses": { @@ -1331,11 +1594,9 @@ "items": { "type": "object", "properties": { - "name": { - "type": "string" - }, "api_key": { - "type": "string" + "type": "string", + "nullable": true }, "id": { "type": "string", @@ -1343,13 +1604,16 @@ }, "type": { "type": "string", - "enum": ["publishable", "secret", "legacy"], + "enum": ["legacy", "publishable", "secret"], "nullable": true }, "prefix": { "type": "string", "nullable": true }, + "name": { + "type": "string" + }, "description": { "type": "string", "nullable": true @@ -1380,7 +1644,7 @@ "nullable": true } }, - "required": ["name", "api_key"] + "required": ["name"] } } } @@ -1398,24 +1662,29 @@ ] }, "post": { - "operationId": "createApiKey", - "summary": "[Alpha] Creates a new API key for the project", + "operationId": "v1-create-project-api-key", + "summary": "Creates a new API key for the project", "parameters": [ - { - "name": "reveal", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, { "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } + }, + { + "name": "reveal", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { + "type": "boolean" + } } ], "requestBody": { @@ -1429,6 +1698,12 @@ "type": "string", "enum": ["publishable", "secret"] }, + "name": { + "type": "string", + "minLength": 4, + "maxLength": 64, + "pattern": "^[a-z_][a-z0-9_]+$" + }, "description": { "type": "string", "nullable": true @@ -1445,7 +1720,7 @@ "nullable": true } }, - "required": ["type"] + "required": ["type", "name"] } } } @@ -1458,11 +1733,9 @@ "schema": { "type": "object", "properties": { - "name": { - "type": "string" - }, "api_key": { - "type": "string" + "type": "string", + "nullable": true }, "id": { "type": "string", @@ -1470,13 +1743,16 @@ }, "type": { "type": "string", - "enum": ["publishable", "secret", "legacy"], + "enum": ["legacy", "publishable", "secret"], "nullable": true }, "prefix": { "type": "string", "nullable": true }, + "name": { + "type": "string" + }, "description": { "type": "string", "nullable": true @@ -1507,7 +1783,108 @@ "nullable": true } }, - "required": ["name", "api_key"] + "required": ["name"] + } + } + } + }, + "403": { + "description": "" + } + }, + "tags": ["Secrets"], + "security": [ + { + "bearer": [] + } + ] + } + }, + "/v1/projects/{ref}/api-keys/legacy": { + "get": { + "operationId": "v1-get-project-legacy-api-keys", + "summary": "Check whether JWT based legacy (anon, service_role) API keys are enabled. This API endpoint will be removed in the future, check for HTTP 404 Not Found.", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": ["enabled"] + } + } + } + }, + "403": { + "description": "" + } + }, + "tags": ["Secrets"], + "security": [ + { + "bearer": [] + } + ] + }, + "put": { + "operationId": "v1-update-project-legacy-api-keys", + "summary": "Disable or re-enable JWT based legacy (anon, service_role) API keys. This API endpoint will be removed in the future, check for HTTP 404 Not Found.", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + }, + { + "name": "enabled", + "required": true, + "in": "query", + "description": "Boolean string, true or false", + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": ["enabled"] } } } @@ -1526,15 +1903,19 @@ }, "/v1/projects/{ref}/api-keys/{id}": { "patch": { - "operationId": "updateApiKey", - "summary": "[Alpha] Updates an API key for the project", + "operationId": "v1-update-project-api-key", + "summary": "Updates an API key for the project", "parameters": [ { - "name": "reveal", - "required": false, - "in": "query", + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", "schema": { - "type": "boolean" + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" } }, { @@ -1542,15 +1923,17 @@ "required": true, "in": "path", "schema": { + "format": "uuid", "type": "string" } }, { - "name": "ref", - "required": true, - "in": "path", + "name": "reveal", + "required": false, + "in": "query", + "description": "Boolean string, true or false", "schema": { - "type": "string" + "type": "boolean" } } ], @@ -1561,6 +1944,12 @@ "schema": { "type": "object", "properties": { + "name": { + "type": "string", + "minLength": 4, + "maxLength": 64, + "pattern": "^[a-z_][a-z0-9_]+$" + }, "description": { "type": "string", "nullable": true @@ -1589,11 +1978,9 @@ "schema": { "type": "object", "properties": { - "name": { - "type": "string" - }, "api_key": { - "type": "string" + "type": "string", + "nullable": true }, "id": { "type": "string", @@ -1601,13 +1988,16 @@ }, "type": { "type": "string", - "enum": ["publishable", "secret", "legacy"], + "enum": ["legacy", "publishable", "secret"], "nullable": true }, "prefix": { "type": "string", "nullable": true }, + "name": { + "type": "string" + }, "description": { "type": "string", "nullable": true @@ -1638,7 +2028,7 @@ "nullable": true } }, - "required": ["name", "api_key"] + "required": ["name"] } } } @@ -1655,15 +2045,19 @@ ] }, "get": { - "operationId": "getApiKey", - "summary": "[Alpha] Get API key", + "operationId": "v1-get-project-api-key", + "summary": "Get API key", "parameters": [ { - "name": "reveal", - "required": false, - "in": "query", + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", "schema": { - "type": "boolean" + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" } }, { @@ -1671,15 +2065,17 @@ "required": true, "in": "path", "schema": { + "format": "uuid", "type": "string" } }, { - "name": "ref", - "required": true, - "in": "path", + "name": "reveal", + "required": false, + "in": "query", + "description": "Boolean string, true or false", "schema": { - "type": "string" + "type": "boolean" } } ], @@ -1691,11 +2087,9 @@ "schema": { "type": "object", "properties": { - "name": { - "type": "string" - }, "api_key": { - "type": "string" + "type": "string", + "nullable": true }, "id": { "type": "string", @@ -1703,13 +2097,16 @@ }, "type": { "type": "string", - "enum": ["publishable", "secret", "legacy"], + "enum": ["legacy", "publishable", "secret"], "nullable": true }, "prefix": { "type": "string", "nullable": true }, + "name": { + "type": "string" + }, "description": { "type": "string", "nullable": true @@ -1740,7 +2137,7 @@ "nullable": true } }, - "required": ["name", "api_key"] + "required": ["name"] } } } @@ -1757,15 +2154,19 @@ ] }, "delete": { - "operationId": "deleteApiKey", - "summary": "[Alpha] Deletes an API key for the project", + "operationId": "v1-delete-project-api-key", + "summary": "Deletes an API key for the project", "parameters": [ { - "name": "reveal", - "required": false, - "in": "query", + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", "schema": { - "type": "boolean" + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" } }, { @@ -1773,13 +2174,32 @@ "required": true, "in": "path", "schema": { + "format": "uuid", "type": "string" } }, { - "name": "ref", - "required": true, - "in": "path", + "name": "reveal", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { + "type": "boolean" + } + }, + { + "name": "was_compromised", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { + "type": "boolean" + } + }, + { + "name": "reason", + "required": false, + "in": "query", "schema": { "type": "string" } @@ -1793,11 +2213,9 @@ "schema": { "type": "object", "properties": { - "name": { - "type": "string" - }, "api_key": { - "type": "string" + "type": "string", + "nullable": true }, "id": { "type": "string", @@ -1805,13 +2223,16 @@ }, "type": { "type": "string", - "enum": ["publishable", "secret", "legacy"], + "enum": ["legacy", "publishable", "secret"], "nullable": true }, "prefix": { "type": "string", "nullable": true }, + "name": { + "type": "string" + }, "description": { "type": "string", "nullable": true @@ -1842,7 +2263,7 @@ "nullable": true } }, - "required": ["name", "api_key"] + "required": ["name"] } } } @@ -1869,7 +2290,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -1885,7 +2310,8 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "name": { "type": "string" @@ -1926,10 +2352,16 @@ ] }, "created_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "updated_at": { - "type": "string" + "type": "string", + "format": "date-time" + }, + "review_requested_at": { + "type": "string", + "format": "date-time" } }, "required": [ @@ -1971,7 +2403,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -1990,6 +2426,9 @@ "git_branch": { "type": "string" }, + "is_default": { + "type": "boolean" + }, "persistent": { "type": "boolean" }, @@ -2010,7 +2449,15 @@ "4xlarge", "8xlarge", "12xlarge", - "16xlarge" + "16xlarge", + "24xlarge", + "24xlarge_optimized_memory", + "24xlarge_optimized_cpu", + "24xlarge_high_memory", + "48xlarge", + "48xlarge_optimized_memory", + "48xlarge_optimized_cpu", + "48xlarge_high_memory" ] }, "release_channel": { @@ -2020,7 +2467,7 @@ }, "postgres_engine": { "type": "string", - "enum": ["15", "17-oriole"], + "enum": ["15", "17", "17-oriole"], "description": "Postgres engine version. If not provided, the latest version will be used." }, "secrets": { @@ -2028,6 +2475,9 @@ "additionalProperties": { "type": "string" } + }, + "with_data": { + "type": "boolean" } }, "required": ["branch_name"] @@ -2044,7 +2494,8 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "name": { "type": "string" @@ -2085,10 +2536,16 @@ ] }, "created_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "updated_at": { - "type": "string" + "type": "string", + "format": "date-time" + }, + "review_requested_at": { + "type": "string", + "format": "date-time" } }, "required": [ @@ -2129,7 +2586,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -2153,6 +2614,126 @@ ] } }, + "/v1/projects/{ref}/branches/{name}": { + "get": { + "operationId": "v1-get-a-branch", + "summary": "Get a database branch", + "description": "Fetches the specified database branch by its name.", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + }, + { + "name": "name", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "project_ref": { + "type": "string" + }, + "parent_project_ref": { + "type": "string" + }, + "is_default": { + "type": "boolean" + }, + "git_branch": { + "type": "string" + }, + "pr_number": { + "type": "integer", + "format": "int32" + }, + "latest_check_run_id": { + "type": "number", + "description": "This field is deprecated and will not be populated.", + "deprecated": true + }, + "persistent": { + "type": "boolean" + }, + "status": { + "type": "string", + "enum": [ + "CREATING_PROJECT", + "RUNNING_MIGRATIONS", + "MIGRATIONS_PASSED", + "MIGRATIONS_FAILED", + "FUNCTIONS_DEPLOYED", + "FUNCTIONS_FAILED" + ] + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "review_requested_at": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "id", + "name", + "project_ref", + "parent_project_ref", + "is_default", + "persistent", + "status", + "created_at", + "updated_at" + ] + } + } + } + }, + "403": { + "description": "" + }, + "500": { + "description": "Failed to fetch database branch" + } + }, + "tags": ["Environments"], + "security": [ + { + "bearer": [] + } + ] + } + }, "/v1/projects/{ref}/custom-hostname": { "get": { "operationId": "v1-get-hostname-config", @@ -2166,6 +2747,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -2179,14 +2761,14 @@ "type": "object", "properties": { "status": { + "type": "string", "enum": [ "1_not_started", "2_initiated", "3_challenge_verified", "4_origin_setup_completed", "5_services_reconfigured" - ], - "type": "string" + ] }, "custom_hostname": { "type": "string" @@ -2200,13 +2782,13 @@ "errors": { "type": "array", "items": { - "type": "object" + "description": "Any JSON-serializable value" } }, "messages": { "type": "array", "items": { - "type": "object" + "description": "Any JSON-serializable value" } }, "result": { @@ -2326,6 +2908,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -2362,6 +2945,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -2391,14 +2975,14 @@ "type": "object", "properties": { "status": { + "type": "string", "enum": [ "1_not_started", "2_initiated", "3_challenge_verified", "4_origin_setup_completed", "5_services_reconfigured" - ], - "type": "string" + ] }, "custom_hostname": { "type": "string" @@ -2412,13 +2996,13 @@ "errors": { "type": "array", "items": { - "type": "object" + "description": "Any JSON-serializable value" } }, "messages": { "type": "array", "items": { - "type": "object" + "description": "Any JSON-serializable value" } }, "result": { @@ -2540,6 +3124,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -2553,14 +3138,14 @@ "type": "object", "properties": { "status": { + "type": "string", "enum": [ "1_not_started", "2_initiated", "3_challenge_verified", "4_origin_setup_completed", "5_services_reconfigured" - ], - "type": "string" + ] }, "custom_hostname": { "type": "string" @@ -2574,13 +3159,13 @@ "errors": { "type": "array", "items": { - "type": "object" + "description": "Any JSON-serializable value" } }, "messages": { "type": "array", "items": { - "type": "object" + "description": "Any JSON-serializable value" } }, "result": { @@ -2702,6 +3287,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -2715,14 +3301,14 @@ "type": "object", "properties": { "status": { + "type": "string", "enum": [ "1_not_started", "2_initiated", "3_challenge_verified", "4_origin_setup_completed", "5_services_reconfigured" - ], - "type": "string" + ] }, "custom_hostname": { "type": "string" @@ -2736,13 +3322,13 @@ "errors": { "type": "array", "items": { - "type": "object" + "description": "Any JSON-serializable value" } }, "messages": { "type": "array", "items": { - "type": "object" + "description": "Any JSON-serializable value" } }, "result": { @@ -2860,7 +3446,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -2900,6 +3490,71 @@ ] } }, + "/v1/projects/{ref}/network-bans/retrieve/enriched": { + "post": { + "operationId": "v1-list-all-network-bans-enriched", + "summary": "[Beta] Gets project's network bans with additional information about which databases they affect", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "banned_ipv4_addresses": { + "type": "array", + "items": { + "type": "object", + "properties": { + "banned_address": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": ["banned_address", "identifier", "type"] + } + } + }, + "required": ["banned_ipv4_addresses"] + } + } + } + }, + "403": { + "description": "" + }, + "500": { + "description": "Failed to retrieve project's enriched network bans" + } + }, + "tags": ["Projects"], + "security": [ + { + "bearer": [] + } + ] + } + }, "/v1/projects/{ref}/network-bans": { "delete": { "operationId": "v1-delete-network-bans", @@ -2909,7 +3564,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -2926,6 +3585,9 @@ "items": { "type": "string" } + }, + "identifier": { + "type": "string" } }, "required": ["ipv4_addresses"] @@ -2961,7 +3623,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3048,7 +3714,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3159,7 +3829,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3203,7 +3877,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3267,6 +3945,9 @@ "in": "path", "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3326,6 +4007,9 @@ "in": "path", "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3414,6 +4098,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3530,6 +4215,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3543,8 +4229,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "integer" }, "ref": { "type": "string" @@ -3580,7 +4265,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3634,7 +4323,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3693,7 +4386,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3713,14 +4410,7 @@ }, "responses": { "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } + "description": "" }, "403": { "description": "" @@ -3746,7 +4436,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3799,7 +4493,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3886,7 +4584,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3932,7 +4634,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -3980,7 +4686,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -4013,7 +4723,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -4075,7 +4789,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -4137,7 +4855,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -4203,7 +4925,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -4236,7 +4962,7 @@ "properties": { "postgres_version": { "type": "string", - "enum": ["15", "17-oriole"] + "enum": ["15", "17", "17-oriole"] }, "release_channel": { "type": "string", @@ -4249,12 +4975,6 @@ "required": ["postgres_version", "release_channel", "app_version"] } }, - "potential_breaking_changes": { - "type": "array", - "items": { - "type": "string" - } - }, "duration_estimate_hours": { "type": "number" }, @@ -4264,7 +4984,19 @@ "type": "string" } }, - "extension_dependent_objects": { + "objects_to_be_dropped": { + "type": "array", + "items": { + "type": "string" + } + }, + "unsupported_extensions": { + "type": "array", + "items": { + "type": "string" + } + }, + "user_defined_objects_in_internal_schemas": { "type": "array", "items": { "type": "string" @@ -4277,10 +5009,11 @@ "current_app_version_release_channel", "latest_app_version", "target_upgrade_versions", - "potential_breaking_changes", "duration_estimate_hours", "legacy_auth_custom_roles", - "extension_dependent_objects" + "objects_to_be_dropped", + "unsupported_extensions", + "user_defined_objects_in_internal_schemas" ] } } @@ -4307,17 +5040,21 @@ "summary": "[Beta] Gets the latest status of the project's upgrade", "parameters": [ { - "name": "tracking_id", - "required": false, - "in": "query", + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } }, { - "name": "ref", - "required": true, - "in": "path", + "name": "tracking_id", + "required": false, + "in": "query", "schema": { "type": "string" } @@ -4414,6 +5151,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -4469,6 +5207,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -4501,7 +5240,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -4572,7 +5315,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -4625,19 +5372,10 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } }, - { - "name": "timeout_ms", - "required": false, - "in": "query", - "schema": { - "minimum": 0, - "maximum": 10000, - "type": "integer" - } - }, { "name": "services", "required": true, @@ -4649,6 +5387,16 @@ "enum": ["auth", "db", "pooler", "realtime", "rest", "storage"] } } + }, + { + "name": "timeout_ms", + "required": false, + "in": "query", + "schema": { + "minimum": 0, + "maximum": 10000, + "type": "integer" + } } ], "responses": { @@ -4661,6 +5409,17 @@ "items": { "type": "object", "properties": { + "name": { + "type": "string", + "enum": ["auth", "db", "pooler", "realtime", "rest", "storage"] + }, + "healthy": { + "type": "boolean" + }, + "status": { + "type": "string", + "enum": ["COMING_UP", "ACTIVE_HEALTHY", "UNHEALTHY"] + }, "info": { "oneOf": [ { @@ -4669,32 +5428,33 @@ "name": { "type": "string", "enum": ["GoTrue"] + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" } }, - "required": ["name"] + "required": ["name", "version", "description"] }, { "type": "object", "properties": { + "healthy": { + "type": "boolean" + }, + "db_connected": { + "type": "boolean" + }, "connected_cluster": { "type": "integer" } }, - "required": ["connected_cluster"] + "required": ["healthy", "db_connected", "connected_cluster"] } ] }, - "name": { - "enum": ["auth", "db", "pooler", "realtime", "rest", "storage"], - "type": "string" - }, - "healthy": { - "type": "boolean" - }, - "status": { - "enum": ["COMING_UP", "ACTIVE_HEALTHY", "UNHEALTHY"], - "type": "string" - }, "error": { "type": "string" } @@ -4720,16 +5480,154 @@ ] } }, - "/v1/projects/{ref}/config/auth/signing-keys": { + "/v1/projects/{ref}/config/auth/signing-keys/legacy": { "post": { - "operationId": "createSigningKeyForProject", - "summary": "[Alpha] Create a new signing key for the project in standby status", + "operationId": "v1-create-legacy-signing-key", + "summary": "Set up the project's existing JWT secret as an in_use JWT signing key. This endpoint will be removed in the future always check for HTTP 404 Not Found.", "parameters": [ { "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + } + ], + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "algorithm": { + "type": "string", + "enum": ["EdDSA", "ES256", "RS256", "HS256"] + }, + "status": { + "type": "string", + "enum": ["in_use", "previously_used", "revoked", "standby"] + }, + "public_jwk": { + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + }, + "required": ["id", "algorithm", "status", "created_at", "updated_at"], + "additionalProperties": false + } + } + } + }, + "403": { + "description": "" + } + }, + "tags": ["Auth"], + "security": [ + { + "bearer": [] + } + ] + }, + "get": { + "operationId": "v1-get-legacy-signing-key", + "summary": "Get the signing key information for the JWT secret imported as signing key for this project. This endpoint will be removed in the future, check for HTTP 404 Not Found.", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "algorithm": { + "type": "string", + "enum": ["EdDSA", "ES256", "RS256", "HS256"] + }, + "status": { + "type": "string", + "enum": ["in_use", "previously_used", "revoked", "standby"] + }, + "public_jwk": { + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + }, + "required": ["id", "algorithm", "status", "created_at", "updated_at"], + "additionalProperties": false + } + } + } + }, + "403": { + "description": "" + } + }, + "tags": ["Auth"], + "security": [ + { + "bearer": [] + } + ] + } + }, + "/v1/projects/{ref}/config/auth/signing-keys": { + "post": { + "operationId": "v1-create-project-signing-key", + "summary": "Create a new signing key for the project in standby status", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -4748,6 +5646,109 @@ "status": { "type": "string", "enum": ["in_use", "standby"] + }, + "private_jwk": { + "discriminator": { + "propertyName": "kty" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "kty": { + "type": "string", + "enum": ["RSA"] + }, + "n": { + "type": "string" + }, + "e": { + "type": "string", + "enum": ["AQAB"] + }, + "d": { + "type": "string" + }, + "p": { + "type": "string" + }, + "q": { + "type": "string" + }, + "dp": { + "type": "string" + }, + "dq": { + "type": "string" + }, + "qi": { + "type": "string" + } + }, + "required": ["kty", "n", "e", "d", "p", "q", "dp", "dq", "qi"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "kty": { + "type": "string", + "enum": ["EC"] + }, + "crv": { + "type": "string", + "enum": ["P-256"] + }, + "x": { + "type": "string" + }, + "y": { + "type": "string" + }, + "d": { + "type": "string" + } + }, + "required": ["kty", "crv", "x", "y", "d"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "kty": { + "type": "string", + "enum": ["OKP"] + }, + "crv": { + "type": "string", + "enum": ["Ed25519"] + }, + "x": { + "type": "string" + }, + "d": { + "type": "string" + } + }, + "required": ["kty", "crv", "x", "d"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "kty": { + "type": "string", + "enum": ["oct"] + }, + "k": { + "type": "string", + "minLength": 16 + } + }, + "required": ["kty", "k"], + "additionalProperties": false + } + ] } }, "required": ["algorithm"], @@ -4758,22 +5759,37 @@ }, "responses": { "201": { - "description": "[Alpha] Create a new signing key for the project in standby status", + "description": "", "content": { "application/json": { "schema": { "type": "object", "properties": { + "id": { + "type": "string", + "format": "uuid" + }, "algorithm": { "type": "string", "enum": ["EdDSA", "ES256", "RS256", "HS256"] }, "status": { "type": "string", - "enum": ["in_use", "standby"] + "enum": ["in_use", "previously_used", "revoked", "standby"] + }, + "public_jwk": { + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" } }, - "required": ["algorithm"], + "required": ["id", "algorithm", "status", "created_at", "updated_at"], "additionalProperties": false } } @@ -4791,14 +5807,18 @@ ] }, "get": { - "operationId": "listSigningKeysForProject", - "summary": "[Alpha] List all signing keys for the project", + "operationId": "v1-get-project-signing-keys", + "summary": "List all signing keys for the project", "parameters": [ { "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -4865,14 +5885,15 @@ }, "/v1/projects/{ref}/config/auth/signing-keys/{id}": { "get": { - "operationId": "getSigningKeyForProject", - "summary": "[Alpha] Get information about a signing key", + "operationId": "v1-get-project-signing-key", + "summary": "Get information about a signing key", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { + "format": "uuid", "type": "string" } }, @@ -4880,7 +5901,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -4935,14 +5960,15 @@ ] }, "delete": { - "operationId": "deleteSigningKey", - "summary": "[Alpha] Remove a signing key from a project, where the status is previously_used", + "operationId": "v1-remove-project-signing-key", + "summary": "Remove a signing key from a project. Only possible if the key has been in revoked status for a while.", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { + "format": "uuid", "type": "string" } }, @@ -4950,7 +5976,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -5005,14 +6035,15 @@ ] }, "patch": { - "operationId": "patchSigningKey", - "summary": "[Alpha] Update a signing key, mainly its status", + "operationId": "v1-update-project-signing-key", + "summary": "Update a signing key, mainly its status", "parameters": [ { "name": "id", "required": true, "in": "path", "schema": { + "format": "uuid", "type": "string" } }, @@ -5020,7 +6051,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -5106,6 +6141,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -5142,6 +6178,15 @@ } }, "required": ["enabled"] + }, + "icebergCatalog": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": ["enabled"] } }, "required": ["imageTransformation", "s3Protocol"] @@ -5178,6 +6223,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -5192,7 +6238,7 @@ "fileSizeLimit": { "type": "integer", "minimum": 0, - "maximum": 53687091200, + "maximum": 536870912000, "format": "int64" }, "features": { @@ -5215,6 +6261,15 @@ } }, "required": ["enabled"] + }, + "icebergCatalog": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": ["enabled"] } }, "required": ["imageTransformation", "s3Protocol"] @@ -5256,6 +6311,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -5328,6 +6384,10 @@ "minimum": 0, "maximum": 262143 }, + "session_replication_role": { + "type": "string", + "enum": ["origin", "replica", "local"] + }, "shared_buffers": { "type": "string" }, @@ -5345,10 +6405,6 @@ }, "work_mem": { "type": "string" - }, - "session_replication_role": { - "enum": ["origin", "replica", "local"], - "type": "string" } } } @@ -5381,6 +6437,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -5452,6 +6509,10 @@ "minimum": 0, "maximum": 262143 }, + "session_replication_role": { + "type": "string", + "enum": ["origin", "replica", "local"] + }, "shared_buffers": { "type": "string" }, @@ -5472,10 +6533,6 @@ }, "restart_database": { "type": "boolean" - }, - "session_replication_role": { - "enum": ["origin", "replica", "local"], - "type": "string" } } } @@ -5550,6 +6607,10 @@ "minimum": 0, "maximum": 262143 }, + "session_replication_role": { + "type": "string", + "enum": ["origin", "replica", "local"] + }, "shared_buffers": { "type": "string" }, @@ -5567,10 +6628,6 @@ }, "work_mem": { "type": "string" - }, - "session_replication_role": { - "enum": ["origin", "replica", "local"], - "type": "string" } } } @@ -5605,6 +6662,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -5617,21 +6675,33 @@ "schema": { "type": "object", "properties": { - "pool_mode": { - "type": "string", - "enum": ["transaction", "session", "statement"] - }, "default_pool_size": { - "type": "number" + "type": "integer" }, "ignore_startup_parameters": { "type": "string" }, "max_client_conn": { - "type": "number" + "type": "integer" + }, + "pool_mode": { + "type": "string", + "enum": ["transaction", "session", "statement"] }, "connection_string": { "type": "string" + }, + "server_idle_timeout": { + "type": "integer" + }, + "server_lifetime": { + "type": "integer" + }, + "query_wait_timeout": { + "type": "integer" + }, + "reserve_pool_size": { + "type": "integer" } } } @@ -5650,7 +6720,7 @@ }, "/v1/projects/{ref}/config/database/pooler": { "get": { - "operationId": "getSupavisorConfig", + "operationId": "v1-get-pooler-config", "summary": "Gets project's supavisor config", "parameters": [ { @@ -5661,6 +6731,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -5675,16 +6746,33 @@ "items": { "type": "object", "properties": { + "identifier": { + "type": "string" + }, "database_type": { "type": "string", "enum": ["PRIMARY", "READ_REPLICA"] }, + "is_using_scram_auth": { + "type": "boolean" + }, + "db_user": { + "type": "string" + }, + "db_host": { + "type": "string" + }, "db_port": { "type": "integer" }, + "db_name": { + "type": "string" + }, + "connection_string": { + "type": "string" + }, "connectionString": { "type": "string", - "deprecated": true, "description": "Use connection_string instead" }, "default_pool_size": { @@ -5696,41 +6784,23 @@ "nullable": true }, "pool_mode": { - "enum": ["transaction", "session"], - "type": "string" - }, - "identifier": { - "type": "string" - }, - "is_using_scram_auth": { - "type": "boolean" - }, - "db_user": { - "type": "string" - }, - "db_host": { - "type": "string" - }, - "db_name": { - "type": "string" - }, - "connection_string": { - "type": "string" + "type": "string", + "enum": ["transaction", "session"] } }, "required": [ - "database_type", - "db_port", - "connectionString", - "default_pool_size", - "max_client_conn", - "pool_mode", "identifier", + "database_type", "is_using_scram_auth", "db_user", "db_host", + "db_port", "db_name", - "connection_string" + "connection_string", + "connectionString", + "default_pool_size", + "max_client_conn", + "pool_mode" ] } } @@ -5749,7 +6819,7 @@ ] }, "patch": { - "operationId": "updateSupavisorConfig", + "operationId": "v1-update-pooler-config", "summary": "Updates project's supavisor config", "parameters": [ { @@ -5760,6 +6830,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -5773,15 +6844,14 @@ "properties": { "default_pool_size": { "type": "integer", - "nullable": true, "minimum": 0, - "maximum": 1000 + "maximum": 3000, + "nullable": true }, "pool_mode": { - "enum": ["transaction", "session"], "type": "string", - "description": "Dedicated pooler mode for the project", - "deprecated": true + "enum": ["transaction", "session"], + "description": "Dedicated pooler mode for the project" } } } @@ -5837,6 +6907,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -5857,83 +6928,6 @@ "type": "integer", "nullable": true }, - "jwt_exp": { - "type": "integer", - "nullable": true - }, - "mailer_otp_exp": { - "type": "integer" - }, - "mailer_otp_length": { - "type": "integer", - "nullable": true - }, - "mfa_max_enrolled_factors": { - "type": "integer", - "nullable": true - }, - "mfa_phone_otp_length": { - "type": "integer" - }, - "mfa_phone_max_frequency": { - "type": "integer", - "nullable": true - }, - "password_min_length": { - "type": "integer", - "nullable": true - }, - "rate_limit_anonymous_users": { - "type": "integer", - "nullable": true - }, - "rate_limit_email_sent": { - "type": "integer", - "nullable": true - }, - "rate_limit_sms_sent": { - "type": "integer", - "nullable": true - }, - "rate_limit_token_refresh": { - "type": "integer", - "nullable": true - }, - "rate_limit_verify": { - "type": "integer", - "nullable": true - }, - "rate_limit_otp": { - "type": "integer", - "nullable": true - }, - "security_refresh_token_reuse_interval": { - "type": "integer", - "nullable": true - }, - "sessions_inactivity_timeout": { - "type": "integer", - "nullable": true - }, - "sessions_timebox": { - "type": "integer", - "nullable": true - }, - "sms_max_frequency": { - "type": "integer", - "nullable": true - }, - "sms_otp_exp": { - "type": "integer", - "nullable": true - }, - "sms_otp_length": { - "type": "integer" - }, - "smtp_max_frequency": { - "type": "integer", - "nullable": true - }, "disable_signup": { "type": "boolean", "nullable": true @@ -6206,6 +7200,10 @@ "type": "string", "nullable": true }, + "external_web3_solana_enabled": { + "type": "boolean", + "nullable": true + }, "external_zoom_client_id": { "type": "string", "nullable": true @@ -6278,6 +7276,22 @@ "type": "string", "nullable": true }, + "hook_before_user_created_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_before_user_created_uri": { + "type": "string", + "nullable": true + }, + "hook_before_user_created_secrets": { + "type": "string", + "nullable": true + }, + "jwt_exp": { + "type": "integer", + "nullable": true + }, "mailer_allow_unverified_email_sign_ins": { "type": "boolean", "nullable": true @@ -6286,6 +7300,13 @@ "type": "boolean", "nullable": true }, + "mailer_otp_exp": { + "type": "integer" + }, + "mailer_otp_length": { + "type": "integer", + "nullable": true + }, "mailer_secure_email_change_enabled": { "type": "boolean", "nullable": true @@ -6338,6 +7359,10 @@ "type": "string", "nullable": true }, + "mfa_max_enrolled_factors": { + "type": "integer", + "nullable": true + }, "mfa_totp_enroll_enabled": { "type": "boolean", "nullable": true @@ -6362,16 +7387,61 @@ "type": "boolean", "nullable": true }, + "mfa_phone_otp_length": { + "type": "integer" + }, "mfa_phone_template": { "type": "string", "nullable": true }, + "mfa_phone_max_frequency": { + "type": "integer", + "nullable": true + }, "password_hibp_enabled": { "type": "boolean", "nullable": true }, + "password_min_length": { + "type": "integer", + "nullable": true + }, "password_required_characters": { "type": "string", + "enum": [ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", + "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", + "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\\\:\"|<>?,./`~", + "" + ], + "nullable": true + }, + "rate_limit_anonymous_users": { + "type": "integer", + "nullable": true + }, + "rate_limit_email_sent": { + "type": "integer", + "nullable": true + }, + "rate_limit_sms_sent": { + "type": "integer", + "nullable": true + }, + "rate_limit_token_refresh": { + "type": "integer", + "nullable": true + }, + "rate_limit_verify": { + "type": "integer", + "nullable": true + }, + "rate_limit_otp": { + "type": "integer", + "nullable": true + }, + "rate_limit_web3": { + "type": "integer", "nullable": true }, "refresh_token_rotation_enabled": { @@ -6396,6 +7466,7 @@ }, "security_captcha_provider": { "type": "string", + "enum": ["turnstile", "hcaptcha"], "nullable": true }, "security_captcha_secret": { @@ -6406,10 +7477,18 @@ "type": "boolean", "nullable": true }, + "security_refresh_token_reuse_interval": { + "type": "integer", + "nullable": true + }, "security_update_password_require_reauthentication": { "type": "boolean", "nullable": true }, + "sessions_inactivity_timeout": { + "type": "integer", + "nullable": true + }, "sessions_single_per_user": { "type": "boolean", "nullable": true @@ -6418,6 +7497,10 @@ "type": "string", "nullable": true }, + "sessions_timebox": { + "type": "integer", + "nullable": true + }, "site_url": { "type": "string", "nullable": true @@ -6426,6 +7509,10 @@ "type": "boolean", "nullable": true }, + "sms_max_frequency": { + "type": "integer", + "nullable": true + }, "sms_messagebird_access_key": { "type": "string", "nullable": true @@ -6434,8 +7521,16 @@ "type": "string", "nullable": true }, + "sms_otp_exp": { + "type": "integer", + "nullable": true + }, + "sms_otp_length": { + "type": "integer" + }, "sms_provider": { "type": "string", + "enum": ["messagebird", "textlocal", "twilio", "twilio_verify", "vonage"], "nullable": true }, "sms_template": { @@ -6448,6 +7543,7 @@ }, "sms_test_otp_valid_until": { "type": "string", + "format": "date-time", "nullable": true }, "sms_textlocal_api_key": { @@ -6500,12 +7596,17 @@ }, "smtp_admin_email": { "type": "string", + "format": "email", "nullable": true }, "smtp_host": { "type": "string", "nullable": true }, + "smtp_max_frequency": { + "type": "integer", + "nullable": true + }, "smtp_pass": { "type": "string", "nullable": true @@ -6530,26 +7631,6 @@ "required": [ "api_max_request_duration", "db_max_pool_size", - "jwt_exp", - "mailer_otp_exp", - "mailer_otp_length", - "mfa_max_enrolled_factors", - "mfa_phone_otp_length", - "mfa_phone_max_frequency", - "password_min_length", - "rate_limit_anonymous_users", - "rate_limit_email_sent", - "rate_limit_sms_sent", - "rate_limit_token_refresh", - "rate_limit_verify", - "rate_limit_otp", - "security_refresh_token_reuse_interval", - "sessions_inactivity_timeout", - "sessions_timebox", - "sms_max_frequency", - "sms_otp_exp", - "sms_otp_length", - "smtp_max_frequency", "disable_signup", "external_anonymous_users_enabled", "external_apple_additional_client_ids", @@ -6618,6 +7699,7 @@ "external_workos_enabled", "external_workos_secret", "external_workos_url", + "external_web3_solana_enabled", "external_zoom_client_id", "external_zoom_enabled", "external_zoom_secret", @@ -6636,8 +7718,14 @@ "hook_send_email_enabled", "hook_send_email_uri", "hook_send_email_secrets", + "hook_before_user_created_enabled", + "hook_before_user_created_uri", + "hook_before_user_created_secrets", + "jwt_exp", "mailer_allow_unverified_email_sign_ins", "mailer_autoconfirm", + "mailer_otp_exp", + "mailer_otp_length", "mailer_secure_email_change_enabled", "mailer_subjects_confirmation", "mailer_subjects_email_change", @@ -6651,15 +7739,26 @@ "mailer_templates_magic_link_content", "mailer_templates_reauthentication_content", "mailer_templates_recovery_content", + "mfa_max_enrolled_factors", "mfa_totp_enroll_enabled", "mfa_totp_verify_enabled", "mfa_phone_enroll_enabled", "mfa_phone_verify_enabled", "mfa_web_authn_enroll_enabled", "mfa_web_authn_verify_enabled", + "mfa_phone_otp_length", "mfa_phone_template", + "mfa_phone_max_frequency", "password_hibp_enabled", + "password_min_length", "password_required_characters", + "rate_limit_anonymous_users", + "rate_limit_email_sent", + "rate_limit_sms_sent", + "rate_limit_token_refresh", + "rate_limit_verify", + "rate_limit_otp", + "rate_limit_web3", "refresh_token_rotation_enabled", "saml_enabled", "saml_external_url", @@ -6668,13 +7767,19 @@ "security_captcha_provider", "security_captcha_secret", "security_manual_linking_enabled", + "security_refresh_token_reuse_interval", "security_update_password_require_reauthentication", + "sessions_inactivity_timeout", "sessions_single_per_user", "sessions_tags", + "sessions_timebox", "site_url", "sms_autoconfirm", + "sms_max_frequency", "sms_messagebird_access_key", "sms_messagebird_originator", + "sms_otp_exp", + "sms_otp_length", "sms_provider", "sms_template", "sms_test_otp", @@ -6693,6 +7798,7 @@ "sms_vonage_from", "smtp_admin_email", "smtp_host", + "smtp_max_frequency", "smtp_pass", "smtp_port", "smtp_sender_name", @@ -6729,6 +7835,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -6740,68 +7847,254 @@ "schema": { "type": "object", "properties": { + "site_url": { + "type": "string", + "pattern": "^[^,]+$", + "nullable": true + }, + "disable_signup": { + "type": "boolean", + "nullable": true + }, "jwt_exp": { "type": "integer", "minimum": 0, - "maximum": 604800 + "maximum": 604800, + "nullable": true + }, + "smtp_admin_email": { + "type": "string", + "format": "email", + "nullable": true + }, + "smtp_host": { + "type": "string", + "nullable": true + }, + "smtp_port": { + "type": "string", + "nullable": true + }, + "smtp_user": { + "type": "string", + "nullable": true + }, + "smtp_pass": { + "type": "string", + "nullable": true }, "smtp_max_frequency": { "type": "integer", "minimum": 0, - "maximum": 32767 + "maximum": 32767, + "nullable": true + }, + "smtp_sender_name": { + "type": "string", + "nullable": true + }, + "mailer_allow_unverified_email_sign_ins": { + "type": "boolean", + "nullable": true + }, + "mailer_autoconfirm": { + "type": "boolean", + "nullable": true + }, + "mailer_subjects_invite": { + "type": "string", + "nullable": true + }, + "mailer_subjects_confirmation": { + "type": "string", + "nullable": true + }, + "mailer_subjects_recovery": { + "type": "string", + "nullable": true + }, + "mailer_subjects_email_change": { + "type": "string", + "nullable": true + }, + "mailer_subjects_magic_link": { + "type": "string", + "nullable": true + }, + "mailer_subjects_reauthentication": { + "type": "string", + "nullable": true + }, + "mailer_templates_invite_content": { + "type": "string", + "nullable": true + }, + "mailer_templates_confirmation_content": { + "type": "string", + "nullable": true + }, + "mailer_templates_recovery_content": { + "type": "string", + "nullable": true + }, + "mailer_templates_email_change_content": { + "type": "string", + "nullable": true + }, + "mailer_templates_magic_link_content": { + "type": "string", + "nullable": true + }, + "mailer_templates_reauthentication_content": { + "type": "string", + "nullable": true }, "mfa_max_enrolled_factors": { "type": "integer", "minimum": 0, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true + }, + "uri_allow_list": { + "type": "string", + "nullable": true + }, + "external_anonymous_users_enabled": { + "type": "boolean", + "nullable": true + }, + "external_email_enabled": { + "type": "boolean", + "nullable": true + }, + "external_phone_enabled": { + "type": "boolean", + "nullable": true + }, + "saml_enabled": { + "type": "boolean", + "nullable": true + }, + "saml_external_url": { + "type": "string", + "pattern": "^[^,]+$", + "nullable": true + }, + "security_captcha_enabled": { + "type": "boolean", + "nullable": true + }, + "security_captcha_provider": { + "type": "string", + "enum": ["turnstile", "hcaptcha"], + "nullable": true + }, + "security_captcha_secret": { + "type": "string", + "nullable": true }, "sessions_timebox": { "type": "integer", - "minimum": 0 + "minimum": 0, + "nullable": true }, "sessions_inactivity_timeout": { "type": "integer", - "minimum": 0 + "minimum": 0, + "nullable": true + }, + "sessions_single_per_user": { + "type": "boolean", + "nullable": true + }, + "sessions_tags": { + "type": "string", + "pattern": "^\\s*([a-zA-Z0-9_-]+(\\s*,+\\s*)?)*\\s*$", + "nullable": true }, "rate_limit_anonymous_users": { "type": "integer", "minimum": 1, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "rate_limit_email_sent": { "type": "integer", "minimum": 1, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "rate_limit_sms_sent": { "type": "integer", "minimum": 1, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "rate_limit_verify": { "type": "integer", "minimum": 1, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "rate_limit_token_refresh": { "type": "integer", "minimum": 1, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "rate_limit_otp": { "type": "integer", "minimum": 1, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true + }, + "rate_limit_web3": { + "type": "integer", + "minimum": 1, + "maximum": 2147483647, + "nullable": true + }, + "mailer_secure_email_change_enabled": { + "type": "boolean", + "nullable": true + }, + "refresh_token_rotation_enabled": { + "type": "boolean", + "nullable": true + }, + "password_hibp_enabled": { + "type": "boolean", + "nullable": true }, "password_min_length": { "type": "integer", "minimum": 6, - "maximum": 32767 + "maximum": 32767, + "nullable": true + }, + "password_required_characters": { + "type": "string", + "enum": [ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", + "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", + "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\\\:\"|<>?,./`~", + "" + ], + "nullable": true + }, + "security_manual_linking_enabled": { + "type": "boolean", + "nullable": true + }, + "security_update_password_require_reauthentication": { + "type": "boolean", + "nullable": true }, "security_refresh_token_reuse_interval": { "type": "integer", "minimum": 0, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "mailer_otp_exp": { "type": "integer", @@ -6811,489 +8104,496 @@ "mailer_otp_length": { "type": "integer", "minimum": 6, - "maximum": 10 + "maximum": 10, + "nullable": true + }, + "sms_autoconfirm": { + "type": "boolean", + "nullable": true }, "sms_max_frequency": { "type": "integer", "minimum": 0, - "maximum": 32767 + "maximum": 32767, + "nullable": true }, "sms_otp_exp": { "type": "integer", "minimum": 0, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "sms_otp_length": { "type": "integer", "minimum": 0, "maximum": 32767 }, + "sms_provider": { + "type": "string", + "enum": ["messagebird", "textlocal", "twilio", "twilio_verify", "vonage"], + "nullable": true + }, + "sms_messagebird_access_key": { + "type": "string", + "nullable": true + }, + "sms_messagebird_originator": { + "type": "string", + "nullable": true + }, + "sms_test_otp": { + "type": "string", + "pattern": "^([0-9]{1,15}=[0-9]+,?)*$", + "nullable": true + }, + "sms_test_otp_valid_until": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "sms_textlocal_api_key": { + "type": "string", + "nullable": true + }, + "sms_textlocal_sender": { + "type": "string", + "nullable": true + }, + "sms_twilio_account_sid": { + "type": "string", + "nullable": true + }, + "sms_twilio_auth_token": { + "type": "string", + "nullable": true + }, + "sms_twilio_content_sid": { + "type": "string", + "nullable": true + }, + "sms_twilio_message_service_sid": { + "type": "string", + "nullable": true + }, + "sms_twilio_verify_account_sid": { + "type": "string", + "nullable": true + }, + "sms_twilio_verify_auth_token": { + "type": "string", + "nullable": true + }, + "sms_twilio_verify_message_service_sid": { + "type": "string", + "nullable": true + }, + "sms_vonage_api_key": { + "type": "string", + "nullable": true + }, + "sms_vonage_api_secret": { + "type": "string", + "nullable": true + }, + "sms_vonage_from": { + "type": "string", + "nullable": true + }, + "sms_template": { + "type": "string", + "nullable": true + }, + "hook_mfa_verification_attempt_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_mfa_verification_attempt_uri": { + "type": "string", + "nullable": true + }, + "hook_mfa_verification_attempt_secrets": { + "type": "string", + "nullable": true + }, + "hook_password_verification_attempt_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_password_verification_attempt_uri": { + "type": "string", + "nullable": true + }, + "hook_password_verification_attempt_secrets": { + "type": "string", + "nullable": true + }, + "hook_custom_access_token_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_custom_access_token_uri": { + "type": "string", + "nullable": true + }, + "hook_custom_access_token_secrets": { + "type": "string", + "nullable": true + }, + "hook_send_sms_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_send_sms_uri": { + "type": "string", + "nullable": true + }, + "hook_send_sms_secrets": { + "type": "string", + "nullable": true + }, + "hook_send_email_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_send_email_uri": { + "type": "string", + "nullable": true + }, + "hook_send_email_secrets": { + "type": "string", + "nullable": true + }, + "hook_before_user_created_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_before_user_created_uri": { + "type": "string", + "nullable": true + }, + "hook_before_user_created_secrets": { + "type": "string", + "nullable": true + }, + "external_apple_enabled": { + "type": "boolean", + "nullable": true + }, + "external_apple_client_id": { + "type": "string", + "nullable": true + }, + "external_apple_secret": { + "type": "string", + "nullable": true + }, + "external_apple_additional_client_ids": { + "type": "string", + "nullable": true + }, + "external_azure_enabled": { + "type": "boolean", + "nullable": true + }, + "external_azure_client_id": { + "type": "string", + "nullable": true + }, + "external_azure_secret": { + "type": "string", + "nullable": true + }, + "external_azure_url": { + "type": "string", + "nullable": true + }, + "external_bitbucket_enabled": { + "type": "boolean", + "nullable": true + }, + "external_bitbucket_client_id": { + "type": "string", + "nullable": true + }, + "external_bitbucket_secret": { + "type": "string", + "nullable": true + }, + "external_discord_enabled": { + "type": "boolean", + "nullable": true + }, + "external_discord_client_id": { + "type": "string", + "nullable": true + }, + "external_discord_secret": { + "type": "string", + "nullable": true + }, + "external_facebook_enabled": { + "type": "boolean", + "nullable": true + }, + "external_facebook_client_id": { + "type": "string", + "nullable": true + }, + "external_facebook_secret": { + "type": "string", + "nullable": true + }, + "external_figma_enabled": { + "type": "boolean", + "nullable": true + }, + "external_figma_client_id": { + "type": "string", + "nullable": true + }, + "external_figma_secret": { + "type": "string", + "nullable": true + }, + "external_github_enabled": { + "type": "boolean", + "nullable": true + }, + "external_github_client_id": { + "type": "string", + "nullable": true + }, + "external_github_secret": { + "type": "string", + "nullable": true + }, + "external_gitlab_enabled": { + "type": "boolean", + "nullable": true + }, + "external_gitlab_client_id": { + "type": "string", + "nullable": true + }, + "external_gitlab_secret": { + "type": "string", + "nullable": true + }, + "external_gitlab_url": { + "type": "string", + "nullable": true + }, + "external_google_enabled": { + "type": "boolean", + "nullable": true + }, + "external_google_client_id": { + "type": "string", + "nullable": true + }, + "external_google_secret": { + "type": "string", + "nullable": true + }, + "external_google_additional_client_ids": { + "type": "string", + "nullable": true + }, + "external_google_skip_nonce_check": { + "type": "boolean", + "nullable": true + }, + "external_kakao_enabled": { + "type": "boolean", + "nullable": true + }, + "external_kakao_client_id": { + "type": "string", + "nullable": true + }, + "external_kakao_secret": { + "type": "string", + "nullable": true + }, + "external_keycloak_enabled": { + "type": "boolean", + "nullable": true + }, + "external_keycloak_client_id": { + "type": "string", + "nullable": true + }, + "external_keycloak_secret": { + "type": "string", + "nullable": true + }, + "external_keycloak_url": { + "type": "string", + "nullable": true + }, + "external_linkedin_oidc_enabled": { + "type": "boolean", + "nullable": true + }, + "external_linkedin_oidc_client_id": { + "type": "string", + "nullable": true + }, + "external_linkedin_oidc_secret": { + "type": "string", + "nullable": true + }, + "external_slack_oidc_enabled": { + "type": "boolean", + "nullable": true + }, + "external_slack_oidc_client_id": { + "type": "string", + "nullable": true + }, + "external_slack_oidc_secret": { + "type": "string", + "nullable": true + }, + "external_notion_enabled": { + "type": "boolean", + "nullable": true + }, + "external_notion_client_id": { + "type": "string", + "nullable": true + }, + "external_notion_secret": { + "type": "string", + "nullable": true + }, + "external_slack_enabled": { + "type": "boolean", + "nullable": true + }, + "external_slack_client_id": { + "type": "string", + "nullable": true + }, + "external_slack_secret": { + "type": "string", + "nullable": true + }, + "external_spotify_enabled": { + "type": "boolean", + "nullable": true + }, + "external_spotify_client_id": { + "type": "string", + "nullable": true + }, + "external_spotify_secret": { + "type": "string", + "nullable": true + }, + "external_twitch_enabled": { + "type": "boolean", + "nullable": true + }, + "external_twitch_client_id": { + "type": "string", + "nullable": true + }, + "external_twitch_secret": { + "type": "string", + "nullable": true + }, + "external_twitter_enabled": { + "type": "boolean", + "nullable": true + }, + "external_twitter_client_id": { + "type": "string", + "nullable": true + }, + "external_twitter_secret": { + "type": "string", + "nullable": true + }, + "external_workos_enabled": { + "type": "boolean", + "nullable": true + }, + "external_workos_client_id": { + "type": "string", + "nullable": true + }, + "external_workos_secret": { + "type": "string", + "nullable": true + }, + "external_workos_url": { + "type": "string", + "nullable": true + }, + "external_web3_solana_enabled": { + "type": "boolean", + "nullable": true + }, + "external_zoom_enabled": { + "type": "boolean", + "nullable": true + }, + "external_zoom_client_id": { + "type": "string", + "nullable": true + }, + "external_zoom_secret": { + "type": "string", + "nullable": true + }, "db_max_pool_size": { - "type": "integer" + "type": "integer", + "nullable": true }, "api_max_request_duration": { - "type": "integer" + "type": "integer", + "nullable": true + }, + "mfa_totp_enroll_enabled": { + "type": "boolean", + "nullable": true + }, + "mfa_totp_verify_enabled": { + "type": "boolean", + "nullable": true + }, + "mfa_web_authn_enroll_enabled": { + "type": "boolean", + "nullable": true + }, + "mfa_web_authn_verify_enabled": { + "type": "boolean", + "nullable": true + }, + "mfa_phone_enroll_enabled": { + "type": "boolean", + "nullable": true + }, + "mfa_phone_verify_enabled": { + "type": "boolean", + "nullable": true }, "mfa_phone_max_frequency": { "type": "integer", "minimum": 0, - "maximum": 32767 + "maximum": 32767, + "nullable": true }, "mfa_phone_otp_length": { "type": "integer", "minimum": 0, - "maximum": 32767 - }, - "site_url": { - "type": "string", - "pattern": "/^[^,]+$/" - }, - "disable_signup": { - "type": "boolean" - }, - "smtp_admin_email": { - "type": "string" - }, - "smtp_host": { - "type": "string" - }, - "smtp_port": { - "type": "string" - }, - "smtp_user": { - "type": "string" - }, - "smtp_pass": { - "type": "string" - }, - "smtp_sender_name": { - "type": "string" - }, - "mailer_allow_unverified_email_sign_ins": { - "type": "boolean" - }, - "mailer_autoconfirm": { - "type": "boolean" - }, - "mailer_subjects_invite": { - "type": "string" - }, - "mailer_subjects_confirmation": { - "type": "string" - }, - "mailer_subjects_recovery": { - "type": "string" - }, - "mailer_subjects_email_change": { - "type": "string" - }, - "mailer_subjects_magic_link": { - "type": "string" - }, - "mailer_subjects_reauthentication": { - "type": "string" - }, - "mailer_templates_invite_content": { - "type": "string" - }, - "mailer_templates_confirmation_content": { - "type": "string" - }, - "mailer_templates_recovery_content": { - "type": "string" - }, - "mailer_templates_email_change_content": { - "type": "string" - }, - "mailer_templates_magic_link_content": { - "type": "string" - }, - "mailer_templates_reauthentication_content": { - "type": "string" - }, - "uri_allow_list": { - "type": "string" - }, - "external_anonymous_users_enabled": { - "type": "boolean" - }, - "external_email_enabled": { - "type": "boolean" - }, - "external_phone_enabled": { - "type": "boolean" - }, - "saml_enabled": { - "type": "boolean" - }, - "saml_external_url": { - "type": "string", - "pattern": "/^[^,]+$/" - }, - "security_captcha_enabled": { - "type": "boolean" - }, - "security_captcha_provider": { - "type": "string" - }, - "security_captcha_secret": { - "type": "string" - }, - "sessions_single_per_user": { - "type": "boolean" - }, - "sessions_tags": { - "type": "string", - "pattern": "/^\\s*([a-z0-9_-]+(\\s*,+\\s*)?)*\\s*$/i" - }, - "mailer_secure_email_change_enabled": { - "type": "boolean" - }, - "refresh_token_rotation_enabled": { - "type": "boolean" - }, - "password_hibp_enabled": { - "type": "boolean" - }, - "password_required_characters": { - "type": "string", - "enum": [ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", - "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", - "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\\\:\"|<>?,./`~", - "" - ] - }, - "security_manual_linking_enabled": { - "type": "boolean" - }, - "security_update_password_require_reauthentication": { - "type": "boolean" - }, - "sms_autoconfirm": { - "type": "boolean" - }, - "sms_provider": { - "type": "string" - }, - "sms_messagebird_access_key": { - "type": "string" - }, - "sms_messagebird_originator": { - "type": "string" - }, - "sms_test_otp": { - "type": "string", - "pattern": "/^([0-9]{1,15}=[0-9]+,?)*$/" - }, - "sms_test_otp_valid_until": { - "type": "string" - }, - "sms_textlocal_api_key": { - "type": "string" - }, - "sms_textlocal_sender": { - "type": "string" - }, - "sms_twilio_account_sid": { - "type": "string" - }, - "sms_twilio_auth_token": { - "type": "string" - }, - "sms_twilio_content_sid": { - "type": "string" - }, - "sms_twilio_message_service_sid": { - "type": "string" - }, - "sms_twilio_verify_account_sid": { - "type": "string" - }, - "sms_twilio_verify_auth_token": { - "type": "string" - }, - "sms_twilio_verify_message_service_sid": { - "type": "string" - }, - "sms_vonage_api_key": { - "type": "string" - }, - "sms_vonage_api_secret": { - "type": "string" - }, - "sms_vonage_from": { - "type": "string" - }, - "sms_template": { - "type": "string" - }, - "hook_mfa_verification_attempt_enabled": { - "type": "boolean" - }, - "hook_mfa_verification_attempt_uri": { - "type": "string" - }, - "hook_mfa_verification_attempt_secrets": { - "type": "string" - }, - "hook_password_verification_attempt_enabled": { - "type": "boolean" - }, - "hook_password_verification_attempt_uri": { - "type": "string" - }, - "hook_password_verification_attempt_secrets": { - "type": "string" - }, - "hook_custom_access_token_enabled": { - "type": "boolean" - }, - "hook_custom_access_token_uri": { - "type": "string" - }, - "hook_custom_access_token_secrets": { - "type": "string" - }, - "hook_send_sms_enabled": { - "type": "boolean" - }, - "hook_send_sms_uri": { - "type": "string" - }, - "hook_send_sms_secrets": { - "type": "string" - }, - "hook_send_email_enabled": { - "type": "boolean" - }, - "hook_send_email_uri": { - "type": "string" - }, - "hook_send_email_secrets": { - "type": "string" - }, - "external_apple_enabled": { - "type": "boolean" - }, - "external_apple_client_id": { - "type": "string" - }, - "external_apple_secret": { - "type": "string" - }, - "external_apple_additional_client_ids": { - "type": "string" - }, - "external_azure_enabled": { - "type": "boolean" - }, - "external_azure_client_id": { - "type": "string" - }, - "external_azure_secret": { - "type": "string" - }, - "external_azure_url": { - "type": "string" - }, - "external_bitbucket_enabled": { - "type": "boolean" - }, - "external_bitbucket_client_id": { - "type": "string" - }, - "external_bitbucket_secret": { - "type": "string" - }, - "external_discord_enabled": { - "type": "boolean" - }, - "external_discord_client_id": { - "type": "string" - }, - "external_discord_secret": { - "type": "string" - }, - "external_facebook_enabled": { - "type": "boolean" - }, - "external_facebook_client_id": { - "type": "string" - }, - "external_facebook_secret": { - "type": "string" - }, - "external_figma_enabled": { - "type": "boolean" - }, - "external_figma_client_id": { - "type": "string" - }, - "external_figma_secret": { - "type": "string" - }, - "external_github_enabled": { - "type": "boolean" - }, - "external_github_client_id": { - "type": "string" - }, - "external_github_secret": { - "type": "string" - }, - "external_gitlab_enabled": { - "type": "boolean" - }, - "external_gitlab_client_id": { - "type": "string" - }, - "external_gitlab_secret": { - "type": "string" - }, - "external_gitlab_url": { - "type": "string" - }, - "external_google_enabled": { - "type": "boolean" - }, - "external_google_client_id": { - "type": "string" - }, - "external_google_secret": { - "type": "string" - }, - "external_google_additional_client_ids": { - "type": "string" - }, - "external_google_skip_nonce_check": { - "type": "boolean" - }, - "external_kakao_enabled": { - "type": "boolean" - }, - "external_kakao_client_id": { - "type": "string" - }, - "external_kakao_secret": { - "type": "string" - }, - "external_keycloak_enabled": { - "type": "boolean" - }, - "external_keycloak_client_id": { - "type": "string" - }, - "external_keycloak_secret": { - "type": "string" - }, - "external_keycloak_url": { - "type": "string" - }, - "external_linkedin_oidc_enabled": { - "type": "boolean" - }, - "external_linkedin_oidc_client_id": { - "type": "string" - }, - "external_linkedin_oidc_secret": { - "type": "string" - }, - "external_slack_oidc_enabled": { - "type": "boolean" - }, - "external_slack_oidc_client_id": { - "type": "string" - }, - "external_slack_oidc_secret": { - "type": "string" - }, - "external_notion_enabled": { - "type": "boolean" - }, - "external_notion_client_id": { - "type": "string" - }, - "external_notion_secret": { - "type": "string" - }, - "external_slack_enabled": { - "type": "boolean" - }, - "external_slack_client_id": { - "type": "string" - }, - "external_slack_secret": { - "type": "string" - }, - "external_spotify_enabled": { - "type": "boolean" - }, - "external_spotify_client_id": { - "type": "string" - }, - "external_spotify_secret": { - "type": "string" - }, - "external_twitch_enabled": { - "type": "boolean" - }, - "external_twitch_client_id": { - "type": "string" - }, - "external_twitch_secret": { - "type": "string" - }, - "external_twitter_enabled": { - "type": "boolean" - }, - "external_twitter_client_id": { - "type": "string" - }, - "external_twitter_secret": { - "type": "string" - }, - "external_workos_enabled": { - "type": "boolean" - }, - "external_workos_client_id": { - "type": "string" - }, - "external_workos_secret": { - "type": "string" - }, - "external_workos_url": { - "type": "string" - }, - "external_zoom_enabled": { - "type": "boolean" - }, - "external_zoom_client_id": { - "type": "string" - }, - "external_zoom_secret": { - "type": "string" - }, - "mfa_totp_enroll_enabled": { - "type": "boolean" - }, - "mfa_totp_verify_enabled": { - "type": "boolean" - }, - "mfa_web_authn_enroll_enabled": { - "type": "boolean" - }, - "mfa_web_authn_verify_enabled": { - "type": "boolean" - }, - "mfa_phone_enroll_enabled": { - "type": "boolean" - }, - "mfa_phone_verify_enabled": { - "type": "boolean" + "maximum": 32767, + "nullable": true }, "mfa_phone_template": { - "type": "string" + "type": "string", + "nullable": true } } } @@ -7316,83 +8616,6 @@ "type": "integer", "nullable": true }, - "jwt_exp": { - "type": "integer", - "nullable": true - }, - "mailer_otp_exp": { - "type": "integer" - }, - "mailer_otp_length": { - "type": "integer", - "nullable": true - }, - "mfa_max_enrolled_factors": { - "type": "integer", - "nullable": true - }, - "mfa_phone_otp_length": { - "type": "integer" - }, - "mfa_phone_max_frequency": { - "type": "integer", - "nullable": true - }, - "password_min_length": { - "type": "integer", - "nullable": true - }, - "rate_limit_anonymous_users": { - "type": "integer", - "nullable": true - }, - "rate_limit_email_sent": { - "type": "integer", - "nullable": true - }, - "rate_limit_sms_sent": { - "type": "integer", - "nullable": true - }, - "rate_limit_token_refresh": { - "type": "integer", - "nullable": true - }, - "rate_limit_verify": { - "type": "integer", - "nullable": true - }, - "rate_limit_otp": { - "type": "integer", - "nullable": true - }, - "security_refresh_token_reuse_interval": { - "type": "integer", - "nullable": true - }, - "sessions_inactivity_timeout": { - "type": "integer", - "nullable": true - }, - "sessions_timebox": { - "type": "integer", - "nullable": true - }, - "sms_max_frequency": { - "type": "integer", - "nullable": true - }, - "sms_otp_exp": { - "type": "integer", - "nullable": true - }, - "sms_otp_length": { - "type": "integer" - }, - "smtp_max_frequency": { - "type": "integer", - "nullable": true - }, "disable_signup": { "type": "boolean", "nullable": true @@ -7665,6 +8888,10 @@ "type": "string", "nullable": true }, + "external_web3_solana_enabled": { + "type": "boolean", + "nullable": true + }, "external_zoom_client_id": { "type": "string", "nullable": true @@ -7737,6 +8964,22 @@ "type": "string", "nullable": true }, + "hook_before_user_created_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_before_user_created_uri": { + "type": "string", + "nullable": true + }, + "hook_before_user_created_secrets": { + "type": "string", + "nullable": true + }, + "jwt_exp": { + "type": "integer", + "nullable": true + }, "mailer_allow_unverified_email_sign_ins": { "type": "boolean", "nullable": true @@ -7745,6 +8988,13 @@ "type": "boolean", "nullable": true }, + "mailer_otp_exp": { + "type": "integer" + }, + "mailer_otp_length": { + "type": "integer", + "nullable": true + }, "mailer_secure_email_change_enabled": { "type": "boolean", "nullable": true @@ -7797,6 +9047,10 @@ "type": "string", "nullable": true }, + "mfa_max_enrolled_factors": { + "type": "integer", + "nullable": true + }, "mfa_totp_enroll_enabled": { "type": "boolean", "nullable": true @@ -7821,16 +9075,61 @@ "type": "boolean", "nullable": true }, + "mfa_phone_otp_length": { + "type": "integer" + }, "mfa_phone_template": { "type": "string", "nullable": true }, + "mfa_phone_max_frequency": { + "type": "integer", + "nullable": true + }, "password_hibp_enabled": { "type": "boolean", "nullable": true }, + "password_min_length": { + "type": "integer", + "nullable": true + }, "password_required_characters": { "type": "string", + "enum": [ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", + "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", + "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\\\:\"|<>?,./`~", + "" + ], + "nullable": true + }, + "rate_limit_anonymous_users": { + "type": "integer", + "nullable": true + }, + "rate_limit_email_sent": { + "type": "integer", + "nullable": true + }, + "rate_limit_sms_sent": { + "type": "integer", + "nullable": true + }, + "rate_limit_token_refresh": { + "type": "integer", + "nullable": true + }, + "rate_limit_verify": { + "type": "integer", + "nullable": true + }, + "rate_limit_otp": { + "type": "integer", + "nullable": true + }, + "rate_limit_web3": { + "type": "integer", "nullable": true }, "refresh_token_rotation_enabled": { @@ -7855,6 +9154,7 @@ }, "security_captcha_provider": { "type": "string", + "enum": ["turnstile", "hcaptcha"], "nullable": true }, "security_captcha_secret": { @@ -7865,10 +9165,18 @@ "type": "boolean", "nullable": true }, + "security_refresh_token_reuse_interval": { + "type": "integer", + "nullable": true + }, "security_update_password_require_reauthentication": { "type": "boolean", "nullable": true }, + "sessions_inactivity_timeout": { + "type": "integer", + "nullable": true + }, "sessions_single_per_user": { "type": "boolean", "nullable": true @@ -7877,6 +9185,10 @@ "type": "string", "nullable": true }, + "sessions_timebox": { + "type": "integer", + "nullable": true + }, "site_url": { "type": "string", "nullable": true @@ -7885,6 +9197,10 @@ "type": "boolean", "nullable": true }, + "sms_max_frequency": { + "type": "integer", + "nullable": true + }, "sms_messagebird_access_key": { "type": "string", "nullable": true @@ -7893,8 +9209,16 @@ "type": "string", "nullable": true }, + "sms_otp_exp": { + "type": "integer", + "nullable": true + }, + "sms_otp_length": { + "type": "integer" + }, "sms_provider": { "type": "string", + "enum": ["messagebird", "textlocal", "twilio", "twilio_verify", "vonage"], "nullable": true }, "sms_template": { @@ -7907,6 +9231,7 @@ }, "sms_test_otp_valid_until": { "type": "string", + "format": "date-time", "nullable": true }, "sms_textlocal_api_key": { @@ -7959,12 +9284,17 @@ }, "smtp_admin_email": { "type": "string", + "format": "email", "nullable": true }, "smtp_host": { "type": "string", "nullable": true }, + "smtp_max_frequency": { + "type": "integer", + "nullable": true + }, "smtp_pass": { "type": "string", "nullable": true @@ -7989,26 +9319,6 @@ "required": [ "api_max_request_duration", "db_max_pool_size", - "jwt_exp", - "mailer_otp_exp", - "mailer_otp_length", - "mfa_max_enrolled_factors", - "mfa_phone_otp_length", - "mfa_phone_max_frequency", - "password_min_length", - "rate_limit_anonymous_users", - "rate_limit_email_sent", - "rate_limit_sms_sent", - "rate_limit_token_refresh", - "rate_limit_verify", - "rate_limit_otp", - "security_refresh_token_reuse_interval", - "sessions_inactivity_timeout", - "sessions_timebox", - "sms_max_frequency", - "sms_otp_exp", - "sms_otp_length", - "smtp_max_frequency", "disable_signup", "external_anonymous_users_enabled", "external_apple_additional_client_ids", @@ -8077,6 +9387,7 @@ "external_workos_enabled", "external_workos_secret", "external_workos_url", + "external_web3_solana_enabled", "external_zoom_client_id", "external_zoom_enabled", "external_zoom_secret", @@ -8095,8 +9406,14 @@ "hook_send_email_enabled", "hook_send_email_uri", "hook_send_email_secrets", + "hook_before_user_created_enabled", + "hook_before_user_created_uri", + "hook_before_user_created_secrets", + "jwt_exp", "mailer_allow_unverified_email_sign_ins", "mailer_autoconfirm", + "mailer_otp_exp", + "mailer_otp_length", "mailer_secure_email_change_enabled", "mailer_subjects_confirmation", "mailer_subjects_email_change", @@ -8110,15 +9427,26 @@ "mailer_templates_magic_link_content", "mailer_templates_reauthentication_content", "mailer_templates_recovery_content", + "mfa_max_enrolled_factors", "mfa_totp_enroll_enabled", "mfa_totp_verify_enabled", "mfa_phone_enroll_enabled", "mfa_phone_verify_enabled", "mfa_web_authn_enroll_enabled", "mfa_web_authn_verify_enabled", + "mfa_phone_otp_length", "mfa_phone_template", + "mfa_phone_max_frequency", "password_hibp_enabled", + "password_min_length", "password_required_characters", + "rate_limit_anonymous_users", + "rate_limit_email_sent", + "rate_limit_sms_sent", + "rate_limit_token_refresh", + "rate_limit_verify", + "rate_limit_otp", + "rate_limit_web3", "refresh_token_rotation_enabled", "saml_enabled", "saml_external_url", @@ -8127,13 +9455,19 @@ "security_captcha_provider", "security_captcha_secret", "security_manual_linking_enabled", + "security_refresh_token_reuse_interval", "security_update_password_require_reauthentication", + "sessions_inactivity_timeout", "sessions_single_per_user", "sessions_tags", + "sessions_timebox", "site_url", "sms_autoconfirm", + "sms_max_frequency", "sms_messagebird_access_key", "sms_messagebird_originator", + "sms_otp_exp", + "sms_otp_length", "sms_provider", "sms_template", "sms_test_otp", @@ -8152,6 +9486,7 @@ "sms_vonage_from", "smtp_admin_email", "smtp_host", + "smtp_max_frequency", "smtp_pass", "smtp_port", "smtp_sender_name", @@ -8179,7 +9514,7 @@ }, "/v1/projects/{ref}/config/auth/third-party-auth": { "post": { - "operationId": "createTPAForProject", + "operationId": "v1-create-project-tpa-integration", "summary": "Creates a new third-party auth integration", "parameters": [ { @@ -8190,6 +9525,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -8207,9 +9543,7 @@ "jwks_url": { "type": "string" }, - "custom_jwks": { - "type": "object" - } + "custom_jwks": {} } } } @@ -8224,7 +9558,8 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "type": { "type": "string" @@ -8238,11 +9573,9 @@ "nullable": true }, "custom_jwks": { - "type": "object", "nullable": true }, "resolved_jwks": { - "type": "object", "nullable": true }, "inserted_at": { @@ -8273,7 +9606,7 @@ ] }, "get": { - "operationId": "listTPAForProject", + "operationId": "v1-list-project-tpa-integrations", "summary": "Lists all third-party auth integrations", "parameters": [ { @@ -8284,6 +9617,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -8299,7 +9633,8 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "type": { "type": "string" @@ -8313,11 +9648,9 @@ "nullable": true }, "custom_jwks": { - "type": "object", "nullable": true }, "resolved_jwks": { - "type": "object", "nullable": true }, "inserted_at": { @@ -8351,7 +9684,7 @@ }, "/v1/projects/{ref}/config/auth/third-party-auth/{tpa_id}": { "delete": { - "operationId": "deleteTPAForProject", + "operationId": "v1-delete-project-tpa-integration", "summary": "Removes a third-party auth integration", "parameters": [ { @@ -8362,6 +9695,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } }, @@ -8370,6 +9704,7 @@ "required": true, "in": "path", "schema": { + "format": "uuid", "type": "string" } } @@ -8383,7 +9718,8 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "type": { "type": "string" @@ -8397,11 +9733,9 @@ "nullable": true }, "custom_jwks": { - "type": "object", "nullable": true }, "resolved_jwks": { - "type": "object", "nullable": true }, "inserted_at": { @@ -8432,7 +9766,7 @@ ] }, "get": { - "operationId": "getTPAForProject", + "operationId": "v1-get-project-tpa-integration", "summary": "Get a third-party integration", "parameters": [ { @@ -8443,6 +9777,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } }, @@ -8451,6 +9786,7 @@ "required": true, "in": "path", "schema": { + "format": "uuid", "type": "string" } } @@ -8464,7 +9800,8 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "type": { "type": "string" @@ -8478,11 +9815,9 @@ "nullable": true }, "custom_jwks": { - "type": "object", "nullable": true }, "resolved_jwks": { - "type": "object", "nullable": true }, "inserted_at": { @@ -8526,6 +9861,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -8555,7 +9891,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -8613,23 +9953,15 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": {}, - "hideDefinitions": ["release_channel", "postgres_engine"] - } - } - } - }, "responses": { "200": { "description": "" @@ -8655,7 +9987,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -8685,7 +10021,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -8732,7 +10072,15 @@ "ci_4xlarge", "ci_8xlarge", "ci_12xlarge", - "ci_16xlarge" + "ci_16xlarge", + "ci_24xlarge", + "ci_24xlarge_optimized_cpu", + "ci_24xlarge_optimized_memory", + "ci_24xlarge_high_memory", + "ci_48xlarge", + "ci_48xlarge_optimized_cpu", + "ci_48xlarge_optimized_memory", + "ci_48xlarge_high_memory" ] }, { @@ -8785,26 +10133,7 @@ "required": ["description", "type", "interval", "amount"] }, "meta": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "number" - }, - { - "type": "boolean" - }, - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "description": "Any JSON-serializable value" } }, "required": ["id", "name", "price"] @@ -8852,7 +10181,15 @@ "ci_4xlarge", "ci_8xlarge", "ci_12xlarge", - "ci_16xlarge" + "ci_16xlarge", + "ci_24xlarge", + "ci_24xlarge_optimized_cpu", + "ci_24xlarge_optimized_memory", + "ci_24xlarge_high_memory", + "ci_48xlarge", + "ci_48xlarge_optimized_cpu", + "ci_48xlarge_optimized_memory", + "ci_48xlarge_high_memory" ] }, { @@ -8905,26 +10242,7 @@ "required": ["description", "type", "interval", "amount"] }, "meta": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "number" - }, - { - "type": "boolean" - }, - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "description": "Any JSON-serializable value" } }, "required": ["id", "name", "price"] @@ -8947,6 +10265,7 @@ "description": "Failed to list project addons" } }, + "tags": ["Billing"], "security": [ { "bearer": [] @@ -8961,7 +10280,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -8987,7 +10310,15 @@ "ci_4xlarge", "ci_8xlarge", "ci_12xlarge", - "ci_16xlarge" + "ci_16xlarge", + "ci_24xlarge", + "ci_24xlarge_optimized_cpu", + "ci_24xlarge_optimized_memory", + "ci_24xlarge_high_memory", + "ci_48xlarge", + "ci_48xlarge_optimized_cpu", + "ci_48xlarge_optimized_memory", + "ci_48xlarge_high_memory" ] }, { @@ -9033,6 +10364,7 @@ "description": "Failed to apply project addon" } }, + "tags": ["Billing"], "security": [ { "bearer": [] @@ -9046,37 +10378,59 @@ "summary": "Removes project addon", "parameters": [ { - "name": "addon_variant", + "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { - "enum": [ - "ci_micro", - "ci_small", - "ci_medium", - "ci_large", - "ci_xlarge", - "ci_2xlarge", - "ci_4xlarge", - "ci_8xlarge", - "ci_12xlarge", - "ci_16xlarge", - "cd_default", - "pitr_7", - "pitr_14", - "pitr_28", - "ipv4_default" - ], + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } }, { - "name": "ref", + "name": "addon_variant", "required": true, "in": "path", - "schema": { - "type": "string" - } + "oneOf": [ + { + "type": "string", + "enum": [ + "ci_micro", + "ci_small", + "ci_medium", + "ci_large", + "ci_xlarge", + "ci_2xlarge", + "ci_4xlarge", + "ci_8xlarge", + "ci_12xlarge", + "ci_16xlarge", + "ci_24xlarge", + "ci_24xlarge_optimized_cpu", + "ci_24xlarge_optimized_memory", + "ci_24xlarge_high_memory", + "ci_48xlarge", + "ci_48xlarge_optimized_cpu", + "ci_48xlarge_optimized_memory", + "ci_48xlarge_high_memory" + ] + }, + { + "type": "string", + "enum": ["cd_default"] + }, + { + "type": "string", + "enum": ["pitr_7", "pitr_14", "pitr_28"] + }, + { + "type": "string", + "enum": ["ipv4_default"] + } + ], + "schema": {} } ], "responses": { @@ -9090,6 +10444,7 @@ "description": "Failed to remove project addon" } }, + "tags": ["Billing"], "security": [ { "bearer": [] @@ -9097,40 +10452,21 @@ ] } }, - "/v1/projects/{ref}/analytics/endpoints/logs.all": { + "/v1/projects/{ref}/claim-token": { "get": { - "operationId": "getLogs", - "summary": "Gets project's logs", + "operationId": "v1-get-project-claim-token", + "x-internal": true, + "summary": "Gets project claim token", "parameters": [ - { - "name": "iso_timestamp_end", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "iso_timestamp_start", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "sql", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, { "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -9143,9 +10479,526 @@ "schema": { "type": "object", "properties": { + "token_alias": { + "type": "string" + }, + "expires_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string", + "format": "uuid" + } + }, + "required": ["token_alias", "expires_at", "created_at", "created_by"] + } + } + } + }, + "403": { + "description": "" + } + }, + "tags": ["Projects"], + "security": [ + { + "bearer": [] + } + ] + }, + "post": { + "operationId": "v1-create-project-claim-token", + "x-internal": true, + "summary": "Creates project claim token", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "token": { + "type": "string" + }, + "token_alias": { + "type": "string" + }, + "expires_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string", + "format": "uuid" + } + }, + "required": ["token", "token_alias", "expires_at", "created_at", "created_by"] + } + } + } + }, + "403": { + "description": "" + } + }, + "tags": ["Projects"], + "security": [ + { + "bearer": [] + } + ] + }, + "delete": { + "operationId": "v1-delete-project-claim-token", + "x-internal": true, + "summary": "Revokes project claim token", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "" + }, + "403": { + "description": "" + } + }, + "tags": ["Projects"], + "security": [ + { + "bearer": [] + } + ] + } + }, + "/v1/projects/{ref}/advisors/performance": { + "get": { + "operationId": "v1-get-performance-advisors", + "summary": "Gets project performance advisors.", + "description": "This is an **experimental** endpoint. It is subject to change or removal in future versions. Use it with caution, as it may not remain supported or stable.", + "deprecated": true, + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "lints": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "unindexed_foreign_keys", + "auth_users_exposed", + "auth_rls_initplan", + "no_primary_key", + "unused_index", + "multiple_permissive_policies", + "policy_exists_rls_disabled", + "rls_enabled_no_policy", + "duplicate_index", + "security_definer_view", + "function_search_path_mutable", + "rls_disabled_in_public", + "extension_in_public", + "rls_references_user_metadata", + "materialized_view_in_api", + "foreign_table_in_api", + "unsupported_reg_types", + "auth_otp_long_expiry", + "auth_otp_short_length", + "ssl_not_enforced", + "network_restrictions_not_set", + "password_requirements_min_length", + "pitr_not_enabled", + "auth_leaked_password_protection", + "auth_insufficient_mfa_options", + "auth_password_policy_missing", + "leaked_service_key", + "no_backup_admin" + ] + }, + "title": { + "type": "string" + }, + "level": { + "type": "string", + "enum": ["ERROR", "WARN", "INFO"] + }, + "facing": { + "type": "string", + "enum": ["EXTERNAL"] + }, + "categories": { + "type": "array", + "items": { + "type": "string", + "enum": ["PERFORMANCE", "SECURITY"] + } + }, + "description": { + "type": "string" + }, + "detail": { + "type": "string" + }, + "remediation": { + "type": "string" + }, + "metadata": { + "type": "object", + "properties": { + "schema": { + "type": "string" + }, + "name": { + "type": "string" + }, + "entity": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "table", + "view", + "auth", + "function", + "extension", + "compliance" + ] + }, + "fkey_name": { + "type": "string" + }, + "fkey_columns": { + "type": "array", + "items": { + "type": "number" + } + } + } + }, + "cache_key": { + "type": "string" + } + }, + "required": [ + "name", + "title", + "level", + "facing", + "categories", + "description", + "detail", + "remediation", + "cache_key" + ] + } + } + }, + "required": ["lints"] + } + } + } + }, + "403": { + "description": "" + } + }, + "tags": ["Advisors"], + "security": [ + { + "bearer": [] + } + ] + } + }, + "/v1/projects/{ref}/advisors/security": { + "get": { + "operationId": "v1-get-security-advisors", + "summary": "Gets project security advisors.", + "description": "This is an **experimental** endpoint. It is subject to change or removal in future versions. Use it with caution, as it may not remain supported or stable.", + "deprecated": true, + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + }, + { + "name": "lint_type", + "required": false, + "in": "query", + "schema": { + "enum": ["sql"], + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "lints": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "unindexed_foreign_keys", + "auth_users_exposed", + "auth_rls_initplan", + "no_primary_key", + "unused_index", + "multiple_permissive_policies", + "policy_exists_rls_disabled", + "rls_enabled_no_policy", + "duplicate_index", + "security_definer_view", + "function_search_path_mutable", + "rls_disabled_in_public", + "extension_in_public", + "rls_references_user_metadata", + "materialized_view_in_api", + "foreign_table_in_api", + "unsupported_reg_types", + "auth_otp_long_expiry", + "auth_otp_short_length", + "ssl_not_enforced", + "network_restrictions_not_set", + "password_requirements_min_length", + "pitr_not_enabled", + "auth_leaked_password_protection", + "auth_insufficient_mfa_options", + "auth_password_policy_missing", + "leaked_service_key", + "no_backup_admin" + ] + }, + "title": { + "type": "string" + }, + "level": { + "type": "string", + "enum": ["ERROR", "WARN", "INFO"] + }, + "facing": { + "type": "string", + "enum": ["EXTERNAL"] + }, + "categories": { + "type": "array", + "items": { + "type": "string", + "enum": ["PERFORMANCE", "SECURITY"] + } + }, + "description": { + "type": "string" + }, + "detail": { + "type": "string" + }, + "remediation": { + "type": "string" + }, + "metadata": { + "type": "object", + "properties": { + "schema": { + "type": "string" + }, + "name": { + "type": "string" + }, + "entity": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "table", + "view", + "auth", + "function", + "extension", + "compliance" + ] + }, + "fkey_name": { + "type": "string" + }, + "fkey_columns": { + "type": "array", + "items": { + "type": "number" + } + } + } + }, + "cache_key": { + "type": "string" + } + }, + "required": [ + "name", + "title", + "level", + "facing", + "categories", + "description", + "detail", + "remediation", + "cache_key" + ] + } + } + }, + "required": ["lints"] + } + } + } + }, + "403": { + "description": "" + } + }, + "tags": ["Advisors"], + "security": [ + { + "bearer": [] + } + ] + } + }, + "/v1/projects/{ref}/analytics/endpoints/logs.all": { + "get": { + "operationId": "v1-get-project-logs", + "summary": "Gets project's logs", + "description": "Executes a SQL query on the project's logs.\n\nEither the 'iso_timestamp_start' and 'iso_timestamp_end' parameters must be provided.\nIf both are not provided, only the last 1 minute of logs will be queried.\nThe timestamp range must be no more than 24 hours and is rounded to the nearest minute. If the range is more than 24 hours, a validation error will be thrown.\n", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + }, + { + "name": "sql", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "iso_timestamp_start", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + }, + { + "name": "iso_timestamp_end", + "required": false, + "in": "query", + "schema": { + "format": "date-time", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "result": { + "type": "array", + "items": {} + }, "error": { "oneOf": [ { + "type": "string" + }, + { + "type": "object", "properties": { "code": { "type": "number" @@ -9153,6 +11006,7 @@ "errors": { "type": "array", "items": { + "type": "object", "properties": { "domain": { "type": "string" @@ -9169,7 +11023,14 @@ "reason": { "type": "string" } - } + }, + "required": [ + "domain", + "location", + "locationType", + "message", + "reason" + ] } }, "message": { @@ -9178,18 +11039,10 @@ "status": { "type": "string" } - } - }, - { - "type": "string" + }, + "required": ["code", "errors", "message", "status"] } ] - }, - "result": { - "type": "array", - "items": { - "type": "object" - } } } } @@ -9208,9 +11061,219 @@ ] } }, + "/v1/projects/{ref}/analytics/endpoints/usage.api-counts": { + "get": { + "operationId": "v1-get-project-usage-api-count", + "summary": "Gets project's usage api counts", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + }, + { + "name": "interval", + "required": false, + "in": "query", + "schema": { + "enum": ["15min", "30min", "1hr", "3hr", "1day", "3day", "7day"], + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "result": { + "type": "array", + "items": {} + }, + "error": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "code": { + "type": "number" + }, + "errors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "domain": { + "type": "string" + }, + "location": { + "type": "string" + }, + "locationType": { + "type": "string" + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + } + }, + "required": [ + "domain", + "location", + "locationType", + "message", + "reason" + ] + } + }, + "message": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": ["code", "errors", "message", "status"] + } + ] + } + } + } + } + } + }, + "403": { + "description": "" + }, + "500": { + "description": "Failed to get project's usage api counts" + } + }, + "tags": ["Analytics"], + "security": [ + { + "bearer": [] + } + ] + } + }, + "/v1/projects/{ref}/analytics/endpoints/usage.api-requests-count": { + "get": { + "operationId": "v1-get-project-usage-request-count", + "summary": "Gets project's usage api requests count", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "result": { + "type": "array", + "items": {} + }, + "error": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "code": { + "type": "number" + }, + "errors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "domain": { + "type": "string" + }, + "location": { + "type": "string" + }, + "locationType": { + "type": "string" + }, + "message": { + "type": "string" + }, + "reason": { + "type": "string" + } + }, + "required": [ + "domain", + "location", + "locationType", + "message", + "reason" + ] + } + }, + "message": { + "type": "string" + }, + "status": { + "type": "string" + } + }, + "required": ["code", "errors", "message", "status"] + } + ] + } + } + } + } + } + }, + "500": { + "description": "Failed to get project's usage api requests count" + } + }, + "tags": ["Analytics"], + "security": [ + { + "bearer": [] + } + ] + } + }, "/v1/projects/{ref}/database/migrations": { "get": { - "operationId": "v1-list-migrations", + "operationId": "v1-list-migration-history", "summary": "[Beta] List applied migration versions", "description": "Only available to selected partner OAuth apps", "parameters": [ @@ -9218,7 +11281,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -9270,21 +11337,47 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + }, + { + "name": "Idempotency-Key", + "required": false, + "in": "header", + "description": "A unique key to ensure the same migration is tracked only once.", "schema": { "type": "string" } } ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "minLength": 1 + }, + "name": { + "type": "string" + } + }, + "required": ["query"] } } + } + }, + "responses": { + "200": { + "description": "" }, "403": { "description": "" @@ -9299,6 +11392,71 @@ "bearer": [] } ] + }, + "put": { + "operationId": "v1-upsert-a-migration", + "summary": "[Beta] Upsert a database migration without applying", + "description": "Only available to selected partner OAuth apps", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + }, + { + "name": "Idempotency-Key", + "required": false, + "in": "header", + "description": "A unique key to ensure the same migration is tracked only once.", + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "query": { + "type": "string", + "minLength": 1 + }, + "name": { + "type": "string" + } + }, + "required": ["query"] + } + } + } + }, + "responses": { + "200": { + "description": "" + }, + "403": { + "description": "" + }, + "500": { + "description": "Failed to upsert database migration" + } + }, + "tags": ["Database"], + "security": [ + { + "bearer": [] + } + ] } }, "/v1/projects/{ref}/database/query": { @@ -9310,7 +11468,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -9325,6 +11487,9 @@ "query": { "type": "string", "minLength": 1 + }, + "read_only": { + "type": "boolean" } }, "required": ["query"] @@ -9334,14 +11499,7 @@ }, "responses": { "201": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "object" - } - } - } + "description": "" }, "403": { "description": "" @@ -9371,6 +11529,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -9396,7 +11555,7 @@ }, "/v1/projects/{ref}/database/context": { "get": { - "operationId": "getDatabaseMetadata", + "operationId": "v1-get-database-metadata", "summary": "Gets database metadata for the given project.", "description": "This is an **experimental** endpoint. It is subject to change or removal in future versions. Use it with caution, as it may not remain supported or stable.", "deprecated": true, @@ -9405,7 +11564,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -9472,7 +11635,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -9504,10 +11671,12 @@ "type": "integer" }, "created_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "updated_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "verify_jwt": { "type": "boolean" @@ -9520,6 +11689,9 @@ }, "import_map_path": { "type": "string" + }, + "ezbr_sha256": { + "type": "string" } }, "required": [ @@ -9555,89 +11727,16 @@ "summary": "Create a function", "description": "This endpoint is deprecated - use the deploy endpoint. Creates a function and adds it to the specified project.", "deprecated": true, - "parameters": [ - { - "name": "import_map_path", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "entrypoint_path", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "import_map", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "verify_jwt", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "name", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "slug", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "ref", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - } - ], "requestBody": { "required": true, "content": { - "application/json": { + "application/vnd.denoland.eszip": { "schema": { - "type": "object", - "properties": { - "slug": { - "type": "string", - "pattern": "^[A-Za-z0-9_-]+$" - }, - "name": { - "type": "string" - }, - "body": { - "type": "string" - }, - "verify_jwt": { - "type": "boolean" - } - }, - "required": ["slug", "name", "body"] + "type": "string", + "format": "binary" } }, - "application/vnd.denoland.eszip": { + "application/json": { "schema": { "type": "object", "properties": { @@ -9660,6 +11759,79 @@ } } }, + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "pattern": "^[A-Za-z0-9_-]+$", + "type": "string" + } + }, + { + "name": "name", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "verify_jwt", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { + "type": "boolean" + } + }, + { + "name": "import_map", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { + "type": "boolean" + } + }, + { + "name": "entrypoint_path", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "import_map_path", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "ezbr_sha256", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + } + ], "responses": { "201": { "description": "", @@ -9685,10 +11857,12 @@ "type": "integer" }, "created_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "updated_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "verify_jwt": { "type": "boolean" @@ -9701,6 +11875,9 @@ }, "import_map_path": { "type": "string" + }, + "ezbr_sha256": { + "type": "string" } }, "required": [ @@ -9739,7 +11916,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -9771,7 +11952,8 @@ "type": "integer" }, "created_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "verify_jwt": { "type": "boolean" @@ -9784,6 +11966,9 @@ }, "import_map_path": { "type": "string" + }, + "ezbr_sha256": { + "type": "string" } }, "required": ["id", "slug", "name", "status", "version"] @@ -9822,10 +12007,12 @@ "type": "integer" }, "created_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "updated_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "verify_jwt": { "type": "boolean" @@ -9838,6 +12025,9 @@ }, "import_map_path": { "type": "string" + }, + "ezbr_sha256": { + "type": "string" } }, "required": [ @@ -9879,11 +12069,15 @@ "description": "A new endpoint to deploy functions. It will create if function does not exist.", "parameters": [ { - "name": "bundleOnly", - "required": false, - "in": "query", + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", "schema": { - "type": "boolean" + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" } }, { @@ -9891,15 +12085,17 @@ "required": false, "in": "query", "schema": { + "pattern": "^[A-Za-z0-9_-]+$", "type": "string" } }, { - "name": "ref", - "required": true, - "in": "path", + "name": "bundleOnly", + "required": false, + "in": "query", + "description": "Boolean string, true or false", "schema": { - "type": "string" + "type": "boolean" } } ], @@ -9972,10 +12168,12 @@ "type": "integer" }, "created_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "updated_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "verify_jwt": { "type": "boolean" @@ -9988,6 +12186,9 @@ }, "import_map_path": { "type": "string" + }, + "ezbr_sha256": { + "type": "string" } }, "required": ["id", "slug", "name", "status", "version"] @@ -10017,19 +12218,24 @@ "description": "Retrieves a function with the specified slug and project.", "parameters": [ { - "name": "function_slug", + "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { - "pattern": "^[A-Za-z0-9_-]+$", + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } }, { - "name": "ref", + "name": "function_slug", "required": true, "in": "path", + "description": "Function slug", "schema": { + "pattern": "^[A-Za-z0-9_-]+$", "type": "string" } } @@ -10059,10 +12265,12 @@ "type": "integer" }, "created_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "updated_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "verify_jwt": { "type": "boolean" @@ -10075,6 +12283,9 @@ }, "import_map_path": { "type": "string" + }, + "ezbr_sha256": { + "type": "string" } }, "required": [ @@ -10108,85 +12319,16 @@ "operationId": "v1-update-a-function", "summary": "Update a function", "description": "Updates a function with the specified slug and project.", - "parameters": [ - { - "name": "import_map_path", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "entrypoint_path", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "import_map", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "verify_jwt", - "required": false, - "in": "query", - "schema": { - "type": "boolean" - } - }, - { - "name": "name", - "required": false, - "in": "query", - "schema": { - "type": "string" - } - }, - { - "name": "function_slug", - "required": true, - "in": "path", - "schema": { - "pattern": "^[A-Za-z0-9_-]+$", - "type": "string" - } - }, - { - "name": "ref", - "required": true, - "in": "path", - "schema": { - "type": "string" - } - } - ], "requestBody": { "required": true, "content": { - "application/json": { + "application/vnd.denoland.eszip": { "schema": { - "type": "object", - "properties": { - "name": { - "type": "string" - }, - "body": { - "type": "string" - }, - "verify_jwt": { - "type": "boolean" - } - } + "type": "string", + "format": "binary" } }, - "application/vnd.denoland.eszip": { + "application/json": { "schema": { "type": "object", "properties": { @@ -10204,6 +12346,89 @@ } } }, + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + }, + { + "name": "function_slug", + "required": true, + "in": "path", + "description": "Function slug", + "schema": { + "pattern": "^[A-Za-z0-9_-]+$", + "type": "string" + } + }, + { + "name": "slug", + "required": false, + "in": "query", + "schema": { + "pattern": "^[A-Za-z0-9_-]+$", + "type": "string" + } + }, + { + "name": "name", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "verify_jwt", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { + "type": "boolean" + } + }, + { + "name": "import_map", + "required": false, + "in": "query", + "description": "Boolean string, true or false", + "schema": { + "type": "boolean" + } + }, + { + "name": "entrypoint_path", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "import_map_path", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + }, + { + "name": "ezbr_sha256", + "required": false, + "in": "query", + "schema": { + "type": "string" + } + } + ], "responses": { "200": { "description": "", @@ -10229,10 +12454,12 @@ "type": "integer" }, "created_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "updated_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "verify_jwt": { "type": "boolean" @@ -10245,6 +12472,9 @@ }, "import_map_path": { "type": "string" + }, + "ezbr_sha256": { + "type": "string" } }, "required": [ @@ -10280,19 +12510,24 @@ "description": "Deletes a function with the specified slug from the specified project.", "parameters": [ { - "name": "function_slug", + "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { - "pattern": "^[A-Za-z0-9_-]+$", + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } }, { - "name": "ref", + "name": "function_slug", "required": true, "in": "path", + "description": "Function slug", "schema": { + "pattern": "^[A-Za-z0-9_-]+$", "type": "string" } } @@ -10323,26 +12558,39 @@ "description": "Retrieves a function body for the specified slug and project.", "parameters": [ { - "name": "function_slug", + "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { - "pattern": "^[A-Za-z0-9_-]+$", + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } }, { - "name": "ref", + "name": "function_slug", "required": true, "in": "path", + "description": "Function slug", "schema": { + "pattern": "^[A-Za-z0-9_-]+$", "type": "string" } } ], "responses": { "200": { - "description": "" + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": {} + } + } + } }, "403": { "description": "" @@ -10372,6 +12620,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -10439,6 +12688,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -10475,10 +12725,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -10491,15 +12751,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -10549,10 +12800,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -10565,15 +12826,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -10645,6 +12897,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -10688,10 +12941,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -10704,15 +12967,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -10790,6 +13044,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } }, @@ -10798,6 +13053,7 @@ "required": true, "in": "path", "schema": { + "format": "uuid", "type": "string" } } @@ -10836,10 +13092,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -10852,15 +13118,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -10932,6 +13189,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } }, @@ -10940,6 +13198,7 @@ "required": true, "in": "path", "schema": { + "format": "uuid", "type": "string" } } @@ -10971,10 +13230,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -10987,15 +13256,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -11044,10 +13304,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -11060,15 +13330,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -11140,6 +13401,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } }, @@ -11148,6 +13410,7 @@ "required": true, "in": "path", "schema": { + "format": "uuid", "type": "string" } } @@ -11186,10 +13449,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -11202,15 +13475,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -11284,6 +13548,7 @@ "schema": { "minLength": 20, "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -11310,6 +13575,9 @@ "items": { "type": "object", "properties": { + "is_physical_backup": { + "type": "boolean" + }, "status": { "type": "string", "enum": [ @@ -11321,26 +13589,21 @@ "CANCELLED" ] }, - "is_physical_backup": { - "type": "boolean" - }, "inserted_at": { "type": "string" } }, - "required": ["status", "is_physical_backup", "inserted_at"] + "required": ["is_physical_backup", "status", "inserted_at"] } }, "physical_backup_data": { "type": "object", "properties": { "earliest_physical_backup_date_unix": { - "type": "integer", - "format": "int64" + "type": "integer" }, "latest_physical_backup_date_unix": { - "type": "integer", - "format": "int64" + "type": "integer" } } } @@ -11380,7 +13643,11 @@ "name": "ref", "required": true, "in": "path", + "description": "Project ref", "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", "type": "string" } } @@ -11419,6 +13686,186 @@ ] } }, + "/v1/projects/{ref}/database/backups/restore-point": { + "post": { + "operationId": "v1-create-restore-point", + "x-internal": true, + "summary": "Initiates a creation of a restore point for a database", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 20 + } + }, + "required": ["name"] + } + } + } + }, + "responses": { + "201": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": ["AVAILABLE", "PENDING", "REMOVED"] + } + }, + "required": ["name", "status"] + } + } + } + } + }, + "tags": ["Database"], + "security": [ + { + "bearer": [] + } + ] + }, + "get": { + "operationId": "v1-get-restore-point", + "x-internal": true, + "summary": "Get restore points for project", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + }, + { + "name": "name", + "required": false, + "in": "query", + "schema": { + "maxLength": 20, + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": ["AVAILABLE", "PENDING", "REMOVED"] + } + }, + "required": ["name", "status"] + } + } + } + }, + "403": { + "description": "" + }, + "500": { + "description": "Failed to get requested restore points" + } + }, + "tags": ["Database"], + "security": [ + { + "bearer": [] + } + ] + } + }, + "/v1/projects/{ref}/database/backups/undo": { + "post": { + "operationId": "v1-undo", + "x-internal": true, + "summary": "Initiates an undo to a given restore point", + "parameters": [ + { + "name": "ref", + "required": true, + "in": "path", + "description": "Project ref", + "schema": { + "minLength": 20, + "maxLength": 20, + "pattern": "^[a-z]+$", + "type": "string" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 20 + } + }, + "required": ["name"] + } + } + } + }, + "responses": { + "201": { + "description": "" + }, + "403": { + "description": "" + } + }, + "tags": ["Database"], + "security": [ + { + "bearer": [] + } + ] + } + }, "/v1/organizations/{slug}/members": { "get": { "operationId": "v1-list-organization-members", @@ -11428,7 +13875,9 @@ "name": "slug", "required": true, "in": "path", + "description": "Organization slug", "schema": { + "pattern": "^[\\w-]+$", "type": "string" } } @@ -11486,6 +13935,87 @@ "name": "slug", "required": true, "in": "path", + "description": "Organization slug", + "schema": { + "pattern": "^[\\w-]+$", + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "plan": { + "type": "string", + "enum": ["free", "pro", "team", "enterprise"] + }, + "opt_in_tags": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "AI_SQL_GENERATOR_OPT_IN", + "AI_DATA_GENERATOR_OPT_IN", + "AI_LOG_GENERATOR_OPT_IN" + ] + } + }, + "allowed_release_channels": { + "type": "array", + "items": { + "type": "string", + "enum": ["internal", "alpha", "beta", "ga", "withdrawn", "preview"] + } + } + }, + "required": ["id", "name", "opt_in_tags", "allowed_release_channels"] + } + } + } + }, + "403": { + "description": "" + } + }, + "tags": ["Organizations"], + "security": [ + { + "bearer": [] + } + ] + } + }, + "/v1/organizations/{slug}/project-claim/{token}": { + "get": { + "operationId": "v1-get-organization-project-claim", + "x-internal": true, + "summary": "Gets project details for the specified organization and claim token", + "parameters": [ + { + "name": "slug", + "required": true, + "in": "path", + "description": "Organization slug", + "schema": { + "pattern": "^[\\w-]+$", + "type": "string" + } + }, + { + "name": "token", + "required": true, + "in": "path", "schema": { "type": "string" } @@ -11499,32 +14029,126 @@ "schema": { "type": "object", "properties": { - "plan": { + "project": { + "type": "object", + "properties": { + "ref": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": ["ref", "name"] + }, + "preview": { + "type": "object", + "properties": { + "valid": { + "type": "boolean" + }, + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": ["key", "message"] + } + }, + "errors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": ["key", "message"] + } + }, + "info": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": ["key", "message"] + } + }, + "members_exceeding_free_project_limit": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "limit": { + "type": "number" + } + }, + "required": ["name", "limit"] + } + }, + "target_organization_eligible": { + "type": "boolean", + "nullable": true + }, + "target_organization_has_free_project_slots": { + "type": "boolean", + "nullable": true + }, + "source_subscription_plan": { + "type": "string", + "enum": ["free", "pro", "team", "enterprise"] + }, + "target_subscription_plan": { + "type": "string", + "enum": ["free", "pro", "team", "enterprise"], + "nullable": true + } + }, + "required": [ + "valid", + "warnings", + "errors", + "info", + "members_exceeding_free_project_limit", + "target_organization_eligible", + "target_organization_has_free_project_slots", + "source_subscription_plan", + "target_subscription_plan" + ] + }, + "expires_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "created_by": { "type": "string", - "enum": ["free", "pro", "team", "enterprise"] - }, - "opt_in_tags": { - "type": "array", - "items": { - "type": "string", - "enum": ["AI_SQL_GENERATOR_OPT_IN"] - } - }, - "allowed_release_channels": { - "type": "array", - "items": { - "type": "string", - "enum": ["internal", "alpha", "beta", "ga", "withdrawn", "preview"] - } - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" + "format": "uuid" } }, - "required": ["opt_in_tags", "allowed_release_channels", "id", "name"] + "required": ["project", "preview", "expires_at", "created_at", "created_by"] } } } @@ -11539,6 +14163,45 @@ "bearer": [] } ] + }, + "post": { + "operationId": "v1-claim-project-for-organization", + "x-internal": true, + "summary": "Claims project for the specified organization", + "parameters": [ + { + "name": "slug", + "required": true, + "in": "path", + "description": "Organization slug", + "schema": { + "pattern": "^[\\w-]+$", + "type": "string" + } + }, + { + "name": "token", + "required": true, + "in": "path", + "schema": { + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "" + }, + "403": { + "description": "" + } + }, + "tags": ["Organizations"], + "security": [ + { + "bearer": [] + } + ] } } }, @@ -11590,7 +14253,9 @@ "type": "string" }, "db_port": { - "type": "number" + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true }, "db_user": { "type": "string" @@ -11639,6 +14304,9 @@ "FUNCTIONS_DEPLOYED", "FUNCTIONS_FAILED" ] + }, + "request_review": { + "type": "boolean" } } }, @@ -11646,7 +14314,8 @@ "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "name": { "type": "string" @@ -11687,10 +14356,16 @@ ] }, "created_at": { - "type": "string" + "type": "string", + "format": "date-time" }, "updated_at": { - "type": "string" + "type": "string", + "format": "date-time" + }, + "review_requested_at": { + "type": "string", + "format": "date-time" } }, "required": [ @@ -11715,6 +14390,14 @@ }, "required": ["message"] }, + "BranchActionBody": { + "type": "object", + "properties": { + "migration_version": { + "type": "string" + } + } + }, "BranchUpdateResponse": { "type": "object", "properties": { @@ -11728,28 +14411,6 @@ }, "required": ["workflow_run_id", "message"] }, - "V1DatabaseResponse": { - "type": "object", - "properties": { - "host": { - "type": "string", - "description": "Database host" - }, - "version": { - "type": "string", - "description": "Database version" - }, - "postgres_engine": { - "type": "string", - "description": "Database engine" - }, - "release_channel": { - "type": "string", - "description": "Release channel" - } - }, - "required": ["host", "version", "postgres_engine", "release_channel"] - }, "V1ProjectWithDatabaseResponse": { "type": "object", "properties": { @@ -11820,7 +14481,7 @@ }, "required": ["id", "organization_id", "name", "region", "created_at", "status", "database"] }, - "V1CreateProjectBodyDto": { + "V1CreateProjectBody": { "type": "object", "properties": { "db_pass": { @@ -11829,6 +14490,7 @@ }, "name": { "type": "string", + "maxLength": 256, "description": "Name of your project" }, "organization_id": { @@ -11873,6 +14535,8 @@ "desired_instance_size": { "type": "string", "enum": [ + "pico", + "nano", "micro", "small", "medium", @@ -11882,7 +14546,15 @@ "4xlarge", "8xlarge", "12xlarge", - "16xlarge" + "16xlarge", + "24xlarge", + "24xlarge_optimized_memory", + "24xlarge_optimized_cpu", + "24xlarge_high_memory", + "48xlarge", + "48xlarge_optimized_memory", + "48xlarge_optimized_cpu", + "48xlarge_high_memory" ] }, "template_url": { @@ -11956,7 +14628,7 @@ }, "required": ["id", "name"] }, - "CreateOrganizationV1Dto": { + "CreateOrganizationV1": { "type": "object", "properties": { "name": { @@ -12015,7 +14687,7 @@ "required": ["access_token", "refresh_token", "expires_in", "token_type"], "additionalProperties": false }, - "OAuthRevokeTokenBodyDto": { + "OAuthRevokeTokenBody": { "type": "object", "properties": { "client_id": { @@ -12032,112 +14704,6 @@ "required": ["client_id", "client_secret", "refresh_token"], "additionalProperties": false }, - "SnippetProject": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - } - }, - "required": ["id", "name"] - }, - "SnippetUser": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "username": { - "type": "string" - } - }, - "required": ["id", "username"] - }, - "SnippetMeta": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "inserted_at": { - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "type": { - "type": "string", - "enum": ["sql"] - }, - "visibility": { - "type": "string", - "enum": ["user", "project", "org", "public"] - }, - "name": { - "type": "string" - }, - "description": { - "type": "string", - "nullable": true - }, - "project": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "name": { - "type": "string" - } - }, - "required": ["id", "name"] - }, - "owner": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "username": { - "type": "string" - } - }, - "required": ["id", "username"] - }, - "updated_by": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "username": { - "type": "string" - } - }, - "required": ["id", "username"] - } - }, - "required": [ - "id", - "inserted_at", - "updated_at", - "type", - "visibility", - "name", - "description", - "project", - "owner", - "updated_by" - ] - }, "SnippetList": { "type": "object", "properties": { @@ -12174,8 +14740,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "number" }, "name": { "type": "string" @@ -12187,8 +14752,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "number" }, "username": { "type": "string" @@ -12200,8 +14764,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "number" }, "username": { "type": "string" @@ -12230,21 +14793,6 @@ }, "required": ["data"] }, - "SnippetContent": { - "type": "object", - "properties": { - "favorite": { - "type": "boolean" - }, - "schema_version": { - "type": "string" - }, - "sql": { - "type": "string" - } - }, - "required": ["favorite", "schema_version", "sql"] - }, "SnippetResponse": { "type": "object", "properties": { @@ -12262,8 +14810,8 @@ "enum": ["sql"] }, "visibility": { - "enum": ["user", "project", "org", "public"], - "type": "string" + "type": "string", + "enum": ["user", "project", "org", "public"] }, "name": { "type": "string" @@ -12276,8 +14824,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "number" }, "name": { "type": "string" @@ -12289,8 +14836,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "number" }, "username": { "type": "string" @@ -12302,8 +14848,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "number" }, "username": { "type": "string" @@ -12344,11 +14889,9 @@ "ApiKeyResponse": { "type": "object", "properties": { - "name": { - "type": "string" - }, "api_key": { - "type": "string" + "type": "string", + "nullable": true }, "id": { "type": "string", @@ -12356,13 +14899,16 @@ }, "type": { "type": "string", - "enum": ["publishable", "secret", "legacy"], + "enum": ["legacy", "publishable", "secret"], "nullable": true }, "prefix": { "type": "string", "nullable": true }, + "name": { + "type": "string" + }, "description": { "type": "string", "nullable": true @@ -12393,7 +14939,16 @@ "nullable": true } }, - "required": ["name", "api_key"] + "required": ["name"] + }, + "LegacyApiKeysResponse": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": ["enabled"] }, "CreateApiKeyBody": { "type": "object", @@ -12402,6 +14957,12 @@ "type": "string", "enum": ["publishable", "secret"] }, + "name": { + "type": "string", + "minLength": 4, + "maxLength": 64, + "pattern": "^[a-z_][a-z0-9_]+$" + }, "description": { "type": "string", "nullable": true @@ -12418,11 +14979,17 @@ "nullable": true } }, - "required": ["type"] + "required": ["type", "name"] }, "UpdateApiKeyBody": { "type": "object", "properties": { + "name": { + "type": "string", + "minLength": 4, + "maxLength": 64, + "pattern": "^[a-z_][a-z0-9_]+$" + }, "description": { "type": "string", "nullable": true @@ -12450,6 +15017,9 @@ "git_branch": { "type": "string" }, + "is_default": { + "type": "boolean" + }, "persistent": { "type": "boolean" }, @@ -12470,7 +15040,15 @@ "4xlarge", "8xlarge", "12xlarge", - "16xlarge" + "16xlarge", + "24xlarge", + "24xlarge_optimized_memory", + "24xlarge_optimized_cpu", + "24xlarge_high_memory", + "48xlarge", + "48xlarge_optimized_memory", + "48xlarge_optimized_cpu", + "48xlarge_high_memory" ] }, "release_channel": { @@ -12480,7 +15058,7 @@ }, "postgres_engine": { "type": "string", - "enum": ["15", "17-oriole"], + "enum": ["15", "17", "17-oriole"], "description": "Postgres engine version. If not provided, the latest version will be used." }, "secrets": { @@ -12488,279 +15066,25 @@ "additionalProperties": { "type": "string" } + }, + "with_data": { + "type": "boolean" } }, "required": ["branch_name"] }, - "ValidationRecord": { - "type": "object", - "properties": { - "txt_name": { - "type": "string" - }, - "txt_value": { - "type": "string" - } - }, - "required": ["txt_name", "txt_value"] - }, - "ValidationError": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - }, - "required": ["message"] - }, - "SslValidation": { - "type": "object", - "properties": { - "status": { - "type": "string" - }, - "validation_records": { - "type": "array", - "items": { - "type": "object", - "properties": { - "txt_name": { - "type": "string" - }, - "txt_value": { - "type": "string" - } - }, - "required": ["txt_name", "txt_value"] - } - }, - "validation_errors": { - "type": "array", - "items": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - }, - "required": ["message"] - } - } - }, - "required": ["status", "validation_records"] - }, - "OwnershipVerification": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "name": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "required": ["type", "name", "value"] - }, - "CustomHostnameDetails": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "hostname": { - "type": "string" - }, - "ssl": { - "type": "object", - "properties": { - "status": { - "type": "string" - }, - "validation_records": { - "type": "array", - "items": { - "type": "object", - "properties": { - "txt_name": { - "type": "string" - }, - "txt_value": { - "type": "string" - } - }, - "required": ["txt_name", "txt_value"] - } - }, - "validation_errors": { - "type": "array", - "items": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - }, - "required": ["message"] - } - } - }, - "required": ["status", "validation_records"] - }, - "ownership_verification": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "name": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "required": ["type", "name", "value"] - }, - "custom_origin_server": { - "type": "string" - }, - "verification_errors": { - "type": "array", - "items": { - "type": "string" - } - }, - "status": { - "type": "string" - } - }, - "required": [ - "id", - "hostname", - "ssl", - "ownership_verification", - "custom_origin_server", - "status" - ] - }, - "CfResponse": { - "type": "object", - "properties": { - "success": { - "type": "boolean" - }, - "errors": { - "type": "array", - "items": { - "type": "object" - } - }, - "messages": { - "type": "array", - "items": { - "type": "object" - } - }, - "result": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "hostname": { - "type": "string" - }, - "ssl": { - "type": "object", - "properties": { - "status": { - "type": "string" - }, - "validation_records": { - "type": "array", - "items": { - "type": "object", - "properties": { - "txt_name": { - "type": "string" - }, - "txt_value": { - "type": "string" - } - }, - "required": ["txt_name", "txt_value"] - } - }, - "validation_errors": { - "type": "array", - "items": { - "type": "object", - "properties": { - "message": { - "type": "string" - } - }, - "required": ["message"] - } - } - }, - "required": ["status", "validation_records"] - }, - "ownership_verification": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "name": { - "type": "string" - }, - "value": { - "type": "string" - } - }, - "required": ["type", "name", "value"] - }, - "custom_origin_server": { - "type": "string" - }, - "verification_errors": { - "type": "array", - "items": { - "type": "string" - } - }, - "status": { - "type": "string" - } - }, - "required": [ - "id", - "hostname", - "ssl", - "ownership_verification", - "custom_origin_server", - "status" - ] - } - }, - "required": ["success", "errors", "messages", "result"] - }, "UpdateCustomHostnameResponse": { "type": "object", "properties": { "status": { + "type": "string", "enum": [ "1_not_started", "2_initiated", "3_challenge_verified", "4_origin_setup_completed", "5_services_reconfigured" - ], - "type": "string" + ] }, "custom_hostname": { "type": "string" @@ -12774,13 +15098,13 @@ "errors": { "type": "array", "items": { - "type": "object" + "description": "Any JSON-serializable value" } }, "messages": { "type": "array", "items": { - "type": "object" + "description": "Any JSON-serializable value" } }, "result": { @@ -12892,6 +15216,30 @@ }, "required": ["banned_ipv4_addresses"] }, + "NetworkBanResponseEnriched": { + "type": "object", + "properties": { + "banned_ipv4_addresses": { + "type": "array", + "items": { + "type": "object", + "properties": { + "banned_address": { + "type": "string" + }, + "identifier": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": ["banned_address", "identifier", "type"] + } + } + }, + "required": ["banned_ipv4_addresses"] + }, "RemoveNetworkBanRequest": { "type": "object", "properties": { @@ -12900,6 +15248,9 @@ "items": { "type": "string" } + }, + "identifier": { + "type": "string" } }, "required": ["ipv4_addresses"] @@ -13057,8 +15408,7 @@ "type": "object", "properties": { "id": { - "type": "integer", - "format": "int64" + "type": "integer" }, "ref": { "type": "string" @@ -13085,21 +15435,24 @@ "required": ["name", "value"] }, "CreateSecretBody": { - "type": "object", - "properties": { - "name": { - "type": "string", - "maxLength": 256, - "pattern": "^(?!SUPABASE_).*", - "description": "Secret name must not start with the SUPABASE_ prefix.", - "example": "string" + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 256, + "pattern": "^(?!SUPABASE_).*", + "description": "Secret name must not start with the SUPABASE_ prefix.", + "example": "string" + }, + "value": { + "type": "string", + "maxLength": 24576 + } }, - "value": { - "type": "string", - "maxLength": 24576 - } - }, - "required": ["name", "value"] + "required": ["name", "value"] + } }, "SslEnforcementResponse": { "type": "object", @@ -13228,7 +15581,7 @@ "properties": { "postgres_version": { "type": "string", - "enum": ["15", "17-oriole"] + "enum": ["15", "17", "17-oriole"] }, "release_channel": { "type": "string", @@ -13241,12 +15594,6 @@ "required": ["postgres_version", "release_channel", "app_version"] } }, - "potential_breaking_changes": { - "type": "array", - "items": { - "type": "string" - } - }, "duration_estimate_hours": { "type": "number" }, @@ -13256,7 +15603,19 @@ "type": "string" } }, - "extension_dependent_objects": { + "objects_to_be_dropped": { + "type": "array", + "items": { + "type": "string" + } + }, + "unsupported_extensions": { + "type": "array", + "items": { + "type": "string" + } + }, + "user_defined_objects_in_internal_schemas": { "type": "array", "items": { "type": "string" @@ -13269,10 +15628,11 @@ "current_app_version_release_channel", "latest_app_version", "target_upgrade_versions", - "potential_breaking_changes", "duration_estimate_hours", "legacy_auth_custom_roles", - "extension_dependent_objects" + "objects_to_be_dropped", + "unsupported_extensions", + "user_defined_objects_in_internal_schemas" ] }, "DatabaseUpgradeStatusResponse": { @@ -13385,28 +15745,20 @@ }, "required": ["database_identifier"] }, - "AuthHealthResponse": { + "V1ServiceHealthResponse": { "type": "object", "properties": { "name": { "type": "string", - "enum": ["GoTrue"] - } - }, - "required": ["name"] - }, - "RealtimeHealthResponse": { - "type": "object", - "properties": { - "connected_cluster": { - "type": "integer" - } - }, - "required": ["connected_cluster"] - }, - "V1ServiceHealthResponse": { - "type": "object", - "properties": { + "enum": ["auth", "db", "pooler", "realtime", "rest", "storage"] + }, + "healthy": { + "type": "boolean" + }, + "status": { + "type": "string", + "enum": ["COMING_UP", "ACTIVE_HEALTHY", "UNHEALTHY"] + }, "info": { "oneOf": [ { @@ -13415,38 +15767,69 @@ "name": { "type": "string", "enum": ["GoTrue"] + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" } }, - "required": ["name"] + "required": ["name", "version", "description"] }, { "type": "object", "properties": { + "healthy": { + "type": "boolean" + }, + "db_connected": { + "type": "boolean" + }, "connected_cluster": { "type": "integer" } }, - "required": ["connected_cluster"] + "required": ["healthy", "db_connected", "connected_cluster"] } ] }, - "name": { - "enum": ["auth", "db", "pooler", "realtime", "rest", "storage"], - "type": "string" - }, - "healthy": { - "type": "boolean" - }, - "status": { - "enum": ["COMING_UP", "ACTIVE_HEALTHY", "UNHEALTHY"], - "type": "string" - }, "error": { "type": "string" } }, "required": ["name", "healthy", "status"] }, + "SigningKeyResponse": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "algorithm": { + "type": "string", + "enum": ["EdDSA", "ES256", "RS256", "HS256"] + }, + "status": { + "type": "string", + "enum": ["in_use", "previously_used", "revoked", "standby"] + }, + "public_jwk": { + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + } + }, + "required": ["id", "algorithm", "status", "created_at", "updated_at"], + "additionalProperties": false + }, "CreateSigningKeyBody": { "type": "object", "properties": { @@ -13457,6 +15840,109 @@ "status": { "type": "string", "enum": ["in_use", "standby"] + }, + "private_jwk": { + "discriminator": { + "propertyName": "kty" + }, + "oneOf": [ + { + "type": "object", + "properties": { + "kty": { + "type": "string", + "enum": ["RSA"] + }, + "n": { + "type": "string" + }, + "e": { + "type": "string", + "enum": ["AQAB"] + }, + "d": { + "type": "string" + }, + "p": { + "type": "string" + }, + "q": { + "type": "string" + }, + "dp": { + "type": "string" + }, + "dq": { + "type": "string" + }, + "qi": { + "type": "string" + } + }, + "required": ["kty", "n", "e", "d", "p", "q", "dp", "dq", "qi"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "kty": { + "type": "string", + "enum": ["EC"] + }, + "crv": { + "type": "string", + "enum": ["P-256"] + }, + "x": { + "type": "string" + }, + "y": { + "type": "string" + }, + "d": { + "type": "string" + } + }, + "required": ["kty", "crv", "x", "y", "d"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "kty": { + "type": "string", + "enum": ["OKP"] + }, + "crv": { + "type": "string", + "enum": ["Ed25519"] + }, + "x": { + "type": "string" + }, + "d": { + "type": "string" + } + }, + "required": ["kty", "crv", "x", "d"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "kty": { + "type": "string", + "enum": ["oct"] + }, + "k": { + "type": "string", + "minLength": 16 + } + }, + "required": ["kty", "k"], + "additionalProperties": false + } + ] } }, "required": ["algorithm"], @@ -13502,36 +15988,6 @@ "required": ["keys"], "additionalProperties": false }, - "SigningKeyResponse": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "algorithm": { - "type": "string", - "enum": ["EdDSA", "ES256", "RS256", "HS256"] - }, - "status": { - "type": "string", - "enum": ["in_use", "previously_used", "revoked", "standby"] - }, - "public_jwk": { - "nullable": true - }, - "created_at": { - "type": "string", - "format": "date-time" - }, - "updated_at": { - "type": "string", - "format": "date-time" - } - }, - "required": ["id", "algorithm", "status", "created_at", "updated_at"], - "additionalProperties": false - }, "UpdateSigningKeyBody": { "type": "object", "properties": { @@ -13543,48 +15999,6 @@ "required": ["status"], "additionalProperties": false }, - "StorageFeatureImageTransformation": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - } - }, - "required": ["enabled"] - }, - "StorageFeatureS3Protocol": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - } - }, - "required": ["enabled"] - }, - "StorageFeatures": { - "type": "object", - "properties": { - "imageTransformation": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - } - }, - "required": ["enabled"] - }, - "s3Protocol": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - } - }, - "required": ["enabled"] - } - }, - "required": ["imageTransformation", "s3Protocol"] - }, "StorageConfigResponse": { "type": "object", "properties": { @@ -13612,6 +16026,15 @@ } }, "required": ["enabled"] + }, + "icebergCatalog": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": ["enabled"] } }, "required": ["imageTransformation", "s3Protocol"] @@ -13625,7 +16048,7 @@ "fileSizeLimit": { "type": "integer", "minimum": 0, - "maximum": 53687091200, + "maximum": 536870912000, "format": "int64" }, "features": { @@ -13648,6 +16071,15 @@ } }, "required": ["enabled"] + }, + "icebergCatalog": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": ["enabled"] } }, "required": ["imageTransformation", "s3Protocol"] @@ -13717,6 +16149,10 @@ "minimum": 0, "maximum": 262143 }, + "session_replication_role": { + "type": "string", + "enum": ["origin", "replica", "local"] + }, "shared_buffers": { "type": "string" }, @@ -13734,10 +16170,6 @@ }, "work_mem": { "type": "string" - }, - "session_replication_role": { - "enum": ["origin", "replica", "local"], - "type": "string" } } }, @@ -13804,6 +16236,10 @@ "minimum": 0, "maximum": 262143 }, + "session_replication_role": { + "type": "string", + "enum": ["origin", "replica", "local"] + }, "shared_buffers": { "type": "string" }, @@ -13824,47 +16260,72 @@ }, "restart_database": { "type": "boolean" - }, - "session_replication_role": { - "enum": ["origin", "replica", "local"], - "type": "string" } } }, "V1PgbouncerConfigResponse": { "type": "object", "properties": { - "pool_mode": { - "type": "string", - "enum": ["transaction", "session", "statement"] - }, "default_pool_size": { - "type": "number" + "type": "integer" }, "ignore_startup_parameters": { "type": "string" }, "max_client_conn": { - "type": "number" + "type": "integer" + }, + "pool_mode": { + "type": "string", + "enum": ["transaction", "session", "statement"] }, "connection_string": { "type": "string" + }, + "server_idle_timeout": { + "type": "integer" + }, + "server_lifetime": { + "type": "integer" + }, + "query_wait_timeout": { + "type": "integer" + }, + "reserve_pool_size": { + "type": "integer" } } }, "SupavisorConfigResponse": { "type": "object", "properties": { + "identifier": { + "type": "string" + }, "database_type": { "type": "string", "enum": ["PRIMARY", "READ_REPLICA"] }, + "is_using_scram_auth": { + "type": "boolean" + }, + "db_user": { + "type": "string" + }, + "db_host": { + "type": "string" + }, "db_port": { "type": "integer" }, + "db_name": { + "type": "string" + }, + "connection_string": { + "type": "string" + }, "connectionString": { "type": "string", - "deprecated": true, "description": "Use connection_string instead" }, "default_pool_size": { @@ -13876,41 +16337,23 @@ "nullable": true }, "pool_mode": { - "enum": ["transaction", "session"], - "type": "string" - }, - "identifier": { - "type": "string" - }, - "is_using_scram_auth": { - "type": "boolean" - }, - "db_user": { - "type": "string" - }, - "db_host": { - "type": "string" - }, - "db_name": { - "type": "string" - }, - "connection_string": { - "type": "string" + "type": "string", + "enum": ["transaction", "session"] } }, "required": [ - "database_type", - "db_port", - "connectionString", - "default_pool_size", - "max_client_conn", - "pool_mode", "identifier", + "database_type", "is_using_scram_auth", "db_user", "db_host", + "db_port", "db_name", - "connection_string" + "connection_string", + "connectionString", + "default_pool_size", + "max_client_conn", + "pool_mode" ] }, "UpdateSupavisorConfigBody": { @@ -13918,15 +16361,14 @@ "properties": { "default_pool_size": { "type": "integer", - "nullable": true, "minimum": 0, - "maximum": 1000 + "maximum": 3000, + "nullable": true }, "pool_mode": { - "enum": ["transaction", "session"], "type": "string", - "description": "Dedicated pooler mode for the project", - "deprecated": true + "enum": ["transaction", "session"], + "description": "Dedicated pooler mode for the project" } } }, @@ -13954,83 +16396,6 @@ "type": "integer", "nullable": true }, - "jwt_exp": { - "type": "integer", - "nullable": true - }, - "mailer_otp_exp": { - "type": "integer" - }, - "mailer_otp_length": { - "type": "integer", - "nullable": true - }, - "mfa_max_enrolled_factors": { - "type": "integer", - "nullable": true - }, - "mfa_phone_otp_length": { - "type": "integer" - }, - "mfa_phone_max_frequency": { - "type": "integer", - "nullable": true - }, - "password_min_length": { - "type": "integer", - "nullable": true - }, - "rate_limit_anonymous_users": { - "type": "integer", - "nullable": true - }, - "rate_limit_email_sent": { - "type": "integer", - "nullable": true - }, - "rate_limit_sms_sent": { - "type": "integer", - "nullable": true - }, - "rate_limit_token_refresh": { - "type": "integer", - "nullable": true - }, - "rate_limit_verify": { - "type": "integer", - "nullable": true - }, - "rate_limit_otp": { - "type": "integer", - "nullable": true - }, - "security_refresh_token_reuse_interval": { - "type": "integer", - "nullable": true - }, - "sessions_inactivity_timeout": { - "type": "integer", - "nullable": true - }, - "sessions_timebox": { - "type": "integer", - "nullable": true - }, - "sms_max_frequency": { - "type": "integer", - "nullable": true - }, - "sms_otp_exp": { - "type": "integer", - "nullable": true - }, - "sms_otp_length": { - "type": "integer" - }, - "smtp_max_frequency": { - "type": "integer", - "nullable": true - }, "disable_signup": { "type": "boolean", "nullable": true @@ -14303,6 +16668,10 @@ "type": "string", "nullable": true }, + "external_web3_solana_enabled": { + "type": "boolean", + "nullable": true + }, "external_zoom_client_id": { "type": "string", "nullable": true @@ -14375,6 +16744,22 @@ "type": "string", "nullable": true }, + "hook_before_user_created_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_before_user_created_uri": { + "type": "string", + "nullable": true + }, + "hook_before_user_created_secrets": { + "type": "string", + "nullable": true + }, + "jwt_exp": { + "type": "integer", + "nullable": true + }, "mailer_allow_unverified_email_sign_ins": { "type": "boolean", "nullable": true @@ -14383,6 +16768,13 @@ "type": "boolean", "nullable": true }, + "mailer_otp_exp": { + "type": "integer" + }, + "mailer_otp_length": { + "type": "integer", + "nullable": true + }, "mailer_secure_email_change_enabled": { "type": "boolean", "nullable": true @@ -14435,6 +16827,10 @@ "type": "string", "nullable": true }, + "mfa_max_enrolled_factors": { + "type": "integer", + "nullable": true + }, "mfa_totp_enroll_enabled": { "type": "boolean", "nullable": true @@ -14459,16 +16855,61 @@ "type": "boolean", "nullable": true }, + "mfa_phone_otp_length": { + "type": "integer" + }, "mfa_phone_template": { "type": "string", "nullable": true }, + "mfa_phone_max_frequency": { + "type": "integer", + "nullable": true + }, "password_hibp_enabled": { "type": "boolean", "nullable": true }, + "password_min_length": { + "type": "integer", + "nullable": true + }, "password_required_characters": { "type": "string", + "enum": [ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", + "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", + "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\\\:\"|<>?,./`~", + "" + ], + "nullable": true + }, + "rate_limit_anonymous_users": { + "type": "integer", + "nullable": true + }, + "rate_limit_email_sent": { + "type": "integer", + "nullable": true + }, + "rate_limit_sms_sent": { + "type": "integer", + "nullable": true + }, + "rate_limit_token_refresh": { + "type": "integer", + "nullable": true + }, + "rate_limit_verify": { + "type": "integer", + "nullable": true + }, + "rate_limit_otp": { + "type": "integer", + "nullable": true + }, + "rate_limit_web3": { + "type": "integer", "nullable": true }, "refresh_token_rotation_enabled": { @@ -14493,6 +16934,7 @@ }, "security_captcha_provider": { "type": "string", + "enum": ["turnstile", "hcaptcha"], "nullable": true }, "security_captcha_secret": { @@ -14503,10 +16945,18 @@ "type": "boolean", "nullable": true }, + "security_refresh_token_reuse_interval": { + "type": "integer", + "nullable": true + }, "security_update_password_require_reauthentication": { "type": "boolean", "nullable": true }, + "sessions_inactivity_timeout": { + "type": "integer", + "nullable": true + }, "sessions_single_per_user": { "type": "boolean", "nullable": true @@ -14515,6 +16965,10 @@ "type": "string", "nullable": true }, + "sessions_timebox": { + "type": "integer", + "nullable": true + }, "site_url": { "type": "string", "nullable": true @@ -14523,6 +16977,10 @@ "type": "boolean", "nullable": true }, + "sms_max_frequency": { + "type": "integer", + "nullable": true + }, "sms_messagebird_access_key": { "type": "string", "nullable": true @@ -14531,8 +16989,16 @@ "type": "string", "nullable": true }, + "sms_otp_exp": { + "type": "integer", + "nullable": true + }, + "sms_otp_length": { + "type": "integer" + }, "sms_provider": { "type": "string", + "enum": ["messagebird", "textlocal", "twilio", "twilio_verify", "vonage"], "nullable": true }, "sms_template": { @@ -14545,6 +17011,7 @@ }, "sms_test_otp_valid_until": { "type": "string", + "format": "date-time", "nullable": true }, "sms_textlocal_api_key": { @@ -14597,12 +17064,17 @@ }, "smtp_admin_email": { "type": "string", + "format": "email", "nullable": true }, "smtp_host": { "type": "string", "nullable": true }, + "smtp_max_frequency": { + "type": "integer", + "nullable": true + }, "smtp_pass": { "type": "string", "nullable": true @@ -14627,26 +17099,6 @@ "required": [ "api_max_request_duration", "db_max_pool_size", - "jwt_exp", - "mailer_otp_exp", - "mailer_otp_length", - "mfa_max_enrolled_factors", - "mfa_phone_otp_length", - "mfa_phone_max_frequency", - "password_min_length", - "rate_limit_anonymous_users", - "rate_limit_email_sent", - "rate_limit_sms_sent", - "rate_limit_token_refresh", - "rate_limit_verify", - "rate_limit_otp", - "security_refresh_token_reuse_interval", - "sessions_inactivity_timeout", - "sessions_timebox", - "sms_max_frequency", - "sms_otp_exp", - "sms_otp_length", - "smtp_max_frequency", "disable_signup", "external_anonymous_users_enabled", "external_apple_additional_client_ids", @@ -14715,6 +17167,7 @@ "external_workos_enabled", "external_workos_secret", "external_workos_url", + "external_web3_solana_enabled", "external_zoom_client_id", "external_zoom_enabled", "external_zoom_secret", @@ -14733,8 +17186,14 @@ "hook_send_email_enabled", "hook_send_email_uri", "hook_send_email_secrets", + "hook_before_user_created_enabled", + "hook_before_user_created_uri", + "hook_before_user_created_secrets", + "jwt_exp", "mailer_allow_unverified_email_sign_ins", "mailer_autoconfirm", + "mailer_otp_exp", + "mailer_otp_length", "mailer_secure_email_change_enabled", "mailer_subjects_confirmation", "mailer_subjects_email_change", @@ -14748,15 +17207,26 @@ "mailer_templates_magic_link_content", "mailer_templates_reauthentication_content", "mailer_templates_recovery_content", + "mfa_max_enrolled_factors", "mfa_totp_enroll_enabled", "mfa_totp_verify_enabled", "mfa_phone_enroll_enabled", "mfa_phone_verify_enabled", "mfa_web_authn_enroll_enabled", "mfa_web_authn_verify_enabled", + "mfa_phone_otp_length", "mfa_phone_template", + "mfa_phone_max_frequency", "password_hibp_enabled", + "password_min_length", "password_required_characters", + "rate_limit_anonymous_users", + "rate_limit_email_sent", + "rate_limit_sms_sent", + "rate_limit_token_refresh", + "rate_limit_verify", + "rate_limit_otp", + "rate_limit_web3", "refresh_token_rotation_enabled", "saml_enabled", "saml_external_url", @@ -14765,13 +17235,19 @@ "security_captcha_provider", "security_captcha_secret", "security_manual_linking_enabled", + "security_refresh_token_reuse_interval", "security_update_password_require_reauthentication", + "sessions_inactivity_timeout", "sessions_single_per_user", "sessions_tags", + "sessions_timebox", "site_url", "sms_autoconfirm", + "sms_max_frequency", "sms_messagebird_access_key", "sms_messagebird_originator", + "sms_otp_exp", + "sms_otp_length", "sms_provider", "sms_template", "sms_test_otp", @@ -14790,6 +17266,7 @@ "sms_vonage_from", "smtp_admin_email", "smtp_host", + "smtp_max_frequency", "smtp_pass", "smtp_port", "smtp_sender_name", @@ -14800,68 +17277,254 @@ "UpdateAuthConfigBody": { "type": "object", "properties": { + "site_url": { + "type": "string", + "pattern": "^[^,]+$", + "nullable": true + }, + "disable_signup": { + "type": "boolean", + "nullable": true + }, "jwt_exp": { "type": "integer", "minimum": 0, - "maximum": 604800 + "maximum": 604800, + "nullable": true + }, + "smtp_admin_email": { + "type": "string", + "format": "email", + "nullable": true + }, + "smtp_host": { + "type": "string", + "nullable": true + }, + "smtp_port": { + "type": "string", + "nullable": true + }, + "smtp_user": { + "type": "string", + "nullable": true + }, + "smtp_pass": { + "type": "string", + "nullable": true }, "smtp_max_frequency": { "type": "integer", "minimum": 0, - "maximum": 32767 + "maximum": 32767, + "nullable": true + }, + "smtp_sender_name": { + "type": "string", + "nullable": true + }, + "mailer_allow_unverified_email_sign_ins": { + "type": "boolean", + "nullable": true + }, + "mailer_autoconfirm": { + "type": "boolean", + "nullable": true + }, + "mailer_subjects_invite": { + "type": "string", + "nullable": true + }, + "mailer_subjects_confirmation": { + "type": "string", + "nullable": true + }, + "mailer_subjects_recovery": { + "type": "string", + "nullable": true + }, + "mailer_subjects_email_change": { + "type": "string", + "nullable": true + }, + "mailer_subjects_magic_link": { + "type": "string", + "nullable": true + }, + "mailer_subjects_reauthentication": { + "type": "string", + "nullable": true + }, + "mailer_templates_invite_content": { + "type": "string", + "nullable": true + }, + "mailer_templates_confirmation_content": { + "type": "string", + "nullable": true + }, + "mailer_templates_recovery_content": { + "type": "string", + "nullable": true + }, + "mailer_templates_email_change_content": { + "type": "string", + "nullable": true + }, + "mailer_templates_magic_link_content": { + "type": "string", + "nullable": true + }, + "mailer_templates_reauthentication_content": { + "type": "string", + "nullable": true }, "mfa_max_enrolled_factors": { "type": "integer", "minimum": 0, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true + }, + "uri_allow_list": { + "type": "string", + "nullable": true + }, + "external_anonymous_users_enabled": { + "type": "boolean", + "nullable": true + }, + "external_email_enabled": { + "type": "boolean", + "nullable": true + }, + "external_phone_enabled": { + "type": "boolean", + "nullable": true + }, + "saml_enabled": { + "type": "boolean", + "nullable": true + }, + "saml_external_url": { + "type": "string", + "pattern": "^[^,]+$", + "nullable": true + }, + "security_captcha_enabled": { + "type": "boolean", + "nullable": true + }, + "security_captcha_provider": { + "type": "string", + "enum": ["turnstile", "hcaptcha"], + "nullable": true + }, + "security_captcha_secret": { + "type": "string", + "nullable": true }, "sessions_timebox": { "type": "integer", - "minimum": 0 + "minimum": 0, + "nullable": true }, "sessions_inactivity_timeout": { "type": "integer", - "minimum": 0 + "minimum": 0, + "nullable": true + }, + "sessions_single_per_user": { + "type": "boolean", + "nullable": true + }, + "sessions_tags": { + "type": "string", + "pattern": "^\\s*([a-zA-Z0-9_-]+(\\s*,+\\s*)?)*\\s*$", + "nullable": true }, "rate_limit_anonymous_users": { "type": "integer", "minimum": 1, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "rate_limit_email_sent": { "type": "integer", "minimum": 1, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "rate_limit_sms_sent": { "type": "integer", "minimum": 1, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "rate_limit_verify": { "type": "integer", "minimum": 1, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "rate_limit_token_refresh": { "type": "integer", "minimum": 1, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "rate_limit_otp": { "type": "integer", "minimum": 1, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true + }, + "rate_limit_web3": { + "type": "integer", + "minimum": 1, + "maximum": 2147483647, + "nullable": true + }, + "mailer_secure_email_change_enabled": { + "type": "boolean", + "nullable": true + }, + "refresh_token_rotation_enabled": { + "type": "boolean", + "nullable": true + }, + "password_hibp_enabled": { + "type": "boolean", + "nullable": true }, "password_min_length": { "type": "integer", "minimum": 6, - "maximum": 32767 + "maximum": 32767, + "nullable": true + }, + "password_required_characters": { + "type": "string", + "enum": [ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", + "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", + "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\\\:\"|<>?,./`~", + "" + ], + "nullable": true + }, + "security_manual_linking_enabled": { + "type": "boolean", + "nullable": true + }, + "security_update_password_require_reauthentication": { + "type": "boolean", + "nullable": true }, "security_refresh_token_reuse_interval": { "type": "integer", "minimum": 0, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "mailer_otp_exp": { "type": "integer", @@ -14871,489 +17534,496 @@ "mailer_otp_length": { "type": "integer", "minimum": 6, - "maximum": 10 + "maximum": 10, + "nullable": true + }, + "sms_autoconfirm": { + "type": "boolean", + "nullable": true }, "sms_max_frequency": { "type": "integer", "minimum": 0, - "maximum": 32767 + "maximum": 32767, + "nullable": true }, "sms_otp_exp": { "type": "integer", "minimum": 0, - "maximum": 2147483647 + "maximum": 2147483647, + "nullable": true }, "sms_otp_length": { "type": "integer", "minimum": 0, "maximum": 32767 }, + "sms_provider": { + "type": "string", + "enum": ["messagebird", "textlocal", "twilio", "twilio_verify", "vonage"], + "nullable": true + }, + "sms_messagebird_access_key": { + "type": "string", + "nullable": true + }, + "sms_messagebird_originator": { + "type": "string", + "nullable": true + }, + "sms_test_otp": { + "type": "string", + "pattern": "^([0-9]{1,15}=[0-9]+,?)*$", + "nullable": true + }, + "sms_test_otp_valid_until": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "sms_textlocal_api_key": { + "type": "string", + "nullable": true + }, + "sms_textlocal_sender": { + "type": "string", + "nullable": true + }, + "sms_twilio_account_sid": { + "type": "string", + "nullable": true + }, + "sms_twilio_auth_token": { + "type": "string", + "nullable": true + }, + "sms_twilio_content_sid": { + "type": "string", + "nullable": true + }, + "sms_twilio_message_service_sid": { + "type": "string", + "nullable": true + }, + "sms_twilio_verify_account_sid": { + "type": "string", + "nullable": true + }, + "sms_twilio_verify_auth_token": { + "type": "string", + "nullable": true + }, + "sms_twilio_verify_message_service_sid": { + "type": "string", + "nullable": true + }, + "sms_vonage_api_key": { + "type": "string", + "nullable": true + }, + "sms_vonage_api_secret": { + "type": "string", + "nullable": true + }, + "sms_vonage_from": { + "type": "string", + "nullable": true + }, + "sms_template": { + "type": "string", + "nullable": true + }, + "hook_mfa_verification_attempt_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_mfa_verification_attempt_uri": { + "type": "string", + "nullable": true + }, + "hook_mfa_verification_attempt_secrets": { + "type": "string", + "nullable": true + }, + "hook_password_verification_attempt_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_password_verification_attempt_uri": { + "type": "string", + "nullable": true + }, + "hook_password_verification_attempt_secrets": { + "type": "string", + "nullable": true + }, + "hook_custom_access_token_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_custom_access_token_uri": { + "type": "string", + "nullable": true + }, + "hook_custom_access_token_secrets": { + "type": "string", + "nullable": true + }, + "hook_send_sms_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_send_sms_uri": { + "type": "string", + "nullable": true + }, + "hook_send_sms_secrets": { + "type": "string", + "nullable": true + }, + "hook_send_email_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_send_email_uri": { + "type": "string", + "nullable": true + }, + "hook_send_email_secrets": { + "type": "string", + "nullable": true + }, + "hook_before_user_created_enabled": { + "type": "boolean", + "nullable": true + }, + "hook_before_user_created_uri": { + "type": "string", + "nullable": true + }, + "hook_before_user_created_secrets": { + "type": "string", + "nullable": true + }, + "external_apple_enabled": { + "type": "boolean", + "nullable": true + }, + "external_apple_client_id": { + "type": "string", + "nullable": true + }, + "external_apple_secret": { + "type": "string", + "nullable": true + }, + "external_apple_additional_client_ids": { + "type": "string", + "nullable": true + }, + "external_azure_enabled": { + "type": "boolean", + "nullable": true + }, + "external_azure_client_id": { + "type": "string", + "nullable": true + }, + "external_azure_secret": { + "type": "string", + "nullable": true + }, + "external_azure_url": { + "type": "string", + "nullable": true + }, + "external_bitbucket_enabled": { + "type": "boolean", + "nullable": true + }, + "external_bitbucket_client_id": { + "type": "string", + "nullable": true + }, + "external_bitbucket_secret": { + "type": "string", + "nullable": true + }, + "external_discord_enabled": { + "type": "boolean", + "nullable": true + }, + "external_discord_client_id": { + "type": "string", + "nullable": true + }, + "external_discord_secret": { + "type": "string", + "nullable": true + }, + "external_facebook_enabled": { + "type": "boolean", + "nullable": true + }, + "external_facebook_client_id": { + "type": "string", + "nullable": true + }, + "external_facebook_secret": { + "type": "string", + "nullable": true + }, + "external_figma_enabled": { + "type": "boolean", + "nullable": true + }, + "external_figma_client_id": { + "type": "string", + "nullable": true + }, + "external_figma_secret": { + "type": "string", + "nullable": true + }, + "external_github_enabled": { + "type": "boolean", + "nullable": true + }, + "external_github_client_id": { + "type": "string", + "nullable": true + }, + "external_github_secret": { + "type": "string", + "nullable": true + }, + "external_gitlab_enabled": { + "type": "boolean", + "nullable": true + }, + "external_gitlab_client_id": { + "type": "string", + "nullable": true + }, + "external_gitlab_secret": { + "type": "string", + "nullable": true + }, + "external_gitlab_url": { + "type": "string", + "nullable": true + }, + "external_google_enabled": { + "type": "boolean", + "nullable": true + }, + "external_google_client_id": { + "type": "string", + "nullable": true + }, + "external_google_secret": { + "type": "string", + "nullable": true + }, + "external_google_additional_client_ids": { + "type": "string", + "nullable": true + }, + "external_google_skip_nonce_check": { + "type": "boolean", + "nullable": true + }, + "external_kakao_enabled": { + "type": "boolean", + "nullable": true + }, + "external_kakao_client_id": { + "type": "string", + "nullable": true + }, + "external_kakao_secret": { + "type": "string", + "nullable": true + }, + "external_keycloak_enabled": { + "type": "boolean", + "nullable": true + }, + "external_keycloak_client_id": { + "type": "string", + "nullable": true + }, + "external_keycloak_secret": { + "type": "string", + "nullable": true + }, + "external_keycloak_url": { + "type": "string", + "nullable": true + }, + "external_linkedin_oidc_enabled": { + "type": "boolean", + "nullable": true + }, + "external_linkedin_oidc_client_id": { + "type": "string", + "nullable": true + }, + "external_linkedin_oidc_secret": { + "type": "string", + "nullable": true + }, + "external_slack_oidc_enabled": { + "type": "boolean", + "nullable": true + }, + "external_slack_oidc_client_id": { + "type": "string", + "nullable": true + }, + "external_slack_oidc_secret": { + "type": "string", + "nullable": true + }, + "external_notion_enabled": { + "type": "boolean", + "nullable": true + }, + "external_notion_client_id": { + "type": "string", + "nullable": true + }, + "external_notion_secret": { + "type": "string", + "nullable": true + }, + "external_slack_enabled": { + "type": "boolean", + "nullable": true + }, + "external_slack_client_id": { + "type": "string", + "nullable": true + }, + "external_slack_secret": { + "type": "string", + "nullable": true + }, + "external_spotify_enabled": { + "type": "boolean", + "nullable": true + }, + "external_spotify_client_id": { + "type": "string", + "nullable": true + }, + "external_spotify_secret": { + "type": "string", + "nullable": true + }, + "external_twitch_enabled": { + "type": "boolean", + "nullable": true + }, + "external_twitch_client_id": { + "type": "string", + "nullable": true + }, + "external_twitch_secret": { + "type": "string", + "nullable": true + }, + "external_twitter_enabled": { + "type": "boolean", + "nullable": true + }, + "external_twitter_client_id": { + "type": "string", + "nullable": true + }, + "external_twitter_secret": { + "type": "string", + "nullable": true + }, + "external_workos_enabled": { + "type": "boolean", + "nullable": true + }, + "external_workos_client_id": { + "type": "string", + "nullable": true + }, + "external_workos_secret": { + "type": "string", + "nullable": true + }, + "external_workos_url": { + "type": "string", + "nullable": true + }, + "external_web3_solana_enabled": { + "type": "boolean", + "nullable": true + }, + "external_zoom_enabled": { + "type": "boolean", + "nullable": true + }, + "external_zoom_client_id": { + "type": "string", + "nullable": true + }, + "external_zoom_secret": { + "type": "string", + "nullable": true + }, "db_max_pool_size": { - "type": "integer" + "type": "integer", + "nullable": true }, "api_max_request_duration": { - "type": "integer" + "type": "integer", + "nullable": true + }, + "mfa_totp_enroll_enabled": { + "type": "boolean", + "nullable": true + }, + "mfa_totp_verify_enabled": { + "type": "boolean", + "nullable": true + }, + "mfa_web_authn_enroll_enabled": { + "type": "boolean", + "nullable": true + }, + "mfa_web_authn_verify_enabled": { + "type": "boolean", + "nullable": true + }, + "mfa_phone_enroll_enabled": { + "type": "boolean", + "nullable": true + }, + "mfa_phone_verify_enabled": { + "type": "boolean", + "nullable": true }, "mfa_phone_max_frequency": { "type": "integer", "minimum": 0, - "maximum": 32767 + "maximum": 32767, + "nullable": true }, "mfa_phone_otp_length": { "type": "integer", "minimum": 0, - "maximum": 32767 - }, - "site_url": { - "type": "string", - "pattern": "/^[^,]+$/" - }, - "disable_signup": { - "type": "boolean" - }, - "smtp_admin_email": { - "type": "string" - }, - "smtp_host": { - "type": "string" - }, - "smtp_port": { - "type": "string" - }, - "smtp_user": { - "type": "string" - }, - "smtp_pass": { - "type": "string" - }, - "smtp_sender_name": { - "type": "string" - }, - "mailer_allow_unverified_email_sign_ins": { - "type": "boolean" - }, - "mailer_autoconfirm": { - "type": "boolean" - }, - "mailer_subjects_invite": { - "type": "string" - }, - "mailer_subjects_confirmation": { - "type": "string" - }, - "mailer_subjects_recovery": { - "type": "string" - }, - "mailer_subjects_email_change": { - "type": "string" - }, - "mailer_subjects_magic_link": { - "type": "string" - }, - "mailer_subjects_reauthentication": { - "type": "string" - }, - "mailer_templates_invite_content": { - "type": "string" - }, - "mailer_templates_confirmation_content": { - "type": "string" - }, - "mailer_templates_recovery_content": { - "type": "string" - }, - "mailer_templates_email_change_content": { - "type": "string" - }, - "mailer_templates_magic_link_content": { - "type": "string" - }, - "mailer_templates_reauthentication_content": { - "type": "string" - }, - "uri_allow_list": { - "type": "string" - }, - "external_anonymous_users_enabled": { - "type": "boolean" - }, - "external_email_enabled": { - "type": "boolean" - }, - "external_phone_enabled": { - "type": "boolean" - }, - "saml_enabled": { - "type": "boolean" - }, - "saml_external_url": { - "type": "string", - "pattern": "/^[^,]+$/" - }, - "security_captcha_enabled": { - "type": "boolean" - }, - "security_captcha_provider": { - "type": "string" - }, - "security_captcha_secret": { - "type": "string" - }, - "sessions_single_per_user": { - "type": "boolean" - }, - "sessions_tags": { - "type": "string", - "pattern": "/^\\s*([a-z0-9_-]+(\\s*,+\\s*)?)*\\s*$/i" - }, - "mailer_secure_email_change_enabled": { - "type": "boolean" - }, - "refresh_token_rotation_enabled": { - "type": "boolean" - }, - "password_hibp_enabled": { - "type": "boolean" - }, - "password_required_characters": { - "type": "string", - "enum": [ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", - "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789", - "abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789:!@#$%^&*()_+-=[]{};'\\\\:\"|<>?,./`~", - "" - ] - }, - "security_manual_linking_enabled": { - "type": "boolean" - }, - "security_update_password_require_reauthentication": { - "type": "boolean" - }, - "sms_autoconfirm": { - "type": "boolean" - }, - "sms_provider": { - "type": "string" - }, - "sms_messagebird_access_key": { - "type": "string" - }, - "sms_messagebird_originator": { - "type": "string" - }, - "sms_test_otp": { - "type": "string", - "pattern": "/^([0-9]{1,15}=[0-9]+,?)*$/" - }, - "sms_test_otp_valid_until": { - "type": "string" - }, - "sms_textlocal_api_key": { - "type": "string" - }, - "sms_textlocal_sender": { - "type": "string" - }, - "sms_twilio_account_sid": { - "type": "string" - }, - "sms_twilio_auth_token": { - "type": "string" - }, - "sms_twilio_content_sid": { - "type": "string" - }, - "sms_twilio_message_service_sid": { - "type": "string" - }, - "sms_twilio_verify_account_sid": { - "type": "string" - }, - "sms_twilio_verify_auth_token": { - "type": "string" - }, - "sms_twilio_verify_message_service_sid": { - "type": "string" - }, - "sms_vonage_api_key": { - "type": "string" - }, - "sms_vonage_api_secret": { - "type": "string" - }, - "sms_vonage_from": { - "type": "string" - }, - "sms_template": { - "type": "string" - }, - "hook_mfa_verification_attempt_enabled": { - "type": "boolean" - }, - "hook_mfa_verification_attempt_uri": { - "type": "string" - }, - "hook_mfa_verification_attempt_secrets": { - "type": "string" - }, - "hook_password_verification_attempt_enabled": { - "type": "boolean" - }, - "hook_password_verification_attempt_uri": { - "type": "string" - }, - "hook_password_verification_attempt_secrets": { - "type": "string" - }, - "hook_custom_access_token_enabled": { - "type": "boolean" - }, - "hook_custom_access_token_uri": { - "type": "string" - }, - "hook_custom_access_token_secrets": { - "type": "string" - }, - "hook_send_sms_enabled": { - "type": "boolean" - }, - "hook_send_sms_uri": { - "type": "string" - }, - "hook_send_sms_secrets": { - "type": "string" - }, - "hook_send_email_enabled": { - "type": "boolean" - }, - "hook_send_email_uri": { - "type": "string" - }, - "hook_send_email_secrets": { - "type": "string" - }, - "external_apple_enabled": { - "type": "boolean" - }, - "external_apple_client_id": { - "type": "string" - }, - "external_apple_secret": { - "type": "string" - }, - "external_apple_additional_client_ids": { - "type": "string" - }, - "external_azure_enabled": { - "type": "boolean" - }, - "external_azure_client_id": { - "type": "string" - }, - "external_azure_secret": { - "type": "string" - }, - "external_azure_url": { - "type": "string" - }, - "external_bitbucket_enabled": { - "type": "boolean" - }, - "external_bitbucket_client_id": { - "type": "string" - }, - "external_bitbucket_secret": { - "type": "string" - }, - "external_discord_enabled": { - "type": "boolean" - }, - "external_discord_client_id": { - "type": "string" - }, - "external_discord_secret": { - "type": "string" - }, - "external_facebook_enabled": { - "type": "boolean" - }, - "external_facebook_client_id": { - "type": "string" - }, - "external_facebook_secret": { - "type": "string" - }, - "external_figma_enabled": { - "type": "boolean" - }, - "external_figma_client_id": { - "type": "string" - }, - "external_figma_secret": { - "type": "string" - }, - "external_github_enabled": { - "type": "boolean" - }, - "external_github_client_id": { - "type": "string" - }, - "external_github_secret": { - "type": "string" - }, - "external_gitlab_enabled": { - "type": "boolean" - }, - "external_gitlab_client_id": { - "type": "string" - }, - "external_gitlab_secret": { - "type": "string" - }, - "external_gitlab_url": { - "type": "string" - }, - "external_google_enabled": { - "type": "boolean" - }, - "external_google_client_id": { - "type": "string" - }, - "external_google_secret": { - "type": "string" - }, - "external_google_additional_client_ids": { - "type": "string" - }, - "external_google_skip_nonce_check": { - "type": "boolean" - }, - "external_kakao_enabled": { - "type": "boolean" - }, - "external_kakao_client_id": { - "type": "string" - }, - "external_kakao_secret": { - "type": "string" - }, - "external_keycloak_enabled": { - "type": "boolean" - }, - "external_keycloak_client_id": { - "type": "string" - }, - "external_keycloak_secret": { - "type": "string" - }, - "external_keycloak_url": { - "type": "string" - }, - "external_linkedin_oidc_enabled": { - "type": "boolean" - }, - "external_linkedin_oidc_client_id": { - "type": "string" - }, - "external_linkedin_oidc_secret": { - "type": "string" - }, - "external_slack_oidc_enabled": { - "type": "boolean" - }, - "external_slack_oidc_client_id": { - "type": "string" - }, - "external_slack_oidc_secret": { - "type": "string" - }, - "external_notion_enabled": { - "type": "boolean" - }, - "external_notion_client_id": { - "type": "string" - }, - "external_notion_secret": { - "type": "string" - }, - "external_slack_enabled": { - "type": "boolean" - }, - "external_slack_client_id": { - "type": "string" - }, - "external_slack_secret": { - "type": "string" - }, - "external_spotify_enabled": { - "type": "boolean" - }, - "external_spotify_client_id": { - "type": "string" - }, - "external_spotify_secret": { - "type": "string" - }, - "external_twitch_enabled": { - "type": "boolean" - }, - "external_twitch_client_id": { - "type": "string" - }, - "external_twitch_secret": { - "type": "string" - }, - "external_twitter_enabled": { - "type": "boolean" - }, - "external_twitter_client_id": { - "type": "string" - }, - "external_twitter_secret": { - "type": "string" - }, - "external_workos_enabled": { - "type": "boolean" - }, - "external_workos_client_id": { - "type": "string" - }, - "external_workos_secret": { - "type": "string" - }, - "external_workos_url": { - "type": "string" - }, - "external_zoom_enabled": { - "type": "boolean" - }, - "external_zoom_client_id": { - "type": "string" - }, - "external_zoom_secret": { - "type": "string" - }, - "mfa_totp_enroll_enabled": { - "type": "boolean" - }, - "mfa_totp_verify_enabled": { - "type": "boolean" - }, - "mfa_web_authn_enroll_enabled": { - "type": "boolean" - }, - "mfa_web_authn_verify_enabled": { - "type": "boolean" - }, - "mfa_phone_enroll_enabled": { - "type": "boolean" - }, - "mfa_phone_verify_enabled": { - "type": "boolean" + "maximum": 32767, + "nullable": true }, "mfa_phone_template": { - "type": "string" + "type": "string", + "nullable": true } } }, @@ -15366,16 +18036,15 @@ "jwks_url": { "type": "string" }, - "custom_jwks": { - "type": "object" - } + "custom_jwks": {} } }, "ThirdPartyAuth": { "type": "object", "properties": { "id": { - "type": "string" + "type": "string", + "format": "uuid" }, "type": { "type": "string" @@ -15389,11 +18058,9 @@ "nullable": true }, "custom_jwks": { - "type": "object", "nullable": true }, "resolved_jwks": { - "type": "object", "nullable": true }, "inserted_at": { @@ -15409,23 +18076,6 @@ }, "required": ["id", "type", "inserted_at", "updated_at"] }, - "ProjectAvailableRestoreVersion": { - "type": "object", - "properties": { - "version": { - "type": "string" - }, - "release_channel": { - "type": "string", - "enum": ["internal", "alpha", "beta", "ga", "withdrawn", "preview"] - }, - "postgres_engine": { - "type": "string", - "enum": ["13", "14", "15", "17", "17-oriole"] - } - }, - "required": ["version", "release_channel", "postgres_engine"] - }, "GetProjectAvailableRestoreVersionsResponse": { "type": "object", "properties": { @@ -15452,12 +18102,7 @@ }, "required": ["available_versions"] }, - "RestoreProjectBodyDto": { - "type": "object", - "properties": {}, - "hideDefinitions": ["release_channel", "postgres_engine"] - }, - "ListProjectAddonsResponseDto": { + "ListProjectAddonsResponse": { "type": "object", "properties": { "selected_addons": { @@ -15494,7 +18139,15 @@ "ci_4xlarge", "ci_8xlarge", "ci_12xlarge", - "ci_16xlarge" + "ci_16xlarge", + "ci_24xlarge", + "ci_24xlarge_optimized_cpu", + "ci_24xlarge_optimized_memory", + "ci_24xlarge_high_memory", + "ci_48xlarge", + "ci_48xlarge_optimized_cpu", + "ci_48xlarge_optimized_memory", + "ci_48xlarge_high_memory" ] }, { @@ -15547,26 +18200,7 @@ "required": ["description", "type", "interval", "amount"] }, "meta": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "number" - }, - { - "type": "boolean" - }, - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "description": "Any JSON-serializable value" } }, "required": ["id", "name", "price"] @@ -15614,7 +18248,15 @@ "ci_4xlarge", "ci_8xlarge", "ci_12xlarge", - "ci_16xlarge" + "ci_16xlarge", + "ci_24xlarge", + "ci_24xlarge_optimized_cpu", + "ci_24xlarge_optimized_memory", + "ci_24xlarge_high_memory", + "ci_48xlarge", + "ci_48xlarge_optimized_cpu", + "ci_48xlarge_optimized_memory", + "ci_48xlarge_high_memory" ] }, { @@ -15667,26 +18309,7 @@ "required": ["description", "type", "interval", "amount"] }, "meta": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "number" - }, - { - "type": "boolean" - }, - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - } + "description": "Any JSON-serializable value" } }, "required": ["id", "name", "price"] @@ -15699,7 +18322,7 @@ }, "required": ["selected_addons", "available_addons"] }, - "ApplyProjectAddonBodyDto": { + "ApplyProjectAddonBody": { "type": "object", "properties": { "addon_variant": { @@ -15716,7 +18339,15 @@ "ci_4xlarge", "ci_8xlarge", "ci_12xlarge", - "ci_16xlarge" + "ci_16xlarge", + "ci_24xlarge", + "ci_24xlarge_optimized_cpu", + "ci_24xlarge_optimized_memory", + "ci_24xlarge_high_memory", + "ci_48xlarge", + "ci_48xlarge_optimized_cpu", + "ci_48xlarge_optimized_memory", + "ci_48xlarge_high_memory" ] }, { @@ -15748,12 +18379,176 @@ }, "required": ["addon_variant", "addon_type"] }, - "V1AnalyticsResponse": { + "ProjectClaimTokenResponse": { "type": "object", "properties": { + "token_alias": { + "type": "string" + }, + "expires_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string", + "format": "uuid" + } + }, + "required": ["token_alias", "expires_at", "created_at", "created_by"] + }, + "CreateProjectClaimTokenResponse": { + "type": "object", + "properties": { + "token": { + "type": "string" + }, + "token_alias": { + "type": "string" + }, + "expires_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string", + "format": "uuid" + } + }, + "required": ["token", "token_alias", "expires_at", "created_at", "created_by"] + }, + "V1ProjectAdvisorsResponse": { + "type": "object", + "properties": { + "lints": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "enum": [ + "unindexed_foreign_keys", + "auth_users_exposed", + "auth_rls_initplan", + "no_primary_key", + "unused_index", + "multiple_permissive_policies", + "policy_exists_rls_disabled", + "rls_enabled_no_policy", + "duplicate_index", + "security_definer_view", + "function_search_path_mutable", + "rls_disabled_in_public", + "extension_in_public", + "rls_references_user_metadata", + "materialized_view_in_api", + "foreign_table_in_api", + "unsupported_reg_types", + "auth_otp_long_expiry", + "auth_otp_short_length", + "ssl_not_enforced", + "network_restrictions_not_set", + "password_requirements_min_length", + "pitr_not_enabled", + "auth_leaked_password_protection", + "auth_insufficient_mfa_options", + "auth_password_policy_missing", + "leaked_service_key", + "no_backup_admin" + ] + }, + "title": { + "type": "string" + }, + "level": { + "type": "string", + "enum": ["ERROR", "WARN", "INFO"] + }, + "facing": { + "type": "string", + "enum": ["EXTERNAL"] + }, + "categories": { + "type": "array", + "items": { + "type": "string", + "enum": ["PERFORMANCE", "SECURITY"] + } + }, + "description": { + "type": "string" + }, + "detail": { + "type": "string" + }, + "remediation": { + "type": "string" + }, + "metadata": { + "type": "object", + "properties": { + "schema": { + "type": "string" + }, + "name": { + "type": "string" + }, + "entity": { + "type": "string" + }, + "type": { + "type": "string", + "enum": ["table", "view", "auth", "function", "extension", "compliance"] + }, + "fkey_name": { + "type": "string" + }, + "fkey_columns": { + "type": "array", + "items": { + "type": "number" + } + } + } + }, + "cache_key": { + "type": "string" + } + }, + "required": [ + "name", + "title", + "level", + "facing", + "categories", + "description", + "detail", + "remediation", + "cache_key" + ] + } + } + }, + "required": ["lints"] + }, + "AnalyticsResponse": { + "type": "object", + "properties": { + "result": { + "type": "array", + "items": {} + }, "error": { "oneOf": [ { + "type": "string" + }, + { + "type": "object", "properties": { "code": { "type": "number" @@ -15761,6 +18556,7 @@ "errors": { "type": "array", "items": { + "type": "object", "properties": { "domain": { "type": "string" @@ -15777,7 +18573,8 @@ "reason": { "type": "string" } - } + }, + "required": ["domain", "location", "locationType", "message", "reason"] } }, "message": { @@ -15786,18 +18583,10 @@ "status": { "type": "string" } - } - }, - { - "type": "string" + }, + "required": ["code", "errors", "message", "status"] } ] - }, - "result": { - "type": "array", - "items": { - "type": "object" - } } } }, @@ -15817,17 +18606,46 @@ "required": ["version"] } }, + "V1CreateMigrationBody": { + "type": "object", + "properties": { + "query": { + "type": "string", + "minLength": 1 + }, + "name": { + "type": "string" + } + }, + "required": ["query"] + }, + "V1UpsertMigrationBody": { + "type": "object", + "properties": { + "query": { + "type": "string", + "minLength": 1 + }, + "name": { + "type": "string" + } + }, + "required": ["query"] + }, "V1RunQueryBody": { "type": "object", "properties": { "query": { "type": "string", "minLength": 1 + }, + "read_only": { + "type": "boolean" } }, "required": ["query"] }, - "GetProjectDbMetadataResponseDto": { + "GetProjectDbMetadataResponse": { "type": "object", "properties": { "databases": { @@ -15879,10 +18697,12 @@ "type": "integer" }, "created_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "updated_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "verify_jwt": { "type": "boolean" @@ -15895,6 +18715,9 @@ }, "import_map_path": { "type": "string" + }, + "ezbr_sha256": { + "type": "string" } }, "required": ["id", "slug", "name", "status", "version", "created_at", "updated_at"] @@ -15919,42 +18742,49 @@ "required": ["slug", "name", "body"] }, "BulkUpdateFunctionBody": { - "type": "object", - "properties": { - "id": { - "type": "string" + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "slug": { + "type": "string", + "pattern": "^[A-Za-z0-9_-]+$" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": ["ACTIVE", "REMOVED", "THROTTLED"] + }, + "version": { + "type": "integer" + }, + "created_at": { + "type": "integer", + "format": "int64" + }, + "verify_jwt": { + "type": "boolean" + }, + "import_map": { + "type": "boolean" + }, + "entrypoint_path": { + "type": "string" + }, + "import_map_path": { + "type": "string" + }, + "ezbr_sha256": { + "type": "string" + } }, - "slug": { - "type": "string", - "pattern": "^[A-Za-z0-9_-]+$" - }, - "name": { - "type": "string" - }, - "status": { - "type": "string", - "enum": ["ACTIVE", "REMOVED", "THROTTLED"] - }, - "version": { - "type": "integer" - }, - "created_at": { - "type": "integer" - }, - "verify_jwt": { - "type": "boolean" - }, - "import_map": { - "type": "boolean" - }, - "entrypoint_path": { - "type": "string" - }, - "import_map_path": { - "type": "string" - } - }, - "required": ["id", "slug", "name", "status", "version"] + "required": ["id", "slug", "name", "status", "version"] + } }, "BulkUpdateFunctionResponse": { "type": "object", @@ -15981,10 +18811,12 @@ "type": "integer" }, "created_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "updated_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "verify_jwt": { "type": "boolean" @@ -15997,6 +18829,9 @@ }, "import_map_path": { "type": "string" + }, + "ezbr_sha256": { + "type": "string" } }, "required": ["id", "slug", "name", "status", "version", "created_at", "updated_at"] @@ -16062,10 +18897,12 @@ "type": "integer" }, "created_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "updated_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "verify_jwt": { "type": "boolean" @@ -16078,6 +18915,9 @@ }, "import_map_path": { "type": "string" + }, + "ezbr_sha256": { + "type": "string" } }, "required": ["id", "slug", "name", "status", "version"] @@ -16102,10 +18942,12 @@ "type": "integer" }, "created_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "updated_at": { - "type": "integer" + "type": "integer", + "format": "int64" }, "verify_jwt": { "type": "boolean" @@ -16118,10 +18960,17 @@ }, "import_map_path": { "type": "string" + }, + "ezbr_sha256": { + "type": "string" } }, "required": ["id", "slug", "name", "status", "version", "created_at", "updated_at"] }, + "StreamableFile": { + "type": "object", + "properties": {} + }, "V1UpdateFunctionBody": { "type": "object", "properties": { @@ -16160,81 +19009,6 @@ }, "required": ["id", "name", "owner", "created_at", "updated_at", "public"] }, - "AttributeValue": { - "type": "object", - "properties": { - "default": { - "oneOf": [ - { - "type": "object" - }, - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ] - }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, - "array": { - "type": "boolean" - } - } - }, - "AttributeMapping": { - "type": "object", - "properties": { - "keys": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "default": { - "oneOf": [ - { - "type": "object" - }, - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ] - }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, - "array": { - "type": "boolean" - } - } - } - } - }, - "required": ["keys"] - }, "CreateProviderBody": { "type": "object", "properties": { @@ -16263,10 +19037,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -16279,15 +19063,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -16300,84 +19075,6 @@ }, "required": ["type"] }, - "SamlDescriptor": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "entity_id": { - "type": "string" - }, - "metadata_url": { - "type": "string" - }, - "metadata_xml": { - "type": "string" - }, - "attribute_mapping": { - "type": "object", - "properties": { - "keys": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "default": { - "oneOf": [ - { - "type": "object" - }, - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ] - }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, - "array": { - "type": "boolean" - } - } - } - } - }, - "required": ["keys"] - } - }, - "required": ["id", "entity_id"] - }, - "Domain": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "domain": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - }, - "required": ["id"] - }, "CreateProviderResponse": { "type": "object", "properties": { @@ -16407,10 +19104,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -16423,111 +19130,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, - "array": { - "type": "boolean" - } - } - } - } - }, - "required": ["keys"] - } - }, - "required": ["id", "entity_id"] - }, - "domains": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "domain": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - }, - "required": ["id"] - } - }, - "created_at": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - }, - "required": ["id"] - }, - "Provider": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "saml": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "entity_id": { - "type": "string" - }, - "metadata_url": { - "type": "string" - }, - "metadata_xml": { - "type": "string" - }, - "attribute_mapping": { - "type": "object", - "properties": { - "keys": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "default": { - "oneOf": [ - { - "type": "object" - }, - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ] - }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -16604,10 +19206,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -16620,15 +19232,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -16704,10 +19307,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -16720,15 +19333,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -16794,10 +19398,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -16810,15 +19424,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -16859,10 +19464,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -16875,15 +19490,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -16955,10 +19561,20 @@ "additionalProperties": { "type": "object", "properties": { + "name": { + "type": "string" + }, + "names": { + "type": "array", + "items": { + "type": "string" + } + }, "default": { "oneOf": [ { - "type": "object" + "type": "object", + "properties": {} }, { "type": "number" @@ -16971,15 +19587,6 @@ } ] }, - "name": { - "type": "string" - }, - "names": { - "type": "array", - "items": { - "type": "string" - } - }, "array": { "type": "boolean" } @@ -17022,35 +19629,6 @@ }, "required": ["id"] }, - "V1Backup": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": ["COMPLETED", "FAILED", "PENDING", "REMOVED", "ARCHIVED", "CANCELLED"] - }, - "is_physical_backup": { - "type": "boolean" - }, - "inserted_at": { - "type": "string" - } - }, - "required": ["status", "is_physical_backup", "inserted_at"] - }, - "V1PhysicalBackup": { - "type": "object", - "properties": { - "earliest_physical_backup_date_unix": { - "type": "integer", - "format": "int64" - }, - "latest_physical_backup_date_unix": { - "type": "integer", - "format": "int64" - } - } - }, "V1BackupsResponse": { "type": "object", "properties": { @@ -17068,30 +19646,28 @@ "items": { "type": "object", "properties": { + "is_physical_backup": { + "type": "boolean" + }, "status": { "type": "string", "enum": ["COMPLETED", "FAILED", "PENDING", "REMOVED", "ARCHIVED", "CANCELLED"] }, - "is_physical_backup": { - "type": "boolean" - }, "inserted_at": { "type": "string" } }, - "required": ["status", "is_physical_backup", "inserted_at"] + "required": ["is_physical_backup", "status", "inserted_at"] } }, "physical_backup_data": { "type": "object", "properties": { "earliest_physical_backup_date_unix": { - "type": "integer", - "format": "int64" + "type": "integer" }, "latest_physical_backup_date_unix": { - "type": "integer", - "format": "int64" + "type": "integer" } } } @@ -17109,6 +19685,39 @@ }, "required": ["recovery_time_target_unix"] }, + "V1RestorePointPostBody": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 20 + } + }, + "required": ["name"] + }, + "V1RestorePointResponse": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": ["AVAILABLE", "PENDING", "REMOVED"] + } + }, + "required": ["name", "status"] + }, + "V1UndoBody": { + "type": "object", + "properties": { + "name": { + "type": "string", + "maxLength": 20 + } + }, + "required": ["name"] + }, "V1OrganizationMemberResponse": { "type": "object", "properties": { @@ -17130,17 +19739,15 @@ }, "required": ["user_id", "user_name", "role_name", "mfa_enabled"] }, - "BillingPlanId": { - "type": "string", - "enum": ["free", "pro", "team", "enterprise"] - }, - "ReleaseChannel": { - "type": "string", - "enum": ["internal", "alpha", "beta", "ga", "withdrawn", "preview"] - }, "V1OrganizationSlugResponse": { "type": "object", "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, "plan": { "type": "string", "enum": ["free", "pro", "team", "enterprise"] @@ -17149,7 +19756,11 @@ "type": "array", "items": { "type": "string", - "enum": ["AI_SQL_GENERATOR_OPT_IN"] + "enum": [ + "AI_SQL_GENERATOR_OPT_IN", + "AI_DATA_GENERATOR_OPT_IN", + "AI_LOG_GENERATOR_OPT_IN" + ] } }, "allowed_release_channels": { @@ -17158,15 +19769,133 @@ "type": "string", "enum": ["internal", "alpha", "beta", "ga", "withdrawn", "preview"] } - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" } }, - "required": ["opt_in_tags", "allowed_release_channels", "id", "name"] + "required": ["id", "name", "opt_in_tags", "allowed_release_channels"] + }, + "OrganizationProjectClaimResponse": { + "type": "object", + "properties": { + "project": { + "type": "object", + "properties": { + "ref": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": ["ref", "name"] + }, + "preview": { + "type": "object", + "properties": { + "valid": { + "type": "boolean" + }, + "warnings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": ["key", "message"] + } + }, + "errors": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": ["key", "message"] + } + }, + "info": { + "type": "array", + "items": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": ["key", "message"] + } + }, + "members_exceeding_free_project_limit": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "limit": { + "type": "number" + } + }, + "required": ["name", "limit"] + } + }, + "target_organization_eligible": { + "type": "boolean", + "nullable": true + }, + "target_organization_has_free_project_slots": { + "type": "boolean", + "nullable": true + }, + "source_subscription_plan": { + "type": "string", + "enum": ["free", "pro", "team", "enterprise"] + }, + "target_subscription_plan": { + "type": "string", + "enum": ["free", "pro", "team", "enterprise"], + "nullable": true + } + }, + "required": [ + "valid", + "warnings", + "errors", + "info", + "members_exceeding_free_project_limit", + "target_organization_eligible", + "target_organization_has_free_project_slots", + "source_subscription_plan", + "target_subscription_plan" + ] + }, + "expires_at": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "created_by": { + "type": "string", + "format": "uuid" + } + }, + "required": ["project", "preview", "expires_at", "created_at", "created_by"] } } } diff --git a/apps/studio/components/grid/components/header/ExportDialog.tsx b/apps/studio/components/grid/components/header/ExportDialog.tsx index 83d6f275d74..59ffe39bd9a 100644 --- a/apps/studio/components/grid/components/header/ExportDialog.tsx +++ b/apps/studio/components/grid/components/header/ExportDialog.tsx @@ -3,7 +3,6 @@ import { getConnectionStrings } from 'components/interfaces/Connect/DatabaseSett import { useReadReplicasQuery } from 'data/read-replicas/replicas-query' import { pluckObjectFields } from 'lib/helpers' import { useState } from 'react' -import { useTableEditorTableStateSnapshot } from 'state/table-editor-table' import { Button, cn, @@ -23,13 +22,14 @@ import { import { Admonition } from 'ui-patterns' interface ExportDialogProps { + table?: { name: string; schema: string } open: boolean onOpenChange: (open: boolean) => void } -export const ExportDialog = ({ open, onOpenChange }: ExportDialogProps) => { +export const ExportDialog = ({ table, open, onOpenChange }: ExportDialogProps) => { const { ref: projectRef } = useParams() - const snap = useTableEditorTableStateSnapshot() + const [selectedTab, setSelectedTab] = useState('csv') const { data: databases } = useReadReplicasQuery({ projectRef }) @@ -47,14 +47,14 @@ export const ExportDialog = ({ open, onOpenChange }: ExportDialogProps) => { poolingInfo: { connectionString: '', db_host: '', db_name: '', db_port: 0, db_user: '' }, }) - const outputName = `${snap.table.name}_rows` + const outputName = `${table?.name}_rows` const csvExportCommand = ` -${connectionStrings.direct.psql} -c "COPY (SELECT * FROM "${snap.table.schema}"."${snap.table.name}") TO STDOUT WITH CSV HEADER DELIMITER ',';" > ${outputName}.csv +${connectionStrings.direct.psql} -c "COPY (SELECT * FROM "${table?.schema}"."${table?.name}") TO STDOUT WITH CSV HEADER DELIMITER ',';" > ${outputName}.csv `.trim() const sqlExportCommand = ` -pg_dump -h ${db_host} -p ${db_port} -d ${db_name} -U ${db_user} --table="${snap.table.schema}.${snap.table.name}" --data-only --column-inserts > ${outputName}.sql +pg_dump -h ${db_host} -p ${db_port} -d ${db_name} -U ${db_user} --table="${table?.schema}.${table?.name}" --data-only --column-inserts > ${outputName}.sql `.trim() return ( diff --git a/apps/studio/components/grid/components/header/Header.tsx b/apps/studio/components/grid/components/header/Header.tsx index 67d4d2886e8..baeeecc662f 100644 --- a/apps/studio/components/grid/components/header/Header.tsx +++ b/apps/studio/components/grid/components/header/Header.tsx @@ -17,7 +17,7 @@ import { useTableRowsCountQuery } from 'data/table-rows/table-rows-count-query' import { fetchAllTableRows, useTableRowsQuery } from 'data/table-rows/table-rows-query' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' -import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' +import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { RoleImpersonationState } from 'lib/role-impersonation' import { useRoleImpersonationStateSnapshot, @@ -80,10 +80,12 @@ export default Header const DefaultHeader = () => { const { ref: projectRef } = useParams() - const tableEditorSnap = useTableEditorStateSnapshot() + const { data: org } = useSelectedOrganizationQuery() + const snap = useTableEditorTableStateSnapshot() - const org = useSelectedOrganization() + const tableEditorSnap = useTableEditorStateSnapshot() const canCreateColumns = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'columns') + const { mutate: sendEvent } = useSendEventMutation() const onAddRow = @@ -277,7 +279,7 @@ const RowHeader = () => { }) } - async function onRowsExportCSV() { + const onRowsExportCSV = async () => { setIsExporting(true) if (snap.allRowsSelected && totalRows > MAX_EXPORT_ROW_COUNT) { @@ -349,7 +351,7 @@ const RowHeader = () => { columns: snap.table!.columns.map((column) => column.name), }) const csvData = new Blob([csv], { type: 'text/csv;charset=utf-8;' }) - toast.success(`Downloaded ${rows.length} rows to CSV`, { + toast.success(`Downloaded ${rows.length} rows as CSV`, { id: toastId, closeButton: true, duration: 4000, @@ -358,7 +360,7 @@ const RowHeader = () => { setIsExporting(false) } - async function onRowsExportSQL() { + const onRowsExportSQL = async () => { setIsExporting(true) if (snap.allRowsSelected && totalRows > MAX_EXPORT_ROW_COUNT) { @@ -423,7 +425,7 @@ const RowHeader = () => { const sqlStatements = formatTableRowsToSQL(snap.table, rows) const sqlData = new Blob([sqlStatements], { type: 'text/sql;charset=utf-8;' }) - toast.success(`Downloading ${rows.length} rows to SQL`, { + toast.success(`Downloading ${rows.length} rows as SQL`, { id: toastId, closeButton: true, duration: 4000, @@ -432,6 +434,27 @@ const RowHeader = () => { setIsExporting(false) } + const onRowsExportJSON = async () => { + if (!project) { + return toast.error('Project is required') + } + + setIsExporting(true) + const toastId = toast.loading( + `Exporting ${snap.selectedRows.size} row${snap.selectedRows.size > 1 ? 's' : ''} from ${snap.table.name}` + ) + const rows = allRows.filter((x) => snap.selectedRows.has(x.idx)) + const sqlData = new Blob([JSON.stringify(rows)], { type: 'text/sql;charset=utf-8;' }) + toast.success(`Downloading ${rows.length} rows as JSON`, { + id: toastId, + closeButton: true, + duration: 4000, + }) + saveAs(sqlData, `${snap.table!.name}_rows.json`) + + setIsExporting(false) + } + function deselectRows() { snap.setSelectedRows(new Set()) } @@ -486,13 +509,15 @@ const RowHeader = () => { Export as SQL {/* [Joshen] Should make this available for all cases, but that'll involve updating the Dialog's SQL output to be dynamic based on any filters applied */} - {snap.allRowsSelected && ( + {snap.allRowsSelected ? ( setShowExportModal(true)}>

Export via CLI

Recommended for large tables

+ ) : ( + Export as JSON )} @@ -509,7 +534,11 @@ const RowHeader = () => { )}
- setShowExportModal(false)} /> + setShowExportModal(false)} + /> ) } diff --git a/apps/studio/components/interfaces/Account/NewAccessTokenButton.test.tsx b/apps/studio/components/interfaces/Account/NewAccessTokenButton.test.tsx new file mode 100644 index 00000000000..18cadf221a2 --- /dev/null +++ b/apps/studio/components/interfaces/Account/NewAccessTokenButton.test.tsx @@ -0,0 +1,97 @@ +import { faker } from '@faker-js/faker' +import { screen, waitFor } from '@testing-library/dom' +import userEvent from '@testing-library/user-event' +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { render } from 'tests/helpers' +import { addAPIMock } from 'tests/lib/msw' +import NewAccessTokenButton from './NewAccessTokenButton' + +describe(`NewAccessTokenButton`, () => { + beforeEach(() => { + addAPIMock({ + method: `post`, + path: `/platform/profile/access-tokens`, + response: { + name: faker.lorem.word(), + scope: faker.helpers.arrayElement(['V0', undefined]), + created_at: faker.date.past().toISOString(), + id: faker.number.int(), + token_alias: faker.lorem.words(), + token: faker.lorem.words(), + }, + }) + }) + + it(`generates regular tokens`, async () => { + const onCreateToken = vi.fn() + render() + + const dialogTrigger = screen.getByRole(`button`, { name: `Generate new token` }) + await userEvent.click(dialogTrigger) + + const nameInput = screen.getByLabelText(`Name`) + await userEvent.type(nameInput, `test{enter}`) + + await waitFor(() => expect(onCreateToken).toHaveBeenCalledTimes(1)) + }) + + it(`generates experimental tokens`, async () => { + const onCreateToken = vi.fn() + render() + + const dropdownTrigger = screen.getByTitle(`Choose token scope`) + await userEvent.click(dropdownTrigger) + + const experimentalMenuItem = await screen.findByRole(`menuitem`, { + name: `Generate token for experimental API`, + }) + await userEvent.click(experimentalMenuItem) + + await waitFor(async () => { + await expect( + screen.findByRole(`heading`, { name: `Generate token for experimental API` }) + ).resolves.toBeInTheDocument() + await expect(screen.findByRole(`alert`)).resolves.toBeInTheDocument() + }) + + const nameInput = screen.getByLabelText(`Name`) + await userEvent.type(nameInput, `test{enter}`) + + await waitFor(() => expect(onCreateToken).toHaveBeenCalledTimes(1)) + }) + + it(`resets the form on close/cancel`, async () => { + render() + + // pass 1: open dialog and fill form + const dialogTrigger = screen.getByRole(`button`, { name: `Generate new token` }) + await userEvent.click(dialogTrigger) + + let nameInput = screen.getByLabelText(`Name`) + await userEvent.type(nameInput, `cancel button test`) + expect(nameInput).toHaveValue(`cancel button test`) + + // reset the form by pressing the cancel button + const cancelButton = screen.getByRole(`button`, { name: `Cancel` }) + await userEvent.click(cancelButton) + + // pass 2: check that the form is reset, then fill it again + await userEvent.click(dialogTrigger) + + nameInput = screen.getByLabelText(`Name`) + expect(nameInput).not.toHaveValue(`cancel button test`) + + await userEvent.type(nameInput, `close modal test`) + expect(nameInput).toHaveValue(`close modal test`) + + // reset the form by closing the dialog + await userEvent.keyboard(`{Escape}`) + + // pass 3: check that the form has been rest again + await userEvent.click(dialogTrigger) + + nameInput = screen.getByLabelText(`Name`) + expect(nameInput).not.toHaveValue(`close modal test`) + }) +}) diff --git a/apps/studio/components/interfaces/Account/NewAccessTokenButton.tsx b/apps/studio/components/interfaces/Account/NewAccessTokenButton.tsx index 360546f9f4a..17a976ac4c8 100644 --- a/apps/studio/components/interfaces/Account/NewAccessTokenButton.tsx +++ b/apps/studio/components/interfaces/Account/NewAccessTokenButton.tsx @@ -1,157 +1,187 @@ +import { zodResolver } from '@hookform/resolvers/zod' import { ChevronDown, ExternalLink } from 'lucide-react' import Link from 'next/link' import { useState } from 'react' +import { type SubmitHandler, useForm } from 'react-hook-form' +import { toast } from 'sonner' +import { z } from 'zod' import { useAccessTokenCreateMutation } from 'data/access-tokens/access-tokens-create-mutation' import { Button, + Dialog, + DialogContent, DialogFooter, + DialogHeader, + DialogSection, + DialogSectionSeparator, + DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, - Form, - Input, - Modal, + Form_Shadcn_, + FormControl_Shadcn_, + FormField_Shadcn_, + Input_Shadcn_, } from 'ui' import { Admonition } from 'ui-patterns' +import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' export interface NewAccessTokenButtonProps { onCreateToken: (token: any) => void } +const TokenSchema = z.object({ + tokenName: z.string().min(1, 'Please enter a name for the token'), +}) + +const formId = 'new-access-token-form' + const NewAccessTokenButton = ({ onCreateToken }: NewAccessTokenButtonProps) => { - const [isOpen, setIsOpen] = useState(false) + const [visible, setVisible] = useState(false) const [tokenScope, setTokenScope] = useState<'V0' | undefined>(undefined) - const validate = (values: any) => { - const errors: any = {} - if (!values.tokenName) errors.tokenName = 'Please enter a name for the token' - return errors - } - - const { mutate: createAccessToken, isLoading } = useAccessTokenCreateMutation({ - onSuccess: (res) => { - onCreateToken(res) - setIsOpen(false) - }, + const form = useForm>({ + resolver: zodResolver(TokenSchema), + defaultValues: { tokenName: '' }, + mode: 'onSubmit', }) + const { mutate: createAccessToken, isLoading } = useAccessTokenCreateMutation() - const onFormSubmit = async (values: any) => { - createAccessToken({ name: values.tokenName, scope: tokenScope }) + const onSubmit: SubmitHandler> = async (values) => { + createAccessToken( + { name: values.tokenName, scope: tokenScope }, + { + onSuccess: (data) => { + toast.success(`Your access token "${data.name}" is ready.`) + form.reset() + onCreateToken(data) + setVisible(false) + }, + } + ) } return ( <> -
-
-
+
+ + + - - -
-
+
+

Generate token for experimental API

+
+ + +
- setIsOpen(!isOpen)} - header={ -
-
- {tokenScope === 'V0' ? 'Generate token for experimental API' : 'Generate New Token'} -
-
- } + { + if (!open) form.reset() + setVisible(open) + }} > -
- {() => ( - <> - {tokenScope === 'V0' && ( - - -

- These include deleting organizations and projects which cannot be undone. - As such, be very careful when using this API. -

-
- -
- - } - /> -
- )} - - + + + {tokenScope === 'V0' ? 'Generate token for experimental API' : 'Generate New Token'} + + + + + {tokenScope === 'V0' && ( + +

+ These include deleting organizations and projects which cannot be undone. As + such, be very careful when using this API. +

+
+ +
+ + } + /> + )} + + + ( + + + + + + )} /> -
- -
- - -
-
- - )} -
-
+ + + + + + + + + ) } diff --git a/apps/studio/components/interfaces/Billing/Payment/AddNewPaymentMethodModal.tsx b/apps/studio/components/interfaces/Billing/Payment/AddNewPaymentMethodModal.tsx index d24f02b8036..e4feb1d958f 100644 --- a/apps/studio/components/interfaces/Billing/Payment/AddNewPaymentMethodModal.tsx +++ b/apps/studio/components/interfaces/Billing/Payment/AddNewPaymentMethodModal.tsx @@ -9,7 +9,6 @@ import { Modal } from 'ui' import { useOrganizationPaymentMethodSetupIntent } from 'data/organizations/organization-payment-method-setup-intent-mutation' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { STRIPE_PUBLIC_KEY } from 'lib/constants' -import { useIsHCaptchaLoaded } from 'stores/hcaptcha-loaded-store' import AddPaymentMethodForm from './AddPaymentMethodForm' import { getStripeElementsAppearanceOptions } from './Payment.utils' @@ -18,8 +17,6 @@ interface AddNewPaymentMethodModalProps { returnUrl: string onCancel: () => void onConfirm: () => void - showSetDefaultCheckbox?: boolean - autoMarkAsDefaultPaymentMethod?: boolean } const stripePromise = loadStripe(STRIPE_PUBLIC_KEY) @@ -29,14 +26,11 @@ const AddNewPaymentMethodModal = ({ returnUrl, onCancel, onConfirm, - showSetDefaultCheckbox, - autoMarkAsDefaultPaymentMethod, }: AddNewPaymentMethodModalProps) => { const { resolvedTheme } = useTheme() const [intent, setIntent] = useState() const selectedOrganization = useSelectedOrganization() - const captchaLoaded = useIsHCaptchaLoaded() const [captchaToken, setCaptchaToken] = useState(null) const [captchaRef, setCaptchaRef] = useState(null) @@ -64,7 +58,7 @@ const AddNewPaymentMethodModal = ({ } const loadPaymentForm = async () => { - if (visible && captchaRef && captchaLoaded) { + if (visible && captchaRef) { let token = captchaToken try { @@ -82,7 +76,7 @@ const AddNewPaymentMethodModal = ({ } loadPaymentForm() - }, [visible, captchaRef, captchaLoaded]) + }, [visible, captchaRef]) const resetCaptcha = () => { setCaptchaToken(null) @@ -142,8 +136,6 @@ const AddNewPaymentMethodModal = ({ returnUrl={returnUrl} onCancel={onLocalCancel} onConfirm={onLocalConfirm} - showSetDefaultCheckbox={showSetDefaultCheckbox} - autoMarkAsDefaultPaymentMethod={autoMarkAsDefaultPaymentMethod} /> diff --git a/apps/studio/components/interfaces/Billing/Payment/AddPaymentMethodForm.tsx b/apps/studio/components/interfaces/Billing/Payment/AddPaymentMethodForm.tsx index cb470dcb3e5..0171d9f5610 100644 --- a/apps/studio/components/interfaces/Billing/Payment/AddPaymentMethodForm.tsx +++ b/apps/studio/components/interfaces/Billing/Payment/AddPaymentMethodForm.tsx @@ -1,49 +1,56 @@ -import { PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js' import { useQueryClient } from '@tanstack/react-query' +import { + NewPaymentMethodElement, + type PaymentMethodElementRef, +} from 'components/interfaces/Organization/BillingSettings/PaymentMethods/NewPaymentMethodElement' import { organizationKeys } from 'data/organizations/keys' +import { useOrganizationCustomerProfileQuery } from 'data/organizations/organization-customer-profile-query' +import { useOrganizationCustomerProfileUpdateMutation } from 'data/organizations/organization-customer-profile-update-mutation' import { useOrganizationPaymentMethodMarkAsDefaultMutation } from 'data/organizations/organization-payment-method-default-mutation' +import { useOrganizationTaxIdQuery } from 'data/organizations/organization-tax-id-query' +import { useOrganizationTaxIdUpdateMutation } from 'data/organizations/organization-tax-id-update-mutation' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' -import { useState } from 'react' +import { isEqual } from 'lodash' +import { useRef, useState } from 'react' import { toast } from 'sonner' import { Button, Checkbox_Shadcn_, Label_Shadcn_, Modal } from 'ui' +import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' interface AddPaymentMethodFormProps { returnUrl: string onCancel: () => void onConfirm: () => void - showSetDefaultCheckbox?: boolean - autoMarkAsDefaultPaymentMethod?: boolean } // Stripe docs recommend to use the new SetupIntent flow over // manually creating and attaching payment methods via the API // Small UX annoyance here, that the page will be refreshed -const AddPaymentMethodForm = ({ - returnUrl, - onCancel, - onConfirm, - showSetDefaultCheckbox = false, - autoMarkAsDefaultPaymentMethod = false, -}: AddPaymentMethodFormProps) => { - const stripe = useStripe() - const elements = useElements() +const AddPaymentMethodForm = ({ onCancel, onConfirm }: AddPaymentMethodFormProps) => { const selectedOrganization = useSelectedOrganization() + const { data: customerProfile, isLoading: customerProfileLoading } = + useOrganizationCustomerProfileQuery({ + slug: selectedOrganization?.slug, + }) + const [isSaving, setIsSaving] = useState(false) - const [isDefault, setIsDefault] = useState(showSetDefaultCheckbox) + const [isDefaultPaymentMethod, setIsDefaultPaymentMethod] = useState(true) + const [isPrimaryBillingAddress, setIsPrimaryBillingAddress] = useState(true) const queryClient = useQueryClient() const { mutateAsync: markAsDefault } = useOrganizationPaymentMethodMarkAsDefaultMutation() + const { mutateAsync: updateCustomerProfile } = useOrganizationCustomerProfileUpdateMutation() + const { mutateAsync: updateTaxId } = useOrganizationTaxIdUpdateMutation() + const { data: taxId, isLoading: isCustomerTaxIdLoading } = useOrganizationTaxIdQuery({ + slug: selectedOrganization?.slug, + }) + + const paymentRef = useRef(null) const handleSubmit = async (event: any) => { event.preventDefault() - if (!stripe || !elements) { - console.error('Stripe.js has not loaded') - return - } - setIsSaving(true) if (document !== undefined) { @@ -51,25 +58,20 @@ const AddPaymentMethodForm = ({ document.body.classList.add('!pointer-events-auto') } - const { error, setupIntent } = await stripe.confirmSetup({ - elements, - redirect: 'if_required', - confirmParams: { return_url: returnUrl }, - }) + const result = await paymentRef.current?.confirmSetup() - if (error) { + if (!result) { setIsSaving(false) - toast.error(error?.message ?? ' Failed to save card details') } else { if ( - (isDefault || autoMarkAsDefaultPaymentMethod) && + isDefaultPaymentMethod && selectedOrganization && - typeof setupIntent?.payment_method === 'string' + typeof result.setupIntent?.payment_method === 'string' ) { try { await markAsDefault({ slug: selectedOrganization.slug, - paymentMethodId: setupIntent.payment_method, + paymentMethodId: result.setupIntent.payment_method, }) await queryClient.invalidateQueries( @@ -82,10 +84,10 @@ const AddPaymentMethodForm = ({ if (!prev) return prev return { ...prev, - defaultPaymentMethodId: setupIntent.payment_method, + defaultPaymentMethodId: result.setupIntent.payment_method, data: prev.data.map((pm: any) => ({ ...pm, - is_default: pm.id === setupIntent.payment_method, + is_default: pm.id === result.setupIntent.payment_method, })), } } @@ -101,6 +103,28 @@ const AddPaymentMethodForm = ({ } } + if (isPrimaryBillingAddress) { + try { + if ( + result.address && + (!isEqual(result.address, customerProfile?.address) || + customerProfile?.billing_name !== result.customerName) + ) { + await updateCustomerProfile({ + slug: selectedOrganization?.slug, + billing_name: result.customerName, + address: result.address, + }) + } + + if (result.taxId && !isEqual(result.taxId, taxId)) { + await updateTaxId({ taxId: result.taxId, slug: selectedOrganization?.slug }) + } + } catch (error) { + toast.error('Failed to update billing address') + } + } + setIsSaving(false) onConfirm() } @@ -110,33 +134,64 @@ const AddPaymentMethodForm = ({ } } + if (customerProfileLoading || isCustomerTaxIdLoading) { + return ( + +
+ + + + + + +
+
+ ) + } + return (
- - {showSetDefaultCheckbox && ( -
- { - if (typeof checked === 'boolean') { - setIsDefault(checked) - } - }} - /> - - Save as default payment method - -
- )} + +
+ { + if (typeof checked === 'boolean') { + setIsDefaultPaymentMethod(checked) + } + }} + /> + + Save as default payment method + +
+ +
+ { + if (typeof checked === 'boolean') { + setIsPrimaryBillingAddress(checked) + } + }} + /> + + Use the billing address as my organization's primary address + +
diff --git a/apps/studio/components/interfaces/Billing/Payment/Payment.utils.ts b/apps/studio/components/interfaces/Billing/Payment/Payment.utils.ts index 51f095f2e3a..ff8b08bbc7d 100644 --- a/apps/studio/components/interfaces/Billing/Payment/Payment.utils.ts +++ b/apps/studio/components/interfaces/Billing/Payment/Payment.utils.ts @@ -4,6 +4,7 @@ export const getStripeElementsAppearanceOptions = ( resolvedTheme: string | undefined ): Appearance => { return { + labels: 'floating', theme: (resolvedTheme?.includes('dark') ? 'night' : 'flat') as 'night' | 'flat', variables: { fontSizeBase: '14px', @@ -12,18 +13,27 @@ export const getStripeElementsAppearanceOptions = ( : 'hsl(0deg 0% 95.3%)', fontFamily: 'var(--font-custom, Circular, custom-font, Helvetica Neue, Helvetica, Arial, sans-serif)', - spacingUnit: '4px', - borderRadius: '.375rem', - gridRowSpacing: '4px', }, rules: { - '.Label': { - // Hide labels - it is obvious enough what the fields are for - fontSize: '0', - }, '.TermsText': { fontSize: '12px', }, + '.Label--floating': { + fontSize: '14px', + }, + '.Label--resting': { + fontSize: '14px', + color: 'rgb(137, 137, 137)', + }, + '.Input': { + boxShadow: 'none', + height: '34px', + lineHeight: '16px', + padding: '8px 12px', + }, + '.AccordionItem': { + boxShadow: 'none', + }, }, } } diff --git a/apps/studio/components/interfaces/BranchManagement/CreateBranchModal.tsx b/apps/studio/components/interfaces/BranchManagement/CreateBranchModal.tsx index a23bf75eb81..266b0a636c4 100644 --- a/apps/studio/components/interfaces/BranchManagement/CreateBranchModal.tsx +++ b/apps/studio/components/interfaces/BranchManagement/CreateBranchModal.tsx @@ -42,13 +42,16 @@ import { Form_Shadcn_, Input_Shadcn_, Label_Shadcn_ as Label, + Switch, cn, } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { PermissionAction } from '@supabase/shared-types/out/constants' +import { useFlag } from 'hooks/ui/useFlag' export const CreateBranchModal = () => { + const allowDataBranching = useFlag('allowDataBranching') const { ref } = useParams() const router = useRouter() const queryClient = useQueryClient() @@ -141,6 +144,7 @@ export const CreateBranchModal = () => { (val) => gitlessBranching || !githubConnection || (val && val.length > 0), 'Git branch name is required when GitHub is connected' ), + withData: z.boolean().default(false).optional(), }) .superRefine(async (val, ctx) => { if (val.gitBranchName && val.gitBranchName.length > 0 && githubConnection?.repository.id) { @@ -164,7 +168,7 @@ export const CreateBranchModal = () => { mode: 'onSubmit', reValidateMode: 'onBlur', resolver: zodResolver(FormSchema), - defaultValues: { branchName: '', gitBranchName: '' }, + defaultValues: { branchName: '', gitBranchName: '', withData: false }, }) const canSubmit = !isCreating && !isChecking @@ -182,7 +186,9 @@ export const CreateBranchModal = () => { createBranch({ projectRef, branchName: data.branchName, + is_default: false, ...(data.gitBranchName ? { gitBranch: data.gitBranchName } : {}), + ...(allowDataBranching ? { withData: data.withData } : {}), }) } @@ -294,6 +300,7 @@ export const CreateBranchModal = () => { )} /> )} + {isLoadingConnections && } {isErrorConnections && ( { )}
-

+

Keep this preview branch in sync with a chosen GitHub branch

@@ -325,6 +332,23 @@ export const CreateBranchModal = () => { )} )} + {allowDataBranching && ( + ( + + + + + + )} + /> + )} diff --git a/apps/studio/components/interfaces/DataWarehouse/CollectionForm.tsx b/apps/studio/components/interfaces/DataWarehouse/CollectionForm.tsx deleted file mode 100644 index 5bb7e906d59..00000000000 --- a/apps/studio/components/interfaces/DataWarehouse/CollectionForm.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import { zodResolver } from '@hookform/resolvers/zod' -import { useForm } from 'react-hook-form' -import { z } from 'zod' - -import { - Button, - FormControl_Shadcn_, - FormField_Shadcn_, - FormMessage_Shadcn_, - Form_Shadcn_, - Input_Shadcn_, - Modal, -} from 'ui' -import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' -import { useCurrentOrgPlan } from 'hooks/misc/useCurrentOrgPlan' -import { Admonition } from 'ui-patterns' -import { useRouter } from 'next/router' -import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' - -export interface CollectionFormValues { - name: string - retention_days: number -} - -interface CollectionFormProps { - initialValues?: Partial - onSubmit: (values: CollectionFormValues) => void - isLoading?: boolean - onCancelClick: () => void -} - -export const CollectionForm = ({ - initialValues, - onSubmit, - isLoading, - onCancelClick, -}: CollectionFormProps) => { - const { plan, isLoading: planLoading } = useCurrentOrgPlan() - const router = useRouter() - const currentOrg = useSelectedOrganization() - - function getMaxRetentionDays() { - if (planLoading) return 1 - if (plan?.id === 'free') return 3 - else return 90 - } - const maxRetentionDays = getMaxRetentionDays() - - const FormSchema = z.object({ - name: z.string().min(1), - retention_days: z.coerce.number().min(1).max(maxRetentionDays).multipleOf(1).positive().int(), - }) - - const form = useForm({ - resolver: zodResolver(FormSchema), - defaultValues: { - name: initialValues?.name || '', - retention_days: initialValues?.retention_days || maxRetentionDays, - }, - }) - - const handleSubmit = form.handleSubmit(onSubmit) - - const formatDays = (days: number) => { - return days === 1 ? `1 day` : `${days} days` - } - - const retentionDescription = (days: number) => { - if (!Boolean(days) || days < 1) - return <>Your plan allows for a maximum of {formatDays(maxRetentionDays)}. - return ( - <> - Your logs will be removed after {formatDays(days)}.
- Your plan allows for a maximum of {formatDays(maxRetentionDays)}. - - ) - } - - return ( - -
- - ( - - - - - - )} - /> - ( - - -
- - - days - -
-
-
- )} - /> -
- - {plan?.id === 'free' && ( - <> - - - Upgrade to the pro plan to increase retention to 90 days. - -
- } - /> - - )} - - - - - - - - ) -} diff --git a/apps/studio/components/interfaces/DataWarehouse/CreateWarehouseCollection.tsx b/apps/studio/components/interfaces/DataWarehouse/CreateWarehouseCollection.tsx deleted file mode 100644 index 4b2126065e4..00000000000 --- a/apps/studio/components/interfaces/DataWarehouse/CreateWarehouseCollection.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { useRouter } from 'next/router' -import { toast } from 'sonner' - -import { useParams } from 'common' -import { useCreateCollection } from 'data/analytics/warehouse-collections-create-mutation' -import { Modal } from 'ui' -import { CollectionForm } from './CollectionForm' - -export const CreateWarehouseCollectionModal = ({ - open, - onOpenChange, -}: { - open: boolean - onOpenChange: (open: boolean) => void -}) => { - const router = useRouter() - const { ref } = useParams() - - const { mutate: createCollection, isLoading } = useCreateCollection({ - onSuccess: (data) => { - onOpenChange(false) - toast.success('Collection created successfully') - router.push(`/project/${ref}/logs/collections/${data.token}`) - }, - onError: (error) => { - toast.error(error.message) - }, - }) - - return ( - onOpenChange(false)} - header="Create collection" - visible={open} - hideFooter - > - onOpenChange(false)} - isLoading={isLoading} - onSubmit={(vals) => { - if (!ref) { - return - } - createCollection({ - projectRef: ref, - name: vals.name, - retention_days: vals.retention_days, - }) - }} - /> - - ) -} diff --git a/apps/studio/components/interfaces/DataWarehouse/TestCollectionDialog.tsx b/apps/studio/components/interfaces/DataWarehouse/TestCollectionDialog.tsx deleted file mode 100644 index 489a9e29860..00000000000 --- a/apps/studio/components/interfaces/DataWarehouse/TestCollectionDialog.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import { Select, SelectContent, SelectItem, SelectTrigger } from '@ui/components/shadcn/ui/select' -import CopyButton from 'components/ui/CopyButton' -import type { WarehouseAccessTokensData } from 'data/analytics/warehouse-access-tokens-query' -import type { WarehouseCollectionsData } from 'data/analytics/warehouse-collections-query' -import { useRouter } from 'next/router' -import { useEffect, useState } from 'react' -import { - Button, - Checkbox_Shadcn_, - CodeBlock, - Input_Shadcn_, - Label_Shadcn_, - Separator, - Sheet, - SheetContent, - SheetDescription, - SheetFooter, - SheetHeader, - SheetSection, - SheetTitle, - SheetTrigger, - Tooltip, - TooltipContent, - TooltipTrigger, -} from 'ui' -import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' - -export function TestCollectionDialog({ - accessTokens, - collectionToken, - collections, - open, - onOpenChange, - onSubmit, -}: { - accessTokens: WarehouseAccessTokensData['data'] - collections: WarehouseCollectionsData - collectionToken: string - projectRef: string - open: boolean - onOpenChange: (open: boolean) => void - onSubmit: () => void -}) { - const router = useRouter() - const BASE_WAREHOUSE_URL = `https://api.warehouse.tech/api/events` - const [testAccessToken, setTestAccessToken] = useState('') - const [selectedCollection, setSelectedCollection] = useState(collectionToken || '') - const [showAccessToken, setShowAccessToken] = useState(false) - - useEffect(() => { - setSelectedCollection(collectionToken) - }, [collectionToken]) - - useEffect(() => { - if (accessTokens.length > 0) { - setTestAccessToken(accessTokens[0].token) - } - }, [accessTokens]) - - const selectedAccessToken = accessTokens.find((token) => token.token === testAccessToken) - const selectedCollectionName = collections?.find((col) => col.token === selectedCollection)?.name - - function getcURL(accessToken: string) { - return `curl -X "POST" "${BASE_WAREHOUSE_URL}?source=${selectedCollection}" \\ - -H 'Content-Type: application/json' \\ - -H 'Authorization: Bearer ${accessToken}' \\ - -d $'{ - "event_message": "Test event message", - "metadata": { - "ip_address": "100.100.100.100", - "request_method": "POST", - "custom_user_data": { - "foo": "bar" - }, - "datacenter": "aws", - "request_headers": { - "connection": "close", - "user_agent": "chrome" - } - } - }' -` - } - - return ( - - - - - - - - {accessTokens.length === 0 && ( - Create an access token to connect to your collection - )} - - - - - Send events to this collection - - Use the following cURL command to send events to this collection - - - - - - - - - - - - - - - - - - - - - - -
-
- setShowAccessToken(!showAccessToken)} - /> - Show access token -
- -
- -
- - {getcURL(showAccessToken ? testAccessToken : '********************')} - -
-
- - -

- Once you send an event, refresh the page to see it in the collection. -

-
- -
-
-
-
- ) -} diff --git a/apps/studio/components/interfaces/DataWarehouse/WarehouseCollectionDetail.tsx b/apps/studio/components/interfaces/DataWarehouse/WarehouseCollectionDetail.tsx deleted file mode 100644 index f0be4402a71..00000000000 --- a/apps/studio/components/interfaces/DataWarehouse/WarehouseCollectionDetail.tsx +++ /dev/null @@ -1,323 +0,0 @@ -import { useKeyboardShortcuts } from 'components/grid/components/common/Hooks' -import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState' -import AlertError from 'components/ui/AlertError' -import LoadingOpacity from 'components/ui/LoadingOpacity' -import ShimmerLine from 'components/ui/ShimmerLine' -import { useWarehouseAccessTokensQuery } from 'data/analytics/warehouse-access-tokens-query' -import { useWarehouseLogDetailQuery } from 'data/analytics/warehouse-collection-log-detail-query' -import { useWarehouseCollectionsQuery } from 'data/analytics/warehouse-collections-query' -import { useWarehouseQueryQuery } from 'data/analytics/warehouse-query' -import dayjs from 'dayjs' -import { useSelectedLog } from 'hooks/analytics/useSelectedLog' -import { useFlag } from 'hooks/ui/useFlag' -import { RefreshCcw, Rewind, Terminal } from 'lucide-react' -import Link from 'next/link' -import { useRouter } from 'next/router' -import { useEffect, useState } from 'react' -import { Button, Tooltip, TooltipContent, TooltipTrigger } from 'ui' -import { LogsDatePicker } from '../Settings/Logs/Logs.DatePickers' -import { DatetimeHelper } from '../Settings/Logs/Logs.types' -import LogTable from '../Settings/Logs/LogTable' -import { TestCollectionDialog } from './TestCollectionDialog' - -const INTERVALS: DatetimeHelper[] = [ - { - text: 'Last hour', - calcFrom: () => dayjs().subtract(1, 'hour').toISOString(), - calcTo: () => dayjs().toISOString(), - default: true, - }, - { - text: 'Last 12 hours', - calcFrom: () => dayjs().subtract(12, 'hour').toISOString(), - calcTo: () => dayjs().toISOString(), - }, - { - text: 'Last day', - calcFrom: () => dayjs().subtract(1, 'day').toISOString(), - calcTo: () => dayjs().toISOString(), - }, - { - text: 'Last 7 days', - calcFrom: () => dayjs().subtract(7, 'day').toISOString(), - calcTo: () => dayjs().toISOString(), - }, -] - -export const WarehouseCollectionDetail = () => { - const router = useRouter() - const collectionToken = router.query.collectionToken as string - const projectRef = router.query.ref as string - const accessTokens = useWarehouseAccessTokensQuery({ projectRef }) - const [testDialogOpen, setTestDialogOpen] = useState(false) - const hasWarehouse = useFlag('warehouse') - - const { data: collections, isLoading: collectionsLoading } = useWarehouseCollectionsQuery( - { projectRef }, - { enabled: hasWarehouse } - ) - const collection = collections?.find((c) => c.token === collectionToken) - const [search, setSearch] = useState('') - const [filters, setFilters] = useState({ - limit: 100, - offset: 0, - search: '', - interval: { - to: INTERVALS[0].calcTo, - from: INTERVALS[0].calcFrom, - }, - }) - const [sql, setSQL] = useState('') - - useEffect(() => { - if (collection) { - const from = filters.interval.from() - const to = filters.interval.to() - - const sql = `select id, timestamp from \`${collection.name}\` -where timestamp >= TIMESTAMP('${from}') -and timestamp <= TIMESTAMP('${to}') -order by timestamp desc limit ${filters.limit} offset ${filters.offset} - ` - - setSQL(sql) - } - }, [filters.interval, filters.limit, filters.offset, collection]) - - const { - isLoading: queryLoading, - data: queryData, - isError, - isRefetching, - } = useWarehouseQueryQuery( - { ref: projectRef, sql }, - { - enabled: !!sql && hasWarehouse, - } - ) - - const formatResults = (results: any) => { - if (!results || !results.length) { - return [] - } - - const r = results.map(({ timestamp, ...r }: any) => { - return { - timestamp: new Date(timestamp / 1000).toLocaleString(), - ...r, - } - }) - - return r - } - - const results = formatResults(queryData?.result) - - function loadMore() { - setFilters({ ...filters, offset: filters.offset + filters.limit }) - } - - const isLoading = queryLoading || collectionsLoading || isRefetching - - function refreshResults(e?: React.FormEvent) { - e?.preventDefault() - setFilters({ - ...filters, - search, - interval: { - to: () => dayjs().toISOString(), - from: () => filters.interval.from() || dayjs().subtract(1, 'hour').toISOString(), - }, - }) - } - - const queryUrl = `/project/${projectRef}/logs/explorer?q=${encodeURIComponent( - sql || '' - )}&source=warehouse` - - useKeyboardShortcuts( - { - r: () => { - document.getElementById('refresh-button')?.focus() - }, - s: () => { - document.getElementById('search-input')?.focus() - }, - }, - ['INPUT', 'BUTTON', 'TEXTAREA'] - ) - - function KeyboardShortcut({ children }: { children: React.ReactNode }) { - return ( - - {children} - - ) - } - - // Selected Log Detail - const [selectedLogId, setSelectedLogId] = useSelectedLog() - const { - data: selectedLogDetail, - isLoading: selectedLogDetailLoading, - error: selectedLogDetailError, - } = useWarehouseLogDetailQuery({ - ref: projectRef, - collectionName: collection?.name, - logId: selectedLogId ?? undefined, - }) - - return ( -
- - -
-
-
-

- {collection?.name} -

- - - - - - Open query in Collections Explorer - -
- - { - refreshResults() - setTestDialogOpen(false) - }} - /> -
-
- - {isError && ( -
- -
- )} - - {!isError && ( - setSelectedLogId(log?.id ?? null)} - selectedLog={selectedLogDetail?.result[0] ?? undefined} - selectedLogError={selectedLogDetailError ?? undefined} - isSelectedLogLoading={!!selectedLogId && selectedLogDetailLoading} - data={results} - showHeader={false} - EmptyState={ - -
-

Try adjusting your filters, send a test event or refresh the results.

-
- - -
-
-
- } - /> - )} -
- - - {!isError && ( -
-
- {results.length > 0 && ( - - )} - {filters.offset !== 0 && ( - <> - - - )} -
-
- )} -
- ) -} diff --git a/apps/studio/components/interfaces/DataWarehouse/WarehouseMenuItem.tsx b/apps/studio/components/interfaces/DataWarehouse/WarehouseMenuItem.tsx deleted file mode 100644 index 9b2c656e254..00000000000 --- a/apps/studio/components/interfaces/DataWarehouse/WarehouseMenuItem.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import { PermissionAction } from '@supabase/shared-types/out/constants' -import { Database, EditIcon, TrashIcon } from 'lucide-react' -import { useRouter } from 'next/router' -import { useState } from 'react' -import { toast } from 'sonner' - -import { useParams } from 'common' -import { useDeleteCollectionMutation } from 'data/analytics/warehouse-collections-delete-mutation' -import { useUpdateCollection } from 'data/analytics/warehouse-collections-update-mutation' -import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' -import { - DropdownMenuItem, - DropdownMenuSeparator, - Modal, - Tooltip, - TooltipContent, - TooltipTrigger, -} from 'ui' - -import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' -import { LogsSidebarItem } from '../Settings/Logs/SidebarV2/SidebarItem' -import { CollectionForm } from './CollectionForm' - -type Props = { - item: { - id: number - token: string - name: string - retention_days: number - } -} - -export const WarehouseMenuItem = ({ item }: Props) => { - const router = useRouter() - const { ref, collectionToken } = useParams() - const projectRef = ref || 'default' - - const [showUpdateDialog, setShowUpdateDialog] = useState(false) - const [showDeleteDialog, setDeleteDialog] = useState(false) - - const canUpdateCollection = useCheckPermissions(PermissionAction.ANALYTICS_WRITE, 'logflare') - - const updateCollection = useUpdateCollection({ - onSuccess: () => { - toast.success('Collection updated successfully') - setShowUpdateDialog(false) - }, - }) - const deleteCollection = useDeleteCollectionMutation({ - onSuccess: () => { - // If the current collection is deleted, redirect to default logs view - if (item.token === collectionToken) { - router.push(`/project/${projectRef}/logs/explorer`) - } - setDeleteDialog(false) - toast.success('Collection deleted successfully') - }, - }) - - const isLoading = updateCollection.isLoading || deleteCollection.isLoading - - return ( - <> - } - href={`/project/${projectRef}/logs/collections/${item.token}`} - dropdownItems={ - <> - - - { - e.preventDefault() - setShowUpdateDialog(true) - }} - > - -
Update collection
-
-
- {!canUpdateCollection && ( - - You need additional permissions to update collections - - )} -
- - - - { - e.preventDefault() - setDeleteDialog(true) - }} - > - -
Delete collection
-
-
- {!canUpdateCollection && ( - - You need additional permissions to delete collections - - )} -
- - } - >
- - setShowUpdateDialog(false)} - hideFooter - header={`Update collection "${item.name}"`} - > - setShowUpdateDialog(false)} - isLoading={isLoading} - initialValues={{ name: item.name, retention_days: item.retention_days }} - onSubmit={({ name, retention_days }) => { - updateCollection.mutate({ - projectRef, - collectionToken: item.token, - name, - retention_days, - }) - }} - /> - - setDeleteDialog(false)} - confirmLabel="Delete collection" - title={`Delete collection "${item.name}"`} - size="small" - onConfirm={() => { - deleteCollection.mutate({ projectRef, collectionToken: item.token }) - }} - > -

- All data in this collection will be deleted. -
-

This action cannot be undone.
-

-
- - ) -} diff --git a/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx b/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx index d51bdf2f5d4..d2830c5468e 100644 --- a/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx +++ b/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx @@ -1,13 +1,15 @@ -import Table from 'components/to-be-cleaned/Table' import { motion } from 'framer-motion' -import { BASE_PATH } from 'lib/constants' import { ArrowUpRight, Circle, Database, MoreVertical, Plus, Search } from 'lucide-react' import { useTheme } from 'next-themes' import Link from 'next/link' import { useMemo } from 'react' import ReactFlow, { Background, Handle, Position, ReactFlowProvider } from 'reactflow' import 'reactflow/dist/style.css' -import { Button, Input } from 'ui' + +import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold' +import Table from 'components/to-be-cleaned/Table' +import { BASE_PATH } from 'lib/constants' +import { Button, Input_Shadcn_ } from 'ui' import { NODE_WIDTH } from '../../Settings/Infrastructure/InfrastructureConfiguration/InstanceConfiguration.constants' const STATIC_NODES = [ @@ -62,6 +64,14 @@ const STATIC_EDGES = [ { id: 'e1-4', source: '1', target: '4', type: 'smoothstep', animated: true }, ] +export const ReplicationComingSoon = () => { + return ( + + + + ) +} + const ReplicationStaticMockup = () => { const nodes = useMemo(() => STATIC_NODES, []) const edges = useMemo(() => STATIC_EDGES, []) @@ -108,16 +118,6 @@ const ReplicationStaticMockup = () => { ) } -const ReplicationComingSoon = () => { - return ( - - - - ) -} - -export default ReplicationComingSoon - const PrimaryNode = ({ data, }: { @@ -251,58 +251,68 @@ const StaticDestinations = () => { return ( <> -
-
-
- } - placeholder="Search..." - /> - -
- Name, - Publication, - Lag, - Status, - , - ]} - className="mt-4" - body={mockRows.map((row, i) => ( - - {row.name} - - - All - {row.tables} tables - - - {row.lag} - - - - {row.status} - - - - - - - ))} - /> +
+
+ + + +
+
+
+ + +
+ +
+
Name, + Publication, + Lag, + Status, + , + ]} + className="mt-4" + body={mockRows.map((row, i) => ( + + {row.name} + + + All + {row.tables} tables + + + {row.lag} + + + + {row.status} + + + + + + + ))} + /> + + + ) diff --git a/apps/studio/components/interfaces/Database/Replication/DestinationRow.tsx b/apps/studio/components/interfaces/Database/Replication/DestinationRow.tsx index edfab6d9239..04386674eeb 100644 --- a/apps/studio/components/interfaces/Database/Replication/DestinationRow.tsx +++ b/apps/studio/components/interfaces/Database/Replication/DestinationRow.tsx @@ -1,26 +1,28 @@ +import { useEffect, useState } from 'react' +import { toast } from 'sonner' + +import { useParams } from 'common' import Table from 'components/to-be-cleaned/Table' import AlertError from 'components/ui/AlertError' +import { useDeleteDestinationMutation } from 'data/replication/delete-destination-mutation' +import { useReplicationPipelineStatusQuery } from 'data/replication/pipeline-status-query' import { ReplicationPipelinesData } from 'data/replication/pipelines-query' +import { useStopPipelineMutation } from 'data/replication/stop-pipeline-mutation' +import { + PipelineStatusRequestStatus, + usePipelineRequestStatus, +} from 'state/replication-pipeline-request-status' import { ResponseError } from 'types' import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' -import RowMenu from './RowMenu' -import PipelineStatus, { PipelineStatusRequestStatus, PipelineStatusName } from './PipelineStatus' -import { useParams } from 'common' -import { - ReplicationPipelineStatusData, - useReplicationPipelineStatusQuery, -} from 'data/replication/pipeline-status-query' -import { useState } from 'react' -import { toast } from 'sonner' -import { useStartPipelineMutation } from 'data/replication/start-pipeline-mutation' -import { useStopPipelineMutation } from 'data/replication/stop-pipeline-mutation' -import { useDeleteDestinationMutation } from 'data/replication/delete-destination-mutation' import DeleteDestination from './DeleteDestination' import DestinationPanel from './DestinationPanel' +import { getStatusName, PIPELINE_ERROR_MESSAGES } from './Pipeline.utils' +import { PipelineStatus, PipelineStatusName } from './PipelineStatus' +import { RowMenu } from './RowMenu' export type Pipeline = ReplicationPipelinesData['pipelines'][0] -const refreshFrequencyMs: number = 5000 +const refreshFrequencyMs: number = 2000 interface DestinationRowProps { sourceId: number | undefined @@ -32,9 +34,10 @@ interface DestinationRowProps { isLoading: boolean isError: boolean isSuccess: boolean + onSelectPipeline?: (pipelineId: number, destinationName: string) => void } -const DestinationRow = ({ +export const DestinationRow = ({ sourceId, destinationId, destinationName, @@ -44,6 +47,7 @@ const DestinationRow = ({ isLoading: isPipelineLoading, isError: isPipelineError, isSuccess: isPipelineSuccess, + onSelectPipeline, }: DestinationRowProps) => { const { ref: projectRef } = useParams() const [showDeleteDestinationForm, setShowDeleteDestinationForm] = useState(false) @@ -62,76 +66,23 @@ const DestinationRow = ({ }, { refetchInterval: refreshFrequencyMs } ) - const [requestStatus, setRequestStatus] = useState( - PipelineStatusRequestStatus.None - ) - const { mutateAsync: startPipeline } = useStartPipelineMutation() + const { getRequestStatus, updatePipelineStatus } = usePipelineRequestStatus() + const requestStatus = pipeline?.id + ? getRequestStatus(pipeline.id) + : PipelineStatusRequestStatus.None + const { mutateAsync: stopPipeline } = useStopPipelineMutation() - const pipelineStatus = pipelineStatusData?.status - const getStatusName = ( - status: ReplicationPipelineStatusData['status'] | undefined - ): string | undefined => { - if (status && typeof status === 'object' && 'name' in status) { - return status.name - } - - return undefined - } - - const statusName = getStatusName(pipelineStatus) - if ( - (requestStatus === PipelineStatusRequestStatus.EnableRequested && - (statusName === PipelineStatusName.STARTED || statusName === PipelineStatusName.FAILED)) || - (requestStatus === PipelineStatusRequestStatus.DisableRequested && - (statusName === PipelineStatusName.STOPPED || statusName === PipelineStatusName.FAILED)) - ) { - setRequestStatus(PipelineStatusRequestStatus.None) - } - - const onEnableClick = async () => { - if (!projectRef) { - console.error('Project ref is required') - return - } - if (!pipeline) { - toast.error('No pipeline found') - return - } - - try { - await startPipeline({ projectRef, pipelineId: pipeline.id }) - } catch (error) { - toast.error('Failed to enable destination') - } - setRequestStatus(PipelineStatusRequestStatus.EnableRequested) - } - const onDisableClick = async () => { - if (!projectRef) { - console.error('Project ref is required') - return - } - if (!pipeline) { - toast.error('No pipeline found') - return - } - - try { - await stopPipeline({ projectRef, pipelineId: pipeline.id }) - } catch (error) { - toast.error('Failed to disable destination') - } - setRequestStatus(PipelineStatusRequestStatus.DisableRequested) - } const { mutateAsync: deleteDestination } = useDeleteDestinationMutation({}) + const pipelineStatus = pipelineStatusData?.status + const statusName = getStatusName(pipelineStatus) + const onDeleteClick = async () => { if (!projectRef) { - console.error('Project ref is required') - return + return console.error('Project ref is required') } if (!pipeline) { - toast.error('No pipeline found') - return + return toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND) } try { @@ -140,24 +91,33 @@ const DestinationRow = ({ // so we don't need to call deletePipeline explicitly await deleteDestination({ projectRef, destinationId: destinationId }) } catch (error) { - toast.error('Failed to delete destination') + toast.error(PIPELINE_ERROR_MESSAGES.DELETE_DESTINATION) } } + useEffect(() => { + if (pipeline?.id) { + updatePipelineStatus(pipeline.id, statusName) + } + }, [pipeline?.id, statusName, updatePipelineStatus]) + return ( <> {isPipelineError && ( - + )} {isPipelineSuccess && ( - - - {isPipelineLoading ? : destinationName} - - {isPipelineLoading ? : type} + { + if (pipeline) onSelectPipeline?.(pipeline.id, destinationName) + }} + > + {isPipelineLoading ? : destinationName} + {isPipelineLoading ? : type} {isPipelineLoading || !pipeline ? ( - + ) : ( + /> )} {isPipelineLoading || !pipeline ? ( - + ) : ( pipeline.config.publication_name )} - setShowDeleteDestinationForm(true)} - onEditClick={() => setShowEditDestinationPanel(true)} - > +
+ setShowDeleteDestinationForm(true)} + onEditClick={() => setShowEditDestinationPanel(true)} + /> +
)} @@ -211,5 +172,3 @@ const DestinationRow = ({ ) } - -export default DestinationRow diff --git a/apps/studio/components/interfaces/Database/Replication/Destinations.tsx b/apps/studio/components/interfaces/Database/Replication/Destinations.tsx index d1bbfc71b5d..8d384e7840d 100644 --- a/apps/studio/components/interfaces/Database/Replication/Destinations.tsx +++ b/apps/studio/components/interfaces/Database/Replication/Destinations.tsx @@ -1,19 +1,26 @@ +import { noop } from 'lodash' +import { Plus, Search } from 'lucide-react' +import { useState } from 'react' + import { useParams } from 'common' import Table from 'components/to-be-cleaned/Table' import AlertError from 'components/ui/AlertError' import { useReplicationDestinationsQuery } from 'data/replication/destinations-query' -import { Plus } from 'lucide-react' -import { Button, cn } from 'ui' -import { GenericSkeletonLoader } from 'ui-patterns' -import DestinationRow from './DestinationRow' import { useReplicationPipelinesQuery } from 'data/replication/pipelines-query' -import { useState } from 'react' -import NewDestinationPanel from './DestinationPanel' import { useReplicationSourcesQuery } from 'data/replication/sources-query' -import { ScaffoldSection, ScaffoldSectionTitle } from 'components/layouts/Scaffold' +import { Button, cn, Input_Shadcn_ } from 'ui' +import { GenericSkeletonLoader } from 'ui-patterns' +import NewDestinationPanel from './DestinationPanel' +import { DestinationRow } from './DestinationRow' +import { PIPELINE_ERROR_MESSAGES } from './Pipeline.utils' -const Destinations = () => { +interface DestinationsProps { + onSelectPipeline?: (pipelineId: number, destinationName: string) => void +} + +export const Destinations = ({ onSelectPipeline = noop }: DestinationsProps) => { const [showNewDestinationPanel, setShowNewDestinationPanel] = useState(false) + const [filterString, setFilterString] = useState('') const { ref: projectRef } = useParams() const { @@ -21,12 +28,11 @@ const Destinations = () => { error: sourcesError, isLoading: isSourcesLoading, isError: isSourcesError, - isSuccess: isSourcesSuccess, } = useReplicationSourcesQuery({ projectRef, }) - let sourceId = sourcesData?.sources.find((s) => s.name === projectRef)?.id + const sourceId = sourcesData?.sources.find((s) => s.name === projectRef)?.id const { data: destinationsData, @@ -50,21 +56,44 @@ const Destinations = () => { const anyDestinations = isDestinationsSuccess && destinationsData.destinations.length > 0 + const filteredDestinations = + filterString.length === 0 + ? destinationsData?.destinations ?? [] + : (destinationsData?.destinations ?? []).filter((destination) => + destination.name.toLowerCase().includes(filterString.toLowerCase()) + ) + return ( <> - -
- Destinations +
+
+
+
+ + setFilterString(e.target.value)} + /> +
+
+
+ +
{(isSourcesLoading || isDestinationsLoading) && } {(isSourcesError || isDestinationsError) && ( )} @@ -77,7 +106,7 @@ const Destinations = () => { Publication, , ]} - body={destinationsData.destinations.map((destination) => { + body={filteredDestinations.map((destination) => { const pipeline = pipelinesData?.pipelines.find( (p) => p.destination_id === destination.id ) @@ -93,7 +122,8 @@ const Destinations = () => { isLoading={isPipelinesLoading} isError={isPipelinesError} isSuccess={isPipelinesSuccess} - > + onSelectPipeline={onSelectPipeline} + /> ) })} >
@@ -124,15 +154,22 @@ const Destinations = () => {
) )} - +
+ + {!isSourcesLoading && + !isDestinationsLoading && + filteredDestinations.length === 0 && + anyDestinations && ( +
+

No destinations match "{filterString}"

+
+ )} setShowNewDestinationPanel(false)} - > + /> ) } - -export default Destinations diff --git a/apps/studio/components/interfaces/Database/Replication/Pipeline.utils.ts b/apps/studio/components/interfaces/Database/Replication/Pipeline.utils.ts new file mode 100644 index 00000000000..d15fe8040cc --- /dev/null +++ b/apps/studio/components/interfaces/Database/Replication/Pipeline.utils.ts @@ -0,0 +1,96 @@ +import { ReplicationPipelineStatusData } from 'data/replication/pipeline-status-query' +import { PipelineStatusRequestStatus } from 'state/replication-pipeline-request-status' + +export const PIPELINE_ERROR_MESSAGES = { + RETRIEVE_PIPELINE: 'Failed to retrieve pipeline information', + RETRIEVE_PIPELINE_STATUS: 'Failed to retrieve pipeline status', + RETRIEVE_REPLICATION_STATUS: 'Failed to retrieve table replication status', + RETRIEVE_DESTINATIONS: 'Failed to retrieve destinations', + ENABLE_DESTINATION: 'Failed to enable destination', + DISABLE_DESTINATION: 'Failed to disable destination', + DELETE_DESTINATION: 'Failed to delete destination', + NO_PIPELINE_FOUND: 'No pipeline found', + COPY_TABLE_STATUS: 'Failed to copy table status', +} as const + +export const getStatusName = ( + status: ReplicationPipelineStatusData['status'] | undefined +): ReplicationPipelineStatusData['status']['name'] | undefined => { + if (status && typeof status === 'object' && 'name' in status) { + return status.name + } + return undefined +} + +export const PIPELINE_STATE_MESSAGES = { + enabling: { + title: 'Pipeline Enabling', + message: 'Starting the pipeline. Table replication will resume once enabled.', + badge: 'Enabling', + }, + disabling: { + title: 'Pipeline Disabling', + message: 'Stopping the pipeline. Table replication will be paused once disabled.', + badge: 'Disabling', + }, + failed: { + title: 'Pipeline Failed', + message: 'Replication has encountered an error. Check the logs for details.', + badge: 'Failed', + }, + stopped: { + title: 'Pipeline Stopped', + message: 'Replication is paused. Enable the pipeline to resume data synchronization.', + badge: 'Stopped', + }, + starting: { + title: 'Pipeline Starting', + message: 'Initializing replication. Table status will be available once running.', + badge: 'Starting', + }, + running: { + title: 'Pipeline Running', + message: 'Replication is active and processing data', + badge: 'Running', + }, + unknown: { + title: 'Pipeline Status Unknown', + message: 'Unable to determine replication status. Check the logs for more information.', + badge: 'Unknown', + }, + notRunning: { + title: 'Pipeline Not Running', + message: 'Replication is not active. Enable the pipeline to start data synchronization.', + badge: 'Disabled', + }, +} as const + +export const getPipelineStateMessages = ( + requestStatus: PipelineStatusRequestStatus | undefined, + statusName: string | undefined +) => { + // Always prioritize request status (enabling/disabling) over pipeline status + if (requestStatus === PipelineStatusRequestStatus.EnableRequested) { + return PIPELINE_STATE_MESSAGES.enabling + } + + if (requestStatus === PipelineStatusRequestStatus.DisableRequested) { + return PIPELINE_STATE_MESSAGES.disabling + } + + // Only check pipeline status if no request is in progress + switch (statusName) { + case 'failed': + return PIPELINE_STATE_MESSAGES.failed + case 'stopped': + return PIPELINE_STATE_MESSAGES.stopped + case 'starting': + return PIPELINE_STATE_MESSAGES.starting + case 'started': + return PIPELINE_STATE_MESSAGES.running + case 'unknown': + return PIPELINE_STATE_MESSAGES.unknown + default: + return PIPELINE_STATE_MESSAGES.notRunning + } +} diff --git a/apps/studio/components/interfaces/Database/Replication/PipelineStatus.tsx b/apps/studio/components/interfaces/Database/Replication/PipelineStatus.tsx index 6fb360e6d97..f81a8df536a 100644 --- a/apps/studio/components/interfaces/Database/Replication/PipelineStatus.tsx +++ b/apps/studio/components/interfaces/Database/Replication/PipelineStatus.tsx @@ -1,28 +1,11 @@ import AlertError from 'components/ui/AlertError' -import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' -import { cn } from 'ui' -import { ResponseError } from 'types' -import { Loader2, ChevronDown, ChevronRight, Copy, AlertTriangle } from 'lucide-react' -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, - Button, - Collapsible_Shadcn_ as Collapsible, - CollapsibleContent_Shadcn_ as CollapsibleContent, - CollapsibleTrigger_Shadcn_ as CollapsibleTrigger, -} from 'ui' -import { useState, useEffect, useRef } from 'react' -import { copyToClipboard } from 'ui' -import { toast } from 'sonner' import { ReplicationPipelineStatusData } from 'data/replication/pipeline-status-query' - -export enum PipelineStatusRequestStatus { - None = 'None', - EnableRequested = 'EnableRequested', - DisableRequested = 'DisableRequested', -} +import { AlertTriangle, Loader2 } from 'lucide-react' +import { PipelineStatusRequestStatus } from 'state/replication-pipeline-request-status' +import { ResponseError } from 'types' +import { cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui' +import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' +import { getPipelineStateMessages, PIPELINE_ERROR_MESSAGES } from './Pipeline.utils' export enum PipelineStatusName { FAILED = 'failed', @@ -41,10 +24,10 @@ interface PipelineStatusProps { isLoading: boolean isError: boolean isSuccess: boolean - requestStatus: PipelineStatusRequestStatus + requestStatus?: PipelineStatusRequestStatus } -const PipelineStatus = ({ +export const PipelineStatus = ({ pipelineStatus, error, isLoading, @@ -52,34 +35,6 @@ const PipelineStatus = ({ isSuccess, requestStatus, }: PipelineStatusProps) => { - const [isErrorDetailsOpen, setIsErrorDetailsOpen] = useState(false) - const errorDetailsRef = useRef(null) - - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if (errorDetailsRef.current && !errorDetailsRef.current.contains(event.target as Node)) { - setIsErrorDetailsOpen(false) - } - } - - if (isErrorDetailsOpen) { - document.addEventListener('mousedown', handleClickOutside) - } - - return () => { - document.removeEventListener('mousedown', handleClickOutside) - } - }, [isErrorDetailsOpen]) - - const handleCopyToClipboard = async (text: string) => { - try { - await copyToClipboard(text) - toast.success('Error details copied to clipboard') - } catch (error) { - toast.error('Failed to copy error details') - } - } - const isFailedStatus = ( status: ReplicationPipelineStatusData['status'] | undefined ): status is FailedStatus => { @@ -91,144 +46,37 @@ const PipelineStatus = ({ ) } - const isLogLoadingState = (failedStatus: FailedStatus): boolean => { - // Right now we hardcode the error message which is returned when k8s is not able to find logs, which seems - // to be a transient error. In case we find a way to properly handle this in the backend, this hack will not - // be needed anymore. + const renderFailedStatus = () => { return ( - failedStatus.message?.startsWith('unable to retrieve container logs for containerd://') === - true - ) - } - - const renderFailedStatus = (failedStatus: FailedStatus) => { - const hasDetails = - failedStatus.message || failedStatus.reason || failedStatus.exit_code !== undefined - const isLoadingLogs = isLogLoadingState(failedStatus) - - return ( -
- - - -
- {isLoadingLogs ? ( - - ) : ( - - )} - Failed -
-
- -

- {isLoadingLogs - ? 'Pipeline failed - logs are being retrieved from container' - : 'Pipeline has failed - expand for error details'} -

-
-
-
- {hasDetails && ( - - - - - -
- {isLoadingLogs ? ( -
- -
-
- Pipeline Failed - Loading logs... -
-
- Error logs are being retrieved from the container. This may take a few - moments. -
-
-
- ) : ( - <> -
- - Pipeline Error Details - - {(failedStatus.message || failedStatus.reason) && ( - - )} -
-
- {failedStatus.exit_code !== undefined && ( -
- - Exit Code: - -
- {failedStatus.exit_code} -
-
- )} - {failedStatus.reason && ( -
- Reason: -
-                            {failedStatus.reason}
-                          
-
- )} - {failedStatus.message && ( -
- - Message: - -
-                            {failedStatus.message}
-                          
-
- )} -
- - )} -
-
-
- )} -
+ + +
+ + Failed +
+
+ + {getPipelineStateMessages(requestStatus, 'failed').message} + +
) } // Map backend statuses to UX-friendly display const getStatusConfig = () => { + const statusName = + pipelineStatus && typeof pipelineStatus === 'object' && 'name' in pipelineStatus + ? pipelineStatus.name + : undefined + + // Get consistent tooltip message using the same logic as other components + const stateMessages = getPipelineStateMessages(requestStatus, statusName) + if (requestStatus === PipelineStatusRequestStatus.EnableRequested) { return { label: 'Enabling...', dot: , color: 'text-brand-600', - tooltip: 'Pipeline is being enabled and will start shortly', + tooltip: stateMessages.message, } } @@ -237,7 +85,7 @@ const PipelineStatus = ({ label: 'Disabling...', dot: , color: 'text-warning-600', - tooltip: 'Pipeline is being disabled and will stop shortly', + tooltip: stateMessages.message, } } @@ -255,35 +103,35 @@ const PipelineStatus = ({ label: 'Starting', dot: , color: 'text-warning-600', - tooltip: 'Pipeline is initializing and will be ready soon', + tooltip: stateMessages.message, } case PipelineStatusName.STARTED: return { label: 'Running', dot:
, color: 'text-brand-600', - tooltip: 'Pipeline is active and processing data', + tooltip: stateMessages.message, } case PipelineStatusName.STOPPED: return { label: 'Stopped', dot:
, color: 'text-foreground-light', - tooltip: 'Pipeline is not running - enable to start processing', + tooltip: stateMessages.message, } case PipelineStatusName.UNKNOWN: return { label: 'Unknown', dot:
, color: 'text-warning-600', - tooltip: 'Pipeline status could not be determined', + tooltip: stateMessages.message, } default: return { label: 'Unknown', dot:
, color: 'text-destructive-600', - tooltip: 'Pipeline status is unclear - check logs for details', + tooltip: stateMessages.message, } } } @@ -301,31 +149,27 @@ const PipelineStatus = ({ return ( <> - {isLoading && } - {isError && } + {isLoading && } + {isError && ( + + )} {isSuccess && ( <> {statusConfig.isFailedStatus ? ( -
{renderFailedStatus(pipelineStatus as FailedStatus)}
+
{renderFailedStatus()}
) : ( - - - -
- {statusConfig.dot} - {statusConfig.label} -
-
- -

{statusConfig.tooltip}

-
-
-
+ + +
+ {statusConfig.dot} + {statusConfig.label} +
+
+ {statusConfig.tooltip} +
)} )} ) } - -export default PipelineStatus diff --git a/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx new file mode 100644 index 00000000000..24d41862f06 --- /dev/null +++ b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.tsx @@ -0,0 +1,396 @@ +import { Activity, AlertTriangle, ChevronLeft, Copy, ExternalLink, Search } from 'lucide-react' +import Link from 'next/link' +import { useEffect, useState } from 'react' +import { toast } from 'sonner' + +import { useParams } from 'common' +import Table from 'components/to-be-cleaned/Table' +import AlertError from 'components/ui/AlertError' +import { ButtonTooltip } from 'components/ui/ButtonTooltip' +import { useReplicationPipelineByIdQuery } from 'data/replication/pipeline-by-id-query' +import { useReplicationPipelineReplicationStatusQuery } from 'data/replication/pipeline-replication-status-query' +import { useReplicationPipelineStatusQuery } from 'data/replication/pipeline-status-query' +import { useStartPipelineMutation } from 'data/replication/start-pipeline-mutation' +import { useStopPipelineMutation } from 'data/replication/stop-pipeline-mutation' +import { + PipelineStatusRequestStatus, + usePipelineRequestStatus, +} from 'state/replication-pipeline-request-status' +import { Badge, Button, cn, copyToClipboard, Input_Shadcn_ } from 'ui' +import { GenericSkeletonLoader } from 'ui-patterns' +import { getStatusName, PIPELINE_ERROR_MESSAGES } from './Pipeline.utils' +import { PipelineStatus } from './PipelineStatus' +import { TableState } from './ReplicationPipelineStatus.types' +import { getDisabledStateConfig, getStatusConfig } from './ReplicationPipelineStatus.utils' + +interface ReplicationPipelineStatusProps { + pipelineId: number + destinationName?: string + onSelectBack: () => void +} + +export const ReplicationPipelineStatus = ({ + pipelineId, + destinationName, + onSelectBack, +}: ReplicationPipelineStatusProps) => { + const { ref: projectRef } = useParams() + const [filterString, setFilterString] = useState('') + + const { getRequestStatus, updatePipelineStatus, setRequestStatus } = usePipelineRequestStatus() + const requestStatus = getRequestStatus(pipelineId) + + const { + data: pipeline, + error: pipelineError, + isLoading: isPipelineLoading, + isError: isPipelineError, + } = useReplicationPipelineByIdQuery({ + projectRef, + pipelineId, + }) + + const { + data: pipelineStatusData, + error: pipelineStatusError, + isLoading: isPipelineStatusLoading, + isError: isPipelineStatusError, + isSuccess: isPipelineStatusSuccess, + } = useReplicationPipelineStatusQuery( + { projectRef, pipelineId }, + { + enabled: !!pipelineId, + refetchInterval: 2000, // Poll every 2 seconds + } + ) + + const { + data: replicationStatusData, + error: statusError, + isLoading: isStatusLoading, + isError: isStatusError, + } = useReplicationPipelineReplicationStatusQuery( + { projectRef, pipelineId }, + { + enabled: !!pipelineId, + refetchInterval: 2000, // Poll every 2 seconds + } + ) + + const { mutateAsync: startPipeline, isLoading: isStartingPipeline } = useStartPipelineMutation() + const { mutateAsync: stopPipeline, isLoading: isStoppingPipeline } = useStopPipelineMutation() + + const statusName = getStatusName(pipelineStatusData?.status) + const config = getDisabledStateConfig({ requestStatus, statusName }) + + const tableStatuses = replicationStatusData?.table_statuses || [] + const filteredTableStatuses = + filterString.length === 0 + ? tableStatuses + : tableStatuses.filter((table: TableState) => + table.table_name.toLowerCase().includes(filterString.toLowerCase()) + ) + + const errorTables = tableStatuses.filter((table: TableState) => table.state.name === 'error') + const hasErrors = errorTables.length > 0 + const isPipelineRunning = statusName === 'started' + const hasTableData = tableStatuses.length > 0 + const isEnablingDisabling = + requestStatus === PipelineStatusRequestStatus.EnableRequested || + requestStatus === PipelineStatusRequestStatus.DisableRequested + const showDisabledState = !isPipelineRunning || isEnablingDisabling + + const handleCopyTableStatus = async (tableName: string, state: TableState['state']) => { + const statusText = `Table: ${tableName}\nStatus: ${state.name}${ + 'message' in state ? `\nError: ${state.message}` : '' + }${'lag' in state ? `\nLag: ${state.lag}ms` : ''}` + + try { + await copyToClipboard(statusText) + toast.success('Table status copied to clipboard') + } catch { + toast.error(PIPELINE_ERROR_MESSAGES.COPY_TABLE_STATUS) + } + } + + const onTogglePipeline = async () => { + if (!projectRef) { + return console.error('Project ref is required') + } + if (!pipeline) { + return toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND) + } + + try { + if (statusName === 'stopped') { + await startPipeline({ projectRef, pipelineId: pipeline.id }) + setRequestStatus(pipeline.id, PipelineStatusRequestStatus.EnableRequested) + } else if (statusName === 'started') { + await stopPipeline({ projectRef, pipelineId: pipeline.id }) + setRequestStatus(pipeline.id, PipelineStatusRequestStatus.DisableRequested) + } + } catch (error) { + toast.error(PIPELINE_ERROR_MESSAGES.ENABLE_DESTINATION) + } + } + + useEffect(() => { + updatePipelineStatus(pipelineId, statusName) + }, [pipelineId, statusName, updatePipelineStatus]) + + if (isPipelineError) { + return ( +
+
+
+
+
+ +
+ ) + } + + return ( +
+ {/* Header with back button and filters */} +
+
+
+
+
+ + setFilterString(e.target.value)} + /> +
+ +
+
+ + {(isPipelineLoading || isStatusLoading) && } + + {isStatusError && ( + + )} + + {hasErrors && ( +
+
+ +
+

+ {errorTables.length} table{errorTables.length > 1 ? 's' : ''} failed +

+

+ Some tables encountered replication errors. Check the logs for detailed error + information. +

+ +
+
+
+ )} + + {hasTableData && ( +
+ {showDisabledState && ( +
+
+
+
{config.icon}
+
+
+

{config.title}

+

{config.message}

+
+
+
+ )} + +
+ Table, + Status, + Details, + + Actions + , + ]} + body={filteredTableStatuses.map((table: TableState, index: number) => { + const statusConfig = getStatusConfig(table.state) + return ( + + +
+

{table.table_name}

+ + } + tooltip={{ content: { side: 'bottom', text: 'Open in Table Editor' } }} + > + + +
+
+ + {showDisabledState ? ( + Not Available + ) : ( + statusConfig.badge + )} + + + {showDisabledState ? ( +

+ Status unavailable while pipeline is {config.badge.toLowerCase()} +

+ ) : ( +
+
{statusConfig.description}
+ {'lag' in table.state && ( +
+ Lag: {table.state.lag}ms +
+ )} +
+ )} +
+ + } + className="px-1.5" + disabled={showDisabledState} + onClick={() => handleCopyTableStatus(table.table_name, table.state)} + tooltip={{ + content: { + side: 'bottom', + text: showDisabledState + ? `Copy unavailable while pipeline is ${config.badge.toLowerCase()}` + : 'Copy status details', + }, + }} + /> + +
+ ) + })} + /> + + + )} + + {filteredTableStatuses.length === 0 && hasTableData && ( + + +
+

No results found

+

+ Your search for "{filterString}" did not return any results +

+
+
+
+ )} + + {!isStatusLoading && tableStatuses.length === 0 && ( +
+
+
+ +
+
+

+ {showDisabledState ? 'Pipeline Not Running' : 'No table status information'} +

+

+ {showDisabledState + ? `The replication pipeline is currently ${statusName || 'not active'}. Table status + information is not available while the pipeline is in this state.` + : `This pipeline doesn't have any table replication status data available yet. The status will appear here once replication begins.`} +

+
+

+ Data refreshes automatically every 2 seconds +

+
+
+ )} + + ) +} diff --git a/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.types.ts b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.types.ts new file mode 100644 index 00000000000..c5ab523ec1e --- /dev/null +++ b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.types.ts @@ -0,0 +1,10 @@ +export type TableState = { + table_id: number + table_name: string + state: + | { name: 'queued' } + | { name: 'copying_table' } + | { name: 'copied_table' } + | { name: 'following_wal'; lag: number } + | { name: 'error'; message: string } +} diff --git a/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.utils.tsx b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.utils.tsx new file mode 100644 index 00000000000..f9ff22a9e9a --- /dev/null +++ b/apps/studio/components/interfaces/Database/Replication/ReplicationPipelineStatus.utils.tsx @@ -0,0 +1,108 @@ +import { ReplicationPipelineStatusData } from 'data/replication/pipeline-status-query' +import { Activity, Clock, HelpCircle, Loader2, XCircle } from 'lucide-react' +import { PipelineStatusRequestStatus } from 'state/replication-pipeline-request-status' +import { Badge } from 'ui' +import { getPipelineStateMessages } from './Pipeline.utils' +import { TableState } from './ReplicationPipelineStatus.types' + +export const getStatusConfig = (state: TableState['state']) => { + switch (state.name) { + case 'queued': + return { + badge: Queued, + description: 'Waiting to start replication', + color: 'text-warning-600', + } + case 'copying_table': + return { + badge: Copying, + description: 'Initial data copy in progress', + color: 'text-brand-600', + } + case 'copied_table': + return { + badge: Copied, + description: 'Initial copy completed', + color: 'text-success-600', + } + case 'following_wal': + return { + badge: Live, + description: `Replicating live changes`, + color: 'text-success-600', + } + case 'error': + return { + badge: Error, + description: state.message, + color: 'text-destructive-600', + } + default: + return { + badge: Unknown, + description: 'Unknown status', + color: 'text-warning-600', + } + } +} + +export const getDisabledStateConfig = ({ + requestStatus, + statusName, +}: { + requestStatus: PipelineStatusRequestStatus + statusName?: ReplicationPipelineStatusData['status']['name'] +}) => { + const { title, message, badge } = getPipelineStateMessages(requestStatus, statusName) + + // Get icon and colors based on current state + const isEnabling = requestStatus === PipelineStatusRequestStatus.EnableRequested + const isDisabling = requestStatus === PipelineStatusRequestStatus.DisableRequested + const isTransitioning = isEnabling || isDisabling + + const icon = isTransitioning ? ( + + ) : statusName === 'failed' ? ( + + ) : statusName === 'starting' ? ( + + ) : statusName === 'unknown' ? ( + + ) : ( + + ) + + const colors = isEnabling + ? { + bg: 'bg-brand-50', + text: 'text-brand-900', + subtext: 'text-brand-700', + iconBg: 'bg-brand-600', + icon: 'text-white dark:text-black', + } + : isDisabling || statusName === 'starting' || statusName === 'unknown' + ? { + bg: 'bg-warning-50', + text: 'text-warning-900', + subtext: 'text-warning-700', + iconBg: 'bg-warning-600', + icon: 'text-white dark:text-black', + } + : statusName === 'failed' + ? { + bg: 'bg-destructive-50', + text: 'text-destructive-900', + subtext: 'text-destructive-700', + iconBg: 'bg-destructive-600', + icon: 'text-white dark:text-black', + } + : { + bg: 'bg-surface-100', + text: 'text-foreground', + subtext: 'text-foreground-light', + iconBg: 'bg-foreground-lighter', + icon: 'text-white dark:text-black', + } + + return { title, message, badge, icon, colors } +} diff --git a/apps/studio/components/interfaces/Database/Replication/RowMenu.tsx b/apps/studio/components/interfaces/Database/Replication/RowMenu.tsx index f09cc703093..ccd49c4b520 100644 --- a/apps/studio/components/interfaces/Database/Replication/RowMenu.tsx +++ b/apps/studio/components/interfaces/Database/Replication/RowMenu.tsx @@ -1,5 +1,14 @@ -import AlertError from 'components/ui/AlertError' import { Edit, MoreVertical, Pause, Play, Trash } from 'lucide-react' +import { toast } from 'sonner' + +import { useParams } from 'common' +import AlertError from 'components/ui/AlertError' +import { useStartPipelineMutation } from 'data/replication/start-pipeline-mutation' +import { useStopPipelineMutation } from 'data/replication/stop-pipeline-mutation' +import { + PipelineStatusRequestStatus, + usePipelineRequestStatus, +} from 'state/replication-pipeline-request-status' import { ResponseError } from 'types' import { Button, @@ -10,29 +19,31 @@ import { DropdownMenuTrigger, } from 'ui' import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' +import { Pipeline } from './DestinationRow' +import { PIPELINE_ERROR_MESSAGES } from './Pipeline.utils' import { PipelineStatusName } from './PipelineStatus' interface RowMenuProps { + pipeline: Pipeline | undefined pipelineStatus: any error: ResponseError | null isLoading: boolean isError: boolean - onEnableClick: () => void - onDisableClick: () => void onEditClick: () => void onDeleteClick: () => void } -const RowMenu = ({ +export const RowMenu = ({ + pipeline, pipelineStatus, error, isLoading, isError, - onEnableClick, - onDisableClick, onEditClick, onDeleteClick, }: RowMenuProps) => { + const { ref: projectRef } = useParams() + const getStatusName = (status: any) => { if (status && typeof status === 'object' && 'name' in status) { return status.name @@ -43,33 +54,104 @@ const RowMenu = ({ const statusName = getStatusName(pipelineStatus) const pipelineEnabled = statusName !== PipelineStatusName.STOPPED + const { mutateAsync: startPipeline } = useStartPipelineMutation() + const { mutateAsync: stopPipeline } = useStopPipelineMutation() + const { setRequestStatus: setGlobalRequestStatus } = usePipelineRequestStatus() + + const onEnablePipeline = async () => { + if (!projectRef) { + return console.error('Project ref is required') + } + if (!pipeline) { + return toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND) + } + + try { + await startPipeline({ projectRef, pipelineId: pipeline.id }) + toast(`Enabling pipeline ${pipeline.destination_name}`) + setGlobalRequestStatus(pipeline.id, PipelineStatusRequestStatus.EnableRequested) + } catch (error) { + toast.error(PIPELINE_ERROR_MESSAGES.ENABLE_DESTINATION) + } + } + + const onDisablePipeline = async () => { + if (!projectRef) { + console.error('Project ref is required') + return + } + if (!pipeline) { + toast.error(PIPELINE_ERROR_MESSAGES.NO_PIPELINE_FOUND) + return + } + + try { + await stopPipeline({ projectRef, pipelineId: pipeline.id }) + toast(`Disabling pipeline ${pipeline.destination_name}`) + setGlobalRequestStatus(pipeline.id, PipelineStatusRequestStatus.DisableRequested) + } catch (error) { + toast.error(PIPELINE_ERROR_MESSAGES.DISABLE_DESTINATION) + } + } + return (
- {isLoading && } - {isError && } + {isLoading && } + {isError && ( + + )} -
) } - -export default RowMenu diff --git a/apps/studio/components/interfaces/Integrations/Integration/MarkdownContent.tsx b/apps/studio/components/interfaces/Integrations/Integration/MarkdownContent.tsx index cd5e5ec7af6..24b4bec4d40 100644 --- a/apps/studio/components/interfaces/Integrations/Integration/MarkdownContent.tsx +++ b/apps/studio/components/interfaces/Integrations/Integration/MarkdownContent.tsx @@ -42,7 +42,7 @@ export const MarkdownContent = ({ integrationId }: { integrationId: string }) => /> )} {supportExpanding && ( -
+
@@ -325,20 +359,11 @@ export const CreateIcebergWrapperSheet = ({ type="primary" form={FORM_ID} htmlType="submit" - disabled={isCreating} - loading={isCreating} + loading={isLoading} > Create wrapper - setCreateSchemaSheetOpen(false)} - onSuccess={(schema) => { - setFieldValue('target_schema', schema) - setCreateSchemaSheetOpen(false) - }} - /> ) }} diff --git a/apps/studio/components/interfaces/Integrations/Wrappers/CreateWrapperSheet.tsx b/apps/studio/components/interfaces/Integrations/Wrappers/CreateWrapperSheet.tsx index d737aa6733b..73b2ce54d26 100644 --- a/apps/studio/components/interfaces/Integrations/Wrappers/CreateWrapperSheet.tsx +++ b/apps/studio/components/interfaces/Integrations/Wrappers/CreateWrapperSheet.tsx @@ -4,19 +4,18 @@ import { Edit, Trash } from 'lucide-react' import { useState } from 'react' import { toast } from 'sonner' -import SchemaEditor from 'components/interfaces/TableGridEditor/SidePanelEditor/SchemaEditor' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { FormSection, FormSectionContent, FormSectionLabel } from 'components/ui/Forms/FormSection' -import SchemaSelector from 'components/ui/SchemaSelector' import { useDatabaseExtensionsQuery } from 'data/database-extensions/database-extensions-query' +import { useSchemaCreateMutation } from 'data/database/schema-create-mutation' import { invalidateSchemasQuery, useSchemasQuery } from 'data/database/schemas-query' import { useFDWCreateMutation } from 'data/fdw/fdw-create-mutation' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' +import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { Button, Form, Input, - Label_Shadcn_, RadioGroupStacked, RadioGroupStackedItem, Separator, @@ -30,7 +29,6 @@ import InputField from './InputField' import { WrapperMeta } from './Wrappers.types' import { makeValidateRequired } from './Wrappers.utils' import WrapperTableEditor from './WrapperTableEditor' -import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' export interface CreateWrapperSheetProps { isClosing: boolean @@ -48,6 +46,7 @@ export const CreateWrapperSheet = ({ onClose, }: CreateWrapperSheetProps) => { const queryClient = useQueryClient() + const { project } = useProjectContext() const org = useSelectedOrganization() const { mutate: sendEvent } = useSendEventMutation() @@ -73,7 +72,7 @@ export const CreateWrapperSheet = ({ const [formErrors, setFormErrors] = useState<{ [k: string]: string }>({}) - const { mutate: createFDW, isLoading: isCreating } = useFDWCreateMutation({ + const { mutateAsync: createFDW, isLoading: isCreatingWrapper } = useFDWCreateMutation({ onSuccess: () => { toast.success(`Successfully created ${wrapperMeta?.label} foreign data wrapper`) setNewTables([]) @@ -85,8 +84,10 @@ export const CreateWrapperSheet = ({ }, }) - // prefetch schemas to make sure the schema selector is populated - useSchemasQuery({ projectRef: project?.ref, connectionString: project?.connectionString }) + const { data: schemas } = useSchemasQuery({ + projectRef: project?.ref!, + connectionString: project?.connectionString, + }) const initialValues = { wrapper_name: '', @@ -98,6 +99,8 @@ export const CreateWrapperSheet = ({ ), } + const { mutateAsync: createSchema, isLoading: isCreatingSchema } = useSchemaCreateMutation() + const onUpdateTable = (values: any) => { setNewTables((prev) => { // if the new values have tableIndex, we are editing an existing table @@ -121,37 +124,70 @@ export const CreateWrapperSheet = ({ if (values.wrapper_name.length === 0) { errors.wrapper_name = 'Please provide a name for your wrapper' } - if (selectedMode === 'tables' && newTables.length === 0) { - errors.tables = 'Please add at least one table' - } - if (selectedMode === 'schema' && values.source_schema.length === 0) { - errors.source_schema = 'Please provide a source schema' - } - if (!isEmpty(errors)) return setFormErrors(errors) - createFDW({ - projectRef: project?.ref, - connectionString: project?.connectionString, - wrapperMeta, - formState: { ...values, server_name: `${values.wrapper_name}_server` }, - mode: selectedMode, - tables: newTables, - sourceSchema: values.source_schema, - targetSchema: values.target_schema, - }) + if (selectedMode === 'tables') { + if (newTables.length === 0) { + errors.tables = 'Please provide at least one table' + } + } + if (selectedMode === 'schema') { + if (values.source_schema.length === 0) { + errors.source_schema = 'Please provide a source schema' + } + if (values.target_schema.length === 0) { + errors.target_schema = 'Please provide an unique target schema' + } + const foundSchema = schemas?.find((s) => s.name === values.target_schema) + if (foundSchema) { + errors.target_schema = 'This schema already exists. Please specify a unique schema name.' + } + } - sendEvent({ - action: 'foreign_data_wrapper_created', - properties: { - wrapperType: wrapperMeta.label, - }, - groups: { - project: project?.ref ?? 'Unknown', - organization: org?.slug ?? 'Unknown', - }, - }) + setFormErrors(errors) + if (!isEmpty(errors)) { + return + } + + try { + await createSchema({ + projectRef: project?.ref, + connectionString: project?.connectionString, + name: values.target_schema, + }) + + await createFDW({ + projectRef: project?.ref, + connectionString: project?.connectionString, + wrapperMeta, + formState: { + ...values, + server_name: `${values.wrapper_name}_server`, + supabase_target_schema: selectedMode === 'schema' ? values.target_schema : undefined, + }, + mode: selectedMode, + tables: newTables, + sourceSchema: values.source_schema, + targetSchema: values.target_schema, + }) + + sendEvent({ + action: 'foreign_data_wrapper_created', + properties: { + wrapperType: wrapperMeta.label, + }, + groups: { + project: project?.ref ?? 'Unknown', + organization: org?.slug ?? 'Unknown', + }, + }) + } catch (error) { + console.error(error) + // The error will be handled by the mutation onError callback (toast.error) + } } + const isLoading = isCreatingWrapper || isCreatingSchema + return ( <>
@@ -161,7 +197,7 @@ export const CreateWrapperSheet = ({ onSubmit={onSubmit} className="flex-grow flex flex-col h-full" > - {({ handleReset, values, initialValues, setFieldValue }: any) => { + {({ values, initialValues, setFieldValue }: any) => { const hasChanges = JSON.stringify(values) !== JSON.stringify(initialValues) const onClosePanel = () => { @@ -236,7 +272,7 @@ export const CreateWrapperSheet = ({

- Define tables where the wrapper data will be shown. + Create foreign tables to query data from {wrapperMeta.label}.

@@ -261,7 +297,8 @@ export const CreateWrapperSheet = ({

- Specify schema in which the wrapper will create tables. + Create all foreign tables from {wrapperMeta.label} in a specified + schema.

@@ -366,38 +403,44 @@ export const CreateWrapperSheet = ({

Foreign Schema

- All wrapper tables will be created in the specified target schema. + You can query your data from the foreign tables in the specified schema + after the wrapper is created.

} > - {wrapperMeta.sourceSchemaOption && ( -
- -

- {wrapperMeta.sourceSchemaOption.description} -

-
- )} + {wrapperMeta.sourceSchemaOption && + !wrapperMeta.sourceSchemaOption?.readOnly && ( + // Hide the field if the source schema is read-only +
+ +

+ {wrapperMeta.sourceSchemaOption.description} +

+
+ )}
- - Target Schema - - setFieldValue('target_schema', schema)} - onSelectCreateSchema={() => setCreateSchemaSheetOpen(true)} +

- Be careful not to use an API exposed schema. + A new schema will be created. For security purposes, the wrapper tables + from the foreign schema cannot be created within an existing schema

@@ -411,7 +454,7 @@ export const CreateWrapperSheet = ({ type="default" htmlType="button" onClick={onClosePanel} - disabled={isCreating} + disabled={isLoading} > Cancel @@ -420,20 +463,11 @@ export const CreateWrapperSheet = ({ type="primary" form={FORM_ID} htmlType="submit" - disabled={isCreating} - loading={isCreating} + loading={isLoading} > Create wrapper - setCreateSchemaSheetOpen(false)} - onSuccess={(schema) => { - setFieldValue('target_schema', schema) - setCreateSchemaSheetOpen(false) - }} - /> ) }} diff --git a/apps/studio/components/interfaces/Integrations/Wrappers/WrapperRow.tsx b/apps/studio/components/interfaces/Integrations/Wrappers/WrapperRow.tsx index fb9763e6701..e7c9c155fbe 100644 --- a/apps/studio/components/interfaces/Integrations/Wrappers/WrapperRow.tsx +++ b/apps/studio/components/interfaces/Integrations/Wrappers/WrapperRow.tsx @@ -107,37 +107,39 @@ const WrapperRow = ({ wrapper }: WrapperRowProps) => {
))} - - } - className="px-1.5" - onClick={() => setEditWrapperShown(true)} - tooltip={{ - content: { - side: 'bottom', - text: !canManageWrappers - ? 'You need additional permissions to edit wrappers' - : 'Edit wrapper', - }, - }} - /> - } - className="px-1.5" - onClick={() => setDeleteWrapperShown(true)} - tooltip={{ - content: { - side: 'bottom', - text: !canManageWrappers - ? 'You need additional permissions to delete wrappers' - : 'Delete wrapper', - }, - }} - /> + +
+ } + className="px-1.5" + onClick={() => setEditWrapperShown(true)} + tooltip={{ + content: { + side: 'bottom', + text: !canManageWrappers + ? 'You need additional permissions to edit wrappers' + : 'Edit wrapper', + }, + }} + /> + } + className="px-1.5" + onClick={() => setDeleteWrapperShown(true)} + tooltip={{ + content: { + side: 'bottom', + text: !canManageWrappers + ? 'You need additional permissions to delete wrappers' + : 'Delete wrapper', + }, + }} + /> +
setIsClosingEditWrapper(true)}> diff --git a/apps/studio/components/interfaces/Integrations/Wrappers/Wrappers.constants.ts b/apps/studio/components/interfaces/Integrations/Wrappers/Wrappers.constants.ts index 4055a4423c3..dad85cda513 100644 --- a/apps/studio/components/interfaces/Integrations/Wrappers/Wrappers.constants.ts +++ b/apps/studio/components/interfaces/Integrations/Wrappers/Wrappers.constants.ts @@ -1,6 +1,6 @@ import { BASE_PATH } from 'lib/constants' import { CreateIcebergWrapperSheet } from './CreateIcebergWrapperSheet' -import type { WrapperMeta } from './Wrappers.types' +import type { ServerOption, WrapperMeta } from './Wrappers.types' export const WRAPPER_HANDLERS = { STRIPE: 'stripe_fdw_handler', @@ -27,6 +27,16 @@ export const WRAPPER_HANDLERS = { ORB: 'wasm_fdw_handler', } +const SUPABASE_TARGET_SCHEMA_OPTION: ServerOption = { + name: 'supabase_target_schema', + label: 'Target Schema', + required: false, + encrypted: false, + secureEntry: false, + readOnly: true, + hidden: true, +} + export const WRAPPERS: WrapperMeta[] = [ { name: 'stripe_wrapper', @@ -55,6 +65,7 @@ export const WRAPPERS: WrapperMeta[] = [ encrypted: false, secureEntry: false, }, + SUPABASE_TARGET_SCHEMA_OPTION, ], }, tables: [ @@ -1775,6 +1786,7 @@ export const WRAPPERS: WrapperMeta[] = [ encrypted: false, secureEntry: false, }, + SUPABASE_TARGET_SCHEMA_OPTION, ], }, canTargetSchema: true, @@ -2224,12 +2236,13 @@ export const WRAPPERS: WrapperMeta[] = [ encrypted: false, secureEntry: false, }, + SUPABASE_TARGET_SCHEMA_OPTION, ], }, canTargetSchema: true, sourceSchemaOption: { name: 'source_schema', - label: 'Source Schema', + label: 'Namespace', description: 'It should match the namespace of the Iceberg catalog.', required: true, encrypted: false, diff --git a/apps/studio/components/interfaces/Integrations/Wrappers/Wrappers.utils.ts b/apps/studio/components/interfaces/Integrations/Wrappers/Wrappers.utils.ts index 63786de027c..cddc265b165 100644 --- a/apps/studio/components/interfaces/Integrations/Wrappers/Wrappers.utils.ts +++ b/apps/studio/components/interfaces/Integrations/Wrappers/Wrappers.utils.ts @@ -12,7 +12,7 @@ export const makeValidateRequired = (options: { name: string; required: boolean ) const requiredArrayOptions = Array.from(requiredArrayOptionsSet) - return (values: any) => { + return (values: Record) => { const errors = Object.fromEntries( Object.entries(values) .flatMap(([key, value]) => diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerData.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerData.tsx index 5082e90df99..a137720baa9 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerData.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerData.tsx @@ -20,15 +20,18 @@ import { useAsyncCheckProjectPermissions, useCheckPermissions, } from 'hooks/misc/useCheckPermissions' -import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' +import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { Button, Card, CardFooter, Form_Shadcn_ as Form } from 'ui' -import { BillingCustomerDataForm } from './BillingCustomerDataForm' +import { + BillingCustomerDataForm, + type BillingCustomerDataFormValues, +} from './BillingCustomerDataForm' import { TAX_IDS } from './TaxID.constants' import { useBillingCustomerDataForm } from './useBillingCustomerDataForm' export const BillingCustomerData = () => { const { slug } = useParams() - const selectedOrganization = useSelectedOrganization() + const { data: selectedOrganization } = useSelectedOrganizationQuery() const { isSuccess: isPermissionsLoaded, can: canReadBillingCustomerData } = useAsyncCheckProjectPermissions(PermissionAction.BILLING_READ, 'stripe.customer') @@ -51,9 +54,13 @@ export const BillingCustomerData = () => { isSuccess: loadedTaxId, } = useOrganizationTaxIdQuery({ slug }) - const initialCustomerData = useMemo( + const initialCustomerData = useMemo>( () => ({ - ...customerProfile?.address, + city: customerProfile?.address?.city ?? undefined, + country: customerProfile?.address?.country, + line1: customerProfile?.address?.line1, + line2: customerProfile?.address?.line2 ?? undefined, + postal_code: customerProfile?.address?.postal_code ?? undefined, billing_name: customerProfile?.billing_name, tax_id_type: taxId?.type, tax_id_value: taxId?.value, diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerDataExistingOrgDialog.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerDataExistingOrgDialog.tsx deleted file mode 100644 index 9fac673f8fc..00000000000 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerDataExistingOrgDialog.tsx +++ /dev/null @@ -1,215 +0,0 @@ -import { PermissionAction } from '@supabase/shared-types/out/constants' -import { Pencil } from 'lucide-react' -import { useMemo, useState } from 'react' -import { toast } from 'sonner' - -import AlertError from 'components/ui/AlertError' -import NoPermission from 'components/ui/NoPermission' -import ShimmeringLoader from 'components/ui/ShimmeringLoader' -import { useOrganizationCustomerProfileQuery } from 'data/organizations/organization-customer-profile-query' -import { useOrganizationCustomerProfileUpdateMutation } from 'data/organizations/organization-customer-profile-update-mutation' -import { useOrganizationTaxIdQuery } from 'data/organizations/organization-tax-id-query' -import { useOrganizationTaxIdUpdateMutation } from 'data/organizations/organization-tax-id-update-mutation' -import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' -import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' -import { - Button, - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogSectionSeparator, - DialogTitle, - Form_Shadcn_ as Form, - Label_Shadcn_ as Label, -} from 'ui' -import { BillingCustomerDataForm } from './BillingCustomerDataForm' -import { TAX_IDS } from './TaxID.constants' -import { useBillingCustomerDataForm } from './useBillingCustomerDataForm' - -export const BillingCustomerDataExistingOrgDialog = () => { - const [open, setOpen] = useState(false) - const [isSubmitting, setIsSubmitting] = useState(false) - - const { slug } = useSelectedOrganization() ?? {} - - const canReadBillingCustomerData = useCheckPermissions( - PermissionAction.BILLING_READ, - 'stripe.customer' - ) - - const canUpdateBillingCustomerData = useCheckPermissions( - PermissionAction.BILLING_WRITE, - 'stripe.customer' - ) - - const { - data: customerProfile, - error, - isLoading, - isSuccess, - } = useOrganizationCustomerProfileQuery({ slug }, { enabled: canReadBillingCustomerData }) - - const { - data: taxId, - error: errorTaxId, - isLoading: isLoadingTaxId, - isSuccess: isSuccessTaxId, - } = useOrganizationTaxIdQuery({ slug }) - - const initialCustomerData = useMemo( - () => ({ - ...customerProfile?.address, - billing_name: customerProfile?.billing_name, - tax_id_type: taxId?.type, - tax_id_value: taxId?.value, - tax_id_name: taxId - ? TAX_IDS.find( - (option) => option.type === taxId.type && option.countryIso2 === taxId.country - )?.name || '' - : '', - }), - [customerProfile, taxId] - ) - - const { mutateAsync: updateCustomerProfile } = useOrganizationCustomerProfileUpdateMutation() - const { mutateAsync: updateTaxId } = useOrganizationTaxIdUpdateMutation() - - const { form, handleSubmit, handleReset, isDirty } = useBillingCustomerDataForm({ - initialCustomerData, - onCustomerDataChange: async (data) => { - setIsSubmitting(true) - - try { - await updateCustomerProfile({ - slug, - address: data.address ?? undefined, - billing_name: data.billing_name, - }) - - await updateTaxId({ slug, taxId: data.tax_id }) - - toast.success('Successfully updated billing data') - - setOpen(false) - - setIsSubmitting(false) - } catch (error: any) { - toast.error(`Failed updating billing data: ${error.message}`) - setIsSubmitting(false) - } - }, - }) - - const handleClose = () => { - handleReset() - setOpen(false) - } - - const getAddressSummary = () => { - if (!customerProfile?.address?.line1) return 'Optionally add a billing address' - - const parts = [ - customerProfile?.billing_name, - customerProfile?.address?.line1, - customerProfile?.address?.city, - customerProfile?.address?.state, - customerProfile?.address?.country, - ].filter(Boolean) - - return parts.join(', ') - } - - const isSubmitDisabled = !isDirty || !canUpdateBillingCustomerData || isSubmitting - - return ( - <> -
- - {!canReadBillingCustomerData ? ( - - ) : ( - <> - {(isLoading || isLoadingTaxId) && ( -
- -
- )} - {(error || errorTaxId) && ( - - )} - {isSuccess && isSuccessTaxId && ( -
-

{getAddressSummary()}

- -
- )} - - )} -
- - { - if (!value) setOpen(false) - else setOpen(true) - }} - > - - - Billing Address & Tax Id - - This will be reflected in every upcoming invoice, past invoices are not affected - - - -
- - - - {!canUpdateBillingCustomerData && ( - - You need additional permissions to manage this organization's billing address - - )} -
- - -
-
- - -
-
- - ) -} diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerDataForm.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerDataForm.tsx index 7b7a8da1333..98e84f71649 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerDataForm.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerDataForm.tsx @@ -2,7 +2,7 @@ import { Check, ChevronsUpDown, X } from 'lucide-react' import { UseFormReturn } from 'react-hook-form' import { z } from 'zod' -import { useState } from 'react' +import { useMemo, useState } from 'react' import { Button, cn, @@ -90,18 +90,24 @@ export const BillingCustomerDataForm = ({ form.setValue('tax_id_value', '', { shouldDirty: true }) } - const { tax_id_name } = form.watch() + const { tax_id_name, country } = form.watch() const selectedTaxId = TAX_IDS.find((option) => option.name === tax_id_name) + const availableTaxIds = useMemo(() => { + return TAX_IDS.filter((taxId) => !country || taxId.countryIso2 === country).sort((a, b) => + a.country.localeCompare(b.country) + ) + }, [country]) + return (
( - + - + @@ -112,9 +118,9 @@ export const BillingCustomerDataForm = ({ control={form.control} name="line1" render={({ field }: { field: any }) => ( - + - + @@ -125,9 +131,13 @@ export const BillingCustomerDataForm = ({ control={form.control} name="line2" render={({ field }: { field: any }) => ( - + - + @@ -139,7 +149,7 @@ export const BillingCustomerDataForm = ({ control={form.control} name="country" render={({ field }: { field: any }) => ( - + @@ -207,9 +217,9 @@ export const BillingCustomerDataForm = ({ control={form.control} name="postal_code" render={({ field }: { field: any }) => ( - + - + @@ -222,9 +232,9 @@ export const BillingCustomerDataForm = ({ control={form.control} name="city" render={({ field }: { field: any }) => ( - + - + @@ -234,9 +244,9 @@ export const BillingCustomerDataForm = ({ control={form.control} name="state" render={({ field }: { field: any }) => ( - + - + @@ -281,26 +291,24 @@ export const BillingCustomerDataForm = ({ No tax ID found. - {TAX_IDS.sort((a, b) => a.country.localeCompare(b.country)).map( - (option) => ( - { - onSelectTaxIdType(option.name) - setShowTaxIDsPopover(false) - }} - > - - {option.country} - {option.name} - - ) - )} + {availableTaxIds.map((option) => ( + { + onSelectTaxIdType(option.name) + setShowTaxIDsPopover(false) + }} + > + + {option.country} - {option.name} + + ))} diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerDataNewOrgDialog.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerDataNewOrgDialog.tsx deleted file mode 100644 index dd072469401..00000000000 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/BillingCustomerDataNewOrgDialog.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import { FocusTrap } from '@headlessui/react' -import { Pencil } from 'lucide-react' -import { useState } from 'react' - -import { - Button, - Dialog, - DialogContent, - DialogFooter, - DialogHeader, - DialogSectionSeparator, - DialogTitle, - DialogTrigger, - Form_Shadcn_ as Form, -} from 'ui' -import { BillingCustomerDataForm } from './BillingCustomerDataForm' -import { FormCustomerData, useBillingCustomerDataForm } from './useBillingCustomerDataForm' - -interface BillingCustomerDataNewOrgDialogProps { - onCustomerDataChange: (data: FormCustomerData) => void -} - -export const BillingCustomerDataNewOrgDialog = ({ - onCustomerDataChange, -}: BillingCustomerDataNewOrgDialogProps) => { - const [open, setOpen] = useState(false) - - const handleDialogClose = () => { - setOpen(false) - } - - const { form, handleSubmit, handleReset, isDirty } = useBillingCustomerDataForm({ - onCustomerDataChange, - }) - - const handleClose = () => { - handleReset() - handleDialogClose() - } - - const getAddressSummary = () => { - const { line1, city, state, country, billing_name } = form.getValues() - - if (!line1) return 'Optionally add a billing address' - - const parts = [billing_name, line1, city, state, country].filter(Boolean) - - return parts.join(', ') - } - - const isSubmitDisabled = !isDirty - - return ( -
- - { - if (!value) handleDialogClose() - else setOpen(true) - }} - > - - - - - - Billing Address & Tax Id - - - {/* - [Joshen] There's something odd going on with using Dialog and RHF FormField here - where the focus keeps going to the Dialog when tabbing across the input fields, hence the FocusTrap - What's weirder is that once you've cycled the focus through all the inputs at least once, the focus - then no longer goes to the Dialog thereafter - FWIW it's likely happening across the dashboard whereever we're using Dialog and FormField - */} - -
- { - e.preventDefault() - e.stopPropagation() - form.handleSubmit(handleSubmit)(e) - - // Only close the dialog if the form is valid - if (!Object.keys(form.formState.errors).length) { - handleDialogClose() - } - }} - > - - -
- - -
-
- - -
-
-
-
- ) -} diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/TaxID.constants.ts b/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/TaxID.constants.ts index dcc09c8e795..290af4077ed 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/TaxID.constants.ts +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/TaxID.constants.ts @@ -1,8 +1,3 @@ -// List of available Tax IDs as reflected in Stripe's web portal -// This was manually ported over so there may be a chance of mistakes -// Last updated as of 29th March 2022. -// The code may not necessarily match with the name (ref SE_VAT) -// https://stripe.com/docs/api/customer_tax_ids/create export interface TaxId { name: string type: string diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/useBillingCustomerDataForm.ts b/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/useBillingCustomerDataForm.ts index aef1e286377..176a3e4f19d 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/useBillingCustomerDataForm.ts +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingCustomerData/useBillingCustomerDataForm.ts @@ -6,9 +6,10 @@ import { BillingCustomerDataFormValues, BillingCustomerDataSchema } from './Bill import { TAX_IDS } from './TaxID.constants' import { sanitizeTaxIdValue } from './TaxID.utils' import { components } from 'api-types' +import type { CustomerAddress } from 'data/organizations/types' export type FormCustomerData = { - address: components['schemas']['CustomerResponse']['address'] | undefined + address: CustomerAddress | undefined billing_name: string tax_id: components['schemas']['TaxIdResponse']['tax_id'] | null } diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/CreditTopUp.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/CreditTopUp.tsx index 341df597737..0638818ac92 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/CreditTopUp.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/CreditTopUp.tsx @@ -7,7 +7,7 @@ import { useQueryClient } from '@tanstack/react-query' import { AlertCircle, Info } from 'lucide-react' import { useTheme } from 'next-themes' import Link from 'next/link' -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' import { toast } from 'sonner' import { z } from 'zod' @@ -17,7 +17,6 @@ import { useOrganizationCreditTopUpMutation } from 'data/organizations/organizat import { subscriptionKeys } from 'data/subscriptions/keys' import { useCheckPermissions, usePermissionsLoaded } from 'hooks/misc/useCheckPermissions' import { STRIPE_PUBLIC_KEY } from 'lib/constants' -import { useIsHCaptchaLoaded } from 'stores/hcaptcha-loaded-store' import { Alert_Shadcn_, AlertDescription_Shadcn_, @@ -40,6 +39,7 @@ import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import PaymentMethodSelection from './Subscription/PaymentMethodSelection' import { PaymentConfirmation } from 'components/interfaces/Billing/Payment/PaymentConfirmation' import { getStripeElementsAppearanceOptions } from 'components/interfaces/Billing/Payment/Payment.utils' +import type { PaymentMethodElementRef } from './PaymentMethods/NewPaymentMethodElement' const stripePromise = loadStripe(STRIPE_PUBLIC_KEY) @@ -48,8 +48,8 @@ const FORM_ID = 'credit-top-up' const FormSchema = z.object({ amount: z.coerce .number() - .gte(100, 'Amount must be between $100 - $999.') - .lte(999, 'Amount must be between $100 - $999.') + .gte(100, 'Amount must be between $100 - $2000.') + .lte(2000, 'Amount must be between $100 - $2000.') .int('Amount must be a whole number.'), paymentMethod: z.string(), }) @@ -59,6 +59,9 @@ type CreditTopUpForm = z.infer export const CreditTopUp = ({ slug }: { slug: string | undefined }) => { const { resolvedTheme } = useTheme() const queryClient = useQueryClient() + const paymentMethodSelectionRef = useRef<{ + createPaymentMethod: PaymentMethodElementRef['createPaymentMethod'] + }>(null) const canTopUpCredits = useCheckPermissions( PermissionAction.BILLING_WRITE, @@ -82,7 +85,6 @@ export const CreditTopUp = ({ slug }: { slug: string | undefined }) => { const [topUpModalVisible, setTopUpModalVisible] = useState(false) const [paymentConfirmationLoading, setPaymentConfirmationLoading] = useState(false) - const captchaLoaded = useIsHCaptchaLoaded() const [captchaToken, setCaptchaToken] = useState(null) const [captchaRef, setCaptchaRef] = useState(null) @@ -96,7 +98,7 @@ export const CreditTopUp = ({ slug }: { slug: string | undefined }) => { } const initHcaptcha = async () => { - if (topUpModalVisible && captchaRef && captchaLoaded) { + if (topUpModalVisible && captchaRef) { let token = captchaToken try { @@ -116,7 +118,7 @@ export const CreditTopUp = ({ slug }: { slug: string | undefined }) => { useEffect(() => { initHcaptcha() - }, [topUpModalVisible, captchaRef, captchaLoaded]) + }, [topUpModalVisible, captchaRef]) const [paymentIntentSecret, setPaymentIntentSecret] = useState('') const [paymentIntentConfirmation, setPaymentIntentConfirmation] = useState() @@ -126,12 +128,20 @@ export const CreditTopUp = ({ slug }: { slug: string | undefined }) => { const token = await initHcaptcha() + const paymentMethodResult = await paymentMethodSelectionRef.current?.createPaymentMethod() + if (!paymentMethodResult) { + return + } + await topUpCredits( { slug, amount, - payment_method_id: paymentMethod, + payment_method_id: paymentMethodResult.paymentMethod.id, hcaptchaToken: token, + address: paymentMethodResult.address, + tax_id: paymentMethodResult.taxId ?? undefined, + billing_name: paymentMethodResult.customerName, }, { onSuccess: (data) => { @@ -225,10 +235,21 @@ export const CreditTopUp = ({ slug }: { slug: string | undefined }) => { /> Top Up Credits - - On successful payment, an invoice will be issued and you'll be granted credits. - Credits will be applied to outstanding and future invoices and are not refundable. The - topped up credits do not expire. + +

+ On successful payment, an invoice will be issued and you'll be granted credits. + Credits will be applied to outstanding and future invoices and are not refundable. + The topped up credits do not expire. +

+

+ For larger discounted credit packages, please{' '} + + reach out. + +

@@ -252,7 +273,7 @@ export const CreditTopUp = ({ slug }: { slug: string | undefined }) => { name="paymentMethod" render={() => ( form.setValue('paymentMethod', pm)} selectedPaymentMethod={form.getValues('paymentMethod')} readOnly={executingTopUp || paymentConfirmationLoading} @@ -260,16 +281,6 @@ export const CreditTopUp = ({ slug }: { slug: string | undefined }) => { )} /> -

- For larger discounted credit packages, please{' '} - - reach out. - -

- {paymentIntentConfirmation && paymentIntentConfirmation.error && ( diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/NewPaymentMethodElement.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/NewPaymentMethodElement.tsx index 9070143191f..11a41994047 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/NewPaymentMethodElement.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/NewPaymentMethodElement.tsx @@ -4,26 +4,148 @@ * If Elements is on a higher level, we risk losing all form state in case a payment fails. */ -import { PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js' -import { forwardRef, useImperativeHandle } from 'react' +import { AddressElement, PaymentElement, useElements, useStripe } from '@stripe/react-stripe-js' +import { + StripeAddressElementChangeEvent, + StripeAddressElementOptions, + type SetupIntent, +} from '@stripe/stripe-js' +import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from 'react' import { toast } from 'sonner' +import { + Button, + Checkbox_Shadcn_, + cn, + Command_Shadcn_ as Command, + CommandEmpty_Shadcn_ as CommandEmpty, + CommandGroup_Shadcn_ as CommandGroup, + CommandInput_Shadcn_ as CommandInput, + CommandItem_Shadcn_ as CommandItem, + CommandList_Shadcn_ as CommandList, + FormControl_Shadcn_ as FormControl, + FormField_Shadcn_ as FormField, + FormItem_Shadcn_, + FormMessage_Shadcn_ as FormMessage, + Input_Shadcn_ as Input, + Popover_Shadcn_ as Popover, + PopoverContent_Shadcn_ as PopoverContent, + PopoverTrigger_Shadcn_ as PopoverTrigger, +} from 'ui' +import { TAX_IDS, type TaxId } from '../BillingCustomerData/TaxID.constants' +import { z } from 'zod' +import { useForm } from 'react-hook-form' +import { Form } from '@ui/components/shadcn/ui/form' +import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' +import { Check, ChevronsUpDown } from 'lucide-react' +import { zodResolver } from '@hookform/resolvers/zod' +import { getURL } from 'lib/helpers' +import type { CustomerAddress, CustomerTaxId } from 'data/organizations/types' +import type { PaymentMethod } from '@stripe/stripe-js' + +export const BillingCustomerDataSchema = z.object({ + tax_id_type: z.string(), + tax_id_value: z.string().min(2, { + message: 'Tax ID needs to be set.', + }), + tax_id_name: z.string(), +}) + +type BillingCustomerDataFormValues = z.infer + +export type PaymentMethodElementRef = { + confirmSetup: () => Promise< + | { + setupIntent: SetupIntent + address: CustomerAddress + customerName: string + taxId: CustomerTaxId | null + } + | undefined + > + createPaymentMethod: () => Promise< + | { + paymentMethod: PaymentMethod + address: CustomerAddress | null + customerName: string | null + taxId: CustomerTaxId | null + } + | undefined + > +} const NewPaymentMethodElement = forwardRef( ( { email, readOnly, + currentAddress, + currentTaxId, + customerName, }: { email?: string | null | undefined readOnly: boolean + currentAddress?: CustomerAddress | null + currentTaxId?: CustomerTaxId | null + customerName?: string | undefined }, ref ) => { const stripe = useStripe() const elements = useElements() - const createPaymentMethod = async () => { + const form = useForm({ + resolver: zodResolver(BillingCustomerDataSchema), + defaultValues: { + tax_id_name: currentTaxId + ? TAX_IDS.find( + (option) => + option.type === currentTaxId.type && option.countryIso2 === currentTaxId.country + )?.name || '' + : '', + tax_id_type: currentTaxId ? currentTaxId.type : '', + tax_id_value: currentTaxId ? currentTaxId.value : '', + }, + }) + + // To avoid rendering the business checkbox prematurely and causing weird layout shifts, we wait until the address element is fully loaded + const [fullyLoaded, setFullyLoaded] = useState(false) + + const [showTaxIDsPopover, setShowTaxIDsPopover] = useState(false) + + const onSelectTaxIdType = (name: string) => { + const selectedTaxIdOption = TAX_IDS.find((option) => option.name === name) + if (!selectedTaxIdOption) return + form.setValue('tax_id_type', selectedTaxIdOption.type) + form.setValue('tax_id_value', '') + form.setValue('tax_id_name', name) + } + + const { tax_id_name } = form.watch() + const selectedTaxId = TAX_IDS.find((option) => option.name === tax_id_name) + + const [purchasingAsBusiness, setPurchasingAsBusiness] = useState(currentTaxId != null) + const [stripeAddress, setStripeAddress] = useState< + StripeAddressElementChangeEvent['value'] | undefined + >(undefined) + + const availableTaxIds = useMemo(() => { + const country = stripeAddress?.address.country || null + + return TAX_IDS.filter((taxId) => country == null || taxId.countryIso2 === country).sort( + (a, b) => a.country.localeCompare(b.country) + ) + }, [stripeAddress]) + + const createPaymentMethod = async (): ReturnType< + PaymentMethodElementRef['createPaymentMethod'] + > => { if (!stripe || !elements) return + await form.trigger() + + if (purchasingAsBusiness && availableTaxIds.length > 0 && !form.getValues('tax_id_value')) { + return + } + await elements.submit() // To avoid double 3DS confirmation, we just create the payment method here, as there might be a confirmation step while doing the actual payment @@ -34,17 +156,209 @@ const NewPaymentMethodElement = forwardRef( toast.error(error?.message ?? ' Failed to process card details') return } - return paymentMethod + + const addressElement = await elements.getElement('address')!.getValue() + return { + paymentMethod, + address: { + ...addressElement.value.address, + line2: addressElement.value.address.line2 || undefined, + }, + customerName: addressElement.value.name, + taxId: getConfiguredTaxId(), + } + } + + function getConfiguredTaxId(): CustomerTaxId | null { + return purchasingAsBusiness && selectedTaxId + ? { + country: selectedTaxId.countryIso2, + type: selectedTaxId.type, + value: form.getValues('tax_id_value'), + } + : null + } + + const confirmSetup = async (): ReturnType => { + if (!stripe || !elements) return + + await elements.submit() + + const { error, setupIntent } = await stripe.confirmSetup({ + elements, + redirect: 'if_required', + confirmParams: { return_url: `${getURL()}/org/_/billing` }, + }) + + if (error || setupIntent == null) { + toast.error(error?.message ?? ' Failed to process card details') + return + } + + const addressElement = await elements.getElement('address')!.getValue() + return { + setupIntent, + address: { + ...addressElement.value.address, + line2: addressElement.value.address.line2 || undefined, + }, + customerName: addressElement.value.name, + taxId: getConfiguredTaxId(), + } } useImperativeHandle(ref, () => ({ createPaymentMethod, + confirmSetup, })) + const addressOptions: StripeAddressElementOptions = useMemo( + () => ({ + mode: 'billing', + autocomplete: { + apiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_KEY!, + mode: 'google_maps_api', + }, + display: { name: purchasingAsBusiness ? 'organization' : 'full' }, + defaultValues: { + address: currentAddress ?? undefined, + name: customerName, + }, + }), + [purchasingAsBusiness] + ) + + // Preselect tax id if there is no more than 2 available tax ids (even if there are two options, first one in the list is likely to be it) + useEffect(() => { + if (availableTaxIds.length && stripeAddress?.address.country && !currentTaxId) { + const taxIdOption = availableTaxIds[0] + form.setValue('tax_id_type', taxIdOption.type) + form.setValue('tax_id_value', '') + form.setValue('tax_id_name', taxIdOption.name) + } + }, [availableTaxIds, stripeAddress]) + return ( - +
+

+ Please ensure CVC and postal codes match what is on file for your card. +

+ + + + {fullyLoaded && ( +
+ setPurchasingAsBusiness(!purchasingAsBusiness)} + /> + +
+ )} + + setStripeAddress(evt.value)} + onReady={() => setFullyLoaded(true)} + /> + + {purchasingAsBusiness && availableTaxIds.length > 0 && ( +
+
+ ( + + + + + + + + + + + + No tax ID found. + + {availableTaxIds.map((option) => ( + { + onSelectTaxIdType(option.name) + setShowTaxIDsPopover(false) + }} + > + + {option.country} - {option.name} + + ))} + + + + + + + + )} + /> + + {selectedTaxId && ( + ( + + + + + + + )} + /> + )} +
+ + )} +
) } ) diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/PaymentMethods.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/PaymentMethods.tsx index 15b85fa5ecf..93e42b96b51 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/PaymentMethods.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/PaymentMethods/PaymentMethods.tsx @@ -176,7 +176,6 @@ const PaymentMethods = () => { setShowAddPaymentMethodModal(false) toast.success('Successfully added new payment method') }} - showSetDefaultCheckbox={true} /> { - const selectedOrganization = useSelectedOrganization() + const { data: selectedOrganization } = useSelectedOrganizationQuery() const orgSlug = selectedOrganization?.slug const features = pickFeatures(plan, billingPartner) diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/MembersExceedLimitModal.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/MembersExceedLimitModal.tsx index 44ea19292d2..510d5a1b8c2 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/MembersExceedLimitModal.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/MembersExceedLimitModal.tsx @@ -1,5 +1,5 @@ import { useFreeProjectLimitCheckQuery } from 'data/organizations/free-project-limit-check-query' -import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' +import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { Button, Modal } from 'ui' export interface MembersExceedLimitModalProps { @@ -8,7 +8,7 @@ export interface MembersExceedLimitModalProps { } const MembersExceedLimitModal = ({ visible, onClose }: MembersExceedLimitModalProps) => { - const selectedOrganization = useSelectedOrganization() + const { data: selectedOrganization } = useSelectedOrganizationQuery() const slug = selectedOrganization?.slug const { data: membersExceededLimit } = useFreeProjectLimitCheckQuery( { slug }, diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PaymentMethodSelection.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PaymentMethodSelection.tsx index 3754491fbd1..b775173a3c8 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PaymentMethodSelection.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PaymentMethodSelection.tsx @@ -1,4 +1,3 @@ -import { PermissionAction } from '@supabase/shared-types/out/constants' import { forwardRef, useCallback, @@ -9,26 +8,30 @@ import { useState, } from 'react' import { toast } from 'sonner' - -import AddNewPaymentMethodModal from 'components/interfaces/Billing/Payment/AddNewPaymentMethodModal' -import { ButtonTooltip } from 'components/ui/ButtonTooltip' import { useOrganizationPaymentMethodsQuery } from 'data/organizations/organization-payment-methods-query' -import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' -import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' +import { + useSelectedOrganization, + useSelectedOrganizationQuery, +} from 'hooks/misc/useSelectedOrganization' import { BASE_PATH, STRIPE_PUBLIC_KEY } from 'lib/constants' -import { getURL } from 'lib/helpers' -import { AlertCircle, CreditCard, Loader, Plus } from 'lucide-react' -import { Listbox } from 'ui' +import { Loader, Plus } from 'lucide-react' +import { Checkbox_Shadcn_, Listbox } from 'ui' import HCaptcha from '@hcaptcha/react-hcaptcha' -import { useIsHCaptchaLoaded } from 'stores/hcaptcha-loaded-store' import { useOrganizationPaymentMethodSetupIntent } from 'data/organizations/organization-payment-method-setup-intent-mutation' import { SetupIntentResponse } from 'data/stripe/setup-intent-mutation' import { loadStripe, PaymentMethod, StripeElementsOptions } from '@stripe/stripe-js' import { getStripeElementsAppearanceOptions } from 'components/interfaces/Billing/Payment/Payment.utils' import { useTheme } from 'next-themes' import { Elements } from '@stripe/react-stripe-js' -import { NewPaymentMethodElement } from '../PaymentMethods/NewPaymentMethodElement' +import { + NewPaymentMethodElement, + type PaymentMethodElementRef, +} from '../PaymentMethods/NewPaymentMethodElement' import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' +import { useFlag } from 'hooks/ui/useFlag' +import { useOrganizationCustomerProfileQuery } from 'data/organizations/organization-customer-profile-query' +import { useOrganizationTaxIdQuery } from 'data/organizations/organization-tax-id-query' +import { useParams } from 'common' const stripePromise = loadStripe(STRIPE_PUBLIC_KEY) @@ -36,7 +39,6 @@ export interface PaymentMethodSelectionProps { selectedPaymentMethod?: string onSelectPaymentMethod: (id: string) => void layout?: 'vertical' | 'horizontal' - createPaymentMethodInline: boolean readOnly: boolean } @@ -45,27 +47,48 @@ const PaymentMethodSelection = forwardRef(function PaymentMethodSelection( selectedPaymentMethod, onSelectPaymentMethod, layout = 'vertical', - createPaymentMethodInline = false, readOnly, }: PaymentMethodSelectionProps, ref ) { - const selectedOrganization = useSelectedOrganization() - const slug = selectedOrganization?.slug - const [showAddNewPaymentMethodModal, setShowAddNewPaymentMethodModal] = useState(false) - const captchaLoaded = useIsHCaptchaLoaded() + const { slug } = useParams() + const { data: selectedOrganization } = useSelectedOrganizationQuery() const [captchaToken, setCaptchaToken] = useState(null) const [captchaRef, setCaptchaRef] = useState(null) const [setupIntent, setSetupIntent] = useState(undefined) + const [useAsDefaultBillingAddress, setUseAsDefaultBillingAddress] = useState(true) const { resolvedTheme } = useTheme() - const paymentRef = useRef<{ createPaymentMethod: () => Promise }>(null) + const paymentRef = useRef(null) const [setupNewPaymentMethod, setSetupNewPaymentMethod] = useState(null) + const { data: customerProfile, isLoading: isCustomerProfileLoading } = + useOrganizationCustomerProfileQuery({ + slug, + }) + const { data: taxId, isLoading: isCustomerTaxIdLoading } = useOrganizationTaxIdQuery({ slug }) - const { - data: paymentMethods, - isLoading, - refetch: refetchPaymentMethods, - } = useOrganizationPaymentMethodsQuery({ slug }) + const hidePaymentMethodsWithoutAddress = useFlag('hidePaymentMethodsWithoutAddress') + + const { data: allPaymentMethods, isLoading } = useOrganizationPaymentMethodsQuery({ slug }) + + const paymentMethods = useMemo(() => { + if (!allPaymentMethods) + return { + data: [], + defaultPaymentMethodId: null, + } + + const filtered = allPaymentMethods.data.filter( + (pm) => !hidePaymentMethodsWithoutAddress || pm.has_address + ) + return { + data: filtered, + defaultPaymentMethodId: allPaymentMethods.data.some( + (pm) => pm.id === allPaymentMethods.defaultPaymentMethodId + ) + ? allPaymentMethods.defaultPaymentMethodId + : null, + } + }, [allPaymentMethods]) const captchaRefCallback = useCallback((node: any) => { setCaptchaRef(node) @@ -98,7 +121,7 @@ const PaymentMethodSelection = forwardRef(function PaymentMethodSelection( } const loadPaymentForm = async () => { - if (setupNewPaymentMethod && createPaymentMethodInline && captchaRef && captchaLoaded) { + if (setupNewPaymentMethod && captchaRef) { let token = captchaToken try { @@ -116,18 +139,13 @@ const PaymentMethodSelection = forwardRef(function PaymentMethodSelection( } loadPaymentForm() - }, [createPaymentMethodInline, captchaRef, captchaLoaded, setupNewPaymentMethod]) + }, [captchaRef, setupNewPaymentMethod]) const resetCaptcha = () => { setCaptchaToken(null) captchaRef?.resetCaptcha() } - const canUpdatePaymentMethods = useCheckPermissions( - PermissionAction.BILLING_WRITE, - 'stripe.payment_methods' - ) - const stripeOptionsPaymentMethod: StripeElementsOptions = useMemo( () => ({ @@ -156,11 +174,27 @@ const PaymentMethodSelection = forwardRef(function PaymentMethodSelection( }, [selectedPaymentMethod, paymentMethods, onSelectPaymentMethod]) // If createPaymentMethod already exists, use it. Otherwise, define it here. - const createPaymentMethod = async () => { + const createPaymentMethod = async (): ReturnType< + PaymentMethodElementRef['createPaymentMethod'] + > => { if (setupNewPaymentMethod || (paymentMethods?.data && paymentMethods.data.length === 0)) { - return paymentRef.current?.createPaymentMethod() + const paymentResult = await paymentRef.current?.createPaymentMethod() + + if (!paymentResult) return paymentResult + + return { + paymentMethod: paymentResult.paymentMethod, + customerName: useAsDefaultBillingAddress ? paymentResult.customerName : null, + address: useAsDefaultBillingAddress ? paymentResult.address : null, + taxId: useAsDefaultBillingAddress ? paymentResult.taxId : null, + } } else { - return { id: selectedPaymentMethod } + return { + paymentMethod: { id: selectedPaymentMethod } as PaymentMethod, + customerName: useAsDefaultBillingAddress ? customerProfile?.billing_name || '' : null, + address: useAsDefaultBillingAddress ? customerProfile?.address ?? null : null, + taxId: useAsDefaultBillingAddress ? taxId ?? null : null, + } } } @@ -197,42 +231,6 @@ const PaymentMethodSelection = forwardRef(function PaymentMethodSelection(

Retrieving payment methods

- ) : paymentMethods?.data.length === 0 && !createPaymentMethodInline ? ( -
-
- -

No payment methods

-
- - } - onClick={() => { - if (createPaymentMethodInline) { - setSetupNewPaymentMethod(true) - } else { - setShowAddNewPaymentMethodModal(true) - } - }} - htmlType="button" - tooltip={{ - content: { - side: 'bottom', - text: !canUpdatePaymentMethods ? ( -
- - You need additional permissions to add new payment methods to this - organization - -
- ) : undefined, - }, - }} - > - Add new -
-
) : paymentMethods?.data && paymentMethods?.data.length > 0 && !setupNewPaymentMethod ? ( { - if (createPaymentMethodInline) { - setSetupNewPaymentMethod(true) - } else { - setShowAddNewPaymentMethodModal(true) - } + setSetupNewPaymentMethod(true) }} > @@ -282,17 +276,39 @@ const PaymentMethodSelection = forwardRef(function PaymentMethodSelection( ) : null} - {stripePromise && setupIntent && ( - - - + {stripePromise && setupIntent && customerProfile && ( + <> + + + + + {/* If the customer already has a billing address, optionally allow overwriting it - if they have no address, we use that as a default */} + {customerProfile?.address != null && ( +
+ setUseAsDefaultBillingAddress(!useAsDefaultBillingAddress)} + /> + +
+ )} + )} - {setupIntentLoading && ( + {(setupIntentLoading || isCustomerProfileLoading || isCustomerTaxIdLoading) && (
@@ -303,26 +319,6 @@ const PaymentMethodSelection = forwardRef(function PaymentMethodSelection(
)}
- - setShowAddNewPaymentMethodModal(false)} - autoMarkAsDefaultPaymentMethod={true} - onConfirm={async () => { - setShowAddNewPaymentMethodModal(false) - toast.success('Successfully added new payment method') - const { data: refetchedPaymentMethods } = await refetchPaymentMethods() - if (refetchedPaymentMethods?.data?.length) { - // Preselect the card that was just added - const mostRecentPaymentMethod = refetchedPaymentMethods?.data.reduce( - (prev, current) => (prev.created > current.created ? prev : current), - refetchedPaymentMethods.data[0] - ) - onSelectPaymentMethod(mostRecentPaymentMethod.id) - } - }} - /> ) }) diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PlanUpdateSidePanel.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PlanUpdateSidePanel.tsx index a91fa6368eb..e8c1082ff4e 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PlanUpdateSidePanel.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/PlanUpdateSidePanel.tsx @@ -18,7 +18,10 @@ import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-que import type { OrgPlan } from 'data/subscriptions/types' import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' -import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' +import { + useSelectedOrganization, + useSelectedOrganizationQuery, +} from 'hooks/misc/useSelectedOrganization' import { formatCurrency } from 'lib/helpers' import { pickFeatures, pickFooter, plans as subscriptionsPlans } from 'shared-data/plans' import { useOrgSettingsPageStateSnapshot } from 'state/organization-settings' @@ -26,14 +29,15 @@ import { Button, SidePanel, cn } from 'ui' import DowngradeModal from './DowngradeModal' import { EnterpriseCard } from './EnterpriseCard' import { ExitSurveyModal } from './ExitSurveyModal' +import { useParams } from 'common' import MembersExceedLimitModal from './MembersExceedLimitModal' import { SubscriptionPlanUpdateDialog } from './SubscriptionPlanUpdateDialog' import UpgradeSurveyModal from './UpgradeModal' const PlanUpdateSidePanel = () => { const router = useRouter() - const selectedOrganization = useSelectedOrganization() - const slug = selectedOrganization?.slug + const { slug } = useParams() + const { data: selectedOrganization } = useSelectedOrganizationQuery() const { mutate: sendEvent } = useSendEventMutation() const originalPlanRef = useRef() diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx index 4b180b48862..88892c72e4b 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx @@ -13,22 +13,23 @@ import { OrganizationBillingSubscriptionPreviewResponse } from 'data/organizatio import { ProjectInfo } from 'data/projects/projects-query' import { useOrgSubscriptionUpdateMutation } from 'data/subscriptions/org-subscription-update-mutation' import { SubscriptionTier } from 'data/subscriptions/types' -import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { PRICING_TIER_PRODUCT_IDS, PROJECT_STATUS, STRIPE_PUBLIC_KEY } from 'lib/constants' import { formatCurrency } from 'lib/helpers' -import { Badge, Button, Dialog, DialogContent, Table, TableBody, TableCell, TableRow } from 'ui' +import { Button, Dialog, DialogContent, Table, TableBody, TableCell, TableRow } from 'ui' import { Admonition } from 'ui-patterns' import { InfoTooltip } from 'ui-patterns/info-tooltip' -import { BillingCustomerDataExistingOrgDialog } from '../BillingCustomerData/BillingCustomerDataExistingOrgDialog' import PaymentMethodSelection from './PaymentMethodSelection' import { useConfirmPendingSubscriptionChangeMutation } from 'data/subscriptions/org-subscription-confirm-pending-change' import { PaymentConfirmation } from 'components/interfaces/Billing/Payment/PaymentConfirmation' import { Elements } from '@stripe/react-stripe-js' -import { loadStripe, PaymentMethod, StripeElementsOptions } from '@stripe/stripe-js' +import { loadStripe, StripeElementsOptions } from '@stripe/stripe-js' import { useTheme } from 'next-themes' import { PaymentIntentResult } from '@stripe/stripe-js' import { getStripeElementsAppearanceOptions } from 'components/interfaces/Billing/Payment/Payment.utils' import { plans as subscriptionsPlans } from 'shared-data/plans' +import type { PaymentMethodElementRef } from '../PaymentMethods/NewPaymentMethodElement' +import { useParams } from 'common' +import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' const stripePromise = loadStripe(STRIPE_PUBLIC_KEY) @@ -76,12 +77,12 @@ export const SubscriptionPlanUpdateDialog = ({ projects, }: Props) => { const { resolvedTheme } = useTheme() - const selectedOrganization = useSelectedOrganization() + const { data: selectedOrganization } = useSelectedOrganizationQuery() const [selectedPaymentMethod, setSelectedPaymentMethod] = useState() const [paymentIntentSecret, setPaymentIntentSecret] = useState(null) const [paymentConfirmationLoading, setPaymentConfirmationLoading] = useState(false) - const paymentMethodSelection = useRef<{ - createPaymentMethod: () => Promise + const paymentMethodSelectionRef = useRef<{ + createPaymentMethod: PaymentMethodElementRef['createPaymentMethod'] }>(null) const billingViaPartner = subscription?.billing_via_partner === true @@ -161,18 +162,14 @@ export const SubscriptionPlanUpdateDialog = ({ setPaymentConfirmationLoading(true) - const paymentMethod = await paymentMethodSelection.current?.createPaymentMethod() - if (paymentMethod) { - setSelectedPaymentMethod(paymentMethod.id) + const result = await paymentMethodSelectionRef.current?.createPaymentMethod() + if (result) { + setSelectedPaymentMethod(result.paymentMethod.id) } else { setPaymentConfirmationLoading(false) } - if ( - !paymentMethod && - subscription?.payment_method_type !== 'invoice' && - changeType === 'upgrade' - ) { + if (!result && subscription?.payment_method_type !== 'invoice' && changeType === 'upgrade') { return } @@ -185,7 +182,10 @@ export const SubscriptionPlanUpdateDialog = ({ updateOrgSubscription({ slug: selectedOrganization?.slug, tier, - paymentMethod: paymentMethod?.id, + paymentMethod: result?.paymentMethod?.id, + address: result?.address, + tax_id: result?.taxId ?? undefined, + billing_name: result?.customerName ?? undefined, }) } @@ -246,13 +246,40 @@ export const SubscriptionPlanUpdateDialog = ({ {/* Left Column */}
-

- {changeType === 'downgrade' ? 'Downgrade' : 'Upgrade'}{' '} - {selectedOrganization?.name} to{' '} - {changeType === 'downgrade' - ? DOWNGRADE_PLAN_HEADINGS[(selectedTier as DowngradePlanHeadingKey) || 'default'] - : PLAN_HEADINGS[(selectedTier as PlanHeadingKey) || 'default']} -

+
+ {!billingViaPartner && subscriptionPreview != null && changeType === 'upgrade' && ( +
+ setSelectedPaymentMethod(pm)} + readOnly={paymentConfirmationLoading || isConfirming || isUpdating} + /> +
+ )} + + {billingViaPartner && ( +
+

+ This organization is billed through our partner{' '} + {billingPartnerLabel(billingPartner)}.{' '} + {billingPartner === 'aws' ? ( + <>The organization's credit balance will be decreased accordingly. + ) : ( + <>You will be charged by them directly. + )} +

+ {billingViaPartner && + billingPartner === 'fly' && + subscriptionPreview?.plan_change_type === 'downgrade' && ( +

+ Your organization will be downgraded at the end of your current billing + cycle. +

+ )} +
+ )} +
{subscriptionPreviewIsLoading && (
@@ -264,48 +291,6 @@ export const SubscriptionPlanUpdateDialog = ({ {subscriptionPreviewInitialized && ( <>
-
-
- {subscriptionPlanMeta?.name} Plan - - New - -
-
- {formatCurrency(newPlanCost)} -
-
- {subscription?.plan?.id !== 'free' && ( -
-
- Unused Time on {subscription?.plan?.name} Plan - - Your previous plan was charged upfront, so a plan change will prorate - any unused time in credits. If the prorated credits exceed the new plan - charge, the excessive credits are added to your organization for future - use. - -
-
- -{formatCurrency(proratedCredit)} -
-
- )} - - {/* Ignore rare case with negative balance (debt) */} - {customerBalance > 0 && ( -
-
- Credits - - Credits will be used first before charging your card. - -
-
- {formatCurrency(customerBalance)} -
-
- )}
Charge today
@@ -324,6 +309,39 @@ export const SubscriptionPlanUpdateDialog = ({ )}
+ + {subscription?.plan?.id !== 'free' && ( +
+
+ Unused Time on {subscription?.plan?.name} Plan + + Your previous plan was charged upfront, so a plan change will prorate + any unused time in credits. If the prorated credits exceed the new plan + charge, the excessive credits are added to your organization for future + use. + +
+
+ -{formatCurrency(proratedCredit)} +
+
+ )} + + {/* Ignore rare case with negative balance (debt) */} + {customerBalance > 0 && ( +
+
+ Credits + + Credits will be used first before charging your card. + +
+
+ {formatCurrency(customerBalance)} +
+
+ )} +
Monthly invoice estimate @@ -542,47 +560,11 @@ export const SubscriptionPlanUpdateDialog = ({
- {!billingViaPartner && subscriptionPreview != null && changeType === 'upgrade' && ( -
- - - setSelectedPaymentMethod(pm)} - createPaymentMethodInline={true} - readOnly={paymentConfirmationLoading || isConfirming || isUpdating} - /> -
- )} - - {billingViaPartner && ( -
-

- This organization is billed through our partner{' '} - {billingPartnerLabel(billingPartner)}.{' '} - {billingPartner === 'aws' ? ( - <>The organization's credit balance will be decreased accordingly. - ) : ( - <>You will be charged by them directly. - )} -

- {billingViaPartner && - billingPartner === 'fly' && - subscriptionPreview?.plan_change_type === 'downgrade' && ( -

- Your organization will be downgraded at the end of your current billing - cycle. -

- )} -
- )} - {projects.filter( (it) => it.status === PROJECT_STATUS.ACTIVE_HEALTHY || it.status === PROJECT_STATUS.COMING_UP - ).length === 0 && + ).length === 5 && subscriptionPreview?.plan_change_type !== 'downgrade' && (
@@ -618,16 +600,13 @@ export const SubscriptionPlanUpdateDialog = ({ )}
- @@ -637,6 +616,13 @@ export const SubscriptionPlanUpdateDialog = ({ {/* Right Column */}
+

+ {changeType === 'downgrade' ? 'Downgrade' : 'Upgrade'}{' '} + {selectedOrganization?.name} to{' '} + {changeType === 'downgrade' + ? DOWNGRADE_PLAN_HEADINGS[(selectedTier as DowngradePlanHeadingKey) || 'default'] + : PLAN_HEADINGS[(selectedTier as PlanHeadingKey) || 'default']} +

{changeType === 'downgrade' ? featuresToLose.length > 0 && (
diff --git a/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx b/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx index 2001d661385..e70d9a7ab87 100644 --- a/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx +++ b/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx @@ -11,20 +11,20 @@ import PartnerManagedResource from 'components/ui/PartnerManagedResource' import { getInvoice } from 'data/invoices/invoice-query' import { useInvoicesCountQuery } from 'data/invoices/invoices-count-query' import { useInvoicesQuery } from 'data/invoices/invoices-query' -import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { formatCurrency } from 'lib/helpers' import { ChevronLeft, ChevronRight, Download, FileText } from 'lucide-react' import { Button } from 'ui' import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' import InvoicePayButton from './InvoicePayButton' +import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' const PAGE_LIMIT = 5 const InvoicesSettings = () => { const [page, setPage] = useState(1) - const selectedOrganization = useSelectedOrganization() - const { slug } = selectedOrganization ?? {} + const { data: selectedOrganization } = useSelectedOrganizationQuery() + const slug = selectedOrganization?.slug const offset = (page - 1) * PAGE_LIMIT const { data: count, isError: isErrorCount } = useInvoicesCountQuery( @@ -33,7 +33,7 @@ const InvoicesSettings = () => { }, { enabled: selectedOrganization?.managed_by === 'supabase' } ) - const { data, error, isLoading, isError, isSuccess } = useInvoicesQuery( + const { data, error, isLoading, isError } = useInvoicesQuery( { slug, offset, diff --git a/apps/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx b/apps/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx index 9721137b0bb..8128ed29eb4 100644 --- a/apps/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx +++ b/apps/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx @@ -32,8 +32,6 @@ import { TooltipTrigger, } from 'ui' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' -import { BillingCustomerDataNewOrgDialog } from '../BillingSettings/BillingCustomerData/BillingCustomerDataNewOrgDialog' -import { FormCustomerData } from '../BillingSettings/BillingCustomerData/useBillingCustomerDataForm' import { useConfirmPendingSubscriptionCreateMutation } from 'data/subscriptions/org-subscription-confirm-pending-create' import { loadStripe } from '@stripe/stripe-js' import { useTheme } from 'next-themes' @@ -41,7 +39,12 @@ import { SetupIntentResponse } from 'data/stripe/setup-intent-mutation' import { useProfile } from 'lib/profile' import { PaymentConfirmation } from 'components/interfaces/Billing/Payment/PaymentConfirmation' import { getStripeElementsAppearanceOptions } from 'components/interfaces/Billing/Payment/Payment.utils' -import { NewPaymentMethodElement } from '../BillingSettings/PaymentMethods/NewPaymentMethodElement' +import { + NewPaymentMethodElement, + type PaymentMethodElementRef, +} from '../BillingSettings/PaymentMethods/NewPaymentMethodElement' +import { components } from 'api-types' +import type { CustomerAddress, CustomerTaxId } from 'data/organizations/types' const ORG_KIND_TYPES = { PERSONAL: 'Personal', @@ -90,6 +93,8 @@ type FormState = z.infer const stripePromise = loadStripe(STRIPE_PUBLIC_KEY) +const newMandatoryAddressInput = true + /** * No org selected yet, create a new one * [Joshen] Need to refactor to use Form_Shadcn here @@ -115,8 +120,6 @@ const NewOrgForm = ({ onPaymentMethodReset, setupIntent, onPlanSelected }: NewOr const [isOrgCreationConfirmationModalVisible, setIsOrgCreationConfirmationModalVisible] = useState(false) - const [customerData, setCustomerData] = useState(null) - const stripeOptionsPaymentMethod: StripeElementsOptions = useMemo( () => ({ @@ -244,7 +247,14 @@ const NewOrgForm = ({ onPaymentMethodReset, setupIntent, onPlanSelected }: NewOr } as StripeElementsOptions }, [paymentIntentSecret, resolvedTheme]) - async function createOrg(paymentMethodId?: string) { + async function createOrg( + paymentMethodId?: string, + customerData?: { + address: CustomerAddress | null + billing_name: string | null + tax_id: CustomerTaxId | null + } + ) { const dbTier = formState.plan === 'PRO' && !formState.spend_cap ? 'PAYG' : formState.plan createOrganization({ @@ -258,12 +268,12 @@ const NewOrgForm = ({ onPaymentMethodReset, setupIntent, onPlanSelected }: NewOr ...(formState.kind == 'COMPANY' ? { size: formState.size } : {}), payment_method: paymentMethodId, billing_name: dbTier === 'FREE' ? undefined : customerData?.billing_name, - address: dbTier === 'FREE' ? undefined : customerData?.address, + address: dbTier === 'FREE' ? null : customerData?.address, tax_id: dbTier === 'FREE' ? undefined : customerData?.tax_id ?? undefined, }) } - const paymentRef = useRef<{ createPaymentMethod: () => Promise }>(null) + const paymentRef = useRef(null) const handleSubmit = async () => { setNewOrgLoading(true) @@ -271,10 +281,16 @@ const NewOrgForm = ({ onPaymentMethodReset, setupIntent, onPlanSelected }: NewOr if (formState.plan === 'FREE') { await createOrg() } else if (!paymentMethod) { - const paymentMethod = await paymentRef.current?.createPaymentMethod() - if (paymentMethod) { - setPaymentMethod(paymentMethod) - createOrg(paymentMethod.id) + const result = await paymentRef.current?.createPaymentMethod() + if (result) { + setPaymentMethod(result.paymentMethod) + const customerData = { + address: result.address, + billing_name: result.customerName, + tax_id: result.taxId, + } + + createOrg(result.paymentMethod.id, customerData) } else { setNewOrgLoading(false) } @@ -340,6 +356,7 @@ const NewOrgForm = ({ onPaymentMethodReset, setupIntent, onPlanSelected }: NewOr
} + className="overflow-visible" >

This is your organization within Supabase.

@@ -532,21 +549,6 @@ const NewOrgForm = ({ onPaymentMethodReset, setupIntent, onPlanSelected }: NewOr )} - {formState.plan !== 'FREE' && ( - -
-
- - Billing Address - -
-
- -
-
-
- )} - {setupIntent && formState.plan !== 'FREE' && ( diff --git a/apps/studio/components/interfaces/Organization/OrgNotFound.tsx b/apps/studio/components/interfaces/Organization/OrgNotFound.tsx index 673c8e116c0..2da3c4f293d 100644 --- a/apps/studio/components/interfaces/Organization/OrgNotFound.tsx +++ b/apps/studio/components/interfaces/Organization/OrgNotFound.tsx @@ -15,17 +15,19 @@ export const OrgNotFound = ({ slug }: { slug?: string }) => { return ( <> - - The selected organization does not exist or you don't have permission to access it.{' '} - {slug ? ( - <> - Contact the owner or administrator to create a new project in the {slug}{' '} - organization. - - ) : ( - <>Contact the owner or administrator to create a new project. - )} - + {slug !== '_' && ( + + The selected organization does not exist or you don't have permission to access it.{' '} + {slug ? ( + <> + Contact the owner or administrator to create a new project in the {slug}{' '} + organization. + + ) : ( + <>Contact the owner or administrator to create a new project. + )} + + )}

Select an organization to create your new project from

diff --git a/apps/studio/components/interfaces/Organization/TeamSettings/MemberRow.tsx b/apps/studio/components/interfaces/Organization/TeamSettings/MemberRow.tsx index 4eccdd17f71..068c517764d 100644 --- a/apps/studio/components/interfaces/Organization/TeamSettings/MemberRow.tsx +++ b/apps/studio/components/interfaces/Organization/TeamSettings/MemberRow.tsx @@ -88,7 +88,7 @@ export const MemberRow = ({ member }: MemberRowProps) => { />

{member.primary_email}

- {member.primary_email === profile?.primary_email && You} + {member.gotrue_id === profile?.gotrue_id && You}
{(member.metadata as any)?.origin && ( diff --git a/apps/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx b/apps/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx index 2de693bc844..3ecc3b3de90 100644 --- a/apps/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx +++ b/apps/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx @@ -54,13 +54,13 @@ const MembersView = ({ searchString }: MembersViewProps) => { const [[user], otherMembers] = partition( filteredMembers, - (m) => m.primary_email === profile?.primary_email + (m) => m.gotrue_id === profile?.gotrue_id ) const sortedMembers = otherMembers.sort((a, b) => (a.primary_email ?? '').localeCompare(b.primary_email ?? '') ) - const userMember = members.find((m) => m.primary_email === profile?.primary_email) + const userMember = members.find((m) => m.gotrue_id === profile?.gotrue_id) const orgScopedRoleIds = (roles?.org_scoped_roles ?? []).map((r) => r.id) const isOrgScopedRole = orgScopedRoleIds.includes(userMember?.role_ids?.[0] ?? -1) diff --git a/apps/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx b/apps/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx index 711deddee3e..7df15972893 100644 --- a/apps/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx +++ b/apps/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx @@ -9,6 +9,7 @@ import { ScaffoldSectionContent, ScaffoldTitle, } from 'components/layouts/Scaffold' +import { DocsButton } from 'components/ui/DocsButton' import { Input } from 'ui-patterns/DataInputs/Input' import { InviteMemberButton } from './InviteMemberButton' import MembersView from './MembersView' @@ -32,6 +33,7 @@ export const TeamSettings = () => { placeholder="Filter members" /> + diff --git a/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesConfirmationModal.tsx b/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesConfirmationModal.tsx index fc37dd4fe91..246dfd391d0 100644 --- a/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesConfirmationModal.tsx +++ b/apps/studio/components/interfaces/Organization/TeamSettings/UpdateRolesPanel/UpdateRolesConfirmationModal.tsx @@ -90,10 +90,11 @@ export const UpdateRolesConfirmationModal = ({ }) toast.success(`Successfully updated role for ${member.username}`) onClose(true) - return } catch (error: any) { + toast.error(`Failed to update role: ${error.message}`) + } finally { setSaving(false) - return toast.error(`Failed to update role: ${error.message}`) + return } } @@ -130,8 +131,10 @@ export const UpdateRolesConfirmationModal = ({ toast.success(`Successfully updated role for ${member.username}`) onClose(true) } catch (error: any) { + toast.error(`Failed to update role: ${error.message}`) + } finally { setSaving(false) - return toast.error(`Failed to update role: ${error.message}`) + return } } diff --git a/apps/studio/components/interfaces/Organization/Usage/Usage.tsx b/apps/studio/components/interfaces/Organization/Usage/Usage.tsx index 5820c6e5ebe..4ec02427c22 100644 --- a/apps/studio/components/interfaces/Organization/Usage/Usage.tsx +++ b/apps/studio/components/interfaces/Organization/Usage/Usage.tsx @@ -195,8 +195,8 @@ const Usage = () => { title="Usage filtered by project" description={
- You are currently viewing usage for the " - {selectedProject?.name || selectedProjectRef}" project. Supabase uses{' '} + You are currently viewing usage for the + {selectedProject?.name || selectedProjectRef} project. Supabase uses{' '} ) : chartData.length > 0 && notAllValuesZero ? ( { const org = useSelectedOrganization() + const { plan: orgPlan } = useCurrentOrgPlan() + const orgPlanId = orgPlan?.id + const [isHoveringUpgrade, setIsHoveringUpgrade] = useState(false) const isAvailable = chart.availableIn === undefined || (orgPlanId && chart.availableIn.includes(orgPlanId)) + const canFetch = orgPlanId !== undefined const { data, @@ -114,7 +117,9 @@ const ReportChart = ({

{chart.label}

This chart is available from{' '} - {!!availableIn?.length ? availableIn[0] : 'Pro'}{' '} + + {!!chart.availableIn?.length ? chart.availableIn[0] : 'Pro'} + {' '} plan and above

diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx index 520c479d647..7f7f3d8a2a2 100644 --- a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx +++ b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureActivity.tsx @@ -416,7 +416,7 @@ const InfrastructureActivity = () => {
) : chartData.length ? ( { const currentPgVersion = (current_app_version ?? '') .split('supabase-postgres-')[1] ?.replace('-orioledb', '') - const isOnNonGenerallyAvailableReleaseChannel = - current_app_version_release_channel && current_app_version_release_channel !== 'ga' + const isVisibleReleaseChannel = + current_app_version_release_channel && + !['ga', 'withdrawn'].includes(current_app_version_release_channel) ? current_app_version_release_channel : undefined const isOrioleDb = useIsOrioleDb() @@ -144,16 +145,16 @@ const InfrastructureInfo = () => { value={currentPgVersion || serviceVersions?.['supabase-postgres'] || ''} label="Postgres version" actions={[ - isOnNonGenerallyAvailableReleaseChannel && ( + isVisibleReleaseChannel && ( - {isOnNonGenerallyAvailableReleaseChannel} + {isVisibleReleaseChannel} - This project uses a {isOnNonGenerallyAvailableReleaseChannel}{' '} - database version release + This project uses a {isVisibleReleaseChannel} database version + release ), diff --git a/apps/studio/components/interfaces/Settings/Logs/LogSelection.tsx b/apps/studio/components/interfaces/Settings/Logs/LogSelection.tsx index ffa1e699e22..0f42bf05878 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogSelection.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogSelection.tsx @@ -22,7 +22,6 @@ export interface LogSelectionProps { onClose: () => void queryType?: QueryType projectRef: string - collectionName?: string isLoading: boolean error?: string | object } @@ -41,8 +40,6 @@ const LogSelection = ({ log, onClose, queryType, isLoading, error }: LogSelectio if (!log) return switch (queryType) { - // case 'warehouse': - // return case 'api': const status = log?.metadata?.[0]?.response?.[0]?.status_code const method = log?.metadata?.[0]?.request?.[0]?.method diff --git a/apps/studio/components/interfaces/Settings/Logs/LogSelectionRenderers/WarehouseSelectionRenderer.tsx b/apps/studio/components/interfaces/Settings/Logs/LogSelectionRenderers/WarehouseSelectionRenderer.tsx deleted file mode 100644 index 43047d1882e..00000000000 --- a/apps/studio/components/interfaces/Settings/Logs/LogSelectionRenderers/WarehouseSelectionRenderer.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { cn } from 'ui' -import { LOGS_TAILWIND_CLASSES } from '../Logs.constants' -import { jsonSyntaxHighlight, SelectionDetailedRow } from '../LogsFormatters' - -const DetailedJsonRow = ({ - label, - value, - code, -}: { - label: string - value: Object - code?: boolean -}) => { - return ( -
- {label} - -
-      
-    
- ) -} - -export const WarehouseSelectionRenderer = ({ log }: any) => { - return ( -
- {Object.entries(log).map(([key, value], index) => { - return ( -
- {value && typeof value === 'object' ? ( - - ) : ( - - )} -
- ) - })} -
- ) -} diff --git a/apps/studio/components/interfaces/Settings/Logs/LogTable.tsx b/apps/studio/components/interfaces/Settings/Logs/LogTable.tsx index 7e043920a4b..bb5e4015af7 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogTable.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogTable.tsx @@ -49,8 +49,6 @@ interface Props { onSave?: () => void hasEditorValue?: boolean className?: string - collectionName?: string // Used for warehouse queries - warehouseError?: string EmptyState?: ReactNode showHeader?: boolean showHistogramToggle?: boolean @@ -79,7 +77,6 @@ const LogTable = ({ onSave, hasEditorValue, className, - collectionName, EmptyState, showHeader = true, showHistogramToggle = true, @@ -154,9 +151,6 @@ const LogTable = ({ columns } else { switch (queryType) { - case 'warehouse': - columns = DEFAULT_COLUMNS - break case 'api': columns = DatabaseApiColumnRender break @@ -460,7 +454,6 @@ const LogTable = ({ log={selectedLog} error={selectedLogError} queryType={queryType} - collectionName={collectionName} /> )} diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.constants.ts b/apps/studio/components/interfaces/Settings/Logs/Logs.constants.ts index c84fe325cc8..9044fd26bd9 100644 --- a/apps/studio/components/interfaces/Settings/Logs/Logs.constants.ts +++ b/apps/studio/components/interfaces/Settings/Logs/Logs.constants.ts @@ -369,7 +369,6 @@ export enum LogsTableName { POSTGREST = 'postgrest_logs', SUPAVISOR = 'supavisor_logs', PGBOUNCER = 'pgbouncer_logs', - WAREHOUSE = 'warehouse_logs', PG_UPGRADE = 'pg_upgrade_logs', PG_CRON = 'pg_cron_logs', } @@ -384,7 +383,6 @@ export const LOGS_TABLES = { storage: LogsTableName.STORAGE, postgrest: LogsTableName.POSTGREST, supavisor: LogsTableName.SUPAVISOR, - warehouse: LogsTableName.WAREHOUSE, pg_upgrade: LogsTableName.PG_UPGRADE, pg_cron: LogsTableName.POSTGRES, pgbouncer: LogsTableName.PGBOUNCER, @@ -401,7 +399,6 @@ export const LOGS_SOURCE_DESCRIPTION = { [LogsTableName.POSTGREST]: 'RESTful API web server logs', [LogsTableName.SUPAVISOR]: 'Shared connection pooler logs for PostgreSQL', [LogsTableName.PGBOUNCER]: 'Dedicated connection pooler for PostgreSQL', - [LogsTableName.WAREHOUSE]: 'Logs obtained from a data warehouse collection', [LogsTableName.PG_UPGRADE]: 'Logs generated by the Postgres version upgrade process', [LogsTableName.PG_CRON]: 'Postgres logs from pg_cron cron jobs', } diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.types.ts b/apps/studio/components/interfaces/Settings/Logs/Logs.types.ts index d1f606a72ff..377f542ebb9 100644 --- a/apps/studio/components/interfaces/Settings/Logs/Logs.types.ts +++ b/apps/studio/components/interfaces/Settings/Logs/Logs.types.ts @@ -90,7 +90,6 @@ export type QueryType = | 'storage' | 'supavisor' | 'postgrest' - | 'warehouse' | 'pg_upgrade' | 'pg_cron' | 'pgbouncer' @@ -145,9 +144,3 @@ export interface DatetimeHelper { disabled?: boolean availableIn?: PlanId[] } - -export interface WarehouseCollection { - name: string - id: number - token: string -} diff --git a/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx b/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx index 5ea8b4292a3..f67aef39e04 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogsQueryPanel.tsx @@ -6,7 +6,6 @@ import { ReactNode, useState } from 'react' import { IS_PLATFORM } from 'common' import Table from 'components/to-be-cleaned/Table' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' -import { useFlag } from 'hooks/ui/useFlag' import { logConstants } from 'shared-data' import { Badge, @@ -28,23 +27,16 @@ import { LogsTableName, } from './Logs.constants' import { DatePickerValue, LogsDatePicker } from './Logs.DatePickers' -import { LogsWarning, LogTemplate, WarehouseCollection } from './Logs.types' -import { WarehouseQueryTemplate } from './Warehouse.utils' +import { LogsWarning, LogTemplate } from './Logs.types' -export type SourceType = 'logs' | 'warehouse' export interface LogsQueryPanelProps { templates?: LogTemplate[] - warehouseTemplates?: WarehouseQueryTemplate[] defaultFrom: string defaultTo: string warnings: LogsWarning[] - warehouseCollections: WarehouseCollection[] - dataSource: SourceType onSelectTemplate: (template: LogTemplate) => void - onSelectWarehouseTemplate: (template: WarehouseQueryTemplate) => void onSelectSource: (source: string) => void onDateChange: (value: DatePickerValue) => void - onDataSourceChange: (sourceType: SourceType) => void } function DropdownMenuItemContent({ name, desc }: { name: ReactNode; desc?: string }) { @@ -58,17 +50,12 @@ function DropdownMenuItemContent({ name, desc }: { name: ReactNode; desc?: strin const LogsQueryPanel = ({ templates = [], - warehouseTemplates = [], defaultFrom, defaultTo, warnings, - warehouseCollections, - dataSource, onSelectTemplate, - onSelectWarehouseTemplate, onSelectSource, onDateChange, - onDataSourceChange, }: LogsQueryPanelProps) => { const [showReference, setShowReference] = useState(false) @@ -78,15 +65,12 @@ const LogsQueryPanel = ({ projectEdgeFunctionAll: edgeFunctionsEnabled, } = useIsFeatureEnabled(['project_auth:all', 'project_storage:all', 'project_edge_function:all']) - const warehouseEnabled = useFlag('warehouse') - const logsTableNames = Object.entries(LogsTableName) .filter(([key]) => { if (key === 'AUTH') return authEnabled if (key === 'STORAGE') return storageEnabled if (key === 'FN_EDGE') return edgeFunctionsEnabled if (key === 'PG_CRON') return false - if (key === 'WAREHOUSE') return false return true }) .map(([, value]) => value) @@ -117,90 +101,31 @@ const LogsQueryPanel = ({
- {warehouseEnabled && ( - - - - - - onDataSourceChange('logs')}> - - - onDataSourceChange('warehouse')}> - - Collections NEW - - } - desc="Query your collections" - /> - - - - )} - - {dataSource === 'warehouse' && ( - - - - - - {warehouseTemplates.map((template) => ( - onSelectWarehouseTemplate(template)} - > - - - ))} - {warehouseCollections.length === 0 && ( - + + + + + + {logsTableNames + .sort((a, b) => a.localeCompare(b)) + .map((source) => ( + onSelectSource(source)}> - )} - - - )} + ))} + + - {dataSource === 'logs' && ( - - - - - - {dataSource === 'logs' && - logsTableNames - .sort((a, b) => a.localeCompare(b)) - .map((source) => ( - onSelectSource(source)}> - - - ))} - - - )} - - {dataSource === 'logs' && IS_PLATFORM && ( + {IS_PLATFORM && (
- {dataSource === 'logs' && ( - -

Field Reference

-
- } - visible={showReference} - cancelText="Close" - onCancel={() => setShowReference(false)} - hideFooter - triggerElement={ + + +

Field Reference

- } - > - -
-

- The following table shows all the available paths that can be queried from each - respective source. Do note that to access nested keys, you would need to perform - the necessary{' '} - - unnesting joins - - -

-
-
- - setShowReference(false)} + icon={} + /> +
+ } + visible={showReference} + cancelText="Close" + onCancel={() => setShowReference(false)} + hideFooter + triggerElement={ + + } + > + +
+

+ The following table shows all the available paths that can be queried from each + respective source. Do note that to access nested keys, you would need to perform + the necessary{' '} + -

- Path - , - - Type - , - ]} - body={schema.fields - .sort((a: any, b: any) => a.path - b.path) - .map((field) => ( - - ))} + unnesting joins + - - ))} - - - )} + +

+ + + + + {logConstants.schemas.map((schema) => ( + +
+ Path + , + + Type + , + ]} + body={schema.fields + .sort((a: any, b: any) => a.path - b.path) + .map((field) => ( + + ))} + /> + + ))} + + diff --git a/apps/studio/components/interfaces/Settings/Logs/Warehouse.utils.ts b/apps/studio/components/interfaces/Settings/Logs/Warehouse.utils.ts deleted file mode 100644 index fdb60183e09..00000000000 --- a/apps/studio/components/interfaces/Settings/Logs/Warehouse.utils.ts +++ /dev/null @@ -1,26 +0,0 @@ -export type WarehouseQueryTemplate = { - query: string - name: string - description: string -} - -export function createWarehouseQueryTemplates( - collections: { name: string }[] -): WarehouseQueryTemplate[] { - return collections.flatMap((collection) => [ - { - query: `select count(*) as event_count from \`${collection.name}\` -where timestamp > timestamp_sub(current_timestamp(), interval 1 day)`, - name: `${collection.name} - Daily Event Count`, - description: `Count events in the last 24 hours from ${collection.name} collection`, - }, - { - query: `select id, timestamp, event_message from \`${collection.name}\` - where timestamp > timestamp_sub(current_timestamp(), interval 1 day) - and event_message like '%YOUR_TEXT_HERE%' - order by timestamp desc limit 10`, - name: `${collection.name} - Filter by text`, - description: `Select last 10 events from ${collection.name} collection`, - }, - ]) -} diff --git a/apps/studio/components/interfaces/Sidebar.tsx b/apps/studio/components/interfaces/Sidebar.tsx index e729bc0b732..44bfc0797c0 100644 --- a/apps/studio/components/interfaces/Sidebar.tsx +++ b/apps/studio/components/interfaces/Sidebar.tsx @@ -223,8 +223,6 @@ const ProjectLinks = () => { const isNewAPIDocsEnabled = useIsAPIDocsSidePanelEnabled() const { securityLints, errorLints } = useLints() - const showWarehouse = useFlag('warehouse') - const activeRoute = router.pathname.split('/')[3] const { @@ -310,14 +308,11 @@ const ProjectLinks = () => { ) } 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 { diff --git a/apps/studio/components/interfaces/SignIn/ForgotPasswordWizard.tsx b/apps/studio/components/interfaces/SignIn/ForgotPasswordWizard.tsx index 4aa80ad51d1..27e8df3b0a3 100644 --- a/apps/studio/components/interfaces/SignIn/ForgotPasswordWizard.tsx +++ b/apps/studio/components/interfaces/SignIn/ForgotPasswordWizard.tsx @@ -60,10 +60,13 @@ const ConfirmResetCodeForm = ({ email }: { email: string }) => { if (user?.factors?.length) { await router.push({ pathname: '/forgot-password-mfa', - query: { returnTo: '/reset-password' }, + query: router.query, }) } else { - await router.push('reset-password') + await router.push({ + pathname: '/reset-password', + query: router.query, + }) } } } diff --git a/apps/studio/components/interfaces/SignIn/ResetPasswordForm.tsx b/apps/studio/components/interfaces/SignIn/ResetPasswordForm.tsx index 868b5d96162..48e513fe0e8 100644 --- a/apps/studio/components/interfaces/SignIn/ResetPasswordForm.tsx +++ b/apps/studio/components/interfaces/SignIn/ResetPasswordForm.tsx @@ -7,7 +7,7 @@ import { useForm } from 'react-hook-form' import { toast } from 'sonner' import { z } from 'zod' -import { auth } from 'lib/gotrue' +import { auth, getReturnToPath } from 'lib/gotrue' import { Button, Form_Shadcn_, @@ -67,7 +67,7 @@ const ResetPasswordForm = () => { // logout all other sessions after changing password await auth.signOut({ scope: 'others' }) - await router.push('/organizations') + await router.push(getReturnToPath('/organizations')) } else { toast.error(`Failed to save password: ${error.message}`, { id: toastId }) if (!WHITELIST_ERRORS.some((e) => error.message.includes(e))) { diff --git a/apps/studio/components/interfaces/SignIn/SignInForm.tsx b/apps/studio/components/interfaces/SignIn/SignInForm.tsx index b25214fa34b..9460c7c2ebb 100644 --- a/apps/studio/components/interfaces/SignIn/SignInForm.tsx +++ b/apps/studio/components/interfaces/SignIn/SignInForm.tsx @@ -4,7 +4,7 @@ import type { AuthError } from '@supabase/supabase-js' import { useQueryClient } from '@tanstack/react-query' import Link from 'next/link' import { useRouter } from 'next/router' -import { useRef, useState } from 'react' +import { useRef, useState, useEffect } from 'react' import { toast } from 'sonner' import { object, string } from 'yup' @@ -28,10 +28,22 @@ const SignInForm = () => { const [captchaToken, setCaptchaToken] = useState(null) const captchaRef = useRef(null) + const [returnTo, setReturnTo] = useState(null) + + useEffect(() => { + // Only call getReturnToPath after component mounts client-side + setReturnTo(getReturnToPath()) + }, []) const { mutate: sendEvent } = useSendEventMutation() const { mutate: addLoginEvent } = useAddLoginEvent() + let forgotPasswordUrl = `/forgot-password` + + if (returnTo && !returnTo.includes('/forgot-password')) { + forgotPasswordUrl = `${forgotPasswordUrl}?returnTo=${encodeURIComponent(returnTo)}` + } + const onSignIn = async ({ email, password }: { email: string; password: string }) => { const toastId = toast.loading('Signing in...') @@ -68,9 +80,12 @@ const SignInForm = () => { addLoginEvent({}) await queryClient.resetQueries() - const returnTo = getReturnToPath() // since we're already on the /sign-in page, prevent redirect loops - router.push(returnTo === '/sign-in' ? '/organizations' : returnTo) + let redirectPath = '/organizations' + if (returnTo && returnTo !== '/sign-in') { + redirectPath = returnTo + } + router.push(redirectPath) } catch (error: any) { toast.error(`Failed to sign in: ${(error as AuthError).message}`, { id: toastId }) Sentry.captureMessage('[CRITICAL] Failed to sign in via EP: ' + error.message) @@ -124,7 +139,7 @@ const SignInForm = () => { {/* positioned using absolute instead of labelOptional prop so tabbing between inputs works smoothly */} Forgot Password? diff --git a/apps/studio/components/interfaces/SignIn/SignInMfaForm.tsx b/apps/studio/components/interfaces/SignIn/SignInMfaForm.tsx index 7543a1a4111..f0ec3eb1381 100644 --- a/apps/studio/components/interfaces/SignIn/SignInMfaForm.tsx +++ b/apps/studio/components/interfaces/SignIn/SignInMfaForm.tsx @@ -18,7 +18,11 @@ const signInSchema = object({ code: string().required('MFA Code is required'), }) -const SignInMfaForm = () => { +interface SignInMfaFormProps { + context?: 'forgot-password' | 'sign-in' +} + +const SignInMfaForm = ({ context = 'sign-in' }: SignInMfaFormProps) => { const router = useRouter() const signOut = useSignOut() const queryClient = useQueryClient() @@ -38,7 +42,15 @@ const SignInMfaForm = () => { } = useMfaChallengeAndVerifyMutation({ onSuccess: async () => { await queryClient.resetQueries() - router.push(getReturnToPath()) + + if (context === 'forgot-password') { + router.push({ + pathname: '/reset-password', + query: router.query, + }) + } else { + router.push(getReturnToPath()) + } }, }) diff --git a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/DecryptedReadOnlyInput.tsx b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/DecryptedReadOnlyInput.tsx index e22c8a8e694..fff4f06387f 100644 --- a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/DecryptedReadOnlyInput.tsx +++ b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/DecryptedReadOnlyInput.tsx @@ -1,8 +1,10 @@ +import { ExternalLink, Eye, EyeOff, Loader } from 'lucide-react' +import { useState } from 'react' + +import { useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { useVaultSecretDecryptedValueQuery } from 'data/vault/vault-secret-decrypted-value-query' -import { Eye, EyeOff, Loader } from 'lucide-react' -import { useState } from 'react' -import { Button, Input } from 'ui' +import { Button, Input, Tooltip, TooltipContent, TooltipTrigger } from 'ui' export const DecryptedReadOnlyInput = ({ value, @@ -15,10 +17,11 @@ export const DecryptedReadOnlyInput = ({ descriptionText: string label: string }) => { - const [showHidden, setShowHidden] = useState(false) + const { ref } = useParams() const { project } = useProjectContext() + const [showHidden, setShowHidden] = useState(false) - const { isLoading: isDecryptedValueLoading, data: decryptedValue } = + const { data: decryptedValue, isLoading: isDecryptedValueLoading } = useVaultSecretDecryptedValueQuery( { projectRef: project?.ref, @@ -29,22 +32,40 @@ export const DecryptedReadOnlyInput = ({ ) const isLoading = isDecryptedValueLoading && showHidden + const renderedValue = secureEntry + ? isLoading + ? 'Fetching value from Vault...' + : showHidden + ? decryptedValue + : value + : value return ( + {label} + + + + + + + View parameter in Vault + + } + value={renderedValue} type={secureEntry ? (isLoading ? 'text' : showHidden ? 'text' : 'password') : 'text'} descriptionText={descriptionText} layout="horizontal" diff --git a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/NamespaceRow.tsx b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/NamespaceRow.tsx index 80d75a33da4..1c716007a56 100644 --- a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/NamespaceRow.tsx +++ b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/NamespaceRow.tsx @@ -1,6 +1,7 @@ import { RefreshCw, SquareArrowOutUpRight } from 'lucide-react' import { useMemo, useState } from 'react' +import type { WrapperMeta } from 'components/interfaces/Integrations/Wrappers/Wrappers.types' import { FormattedWrapperTable } from 'components/interfaces/Integrations/Wrappers/Wrappers.utils' import { ImportForeignSchemaDialog } from 'components/interfaces/Storage/ImportForeignSchemaDialog' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' @@ -9,34 +10,32 @@ import { useFDWImportForeignSchemaMutation } from 'data/fdw/fdw-import-foreign-s import { FDW } from 'data/fdw/fdws-query' import { useIcebergNamespaceTablesQuery } from 'data/storage/iceberg-namespace-tables-query' import { BASE_PATH } from 'lib/constants' -import { Badge, Button, TableCell, TableRow } from 'ui' +import { Button, cn, TableCell, TableRow } from 'ui' type NamespaceRowProps = { bucketName: string namespace: string schema: string - excludedSchemas: string[] tables: (FormattedWrapperTable & { id: number })[] token: string wrapperInstance: FDW wrapperValues: Record + wrapperMeta: WrapperMeta } export const NamespaceRow = ({ bucketName, namespace, schema, - excludedSchemas, tables, token, wrapperInstance, wrapperValues, + wrapperMeta, }: NamespaceRowProps) => { const { project } = useProjectContext() const [importForeignSchemaShown, setImportForeignSchemaShown] = useState(false) - const targetSchema = tables[0]?.schema_name ?? '' - const { data: tablesData, isLoading: isLoadingNamespaceTables } = useIcebergNamespaceTablesQuery( { catalogUri: wrapperValues.catalog_uri, @@ -56,7 +55,7 @@ export const NamespaceRow = ({ connectionString: project?.connectionString, serverName: wrapperInstance.server_name, sourceSchema: namespace, - targetSchema: targetSchema, + targetSchema: schema, }) } @@ -76,11 +75,11 @@ export const NamespaceRow = ({ return ( - - {namespace} + {namespace} + + {schema ?? 'No schema'} - {schema && {schema}} - + {tablesData ? `${tables.length}/${tablesData.length} connected tables` : ``} @@ -96,15 +95,15 @@ export const NamespaceRow = ({ Sync {schema ? ( - - - + ) : ( - Table Editor + Open in Table Editor )} @@ -122,8 +121,7 @@ export const NamespaceRow = ({ setImportForeignSchemaShown(false)} /> diff --git a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/index.tsx b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/index.tsx index 49aead6e1f0..25945e40d77 100644 --- a/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/index.tsx +++ b/apps/studio/components/interfaces/Storage/AnalyticBucketDetails/index.tsx @@ -127,7 +127,6 @@ export const AnalyticBucketDetails = ({ bucket }: { bucket: Bucket }) => { } }) }, [wrapperTables, namespacesData]) - const excludedSchemas = uniq(namespaces.map((n) => n.schema)) const wrappersExtension = extensionsData?.find((ext) => ext.name === 'wrappers') @@ -196,10 +195,10 @@ export const AnalyticBucketDetails = ({ bucket }: { bucket: Bucket }) => {
- Namespace - Schema - Tables - Actions + Namespace + Schema + Tables + @@ -209,11 +208,11 @@ export const AnalyticBucketDetails = ({ bucket }: { bucket: Bucket }) => { bucketName={bucket.name} namespace={namespace} schema={schema} - excludedSchemas={excludedSchemas} tables={tables as any} token={token!} wrapperInstance={wrapperInstance} wrapperValues={wrapperValues} + wrapperMeta={wrapperMeta} /> ))} diff --git a/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx b/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx index 1523c488ee8..75911012462 100644 --- a/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx +++ b/apps/studio/components/interfaces/Storage/CreateBucketModal.tsx @@ -11,10 +11,7 @@ import z from 'zod' import { useParams } from 'common' import { useIcebergWrapperExtension } from 'components/interfaces/Storage/AnalyticBucketDetails/useIcebergWrapper' import { StorageSizeUnits } from 'components/interfaces/Storage/StorageSettings/StorageSettings.constants' -import { - convertFromBytes, - convertToBytes, -} from 'components/interfaces/Storage/StorageSettings/StorageSettings.utils' +import { InlineLink } from 'components/ui/InlineLink' import { useProjectStorageConfigQuery } from 'data/config/project-storage-config-query' import { useBucketCreateMutation } from 'data/storage/bucket-create-mutation' import { useIcebergWrapperCreateMutation } from 'data/storage/iceberg-wrapper-create-mutation' @@ -42,6 +39,7 @@ import { } from 'ui' import { Admonition } from 'ui-patterns/admonition' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' +import { convertFromBytes, convertToBytes } from './StorageSettings/StorageSettings.utils' export interface CreateBucketModalProps { visible: boolean @@ -80,7 +78,10 @@ const CreateBucketModal = ({ visible, onClose }: CreateBucketModalProps) => { const { mutate: sendEvent } = useSendEventMutation() const router = useRouter() - const { mutateAsync: createBucket, isLoading: isCreating } = useBucketCreateMutation() + const { mutateAsync: createBucket, isLoading: isCreating } = useBucketCreateMutation({ + // [Joshen] Silencing the error here as it's being handled in onSubmit + onError: () => {}, + }) const { mutateAsync: createIcebergWrapper, isLoading: isCreatingIcebergWrapper } = useIcebergWrapperCreateMutation() @@ -150,9 +151,8 @@ const CreateBucketModal = ({ visible, onClose }: CreateBucketModalProps) => { toast.success(`Successfully created bucket ${values.name}`) router.push(`/project/${ref}/storage/buckets/${values.name}`) onClose() - } catch (error) { - console.error(error) - toast.error('Failed to create bucket') + } catch (error: any) { + toast.error(`Failed to create bucket: ${error.message}`) } } @@ -235,20 +235,16 @@ const CreateBucketModal = ({ visible, onClose }: CreateBucketModalProps) => {

- {icebergCatalogEnabled ? null : ( + {!icebergCatalogEnabled && (
- - This feature is currently in alpha and not yet enabled for your - project. Sign up{' '} - + + This is currently in alpha and not enabled for your project. Sign + up{' '} + here - + + .
)} diff --git a/apps/studio/components/interfaces/Storage/ImportForeignSchemaDialog.tsx b/apps/studio/components/interfaces/Storage/ImportForeignSchemaDialog.tsx index a173dbcfa52..c54d1e3bd45 100644 --- a/apps/studio/components/interfaces/Storage/ImportForeignSchemaDialog.tsx +++ b/apps/studio/components/interfaces/Storage/ImportForeignSchemaDialog.tsx @@ -1,5 +1,5 @@ import { zodResolver } from '@hookform/resolvers/zod' -import { snakeCase } from 'lodash' +import { snakeCase, uniq } from 'lodash' import { useEffect, useState } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' import { toast } from 'sonner' @@ -7,41 +7,30 @@ import z from 'zod' import { useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' -import SchemaSelector from 'components/ui/SchemaSelector' +import { useSchemaCreateMutation } from 'data/database/schema-create-mutation' +import { useSchemasQuery } from 'data/database/schemas-query' import { useFDWImportForeignSchemaMutation } from 'data/fdw/fdw-import-foreign-schema-mutation' -import { - Button, - Form_Shadcn_, - FormControl_Shadcn_, - FormField_Shadcn_, - Input_Shadcn_, - Modal, -} from 'ui' +import { useFDWUpdateMutation } from 'data/fdw/fdw-update-mutation' +import { getFDWs } from 'data/fdw/fdws-query' +import { Button, Form_Shadcn_, FormField_Shadcn_, Input_Shadcn_, Modal } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' +import type { WrapperMeta } from '../Integrations/Wrappers/Wrappers.types' +import { formatWrapperTables } from '../Integrations/Wrappers/Wrappers.utils' import SchemaEditor from '../TableGridEditor/SidePanelEditor/SchemaEditor' +import { getDecryptedParameters } from './ImportForeignSchemaDialog.utils' export interface ImportForeignSchemaDialogProps { bucketName: string namespace: string - excludedSchemas: string[] - wrapperValues: Record + wrapperMeta: WrapperMeta visible: boolean onClose: () => void } -const FormSchema = z.object({ - bucketName: z.string().trim(), - sourceNamespace: z.string().trim(), - targetSchema: z.string().trim(), -}) - -export type ImportForeignSchemaForm = z.infer - export const ImportForeignSchemaDialog = ({ bucketName, namespace, - excludedSchemas, - wrapperValues, + wrapperMeta, visible, onClose, }: ImportForeignSchemaDialogProps) => { @@ -50,14 +39,34 @@ export const ImportForeignSchemaDialog = ({ const [loading, setLoading] = useState(false) const [createSchemaSheetOpen, setCreateSchemaSheetOpen] = useState(false) - const { mutateAsync: importForeignSchema } = useFDWImportForeignSchemaMutation({ + const { mutateAsync: importForeignSchema } = useFDWImportForeignSchemaMutation({}) + const { mutateAsync: updateFDW } = useFDWUpdateMutation({ onSuccess: () => { toast.success(`Successfully connected ${bucketName} to the database.`) onClose() }, }) - const form = useForm({ + const { data: schemas } = useSchemasQuery({ projectRef: project?.ref! }) + + const FormSchema = z.object({ + bucketName: z.string().trim(), + sourceNamespace: z.string().trim(), + targetSchema: z + .string() + .trim() + .min(1, 'Schema name is required') + .refine( + (val) => { + return !schemas?.find((s) => s.name === val) + }, + { + message: 'This schema already exists. Please specify a unique schema name.', + } + ), + }) + + const form = useForm>({ resolver: zodResolver(FormSchema), defaultValues: { bucketName, @@ -66,18 +75,67 @@ export const ImportForeignSchemaDialog = ({ }, }) - const onSubmit: SubmitHandler = async (values) => { + const { mutateAsync: createSchema } = useSchemaCreateMutation() + + const onSubmit: SubmitHandler> = async (values) => { + const serverName = `${snakeCase(values.bucketName)}_fdw_server` + if (!ref) return console.error('Project ref is required') setLoading(true) try { + await createSchema({ + projectRef: ref, + connectionString: project?.connectionString, + name: values.targetSchema, + }) + await importForeignSchema({ projectRef: ref, connectionString: project?.connectionString, - serverName: `${snakeCase(values.bucketName)}_fdw_server`, + serverName: serverName, sourceSchema: values.sourceNamespace, targetSchema: values.targetSchema, }) + + const FDWs = await getFDWs({ projectRef: ref, connectionString: project?.connectionString }) + const wrapper = FDWs.find((fdw) => fdw.server_name === serverName) + if (!wrapper) { + throw new Error(`Foreign data wrapper with server name ${serverName} not found`) + } + + const serverOptions = await getDecryptedParameters({ + ref: project?.ref, + connectionString: project?.connectionString ?? undefined, + serverName, + }) + + const formValues: Record = { + wrapper_name: wrapper.name, + server_name: wrapper.server_name, + ...serverOptions, + } + + const targetSchemas = (formValues['supabase_target_schema'] || '') + .split(',') + .map((s) => s.trim()) + + const wrapperTables = formatWrapperTables(wrapper, wrapperMeta) + + await updateFDW({ + projectRef: project?.ref, + connectionString: project?.connectionString, + wrapper: wrapper, + wrapperMeta: wrapperMeta, + formState: { + ...formValues, + server_name: serverName, + supabase_target_schema: uniq([...targetSchemas, values.targetSchema]) + .filter(Boolean) + .join(','), + }, + tables: wrapperTables, + }) } catch (error: any) { // error will be handled by the mutation onError callback } finally { @@ -110,35 +168,16 @@ export const ImportForeignSchemaDialog = ({
- ( - - - - - - )} - /> - ( - field.onChange(schema)} - onSelectCreateSchema={() => setCreateSchemaSheetOpen(true)} - /> + )} /> diff --git a/apps/studio/components/interfaces/Storage/ImportForeignSchemaDialog.utils.ts b/apps/studio/components/interfaces/Storage/ImportForeignSchemaDialog.utils.ts new file mode 100644 index 00000000000..923e41ae733 --- /dev/null +++ b/apps/studio/components/interfaces/Storage/ImportForeignSchemaDialog.utils.ts @@ -0,0 +1,51 @@ +import { getFDWs } from 'data/fdw/fdws-query' +import { getDecryptedValues } from 'data/vault/vault-secret-decrypted-value-query' +import { INTEGRATIONS } from '../Integrations/Landing/Integrations.constants' +import { WrapperMeta } from '../Integrations/Wrappers/Wrappers.types' +import { convertKVStringArrayToJson } from '../Integrations/Wrappers/Wrappers.utils' + +export const getDecryptedParameters = async ({ + ref, + connectionString, + serverName, +}: { + ref?: string + connectionString?: string + serverName: string +}) => { + const integration = INTEGRATIONS.find((i) => i.id === 'iceberg_wrapper' && i.type === 'wrapper') + const wrapperMeta = (integration?.type === 'wrapper' && integration.meta) as WrapperMeta + const wrapperServerOptions = wrapperMeta.server.options + + const FDWs = await getFDWs({ projectRef: ref, connectionString: connectionString }) + const wrapper = FDWs.find((fdw) => fdw.server_name === serverName) + const serverOptions = convertKVStringArrayToJson(wrapper?.server_options ?? []) + + const paramsToBeDecrypted = Object.fromEntries( + new Map( + Object.entries(serverOptions).filter(([key, value]) => { + return wrapperServerOptions.find((option) => option.name === key)?.encrypted + }) + ) + ) + + const decryptedValues = await getDecryptedValues({ + projectRef: ref, + connectionString: connectionString, + ids: Object.values(paramsToBeDecrypted), + }) + + const paramsWithDecryptedValues = Object.fromEntries( + new Map( + Object.entries(paramsToBeDecrypted).map(([name, id]) => { + const decryptedValue = decryptedValues[id] + return [name, decryptedValue] + }) + ) + ) + + return { + ...serverOptions, + ...paramsWithDecryptedValues, + } +} diff --git a/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.constants.ts b/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.constants.ts index b46ecf2cca5..411a4fb9507 100644 --- a/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.constants.ts +++ b/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.constants.ts @@ -1,4 +1,8 @@ -export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES = 50 * 1024 * 1024 * 1024 // 50 GB +export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_FREE_PLAN = 50 * 1024 * 1024 // 50 MB +/** Max bytes for capped storage (i.e. Pro with spend cap) */ +export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED = 50 * 1024 * 1024 * 1024 // 50 GB +/** Max bytes for uncapped storage (i.e. Pro without spend cap, Team, Enterprise) */ +export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED = 500 * 1024 * 1024 * 1024 // 500 GB export enum StorageSizeUnits { BYTES = 'bytes', diff --git a/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx b/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx index 536b07fde31..43868c50c03 100644 --- a/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx +++ b/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx @@ -1,7 +1,6 @@ import { zodResolver } from '@hookform/resolvers/zod' import { PermissionAction } from '@supabase/shared-types/out/constants' -import { Clock } from 'lucide-react' -import { useEffect, useState } from 'react' +import { useEffect, useMemo, useState } from 'react' import { SubmitHandler, useForm } from 'react-hook-form' import { toast } from 'sonner' import * as z from 'zod' @@ -16,6 +15,7 @@ import { useProjectStorageConfigQuery } from 'data/config/project-storage-config import { useProjectStorageConfigUpdateUpdateMutation } from 'data/config/project-storage-config-update-mutation' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' +import { formatBytes } from 'lib/helpers' import { Button, FormControl_Shadcn_, @@ -32,7 +32,12 @@ import { Select_Shadcn_, Switch, } from 'ui' -import { STORAGE_FILE_SIZE_LIMIT_MAX_BYTES, StorageSizeUnits } from './StorageSettings.constants' +import { + STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED, + STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_FREE_PLAN, + STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED, + StorageSizeUnits, +} from './StorageSettings.constants' import { convertFromBytes, convertToBytes } from './StorageSettings.utils' interface StorageSettingsState { @@ -56,6 +61,8 @@ const StorageSettings = () => { const organization = useSelectedOrganization() const isFreeTier = organization?.plan.id === 'free' + const isSpendCapOn = + organization?.plan.id === 'pro' && organization?.usage_billing_enabled === false const [initialValues, setInitialValues] = useState({ fileSizeLimit: 0, @@ -84,9 +91,17 @@ const StorageSettings = () => { } }, [isSuccess, config]) - const formattedMaxSizeBytes = `${new Intl.NumberFormat('en-US').format( - STORAGE_FILE_SIZE_LIMIT_MAX_BYTES - )} bytes` + const maxBytes = useMemo(() => { + if (organization?.plan.id === 'free') { + return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_FREE_PLAN + } else if (organization?.usage_billing_enabled) { + return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED + } else { + return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED + } + }, [organization]) + + const formattedMaxSizeBytes = `${new Intl.NumberFormat('en-US').format(maxBytes)} bytes` const FormSchema = z .object({ @@ -96,7 +111,7 @@ const StorageSettings = () => { }) .superRefine((data, ctx) => { const { unit, fileSizeLimit } = data - const { value: formattedMaxLimit } = convertFromBytes(STORAGE_FILE_SIZE_LIMIT_MAX_BYTES, unit) + const { value: formattedMaxLimit } = convertFromBytes(maxBytes, unit) if (fileSizeLimit > formattedMaxLimit) { ctx.addIssue({ @@ -156,7 +171,7 @@ const StorageSettings = () => {
-
+
{ limit, storageUnit ).toLocaleString()} bytes. `} - Maximum size in bytes of a file that can be uploaded is 50 GB ( - {formattedMaxSizeBytes}). + Maximum upload file size is {formatBytes(maxBytes)}.

@@ -261,9 +275,18 @@ const StorageSettings = () => { {isFreeTier && (
} primaryText="Free Plan has a fixed upload file size limit of 50 MB." - secondaryText="Upgrade to the Pro Plan for a configurable upload file size limit of up to 50 GB." + secondaryText={`Upgrade to Pro Plan for a configurable upload file size limit of ${formatBytes(STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED)} and unlock image transformations.`} + source="storageSizeLimit" + /> +
+ )} + {isSpendCapOn && ( +
+
diff --git a/apps/studio/components/layouts/DefaultLayout.tsx b/apps/studio/components/layouts/DefaultLayout.tsx index 4280bacbcce..c9b8a705428 100644 --- a/apps/studio/components/layouts/DefaultLayout.tsx +++ b/apps/studio/components/layouts/DefaultLayout.tsx @@ -1,10 +1,11 @@ +import { useRouter } from 'next/router' import { PropsWithChildren } from 'react' import { useParams } from 'common' import { AppBannerWrapper } from 'components/interfaces/App' import { AppBannerContextProvider } from 'components/interfaces/App/AppBannerWrapperContext' import { Sidebar } from 'components/interfaces/Sidebar' -import { useRouter } from 'next/router' +import { useCheckLatestDeploy } from 'hooks/use-check-latest-deploy' import { SidebarProvider } from 'ui' import { LayoutHeader } from './ProjectLayout/LayoutHeader' import MobileNavigationBar from './ProjectLayout/NavigationBar/MobileNavigationBar' @@ -29,6 +30,8 @@ const DefaultLayout = ({ children, headerTitle }: PropsWithChildren diff --git a/apps/studio/components/layouts/LogsLayout/LogsSidebarMenuV2.tsx b/apps/studio/components/layouts/LogsLayout/LogsSidebarMenuV2.tsx index 8f6d7571d47..785bb992b68 100644 --- a/apps/studio/components/layouts/LogsLayout/LogsSidebarMenuV2.tsx +++ b/apps/studio/components/layouts/LogsLayout/LogsSidebarMenuV2.tsx @@ -9,13 +9,9 @@ import { useFeaturePreviewModal, useUnifiedLogsPreview, } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' -import { CreateWarehouseCollectionModal } from 'components/interfaces/DataWarehouse/CreateWarehouseCollection' -import { WarehouseMenuItem } from 'components/interfaces/DataWarehouse/WarehouseMenuItem' import SavedQueriesItem from 'components/interfaces/Settings/Logs/Logs.SavedQueriesItem' import { LogsSidebarItem } from 'components/interfaces/Settings/Logs/SidebarV2/SidebarItem' import { ButtonTooltip } from 'components/ui/ButtonTooltip' -import { useWarehouseCollectionsQuery } from 'data/analytics/warehouse-collections-query' -import { useWarehouseTenantQuery } from 'data/analytics/warehouse-tenant-query' import { useContentQuery } from 'data/content/content-query' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useCurrentOrgPlan } from 'hooks/misc/useCurrentOrgPlan' @@ -27,14 +23,7 @@ import { Collapsible_Shadcn_, CollapsibleContent_Shadcn_, CollapsibleTrigger_Shadcn_, - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, Separator, - Tooltip, - TooltipContent, - TooltipTrigger, } from 'ui' import { InnerSideBarEmptyPanel, @@ -90,16 +79,11 @@ export function LogsSidebarMenuV2() { const router = useRouter() const { ref } = useParams() as { ref: string } - const warehouseEnabled = useFlag('warehouse') const unifiedLogsFlagEnabled = useFlag('unifiedLogs') const { selectFeaturePreview } = useFeaturePreviewModal() const { enable: enableUnifiedLogs } = useUnifiedLogsPreview() const [searchText, setSearchText] = useState('') - const [createCollectionOpen, setCreateCollectionOpen] = useState(false) - const canCreateCollection = useCheckPermissions(PermissionAction.ANALYTICS_WRITE, 'logflare') - - const { data: tenantData } = useWarehouseTenantQuery({ projectRef: ref }) const { projectAuthAll: authEnabled, @@ -107,11 +91,6 @@ export function LogsSidebarMenuV2() { realtimeAll: realtimeEnabled, } = useIsFeatureEnabled(['project_storage:all', 'project_auth:all', 'realtime:all']) - const { data: whCollections, isLoading: whCollectionsLoading } = useWarehouseCollectionsQuery( - { projectRef: ref }, - { enabled: IS_PLATFORM && warehouseEnabled && !!tenantData } - ) - const { plan: orgPlan, isLoading: isOrgPlanLoading } = useCurrentOrgPlan() const isFreePlan = !isOrgPlanLoading && orgPlan?.id === 'free' @@ -223,25 +202,24 @@ export function LogsSidebarMenuV2() { const filteredOperationalLogs = OPERATIONAL_COLLECTIONS.filter((collection) => { return collection?.name.toLowerCase().includes(searchText.toLowerCase()) }) - const filteredWarehouse = whCollections?.filter((collection) => { - return collection.name.toLowerCase().includes(searchText.toLowerCase()) - }) return (
- Coming soon} - title="New logs" - description="Get early access" - actions={ - - - - } - /> + {IS_PLATFORM && ( + Coming soon} + title="New logs" + description="Get early access" + actions={ + + + + } + /> + )} {isUnifiedLogsPreviewAvailable && ( - {warehouseEnabled ? ( - - - - - - {!canCreateCollection && ( - - You need additional permissions to create a collection - - )} - - - - ) : ( -
@@ -359,17 +292,6 @@ export function LogsSidebarMenuV2() { /> ) })} - {whCollectionsLoading && warehouseEnabled ? ( -
- -
- ) : filteredWarehouse?.length ? ( -
- {filteredWarehouse.map((collection) => ( - - ))} -
- ) : null} {OPERATIONAL_COLLECTIONS.length > 0 && ( <> diff --git a/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx b/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx index 62cea948bb6..f6c47c59ede 100644 --- a/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx +++ b/apps/studio/components/layouts/TableEditorLayout/EntityListItem.tsx @@ -51,6 +51,7 @@ export interface EntityListItemProps { projectRef: string isLocked: boolean isActive?: boolean + onExportCLI: () => void } // [jordi] Used to determine the entity is a table and not a view or other unsupported entity type @@ -64,6 +65,7 @@ const EntityListItem: ItemRenderer = ({ item: entity, isLocked, isActive: _isActive, + onExportCLI, }) => { const { project } = useProjectContext() const snap = useTableEditorStateSnapshot() @@ -386,6 +388,16 @@ const EntityListItem: ItemRenderer = ({ > Export table as SQL + { + e.stopPropagation() + onExportCLI() + }} + > + Export table via CLI + diff --git a/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx b/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx index 84bd7f4f689..3262273143e 100644 --- a/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx +++ b/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx @@ -5,6 +5,7 @@ import { useEffect, useMemo, useState } from 'react' import { useParams } from 'common' import { useBreakpoint } from 'common/hooks/useBreakpoint' +import { ExportDialog } from 'components/grid/components/header/ExportDialog' import { ProtectedSchemaModal } from 'components/interfaces/Database/ProtectedSchemaWarning' import EditorMenuListSkeleton from 'components/layouts/TableEditorLayout/EditorMenuListSkeleton' import AlertError from 'components/ui/AlertError' @@ -52,6 +53,7 @@ const TableEditorMenu = () => { const [showModal, setShowModal] = useState(false) const [searchText, setSearchText] = useState('') + const [tableToExport, setTableToExport] = useState<{ name: string; schema: string }>() const [visibleTypes, setVisibleTypes] = useState(Object.values(ENTITY_TYPE)) const [sort, setSort] = useLocalStorage<'alphabetical' | 'grouped-alphabetical'>( 'table-editor-sort', @@ -281,6 +283,11 @@ const TableEditorMenu = () => { projectRef: project?.ref!, id: Number(id), isLocked, + onExportCLI: () => { + const entity = entityTypes?.find((x) => x.id === id) + if (!entity) return + setTableToExport({ name: entity.name, schema: entity.schema }) + }, }} getItemSize={() => 28} hasNextPage={hasNextPage} @@ -294,6 +301,13 @@ const TableEditorMenu = () => {
+ { + if (!open) setTableToExport(undefined) + }} + /> setShowModal(false)} /> ) diff --git a/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterControlsDrawer.tsx b/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterControlsDrawer.tsx index 835d77cc54e..0267de60693 100644 --- a/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterControlsDrawer.tsx +++ b/apps/studio/components/ui/DataTable/DataTableFilters/DataTableFilterControlsDrawer.tsx @@ -15,7 +15,6 @@ import { DrawerTrigger, Tooltip, TooltipContent, - TooltipProvider, TooltipTrigger, } from 'ui' import { useMediaQuery } from '../hooks/useMediaQuery' @@ -32,26 +31,24 @@ export function DataTableFilterControlsDrawer() { return ( - - - - - - - - -

- Toggle controls with{' '} - - ⌘ - B - -

-
-
-
+ + + + + + + +

+ Toggle controls with{' '} + + ⌘ + B + +

+
+
diff --git a/apps/studio/components/ui/DataTable/DataTableResetButton.tsx b/apps/studio/components/ui/DataTable/DataTableResetButton.tsx index 0e9c18c8911..3e373ff2ee5 100644 --- a/apps/studio/components/ui/DataTable/DataTableResetButton.tsx +++ b/apps/studio/components/ui/DataTable/DataTableResetButton.tsx @@ -1,7 +1,7 @@ import { X } from 'lucide-react' import { useHotKey } from 'hooks/ui/useHotKey' -import { Button, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from 'ui' +import { Button, Tooltip, TooltipContent, TooltipTrigger } from 'ui' import { Kbd } from './primitives/Kbd' import { useDataTable } from './providers/DataTableProvider' @@ -10,28 +10,21 @@ export function DataTableResetButton() { useHotKey(table.resetColumnFilters, 'Escape') return ( - - - - - - -

- Reset filters with{' '} - - ⌘ - Esc - -

-
-
-
+ + + + + +

+ Reset filters with{' '} + + ⌘ + Esc + +

+
+
) } diff --git a/apps/studio/components/ui/DataTable/DataTableSheetDetails.tsx b/apps/studio/components/ui/DataTable/DataTableSheetDetails.tsx index 4a5ed2200fe..29f7a7f0495 100644 --- a/apps/studio/components/ui/DataTable/DataTableSheetDetails.tsx +++ b/apps/studio/components/ui/DataTable/DataTableSheetDetails.tsx @@ -1,16 +1,7 @@ import { ChevronDown, ChevronUp, X } from 'lucide-react' import { ReactNode, useCallback, useEffect, useMemo } from 'react' -import { - Button, - cn, - Separator, - Skeleton, - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from 'ui' +import { Button, cn, Separator, Skeleton, Tooltip, TooltipContent, TooltipTrigger } from 'ui' import { Kbd } from './primitives/Kbd' import { useDataTable } from './providers/DataTableProvider' @@ -79,44 +70,41 @@ export function DataTableSheetDetails({ {isLoading && !selectedRowKey ? : title}
- - - - - - -

- Toggle controls with{' '} - - ⌘ - B - -

-
-
-
+ + + + + +

+ Toggle controls with{' '} + + ⌘ + B + +

+
+
diff --git a/apps/studio/components/ui/UpgradeToPro.tsx b/apps/studio/components/ui/UpgradeToPro.tsx index b9c3ddf9ba2..a98778b7f39 100644 --- a/apps/studio/components/ui/UpgradeToPro.tsx +++ b/apps/studio/components/ui/UpgradeToPro.tsx @@ -69,7 +69,7 @@ const UpgradeToPro = ({ }, }} > - Reset database password + {buttonText || (plan === 'free' ? 'Upgrade to Pro' : 'Enable add on')} ) : ( + +
+
+ ) +} + +// This hook checks if the user is using old Studio pages and shows a toast to refresh the page. It's only triggered if +// there's a new version of Studio is available, and the user has been on the old dashboard (based on commit) for more than 24 hours. +// [Joshen] K-Dog has a suggestion here to bring down the time period here by checking commits +export function useCheckLatestDeploy() { + const showRefreshToast = useFlag('showRefreshToast') + + const [currentCommitTime, setCurrentCommitTime] = useState('') + const [isToastShown, setIsToastShown] = useState(false) + + const { data: commit } = useDeploymentCommitQuery({ + enabled: IS_PLATFORM && showRefreshToast, + staleTime: 10000, // 10 seconds + }) + + useEffect(() => { + // if the fetched commit is undefined is undefined + if (!commit || commit.commitTime === 'unknown') { + return + } + + // set the current commit on first load + if (!currentCommitTime) { + setCurrentCommitTime(commit.commitTime) + return + } + + // if the current commit is the same as the fetched commit, do nothing + if (currentCommitTime === commit.commitTime) { + return + } + + // prevent showing the toast again if user has already seen and dismissed it + if (isToastShown) { + return + } + + // check if the time difference between commits is more than 24 hours + const hourDiff = dayjs(commit.commitTime).diff(dayjs(currentCommitTime), 'hour') + if (hourDiff < 24) { + return + } + + // show the toast + toast.custom((id) => , { + duration: Infinity, + position: 'bottom-right', + }) + setIsToastShown(true) + }, [commit, isToastShown, currentCommitTime]) +} diff --git a/apps/studio/lib/ai/model.test.ts b/apps/studio/lib/ai/model.test.ts index 0f4f75eca3d..8418ad36893 100644 --- a/apps/studio/lib/ai/model.test.ts +++ b/apps/studio/lib/ai/model.test.ts @@ -18,7 +18,7 @@ describe('getModel', () => { beforeEach(() => { vi.resetAllMocks() - vi.stubEnv('AWS_BEDROCK_PROFILE', 'test') + vi.stubEnv('AWS_BEDROCK_ROLE_ARN', 'test') }) afterEach(() => { diff --git a/apps/studio/lib/ai/model.ts b/apps/studio/lib/ai/model.ts index 57698f2aaeb..79e52cd4e20 100644 --- a/apps/studio/lib/ai/model.ts +++ b/apps/studio/lib/ai/model.ts @@ -33,10 +33,10 @@ export const ModelErrorMessage = export async function getModel(routingKey?: string, isLimited?: boolean): Promise { const hasAwsCredentials = await checkAwsCredentials() - const hasAwsBedrockprofile = !!process.env.AWS_BEDROCK_PROFILE + const hasAwsBedrockRoleArn = !!process.env.AWS_BEDROCK_ROLE_ARN const hasOpenAIKey = !!process.env.OPENAI_API_KEY - if (hasAwsBedrockprofile && hasAwsCredentials) { + if (hasAwsBedrockRoleArn && hasAwsCredentials) { const bedrockModel = IS_THROTTLED || isLimited ? BEDROCK_NORMAL_MODEL : BEDROCK_PRO_MODEL const bedrock = createRoutedBedrock(routingKey) @@ -45,6 +45,7 @@ export async function getModel(routingKey?: string, isLimited?: boolean): Promis } } + // [Joshen] Only for local/self-hosted, hosted should always only use bedrock if (hasOpenAIKey) { return { model: openai(OPENAI_MODEL), diff --git a/apps/studio/lib/auth.tsx b/apps/studio/lib/auth.tsx index 18ef96866c1..92663680c91 100644 --- a/apps/studio/lib/auth.tsx +++ b/apps/studio/lib/auth.tsx @@ -8,6 +8,7 @@ import { import { PropsWithChildren, useCallback, useEffect } from 'react' import { toast } from 'sonner' +import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state' import { GOTRUE_ERRORS, IS_PLATFORM } from './constants' const AuthErrorToaster = ({ children }: PropsWithChildren) => { @@ -42,12 +43,15 @@ export { useAuth, useIsLoggedIn, useSession, useUser } from 'common' export function useSignOut() { const queryClient = useQueryClient() + const { clearStorage: clearAssistantStorage } = useAiAssistantStateSnapshot() return useCallback(async () => { const result = await gotrueClient.signOut() clearLocalStorage() + // Clear Assistant IndexedDB + await clearAssistantStorage() await queryClient.clear() return result - }, [queryClient]) + }, [queryClient, clearAssistantStorage]) } diff --git a/apps/studio/lib/gotrue.ts b/apps/studio/lib/gotrue.ts index 0c96b31a04d..42c65673ae6 100644 --- a/apps/studio/lib/gotrue.ts +++ b/apps/studio/lib/gotrue.ts @@ -73,6 +73,11 @@ export const buildPathWithParams = (pathname: string) => { } export const getReturnToPath = (fallback = DEFAULT_FALLBACK_PATH) => { + // If we're in a server environment, return the fallback + if (typeof location === 'undefined') { + return fallback + } + const searchParams = new URLSearchParams(location.search) let returnTo = searchParams.get('returnTo') ?? fallback diff --git a/apps/studio/middleware.ts b/apps/studio/middleware.ts index 912c8ccb14a..17896820bdc 100644 --- a/apps/studio/middleware.ts +++ b/apps/studio/middleware.ts @@ -16,6 +16,7 @@ const HOSTED_SUPPORTED_API_URLS = [ '/ai/feedback/classify', '/get-ip-address', '/get-utc-time', + '/get-deployment-commit', '/check-cname', '/edge-functions/test', '/edge-functions/body', diff --git a/apps/studio/package.json b/apps/studio/package.json index 3085a3d6de7..8ee4acc6c77 100644 --- a/apps/studio/package.json +++ b/apps/studio/package.json @@ -51,8 +51,8 @@ "@radix-ui/react-visually-hidden": "^1.1.3", "@sentry/nextjs": "^8.52.1", "@std/path": "npm:@jsr/std__path@^1.0.8", - "@stripe/react-stripe-js": "^3.1.1", - "@stripe/stripe-js": "^5.5.0", + "@stripe/react-stripe-js": "^3.7.0", + "@stripe/stripe-js": "^7.5.0", "@supabase/auth-js": "catalog:", "@supabase/mcp-server-supabase": "^0.4.4", "@supabase/mcp-utils": "^0.2.0", @@ -65,7 +65,6 @@ "@tanstack/react-query-devtools": "4.35.7", "@tanstack/react-table": "^8.21.3", "@uidotdev/usehooks": "^2.4.1", - "@vercel/flags": "^2.6.0", "@vercel/functions": "^2.1.0", "@vitejs/plugin-react": "^4.3.4", "@zip.js/zip.js": "^2.7.29", @@ -146,6 +145,7 @@ "zxcvbn": "^4.4.2" }, "devDependencies": { + "@faker-js/faker": "^9.9.0", "@graphql-codegen/cli": "5.0.5", "@graphql-typed-document-node/core": "^3.2.0", "@radix-ui/react-use-escape-keydown": "^1.0.3", diff --git a/apps/studio/pages/_app.tsx b/apps/studio/pages/_app.tsx index bbf227f10cb..671af91dc79 100644 --- a/apps/studio/pages/_app.tsx +++ b/apps/studio/pages/_app.tsx @@ -47,7 +47,6 @@ import { getFlags as getConfigCatFlags } from 'lib/configcat' import { API_URL, BASE_PATH, IS_PLATFORM } from 'lib/constants' import { ProfileProvider } from 'lib/profile' import { Telemetry } from 'lib/telemetry' -import HCaptchaLoadedStore from 'stores/hcaptcha-loaded-store' import { AppPropsWithLayout } from 'types' import { SonnerToaster, TooltipProvider } from 'ui' import { CommandProvider } from 'ui-patterns/CommandMenu' @@ -140,7 +139,6 @@ function CustomApp({ Component, pageProps }: AppPropsWithLayout) { - {!isTestEnv && } {!isTestEnv && ( )} diff --git a/apps/studio/pages/api/get-deployment-commit.ts b/apps/studio/pages/api/get-deployment-commit.ts new file mode 100644 index 00000000000..5b7cde2c504 --- /dev/null +++ b/apps/studio/pages/api/get-deployment-commit.ts @@ -0,0 +1,40 @@ +import { NextApiRequest, NextApiResponse } from 'next' + +async function getCommitTime(commitSha: string) { + try { + const response = await fetch(`https://github.com/supabase/supabase/commit/${commitSha}.json`, { + headers: { + Accept: 'application/json', + }, + }) + + if (!response.ok) { + throw new Error('Failed to fetch commit details') + } + + const data = await response.json() + return new Date(data.payload.commit.committedDate).toISOString() + } catch (error) { + console.error('Error fetching commit time:', error) + return 'unknown' + } +} + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse<{ commitSha: string; commitTime: string }> +) { + // Set cache control headers for 10 minutes so that we don't get banned by Github API + res.setHeader('Cache-Control', 's-maxage=600') + + // Get the build commit SHA from Vercel environment variable + const commitSha = process.env.VERCEL_GIT_COMMIT_SHA || 'development' + + // Only fetch commit time if we have a valid SHA + const commitTime = commitSha !== 'development' ? await getCommitTime(commitSha) : 'unknown' + + res.status(200).json({ + commitSha, + commitTime, + }) +} diff --git a/apps/studio/pages/authorize.tsx b/apps/studio/pages/authorize.tsx index 49e60d48ac9..70ebd6c5e47 100644 --- a/apps/studio/pages/authorize.tsx +++ b/apps/studio/pages/authorize.tsx @@ -30,7 +30,7 @@ import { const APIAuthorizationPage: NextPageWithLayout = () => { const router = useRouter() - const { auth_id } = useParams() + const { auth_id, organization_slug } = useParams() const [isApproving, setIsApproving] = useState(false) const [isDeclining, setIsDeclining] = useState(false) const [selectedOrgSlug, setSelectedOrgSlug] = useState() @@ -58,7 +58,11 @@ const APIAuthorizationPage: NextPageWithLayout = () => { useEffect(() => { if (isSuccessOrganizations && organizations.length > 0) { - setSelectedOrgSlug(organizations[0].slug) + if (organization_slug) { + setSelectedOrgSlug(organizations.find(({ slug }) => slug === organization_slug)?.slug) + } else { + setSelectedOrgSlug(organizations[0].slug) + } } // eslint-disable-next-line react-hooks/exhaustive-deps }, [isSuccessOrganizations]) @@ -168,7 +172,9 @@ const APIAuthorizationPage: NextPageWithLayout = () => { - - -
- -
- diff --git a/examples/user-management/angular-user-management/src/app/account/account.component.css b/examples/user-management/angular-user-management/src/app/account/account.css similarity index 100% rename from examples/user-management/angular-user-management/src/app/account/account.component.css rename to examples/user-management/angular-user-management/src/app/account/account.css diff --git a/examples/user-management/angular-user-management/src/app/account/account.html b/examples/user-management/angular-user-management/src/app/account/account.html new file mode 100644 index 00000000000..0d7402d43af --- /dev/null +++ b/examples/user-management/angular-user-management/src/app/account/account.html @@ -0,0 +1,30 @@ +
+ + +
+ + +
+
+ + +
+
+ + +
+ +
+ +
+ +
+ +
+ diff --git a/examples/user-management/angular-user-management/src/app/account/account.component.spec.ts b/examples/user-management/angular-user-management/src/app/account/account.spec.ts similarity index 72% rename from examples/user-management/angular-user-management/src/app/account/account.component.spec.ts rename to examples/user-management/angular-user-management/src/app/account/account.spec.ts index d954515dd33..bb7f3cfafe2 100644 --- a/examples/user-management/angular-user-management/src/app/account/account.component.spec.ts +++ b/examples/user-management/angular-user-management/src/app/account/account.spec.ts @@ -1,16 +1,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { AccountComponent } from './account.component'; +import { AccountComponent } from './account'; -describe('AccountComponent', () => { +describe('Account', () => { let component: AccountComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ AccountComponent ] - }) - .compileComponents(); + declarations: [AccountComponent], + }).compileComponents(); fixture = TestBed.createComponent(AccountComponent); component = fixture.componentInstance; diff --git a/examples/user-management/angular-user-management/src/app/account/account.component.ts b/examples/user-management/angular-user-management/src/app/account/account.ts similarity index 82% rename from examples/user-management/angular-user-management/src/app/account/account.component.ts rename to examples/user-management/angular-user-management/src/app/account/account.ts index 8024df9328f..3651451df16 100644 --- a/examples/user-management/angular-user-management/src/app/account/account.component.ts +++ b/examples/user-management/angular-user-management/src/app/account/account.ts @@ -1,30 +1,42 @@ import { Component, Input, OnInit } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { FormBuilder, FormGroup } from '@angular/forms'; import { AuthSession } from '@supabase/supabase-js'; import { Profile, SupabaseService } from '../supabase.service'; @Component({ selector: 'app-account', - templateUrl: './account.component.html', - styleUrls: ['./account.component.css'], + templateUrl: './account.html', + styleUrls: ['./account.css'], + standalone: false, }) export class AccountComponent implements OnInit { loading = false; profile!: Profile; + updateProfileForm!: FormGroup; + + get avatarUrl() { + return this.updateProfileForm.value.avatar_url as string; + } + async updateAvatar(event: string): Promise { + this.updateProfileForm.patchValue({ + avatar_url: event, + }); + await this.updateProfile(); + } @Input() session!: AuthSession; - updateProfileForm = this.formBuilder.group({ - username: '', - website: '', - avatar_url: '', - }); - constructor( private readonly supabase: SupabaseService, private formBuilder: FormBuilder - ) {} + ) { + this.updateProfileForm = this.formBuilder.group({ + username: '', + website: '', + avatar_url: '', + }); + } async ngOnInit(): Promise { await this.getProfile(); @@ -37,15 +49,15 @@ export class AccountComponent implements OnInit { }); } - get avatarUrl() { - return this.updateProfileForm.value.avatar_url as string; - } - async getProfile() { try { this.loading = true; const { user } = this.session; - let { data: profile, error, status } = await this.supabase.profile(user); + const { + data: profile, + error, + status, + } = await this.supabase.profile(user); if (error && status !== 406) { throw error; @@ -63,13 +75,6 @@ export class AccountComponent implements OnInit { } } - async updateAvatar(event: string): Promise { - this.updateProfileForm.patchValue({ - avatar_url: event, - }); - await this.updateProfile(); - } - async updateProfile(): Promise { try { this.loading = true; diff --git a/examples/user-management/angular-user-management/src/app/app.component.css b/examples/user-management/angular-user-management/src/app/app.css similarity index 100% rename from examples/user-management/angular-user-management/src/app/app.component.css rename to examples/user-management/angular-user-management/src/app/app.css diff --git a/examples/user-management/angular-user-management/src/app/app.component.html b/examples/user-management/angular-user-management/src/app/app.html similarity index 100% rename from examples/user-management/angular-user-management/src/app/app.component.html rename to examples/user-management/angular-user-management/src/app/app.html diff --git a/examples/user-management/angular-user-management/src/app/app.module.ts b/examples/user-management/angular-user-management/src/app/app.module.ts index e02bd520565..f037601ec76 100644 --- a/examples/user-management/angular-user-management/src/app/app.module.ts +++ b/examples/user-management/angular-user-management/src/app/app.module.ts @@ -1,11 +1,11 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; -import { AppComponent } from './app.component'; -import { AuthComponent } from './auth/auth.component'; -import { AccountComponent } from './account/account.component'; +import { AppComponent } from './app'; +import { AuthComponent } from './auth/auth'; +import { AccountComponent } from './account/account'; import { ReactiveFormsModule } from '@angular/forms'; -import { AvatarComponent } from './avatar/avatar.component'; +import { AvatarComponent } from './avatar/avatar'; @NgModule({ declarations: [ @@ -17,5 +17,6 @@ import { AvatarComponent } from './avatar/avatar.component'; imports: [BrowserModule, ReactiveFormsModule], providers: [], bootstrap: [AppComponent], + exports: [AppComponent, AuthComponent, AccountComponent, AvatarComponent], }) export class AppModule {} diff --git a/examples/user-management/angular-user-management/src/app/app.component.spec.ts b/examples/user-management/angular-user-management/src/app/app.spec.ts similarity index 52% rename from examples/user-management/angular-user-management/src/app/app.component.spec.ts rename to examples/user-management/angular-user-management/src/app/app.spec.ts index 4b068a28656..e569c5688e4 100644 --- a/examples/user-management/angular-user-management/src/app/app.component.spec.ts +++ b/examples/user-management/angular-user-management/src/app/app.spec.ts @@ -1,12 +1,12 @@ +import { provideZonelessChangeDetection } from '@angular/core'; import { TestBed } from '@angular/core/testing'; -import { AppComponent } from './app.component'; +import { AppComponent } from './app'; -describe('AppComponent', () => { +describe('App', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ - AppComponent - ], + declarations: [AppComponent], + providers: [provideZonelessChangeDetection()], }).compileComponents(); }); @@ -16,16 +16,12 @@ describe('AppComponent', () => { expect(app).toBeTruthy(); }); - it(`should have as title 'angular-user-management'`, () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app.title).toEqual('angular-user-management'); - }); - it('should render title', () => { const fixture = TestBed.createComponent(AppComponent); fixture.detectChanges(); const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('.content span')?.textContent).toContain('angular-user-management app is running!'); + expect(compiled.querySelector('h1')?.textContent).toContain( + 'Hello, supabase-angular' + ); }); }); diff --git a/examples/user-management/angular-user-management/src/app/app.component.ts b/examples/user-management/angular-user-management/src/app/app.ts similarity index 73% rename from examples/user-management/angular-user-management/src/app/app.component.ts rename to examples/user-management/angular-user-management/src/app/app.ts index d668fc68d68..52a77efd0b4 100644 --- a/examples/user-management/angular-user-management/src/app/app.component.ts +++ b/examples/user-management/angular-user-management/src/app/app.ts @@ -3,17 +3,18 @@ import { SupabaseService } from './supabase.service'; @Component({ selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['./app.component.css'], + templateUrl: './app.html', + styleUrls: ['./app.css'], + standalone: false, }) export class AppComponent implements OnInit { - title = 'angular-user-management'; - - session = this.supabase.session; - constructor(private readonly supabase: SupabaseService) {} + title = 'angular-user-management'; + session: any; + ngOnInit() { + this.session = this.supabase.session; this.supabase.authChanges((_, session) => (this.session = session)); } } diff --git a/examples/user-management/angular-user-management/src/app/auth/auth.component.css b/examples/user-management/angular-user-management/src/app/auth/auth.css similarity index 100% rename from examples/user-management/angular-user-management/src/app/auth/auth.component.css rename to examples/user-management/angular-user-management/src/app/auth/auth.css diff --git a/examples/user-management/angular-user-management/src/app/auth/auth.component.html b/examples/user-management/angular-user-management/src/app/auth/auth.html similarity index 78% rename from examples/user-management/angular-user-management/src/app/auth/auth.component.html rename to examples/user-management/angular-user-management/src/app/auth/auth.html index 874130cccc7..fa9515c379b 100644 --- a/examples/user-management/angular-user-management/src/app/auth/auth.component.html +++ b/examples/user-management/angular-user-management/src/app/auth/auth.html @@ -14,12 +14,8 @@ />
-
diff --git a/examples/user-management/angular-user-management/src/app/auth/auth.component.spec.ts b/examples/user-management/angular-user-management/src/app/auth/auth.spec.ts similarity index 73% rename from examples/user-management/angular-user-management/src/app/auth/auth.component.spec.ts rename to examples/user-management/angular-user-management/src/app/auth/auth.spec.ts index ce785350ec7..113a852f27d 100644 --- a/examples/user-management/angular-user-management/src/app/auth/auth.component.spec.ts +++ b/examples/user-management/angular-user-management/src/app/auth/auth.spec.ts @@ -1,16 +1,15 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { AuthComponent } from './auth.component'; +import { AuthComponent } from './auth'; -describe('AuthComponent', () => { +describe('Auth', () => { let component: AuthComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ AuthComponent ] - }) - .compileComponents(); + declarations: [AuthComponent], + }).compileComponents(); fixture = TestBed.createComponent(AuthComponent); component = fixture.componentInstance; diff --git a/examples/user-management/angular-user-management/src/app/auth/auth.component.ts b/examples/user-management/angular-user-management/src/app/auth/auth.ts similarity index 67% rename from examples/user-management/angular-user-management/src/app/auth/auth.component.ts rename to examples/user-management/angular-user-management/src/app/auth/auth.ts index 6e80c347ac7..17feb934d9d 100644 --- a/examples/user-management/angular-user-management/src/app/auth/auth.component.ts +++ b/examples/user-management/angular-user-management/src/app/auth/auth.ts @@ -1,25 +1,26 @@ -import { Component, OnInit } from '@angular/core'; -import { FormBuilder } from '@angular/forms'; +import { Component } from '@angular/core'; +import { FormBuilder, FormGroup } from '@angular/forms'; import { SupabaseService } from '../supabase.service'; @Component({ selector: 'app-auth', - templateUrl: './auth.component.html', - styleUrls: ['./auth.component.css'], + templateUrl: './auth.html', + styleUrls: ['./auth.css'], + standalone: false, }) -export class AuthComponent implements OnInit { - loading = false; - - signInForm = this.formBuilder.group({ - email: '', - }); - +export class AuthComponent { + signInForm!: FormGroup; constructor( private readonly supabase: SupabaseService, private readonly formBuilder: FormBuilder ) {} - ngOnInit(): void {} + loading = false; + ngOnInit() { + this.signInForm = this.formBuilder.group({ + email: '', + }); + } async onSubmit(): Promise { try { diff --git a/examples/user-management/angular-user-management/src/app/avatar/avatar.component.css b/examples/user-management/angular-user-management/src/app/avatar/avatar.css similarity index 100% rename from examples/user-management/angular-user-management/src/app/avatar/avatar.component.css rename to examples/user-management/angular-user-management/src/app/avatar/avatar.css diff --git a/examples/user-management/angular-user-management/src/app/avatar/avatar.component.html b/examples/user-management/angular-user-management/src/app/avatar/avatar.html similarity index 82% rename from examples/user-management/angular-user-management/src/app/avatar/avatar.component.html rename to examples/user-management/angular-user-management/src/app/avatar/avatar.html index b86e45f254c..c3dc3b0a615 100644 --- a/examples/user-management/angular-user-management/src/app/avatar/avatar.component.html +++ b/examples/user-management/angular-user-management/src/app/avatar/avatar.html @@ -14,10 +14,10 @@ >
{ +describe('Avatar', () => { let component: AvatarComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ AvatarComponent ] - }) - .compileComponents(); + declarations: [AvatarComponent], + }).compileComponents(); fixture = TestBed.createComponent(AvatarComponent); component = fixture.componentInstance; diff --git a/examples/user-management/angular-user-management/src/app/avatar/avatar.component.ts b/examples/user-management/angular-user-management/src/app/avatar/avatar.ts similarity index 86% rename from examples/user-management/angular-user-management/src/app/avatar/avatar.component.ts rename to examples/user-management/angular-user-management/src/app/avatar/avatar.ts index f89dfaa2054..584b9a83b21 100644 --- a/examples/user-management/angular-user-management/src/app/avatar/avatar.component.ts +++ b/examples/user-management/angular-user-management/src/app/avatar/avatar.ts @@ -1,13 +1,14 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { SafeResourceUrl, DomSanitizer } from '@angular/platform-browser'; import { SupabaseService } from '../supabase.service'; @Component({ selector: 'app-avatar', - templateUrl: './avatar.component.html', - styleUrls: ['./avatar.component.css'], + templateUrl: './avatar.html', + styleUrls: ['./avatar.css'], + standalone: false, }) -export class AvatarComponent implements OnInit { +export class AvatarComponent { _avatarUrl: SafeResourceUrl | undefined; uploading = false; @@ -25,8 +26,6 @@ export class AvatarComponent implements OnInit { private readonly dom: DomSanitizer ) {} - ngOnInit(): void {} - async downloadImage(path: string) { try { const { data } = await this.supabase.downLoadImage(path); diff --git a/examples/user-management/angular-user-management/src/app/supabase.service.spec.ts b/examples/user-management/angular-user-management/src/app/supabase.service.spec.ts deleted file mode 100644 index 6e1ce7dba08..00000000000 --- a/examples/user-management/angular-user-management/src/app/supabase.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { TestBed } from '@angular/core/testing'; - -import { SupabaseService } from './supabase.service'; - -describe('SupabaseService', () => { - let service: SupabaseService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(SupabaseService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/examples/user-management/angular-user-management/src/app/supabase.service.ts b/examples/user-management/angular-user-management/src/app/supabase.service.ts index efb0dfde211..de672c3394a 100644 --- a/examples/user-management/angular-user-management/src/app/supabase.service.ts +++ b/examples/user-management/angular-user-management/src/app/supabase.service.ts @@ -7,8 +7,7 @@ import { SupabaseClient, User, } from '@supabase/supabase-js'; -import { environment } from 'src/environments/environment'; -import { Database } from 'src/schema'; +import { environment } from '../environments/environment'; export interface Profile { id?: string; @@ -21,11 +20,11 @@ export interface Profile { providedIn: 'root', }) export class SupabaseService { - private supabase: SupabaseClient; + private supabase: SupabaseClient; _session: AuthSession | null = null; constructor() { - this.supabase = createClient( + this.supabase = createClient( environment.supabaseUrl, environment.supabaseKey ); diff --git a/examples/user-management/angular-user-management/src/assets/.gitkeep b/examples/user-management/angular-user-management/src/assets/.gitkeep deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/examples/user-management/angular-user-management/src/favicon.ico b/examples/user-management/angular-user-management/src/favicon.ico deleted file mode 100644 index 997406ad22c..00000000000 Binary files a/examples/user-management/angular-user-management/src/favicon.ico and /dev/null differ diff --git a/examples/user-management/angular-user-management/src/index.html b/examples/user-management/angular-user-management/src/index.html index 8e43ba66172..e46cb157e71 100644 --- a/examples/user-management/angular-user-management/src/index.html +++ b/examples/user-management/angular-user-management/src/index.html @@ -1,13 +1,13 @@ - + - - - AngularUserManagement - - - - - - - + + + SupabaseAngular + + + + + + + diff --git a/examples/user-management/angular-user-management/src/main.ts b/examples/user-management/angular-user-management/src/main.ts index c7b673cf44b..71eb23b9177 100644 --- a/examples/user-management/angular-user-management/src/main.ts +++ b/examples/user-management/angular-user-management/src/main.ts @@ -1,12 +1,8 @@ -import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - +import { platformBrowser } from '@angular/platform-browser'; import { AppModule } from './app/app.module'; -import { environment } from './environments/environment'; -if (environment.production) { - enableProdMode(); -} - -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.error(err)); +platformBrowser() + .bootstrapModule(AppModule, { + ngZoneEventCoalescing: true, + }) + .catch((err) => console.error(err)); diff --git a/examples/user-management/angular-user-management/src/polyfills.ts b/examples/user-management/angular-user-management/src/polyfills.ts deleted file mode 100644 index 429bb9ef2d3..00000000000 --- a/examples/user-management/angular-user-management/src/polyfills.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * This file includes polyfills needed by Angular and is loaded before the app. - * You can add your own extra polyfills to this file. - * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * - * The current setup is for so-called "evergreen" browsers; the last versions of browsers that - * automatically update themselves. This includes recent versions of Safari, Chrome (including - * Opera), Edge on the desktop, and iOS and Chrome on mobile. - * - * Learn more in https://angular.io/guide/browser-support - */ - -/*************************************************************************************************** - * BROWSER POLYFILLS - */ - -/** - * By default, zone.js will patch all possible macroTask and DomEvents - * user can disable parts of macroTask/DomEvents patch by setting following flags - * because those flags need to be set before `zone.js` being loaded, and webpack - * will put import in the top of bundle, so user need to create a separate file - * in this directory (for example: zone-flags.ts), and put the following flags - * into that file, and then add the following code before importing zone.js. - * import './zone-flags'; - * - * The flags allowed in zone-flags.ts are listed here. - * - * The following flags will work for all browsers. - * - * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame - * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick - * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames - * - * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js - * with the following flag, it will bypass `zone.js` patch for IE/Edge - * - * (window as any).__Zone_enable_cross_context_check = true; - * - */ - -/*************************************************************************************************** - * Zone JS is required by default for Angular itself. - */ -import 'zone.js'; // Included with Angular CLI. - - -/*************************************************************************************************** - * APPLICATION IMPORTS - */ diff --git a/examples/user-management/angular-user-management/src/schema.ts b/examples/user-management/angular-user-management/src/schema.ts deleted file mode 100644 index 4593c033977..00000000000 --- a/examples/user-management/angular-user-management/src/schema.ts +++ /dev/null @@ -1,40 +0,0 @@ -export type Json = string | number | boolean | null | { [key: string]: Json } | Json[] - -export interface Database { - public: { - Tables: { - profiles: { - Row: { - id: string - updated_at: string | null - username: string | null - avatar_url: string | null - website: string | null - } - Insert: { - id: string - updated_at?: string | null - username?: string | null - avatar_url?: string | null - website?: string | null - } - Update: { - id?: string - updated_at?: string | null - username?: string | null - avatar_url?: string | null - website?: string | null - } - } - } - Views: { - [_ in never]: never - } - Functions: { - [_ in never]: never - } - Enums: { - [_ in never]: never - } - } -} diff --git a/examples/user-management/angular-user-management/src/styles.css b/examples/user-management/angular-user-management/src/styles.css index 54b465ff8c0..cb0b2e27142 100644 --- a/examples/user-management/angular-user-management/src/styles.css +++ b/examples/user-management/angular-user-management/src/styles.css @@ -1,53 +1,57 @@ +/* You can add global styles to this file, and also import other style files */ html, body { - --custom-font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, - Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; - --custom-bg-color: #101010; - --custom-panel-color: #222; - --custom-box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.8); - --custom-color: #fff; - --custom-color-brand: #24b47e; - --custom-color-secondary: #666; - --custom-border: 1px solid #333; - --custom-border-radius: 5px; - --custom-spacing: 5px; + --custom-font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, + Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; + --custom-bg-color: #101010; + --custom-panel-color: #222; + --custom-box-shadow: 0 2px 8px 0 rgba(0, 0, 0, 0.8); + --custom-color: #fff; + --custom-color-brand: #24b47e; + --custom-color-secondary: #666; + --custom-border: 1px solid #333; + --custom-border-radius: 5px; + --custom-spacing: 5px; - padding: 0; - margin: 0; - font-family: var(--custom-font-family); - background-color: var(--custom-bg-color); + padding: 0; + margin: 0; + font-family: var(--custom-font-family); + background-color: var(--custom-bg-color); } * { - color: var(--custom-color); - font-family: var(--custom-font-family); - box-sizing: border-box; + color: var(--custom-color); + font-family: var(--custom-font-family); + box-sizing: border-box; } html, body, #__next { - height: 100vh; - width: 100vw; - overflow-x: hidden; + height: 100vh; + width: 100vw; + overflow-x: hidden; } /* Grid */ .container { - width: 90%; - margin-left: auto; - margin-right: auto; + width: 90%; + margin-left: auto; + margin-right: auto; } + .row { - position: relative; - width: 100%; + position: relative; + width: 100%; } + .row [class^="col"] { - float: left; - margin: 0.5rem 2%; - min-height: 0.125rem; + float: left; + margin: 0.5rem 2%; + min-height: 0.125rem; } + .col-1, .col-2, .col-3, @@ -60,313 +64,363 @@ body, .col-10, .col-11, .col-12 { - width: 96%; + width: 96%; } + .col-1-sm { - width: 4.33%; + width: 4.33%; } + .col-2-sm { - width: 12.66%; + width: 12.66%; } + .col-3-sm { - width: 21%; + width: 21%; } + .col-4-sm { - width: 29.33%; + width: 29.33%; } + .col-5-sm { - width: 37.66%; + width: 37.66%; } + .col-6-sm { - width: 46%; + width: 46%; } + .col-7-sm { - width: 54.33%; + width: 54.33%; } + .col-8-sm { - width: 62.66%; + width: 62.66%; } + .col-9-sm { - width: 71%; + width: 71%; } + .col-10-sm { - width: 79.33%; + width: 79.33%; } + .col-11-sm { - width: 87.66%; + width: 87.66%; } + .col-12-sm { - width: 96%; + width: 96%; } + .row::after { - content: ""; - display: table; - clear: both; + content: ""; + display: table; + clear: both; } + .hidden-sm { - display: none; + display: none; } @media only screen and (min-width: 33.75em) { - /* 540px */ - .container { - width: 80%; - } + + /* 540px */ + .container { + width: 80%; + } } @media only screen and (min-width: 45em) { - /* 720px */ - .col-1 { - width: 4.33%; - } - .col-2 { - width: 12.66%; - } - .col-3 { - width: 21%; - } - .col-4 { - width: 29.33%; - } - .col-5 { - width: 37.66%; - } - .col-6 { - width: 46%; - } - .col-7 { - width: 54.33%; - } - .col-8 { - width: 62.66%; - } - .col-9 { - width: 71%; - } - .col-10 { - width: 79.33%; - } - .col-11 { - width: 87.66%; - } - .col-12 { - width: 96%; - } - .hidden-sm { - display: block; - } + + /* 720px */ + .col-1 { + width: 4.33%; + } + + .col-2 { + width: 12.66%; + } + + .col-3 { + width: 21%; + } + + .col-4 { + width: 29.33%; + } + + .col-5 { + width: 37.66%; + } + + .col-6 { + width: 46%; + } + + .col-7 { + width: 54.33%; + } + + .col-8 { + width: 62.66%; + } + + .col-9 { + width: 71%; + } + + .col-10 { + width: 79.33%; + } + + .col-11 { + width: 87.66%; + } + + .col-12 { + width: 96%; + } + + .hidden-sm { + display: block; + } } @media only screen and (min-width: 60em) { - /* 960px */ - .container { - width: 75%; - max-width: 60rem; - } + + /* 960px */ + .container { + width: 75%; + max-width: 60rem; + } } /* Forms */ label { - display: block; - margin: 5px 0; - color: var(--custom-color-secondary); - font-size: 0.8rem; - text-transform: uppercase; + display: block; + margin: 5px 0; + color: var(--custom-color-secondary); + font-size: 0.8rem; + text-transform: uppercase; } input { - width: 100%; - border-radius: 5px; - border: var(--custom-border); - padding: 8px; - font-size: 0.9rem; - background-color: var(--custom-bg-color); - color: var(--custom-color); + width: 100%; + border-radius: 5px; + border: var(--custom-border); + padding: 8px; + font-size: 0.9rem; + background-color: var(--custom-bg-color); + color: var(--custom-color); } input[disabled] { - color: var(--custom-color-secondary); + color: var(--custom-color-secondary); } /* Utils */ .block { - display: block; - width: 100%; + display: block; + width: 100%; } + .inline-block { - display: inline-block; - width: 100%; + display: inline-block; + width: 100%; } + .flex { - display: flex; + display: flex; } + .flex.column { - flex-direction: column; + flex-direction: column; } + .flex.row { - flex-direction: row; + flex-direction: row; } + .flex.flex-1 { - flex: 1 1 0; + flex: 1 1 0; } + .flex-end { - justify-content: flex-end; + justify-content: flex-end; } + .flex-center { - justify-content: center; + justify-content: center; } + .items-center { - align-items: center; + align-items: center; } + .text-sm { - font-size: 0.8rem; - font-weight: 300; + font-size: 0.8rem; + font-weight: 300; } + .text-right { - text-align: right; + text-align: right; } + .font-light { - font-weight: 300; + font-weight: 300; } + .opacity-half { - opacity: 50%; + opacity: 50%; } /* Button */ button, .button { - color: var(--custom-color); - border: var(--custom-border); - background-color: var(--custom-bg-color); - display: inline-block; - text-align: center; - border-radius: var(--custom-border-radius); - padding: 0.5rem 1rem; - cursor: pointer; - text-align: center; - font-size: 0.9rem; - text-transform: uppercase; + color: var(--custom-color); + border: var(--custom-border); + background-color: var(--custom-bg-color); + display: inline-block; + text-align: center; + border-radius: var(--custom-border-radius); + padding: 0.5rem 1rem; + cursor: pointer; + text-align: center; + font-size: 0.9rem; + text-transform: uppercase; } button.primary, .button.primary { - background-color: var(--custom-color-brand); - border: 1px solid var(--custom-color-brand); + background-color: var(--custom-color-brand); + border: 1px solid var(--custom-color-brand); } /* Widgets */ .card { - width: 100%; - display: block; - border: var(--custom-border); - border-radius: var(--custom-border-radius); - padding: var(--custom-spacing); + width: 100%; + display: block; + border: var(--custom-border); + border-radius: var(--custom-border-radius); + padding: var(--custom-spacing); } .avatar { - border-radius: var(--custom-border-radius); - overflow: hidden; - max-width: 100%; + border-radius: var(--custom-border-radius); + overflow: hidden; + max-width: 100%; } + .avatar.image { - object-fit: cover; + object-fit: cover; } + .avatar.no-image { - background-color: #333; - border: 1px solid rgb(200, 200, 200); - border-radius: 5px; + background-color: #333; + border: 1px solid rgb(200, 200, 200); + border-radius: 5px; } .footer { - position: absolute; - max-width: 100%; - bottom: 0; - left: 0; - right: 0; - display: flex; - flex-flow: row; - border-top: var(--custom-border); - background-color: var(--custom-bg-color); + position: absolute; + max-width: 100%; + bottom: 0; + left: 0; + right: 0; + display: flex; + flex-flow: row; + border-top: var(--custom-border); + background-color: var(--custom-bg-color); } + .footer div { - padding: var(--custom-spacing); - display: flex; - align-items: center; - width: 100%; + padding: var(--custom-spacing); + display: flex; + align-items: center; + width: 100%; } -.footer div > img { - height: 20px; - margin-left: 10px; + +.footer div>img { + height: 20px; + margin-left: 10px; } -.footer > div:first-child { - display: none; + +.footer>div:first-child { + display: none; } -.footer > div:nth-child(2) { - justify-content: left; + +.footer>div:nth-child(2) { + justify-content: left; } @media only screen and (min-width: 60em) { - /* 960px */ - .footer > div:first-child { - display: flex; - } - .footer > div:nth-child(2) { - justify-content: center; - } + + /* 960px */ + .footer>div:first-child { + display: flex; + } + + .footer>div:nth-child(2) { + justify-content: center; + } } @keyframes spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } } .mainHeader { - width: 100%; - font-size: 1.3rem; - margin-bottom: 20px; + width: 100%; + font-size: 1.3rem; + margin-bottom: 20px; } .avatarPlaceholder { - border: var(--custom-border); - border-radius: var(--custom-border-radius); - width: 35px; - height: 35px; - background-color: rgba(255, 255, 255, 0.2); - display: flex; - align-items: center; - justify-content: center; + border: var(--custom-border); + border-radius: var(--custom-border-radius); + width: 35px; + height: 35px; + background-color: rgba(255, 255, 255, 0.2); + display: flex; + align-items: center; + justify-content: center; } .form-widget { - display: flex; - flex-direction: column; - gap: 20px; + display: flex; + flex-direction: column; + gap: 20px; } -.form-widget > .button { - display: flex; - align-items: center; - justify-content: center; - border: none; - background-color: #444444; - text-transform: none !important; - transition: all 0.2s ease; +.form-widget>.button { + display: flex; + align-items: center; + justify-content: center; + border: none; + background-color: #444444; + text-transform: none !important; + transition: all 0.2s ease; } .form-widget .button:hover { - background-color: #2a2a2a; + background-color: #2a2a2a; } -.form-widget .button > .loader { - width: 17px; - animation: spin 1s linear infinite; - filter: invert(1); -} +.form-widget .button>.loader { + width: 17px; + animation: spin 1s linear infinite; + filter: invert(1); +} \ No newline at end of file diff --git a/examples/user-management/angular-user-management/src/test.ts b/examples/user-management/angular-user-management/src/test.ts deleted file mode 100644 index c04c876075f..00000000000 --- a/examples/user-management/angular-user-management/src/test.ts +++ /dev/null @@ -1,26 +0,0 @@ -// This file is required by karma.conf.js and loads recursively all the .spec and framework files - -import 'zone.js/testing'; -import { getTestBed } from '@angular/core/testing'; -import { - BrowserDynamicTestingModule, - platformBrowserDynamicTesting -} from '@angular/platform-browser-dynamic/testing'; - -declare const require: { - context(path: string, deep?: boolean, filter?: RegExp): { - (id: string): T; - keys(): string[]; - }; -}; - -// First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, - platformBrowserDynamicTesting(), -); - -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().forEach(context); diff --git a/examples/user-management/angular-user-management/tsconfig.app.json b/examples/user-management/angular-user-management/tsconfig.app.json index 82d91dc4a4d..264f459bf87 100644 --- a/examples/user-management/angular-user-management/tsconfig.app.json +++ b/examples/user-management/angular-user-management/tsconfig.app.json @@ -1,15 +1,15 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/app", "types": [] }, - "files": [ - "src/main.ts", - "src/polyfills.ts" - ], "include": [ - "src/**/*.d.ts" + "src/**/*.ts" + ], + "exclude": [ + "src/**/*.spec.ts" ] } diff --git a/examples/user-management/angular-user-management/tsconfig.json b/examples/user-management/angular-user-management/tsconfig.json index ff06eae10c5..e4955f26b14 100644 --- a/examples/user-management/angular-user-management/tsconfig.json +++ b/examples/user-management/angular-user-management/tsconfig.json @@ -1,32 +1,34 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { "compileOnSave": false, "compilerOptions": { - "baseUrl": "./", - "outDir": "./dist/out-tsc", - "forceConsistentCasingInFileNames": true, "strict": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, - "sourceMap": true, - "declaration": false, - "downlevelIteration": true, + "skipLibCheck": true, + "isolatedModules": true, "experimentalDecorators": true, - "moduleResolution": "node", "importHelpers": true, - "target": "es2020", - "module": "es2020", - "lib": [ - "es2020", - "dom" - ] + "target": "ES2022", + "module": "preserve" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, "strictInjectionParameters": true, "strictInputAccessModifiers": true, + "typeCheckHostBindings": true, "strictTemplates": true - } + }, + "files": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] } diff --git a/examples/user-management/angular-user-management/tsconfig.spec.json b/examples/user-management/angular-user-management/tsconfig.spec.json index 092345b02e8..e00e30e6d4f 100644 --- a/examples/user-management/angular-user-management/tsconfig.spec.json +++ b/examples/user-management/angular-user-management/tsconfig.spec.json @@ -1,18 +1,10 @@ -/* To learn more about this file see: https://angular.io/config/tsconfig. */ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./out-tsc/spec", - "types": [ - "jasmine" - ] + "types": ["jasmine"] }, - "files": [ - "src/test.ts", - "src/polyfills.ts" - ], - "include": [ - "src/**/*.spec.ts", - "src/**/*.d.ts" - ] + "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] } diff --git a/i18n/README.hr.md b/i18n/README.hr.md new file mode 100644 index 00000000000..5e71e916452 --- /dev/null +++ b/i18n/README.hr.md @@ -0,0 +1,289 @@ +

+ + +

+ +--- + +# Supabase + +[Supabase](https://supabase.com) je open source alternativa Firebaseu. Izgrađujemo funkcionalnosti Firebasea koristeći enterprise-grade open source alate. + +- [x] Hostana Postgres baza podataka. [Dokumentacija](https://supabase.com/docs/guides/database) +- [x] Autentifikacija i autorizacija. [Dokumentacija](https://supabase.com/docs/guides/auth) +- [x] Automatski generirani API-ji. + - [x] REST. [Dokumentacija](https://supabase.com/docs/guides/api#rest-api-overview) + - [x] GraphQL. [Dokumentacija](https://supabase.com/docs/guides/api#graphql-api-overview) + - [x] Pretplate u realnom vremenu. [Dokumentacija](https://supabase.com/docs/guides/api#realtime-api-overview) +- [x] Funkcije. + - [x] Funkcije baza podataka. [Dokumentacija](https://supabase.com/docs/guides/database/functions) + - [x] Edge funkcije [Dokumentacija](https://supabase.com/docs/guides/functions) +- [x] Pohranjivanje datoteka. [Dokumentacija](https://supabase.com/docs/guides/storage) +- [x] Panel + +![Supabase Dashboard](https://raw.githubusercontent.com/supabase/supabase/master/apps/www/public/images/github/supabase-dashboard.png) + +Pratite "izdanja" ovog repozitorija da bi bili obaviješteni o većim ažuriranjima. + +Watch this repo + +## Dokumentacija + +Za cjelovitu dokumentaciju, posjetite [supabase.com/docs](https://supabase.com/docs) + +Za informacije kako doprinijeti razvoju, posjetite [Početak rada](./DEVELOPERS.md) + +## Zajednica & Podrška + +- [Forum zajednice](https://github.com/supabase/supabase/discussions). Najbolje služi za: pomoć pri izgradnji, diskusiji o najboljoj praksi oko baza podataka. +- [GitHub Issues](https://github.com/supabase/supabase/issues). Najbolje služi za: bugove i greške na koje ste naišli koristeći Supabase. +- [Email potpora](https://supabase.com/docs/support#business-support). Najbolje služi za: probleme oko vaše baze podataka ili oko infrastrukture. +- [Discord](https://discord.supabase.com). Najbolje služi za: pokazati vaše aplikacije i druženje sa ostatkom zajednice. + +## Kako funkcionira + +Supabase je kombinacija alata otvorenog koda. Izgrađujemo funkcionalnosti Firebasea koristeći enterprise-grade open source alate. Ako postoje alati i zajednice, sa MIT, Apache 2, ili ekvivalentnim otvorenim licencama, koristit ćemo i podržati taj alat. Ako alat ne postoji, mi ga izgrađujemo i otvaramo taj kod javno. Supabase nije preslika Firebasea. Naš cilj je razvijateljima dati osjećaj kao da koriste Firebase ali sa alatima otvorenog koda. + +**Arhitektura** + +Supabase je [hostana platforma](https://supabase.com/dashboard). Možete se registrirati i odmah počet koristiti Supabase bez ikakvih instalacija. +Također možete ju [samostalno hostati](https://supabase.com/docs/guides/hosting/overview) i [razvijati lokalno](https://supabase.com/docs/guides/local-development). + +![Arhitektura](apps/docs/public/img/supabase-architecture.svg) + +- [Postgres](https://www.postgresql.org/) je objektno-relacijska baza podataka koja je aktivno u razvoju preko 30 godina i na glasu je kao jako pouzdana, robusna i performantna. +- [Realtime](https://github.com/supabase/realtime) je Elixir server koji vam dopušta da prisluškujete unose, ažuriranja i brisanja u PostgreSQL bazi koristeći websockete. Realtime prati Postgres-ovu funkcionalnost repliciranja i osluškuje promjene u bazi podataka, te iste upakira u JSON, na kraju emitira taj JSON preko websocketa do autoriziranih klijenata. +- [PostgREST](http://postgrest.org/) je web server koji pretvara vašu PostgreSQL bazu podataka u RESTful API. +- [GoTrue](https://github.com/supabase/gotrue) je API koji koristi JWT za upravaljanje korisnika i izdavanje JWT tokena. +- [Storage](https://github.com/supabase/storage-api) pruža RESTful sučelje za upravljanje datoteka spremljenih u S3, koristeći Postgres za upravljanje dozvola. +- [pg_graphql](http://github.com/supabase/pg_graphql/) je PostgreSQL ekstenzija koja pruža Graphql API. +- [postgres-meta](https://github.com/supabase/postgres-meta) je RESTful API za upravljanje vaše Postgres baze podatake, dopuštajući vam da dohvatite tablice, dodate uloge, i izvršite upite prema bazi, itd. +- [Kong](https://github.com/Kong/kong) je cloud-native API gateway. + +#### Klijentske knjižnice + +Naš pristup za klijentske knjižnice je modularan. Svaka pod-knjižnica je samostalna implementacija za svaki vanjski sistem. Ovo je jedan od načina kako podržavamo postojeće alate. + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
JezikKlijentFeature-Clients (upakirani u Supabase klijent)
SupabasePostgRESTGoTrueRealtimeStorageFunctions
⚡️ Službeno ⚡️
JavaScript (TypeScript)supabase-jspostgrest-jsgotrue-jsrealtime-jsstorage-jsfunctions-js
Fluttersupabase-flutterpostgrest-dartgotrue-dartrealtime-dartstorage-dartfunctions-dart
Swiftsupabase-swiftpostgrest-swiftauth-swiftrealtime-swiftstorage-swiftfunctions-swift
Pythonsupabase-pypostgrest-pygotrue-pyrealtime-pystorage-pyfunctions-py
💚 Zajednica 💚
C#supabase-csharppostgrest-csharpgotrue-csharprealtime-csharpstorage-csharpfunctions-csharp
Go-postgrest-gogotrue-go-storage-gofunctions-go
Java--gotrue-java-storage-java-
Kotlinsupabase-ktpostgrest-ktauth-ktrealtime-ktstorage-ktfunctions-kt
Rubysupabase-rbpostgrest-rb----
Rust-postgrest-rs----
Godot Engine (GDScript)supabase-gdscriptpostgrest-gdscriptgotrue-gdscriptrealtime-gdscriptstorage-gdscriptfunctions-gdscript
+ + + + +## Značke + +![Made with Supabase](./apps/www/public/badge-made-with-supabase.svg) + +```md +[![Made with Supabase](https://supabase.com/badge-made-with-supabase.svg)](https://supabase.com) +``` + +```html + + Made with Supabase + +``` + +![Made with Supabase (dark)](./apps/www/public/badge-made-with-supabase-dark.svg) + +```md +[![Made with Supabase](https://supabase.com/badge-made-with-supabase-dark.svg)](https://supabase.com) +``` + +```html + + Made with Supabase + +``` + +## Prijevodi + +- [Arapski | العربية](/i18n/README.ar.md) +- [Albanski / Shqip](/i18n/README.sq.md) +- [Bengalski / বাংলা](/i18n/README.bn.md) +- [Bugarski / Български](/i18n/README.bg.md) +- [Katalonski / Català](/i18n/README.ca.md) +- [Hrvatski](/i18n/README.ca.md) +- [Češki / čeština](/i18n/README.cs.md) +- [Danski / Dansk](/i18n/README.da.md) +- [Nizozemski / Nederlands](/i18n/README.nl.md) +- [Engleski](https://github.com/supabase/supabase) +- [Estonski / eesti keel](/i18n/README.et.md) +- [Finski / Suomalainen](/i18n/README.fi.md) +- [Francuski / Français](/i18n/README.fr.md) +- [Njemački / Deutsch](/i18n/README.de.md) +- [Grčki / Ελληνικά](/i18n/README.el.md) +- [Gudžaratski / ગુજરાતી](/i18n/README.gu.md) +- [Hebrejski / עברית](/i18n/README.he.md) +- [Hindski / हिंदी](/i18n/README.hi.md) +- [Mađarski / Magyar](/i18n/README.hu.md) +- [Nepalski / नेपाली](/i18n/README.ne.md) +- [Indonezijski / Bahasa Indonezija](/i18n/README.id.md) +- [Talijanski / Italian](/i18n/README.it.md) +- [Japanski / 日本語](/i18n/README.jp.md) +- [korejski / 한국어](/i18n/README.ko.md) +- [Litavski / lietuvių](/i18n/README.lt.md) +- [Latvijski / latviski](/i18n/README.lv.md) +- [Malajski / Bahasa Malaysia](/i18n/README.ms.md) +- [Norveški (Bokmål) / Norsk (Bokmål)](/i18n/README.nb.md) +- [Perzijski / فارسی](/i18n/README.fa.md) +- [Poljski / Polski](/i18n/README.pl.md) +- [Portugalski / Português](/i18n/README.pt.md) +- [Portugalski (Brazilski) / Português Brasileiro](/i18n/README.pt-br.md) +- [Rumunjski / Română](/i18n/README.ro.md) +- [Ruski / Pусский](/i18n/README.ru.md) +- [Srpski / Srpski](/i18n/README.sr.md) +- [Singalski / සිංහල](/i18n/README.si.md) +- [Slovački / slovenský](/i18n/README.sk.md) +- [Slovenski / Slovenščina](/i18n/README.sl.md) +- [Španjolski / Español](/i18n/README.es.md) +- [Pojednostavljeni kineski / 简体中文](/i18n/README.zh-cn.md) +- [Švedski / Svenska](/i18n/README.sv.md) +- [Tajski / ไทย](/i18n/README.th.md) +- [Tradicionalni kineski / 繁體中文](/i18n/README.zh-tw.md) +- [Turski / Türkçe](/i18n/README.tr.md) +- [Ukrajinski / Українська](/i18n/README.uk.md) +- [Vijetnamski / Tiếng Việt](/i18n/README.vi-vn.md) +- [Popis prijevoda](/i18n/languages.md) diff --git a/i18n/README.si.md b/i18n/README.si.md index f89b9805fd2..c995827f8c7 100644 --- a/i18n/README.si.md +++ b/i18n/README.si.md @@ -9,82 +9,281 @@ [Supabase](https://supabase.com) යනු විවෘත පරිශීලක Firebase වෙනුවට ආදේශකයකි.අපි Firebase වල ඇති පහසුකම් enterprise-grade විවෘත පරිශීලක මෘදුකාංග භාවිතා කරමින් නිපදවනු ලබයි. -- [x] Hosted Postgres Database -- [x] Realtime subscriptions -- [x] Authentication and authorization -- [x] Auto-generated APIs +- [x] Hosted Postgres Database. [Docs](https://supabase.com/docs/guides/database) +- [x] Authentication and Authorization. [Docs](https://supabase.com/docs/guides/auth) +- [x] Auto-generated APIs. + - [x] REST. [Docs](https://supabase.com/docs/guides/api) + - [x] GraphQL. [Docs](https://supabase.com/docs/guides/graphql) + - [x] Realtime subscriptions. [Docs](https://supabase.com/docs/guides/realtime) +- [x] Functions. + - [x] Database Functions. [Docs](https://supabase.com/docs/guides/database/functions) + - [x] Edge Functions [Docs](https://supabase.com/docs/guides/functions) +- [x] File Storage. [Docs](https://supabase.com/docs/guides/storage) +- [x] AI + Vector/Embeddings Toolkit. [Docs](https://supabase.com/docs/guides/ai) - [x] Dashboard -- [x] Storage -- [x] Functions ![Supabase Dashboard](https://raw.githubusercontent.com/supabase/supabase/master/apps/www/public/images/github/supabase-dashboard.png) +ප්‍රධාන යාවත්කාල කිරීම් පිළිබඳ දැනුම් දීම සඳහා "releases" පිළිබඳව අවධානයෙන් සිටින්න. + +Watch this repo + ## ප්‍රලේඛනය සම්පූර්ණ විස්තරය කියවන්න ,මෙතනින් [supabase.com/docs](https://supabase.com/docs) +දායක වන ආකාරය බැලීමට, ආරම්භ කිරීම වෙත [පිවිසෙන්න](./DEVELOPERS.md) + ## Community එක හා සහයෝගය ගැනීමට - [Community Forum](https://github.com/supabase/supabase/discussions). වඩාත්ම සුදුසු: දේවල් සෑදීමට උදව් ගැනීම.database best practices පිළිබඳ සාකච්ඡා කිරීමට. - [GitHub Issues](https://github.com/supabase/supabase/issues). වඩාත්ම සුදුසු: Supabase භාවිතා කිරීමේදී ඔබට හමුවන වැරදී හා ප්‍රශ්න. - [Email Support](https://supabase.com/docs/support#business-support). වඩාත්ම සුදුසු: database හා infrastructure පිළිබඳ ඔබේ ඇති ප්‍රශ්න. - -## තත්ත්වය - -- [x] Alpha: අප දැනට Supabase විවෘත නොවන පාරිභෝගිකයන් සමඟ පරීක්ෂා කරනවා -- [x] Public Alpha: ඕනෑම කෙනෙකුට [supabase.com/dashboard](https://supabase.com/dashboard) මඟින් සම්බන්ධ විය හැකිය.නමුත් හෙමිහිට පරීක්ෂා කරන්න, දැනට kinks කිහිපයක් පමණයි ඇත්තේ -- [x] Public Beta: non-enterprise භාවිතයන් ගොඩකට ස්ථාවරයි -- [ ] Public: භාවිතා කිරීම සඳහා සූදානම් - -අප දැනට පොදු beta තත්වයේ සිටින්නෙ. ප්‍රධාන නිකුත්කිරීම් හා දැනුවත් වීම් සඳහා "releases" පිළිබඳව අවධානයෙන් සිටින්න. - -Watch this repo - ---- +- [Discord](https://discord.supabase.com). වඩාත්ම සුදුසු: ඔබගේ applications බෙදාගැනීම සහ community සමඟ රැඳී සිටීම. ## කොහොමද මෙය වැඩකරන්නෙ Supabase යනු විවෘත පරිශීලක උපාංග කිහිපයක එකතුවකි.අප Firebase වල ඇති පහසුකම් enterprise-grade, විවෘත පරිශීලක උපාංග භාවිතා කරමින් නිපදවයි.උපාංග හා communities ඇත්නම්,ඒවා MIT,Apache 2, හෝ ඒ ආකාරයේ open license නම් අප ඒවා භාවිතා කර සහයෝගය දක්වනවා.එවැනි උපාංග නොමැතිනම්,අප ඒවා විවෘත පරිශීලක විදියට නිපදවනවා.Supabase යනු 1-ට-1 Firebase වලට සමාන දෙයක් නොවේ.අපේ අරමුණ developers ලට Firebase වැනි අත්දැකීමක් විවෘත පරිශීලක උපාංග මඟින් ලබාදීමයි. -**Current architecture** +**Architecture** -Supabase යනු [hosted platform](https://supabase.com/dashboard) එකකි.ඔබට දැන් සම්බන්ධ වී කිසිම දෙයක් install නොකර Supabase භාවිත කල හැකිය.අප තවමත් local development අත්දැකීම සාදමින් සිටියි- මෙය තමයි අපේ දැනට ප්‍රධාන අරමුණ,platform එකේ ස්ථාවර බව සමඟ. +Supabase යනු [hosted platform](https://supabase.com/dashboard). ඔබට කිසිවක් ස්ථාපනය නොකර ලියාපදිංචි වී Supabase භාවිතා කිරීම ආරම්භ කළ හැකිය. +ඔබට [self-host](https://supabase.com/docs/guides/hosting/overview) සහ [develop locally](https://supabase.com/docs/guides/local-development) කළ හැකිය. -![Architecture](https://github.com/supabase/supabase/blob/master/apps/docs/public/img/supabase-architecture.svg) +![Architecture](apps/docs/public/img/supabase-architecture.svg) - [PostgreSQL](https://www.postgresql.org/) යනු අවුරුදු 30 වඩා කාලයක් ක්‍රියාත්මක වෙමින් පවතින object-relational database system එකක් වන අතර එය විශ්වාසනීයත්වයට,ක්‍රියාකාරීත්වයට හා feature robustness බවට කීර්තියක් අත්පත්කරගෙන සිටියි -- [Realtime](https://github.com/supabase/realtime) is an Elixir server that allows you to listen to PostgreSQL inserts, updates, and deletes using websockets. Supabase listens to Postgres' built-in replication functionality, converts the replication byte stream into JSON, then broadcasts the JSON over websockets. +- [Realtime](https://github.com/supabase/realtime) is an Elixir server that allows you to listen to PostgreSQL inserts, updates, and deletes using websockets. Realtime polls Postgres' built-in replication functionality for database changes, converts changes to JSON, then broadcasts the JSON over websockets to authorized clients. - [PostgREST](http://postgrest.org/) යනු PostgreSQL database කෙලින්ම RESTful API එකක් බවට පරිවර්තනය කරන web server එකකි. -- [Storage](https://github.com/supabase/storage-api) pS3 වල ගබඩා කර ඇති files හසුරවන්න RESTful interface එකක් ලබාදීම, Postgres භාවිත කරමින් අවසරයන් හැසිරවීමට +- [GoTrue](https://github.com/netlify/gotrue) JWT tokens භාවිත කරන්නන්ව හැසිරවීමට JWT ආශ්‍රිත APi එකකි. +- [Storage](https://github.com/supabase/storage-api) S3 වල ගබඩා කර ඇති files හසුරවන්න RESTful interface එකක් ලබාදීම, Postgres භාවිත කරමින් අවසරයන් හැසිරවීමට +- [pg_graphql](http://github.com/supabase/pg_graphql/) a PostgreSQL extension that exposes a GraphQL API - [postgres-meta](https://github.com/supabase/postgres-meta) Postgres හැසිරවීමට RESTful API එකක් ලබාදෙන අතර table වල දත්ත ලබාගැනීමට,roles add කිරීමට හා queries run කිරීම කර දෙයි -- [GoTrue](https://github.com/netlify/gotrue) SWT tokens භාවිත කරන්නන්ව හැසිරවීමට SWT ආශ්‍රිත APi එකකි/ - [Kong](https://github.com/Kong/kong) is a cloud-native API gateway. #### Client libraries -Our client library is modular. Each sub-library is a standalone implementation for a single external system. This is one of the ways we support existing tools. +Client libraries සඳහා අපගේ ප්‍රවේශය modular වේ. සෑම sub-library තනි බාහිර පද්ධතියක් සඳහා ස්වාධීනව ක්‍රියාත්මක කිරීමකි. අපි දැනට පවතින මෙවලම් සඳහා සහය දක්වන ක්‍රමවලින් එකකි. -- **`supabase-{lang}`**: Combines libraries and adds enrichments. - - `postgrest-{lang}`: Client library to work with [PostgREST](https://github.com/postgrest/postgrest) - - `realtime-{lang}`: Client library to work with [Realtime](https://github.com/supabase/realtime) - - `gotrue-{lang}`: Client library to work with [GoTrue](https://github.com/netlify/gotrue) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LanguageClientFeature-Clients (bundled in Supabase client)
SupabasePostgRESTGoTrueRealtimeStorageFunctions
⚡️ Official ⚡️
JavaScript (TypeScript)supabase-jspostgrest-jsgotrue-jsrealtime-jsstorage-jsfunctions-js
Fluttersupabase-flutterpostgrest-dartgotrue-dartrealtime-dartstorage-dartfunctions-dart
Swiftsupabase-swiftpostgrest-swiftauth-swiftrealtime-swiftstorage-swiftfunctions-swift
Pythonsupabase-pypostgrest-pygotrue-pyrealtime-pystorage-pyfunctions-py
💚 Community 💚
C#supabase-csharppostgrest-csharpgotrue-csharprealtime-csharpstorage-csharpfunctions-csharp
Go-postgrest-gogotrue-go-storage-gofunctions-go
Java--gotrue-java-storage-java-
Kotlinsupabase-ktpostgrest-ktauth-ktrealtime-ktstorage-ktfunctions-kt
Rubysupabase-rbpostgrest-rb----
Rust-postgrest-rs----
Godot Engine (GDScript)supabase-gdscriptpostgrest-gdscriptgotrue-gdscriptrealtime-gdscriptstorage-gdscriptfunctions-gdscript
-| Repo | Official | Community | -| --------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| **`supabase-{lang}`** | [`JS`](https://github.com/supabase/supabase-js) | [`C#`](https://github.com/supabase/supabase-csharp) \| [`Flutter`](https://github.com/supabase/supabase-flutter) \| [`Python`](https://github.com/supabase/supabase-py) \| `Rust` \| [`Ruby`](https://github.com/supabase/supabase-rb) \| `Go` | -| `postgrest-{lang}` | [`JS`](https://github.com/supabase/postgrest-js) | [`C#`](https://github.com/supabase/postgrest-csharp) \| [`Dart`](https://github.com/supabase/postgrest-dart) \| [`Python`](https://github.com/supabase/postgrest-py) \| [`Rust`](https://github.com/supabase/postgrest-rs) \| [`Ruby`](https://github.com/supabase/postgrest-rb) \| [`Go`](https://github.com/supabase/postgrest-go) | -| `realtime-{lang}` | [`JS`](https://github.com/supabase/realtime-js) | [`C#`](https://github.com/supabase/realtime-csharp) \| [`Dart`](https://github.com/supabase/realtime-dart) \| [`Python`](https://github.com/supabase/realtime-py) \| `Rust` \| `Ruby` \| `Go` | -| `gotrue-{lang}` | [`JS`](https://github.com/supabase/gotrue-js) | [`C#`](https://github.com/supabase/gotrue-csharp) \| [`Dart`](https://github.com/supabase/gotrue-dart) \| [`Python`](https://github.com/supabase/gotrue-py) \| `Rust` \| `Ruby` \| `Go` | - - + +## Badges + +![Made with Supabase](./apps/www/public/badge-made-with-supabase.svg) + +```md +[![Made with Supabase](https://supabase.com/badge-made-with-supabase.svg)](https://supabase.com) +``` + +```html + + Made with Supabase + +``` + +![Made with Supabase (dark)](./apps/www/public/badge-made-with-supabase-dark.svg) + +```md +[![Made with Supabase](https://supabase.com/badge-made-with-supabase-dark.svg)](https://supabase.com) +``` + +```html + + Made with Supabase + +``` + ## Translations +- [Arabic | العربية](/i18n/README.ar.md) +- [Albanian / Shqip](/i18n/README.sq.md) +- [Bangla / বাংলা](/i18n/README.bn.md) +- [Bulgarian / Български](/i18n/README.bg.md) +- [Catalan / Català](/i18n/README.ca.md) +- [Czech / čeština](/i18n/README.cs.md) +- [Danish / Dansk](/i18n/README.da.md) +- [Dutch / Nederlands](/i18n/README.nl.md) +- [English](https://github.com/supabase/supabase) +- [Estonian / eesti keel](/i18n/README.et.md) +- [Finnish / Suomalainen](/i18n/README.fi.md) +- [French / Français](/i18n/README.fr.md) +- [German / Deutsch](/i18n/README.de.md) +- [Greek / Ελληνικά](/i18n/README.el.md) +- [Gujarati / ગુજરાતી](/i18n/README.gu.md) +- [Hebrew / עברית](/i18n/README.he.md) +- [Hindi / हिंदी](/i18n/README.hi.md) +- [Hungarian / Magyar](/i18n/README.hu.md) +- [Nepali / नेपाली](/i18n/README.ne.md) +- [Indonesian / Bahasa Indonesia](/i18n/README.id.md) +- [Italiano / Italian](/i18n/README.it.md) +- [Japanese / 日本語](/i18n/README.jp.md) +- [Korean / 한국어](/i18n/README.ko.md) +- [Lithuanian / lietuvių](/i18n/README.lt.md) +- [Latvian / latviski](/i18n/README.lv.md) +- [Malay / Bahasa Malaysia](/i18n/README.ms.md) +- [Norwegian (Bokmål) / Norsk (Bokmål)](/i18n/README.nb.md) +- [Persian / فارسی](/i18n/README.fa.md) +- [Polish / Polski](/i18n/README.pl.md) +- [Portuguese / Português](/i18n/README.pt.md) +- [Portuguese (Brazilian) / Português Brasileiro](/i18n/README.pt-br.md) +- [Romanian / Română](/i18n/README.ro.md) +- [Russian / Pусский](/i18n/README.ru.md) +- [Serbian / Srpski](/i18n/README.sr.md) +- [Sinhala / සිංහල](/i18n/README.si.md) +- [Slovak / slovenský](/i18n/README.sk.md) +- [Slovenian / Slovenščina](/i18n/README.sl.md) +- [Spanish / Español](/i18n/README.es.md) +- [Simplified Chinese / 简体中文](/i18n/README.zh-cn.md) +- [Swedish / Svenska](/i18n/README.sv.md) +- [Thai / ไทย](/i18n/README.th.md) +- [Traditional Chinese / 繁體中文](/i18n/README.zh-tw.md) +- [Turkish / Türkçe](/i18n/README.tr.md) +- [Ukrainian / Українська](/i18n/README.uk.md) +- [Vietnamese / Tiếng Việt](/i18n/README.vi-vn.md) - [List of translations](/i18n/languages.md) - ---- - -## Sponsors - -[![New Sponsor](https://user-images.githubusercontent.com/10214025/90518111-e74bbb00-e198-11ea-8f88-c9e3c1aa4b5b.png)](https://github.com/sponsors/supabase) diff --git a/i18n/languages.md b/i18n/languages.md index 1117bb0a02c..a6a7de8cb61 100644 --- a/i18n/languages.md +++ b/i18n/languages.md @@ -5,6 +5,7 @@ - [Bangla / বাংলা](/i18n/README.bn.md) - [Bulgarian / Български](/i18n/README.bg.md) - [Catalan / Català](/i18n/README.ca.md) +- [Croatian / hrvatski](/i18n/README.hr.md) - [Czech / čeština](/i18n/README.cs.md) - [Danish / Dansk](/i18n/README.da.md) - [Dutch / Nederlands](/i18n/README.nl.md) diff --git a/package.json b/package.json index abe0d27ac45..8f9346a77fb 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,8 @@ "@tanstack/directive-functions-plugin>vite": "catalog:", "@tanstack/react-start-plugin>vite": "catalog:", "esbuild": "^0.25.2", - "payload>image-size": "1.2.1" + "payload>image-size": "1.2.1", + "@redocly/respect-core>form-data": "^4.0.4" }, "neverBuiltDependencies": ["libpg-query"] }, diff --git a/packages/api-types/types/api.d.ts b/packages/api-types/types/api.d.ts index 0f93564023b..4a56c1365e3 100644 --- a/packages/api-types/types/api.d.ts +++ b/packages/api-types/types/api.d.ts @@ -1603,7 +1603,7 @@ export interface components { role: string } | null /** @enum {string|null} */ - type?: 'publishable' | 'secret' | 'legacy' | null + type?: 'legacy' | 'publishable' | 'secret' | null /** Format: date-time */ updated_at?: string | null } @@ -1984,6 +1984,7 @@ export interface components { secrets?: { [key: string]: string } + with_data?: boolean } CreateOrganizationV1: { name: string @@ -2449,6 +2450,11 @@ export interface components { grant_type?: 'authorization_code' | 'refresh_token' redirect_uri?: string refresh_token?: string + /** + * @description Resource indicator for MCP (Model Context Protocol) clients + * @enum {string} + */ + resource?: 'http://localhost:8080/mcp' } OAuthTokenResponse: { access_token: string @@ -3696,7 +3702,11 @@ export interface operations { client_id: string code_challenge?: string code_challenge_method?: 'plain' | 'sha256' | 'S256' + /** @description Organization slug */ + organization_slug?: string redirect_uri: string + /** @description Resource indicator for MCP (Model Context Protocol) clients */ + resource?: 'http://localhost:8080/mcp' response_mode?: string response_type: 'code' | 'token' | 'id_token token' scope?: string diff --git a/packages/api-types/types/platform.d.ts b/packages/api-types/types/platform.d.ts index 00a3b169b1a..0eff38d7b21 100644 --- a/packages/api-types/types/platform.d.ts +++ b/packages/api-types/types/platform.d.ts @@ -3391,6 +3391,23 @@ export interface paths { patch?: never trace?: never } + '/platform/replication/{ref}/pipelines/{pipeline_id}/replication-status': { + parameters: { + query?: never + header?: never + path?: never + cookie?: never + } + /** Get the status of a replication pipeline. */ + get: operations['ReplicationPipelinesController_getPipelineReplicationStatus'] + put?: never + post?: never + delete?: never + options?: never + head?: never + patch?: never + trace?: never + } '/platform/replication/{ref}/pipelines/{pipeline_id}/start': { parameters: { query?: never @@ -4282,12 +4299,12 @@ export interface components { BillingCustomerUpdateBody: { additional_emails?: string[] address?: { - city?: string + city?: string | null country: string line1: string - line2?: string - postal_code?: string - state?: string + line2?: string | null + postal_code?: string | null + state?: string | null } billing_name?: string } @@ -4621,12 +4638,12 @@ export interface components { } CreateOrganizationBody: { address?: { - city?: string + city?: string | null country: string line1: string - line2?: string - postal_code?: string - state?: string + line2?: string | null + postal_code?: string | null + state?: string | null } billing_name?: string kind?: string @@ -4685,7 +4702,8 @@ export interface components { } CreateProjectBody: { auth_site_url?: string - cloud_provider: string + /** @enum {string} */ + cloud_provider: 'AWS' | 'FLY' | 'AWS_K8S' custom_supabase_internal_requests?: { ami: { search_tags?: { @@ -5027,9 +5045,23 @@ export interface components { teamId?: string } CreditsTopUpRequest: { + address?: { + city?: string | null + country: string + line1: string + line2?: string | null + postal_code?: string | null + state?: string | null + } amount: number + billing_name?: string hcaptcha_token?: string payment_method_id: string + tax_id?: { + country: string + type: string + value: string + } } CreditsTopUpResponse: { payment_intent_secret?: string @@ -5046,12 +5078,12 @@ export interface components { CustomerResponse: { additional_emails: string[] | null address?: { - city?: string + city?: string | null country: string line1: string - line2?: string - postal_code?: string - state?: string + line2?: string | null + postal_code?: string | null + state?: string | null } balance: number billing_name?: string @@ -5060,7 +5092,7 @@ export interface components { } DatabaseDetailResponse: { /** @enum {string} */ - cloud_provider: 'AWS' | 'AWS_K8S' | 'FLY' + cloud_provider: 'AWS' | 'FLY' | 'AWS_K8S' /** @default null */ connection_string_read_only?: string | null /** @default null */ @@ -5264,10 +5296,6 @@ export interface components { file_url: string fileUrl: string } - GetAvailableRegionsBody: { - cloud_provider: string - organization_slug: string - } GetContentCountResponse: { count: number } @@ -6359,7 +6387,8 @@ export interface components { }[] } OrganizationSlugAvailableVersionsBody: { - provider: string + /** @enum {string} */ + provider: 'AWS' | 'FLY' | 'AWS_K8S' region: string } OrganizationSlugAvailableVersionsResponse: { @@ -6536,6 +6565,7 @@ export interface components { last4: string } created: number + has_address: boolean id: string is_default: boolean type: string @@ -7258,7 +7288,7 @@ export interface components { code: string name: string /** @enum {string} */ - provider: 'AWS' | 'AWS_K8S' | 'FLY' + provider: 'AWS' | 'FLY' | 'AWS_K8S' /** @enum {string} */ status?: 'capacity' | 'other' /** @enum {string} */ @@ -7277,7 +7307,7 @@ export interface components { code: string name: string /** @enum {string} */ - provider: 'AWS' | 'AWS_K8S' | 'FLY' + provider: 'AWS' | 'FLY' | 'AWS_K8S' /** @enum {string} */ status?: 'capacity' | 'other' /** @enum {string} */ @@ -7338,6 +7368,41 @@ export interface components { tenant_id: string }[] } + ReplicationPipelineReplicationStatusResponse: { + /** @description Pipeline id */ + pipeline_id: number + /** @description Table statuses */ + table_statuses: { + /** @description Table replication state */ + state: + | { + /** @enum {string} */ + name: 'queued' + } + | { + /** @enum {string} */ + name: 'copying_table' + } + | { + /** @enum {string} */ + name: 'copied_table' + } + | { + lag: number + /** @enum {string} */ + name: 'following_wal' + } + | { + message: string + /** @enum {string} */ + name: 'error' + } + /** @description Table id (internal Postgres OID) */ + table_id: number + /** @description Table name */ + table_name: string + }[] + } ReplicationPipelineResponse: { /** @description Pipeline config */ config: { @@ -8584,7 +8649,21 @@ export interface components { fileSizeLimit?: number } UpdateSubscriptionBody: { + address?: { + city?: string | null + country: string + line1: string + line2?: string | null + postal_code?: string | null + state?: string | null + } + billing_name?: string payment_method?: string + tax_id?: { + country: string + type: string + value: string + } /** @enum {string} */ tier: 'tier_free' | 'tier_pro' | 'tier_payg' | 'tier_team' | 'tier_enterprise' } @@ -11694,6 +11773,12 @@ export interface operations { 'application/json': components['schemas']['InvitationResponse'] } } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to get organization invitations */ 500: { headers: { @@ -11725,6 +11810,12 @@ export interface operations { } content?: never } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to create organization invitation */ 500: { headers: { @@ -12635,7 +12726,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -12673,7 +12765,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -12715,7 +12808,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -12760,7 +12854,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -12798,7 +12893,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -12844,7 +12940,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -12884,7 +12981,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -12926,7 +13024,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -12964,7 +13063,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13008,7 +13108,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13051,7 +13152,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13092,7 +13194,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13130,7 +13233,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13174,7 +13278,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13214,7 +13319,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13264,7 +13370,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13305,7 +13412,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13343,7 +13451,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13387,7 +13496,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13427,7 +13537,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13469,7 +13580,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13507,7 +13619,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13551,7 +13664,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13591,7 +13705,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13633,7 +13748,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13737,7 +13853,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13775,7 +13892,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13819,7 +13937,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13859,7 +13978,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13909,7 +14029,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13947,7 +14068,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -13993,7 +14115,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -14033,7 +14156,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -14078,7 +14202,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -14116,7 +14241,8 @@ export interface operations { query?: never header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -14160,7 +14286,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -14200,7 +14327,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -14245,7 +14373,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -14291,7 +14420,8 @@ export interface operations { } header: { 'x-connection-encrypted': string - 'x-pg-application-name': string + /** @description PostgreSQL connection application name */ + 'x-pg-application-name'?: string } path: { /** @description Project ref */ @@ -16539,6 +16669,12 @@ export interface operations { 'application/json': components['schemas']['GetUserContentResponse'] } } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to retrieve project's content */ 500: { headers: { @@ -16570,6 +16706,12 @@ export interface operations { } content?: never } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to update project's content */ 500: { headers: { @@ -16603,6 +16745,12 @@ export interface operations { 'application/json': components['schemas']['UserContentObject'] } } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to create project's content */ 500: { headers: { @@ -16634,6 +16782,12 @@ export interface operations { 'application/json': components['schemas']['BulkDeleteUserContentResponse'][] } } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to delete project's contents */ 500: { headers: { @@ -16666,6 +16820,12 @@ export interface operations { 'application/json': components['schemas']['GetContentCountV2Response'] } } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to retrieve user's content counts */ 500: { headers: { @@ -16703,6 +16863,12 @@ export interface operations { 'application/json': components['schemas']['GetUserContentFolderResponse'] } } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to retrieve project's content root folder */ 500: { headers: { @@ -16736,6 +16902,12 @@ export interface operations { 'application/json': components['schemas']['CreateUserContentFolderResponse'] } } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to create project's content folder */ 500: { headers: { @@ -16762,6 +16934,12 @@ export interface operations { } content?: never } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to delete project's content folders */ 500: { headers: { @@ -16799,6 +16977,12 @@ export interface operations { 'application/json': components['schemas']['GetUserContentFolderResponse'] } } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to retrieve project's content folder */ 500: { headers: { @@ -16832,6 +17016,12 @@ export interface operations { } content?: never } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to update project's content folder */ 500: { headers: { @@ -16862,6 +17052,12 @@ export interface operations { 'application/json': components['schemas']['GetUserContentByIdResponse'] } } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to retrieve project's content by the given id */ 500: { headers: { @@ -17994,6 +18190,12 @@ export interface operations { } content?: never } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } /** @description Failed to get project's status */ 500: { headers: { @@ -18067,16 +18269,15 @@ export interface operations { } ProjectsController_getRegions: { parameters: { - query?: never + query: { + cloud_provider: 'AWS' | 'FLY' | 'AWS_K8S' + organization_slug: string + } header?: never path?: never cookie?: never } - requestBody: { - content: { - 'application/json': components['schemas']['GetAvailableRegionsBody'] - } - } + requestBody?: never responses: { 200: { headers: { @@ -18107,6 +18308,12 @@ export interface operations { 'application/json': components['schemas']['GetProjectByFlyExtensionIdResponse'] } } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } } } ReplicationDestinationsController_getDestinations: { @@ -18571,6 +18778,44 @@ export interface operations { } } } + ReplicationPipelinesController_getPipelineReplicationStatus: { + parameters: { + query?: never + header?: never + path: { + /** @description Pipeline id */ + pipeline_id: number + /** @description Project ref */ + ref: string + } + cookie?: never + } + requestBody?: never + responses: { + /** @description Returns the pipeline replication status. */ + 200: { + headers: { + [name: string]: unknown + } + content: { + 'application/json': components['schemas']['ReplicationPipelineReplicationStatusResponse'] + } + } + 403: { + headers: { + [name: string]: unknown + } + content?: never + } + /** @description Failed to get pipeline replication status. */ + 500: { + headers: { + [name: string]: unknown + } + content?: never + } + } + } ReplicationPipelinesController_startPipeline: { parameters: { query?: never diff --git a/packages/shared-data/pricing.ts b/packages/shared-data/pricing.ts index f5cce2fb715..8f5603aa739 100644 --- a/packages/shared-data/pricing.ts +++ b/packages/shared-data/pricing.ts @@ -421,8 +421,8 @@ export const pricing: Pricing = { title: 'Max file upload size', plans: { free: '50 MB', - pro: '50 GB', - team: '50 GB', + pro: '500 GB', + team: '500 GB', enterprise: 'Custom', }, usage_based: false, diff --git a/packages/ui-patterns/package.json b/packages/ui-patterns/package.json index 7aef2c0ba5b..432bc533630 100644 --- a/packages/ui-patterns/package.json +++ b/packages/ui-patterns/package.json @@ -544,6 +544,6 @@ "vitest": "^3.0.5" }, "peerDependencies": { - "next": "*" + "next": "catalog:" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f2a902f587..20afe3bccee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,8 +25,8 @@ catalogs: specifier: ^18.3.0 version: 18.3.0 next: - specifier: 15.3.1 - version: 15.3.1 + specifier: 15.3.3 + version: 15.3.3 react: specifier: ^18.3.0 version: 18.3.1 @@ -46,6 +46,7 @@ overrides: '@tanstack/react-start-plugin>vite': ^6.2.7 esbuild: ^0.25.2 payload>image-size: 1.2.1 + '@redocly/respect-core>form-data': ^4.0.4 importers: @@ -89,34 +90,34 @@ importers: dependencies: '@payloadcms/db-postgres': specifier: 3.33.0 - version: 3.33.0(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/react@18.3.3)(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react@18.3.1)(supports-color@8.1.1) + version: 3.33.0(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/react@18.3.3)(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react@18.3.1)(supports-color@8.1.1) '@payloadcms/live-preview-react': specifier: ^3.33.0 version: 3.43.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@payloadcms/next': specifier: 3.33.0 - version: 3.33.0(@types/react@18.3.3)(graphql@16.10.0)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) + version: 3.33.0(@types/react@18.3.3)(graphql@16.11.0)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) '@payloadcms/payload-cloud': specifier: 3.33.0 - version: 3.33.0(encoding@0.1.13)(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3)) + version: 3.33.0(encoding@0.1.13)(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3)) '@payloadcms/plugin-form-builder': specifier: 3.33.0 - version: 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) + version: 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) '@payloadcms/plugin-nested-docs': specifier: 3.33.0 - version: 3.33.0(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3)) + version: 3.33.0(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3)) '@payloadcms/plugin-seo': specifier: 3.33.0 - version: 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) + version: 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) '@payloadcms/richtext-lexical': specifier: 3.33.0 - version: 3.33.0(@faceless-ui/modal@3.0.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@faceless-ui/scroll-info@2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@payloadcms/next@3.33.0(@types/react@18.3.3)(graphql@16.10.0)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3))(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)(yjs@13.6.27) + version: 3.33.0(@faceless-ui/modal@3.0.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@faceless-ui/scroll-info@2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@payloadcms/next@3.33.0(@types/react@18.3.3)(graphql@16.11.0)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3))(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)(yjs@13.6.27) '@payloadcms/storage-s3': specifier: 3.33.0 - version: 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) + version: 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) '@payloadcms/ui': specifier: 3.33.0 - version: 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) + version: 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) common: specifier: workspace:* version: link:../../packages/common @@ -131,13 +132,13 @@ importers: version: link:../../packages/eslint-config-supabase graphql: specifier: ^16.8.1 - version: 16.10.0 + version: 16.11.0 next: specifier: 'catalog:' - version: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + version: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) payload: specifier: 3.33.0 - version: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + version: 3.33.0(graphql@16.11.0)(typescript@5.7.3) react: specifier: 'catalog:' version: 18.3.1 @@ -186,10 +187,10 @@ importers: version: 1.2.0 next: specifier: 'catalog:' - version: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + version: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) next-contentlayer2: specifier: 0.4.6 - version: 0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1) + version: 0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1) next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -331,7 +332,7 @@ importers: version: 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@sentry/nextjs': specifier: ^9.15.0 - version: 9.15.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0) + version: 9.15.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0) '@supabase/supabase-js': specifier: 'catalog:' version: 2.49.3 @@ -370,10 +371,10 @@ importers: version: 2.0.0 graphql: specifier: ^16.10.0 - version: 16.10.0 + version: 16.11.0 graphql-validation-complexity: specifier: ^0.4.2 - version: 0.4.2(graphql@16.10.0) + version: 0.4.2(graphql@16.11.0) gray-matter: specifier: ^4.0.3 version: 4.0.3 @@ -439,7 +440,7 @@ importers: version: 1.0.1 next: specifier: 'catalog:' - version: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + version: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) next-mdx-remote: specifier: ^4.4.1 version: 4.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1) @@ -451,7 +452,7 @@ importers: version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nuqs: specifier: ^1.19.1 - version: 1.19.1(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)) + version: 1.19.1(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)) openai: specifier: ^4.20.1 version: 4.71.1(encoding@0.1.13)(zod@3.23.8) @@ -533,16 +534,16 @@ importers: devDependencies: '@graphiql/toolkit': specifier: ^0.9.1 - version: 0.9.1(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0) + version: 0.9.1(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0) '@graphql-codegen/cli': specifier: 5.0.5 - version: 5.0.5(@parcel/watcher@2.5.1)(@types/node@22.13.14)(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.10.0))(graphql@16.10.0)(supports-color@8.1.1)(typescript@5.5.2) + version: 5.0.5(@parcel/watcher@2.5.1)(@types/node@22.13.14)(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0)(supports-color@8.1.1)(typescript@5.5.2) '@graphql-codegen/typescript': specifier: 4.1.6 - version: 4.1.6(encoding@0.1.13)(graphql@16.10.0) + version: 4.1.6(encoding@0.1.13)(graphql@16.11.0) '@graphql-codegen/typescript-resolvers': specifier: 4.5.0 - version: 4.5.0(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.10.0))(graphql@16.10.0) + version: 4.5.0(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0) '@supabase/supa-mdx-lint': specifier: 0.3.0-alpha version: 0.3.0-alpha @@ -605,7 +606,7 @@ importers: version: 13.2.2 graphiql: specifier: ^4.0.2 - version: 4.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 4.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) openapi-types: specifier: ^12.1.3 version: 12.1.3 @@ -615,6 +616,9 @@ importers: shiki: specifier: ^3.2.1 version: 3.2.1 + shx: + specifier: ^0.4.0 + version: 0.4.0 simple-git: specifier: ^3.24.0 version: 3.24.0(supports-color@8.1.1) @@ -725,16 +729,16 @@ importers: version: 1.1.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@sentry/nextjs': specifier: ^8.52.1 - version: 8.52.1(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0) + version: 8.52.1(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0) '@std/path': specifier: npm:@jsr/std__path@^1.0.8 version: '@jsr/std__path@1.0.8' '@stripe/react-stripe-js': - specifier: ^3.1.1 - version: 3.1.1(@stripe/stripe-js@5.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^3.7.0 + version: 3.7.0(@stripe/stripe-js@7.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@stripe/stripe-js': - specifier: ^5.5.0 - version: 5.5.0 + specifier: ^7.5.0 + version: 7.5.0 '@supabase/auth-js': specifier: 'catalog:' version: 2.71.1-rc.1 @@ -771,9 +775,6 @@ importers: '@uidotdev/usehooks': specifier: ^2.4.1 version: 2.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@vercel/flags': - specifier: ^2.6.0 - version: 2.6.3(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@vercel/functions': specifier: ^2.1.0 version: 2.1.0(@aws-sdk/credential-provider-web-identity@3.830.0) @@ -866,13 +867,13 @@ importers: version: 0.52.2 next: specifier: 'catalog:' - version: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + version: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nuqs: specifier: ^2.4.1 - version: 2.4.1(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 2.4.1(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) openai: specifier: ^4.20.1 version: 4.71.1(encoding@0.1.13)(zod@3.23.8) @@ -1009,6 +1010,9 @@ importers: specifier: ^4.4.2 version: 4.4.2 devDependencies: + '@faker-js/faker': + specifier: ^9.9.0 + version: 9.9.0 '@graphql-codegen/cli': specifier: 5.0.5 version: 5.0.5(@parcel/watcher@2.5.1)(@types/node@22.13.14)(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0)(supports-color@8.1.1)(typescript@5.5.2) @@ -1140,7 +1144,7 @@ importers: version: 2.4.11(typescript@5.5.2) next-router-mock: specifier: ^0.9.13 - version: 0.9.13(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1) + version: 0.9.13(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1) postcss: specifier: ^8.5.3 version: 8.5.3 @@ -1308,10 +1312,10 @@ importers: version: 0.436.0(react@18.3.1) next: specifier: 'catalog:' - version: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + version: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) next-contentlayer2: specifier: 0.4.6 - version: 0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1) + version: 0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1) next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1564,16 +1568,16 @@ importers: version: 1.2.0 next: specifier: 'catalog:' - version: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + version: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) next-contentlayer2: specifier: 0.5.3 - version: 0.5.3(contentlayer2@0.5.3(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1) + version: 0.5.3(contentlayer2@0.5.3(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1) next-mdx-remote: specifier: ^4.4.1 version: 4.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1) next-seo: specifier: ^6.5.0 - version: 6.5.0(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 6.5.0(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1836,13 +1840,13 @@ importers: version: 0.7.9 flags: specifier: ^4.0.0 - version: 4.0.1(@opentelemetry/api@1.9.0)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 4.0.1(@opentelemetry/api@1.9.0)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) lodash: specifier: ^4.17.21 version: 4.17.21 next: specifier: 'catalog:' - version: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + version: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -2120,7 +2124,7 @@ importers: version: 0.436.0(react@18.3.1) next: specifier: 'catalog:' - version: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + version: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -2184,7 +2188,7 @@ importers: version: 6.6.3 '@testing-library/react': specifier: ^16.0.0 - version: 16.0.0(@testing-library/dom@10.1.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 16.0.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@types/lodash': specifier: 4.17.5 version: 4.17.5 @@ -2288,8 +2292,8 @@ importers: specifier: '*' version: 0.52.2 next: - specifier: '*' - version: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + specifier: 'catalog:' + version: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) next-themes: specifier: '*' version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -2398,7 +2402,7 @@ importers: version: link:../api-types next-router-mock: specifier: ^0.9.13 - version: 0.9.13(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1) + version: 0.9.13(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1) tsx: specifier: ^4.19.3 version: 4.19.3 @@ -3659,6 +3663,10 @@ packages: resolution: {integrity: sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==} engines: {node: '>=14.0.0', npm: '>=6.0.0'} + '@faker-js/faker@9.9.0': + resolution: {integrity: sha512-OEl393iCOoo/z8bMezRlJu+GlRGlsKbUAN7jKB6LhnKoqKve5DXRpalbItIIcwnCjs1k/FOPjFzcA6Qn+H+YbA==} + engines: {node: '>=18.0.0', npm: '>=9.0.0'} + '@fal-works/esbuild-plugin-global-externals@2.1.2': resolution: {integrity: sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==} @@ -4550,6 +4558,9 @@ packages: '@next/env@15.3.1': resolution: {integrity: sha512-cwK27QdzrMblHSn9DZRV+DQscHXRuJv6MydlJRpFSqJWZrTYMLzKDeyueJNN9MGd8NNiUKzDQADAf+dMLXX7YQ==} + '@next/env@15.3.3': + resolution: {integrity: sha512-OdiMrzCl2Xi0VTjiQQUK0Xh7bJHnOuET2s+3V+Y40WJBAXrJeGA3f+I8MZJ/YQ3mVGi5XGR1L66oFlgqXhQ4Vw==} + '@next/eslint-plugin-next@15.3.1': resolution: {integrity: sha512-oEs4dsfM6iyER3jTzMm4kDSbrQJq8wZw5fmT6fg2V3SMo+kgG+cShzLfEV20senZzv8VF+puNLheiGPlBGsv2A==} @@ -4564,50 +4575,50 @@ packages: '@mdx-js/react': optional: true - '@next/swc-darwin-arm64@15.3.1': - resolution: {integrity: sha512-hjDw4f4/nla+6wysBL07z52Gs55Gttp5Bsk5/8AncQLJoisvTBP0pRIBK/B16/KqQyH+uN4Ww8KkcAqJODYH3w==} + '@next/swc-darwin-arm64@15.3.3': + resolution: {integrity: sha512-WRJERLuH+O3oYB4yZNVahSVFmtxRNjNF1I1c34tYMoJb0Pve+7/RaLAJJizyYiFhjYNGHRAE1Ri2Fd23zgDqhg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.3.1': - resolution: {integrity: sha512-q+aw+cJ2ooVYdCEqZVk+T4Ni10jF6Fo5DfpEV51OupMaV5XL6pf3GCzrk6kSSZBsMKZtVC1Zm/xaNBFpA6bJ2g==} + '@next/swc-darwin-x64@15.3.3': + resolution: {integrity: sha512-XHdzH/yBc55lu78k/XwtuFR/ZXUTcflpRXcsu0nKmF45U96jt1tsOZhVrn5YH+paw66zOANpOnFQ9i6/j+UYvw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.3.1': - resolution: {integrity: sha512-wBQ+jGUI3N0QZyWmmvRHjXjTWFy8o+zPFLSOyAyGFI94oJi+kK/LIZFJXeykvgXUk1NLDAEFDZw/NVINhdk9FQ==} + '@next/swc-linux-arm64-gnu@15.3.3': + resolution: {integrity: sha512-VZ3sYL2LXB8znNGcjhocikEkag/8xiLgnvQts41tq6i+wql63SMS1Q6N8RVXHw5pEUjiof+II3HkDd7GFcgkzw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@15.3.1': - resolution: {integrity: sha512-IIxXEXRti/AulO9lWRHiCpUUR8AR/ZYLPALgiIg/9ENzMzLn3l0NSxVdva7R/VDcuSEBo0eGVCe3evSIHNz0Hg==} + '@next/swc-linux-arm64-musl@15.3.3': + resolution: {integrity: sha512-h6Y1fLU4RWAp1HPNJWDYBQ+e3G7sLckyBXhmH9ajn8l/RSMnhbuPBV/fXmy3muMcVwoJdHL+UtzRzs0nXOf9SA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@15.3.1': - resolution: {integrity: sha512-bfI4AMhySJbyXQIKH5rmLJ5/BP7bPwuxauTvVEiJ/ADoddaA9fgyNNCcsbu9SlqfHDoZmfI6g2EjzLwbsVTr5A==} + '@next/swc-linux-x64-gnu@15.3.3': + resolution: {integrity: sha512-jJ8HRiF3N8Zw6hGlytCj5BiHyG/K+fnTKVDEKvUCyiQ/0r5tgwO7OgaRiOjjRoIx2vwLR+Rz8hQoPrnmFbJdfw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@15.3.1': - resolution: {integrity: sha512-FeAbR7FYMWR+Z+M5iSGytVryKHiAsc0x3Nc3J+FD5NVbD5Mqz7fTSy8CYliXinn7T26nDMbpExRUI/4ekTvoiA==} + '@next/swc-linux-x64-musl@15.3.3': + resolution: {integrity: sha512-HrUcTr4N+RgiiGn3jjeT6Oo208UT/7BuTr7K0mdKRBtTbT4v9zJqCDKO97DUqqoBK1qyzP1RwvrWTvU6EPh/Cw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@15.3.1': - resolution: {integrity: sha512-yP7FueWjphQEPpJQ2oKmshk/ppOt+0/bB8JC8svPUZNy0Pi3KbPx2Llkzv1p8CoQa+D2wknINlJpHf3vtChVBw==} + '@next/swc-win32-arm64-msvc@15.3.3': + resolution: {integrity: sha512-SxorONgi6K7ZUysMtRF3mIeHC5aA3IQLmKFQzU0OuhuUYwpOBc1ypaLJLP5Bf3M9k53KUUUj4vTPwzGvl/NwlQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.3.1': - resolution: {integrity: sha512-3PMvF2zRJAifcRNni9uMk/gulWfWS+qVI/pagd+4yLF5bcXPZPPH2xlYRYOsUjmCJOXSTAC2PjRzbhsRzR2fDQ==} + '@next/swc-win32-x64-msvc@15.3.3': + resolution: {integrity: sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -8046,15 +8057,15 @@ packages: '@stitches/core@1.2.8': resolution: {integrity: sha512-Gfkvwk9o9kE9r9XNBmJRfV8zONvXThnm1tcuojL04Uy5uRyqg93DC83lDebl0rocZCfKSjUv+fWYtMQmEDJldg==} - '@stripe/react-stripe-js@3.1.1': - resolution: {integrity: sha512-+JzYFgUivVD7koqYV7LmLlt9edDMAwKH7XhZAHFQMo7NeRC+6D2JmQGzp9tygWerzwttwFLlExGp4rAOvD6l9g==} + '@stripe/react-stripe-js@3.7.0': + resolution: {integrity: sha512-PYls/2S9l0FF+2n0wHaEJsEU8x7CmBagiH7zYOsxbBlLIHEsqUIQ4MlIAbV9Zg6xwT8jlYdlRIyBTHmO3yM7kQ==} peerDependencies: - '@stripe/stripe-js': ^1.44.1 || ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + '@stripe/stripe-js': '>=1.44.1 <8.0.0' react: '>=16.8.0 <20.0.0' react-dom: '>=16.8.0 <20.0.0' - '@stripe/stripe-js@5.5.0': - resolution: {integrity: sha512-lkfjyAd34aeMpTKKcEVfy8IUyEsjuAT3t9EXr5yZDtdIUncnZpedl/xLV16Dkd4z+fQwixScsCCDxSMNtBOgpQ==} + '@stripe/stripe-js@7.5.0': + resolution: {integrity: sha512-Cq3KKe+G1o7PSBMbmrgpT2JgBeyH2THHr3RdIX2MqF7AnBuspIMgtZ3ktcCgP7kZsTMvnmWymr7zZCT1zeWbMw==} engines: {node: '>=12.16'} '@supabase/auth-js@2.71.1-rc.1': @@ -8421,6 +8432,10 @@ packages: resolution: {integrity: sha512-wdsYKy5zupPyLCW2Je5DLHSxSfbIp6h80WoHOQc+RPtmPGA52O9x5MJEkv92Sjonpq+poOAtUKhh1kBGAXBrNA==} engines: {node: '>=18'} + '@testing-library/dom@10.4.0': + resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} + engines: {node: '>=18'} + '@testing-library/jest-dom@6.6.3': resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} @@ -9019,24 +9034,6 @@ packages: '@usercentrics/cmp-browser-sdk@4.42.0': resolution: {integrity: sha512-/cik01TdeiYUV1+EasK83Ip05TDqmowM5tmWGfRDdaOrneSic5BedBdJHKya4pP2vwiYAtzDYVJe4BwPt2M16g==} - '@vercel/flags@2.6.3': - resolution: {integrity: sha512-knLDYS8MCc/zx4FF6KRzHQzAKdo8VQ3V0PEBI5WvRlFIhcqhcEomxHoK41/xMnNEQvUzETACHw610/i/pVwYSA==} - deprecated: This package has been renamed to flags. It offers the same functionality under a new name. Please follow the upgrade guide https://github.com/vercel/flags/blob/main/packages/flags/guides/upgrade-to-v4.md - peerDependencies: - '@sveltejs/kit': '*' - next: '*' - react: '*' - react-dom: '*' - peerDependenciesMeta: - '@sveltejs/kit': - optional: true - next: - optional: true - react: - optional: true - react-dom: - optional: true - '@vercel/functions@2.1.0': resolution: {integrity: sha512-1gSbK9zfrbJxk1JTBVERDhLi01mK3fz+gw4GjOjZwHnqs0zsBhQA70HGVtXQX/Z3BTRMfbpAEMVDfhecRw0lDA==} engines: {node: '>= 18'} @@ -9730,11 +9727,11 @@ packages: resolution: {integrity: sha512-2hCgjEmP8YLWQ130n2FerGv7rYpfBmnmp9Uy2Le1vge6X3gZIfSmEzP5QTDElFxcvVcXlEn8Aq6MU/PZygIOog==} engines: {node: '>=14.16'} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -11481,6 +11478,10 @@ packages: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} + execa@1.0.0: + resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==} + engines: {node: '>=6'} + execa@7.2.0: resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} @@ -11637,8 +11638,8 @@ packages: fastify-plugin@4.5.1: resolution: {integrity: sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==} - fastify@4.29.0: - resolution: {integrity: sha512-MaaUHUGcCgC8fXQDsDtioaCcag1fmPJ9j64vAKunqZF4aSub040ZGi/ag8NGE2714yREPOKZuHCfpPzuUD3UQQ==} + fastify@4.29.1: + resolution: {integrity: sha512-m2kMNHIG92tSNWv+Z3UeTR9AWLLuo7KctC7mlFPtMEVrfjIhmQhkQnT9v15qA/BfVq3vvj134Y0jl9SBje3jXQ==} fastq@1.18.0: resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==} @@ -11791,12 +11792,8 @@ packages: form-data-encoder@1.7.2: resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} - form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} - - form-data@4.0.2: - resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} format@0.2.2: @@ -11938,14 +11935,6 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} - - get-intrinsic@1.2.7: - resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} - engines: {node: '>= 0.4'} - get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} @@ -11969,6 +11958,10 @@ packages: resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} engines: {node: '>=10'} + get-stream@4.1.0: + resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} + engines: {node: '>=6'} + get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -12182,10 +12175,6 @@ packages: ws: optional: true - graphql@16.10.0: - resolution: {integrity: sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==} - engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - graphql@16.11.0: resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} @@ -12247,10 +12236,6 @@ packages: resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} engines: {node: '>= 0.4'} - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -12601,6 +12586,10 @@ packages: resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} engines: {node: '>=12'} + interpret@1.4.0: + resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} + engines: {node: '>= 0.10'} + intersection-observer@0.10.0: resolution: {integrity: sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==} @@ -12882,6 +12871,10 @@ packages: resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} + is-stream@1.1.0: + resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} + engines: {node: '>=0.10.0'} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -13070,9 +13063,6 @@ packages: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true - jose@5.2.1: - resolution: {integrity: sha512-qiaQhtQRw6YrOaOj0v59h3R6hUY9NvxBmmnMfKemkqYmBB0tEc97NbLP7ix44VP5p9/0YHG8Vyhzuo5YBNwviA==} - jose@5.9.6: resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==} @@ -14339,8 +14329,8 @@ packages: next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} - next@15.3.1: - resolution: {integrity: sha512-8+dDV0xNLOgHlyBxP1GwHGVaNXsmp+2NhZEYrXr24GWLHtt27YrBPbPuHvzlhi7kZNYjeJNR93IF5zfFu5UL0g==} + next@15.3.3: + resolution: {integrity: sha512-JqNj29hHNmCLtNvd090SyRbXJiivQ+58XjCcrC50Crb5g5u2zi7Y2YivbsEfzk6AtVI80akdOQbaMZwWB1Hthw==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: @@ -14514,6 +14504,10 @@ packages: engines: {node: '>= 4'} hasBin: true + npm-run-path@2.0.2: + resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} + engines: {node: '>=4'} + npm-run-path@5.3.0: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -14776,6 +14770,10 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} + p-finally@1.0.0: + resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} + engines: {node: '>=4'} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -15889,6 +15887,10 @@ packages: react: ^16.0.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + rechoir@0.6.2: + resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} + engines: {node: '>= 0.10'} + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -16420,6 +16422,11 @@ packages: shell-quote@1.8.1: resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + shelljs@0.9.2: + resolution: {integrity: sha512-S3I64fEiKgTZzKCC46zT/Ib9meqofLrQVbpSswtjFfAVDW+AZ54WTnAM/3/yENoxz/V1Cy6u3kiiEbQ4DNphvw==} + engines: {node: '>=18'} + hasBin: true + shiki@1.6.0: resolution: {integrity: sha512-P31ROeXcVgW/k3Z+vUUErcxoTah7ZRaimctOpzGuqAntqnnSmx1HOsvnbAB8Z2qfXPRhw61yptAzCsuKOhTHwQ==} @@ -16447,6 +16454,11 @@ packages: should@13.2.3: resolution: {integrity: sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==} + shx@0.4.0: + resolution: {integrity: sha512-Z0KixSIlGPpijKgcH6oCMCbltPImvaKy0sGH8AkLRXw1KyzpKtaCTizP2xen+hNDqVF4xxgvA0KXSb9o4Q6hnA==} + engines: {node: '>=18'} + hasBin: true + side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -16820,6 +16832,10 @@ packages: resolution: {integrity: sha512-p9LsUieSjWNNAxVCXLeilaDlmuUOrDS5/dF9znM1nZc7EGX5+zEFC0bEevsNIaldjlks+2jns5Siz6F9iK6jwA==} engines: {node: '>=0.10.0'} + strip-eof@1.0.0: + resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} + engines: {node: '>=0.10.0'} + strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} @@ -18493,22 +18509,6 @@ snapshots: '@types/json-schema': 7.0.15 js-yaml: 4.1.0 - '@ardatan/relay-compiler@12.0.3(encoding@0.1.13)(graphql@16.10.0)': - dependencies: - '@babel/generator': 7.27.0 - '@babel/parser': 7.27.0 - '@babel/runtime': 7.26.10 - chalk: 4.1.2 - fb-watchman: 2.0.2 - graphql: 16.10.0 - immutable: 3.7.6 - invariant: 2.2.4 - nullthrows: 1.1.1 - relay-runtime: 12.0.0(encoding@0.1.13) - signedsource: 1.0.0 - transitivePeerDependencies: - - encoding - '@ardatan/relay-compiler@12.0.3(encoding@0.1.13)(graphql@16.11.0)': dependencies: '@babel/generator': 7.27.0 @@ -20296,7 +20296,7 @@ snapshots: '@contentlayer2/core': 0.5.3(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1) '@contentlayer2/utils': 0.5.3 chokidar: 3.5.3 - fast-glob: 3.3.2 + fast-glob: 3.3.3 gray-matter: 4.0.3 imagescript: 1.3.0 micromatch: 4.0.8 @@ -20751,12 +20751,14 @@ snapshots: '@faker-js/faker@7.6.0': {} + '@faker-js/faker@9.9.0': {} + '@fal-works/esbuild-plugin-global-externals@2.1.2': {} '@fastify/ajv-compiler@3.6.0': dependencies: - ajv: 8.12.0 - ajv-formats: 2.1.1(ajv@8.12.0) + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) fast-uri: 2.4.0 '@fastify/busboy@3.1.1': {} @@ -20839,11 +20841,11 @@ snapshots: '@gar/promisify@1.1.3': {} - '@graphiql/plugin-doc-explorer@0.0.1(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@graphiql/plugin-doc-explorer@0.0.1(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@graphiql/react': 0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@graphiql/react': 0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@headlessui/react': 2.2.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - graphql: 16.10.0 + graphql: 16.11.0 react: 18.3.1 react-compiler-runtime: 19.1.0-rc.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1) @@ -20855,10 +20857,10 @@ snapshots: - '@types/react-dom' - graphql-ws - '@graphiql/plugin-history@0.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@graphiql/plugin-history@0.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@graphiql/react': 0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@graphiql/toolkit': 0.11.2(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0) + '@graphiql/react': 0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@graphiql/toolkit': 0.11.2(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0) react: 18.3.1 react-compiler-runtime: 19.1.0-rc.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1) @@ -20898,9 +20900,9 @@ snapshots: - '@types/react-dom' - graphql-ws - '@graphiql/react@0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@graphiql/react@0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@graphiql/toolkit': 0.11.2(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0) + '@graphiql/toolkit': 0.11.2(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0) '@radix-ui/react-dialog': 1.1.11(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-dropdown-menu': 2.1.12(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-tooltip': 1.2.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -20908,12 +20910,12 @@ snapshots: '@types/codemirror': 5.60.10 clsx: 1.2.1 codemirror: 5.65.15 - codemirror-graphql: 2.2.1(@codemirror/language@6.11.0)(codemirror@5.65.15)(graphql@16.10.0) + codemirror-graphql: 2.2.1(@codemirror/language@6.11.0)(codemirror@5.65.15)(graphql@16.11.0) copy-to-clipboard: 3.3.3 framer-motion: 12.9.5(@emotion/is-prop-valid@1.2.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) get-value: 3.0.1 - graphql: 16.10.0 - graphql-language-service: 5.3.1(graphql@16.10.0) + graphql: 16.11.0 + graphql-language-service: 5.3.1(graphql@16.11.0) markdown-it: 14.1.0 react: 18.3.1 react-compiler-runtime: 19.1.0-rc.1(react@18.3.1) @@ -20927,13 +20929,13 @@ snapshots: - '@types/react-dom' - graphql-ws - '@graphiql/toolkit@0.11.2(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0)': + '@graphiql/toolkit@0.11.2(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0)': dependencies: '@n1ru4l/push-pull-async-iterable-iterator': 3.2.0 - graphql: 16.10.0 + graphql: 16.11.0 meros: 1.3.0(@types/node@22.13.14) optionalDependencies: - graphql-ws: 6.0.4(graphql@16.10.0)(ws@8.18.1) + graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.18.1) transitivePeerDependencies: - '@types/node' @@ -20947,81 +20949,22 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@graphiql/toolkit@0.9.1(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0)': + '@graphiql/toolkit@0.9.1(@types/node@22.13.14)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0)': dependencies: '@n1ru4l/push-pull-async-iterable-iterator': 3.2.0 - graphql: 16.10.0 + graphql: 16.11.0 meros: 1.3.0(@types/node@22.13.14) optionalDependencies: - graphql-ws: 6.0.4(graphql@16.10.0)(ws@8.18.1) + graphql-ws: 6.0.4(graphql@16.11.0)(ws@8.18.1) transitivePeerDependencies: - '@types/node' - '@graphql-codegen/add@5.0.3(graphql@16.10.0)': - dependencies: - '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - graphql: 16.10.0 - tslib: 2.6.2 - '@graphql-codegen/add@5.0.3(graphql@16.11.0)': dependencies: '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.11.0) graphql: 16.11.0 tslib: 2.6.2 - '@graphql-codegen/cli@5.0.5(@parcel/watcher@2.5.1)(@types/node@22.13.14)(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.10.0))(graphql@16.10.0)(supports-color@8.1.1)(typescript@5.5.2)': - dependencies: - '@babel/generator': 7.27.0 - '@babel/template': 7.27.0 - '@babel/types': 7.27.0 - '@graphql-codegen/client-preset': 4.8.0(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.10.0))(graphql@16.10.0) - '@graphql-codegen/core': 4.0.2(graphql@16.10.0) - '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - '@graphql-tools/apollo-engine-loader': 8.0.20(graphql@16.10.0) - '@graphql-tools/code-file-loader': 8.1.20(graphql@16.10.0)(supports-color@8.1.1) - '@graphql-tools/git-loader': 8.0.24(graphql@16.10.0)(supports-color@8.1.1) - '@graphql-tools/github-loader': 8.0.20(@types/node@22.13.14)(graphql@16.10.0)(supports-color@8.1.1) - '@graphql-tools/graphql-file-loader': 8.0.19(graphql@16.10.0) - '@graphql-tools/json-file-loader': 8.0.18(graphql@16.10.0) - '@graphql-tools/load': 8.1.0(graphql@16.10.0) - '@graphql-tools/prisma-loader': 8.0.17(@types/node@22.13.14)(encoding@0.1.13)(graphql@16.10.0)(supports-color@8.1.1) - '@graphql-tools/url-loader': 8.0.31(@types/node@22.13.14)(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@whatwg-node/fetch': 0.10.6 - chalk: 4.1.2 - cosmiconfig: 8.3.6(typescript@5.5.2) - debounce: 1.2.1 - detect-indent: 6.1.0 - graphql: 16.10.0 - graphql-config: 5.1.4(@types/node@22.13.14)(graphql@16.10.0)(typescript@5.5.2) - inquirer: 8.2.6 - is-glob: 4.0.3 - jiti: 1.21.7 - json-to-pretty-yaml: 1.2.2 - listr2: 4.0.5 - log-symbols: 4.1.0 - micromatch: 4.0.8 - shell-quote: 1.8.1 - string-env-interpolation: 1.0.1 - ts-log: 2.2.7 - tslib: 2.8.1 - yaml: 2.4.5 - yargs: 17.7.2 - optionalDependencies: - '@parcel/watcher': 2.5.1 - transitivePeerDependencies: - - '@fastify/websocket' - - '@types/node' - - bufferutil - - cosmiconfig-toml-loader - - encoding - - enquirer - - graphql-sock - - supports-color - - typescript - - uWebSockets.js - - utf-8-validate - '@graphql-codegen/cli@5.0.5(@parcel/watcher@2.5.1)(@types/node@22.13.14)(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0)(supports-color@8.1.1)(typescript@5.5.2)': dependencies: '@babel/generator': 7.27.0 @@ -21075,26 +21018,6 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-codegen/client-preset@4.8.0(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.10.0))(graphql@16.10.0)': - dependencies: - '@babel/helper-plugin-utils': 7.26.5 - '@babel/template': 7.27.0 - '@graphql-codegen/add': 5.0.3(graphql@16.10.0) - '@graphql-codegen/gql-tag-operations': 4.0.17(encoding@0.1.13)(graphql@16.10.0) - '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - '@graphql-codegen/typed-document-node': 5.1.1(encoding@0.1.13)(graphql@16.10.0) - '@graphql-codegen/typescript': 4.1.6(encoding@0.1.13)(graphql@16.10.0) - '@graphql-codegen/typescript-operations': 4.6.0(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.10.0))(graphql@16.10.0) - '@graphql-codegen/visitor-plugin-common': 5.8.0(encoding@0.1.13)(graphql@16.10.0) - '@graphql-tools/documents': 1.0.1(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@graphql-typed-document-node/core': 3.2.0(graphql@16.10.0) - graphql: 16.10.0 - graphql-sock: 1.0.1(graphql@16.10.0) - tslib: 2.6.2 - transitivePeerDependencies: - - encoding - '@graphql-codegen/client-preset@4.8.0(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0)': dependencies: '@babel/helper-plugin-utils': 7.26.5 @@ -21115,14 +21038,6 @@ snapshots: transitivePeerDependencies: - encoding - '@graphql-codegen/core@4.0.2(graphql@16.10.0)': - dependencies: - '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - '@graphql-tools/schema': 10.0.23(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - graphql: 16.10.0 - tslib: 2.6.2 - '@graphql-codegen/core@4.0.2(graphql@16.11.0)': dependencies: '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.11.0) @@ -21131,17 +21046,6 @@ snapshots: graphql: 16.11.0 tslib: 2.6.2 - '@graphql-codegen/gql-tag-operations@4.0.17(encoding@0.1.13)(graphql@16.10.0)': - dependencies: - '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - '@graphql-codegen/visitor-plugin-common': 5.8.0(encoding@0.1.13)(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - auto-bind: 4.0.0 - graphql: 16.10.0 - tslib: 2.6.2 - transitivePeerDependencies: - - encoding - '@graphql-codegen/gql-tag-operations@4.0.17(encoding@0.1.13)(graphql@16.11.0)': dependencies: '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.11.0) @@ -21153,16 +21057,6 @@ snapshots: transitivePeerDependencies: - encoding - '@graphql-codegen/plugin-helpers@5.1.0(graphql@16.10.0)': - dependencies: - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - change-case-all: 1.0.15 - common-tags: 1.8.2 - graphql: 16.10.0 - import-from: 4.0.0 - lodash: 4.17.21 - tslib: 2.6.2 - '@graphql-codegen/plugin-helpers@5.1.0(graphql@16.11.0)': dependencies: '@graphql-tools/utils': 10.8.6(graphql@16.11.0) @@ -21173,13 +21067,6 @@ snapshots: lodash: 4.17.21 tslib: 2.6.2 - '@graphql-codegen/schema-ast@4.1.0(graphql@16.10.0)': - dependencies: - '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - graphql: 16.10.0 - tslib: 2.6.2 - '@graphql-codegen/schema-ast@4.1.0(graphql@16.11.0)': dependencies: '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.11.0) @@ -21187,17 +21074,6 @@ snapshots: graphql: 16.11.0 tslib: 2.6.2 - '@graphql-codegen/typed-document-node@5.1.1(encoding@0.1.13)(graphql@16.10.0)': - dependencies: - '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - '@graphql-codegen/visitor-plugin-common': 5.8.0(encoding@0.1.13)(graphql@16.10.0) - auto-bind: 4.0.0 - change-case-all: 1.0.15 - graphql: 16.10.0 - tslib: 2.6.2 - transitivePeerDependencies: - - encoding - '@graphql-codegen/typed-document-node@5.1.1(encoding@0.1.13)(graphql@16.11.0)': dependencies: '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.11.0) @@ -21209,18 +21085,6 @@ snapshots: transitivePeerDependencies: - encoding - '@graphql-codegen/typescript-operations@4.6.0(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.10.0))(graphql@16.10.0)': - dependencies: - '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - '@graphql-codegen/typescript': 4.1.6(encoding@0.1.13)(graphql@16.10.0) - '@graphql-codegen/visitor-plugin-common': 5.8.0(encoding@0.1.13)(graphql@16.10.0) - auto-bind: 4.0.0 - graphql: 16.10.0 - graphql-sock: 1.0.1(graphql@16.10.0) - tslib: 2.6.2 - transitivePeerDependencies: - - encoding - '@graphql-codegen/typescript-operations@4.6.0(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0)': dependencies: '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.11.0) @@ -21233,26 +21097,15 @@ snapshots: transitivePeerDependencies: - encoding - '@graphql-codegen/typescript-resolvers@4.5.0(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.10.0))(graphql@16.10.0)': + '@graphql-codegen/typescript-resolvers@4.5.0(encoding@0.1.13)(graphql-sock@1.0.1(graphql@16.11.0))(graphql@16.11.0)': dependencies: - '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - '@graphql-codegen/typescript': 4.1.6(encoding@0.1.13)(graphql@16.10.0) - '@graphql-codegen/visitor-plugin-common': 5.8.0(encoding@0.1.13)(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.11.0) + '@graphql-codegen/typescript': 4.1.6(encoding@0.1.13)(graphql@16.11.0) + '@graphql-codegen/visitor-plugin-common': 5.8.0(encoding@0.1.13)(graphql@16.11.0) + '@graphql-tools/utils': 10.8.6(graphql@16.11.0) auto-bind: 4.0.0 - graphql: 16.10.0 - graphql-sock: 1.0.1(graphql@16.10.0) - tslib: 2.6.2 - transitivePeerDependencies: - - encoding - - '@graphql-codegen/typescript@4.1.6(encoding@0.1.13)(graphql@16.10.0)': - dependencies: - '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - '@graphql-codegen/schema-ast': 4.1.0(graphql@16.10.0) - '@graphql-codegen/visitor-plugin-common': 5.8.0(encoding@0.1.13)(graphql@16.10.0) - auto-bind: 4.0.0 - graphql: 16.10.0 + graphql: 16.11.0 + graphql-sock: 1.0.1(graphql@16.11.0) tslib: 2.6.2 transitivePeerDependencies: - encoding @@ -21268,22 +21121,6 @@ snapshots: transitivePeerDependencies: - encoding - '@graphql-codegen/visitor-plugin-common@5.8.0(encoding@0.1.13)(graphql@16.10.0)': - dependencies: - '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - '@graphql-tools/optimize': 2.0.0(graphql@16.10.0) - '@graphql-tools/relay-operation-optimizer': 7.0.19(encoding@0.1.13)(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - auto-bind: 4.0.0 - change-case-all: 1.0.15 - dependency-graph: 0.11.0 - graphql: 16.10.0 - graphql-tag: 2.12.6(graphql@16.10.0) - parse-filepath: 1.0.2 - tslib: 2.6.2 - transitivePeerDependencies: - - encoding - '@graphql-codegen/visitor-plugin-common@5.8.0(encoding@0.1.13)(graphql@16.11.0)': dependencies: '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.11.0) @@ -21302,14 +21139,6 @@ snapshots: '@graphql-hive/signal@1.0.0': {} - '@graphql-tools/apollo-engine-loader@8.0.20(graphql@16.10.0)': - dependencies: - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@whatwg-node/fetch': 0.10.6 - graphql: 16.10.0 - sync-fetch: 0.6.0-2 - tslib: 2.8.1 - '@graphql-tools/apollo-engine-loader@8.0.20(graphql@16.11.0)': dependencies: '@graphql-tools/utils': 10.8.6(graphql@16.11.0) @@ -21318,14 +21147,6 @@ snapshots: sync-fetch: 0.6.0-2 tslib: 2.8.1 - '@graphql-tools/batch-execute@9.0.15(graphql@16.10.0)': - dependencies: - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@whatwg-node/promise-helpers': 1.3.1 - dataloader: 2.2.3 - graphql: 16.10.0 - tslib: 2.8.1 - '@graphql-tools/batch-execute@9.0.15(graphql@16.11.0)': dependencies: '@graphql-tools/utils': 10.8.6(graphql@16.11.0) @@ -21334,17 +21155,6 @@ snapshots: graphql: 16.11.0 tslib: 2.8.1 - '@graphql-tools/code-file-loader@8.1.20(graphql@16.10.0)(supports-color@8.1.1)': - dependencies: - '@graphql-tools/graphql-tag-pluck': 8.3.19(graphql@16.10.0)(supports-color@8.1.1) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - globby: 11.1.0 - graphql: 16.10.0 - tslib: 2.8.1 - unixify: 1.0.0 - transitivePeerDependencies: - - supports-color - '@graphql-tools/code-file-loader@8.1.20(graphql@16.11.0)(supports-color@8.1.1)': dependencies: '@graphql-tools/graphql-tag-pluck': 8.3.19(graphql@16.11.0)(supports-color@8.1.1) @@ -21356,19 +21166,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@graphql-tools/delegate@10.2.17(graphql@16.10.0)': - dependencies: - '@graphql-tools/batch-execute': 9.0.15(graphql@16.10.0) - '@graphql-tools/executor': 1.4.7(graphql@16.10.0) - '@graphql-tools/schema': 10.0.23(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@repeaterjs/repeater': 3.0.6 - '@whatwg-node/promise-helpers': 1.3.1 - dataloader: 2.2.3 - dset: 3.1.4 - graphql: 16.10.0 - tslib: 2.8.1 - '@graphql-tools/delegate@10.2.17(graphql@16.11.0)': dependencies: '@graphql-tools/batch-execute': 9.0.15(graphql@16.11.0) @@ -21382,46 +21179,18 @@ snapshots: graphql: 16.11.0 tslib: 2.8.1 - '@graphql-tools/documents@1.0.1(graphql@16.10.0)': - dependencies: - graphql: 16.10.0 - lodash.sortby: 4.7.0 - tslib: 2.8.1 - '@graphql-tools/documents@1.0.1(graphql@16.11.0)': dependencies: graphql: 16.11.0 lodash.sortby: 4.7.0 tslib: 2.8.1 - '@graphql-tools/executor-common@0.0.4(graphql@16.10.0)': - dependencies: - '@envelop/core': 5.2.3 - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - graphql: 16.10.0 - '@graphql-tools/executor-common@0.0.4(graphql@16.11.0)': dependencies: '@envelop/core': 5.2.3 '@graphql-tools/utils': 10.8.6(graphql@16.11.0) graphql: 16.11.0 - '@graphql-tools/executor-graphql-ws@2.0.5(graphql@16.10.0)': - dependencies: - '@graphql-tools/executor-common': 0.0.4(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@whatwg-node/disposablestack': 0.0.6 - graphql: 16.10.0 - graphql-ws: 6.0.4(graphql@16.10.0)(ws@8.18.3) - isomorphic-ws: 5.0.0(ws@8.18.3) - tslib: 2.8.1 - ws: 8.18.3 - transitivePeerDependencies: - - '@fastify/websocket' - - bufferutil - - uWebSockets.js - - utf-8-validate - '@graphql-tools/executor-graphql-ws@2.0.5(graphql@16.11.0)': dependencies: '@graphql-tools/executor-common': 0.0.4(graphql@16.11.0) @@ -21438,21 +21207,6 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-tools/executor-http@1.3.3(@types/node@22.13.14)(graphql@16.10.0)': - dependencies: - '@graphql-hive/signal': 1.0.0 - '@graphql-tools/executor-common': 0.0.4(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@repeaterjs/repeater': 3.0.6 - '@whatwg-node/disposablestack': 0.0.6 - '@whatwg-node/fetch': 0.10.6 - '@whatwg-node/promise-helpers': 1.3.1 - graphql: 16.10.0 - meros: 1.3.0(@types/node@22.13.14) - tslib: 2.8.1 - transitivePeerDependencies: - - '@types/node' - '@graphql-tools/executor-http@1.3.3(@types/node@22.13.14)(graphql@16.11.0)': dependencies: '@graphql-hive/signal': 1.0.0 @@ -21468,18 +21222,6 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@graphql-tools/executor-legacy-ws@1.1.17(graphql@16.10.0)': - dependencies: - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@types/ws': 8.5.10 - graphql: 16.10.0 - isomorphic-ws: 5.0.0(ws@8.18.3) - tslib: 2.8.1 - ws: 8.18.3 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - '@graphql-tools/executor-legacy-ws@1.1.17(graphql@16.11.0)': dependencies: '@graphql-tools/utils': 10.8.6(graphql@16.11.0) @@ -21492,16 +21234,6 @@ snapshots: - bufferutil - utf-8-validate - '@graphql-tools/executor@1.4.7(graphql@16.10.0)': - dependencies: - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@graphql-typed-document-node/core': 3.2.0(graphql@16.10.0) - '@repeaterjs/repeater': 3.0.6 - '@whatwg-node/disposablestack': 0.0.6 - '@whatwg-node/promise-helpers': 1.3.1 - graphql: 16.10.0 - tslib: 2.8.1 - '@graphql-tools/executor@1.4.7(graphql@16.11.0)': dependencies: '@graphql-tools/utils': 10.8.6(graphql@16.11.0) @@ -21512,18 +21244,6 @@ snapshots: graphql: 16.11.0 tslib: 2.8.1 - '@graphql-tools/git-loader@8.0.24(graphql@16.10.0)(supports-color@8.1.1)': - dependencies: - '@graphql-tools/graphql-tag-pluck': 8.3.19(graphql@16.10.0)(supports-color@8.1.1) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - graphql: 16.10.0 - is-glob: 4.0.3 - micromatch: 4.0.8 - tslib: 2.8.1 - unixify: 1.0.0 - transitivePeerDependencies: - - supports-color - '@graphql-tools/git-loader@8.0.24(graphql@16.11.0)(supports-color@8.1.1)': dependencies: '@graphql-tools/graphql-tag-pluck': 8.3.19(graphql@16.11.0)(supports-color@8.1.1) @@ -21536,20 +21256,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@graphql-tools/github-loader@8.0.20(@types/node@22.13.14)(graphql@16.10.0)(supports-color@8.1.1)': - dependencies: - '@graphql-tools/executor-http': 1.3.3(@types/node@22.13.14)(graphql@16.10.0) - '@graphql-tools/graphql-tag-pluck': 8.3.19(graphql@16.10.0)(supports-color@8.1.1) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@whatwg-node/fetch': 0.10.6 - '@whatwg-node/promise-helpers': 1.3.1 - graphql: 16.10.0 - sync-fetch: 0.6.0-2 - tslib: 2.8.1 - transitivePeerDependencies: - - '@types/node' - - supports-color - '@graphql-tools/github-loader@8.0.20(@types/node@22.13.14)(graphql@16.11.0)(supports-color@8.1.1)': dependencies: '@graphql-tools/executor-http': 1.3.3(@types/node@22.13.14)(graphql@16.11.0) @@ -21564,15 +21270,6 @@ snapshots: - '@types/node' - supports-color - '@graphql-tools/graphql-file-loader@8.0.19(graphql@16.10.0)': - dependencies: - '@graphql-tools/import': 7.0.18(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - globby: 11.1.0 - graphql: 16.10.0 - tslib: 2.8.1 - unixify: 1.0.0 - '@graphql-tools/graphql-file-loader@8.0.19(graphql@16.11.0)': dependencies: '@graphql-tools/import': 7.0.18(graphql@16.11.0) @@ -21582,19 +21279,6 @@ snapshots: tslib: 2.8.1 unixify: 1.0.0 - '@graphql-tools/graphql-tag-pluck@8.3.19(graphql@16.10.0)(supports-color@8.1.1)': - dependencies: - '@babel/core': 7.26.10(supports-color@8.1.1) - '@babel/parser': 7.27.0 - '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.26.10(supports-color@8.1.1)) - '@babel/traverse': 7.27.0(supports-color@8.1.1) - '@babel/types': 7.27.0 - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - graphql: 16.10.0 - tslib: 2.8.1 - transitivePeerDependencies: - - supports-color - '@graphql-tools/graphql-tag-pluck@8.3.19(graphql@16.11.0)(supports-color@8.1.1)': dependencies: '@babel/core': 7.26.10(supports-color@8.1.1) @@ -21608,13 +21292,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@graphql-tools/import@7.0.18(graphql@16.10.0)': - dependencies: - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - graphql: 16.10.0 - resolve-from: 5.0.0 - tslib: 2.8.1 - '@graphql-tools/import@7.0.18(graphql@16.11.0)': dependencies: '@graphql-tools/utils': 10.8.6(graphql@16.11.0) @@ -21622,14 +21299,6 @@ snapshots: resolve-from: 5.0.0 tslib: 2.8.1 - '@graphql-tools/json-file-loader@8.0.18(graphql@16.10.0)': - dependencies: - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - globby: 11.1.0 - graphql: 16.10.0 - tslib: 2.8.1 - unixify: 1.0.0 - '@graphql-tools/json-file-loader@8.0.18(graphql@16.11.0)': dependencies: '@graphql-tools/utils': 10.8.6(graphql@16.11.0) @@ -21638,14 +21307,6 @@ snapshots: tslib: 2.8.1 unixify: 1.0.0 - '@graphql-tools/load@8.1.0(graphql@16.10.0)': - dependencies: - '@graphql-tools/schema': 10.0.23(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - graphql: 16.10.0 - p-limit: 3.1.0 - tslib: 2.8.1 - '@graphql-tools/load@8.1.0(graphql@16.11.0)': dependencies: '@graphql-tools/schema': 10.0.23(graphql@16.11.0) @@ -21654,56 +21315,17 @@ snapshots: p-limit: 3.1.0 tslib: 2.8.1 - '@graphql-tools/merge@9.0.24(graphql@16.10.0)': - dependencies: - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - graphql: 16.10.0 - tslib: 2.8.1 - '@graphql-tools/merge@9.0.24(graphql@16.11.0)': dependencies: '@graphql-tools/utils': 10.8.6(graphql@16.11.0) graphql: 16.11.0 tslib: 2.8.1 - '@graphql-tools/optimize@2.0.0(graphql@16.10.0)': - dependencies: - graphql: 16.10.0 - tslib: 2.8.1 - '@graphql-tools/optimize@2.0.0(graphql@16.11.0)': dependencies: graphql: 16.11.0 tslib: 2.8.1 - '@graphql-tools/prisma-loader@8.0.17(@types/node@22.13.14)(encoding@0.1.13)(graphql@16.10.0)(supports-color@8.1.1)': - dependencies: - '@graphql-tools/url-loader': 8.0.31(@types/node@22.13.14)(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@types/js-yaml': 4.0.6 - '@whatwg-node/fetch': 0.10.6 - chalk: 4.1.2 - debug: 4.4.0(supports-color@8.1.1) - dotenv: 16.5.0 - graphql: 16.10.0 - graphql-request: 6.1.0(encoding@0.1.13)(graphql@16.10.0) - http-proxy-agent: 7.0.2(supports-color@8.1.1) - https-proxy-agent: 7.0.6(supports-color@8.1.1) - jose: 5.9.6 - js-yaml: 4.1.0 - lodash: 4.17.21 - scuid: 1.1.0 - tslib: 2.8.1 - yaml-ast-parser: 0.0.43 - transitivePeerDependencies: - - '@fastify/websocket' - - '@types/node' - - bufferutil - - encoding - - supports-color - - uWebSockets.js - - utf-8-validate - '@graphql-tools/prisma-loader@8.0.17(@types/node@22.13.14)(encoding@0.1.13)(graphql@16.11.0)(supports-color@8.1.1)': dependencies: '@graphql-tools/url-loader': 8.0.31(@types/node@22.13.14)(graphql@16.11.0) @@ -21732,15 +21354,6 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-tools/relay-operation-optimizer@7.0.19(encoding@0.1.13)(graphql@16.10.0)': - dependencies: - '@ardatan/relay-compiler': 12.0.3(encoding@0.1.13)(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - graphql: 16.10.0 - tslib: 2.8.1 - transitivePeerDependencies: - - encoding - '@graphql-tools/relay-operation-optimizer@7.0.19(encoding@0.1.13)(graphql@16.11.0)': dependencies: '@ardatan/relay-compiler': 12.0.3(encoding@0.1.13)(graphql@16.11.0) @@ -21750,13 +21363,6 @@ snapshots: transitivePeerDependencies: - encoding - '@graphql-tools/schema@10.0.23(graphql@16.10.0)': - dependencies: - '@graphql-tools/merge': 9.0.24(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - graphql: 16.10.0 - tslib: 2.8.1 - '@graphql-tools/schema@10.0.23(graphql@16.11.0)': dependencies: '@graphql-tools/merge': 9.0.24(graphql@16.11.0) @@ -21764,28 +21370,6 @@ snapshots: graphql: 16.11.0 tslib: 2.8.1 - '@graphql-tools/url-loader@8.0.31(@types/node@22.13.14)(graphql@16.10.0)': - dependencies: - '@graphql-tools/executor-graphql-ws': 2.0.5(graphql@16.10.0) - '@graphql-tools/executor-http': 1.3.3(@types/node@22.13.14)(graphql@16.10.0) - '@graphql-tools/executor-legacy-ws': 1.1.17(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@graphql-tools/wrap': 10.0.35(graphql@16.10.0) - '@types/ws': 8.5.10 - '@whatwg-node/fetch': 0.10.6 - '@whatwg-node/promise-helpers': 1.3.1 - graphql: 16.10.0 - isomorphic-ws: 5.0.0(ws@8.18.1) - sync-fetch: 0.6.0-2 - tslib: 2.8.1 - ws: 8.18.1 - transitivePeerDependencies: - - '@fastify/websocket' - - '@types/node' - - bufferutil - - uWebSockets.js - - utf-8-validate - '@graphql-tools/url-loader@8.0.31(@types/node@22.13.14)(graphql@16.11.0)': dependencies: '@graphql-tools/executor-graphql-ws': 2.0.5(graphql@16.11.0) @@ -21808,15 +21392,6 @@ snapshots: - uWebSockets.js - utf-8-validate - '@graphql-tools/utils@10.8.6(graphql@16.10.0)': - dependencies: - '@graphql-typed-document-node/core': 3.2.0(graphql@16.10.0) - '@whatwg-node/promise-helpers': 1.3.1 - cross-inspect: 1.0.1 - dset: 3.1.4 - graphql: 16.10.0 - tslib: 2.8.1 - '@graphql-tools/utils@10.8.6(graphql@16.11.0)': dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.11.0) @@ -21826,15 +21401,6 @@ snapshots: graphql: 16.11.0 tslib: 2.8.1 - '@graphql-tools/wrap@10.0.35(graphql@16.10.0)': - dependencies: - '@graphql-tools/delegate': 10.2.17(graphql@16.10.0) - '@graphql-tools/schema': 10.0.23(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@whatwg-node/promise-helpers': 1.3.1 - graphql: 16.10.0 - tslib: 2.8.1 - '@graphql-tools/wrap@10.0.35(graphql@16.11.0)': dependencies: '@graphql-tools/delegate': 10.2.17(graphql@16.11.0) @@ -21844,10 +21410,6 @@ snapshots: graphql: 16.11.0 tslib: 2.8.1 - '@graphql-typed-document-node/core@3.2.0(graphql@16.10.0)': - dependencies: - graphql: 16.10.0 - '@graphql-typed-document-node/core@3.2.0(graphql@16.11.0)': dependencies: graphql: 16.11.0 @@ -21860,8 +21422,8 @@ snapshots: '@sinclair/typebox': 0.31.28 close-with-grace: 1.3.0 crypto-js: 4.2.0 - fastify: 4.29.0 - fastify-metrics: 10.6.0(fastify@4.29.0) + fastify: 4.29.1 + fastify-metrics: 10.6.0(fastify@4.29.1) pg: 8.11.3 pg-connection-string: 2.6.2 pg-format: 1.0.4 @@ -22592,6 +22154,8 @@ snapshots: '@next/env@15.3.1': {} + '@next/env@15.3.3': {} + '@next/eslint-plugin-next@15.3.1': dependencies: fast-glob: 3.3.1 @@ -22610,28 +22174,28 @@ snapshots: '@mdx-js/loader': 2.3.0(supports-color@8.1.1)(webpack@5.94.0) '@mdx-js/react': 2.3.0(react@18.3.1) - '@next/swc-darwin-arm64@15.3.1': + '@next/swc-darwin-arm64@15.3.3': optional: true - '@next/swc-darwin-x64@15.3.1': + '@next/swc-darwin-x64@15.3.3': optional: true - '@next/swc-linux-arm64-gnu@15.3.1': + '@next/swc-linux-arm64-gnu@15.3.3': optional: true - '@next/swc-linux-arm64-musl@15.3.1': + '@next/swc-linux-arm64-musl@15.3.3': optional: true - '@next/swc-linux-x64-gnu@15.3.1': + '@next/swc-linux-x64-gnu@15.3.3': optional: true - '@next/swc-linux-x64-musl@15.3.1': + '@next/swc-linux-x64-musl@15.3.3': optional: true - '@next/swc-win32-arm64-msvc@15.3.1': + '@next/swc-win32-arm64-msvc@15.3.3': optional: true - '@next/swc-win32-x64-msvc@15.3.1': + '@next/swc-win32-x64-msvc@15.3.3': optional: true '@nodelib/fs.scandir@2.1.5': @@ -23567,14 +23131,14 @@ snapshots: '@parcel/watcher-win32-ia32': 2.5.1 '@parcel/watcher-win32-x64': 2.5.1 - '@payloadcms/db-postgres@3.33.0(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/react@18.3.3)(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react@18.3.1)(supports-color@8.1.1)': + '@payloadcms/db-postgres@3.33.0(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/react@18.3.3)(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react@18.3.1)(supports-color@8.1.1)': dependencies: - '@payloadcms/drizzle': 3.33.0(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@18.3.3)(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(pg@8.11.3)(react@18.3.1) + '@payloadcms/drizzle': 3.33.0(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@18.3.3)(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(pg@8.11.3)(react@18.3.1) '@types/pg': 8.10.2 console-table-printer: 2.12.1 drizzle-kit: 0.28.0(supports-color@8.1.1) drizzle-orm: 0.36.1(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@18.3.3)(pg@8.11.3)(react@18.3.1) - payload: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + payload: 3.33.0(graphql@16.11.0)(typescript@5.7.3) pg: 8.11.3 prompts: 2.4.2 to-snake-case: 1.0.0 @@ -23610,11 +23174,11 @@ snapshots: - sqlite3 - supports-color - '@payloadcms/drizzle@3.33.0(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@18.3.3)(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(pg@8.11.3)(react@18.3.1)': + '@payloadcms/drizzle@3.33.0(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@18.3.3)(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(pg@8.11.3)(react@18.3.1)': dependencies: console-table-printer: 2.12.1 drizzle-orm: 0.36.1(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.10.2)(@types/react@18.3.3)(pg@8.11.3)(react@18.3.1) - payload: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + payload: 3.33.0(graphql@16.11.0)(typescript@5.7.3) prompts: 2.4.2 to-snake-case: 1.0.0 uuid: 9.0.0 @@ -23649,16 +23213,16 @@ snapshots: - sql.js - sqlite3 - '@payloadcms/email-nodemailer@3.33.0(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))': + '@payloadcms/email-nodemailer@3.33.0(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))': dependencies: nodemailer: 6.9.16 - payload: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + payload: 3.33.0(graphql@16.11.0)(typescript@5.7.3) - '@payloadcms/graphql@3.33.0(graphql@16.10.0)(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(typescript@5.7.3)': + '@payloadcms/graphql@3.33.0(graphql@16.11.0)(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(typescript@5.7.3)': dependencies: - graphql: 16.10.0 - graphql-scalars: 1.22.2(graphql@16.10.0) - payload: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + graphql: 16.11.0 + graphql-scalars: 1.22.2(graphql@16.11.0) + payload: 3.33.0(graphql@16.11.0)(typescript@5.7.3) pluralize: 8.0.0 ts-essentials: 10.0.3(typescript@5.7.3) tsx: 4.19.2 @@ -23673,22 +23237,22 @@ snapshots: '@payloadcms/live-preview@3.43.0': {} - '@payloadcms/next@3.33.0(@types/react@18.3.3)(graphql@16.10.0)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)': + '@payloadcms/next@3.33.0(@types/react@18.3.3)(graphql@16.11.0)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)': dependencies: '@dnd-kit/core': 6.0.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@payloadcms/graphql': 3.33.0(graphql@16.10.0)(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(typescript@5.7.3) + '@payloadcms/graphql': 3.33.0(graphql@16.11.0)(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(typescript@5.7.3) '@payloadcms/translations': 3.33.0 - '@payloadcms/ui': 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) + '@payloadcms/ui': 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) busboy: 1.6.0 dequal: 2.0.3 file-type: 19.3.0 - graphql: 16.10.0 - graphql-http: 1.22.4(graphql@16.10.0) + graphql: 16.11.0 + graphql-http: 1.22.4(graphql@16.11.0) graphql-playground-html: 1.6.30 http-status: 2.1.0 - next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + next: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) path-to-regexp: 6.3.0 - payload: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + payload: 3.33.0(graphql@16.11.0)(typescript@5.7.3) qs-esm: 7.0.2 react-diff-viewer-continued: 4.0.5(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1) sass: 1.77.4 @@ -23701,25 +23265,25 @@ snapshots: - supports-color - typescript - '@payloadcms/payload-cloud@3.33.0(encoding@0.1.13)(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))': + '@payloadcms/payload-cloud@3.33.0(encoding@0.1.13)(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))': dependencies: '@aws-sdk/client-cognito-identity': 3.830.0 '@aws-sdk/client-s3': 3.830.0 '@aws-sdk/credential-providers': 3.830.0 '@aws-sdk/lib-storage': 3.830.0(@aws-sdk/client-s3@3.830.0) - '@payloadcms/email-nodemailer': 3.33.0(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3)) + '@payloadcms/email-nodemailer': 3.33.0(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3)) amazon-cognito-identity-js: 6.3.15(encoding@0.1.13) nodemailer: 6.9.16 - payload: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + payload: 3.33.0(graphql@16.11.0)(typescript@5.7.3) transitivePeerDependencies: - aws-crt - encoding - '@payloadcms/plugin-cloud-storage@3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)': + '@payloadcms/plugin-cloud-storage@3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)': dependencies: - '@payloadcms/ui': 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) + '@payloadcms/ui': 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) find-node-modules: 2.1.3 - payload: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + payload: 3.33.0(graphql@16.11.0)(typescript@5.7.3) range-parser: 1.2.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -23730,11 +23294,11 @@ snapshots: - supports-color - typescript - '@payloadcms/plugin-form-builder@3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)': + '@payloadcms/plugin-form-builder@3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)': dependencies: - '@payloadcms/ui': 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) + '@payloadcms/ui': 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) escape-html: 1.0.3 - payload: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + payload: 3.33.0(graphql@16.11.0)(typescript@5.7.3) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: @@ -23744,15 +23308,15 @@ snapshots: - supports-color - typescript - '@payloadcms/plugin-nested-docs@3.33.0(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))': + '@payloadcms/plugin-nested-docs@3.33.0(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))': dependencies: - payload: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + payload: 3.33.0(graphql@16.11.0)(typescript@5.7.3) - '@payloadcms/plugin-seo@3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)': + '@payloadcms/plugin-seo@3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)': dependencies: '@payloadcms/translations': 3.33.0 - '@payloadcms/ui': 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) - payload: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + '@payloadcms/ui': 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) + payload: 3.33.0(graphql@16.11.0)(typescript@5.7.3) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: @@ -23762,7 +23326,7 @@ snapshots: - supports-color - typescript - '@payloadcms/richtext-lexical@3.33.0(@faceless-ui/modal@3.0.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@faceless-ui/scroll-info@2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@payloadcms/next@3.33.0(@types/react@18.3.3)(graphql@16.10.0)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3))(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)(yjs@13.6.27)': + '@payloadcms/richtext-lexical@3.33.0(@faceless-ui/modal@3.0.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@faceless-ui/scroll-info@2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@payloadcms/next@3.33.0(@types/react@18.3.3)(graphql@16.11.0)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3))(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)(yjs@13.6.27)': dependencies: '@faceless-ui/modal': 3.0.0-beta.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@faceless-ui/scroll-info': 2.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -23776,9 +23340,9 @@ snapshots: '@lexical/selection': 0.28.0 '@lexical/table': 0.28.0 '@lexical/utils': 0.28.0 - '@payloadcms/next': 3.33.0(@types/react@18.3.3)(graphql@16.10.0)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) + '@payloadcms/next': 3.33.0(@types/react@18.3.3)(graphql@16.11.0)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) '@payloadcms/translations': 3.33.0 - '@payloadcms/ui': 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) + '@payloadcms/ui': 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) '@types/uuid': 10.0.0 acorn: 8.12.1 bson-objectid: 2.0.4 @@ -23789,7 +23353,7 @@ snapshots: mdast-util-from-markdown: 2.0.2(supports-color@8.1.1) mdast-util-mdx-jsx: 3.1.3(supports-color@8.1.1) micromark-extension-mdx-jsx: 3.0.1 - payload: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + payload: 3.33.0(graphql@16.11.0)(typescript@5.7.3) qs-esm: 7.0.2 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -23804,13 +23368,13 @@ snapshots: - typescript - yjs - '@payloadcms/storage-s3@3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)': + '@payloadcms/storage-s3@3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)': dependencies: '@aws-sdk/client-s3': 3.830.0 '@aws-sdk/lib-storage': 3.830.0(@aws-sdk/client-s3@3.830.0) '@aws-sdk/s3-request-presigner': 3.830.0 - '@payloadcms/plugin-cloud-storage': 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) - payload: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + '@payloadcms/plugin-cloud-storage': 3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3) + payload: 3.33.0(graphql@16.11.0)(typescript@5.7.3) transitivePeerDependencies: - '@types/react' - aws-crt @@ -23825,7 +23389,7 @@ snapshots: dependencies: date-fns: 4.1.0 - '@payloadcms/ui@3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.10.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)': + '@payloadcms/ui@3.33.0(@types/react@18.3.3)(monaco-editor@0.52.2)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(payload@3.33.0(graphql@16.11.0)(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1)(typescript@5.7.3)': dependencies: '@date-fns/tz': 1.2.0 '@dnd-kit/core': 6.0.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -23839,9 +23403,9 @@ snapshots: date-fns: 4.1.0 dequal: 2.0.3 md5: 2.3.0 - next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + next: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) object-to-formdata: 4.5.1 - payload: 3.33.0(graphql@16.10.0)(typescript@5.7.3) + payload: 3.33.0(graphql@16.11.0)(typescript@5.7.3) qs-esm: 7.0.2 react: 18.3.1 react-datepicker: 7.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -25581,7 +25145,7 @@ snapshots: colorette: 1.4.0 core-js: 3.35.0 dotenv: 16.5.0 - form-data: 4.0.2 + form-data: 4.0.4 get-port-please: 3.1.2 glob: 7.2.3 handlebars: 4.7.8 @@ -25660,7 +25224,7 @@ snapshots: concat-stream: 2.0.0 cookie: 0.7.2 dotenv: 16.4.5 - form-data: 4.0.0 + form-data: 4.0.4 jest-diff: 29.7.0 jest-matcher-utils: 29.7.0 js-yaml: 4.1.0 @@ -26071,7 +25635,7 @@ snapshots: '@sentry/core@9.15.0': {} - '@sentry/nextjs@8.52.1(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0)': + '@sentry/nextjs@8.52.1(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.56.0(@opentelemetry/api@1.9.0)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.28.0 @@ -26084,7 +25648,7 @@ snapshots: '@sentry/vercel-edge': 8.52.1 '@sentry/webpack-plugin': 2.22.7(encoding@0.1.13)(supports-color@8.1.1)(webpack@5.94.0) chalk: 3.0.0 - next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + next: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) resolve: 1.22.8 rollup: 3.29.5 stacktrace-parser: 0.1.10 @@ -26097,7 +25661,7 @@ snapshots: - supports-color - webpack - '@sentry/nextjs@9.15.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0)': + '@sentry/nextjs@9.15.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)(supports-color@8.1.1))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1)(supports-color@8.1.1)(webpack@5.94.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.32.0 @@ -26110,7 +25674,7 @@ snapshots: '@sentry/vercel-edge': 9.15.0 '@sentry/webpack-plugin': 3.3.1(encoding@0.1.13)(supports-color@8.1.1)(webpack@5.94.0) chalk: 3.0.0 - next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + next: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) resolve: 1.22.8 rollup: 4.35.0 stacktrace-parser: 0.1.10 @@ -26900,14 +26464,14 @@ snapshots: '@stitches/core@1.2.8': {} - '@stripe/react-stripe-js@3.1.1(@stripe/stripe-js@5.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@stripe/react-stripe-js@3.7.0(@stripe/stripe-js@7.5.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@stripe/stripe-js': 5.5.0 + '@stripe/stripe-js': 7.5.0 prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@stripe/stripe-js@5.5.0': {} + '@stripe/stripe-js@7.5.0': {} '@supabase/auth-js@2.71.1-rc.1': dependencies: @@ -27715,6 +27279,17 @@ snapshots: lz-string: 1.5.0 pretty-format: 27.5.1 + '@testing-library/dom@10.4.0': + dependencies: + '@babel/code-frame': 7.26.2 + '@babel/runtime': 7.26.10 + '@types/aria-query': 5.0.2 + aria-query: 5.3.0 + chalk: 4.1.2 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + pretty-format: 27.5.1 + '@testing-library/jest-dom@6.6.3': dependencies: '@adobe/css-tools': 4.4.0 @@ -27735,6 +27310,16 @@ snapshots: '@types/react': 18.3.3 '@types/react-dom': 18.3.0 + '@testing-library/react@16.0.0(@testing-library/dom@10.4.0)(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.26.10 + '@testing-library/dom': 10.4.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.3 + '@types/react-dom': 18.3.0 + '@testing-library/user-event@14.6.1(@testing-library/dom@10.1.0)': dependencies: '@testing-library/dom': 10.1.0 @@ -27748,7 +27333,7 @@ snapshots: '@ts-morph/common@0.19.0': dependencies: - fast-glob: 3.3.2 + fast-glob: 3.3.3 minimatch: 7.4.6 mkdirp: 2.1.6 path-browserify: 1.0.1 @@ -28020,7 +27605,7 @@ snapshots: '@types/graphql-validation-complexity@0.4.4': dependencies: - graphql: 16.10.0 + graphql: 16.11.0 '@types/hast@2.3.6': dependencies: @@ -28104,7 +27689,7 @@ snapshots: '@types/node-fetch@2.6.6': dependencies: '@types/node': 22.13.14 - form-data: 4.0.2 + form-data: 4.0.4 '@types/node@18.18.13': dependencies: @@ -28425,14 +28010,6 @@ snapshots: lz-string: 1.5.0 uuid: 9.0.1 - '@vercel/flags@2.6.3(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - jose: 5.2.1 - optionalDependencies: - next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - '@vercel/functions@2.1.0(@aws-sdk/credential-provider-web-identity@3.830.0)': optionalDependencies: '@aws-sdk/credential-provider-web-identity': 3.830.0 @@ -28887,9 +28464,9 @@ snapshots: optionalDependencies: ajv: 8.17.1 - ajv-formats@3.0.1(ajv@8.12.0): + ajv-formats@3.0.1(ajv@8.17.1): optionalDependencies: - ajv: 8.12.0 + ajv: 8.17.1 ajv-keywords@3.5.2(ajv@6.12.6): dependencies: @@ -29049,7 +28626,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.1.1 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 is-string: 1.0.7 array-timsort@1.0.3: {} @@ -29110,7 +28687,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.3 es-errors: 1.3.0 - get-intrinsic: 1.2.4 + get-intrinsic: 1.3.0 is-array-buffer: 3.0.4 is-shared-array-buffer: 1.0.3 @@ -29195,7 +28772,7 @@ snapshots: axios@1.10.0: dependencies: follow-redirects: 1.15.9 - form-data: 4.0.2 + form-data: 4.0.4 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug @@ -29327,12 +28904,12 @@ snapshots: widest-line: 4.0.1 wrap-ansi: 8.1.0 - brace-expansion@1.1.11: + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.1: + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 @@ -29457,14 +29034,14 @@ snapshots: es-define-property: 1.0.0 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 + get-intrinsic: 1.3.0 set-function-length: 1.2.2 call-bind@1.0.8: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 set-function-length: 1.2.2 call-bound@1.0.4: @@ -29747,13 +29324,13 @@ snapshots: graphql: 16.11.0 graphql-language-service: 5.2.0(graphql@16.11.0) - codemirror-graphql@2.2.1(@codemirror/language@6.11.0)(codemirror@5.65.15)(graphql@16.10.0): + codemirror-graphql@2.2.1(@codemirror/language@6.11.0)(codemirror@5.65.15)(graphql@16.11.0): dependencies: '@codemirror/language': 6.11.0 '@types/codemirror': 0.0.90 codemirror: 5.65.15 - graphql: 16.10.0 - graphql-language-service: 5.3.1(graphql@16.10.0) + graphql: 16.11.0 + graphql-language-service: 5.3.1(graphql@16.11.0) codemirror@5.65.15: {} @@ -30334,9 +29911,9 @@ snapshots: define-data-property@1.1.4: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 es-errors: 1.3.0 - gopd: 1.0.1 + gopd: 1.2.0 define-lazy-prop@2.0.0: {} @@ -30593,13 +30170,13 @@ snapshots: es-set-tostringtag: 2.1.0 es-to-primitive: 1.2.1 function.prototype.name: 1.1.6 - get-intrinsic: 1.2.4 + get-intrinsic: 1.3.0 get-symbol-description: 1.0.2 globalthis: 1.0.3 gopd: 1.0.1 has-property-descriptors: 1.0.2 has-proto: 1.0.3 - has-symbols: 1.0.3 + has-symbols: 1.1.0 hasown: 2.0.2 internal-slot: 1.0.7 is-array-buffer: 3.0.4 @@ -30686,7 +30263,7 @@ snapshots: es-define-property@1.0.0: dependencies: - get-intrinsic: 1.2.4 + get-intrinsic: 1.3.0 es-define-property@1.0.1: {} @@ -30701,7 +30278,7 @@ snapshots: es-errors: 1.3.0 es-set-tostringtag: 2.1.0 function-bind: 1.1.2 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 globalthis: 1.0.4 gopd: 1.2.0 has-property-descriptors: 1.0.2 @@ -31203,6 +30780,16 @@ snapshots: dependencies: eventsource-parser: 3.0.2 + execa@1.0.0: + dependencies: + cross-spawn: 6.0.6 + get-stream: 4.1.0 + is-stream: 1.1.0 + npm-run-path: 2.0.2 + p-finally: 1.0.0 + signal-exit: 3.0.7 + strip-eof: 1.0.0 + execa@7.2.0: dependencies: cross-spawn: 7.0.6 @@ -31346,8 +30933,8 @@ snapshots: fast-json-stringify@5.16.1: dependencies: '@fastify/merge-json-schemas': 0.1.1 - ajv: 8.12.0 - ajv-formats: 3.0.1(ajv@8.12.0) + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) fast-deep-equal: 3.1.3 fast-uri: 2.4.0 json-schema-ref-resolver: 1.0.1 @@ -31385,15 +30972,15 @@ snapshots: fastest-stable-stringify@2.0.2: {} - fastify-metrics@10.6.0(fastify@4.29.0): + fastify-metrics@10.6.0(fastify@4.29.1): dependencies: - fastify: 4.29.0 + fastify: 4.29.1 fastify-plugin: 4.5.1 prom-client: 14.2.0 fastify-plugin@4.5.1: {} - fastify@4.29.0: + fastify@4.29.1: dependencies: '@fastify/ajv-compiler': 3.6.0 '@fastify/error': 3.4.1 @@ -31529,13 +31116,13 @@ snapshots: micromatch: 4.0.8 resolve-dir: 1.0.1 - flags@4.0.1(@opentelemetry/api@1.9.0)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + flags@4.0.1(@opentelemetry/api@1.9.0)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@edge-runtime/cookies': 5.0.2 jose: 5.9.6 optionalDependencies: '@opentelemetry/api': 1.9.0 - next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + next: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -31572,17 +31159,12 @@ snapshots: form-data-encoder@1.7.2: {} - form-data@4.0.0: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - - form-data@4.0.2: + form-data@4.0.4: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 + hasown: 2.0.2 mime-types: 2.1.35 format@0.2.2: {} @@ -31735,27 +31317,6 @@ snapshots: get-caller-file@2.0.5: {} - get-intrinsic@1.2.4: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - - get-intrinsic@1.2.7: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 @@ -31782,6 +31343,10 @@ snapshots: get-stdin@8.0.0: {} + get-stream@4.1.0: + dependencies: + pump: 3.0.3 + get-stream@6.0.1: {} get-stream@8.0.1: {} @@ -31790,7 +31355,7 @@ snapshots: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 - get-intrinsic: 1.2.4 + get-intrinsic: 1.3.0 get-symbol-description@1.1.0: dependencies: @@ -31943,7 +31508,7 @@ snapshots: gopd@1.0.1: dependencies: - get-intrinsic: 1.2.4 + get-intrinsic: 1.3.0 gopd@1.2.0: {} @@ -31951,12 +31516,12 @@ snapshots: graphemer@1.4.0: {} - graphiql@4.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + graphiql@4.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@graphiql/plugin-doc-explorer': 0.0.1(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@graphiql/plugin-history': 0.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@graphiql/react': 0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - graphql: 16.10.0 + '@graphiql/plugin-doc-explorer': 0.0.1(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@graphiql/plugin-history': 0.0.2(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@graphiql/react': 0.32.0(@codemirror/language@6.11.0)(@emotion/is-prop-valid@1.2.1)(@types/node@22.13.14)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1))(graphql@16.11.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + graphql: 16.11.0 react: 18.3.1 react-compiler-runtime: 19.1.0-rc.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1) @@ -31968,28 +31533,6 @@ snapshots: - '@types/react-dom' - graphql-ws - graphql-config@5.1.4(@types/node@22.13.14)(graphql@16.10.0)(typescript@5.5.2): - dependencies: - '@graphql-tools/graphql-file-loader': 8.0.19(graphql@16.10.0) - '@graphql-tools/json-file-loader': 8.0.18(graphql@16.10.0) - '@graphql-tools/load': 8.1.0(graphql@16.10.0) - '@graphql-tools/merge': 9.0.24(graphql@16.10.0) - '@graphql-tools/url-loader': 8.0.31(@types/node@22.13.14)(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - cosmiconfig: 9.0.0(typescript@5.5.2) - graphql: 16.10.0 - jiti: 2.4.2 - minimatch: 10.0.1 - string-env-interpolation: 1.0.1 - tslib: 2.8.1 - transitivePeerDependencies: - - '@fastify/websocket' - - '@types/node' - - bufferutil - - typescript - - uWebSockets.js - - utf-8-validate - graphql-config@5.1.4(@types/node@22.13.14)(graphql@16.11.0)(typescript@5.5.2): dependencies: '@graphql-tools/graphql-file-loader': 8.0.19(graphql@16.11.0) @@ -32012,9 +31555,9 @@ snapshots: - uWebSockets.js - utf-8-validate - graphql-http@1.22.4(graphql@16.10.0): + graphql-http@1.22.4(graphql@16.11.0): dependencies: - graphql: 16.10.0 + graphql: 16.11.0 graphql-language-service@5.2.0(graphql@16.11.0): dependencies: @@ -32022,10 +31565,10 @@ snapshots: nullthrows: 1.1.1 vscode-languageserver-types: 3.17.5 - graphql-language-service@5.3.1(graphql@16.10.0): + graphql-language-service@5.3.1(graphql@16.11.0): dependencies: debounce-promise: 3.1.2 - graphql: 16.10.0 + graphql: 16.11.0 nullthrows: 1.1.1 vscode-languageserver-types: 3.17.5 @@ -32033,14 +31576,6 @@ snapshots: dependencies: xss: 1.0.15 - graphql-request@6.1.0(encoding@0.1.13)(graphql@16.10.0): - dependencies: - '@graphql-typed-document-node/core': 3.2.0(graphql@16.10.0) - cross-fetch: 3.2.0(encoding@0.1.13) - graphql: 16.10.0 - transitivePeerDependencies: - - encoding - graphql-request@6.1.0(encoding@0.1.13)(graphql@16.11.0): dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.11.0) @@ -32049,59 +31584,42 @@ snapshots: transitivePeerDependencies: - encoding - graphql-scalars@1.22.2(graphql@16.10.0): + graphql-scalars@1.22.2(graphql@16.11.0): dependencies: - graphql: 16.10.0 + graphql: 16.11.0 tslib: 2.8.1 - graphql-sock@1.0.1(graphql@16.10.0): - dependencies: - graphql: 16.10.0 - graphql-sock@1.0.1(graphql@16.11.0): dependencies: graphql: 16.11.0 - graphql-tag@2.12.6(graphql@16.10.0): - dependencies: - graphql: 16.10.0 - tslib: 2.8.1 - graphql-tag@2.12.6(graphql@16.11.0): dependencies: graphql: 16.11.0 tslib: 2.8.1 - graphql-validation-complexity@0.4.2(graphql@16.10.0): + graphql-validation-complexity@0.4.2(graphql@16.11.0): dependencies: - graphql: 16.10.0 + graphql: 16.11.0 warning: 4.0.3 graphql-ws@5.14.1(graphql@16.11.0): dependencies: graphql: 16.11.0 - graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.1): + graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.1): dependencies: - graphql: 16.10.0 + graphql: 16.11.0 optionalDependencies: ws: 8.18.1 optional: true - graphql-ws@6.0.4(graphql@16.10.0)(ws@8.18.3): - dependencies: - graphql: 16.10.0 - optionalDependencies: - ws: 8.18.3 - graphql-ws@6.0.4(graphql@16.11.0)(ws@8.18.3): dependencies: graphql: 16.11.0 optionalDependencies: ws: 8.18.3 - graphql@16.10.0: {} - graphql@16.11.0: {} gray-matter@2.1.1: @@ -32177,7 +31695,7 @@ snapshots: has-property-descriptors@1.0.2: dependencies: - es-define-property: 1.0.0 + es-define-property: 1.0.1 has-proto@1.0.3: {} @@ -32185,8 +31703,6 @@ snapshots: dependencies: dunder-proto: 1.0.1 - has-symbols@1.0.3: {} - has-symbols@1.1.0: {} has-tostringtag@1.0.2: @@ -32710,6 +32226,8 @@ snapshots: internmap@2.0.3: {} + interpret@1.4.0: {} + intersection-observer@0.10.0: {} invariant@2.2.4: @@ -32763,7 +32281,7 @@ snapshots: is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 - get-intrinsic: 1.2.4 + get-intrinsic: 1.3.0 is-array-buffer@3.0.5: dependencies: @@ -32964,6 +32482,8 @@ snapshots: dependencies: call-bound: 1.0.4 + is-stream@1.1.0: {} + is-stream@2.0.1: {} is-stream@3.0.0: {} @@ -32979,7 +32499,7 @@ snapshots: is-symbol@1.0.4: dependencies: - has-symbols: 1.0.3 + has-symbols: 1.1.0 is-symbol@1.1.1: dependencies: @@ -33105,7 +32625,7 @@ snapshots: dependencies: define-data-property: 1.1.4 es-object-atoms: 1.1.1 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 get-proto: 1.0.1 has-symbols: 1.1.0 set-function-name: 2.0.2 @@ -33155,8 +32675,6 @@ snapshots: jiti@2.4.2: {} - jose@5.2.1: {} - jose@5.9.6: {} jotai@2.8.1(@types/react@18.3.3)(react@18.3.1): @@ -33215,7 +32733,7 @@ snapshots: decimal.js: 10.5.0 domexception: 4.0.0 escodegen: 2.1.0 - form-data: 4.0.2 + form-data: 4.0.4 html-encoding-sniffer: 3.0.0 http-proxy-agent: 5.0.0(supports-color@8.1.1) https-proxy-agent: 5.0.1(supports-color@8.1.1) @@ -34778,31 +34296,31 @@ snapshots: minimatch@10.0.1: dependencies: - brace-expansion: 2.0.1 + brace-expansion: 2.0.2 minimatch@3.1.2: dependencies: - brace-expansion: 1.1.11 + brace-expansion: 1.1.12 minimatch@5.1.6: dependencies: - brace-expansion: 2.0.1 + brace-expansion: 2.0.2 minimatch@7.4.6: dependencies: - brace-expansion: 2.0.1 + brace-expansion: 2.0.2 minimatch@8.0.4: dependencies: - brace-expansion: 2.0.1 + brace-expansion: 2.0.2 minimatch@9.0.3: dependencies: - brace-expansion: 2.0.1 + brace-expansion: 2.0.2 minimatch@9.0.5: dependencies: - brace-expansion: 2.0.1 + brace-expansion: 2.0.2 minimist@1.2.8: {} @@ -34937,7 +34455,7 @@ snapshots: '@types/cookie': 0.6.0 '@types/statuses': 2.0.5 chalk: 4.1.2 - graphql: 16.10.0 + graphql: 16.11.0 headers-polyfill: 4.0.3 is-node-process: 1.2.0 outvariant: 1.4.3 @@ -34959,7 +34477,7 @@ snapshots: '@open-draft/until': 2.1.0 '@types/cookie': 0.6.0 '@types/statuses': 2.0.5 - graphql: 16.10.0 + graphql: 16.11.0 headers-polyfill: 4.0.3 is-node-process: 1.2.0 outvariant: 1.4.3 @@ -35020,12 +34538,12 @@ snapshots: neo-async@2.6.2: {} - next-contentlayer2@0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1): + next-contentlayer2@0.4.6(contentlayer2@0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1): dependencies: '@contentlayer2/core': 0.4.3(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1) '@contentlayer2/utils': 0.4.3 contentlayer2: 0.4.6(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1) - next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + next: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: @@ -35034,12 +34552,12 @@ snapshots: - markdown-wasm - supports-color - next-contentlayer2@0.5.3(contentlayer2@0.5.3(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1): + next-contentlayer2@0.5.3(contentlayer2@0.5.3(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1))(esbuild@0.25.2)(markdown-wasm@1.2.0)(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(supports-color@8.1.1): dependencies: '@contentlayer2/core': 0.5.3(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1) '@contentlayer2/utils': 0.5.3 contentlayer2: 0.5.3(esbuild@0.25.2)(markdown-wasm@1.2.0)(supports-color@8.1.1) - next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + next: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: @@ -35063,14 +34581,14 @@ snapshots: dependencies: js-yaml-loader: 1.2.2 - next-router-mock@0.9.13(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1): + next-router-mock@0.9.13(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1): dependencies: - next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + next: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) react: 18.3.1 - next-seo@6.5.0(next@15.3.1(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next-seo@6.5.0(next@15.3.3(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + next: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -35081,9 +34599,9 @@ snapshots: next-tick@1.1.0: {} - next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4): + next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4): dependencies: - '@next/env': 15.3.1 + '@next/env': 15.3.3 '@swc/counter': 0.1.3 '@swc/helpers': 0.5.15 busboy: 1.6.0 @@ -35093,14 +34611,14 @@ snapshots: react-dom: 18.3.1(react@18.3.1) styled-jsx: 5.1.6(@babel/core@7.26.10(supports-color@8.1.1))(babel-plugin-macros@3.1.0)(react@18.3.1) optionalDependencies: - '@next/swc-darwin-arm64': 15.3.1 - '@next/swc-darwin-x64': 15.3.1 - '@next/swc-linux-arm64-gnu': 15.3.1 - '@next/swc-linux-arm64-musl': 15.3.1 - '@next/swc-linux-x64-gnu': 15.3.1 - '@next/swc-linux-x64-musl': 15.3.1 - '@next/swc-win32-arm64-msvc': 15.3.1 - '@next/swc-win32-x64-msvc': 15.3.1 + '@next/swc-darwin-arm64': 15.3.3 + '@next/swc-darwin-x64': 15.3.3 + '@next/swc-linux-arm64-gnu': 15.3.3 + '@next/swc-linux-arm64-musl': 15.3.3 + '@next/swc-linux-x64-gnu': 15.3.3 + '@next/swc-linux-x64-musl': 15.3.3 + '@next/swc-win32-arm64-msvc': 15.3.3 + '@next/swc-win32-x64-msvc': 15.3.3 '@opentelemetry/api': 1.9.0 '@playwright/test': 1.53.0 sass: 1.77.4 @@ -35374,6 +34892,10 @@ snapshots: shell-quote: 1.8.1 string.prototype.padend: 3.1.5 + npm-run-path@2.0.2: + dependencies: + path-key: 2.0.1 + npm-run-path@5.3.0: dependencies: path-key: 4.0.0 @@ -35400,17 +34922,17 @@ snapshots: number-flow@0.3.7: {} - nuqs@1.19.1(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)): + nuqs@1.19.1(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)): dependencies: mitt: 3.0.1 - next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + next: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) - nuqs@2.4.1(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): + nuqs@2.4.1(next@15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): dependencies: mitt: 3.0.1 react: 18.3.1 optionalDependencies: - next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) + next: 15.3.3(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.53.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4) react-router: 7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nwsapi@2.2.20: @@ -35471,7 +34993,7 @@ snapshots: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - has-symbols: 1.0.3 + has-symbols: 1.1.0 object-keys: 1.1.1 object.assign@4.1.7: @@ -35688,6 +35210,8 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + p-finally@1.0.0: {} + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -35846,7 +35370,7 @@ snapshots: pathval@2.0.0: {} - payload@3.33.0(graphql@16.10.0)(typescript@5.7.3): + payload@3.33.0(graphql@16.11.0)(typescript@5.7.3): dependencies: '@next/env': 15.3.1 '@payloadcms/translations': 3.33.0 @@ -35861,7 +35385,7 @@ snapshots: deepmerge: 4.3.1 file-type: 19.3.0 get-tsconfig: 4.8.1 - graphql: 16.10.0 + graphql: 16.11.0 http-status: 2.1.0 image-size: 1.2.1 jose: 5.9.6 @@ -36991,6 +36515,10 @@ snapshots: tiny-invariant: 1.3.3 victory-vendor: 36.6.11 + rechoir@0.6.2: + dependencies: + resolve: 1.22.10 + redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -37005,7 +36533,7 @@ snapshots: redoc@2.4.0(core-js@3.35.0)(encoding@0.1.13)(mobx@6.11.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(supports-color@8.1.1): dependencies: '@redocly/openapi-core': 1.34.1(supports-color@8.1.1) - classnames: 2.3.2 + classnames: 2.5.1 core-js: 3.35.0 decko: 1.2.0 dompurify: 3.2.4 @@ -37020,7 +36548,7 @@ snapshots: path-browserify: 1.0.1 perfect-scrollbar: 1.5.5 polished: 4.2.2 - prismjs: 1.29.0 + prismjs: 1.30.0 prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -37499,15 +37027,15 @@ snapshots: safe-array-concat@1.1.2: dependencies: call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 isarray: 2.0.5 safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 has-symbols: 1.1.0 isarray: 2.0.5 @@ -37697,8 +37225,8 @@ snapshots: define-data-property: 1.1.4 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 has-property-descriptors: 1.0.2 set-function-name@2.0.2: @@ -37816,6 +37344,13 @@ snapshots: shell-quote@1.8.1: {} + shelljs@0.9.2: + dependencies: + execa: 1.0.0 + fast-glob: 3.3.3 + interpret: 1.4.0 + rechoir: 0.6.2 + shiki@1.6.0: dependencies: '@shikijs/core': 1.6.0 @@ -37859,6 +37394,11 @@ snapshots: should-type-adaptors: 1.1.0 should-util: 1.0.1 + shx@0.4.0: + dependencies: + minimist: 1.2.8 + shelljs: 0.9.2 + side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -38136,7 +37676,7 @@ snapshots: dependencies: '@open-draft/deferred-promise': 2.2.0 dotenv: 16.5.0 - mime-db: 1.53.0 + mime-db: 1.54.0 outvariant: 1.4.3 statuses@2.0.1: {} @@ -38204,7 +37744,7 @@ snapshots: es-abstract: 1.24.0 es-errors: 1.3.0 es-object-atoms: 1.1.1 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 gopd: 1.2.0 has-symbols: 1.1.0 internal-slot: 1.1.0 @@ -38238,7 +37778,7 @@ snapshots: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 string.prototype.trimend@1.0.8: dependencies: @@ -38257,7 +37797,7 @@ snapshots: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-object-atoms: 1.0.0 + es-object-atoms: 1.1.1 string_decoder@1.1.1: dependencies: @@ -38292,6 +37832,8 @@ snapshots: strip-color@0.1.0: {} + strip-eof@1.0.0: {} + strip-final-newline@3.0.0: {} strip-indent@3.0.0: @@ -39005,7 +38547,7 @@ snapshots: dependencies: call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.2.0 has-proto: 1.0.3 is-typed-array: 1.1.13 @@ -39022,7 +38564,7 @@ snapshots: available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.2.0 has-proto: 1.0.3 is-typed-array: 1.1.13 @@ -39040,7 +38582,7 @@ snapshots: dependencies: call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.2.0 has-proto: 1.0.3 is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 @@ -39083,7 +38625,7 @@ snapshots: dependencies: call-bind: 1.0.7 has-bigints: 1.0.2 - has-symbols: 1.0.3 + has-symbols: 1.1.0 which-boxed-primitive: 1.0.2 unbox-primitive@1.1.0: @@ -40002,7 +39544,7 @@ snapshots: available-typed-arrays: 1.0.7 call-bind: 1.0.7 for-each: 0.3.3 - gopd: 1.0.1 + gopd: 1.2.0 has-tostringtag: 1.0.2 which-typed-array@1.1.19: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 8ebffb10ef1..29c70e50264 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -8,7 +8,7 @@ catalog: '@supabase/auth-js': 2.71.1-rc.1 '@supabase/supabase-js': ^2.47.14 '@supabase/realtime-js': ^2.11.3 - next: 15.3.1 + next: 15.3.3 'react': '^18.3.0' 'react-dom': '^18.3.0' '@types/react': '^18.3.0' diff --git a/supa-mdx-lint/Rule001HeadingCase.toml b/supa-mdx-lint/Rule001HeadingCase.toml index c5afd292546..770ce4ba94f 100644 --- a/supa-mdx-lint/Rule001HeadingCase.toml +++ b/supa-mdx-lint/Rule001HeadingCase.toml @@ -124,6 +124,7 @@ may_uppercase = [ "Llamafile", "Logs Explorer", "Magic Link", + "Mailpit", "Management API", "Mixpeek", "Mixpeek Embed", diff --git a/supa-mdx-lint/Rule003Spelling.toml b/supa-mdx-lint/Rule003Spelling.toml index 449ffc5cf04..c38cdf3637e 100644 --- a/supa-mdx-lint/Rule003Spelling.toml +++ b/supa-mdx-lint/Rule003Spelling.toml @@ -204,6 +204,7 @@ allow_list = [ "Logflare", "Lua", "Mailgun", + "Mailpit", "Mailtrap", "Mansueli", "Metabase", diff --git a/turbo.json b/turbo.json index fe75e665de7..c1a8a6dc53f 100644 --- a/turbo.json +++ b/turbo.json @@ -61,6 +61,7 @@ "NEXT_PUBLIC_NODE_ENV", "NEXT_PUBLIC_GOTRUE_URL", "NEXT_PUBLIC_VERCEL_BRANCH_URL", + "NEXT_PUBLIC_GOOGLE_MAPS_KEY", "NODE_ENV", "SUPABASE_URL", // These envs are used in the packages @@ -101,7 +102,7 @@ "AI_PRO_MODEL", "AI_NORMAL_MODEL" ], - "passThroughEnv": ["CURRENT_CLI_VERSION"], + "passThroughEnv": ["CURRENT_CLI_VERSION", "VERCEL_GIT_COMMIT_SHA"], "outputs": [".next/**", "!.next/cache/**"] }, "www#build": {