diff --git a/apps/docs/content/guides/database/connecting-to-postgres.mdx b/apps/docs/content/guides/database/connecting-to-postgres.mdx index a43d4df686567..45cd80cc50aa7 100644 --- a/apps/docs/content/guides/database/connecting-to-postgres.mdx +++ b/apps/docs/content/guides/database/connecting-to-postgres.mdx @@ -93,7 +93,7 @@ The connection string looks like this: postgres://postgres.apbkobhfnmcqqzqeeqss:[YOUR-PASSWORD]@aws-0-[REGION].pooler.supabase.com:5432/postgres ``` -Get your project's Session pooler connection string from your project dashboard by clicking [Connect](/dashboard/project/_?showConnect=true). +Get your project's Session pooler connection string from your project dashboard by clicking [Connect](/dashboard/project/_?showConnect=true&method=session). ### Supavisor transaction mode @@ -111,7 +111,7 @@ The connection string looks like this: postgres://postgres.apbkobhfnmcqqzqeeqss:[YOUR-PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres ``` -Get your project's Transaction pooler connection string from your project dashboard by clicking [Connect](/dashboard/project/_?showConnect=true). +Get your project's Transaction pooler connection string from your project dashboard by clicking [Connect](/dashboard/project/_?showConnect=true&method=transaction). ## Dedicated pooler @@ -119,7 +119,7 @@ For paying customers, we provision a Dedicated Pooler ([PgBouncer](https://www.p The Dedicated Pooler ensures best performance and latency, while using up more of your project's compute resources. If your network supports IPv6 or you have the IPv4 add-on, we encourage you to use the Dedicated Pooler over the Shared Pooler. -Get your project's Dedicated pooler connection string from your project dashboard by clicking [Connect](/dashboard/project/_?showConnect=true). +Get your project's Dedicated pooler connection string from your project dashboard by clicking [Connect](/dashboard/project/_?showConnect=true&method=transaction). diff --git a/apps/docs/content/guides/database/connecting-to-postgres/serverless-drivers.mdx b/apps/docs/content/guides/database/connecting-to-postgres/serverless-drivers.mdx index 4f2cf3f9fbb73..eac4f7bf93acf 100644 --- a/apps/docs/content/guides/database/connecting-to-postgres/serverless-drivers.mdx +++ b/apps/docs/content/guides/database/connecting-to-postgres/serverless-drivers.mdx @@ -64,7 +64,7 @@ Choose one of these Vercel Deploy Templates which use our [Vercel Deploy Integra ### Manual configuration -In your [`Database Settings`](/dashboard/project/_?showConnect=true) and copy the URI from the `Transaction pooler` section and save it as the `POSTGRES_URL` environment variable. Remember to replace the password placeholder with your actual database password and add the following suffix `?workaround=supabase-pooler.vercel`. +In your [`Database Settings`](/dashboard/project/_?showConnect=true&method=transaction) and copy the URI from the `Transaction pooler` section and save it as the `POSTGRES_URL` environment variable. Remember to replace the password placeholder with your actual database password and add the following suffix `?workaround=supabase-pooler.vercel`. ```txt .env.local POSTGRES_URL="postgres://postgres.cfcxynqnhdybqtbhjemm:[YOUR-PASSWORD]@aws-0-ap-southeast-1.pooler.supabase.com:6543/postgres?workaround=supabase-pooler.vercel" diff --git a/apps/docs/content/guides/database/postgres-js.mdx b/apps/docs/content/guides/database/postgres-js.mdx index 325643300ec13..780f1acf58803 100644 --- a/apps/docs/content/guides/database/postgres-js.mdx +++ b/apps/docs/content/guides/database/postgres-js.mdx @@ -36,7 +36,7 @@ hideToc: true Create a `db.js` file with the connection details. - To get your connection details, go to the [**Connect** panel](/dashboard/project/_?showConnect=true). Choose **Transaction pooler** if you're on a platform with transient connections, such as a serverless function, and **Session pooler** if you have a long-lived connection. Copy the URI and save it as the environment variable `DATABASE_URL`. + To get your connection details, go to the [**Connect** panel](/dashboard/project/_?showConnect=true). Choose [**Transaction pooler**](/dashboard/project/_?showConnect=true&method=transaction) if you're on a platform with transient connections, such as a serverless function, and [**Session pooler**](/dashboard/project/_?showConnect=true&method=session) if you have a long-lived connection. Copy the URI and save it as the environment variable `DATABASE_URL`. diff --git a/apps/docs/content/guides/database/psql.mdx b/apps/docs/content/guides/database/psql.mdx index d3af3e94a084e..493d11f198ba0 100644 --- a/apps/docs/content/guides/database/psql.mdx +++ b/apps/docs/content/guides/database/psql.mdx @@ -17,7 +17,7 @@ You can obtain your connection info and Server root certificate from your applic Download your [SSL certificate](#connecting-with-ssl) to `/path/to/prod-supabase.cer`. -Find your connection settings. Go to the project [**Connect** panel](/dashboard/project/_?showConnect=true) and copy the URL from the `Session pooler` section, and copy the parameters into the connection string: +Find your connection settings. Go to the project [**Connect** panel](/dashboard/project/_?showConnect=true&method=session) and copy the URL from the `Session pooler` section, and copy the parameters into the connection string: ```shell psql "sslmode=verify-full sslrootcert=/path/to/prod-supabase.cer host=[CLOUD_PROVIDER]-0-[REGION].pooler.supabase.com dbname=postgres user=postgres.[PROJECT_REF]" diff --git a/apps/docs/content/guides/deployment/branching/github-integration.mdx b/apps/docs/content/guides/deployment/branching/github-integration.mdx index 48d0aeb7207de..84622862a5c11 100644 --- a/apps/docs/content/guides/deployment/branching/github-integration.mdx +++ b/apps/docs/content/guides/deployment/branching/github-integration.mdx @@ -37,7 +37,7 @@ You will be using the [Supabase CLI](/docs/guides/cli) to initialise your local - Pull your database changes using `supabase db pull`. To get your database connection string, go to your project dashboard, click [Connect](/dashboard/project/_?showConnect=true) and look for the Session pooler connection string. + Pull your database changes using `supabase db pull`. To get your database connection string, go to your project dashboard, click [Connect](/dashboard/project/_?showConnect=true&method=session) and look for the Session pooler connection string. ```markdown supabase db pull --db-url diff --git a/apps/docs/content/guides/getting-started/quickstarts/laravel.mdx b/apps/docs/content/guides/getting-started/quickstarts/laravel.mdx index 0409238ad8144..381caecf2e5d8 100644 --- a/apps/docs/content/guides/getting-started/quickstarts/laravel.mdx +++ b/apps/docs/content/guides/getting-started/quickstarts/laravel.mdx @@ -49,7 +49,7 @@ hideToc: true Go to [database.new](https://database.new) and create a new Supabase project. Save your database password securely. - When your project is up and running, navigate to your project dashboard and click on [Connect](/dashboard/project/_?showConnect=true). + When your project is up and running, navigate to your project dashboard and click on [Connect](/dashboard/project/_?showConnect=true&method=session). Look for the Session Pooler connection string and copy the string. You will need to replace the Password with your saved database password. You can reset your database password in your [Database Settings](/dashboard/project/_/database/settings) if you do not have it. diff --git a/apps/docs/content/guides/getting-started/quickstarts/redwoodjs.mdx b/apps/docs/content/guides/getting-started/quickstarts/redwoodjs.mdx index 144617f30a2c8..7f6a4f66a687a 100644 --- a/apps/docs/content/guides/getting-started/quickstarts/redwoodjs.mdx +++ b/apps/docs/content/guides/getting-started/quickstarts/redwoodjs.mdx @@ -27,7 +27,7 @@ hideToc: true - Open the project [**Connect** panel](/dashboard/project/_?showConnect=true). This quickstart connects using the **Transaction pooler** and **Session pooler** mode. Transaction mode is used for application queries and Session mode is used for running migrations with Prisma. + Open the project [**Connect** panel](/dashboard/project/_?showConnect=true). This quickstart connects using the [**Transaction pooler**](/dashboard/project/_?showConnect=true&method=transaction) and [**Session pooler**](/dashboard/project/_?showConnect=true&method=session) mode. Transaction mode is used for application queries and Session mode is used for running migrations with Prisma. To do this, set the connection mode to `Transaction` in the [Database Settings page](/dashboard/project/_/database/settings) and copy the connection string and append `?pgbouncer=true&&connection_limit=1`. `pgbouncer=true` disables Prisma from generating prepared statements. This is required since our connection pooler does not support prepared statements in transaction mode yet. The `connection_limit=1` parameter is only required if you are using Prisma from a serverless environment. This is the Transaction mode connection string. diff --git a/apps/docs/content/guides/getting-started/quickstarts/ruby-on-rails.mdx b/apps/docs/content/guides/getting-started/quickstarts/ruby-on-rails.mdx index f2ffcf4ffb993..bec7d960cfd9e 100644 --- a/apps/docs/content/guides/getting-started/quickstarts/ruby-on-rails.mdx +++ b/apps/docs/content/guides/getting-started/quickstarts/ruby-on-rails.mdx @@ -31,7 +31,7 @@ hideToc: true Go to [database.new](https://database.new) and create a new Supabase project. Save your database password securely. - When your project is up and running, navigate to your project dashboard and click on [Connect](/dashboard/project/_?showConnect=true). + When your project is up and running, navigate to your project dashboard and click on [Connect](/dashboard/project/_?showConnect=true&method=session). Look for the Session Pooler connection string and copy the string. You will need to replace the Password with your saved database password. You can reset your database password in your [Database Settings](/dashboard/project/_/database/settings) if you do not have it. diff --git a/apps/docs/content/guides/platform/migrating-to-supabase/amazon-rds.mdx b/apps/docs/content/guides/platform/migrating-to-supabase/amazon-rds.mdx index 0acb893a7be33..eeba81443d129 100644 --- a/apps/docs/content/guides/platform/migrating-to-supabase/amazon-rds.mdx +++ b/apps/docs/content/guides/platform/migrating-to-supabase/amazon-rds.mdx @@ -23,7 +23,7 @@ Supabase's core is Postgres, enabling the use of row-level security and providin ## Retrieve your Supabase host [#retrieve-supabase-host] 1. If you're new to Supabase, [create a project](https://database.new). Make a note of your password, you will need this later. If you forget it, you can [reset it here](/dashboard/project/_/database/settings). -1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true) +1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true&method=session) 1. Under the Session pooler, click on the View parameters under the connect string. Note your Host (`$SUPABASE_HOST`). ![Finding Supabase host address](/docs/img/guides/resources/migrating-to-supabase/amazon-rds/database-settings-host.png) diff --git a/apps/docs/content/guides/platform/migrating-to-supabase/firebase-auth.mdx b/apps/docs/content/guides/platform/migrating-to-supabase/firebase-auth.mdx index ea79e0446a801..5323e90b9f93f 100644 --- a/apps/docs/content/guides/platform/migrating-to-supabase/firebase-auth.mdx +++ b/apps/docs/content/guides/platform/migrating-to-supabase/firebase-auth.mdx @@ -30,7 +30,7 @@ Supabase provides several [tools](https://github.com/supabase-community/firebase } ``` -1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true) +1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true&method=session) 1. Under the Session pooler, click on the View parameters under the connect string. Replace the `Host` and `User` fields with the values shown. 1. Enter the password you used when you created your Supabase project in the `password` entry in the `supabase-service.json` file. 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 cc7f823dd23b2..de95b7aee8b7d 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 @@ -29,7 +29,7 @@ The Firestore `collection` is "flattened" and converted to a table with basic co } ``` -1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true) +1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true&method=session) 1. Under the Session pooler, click on the View parameters under the connect string. Replace the `Host` and `User` fields with the values shown. 1. Enter the password you used when you created your Supabase project in the `password` entry in the `supabase-service.json` file. diff --git a/apps/docs/content/guides/platform/migrating-to-supabase/heroku.mdx b/apps/docs/content/guides/platform/migrating-to-supabase/heroku.mdx index d7e813db43609..f361f224f8dcd 100644 --- a/apps/docs/content/guides/platform/migrating-to-supabase/heroku.mdx +++ b/apps/docs/content/guides/platform/migrating-to-supabase/heroku.mdx @@ -35,7 +35,7 @@ Alternatively, use the [Heroku to Supabase migration tool](https://migrate.supab ## Retrieve your Supabase connection string [#retrieve-supabase-connection-string] 1. If you're new to Supabase, [create a project](/dashboard). -1. Get your project's Session pooler connection string from your project dashboard by clicking [Connect](/dashboard/project/_?showConnect=true). +1. Get your project's Session pooler connection string from your project dashboard by clicking [Connect](/dashboard/project/_?showConnect=true&method=session). 1. Replace [YOUR-PASSWORD] in the connection string with your database password. You can reset your database password on the [Database Settings page](/dashboard/project/_/database/settings) if you do not have it. ## Export your Heroku database to a file [#export-heroku-database] diff --git a/apps/docs/content/guides/platform/migrating-to-supabase/mssql.mdx b/apps/docs/content/guides/platform/migrating-to-supabase/mssql.mdx index 1b2feca670de2..e555d0b662210 100644 --- a/apps/docs/content/guides/platform/migrating-to-supabase/mssql.mdx +++ b/apps/docs/content/guides/platform/migrating-to-supabase/mssql.mdx @@ -22,7 +22,7 @@ Before you begin the migration, you need to collect essential information about 1. If you're new to Supabase, [create a project](/dashboard). Make a note of your password, you will need this later. If you forget it, you can [reset it here](/dashboard/project/_/database/settings). -1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true) +1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true&method=session) 1. Under the Session pooler, click on the View parameters under the connect string. Note your Host (`$SUPABASE_HOST`). ![Finding Supabase host address](/docs/img/guides/resources/migrating-to-supabase/mssql/database-settings-host.png) diff --git a/apps/docs/content/guides/platform/migrating-to-supabase/mysql.mdx b/apps/docs/content/guides/platform/migrating-to-supabase/mysql.mdx index e5b63842e1ab3..cbd94f07fdfbe 100644 --- a/apps/docs/content/guides/platform/migrating-to-supabase/mysql.mdx +++ b/apps/docs/content/guides/platform/migrating-to-supabase/mysql.mdx @@ -23,7 +23,7 @@ Before you begin the migration, you need to collect essential information about 1. If you're new to Supabase, [create a project](/dashboard). Make a note of your password, you will need this later. If you forget it, you can [reset it here](/dashboard/project/_/database/settings). -1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true) +1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true&method=session) 1. Under the Session pooler, click on the View parameters under the connect string. Note your Host (`$SUPABASE_HOST`). ![Finding Supabase host address](/docs/img/guides/resources/migrating-to-supabase/mysql/database-settings-host.png) diff --git a/apps/docs/content/guides/platform/migrating-to-supabase/neon.mdx b/apps/docs/content/guides/platform/migrating-to-supabase/neon.mdx index 08422bdd83c0b..73907c736ea63 100644 --- a/apps/docs/content/guides/platform/migrating-to-supabase/neon.mdx +++ b/apps/docs/content/guides/platform/migrating-to-supabase/neon.mdx @@ -34,7 +34,7 @@ export OLD_DB_URL="postgresql://neondb_owner:xxxxxxxxxxxxxxx-random-word-yyyyyyy 1. If you're new to Supabase, [create a project](/dashboard). Make a note of your password, you will need this later. If you forget it, you can [reset it here](/dashboard/project/_/database/settings). -1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true) +1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true&method=session) 1. Under the Session pooler, click the **Copy** button to the right of your connection string to copy it to the clipboard. ## Set your `NEW_DB_URL` environment variable diff --git a/apps/docs/content/guides/platform/migrating-to-supabase/render.mdx b/apps/docs/content/guides/platform/migrating-to-supabase/render.mdx index c44934d64bfae..1e12f8e6ca286 100644 --- a/apps/docs/content/guides/platform/migrating-to-supabase/render.mdx +++ b/apps/docs/content/guides/platform/migrating-to-supabase/render.mdx @@ -29,7 +29,7 @@ Example: 1. If you're new to Supabase, [create a project](/dashboard). Make a note of your password, you will need this later. If you forget it, you can [reset it here](/dashboard/project/_/database/settings). -1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true) +1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true&method=session) 1. Under Session pooler, Copy the connection string and replace the password placeholder with your database password. diff --git a/apps/docs/content/guides/platform/migrating-to-supabase/vercel-postgres.mdx b/apps/docs/content/guides/platform/migrating-to-supabase/vercel-postgres.mdx index 9c269ba4b7cf3..dbd73ffa4006e 100644 --- a/apps/docs/content/guides/platform/migrating-to-supabase/vercel-postgres.mdx +++ b/apps/docs/content/guides/platform/migrating-to-supabase/vercel-postgres.mdx @@ -41,7 +41,7 @@ export OLD_DB_URL="postgres://default:xxxxxxxxxxxx@yy-yyyyy-yyyyyy-yyyyyyy.us-we 1. If you're new to Supabase, [create a project](/dashboard). Make a note of your password, you will need this later. If you forget it, you can [reset it here](/dashboard/project/_/database/settings). -1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true) +1. On your project dashboard, click [Connect](/dashboard/project/_?showConnect=true&method=session) 1. Under the Session pooler, click the **Copy** button to the right of your connection string to copy it to the clipboard. ## Set your `NEW_DB_URL` environment variable 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 41dcab252ec01..a012aaa51aac2 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 @@ -25,7 +25,7 @@ breadcrumb: 'Migrations' - Use the Session pooler connection string by default. If your ISP supports IPv6 or you have the IPv4 add-on enabled, use the direct connection string. + Use the [Session pooler](/dashboard/project/_?showConnect=true&method=session) connection string by default. If your ISP supports IPv6 or you have the IPv4 add-on enabled, use the direct connection string. diff --git a/apps/docs/content/guides/platform/migrating-within-supabase/dashboard-restore.mdx b/apps/docs/content/guides/platform/migrating-within-supabase/dashboard-restore.mdx index 3a10343abb44b..476c4a9aa87ce 100644 --- a/apps/docs/content/guides/platform/migrating-within-supabase/dashboard-restore.mdx +++ b/apps/docs/content/guides/platform/migrating-within-supabase/dashboard-restore.mdx @@ -68,7 +68,7 @@ Here are some things that are not stored directly in your database and will requ - Use the Session pooler connection string by default. If your ISP supports IPv6 or you have the IPv4 add-on enabled, use the direct connection string. + Use the [Session pooler](/dashboard/project/_?showConnect=true&method=session) connection string by default. If your ISP supports IPv6 or you have the IPv4 add-on enabled, use the direct connection string. diff --git a/apps/docs/content/troubleshooting/avoiding-timeouts-in-long-running-queries-6nmbdN.mdx b/apps/docs/content/troubleshooting/avoiding-timeouts-in-long-running-queries-6nmbdN.mdx index ba6976c940ad8..1c370233dd1f9 100644 --- a/apps/docs/content/troubleshooting/avoiding-timeouts-in-long-running-queries-6nmbdN.mdx +++ b/apps/docs/content/troubleshooting/avoiding-timeouts-in-long-running-queries-6nmbdN.mdx @@ -33,7 +33,7 @@ sudo apt-get update sudo apt-get install postgresql-client ``` -Once installed, you can find your PSQL string on the dashboard by clicking [connect](/dashboard/project/_?showConnect=true). Make sure if you are using the pooler connection that it is the Session pooler (port 5432). +Once installed, you can find your PSQL string on the dashboard by clicking [connect](/dashboard/project/_?showConnect=true). Make sure if you are using the pooler connection that it is the [Session pooler](/dashboard/project/_?showConnect=true&method=session) (port 5432). diff --git a/apps/docs/content/troubleshooting/edge-function-shutdown-reasons-explained.mdx b/apps/docs/content/troubleshooting/edge-function-shutdown-reasons-explained.mdx new file mode 100644 index 0000000000000..67816d99b72ba --- /dev/null +++ b/apps/docs/content/troubleshooting/edge-function-shutdown-reasons-explained.mdx @@ -0,0 +1,251 @@ +--- +title = "Edge Function shutdown reasons explained" +topics = [ "functions" ] +keywords = [ "shutdown", "termination", "event loop", "wall clock", "cpu time", "memory", "early drop" ] +database_id = "" + +[[errors]] +http_status_code = 546 +message = "Edge Function 'wall clock time limit reached'" + +[[errors]] +message = "Edge Function terminated due to memory limit" + +[[errors]] +message = "Edge Function terminated due to CPU limit" +--- + +Learn about the different reasons why an Edge Function (worker) might shut down and how to handle each scenario effectively. + +## Understanding shutdown events + +When an Edge Function stops executing, the runtime emits a `ShutdownEvent` with a specific `reason` that explains why the worker ended. Understanding these shutdown reasons helps you diagnose issues, optimize your functions, and build more resilient serverless applications. + +These events are surfaced through logs and observability tools, allowing you to monitor function behavior and take appropriate action such as implementing retries, adjusting resource limits, or optimizing your code. + +## Shutdown reasons + +### EventLoopCompleted + +**What it means:** The function's event loop finished naturally. There are no more pending tasks, timers, or microtasks. The worker completed all scheduled work successfully. + +**When it happens:** This is the normal, graceful shutdown scenario. All synchronous code has executed, and all awaited promises have resolved. + +**What to do:** Nothing special. This indicates successful completion. No retry is needed. + +### WallClockTime + +**What it means:** The worker exceeded the configured wall-clock timeout. This measures the total elapsed real time from when the function started, including time spent waiting for I/O, external API calls, and sleeps. + +**When it happens:** Your function is taking too long to complete, regardless of how much actual computing it's doing. Currently, the wall clock limit is set at 400 seconds. + +**What to do:** + +- Break down long-running tasks into smaller functions +- Use streaming responses for operations that take time +- Consider moving long-running work to background jobs or queues +- Ensure your code handles partial completion gracefully +- Make operations idempotent so they can safely retry from the beginning + +**Related:** See [Edge Function 'wall clock time limit reached'](./edge-function-wall-clock-time-limit-reached-Nk38bW) for more details. + +### CPUTime + +**What it means:** The worker consumed more CPU time than allowed. CPU time measures actual processing cycles used by your code, excluding time spent waiting for I/O or sleeping. Currently limited to 200 milliseconds. + +**When it happens:** Your function is performing too much computation. This includes complex calculations, data processing, encryption, or other CPU-intensive operations. + +**What to do:** + +- Profile your code to identify CPU-intensive sections +- Optimize algorithms for better performance +- Consider caching computed results +- Move heavy processing to background jobs or external services +- Break large datasets into smaller chunks +- Use more efficient data structures + +### Memory + +**What it means:** The worker's memory usage exceeded the allowed limit. The `ShutdownEvent` includes detailed memory data showing total memory, heap usage, and external allocations. + +**When it happens:** Your function is consuming too much RAM. This commonly happens when buffering large files, loading entire datasets into memory, or creating many objects without cleanup. + +**What to do:** + +- Use streaming instead of buffering entire files or responses +- Process data in chunks rather than loading everything at once +- Be mindful of closures and variables that might prevent garbage collection +- Avoid accumulating large arrays or objects +- Review the `memory_used` fields in logs to identify whether heap or external memory is the issue + +### EarlyDrop + +**What it means:** The runtime detected that the function has completed all its work and can be shut down early, before reaching any resource limits. This is actually the most common shutdown reason and typically indicates efficient function execution. + +**When it happens:** Your function has finished processing the request, sent the response, and has no remaining async work (pending promises, timers, or callbacks). The runtime recognizes that the worker can be safely terminated without waiting for timeouts or other limits. + +**Why this is good:** `EarlyDrop` means your function is running efficiently. It completed quickly, didn't exhaust resources, and the runtime could reclaim the worker for other requests. Most well-designed functions should end with `EarlyDrop`. + +**What to do:** + +- Nothing: this is the desired outcome for most functions +- If you're seeing `EarlyDrop` but expected more work to happen, check for: + - Promises that weren't properly awaited + - Event listeners or timers that weren't cleaned up + - Background tasks that should have completed but didn't +- If you intentionally have background work that should continue after the response, make sure those promises are awaited before the function returns + +### TerminationRequested + +**What it means:** An external request explicitly asked the runtime to terminate the worker. This could be from orchestration systems, manual intervention, platform updates, deployments, or a user-initiated cancellation. + +**When it happens:** The platform needs to stop your function immediately, often during deployments or infrastructure maintenance. + +**What to do:** + +- Implement graceful cleanup code, but expect it might not always run +- Design for abrupt termination. Use durable storage to persist work in progress +- Make operations atomic or use transactions where possible +- Track execution state externally so incomplete work can be detected and resumed +- Test your function's behavior when forcibly stopped + +## Additional diagnostic events + +While not shutdown reasons themselves, these events provide important context: + +- **BootEvent / BootFailure:** Indicates whether a worker started successfully or failed during initialization +- **UncaughtException:** Signals an unhandled error with the exception message and CPU time used +- **LogEvent:** Runtime-emitted logs with severity levels (Debug, Info, Warning, Error) +- **WorkerMemoryUsed:** Detailed memory snapshot including total, heap, external memory, and memory checker data + +## Shutdown event metadata + +Each `ShutdownEvent` includes valuable diagnostic information: + +- `reason`: One of the shutdown reasons described above +- `cpu_time_used`: Amount of CPU time consumed (in milliseconds) +- `memory_used`: Memory snapshot at shutdown with breakdown of total, heap, and external memory +- `execution_id`: Unique identifier for tracking this specific execution across logs and retries + +## Best practices for resilient functions + +### 1. Design for idempotency + +Make your functions safe to run multiple times with the same input. Use execution IDs from metadata to detect duplicate runs and avoid repeating side effects. + +```typescript +// Store execution_id to detect retries +const executionId = Deno.env.get('EXECUTION_ID') +const alreadyProcessed = await checkIfProcessed(executionId) + +if (alreadyProcessed) { + return new Response('Already processed', { status: 200 }) +} +``` + +### 2. Implement checkpointing. + +Save progress frequently to durable storage + +```typescript +// Save progress incrementally +for (const batch of dataBatches) { + await processBatch(batch) + await saveProgress(batchId) +} +``` + +### 3. Use streaming for large data + +Avoid loading entire files or responses into memory. Stream data to reduce memory footprint. + +```typescript +// Stream responses instead of buffering +return new Response(readableStream, { + headers: { 'Content-Type': 'application/json' }, +}) +``` + +### 4. Monitor and alert + +Track shutdown reasons in your observability system. Set up alerts for: + +- Frequent `Memory` shutdowns: investigate memory usage patterns +- Frequent `CPUTime` shutdowns: optimize computational work +- Frequent `WallClockTime` shutdowns: reduce latency or break up work +- Frequent `EarlyDrop` or `TerminationRequested`: check platform scaling and deployment patterns + +Access your function logs at [Functions Logs](/dashboard/project/_/functions). + +### 5. Handle cleanup gracefully + +While you can't always rely on cleanup code running, implement it anyway for the cases where graceful shutdown is possible. + +```typescript +// Cleanup handler (may not always run) +addEventListener('unload', () => { + // Close connections, flush buffers, etc. + cleanup() +}) +``` + +## Example shutdown event payloads + +**Normal completion:** + +```json +{ + "event": { + "Shutdown": { + "reason": "EventLoopCompleted", + "cpu_time_used": 12, + "memory_used": { + "total": 1048576, + "heap": 512000, + "external": 1000 + } + } + }, + "metadata": { + "execution_id": "4b6a4e2e-7c4d-4f8b-9e1a-2d3c4e5f6a7b" + } +} +``` + +**Wall-clock timeout:** + +```json +{ + "event": { + "Shutdown": { + "reason": "WallClockTime", + "cpu_time_used": 50, + "memory_used": { + "total": 2097152, + "heap": 1024000, + "external": 5000 + } + } + }, + "metadata": { + "execution_id": "5c7b5f3f-8d5e-5g9c-0f2b-3e4d5f6g7h8i" + } +} +``` + +## Troubleshooting checklist + +Use this quick reference when investigating shutdown issues: + +| Shutdown Reason | Primary Action | +| ---------------------------------------------- | --------------------------------------------------------------------------------------- | +| Many `Memory` shutdowns | Switch to streaming; process data in chunks; investigate heap vs external allocations | +| Many `CPUTime` shutdowns | Optimize algorithms; cache results; move heavy compute to background workers | +| Many `WallClockTime` shutdowns | Reduce I/O waits; use async operations; break into smaller functions | +| Frequent `EarlyDrop` or `TerminationRequested` | Check platform scaling policies; review deployment logs; implement better checkpointing | + +## Additional resources + +- [Debugging Edge Functions](/docs/guides/functions/logging) +- [Edge Function limits](/docs/guides/functions/limits) +- [Inspecting Edge Function environment variables](./inspecting-edge-function-environment-variables-wg5qOQ) diff --git a/apps/docs/content/troubleshooting/increase-vector-lookup-speeds-by-applying-an-hsnw-index-ohLHUM.mdx b/apps/docs/content/troubleshooting/increase-vector-lookup-speeds-by-applying-an-hsnw-index-ohLHUM.mdx index cb16fd0202add..0557d1b156a05 100644 --- a/apps/docs/content/troubleshooting/increase-vector-lookup-speeds-by-applying-an-hsnw-index-ohLHUM.mdx +++ b/apps/docs/content/troubleshooting/increase-vector-lookup-speeds-by-applying-an-hsnw-index-ohLHUM.mdx @@ -46,7 +46,7 @@ sudo apt-get update sudo apt-get install postgresql-client ``` -Once installed, you can find your PSQL string on the dashboard by clicking [connect](/dashboard/project/_?showConnect=true). Make sure if you are using the pooler connection that it is the Session pooler (port 5432). +Once installed, you can find your PSQL string on the dashboard by clicking [connect](/dashboard/project/_?showConnect=true). Make sure if you are using the pooler connection that it is the [Session pooler](/dashboard/project/_?showConnect=true&method=session) (port 5432). If your network can use IPv6, consider using the direct connection string instead of Supavisor. It's not mandatory, but for tasks that run a long time, it's best to reduce network complexity. To check if your network is compatible, use this cURL command to request your IPv6 address: diff --git a/apps/docs/content/troubleshooting/supavisor-faq-YyP5tI.mdx b/apps/docs/content/troubleshooting/supavisor-faq-YyP5tI.mdx index 76bf16f49604d..e2fa087a8aac0 100644 --- a/apps/docs/content/troubleshooting/supavisor-faq-YyP5tI.mdx +++ b/apps/docs/content/troubleshooting/supavisor-faq-YyP5tI.mdx @@ -89,7 +89,7 @@ Now, imagine the tournament organizers decide to expand the venue to house 200 p ### In the context of Supavisor, what does "pool size" mean? -"Pool size" refers to the maximum number of direct connections the pooler can maintain per unique user, database, and mode combination. You can adjust it in the [project connect page](/dashboard/project/_?showConnect=true) to strike a balance between efficient resource utilization and accommodating peak traffic. +"Pool size" refers to the maximum number of direct connections the pooler can maintain per unique user, database, and mode combination. You can adjust it in the [project connect page](/dashboard/project/_/database/settings) to strike a balance between efficient resource utilization and accommodating peak traffic. ### What is the "user+db+mode" combination? diff --git a/apps/docs/content/troubleshooting/using-sqlalchemy-with-supabase-FUqebT.mdx b/apps/docs/content/troubleshooting/using-sqlalchemy-with-supabase-FUqebT.mdx index a26ed6ff6e12f..fffe80e129a0e 100644 --- a/apps/docs/content/troubleshooting/using-sqlalchemy-with-supabase-FUqebT.mdx +++ b/apps/docs/content/troubleshooting/using-sqlalchemy-with-supabase-FUqebT.mdx @@ -15,7 +15,7 @@ If you are deploying to: - serverless functions - horizontally auto-scaling deployments -It is recommended that you connect with the pooler in transaction mode (port 6543), which can be found on the dashboard by clicking [Connect](/dashboard/project/_?showConnect=true). +It is recommended that you connect with the pooler in transaction mode (port 6543), which can be found on the dashboard by clicking [Connect](/dashboard/project/_?showConnect=true&method=transaction). ```sh # Example transaction mode string: diff --git a/apps/docs/public/humans.txt b/apps/docs/public/humans.txt index 3c5239228cd28..e24faa90c4600 100644 --- a/apps/docs/public/humans.txt +++ b/apps/docs/public/humans.txt @@ -119,6 +119,7 @@ Paul Cioanca Paul Copplestone Pavel Borisov Paweł Gulbinowicz +Pedro Rodrigues Peter Lyn Peter Soderberg Qiao Han diff --git a/apps/docs/spec/analytics_v0_config.yaml b/apps/docs/spec/analytics_v0_config.yaml index 1207dc95aed5a..be5a9c8ffd49f 100644 --- a/apps/docs/spec/analytics_v0_config.yaml +++ b/apps/docs/spec/analytics_v0_config.yaml @@ -34,16 +34,7 @@ parameters: default: '' type: 'string' description: | - Allows you to pass in an API key that will used for authentication for ingestion only. This is intended for programmatic usage where an external program sets the API key. A default ingestion API key will be automatically generated. - - id: 'LOGFLARE_PRIVATE_ACCESS_TOKEN' # {string} A unique identifier for this param. - title: 'LOGFLARE_PRIVATE_ACCESS_TOKEN' # {string} Any name. - tags: ['general'] # {string[]} These tags are useful for grouping parameters - links: [] # {string[]} These tags are useful for grouping parameters - required: true - default: '' - type: 'string' - description: | - Allows you to pass in an Management API key that will used for authentication. This is intended for programmatic usage where an external program sets the API key. This key is considered secret. + Allows you to pass in an API key that will used for authentication. This is intended for programmatic usage where an external program sets the API key. If this value is not provided, the default API key will be automatically generated. - id: 'LOGFLARE_SUPABASE_MODE' # {string} A unique identifier for this param. title: 'LOGFLARE_SUPABASE_MODE' # {string} Any name. tags: ['general'] # {string[]} These tags are useful for grouping parameters diff --git a/apps/docs/spec/functions_v0_config.yaml b/apps/docs/spec/functions_v0_config.yaml index bae7d2557b09b..664981e24bb43 100644 --- a/apps/docs/spec/functions_v0_config.yaml +++ b/apps/docs/spec/functions_v0_config.yaml @@ -14,3 +14,24 @@ info: - id: general title: General Settings description: General server settings. + +# This section is an array of public functions which a user might need to execute. +parameters: + - id: 'LOGFLARE_SINGLE_TENANT' # {string} A unique identifier for this param. + title: 'LOGFLARE_SINGLE_TENANT' # {string} Any name. + tags: ['general'] # {string[]} These tags are useful for grouping parameters + links: [] # {string[]} These tags are useful for grouping parameters + required: true + default: 'true' + type: 'boolean' + description: | + This is will seed a singular user into the database, and will disable browser authentication. All browser usage will default to this user. Inviting team users and other team-related functionality is currently not supported for self-hosted. Logflare self-hosted is currently intended for single-user experience only. + - id: 'LOGFLARE_PUBLIC_ACCESS_TOKEN' # {string} A unique identifier for this param. + title: 'LOGFLARE_PUBLIC_ACCESS_TOKEN' # {string} Any name. + tags: ['general'] # {string[]} These tags are useful for grouping parameters + links: [] # {string[]} These tags are useful for grouping parameters + required: true + default: '' + type: 'string' + description: | + Allows you to pass in an API key that will used for authentication. This is intended for programmatic usage where an external program sets the API key. It is advised to use the UI to configure the access tokens instead. If this value is not provided, the default API key will be automatically generated. diff --git a/apps/studio/components/interfaces/Auth/AuthProvidersForm/AuthProvidersForm.tsx b/apps/studio/components/interfaces/Auth/AuthProvidersForm/index.tsx similarity index 100% rename from apps/studio/components/interfaces/Auth/AuthProvidersForm/AuthProvidersForm.tsx rename to apps/studio/components/interfaces/Auth/AuthProvidersForm/index.tsx diff --git a/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx b/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm.tsx similarity index 99% rename from apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx rename to apps/studio/components/interfaces/Auth/BasicAuthSettingsForm.tsx index 3bdb420931859..b5da9b10312ef 100644 --- a/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm.tsx +++ b/apps/studio/components/interfaces/Auth/BasicAuthSettingsForm.tsx @@ -33,7 +33,7 @@ import { } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' -import { NO_REQUIRED_CHARACTERS } from '../Auth.constants' +import { NO_REQUIRED_CHARACTERS } from './Auth.constants' const schema = object({ DISABLE_SIGNUP: boolean().required(), diff --git a/apps/studio/components/interfaces/Connect/ConnectionPanel.tsx b/apps/studio/components/interfaces/Connect/ConnectionPanel.tsx index abd8f1bad7224..e168603b7b097 100644 --- a/apps/studio/components/interfaces/Connect/ConnectionPanel.tsx +++ b/apps/studio/components/interfaces/Connect/ConnectionPanel.tsx @@ -15,7 +15,6 @@ import { Collapsible_Shadcn_, CollapsibleContent_Shadcn_, CollapsibleTrigger_Shadcn_, - Separator, WarningIcon, } from 'ui' import { Admonition } from 'ui-patterns' @@ -288,7 +287,6 @@ export const ConnectionPanel = ({ )} - {isTransactionDedicatedPooler && } {children} diff --git a/apps/studio/components/interfaces/Connect/DatabaseConnectionString.tsx b/apps/studio/components/interfaces/Connect/DatabaseConnectionString.tsx index 4549a1dee544a..72ad757d9a824 100644 --- a/apps/studio/components/interfaces/Connect/DatabaseConnectionString.tsx +++ b/apps/studio/components/interfaces/Connect/DatabaseConnectionString.tsx @@ -1,4 +1,4 @@ -import { BookOpen, ChevronDown, ChevronRight, ExternalLink } from 'lucide-react' +import { BookOpen, ChevronDown, ExternalLink } from 'lucide-react' import { parseAsString, useQueryState } from 'nuqs' import { HTMLAttributes, ReactNode, useEffect, useMemo, useState } from 'react' import Link from 'next/link' @@ -32,7 +32,6 @@ import { SelectValue_Shadcn_, Select_Shadcn_, Separator, - TextLink, cn, } from 'ui' import { Admonition } from 'ui-patterns' @@ -477,45 +476,48 @@ export const DatabaseConnectionString = () => { onCopyCallback={() => handleCopy(selectedTab, 'transaction_pooler')} > {!sharedPoolerPreferred && !ipv4Addon && ( - - - - - - handleCopy(selectedTab, 'transaction_pooler')} - /> -

- Only recommended when your network does not support IPv6. Added latency - compared to dedicated pooler. -

-
-
+ + + + handleCopy(selectedTab, 'transaction_pooler')} + /> +

+ Only recommended when your network does not support IPv6. Added latency + compared to dedicated pooler. +

+
+ + )} )} diff --git a/apps/studio/components/interfaces/Database/Backups/PITR/pitr-form.tsx b/apps/studio/components/interfaces/Database/Backups/PITR/PITRForm.tsx similarity index 99% rename from apps/studio/components/interfaces/Database/Backups/PITR/pitr-form.tsx rename to apps/studio/components/interfaces/Database/Backups/PITR/PITRForm.tsx index d661beb01eec0..a476baeed7a14 100644 --- a/apps/studio/components/interfaces/Database/Backups/PITR/pitr-form.tsx +++ b/apps/studio/components/interfaces/Database/Backups/PITR/PITRForm.tsx @@ -1,20 +1,21 @@ -import { ButtonTooltip } from 'components/ui/ButtonTooltip' -import DatePicker from 'react-datepicker' -import { FormPanel } from 'components/ui/Forms/FormPanel' import { format } from 'date-fns' -import InformationBox from 'components/ui/InformationBox' import dayjs from 'dayjs' import { ChevronLeft, ChevronRight, HelpCircle } from 'lucide-react' +import { useMemo, useState } from 'react' +import DatePicker from 'react-datepicker' + +import { ButtonTooltip } from 'components/ui/ButtonTooltip' +import { FormPanel } from 'components/ui/Forms/FormPanel' +import InformationBox from 'components/ui/InformationBox' +import { Timezone } from './PITR.types' import { constrainDateToRange, formatNumberToTwoDigits, getClientTimezone, getDatesBetweenRange, } from './PITR.utils' -import { TimezoneSelection } from './TimezoneSelection' import TimeInput from './TimeInput' -import { Timezone } from './PITR.types' -import { useMemo, useState } from 'react' +import { TimezoneSelection } from './TimezoneSelection' type Props = { onSubmit: (data: { diff --git a/apps/studio/components/interfaces/Database/Backups/PITR/PITRSelection.tsx b/apps/studio/components/interfaces/Database/Backups/PITR/PITRSelection.tsx index 2d2ad64b03163..786cda8b37195 100644 --- a/apps/studio/components/interfaces/Database/Backups/PITR/PITRSelection.tsx +++ b/apps/studio/components/interfaces/Database/Backups/PITR/PITRSelection.tsx @@ -21,8 +21,8 @@ import { BackupsEmpty } from '../BackupsEmpty' import { BackupsStorageAlert } from '../BackupsStorageAlert' import type { Timezone } from './PITR.types' import { getClientTimezone } from './PITR.utils' +import { PITRForm } from './PITRForm' import PITRStatus from './PITRStatus' -import { PITRForm } from './pitr-form' export const PITRSelection = () => { const router = useRouter() diff --git a/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx b/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx index 555921cb74d07..7f3d0363c01e0 100644 --- a/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx +++ b/apps/studio/components/interfaces/Database/RestoreToNewProject/RestoreToNewProject.tsx @@ -3,7 +3,7 @@ import { Loader2 } from 'lucide-react' import Link from 'next/link' import { useState } from 'react' -import { PITRForm } from 'components/interfaces/Database/Backups/PITR/pitr-form' +import { PITRForm } from 'components/interfaces/Database/Backups/PITR/PITRForm' import { BackupsList } from 'components/interfaces/Database/Backups/RestoreToNewProject/BackupsList' import { ConfirmRestoreDialog } from 'components/interfaces/Database/Backups/RestoreToNewProject/ConfirmRestoreDialog' import { CreateNewProjectDialog } from 'components/interfaces/Database/Backups/RestoreToNewProject/CreateNewProjectDialog' diff --git a/apps/studio/components/interfaces/DiskManagement/DiskManagementForm.tsx b/apps/studio/components/interfaces/DiskManagement/DiskManagementForm.tsx index 19ab6c1881f0a..bd3756bd664d7 100644 --- a/apps/studio/components/interfaces/DiskManagement/DiskManagementForm.tsx +++ b/apps/studio/components/interfaces/DiskManagement/DiskManagementForm.tsx @@ -89,7 +89,7 @@ export function DiskManagementForm() { }, }) - const { hasAccess, entitlementConfig } = useCheckEntitlements('instances.compute_update') + const { hasAccess } = useCheckEntitlements('instances.compute_update') const [isDialogOpen, setIsDialogOpen] = useState(false) const [refetchInterval, setRefetchInterval] = useState(false) diff --git a/apps/studio/components/interfaces/Docs/GeneralContent.tsx b/apps/studio/components/interfaces/Docs/GeneralContent.tsx index 0550dfc1c42bd..72be21d3d769c 100644 --- a/apps/studio/components/interfaces/Docs/GeneralContent.tsx +++ b/apps/studio/components/interfaces/Docs/GeneralContent.tsx @@ -10,7 +10,7 @@ interface GeneralContentProps { showApiKey: string } -const GeneralContent = ({ selectedLang, page, showApiKey }: GeneralContentProps) => { +export const GeneralContent = ({ selectedLang, page, showApiKey }: GeneralContentProps) => { let selected = page?.toLowerCase() if (selected == 'intro' || selected == null) return if (selected == 'auth') @@ -27,5 +27,3 @@ const GeneralContent = ({ selectedLang, page, showApiKey }: GeneralContentProps) ) } - -export default GeneralContent diff --git a/apps/studio/components/interfaces/Docs/LangSelector.tsx b/apps/studio/components/interfaces/Docs/LangSelector.tsx index 341071c2b7ecb..5b8c76559103b 100644 --- a/apps/studio/components/interfaces/Docs/LangSelector.tsx +++ b/apps/studio/components/interfaces/Docs/LangSelector.tsx @@ -25,7 +25,7 @@ interface LangSelectorProps { setSelectedApiKey: (showApiKey: showApiKey) => void } -const LangSelector = ({ +export const LangSelector = ({ selectedLang, selectedApiKey, setSelectedLang, @@ -165,5 +165,3 @@ const LangSelector = ({ ) } - -export default LangSelector diff --git a/apps/studio/components/interfaces/Docs/ResourceContent.tsx b/apps/studio/components/interfaces/Docs/ResourceContent.tsx index 610eb85c4ddc7..e055021341248 100644 --- a/apps/studio/components/interfaces/Docs/ResourceContent.tsx +++ b/apps/studio/components/interfaces/Docs/ResourceContent.tsx @@ -19,7 +19,7 @@ interface ResourceContentProps { refreshDocs: () => void } -const ResourceContent = ({ +export const ResourceContent = ({ apiEndpoint, resourceId, resources, @@ -327,5 +327,3 @@ const ResourceContent = ({ ) } - -export default ResourceContent diff --git a/apps/studio/components/interfaces/Docs/RpcContent.tsx b/apps/studio/components/interfaces/Docs/RpcContent.tsx index 764e38814fea0..e54ae42e3f8ec 100644 --- a/apps/studio/components/interfaces/Docs/RpcContent.tsx +++ b/apps/studio/components/interfaces/Docs/RpcContent.tsx @@ -19,7 +19,7 @@ interface RpcContentProps { refreshDocs: () => void } -const RpcContent = ({ +export const RpcContent = ({ rpcId, rpcs, paths, @@ -100,5 +100,3 @@ const RpcContent = ({ ) } - -export default RpcContent diff --git a/apps/studio/components/interfaces/Docs/index.ts b/apps/studio/components/interfaces/Docs/index.ts deleted file mode 100644 index c0e0dc2ccfe99..0000000000000 --- a/apps/studio/components/interfaces/Docs/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { default as GeneralContent } from './GeneralContent' -export { default as ResourceContent } from './ResourceContent' -export { default as RpcContent } from './RpcContent' diff --git a/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionTesterSheet.tsx b/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionTesterSheet.tsx index 8d2d000c977a5..11366aa59eaf4 100644 --- a/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionTesterSheet.tsx +++ b/apps/studio/components/interfaces/Functions/EdgeFunctionDetails/EdgeFunctionTesterSheet.tsx @@ -5,7 +5,7 @@ import { useFieldArray, useForm } from 'react-hook-form' import * as z from 'zod' import { useParams } from 'common' -import { RoleImpersonationPopover } from 'components/interfaces/RoleImpersonationSelector' +import { RoleImpersonationPopover } from 'components/interfaces/RoleImpersonationSelector/RoleImpersonationPopover' import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query' import { useSessionAccessTokenQuery } from 'data/auth/session-access-token-query' import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query' diff --git a/apps/studio/components/interfaces/LogDrains/AnimatedLogos.tsx b/apps/studio/components/interfaces/LogDrains/AnimatedLogos.tsx new file mode 100644 index 0000000000000..1b171f9ca2b5f --- /dev/null +++ b/apps/studio/components/interfaces/LogDrains/AnimatedLogos.tsx @@ -0,0 +1,121 @@ +import { useState, useEffect } from 'react' +import { motion, AnimatePresence } from 'framer-motion' +import { cn } from 'ui' +import { Datadog, Grafana, Sentry } from 'icons' +import { BracesIcon } from 'lucide-react' + +export const AnimatedLogos = () => { + const [currIndex, setCurrIndex] = useState(0) + const timer = 2500 + const iconSize = 36 + + const logos = [ + { + id: 'datadog', + name: 'Datadog', + icon: , + }, + { + id: 'loki', + name: 'Loki', + icon: , + }, + { id: 'https', name: 'HTTPS', icon: }, + { + id: 'sentry', + name: 'Sentry', + icon: , + }, + ] + + useEffect(() => { + const interval = setInterval(() => { + setCurrIndex((prev) => (prev + 1) % logos.length) + }, timer) + return () => clearInterval(interval) + }, [logos.length]) + + const getPreviousIndex = () => (currIndex - 1 + logos.length) % logos.length + const getNextIndex = () => (currIndex + 1) % logos.length + + const getPosition = (index: number) => { + if (index === currIndex) return 'center' + if (index === getPreviousIndex()) return 'left' + if (index === getNextIndex()) return 'right' + return 'hidden' + } + + const logoVariants = { + hidden: { + x: 'calc(-50% + 120px)', + y: '-50%', + scale: 0.6, + opacity: 0, + filter: 'blur(1px)', + }, + right: { + x: 'calc(-50% + 80px)', + y: '-50%', + scale: 0.8, + opacity: 0.5, + zIndex: 2, + filter: 'blur(1px)', + }, + center: { + x: '-50%', + y: '-50%', + scale: 1, + opacity: 1, + zIndex: 3, + filter: 'blur(0px)', + }, + left: { + x: 'calc(-50% - 80px)', + y: '-50%', + scale: 0.8, + opacity: 0.5, + zIndex: 2, + filter: 'blur(1px)', + }, + exit: { + x: 'calc(-50% - 120px)', + y: '-50%', + scale: 0.6, + opacity: 0, + filter: 'blur(1px)', + }, + } + + const visibleIndices = [getPreviousIndex(), currIndex, getNextIndex()] + + return ( +
+ + {logos.map((logo, index) => { + if (!visibleIndices.includes(index)) return null + + const position = getPosition(index) + const isCenter = index === currIndex + + return ( + + {logo.icon} + + ) + })} + +
+
+ ) +} diff --git a/apps/studio/components/interfaces/LogDrains/LogDrainDestinationSheetForm.tsx b/apps/studio/components/interfaces/LogDrains/LogDrainDestinationSheetForm.tsx index 7a87fc4ef6925..77fa65d0b8811 100644 --- a/apps/studio/components/interfaces/LogDrains/LogDrainDestinationSheetForm.tsx +++ b/apps/studio/components/interfaces/LogDrains/LogDrainDestinationSheetForm.tsx @@ -5,7 +5,7 @@ import { useForm } from 'react-hook-form' import { toast } from 'sonner' import { z } from 'zod' -import { useFlag, useParams } from 'common' +import { IS_PLATFORM, useFlag, useParams } from 'common' import { DocsButton } from 'components/ui/DocsButton' import { LogDrainData, useLogDrainsQuery } from 'data/log-drains/log-drains-query' import { DOCS_URL } from 'lib/constants' @@ -265,7 +265,8 @@ export function LogDrainDestinationSheetForm({ // Temp check to make sure the name is unique const logDrainName = form.getValues('name') - const logDrainExists = logDrains?.find((drain) => drain.name === logDrainName) + const logDrainExists = + !!logDrains?.length && logDrains?.find((drain) => drain.name === logDrainName) if (logDrainExists && mode === 'create') { toast.error('Log drain name already exists') return @@ -569,7 +570,9 @@ export function LogDrainDestinationSheetForm({
- + void onUpdateDrainClick: (drain: LogDrainData) => void }) { - const { data: org } = useSelectedOrganizationQuery() - const { isLoading: orgPlanLoading, plan } = useCurrentOrgPlan() const logDrainsEnabled = !orgPlanLoading && (plan?.id === 'team' || plan?.id === 'enterprise') @@ -56,6 +53,7 @@ export function LogDrains({ } ) const sentryEnabled = useFlag('SentryLogDrain') + const hasLogDrains = !!logDrains?.length const { mutate: deleteLogDrain } = useDeleteLogDrainMutation({ onSuccess: () => { @@ -70,16 +68,7 @@ export function LogDrains({ }) if (!orgPlanLoading && !logDrainsEnabled) { - return ( - - - - ) + return } if (isLoading || orgPlanLoading) { @@ -90,7 +79,7 @@ export function LogDrains({ ) } - if (!isLoading && logDrains?.length === 0) { + if (!isLoading && !hasLogDrains) { return (
{LOG_DRAIN_TYPES.filter((t) => t.value !== 'sentry' || sentryEnabled).map((src) => ( diff --git a/apps/studio/components/interfaces/LogDrains/LogDrainsEmpty.tsx b/apps/studio/components/interfaces/LogDrains/LogDrainsEmpty.tsx new file mode 100644 index 0000000000000..9beca59953617 --- /dev/null +++ b/apps/studio/components/interfaces/LogDrains/LogDrainsEmpty.tsx @@ -0,0 +1,87 @@ +import { Button, Card, cn } from 'ui' +import Link from 'next/link' +import { AnimatedLogos } from './AnimatedLogos' +import Image from 'next/image' +import { BASE_PATH } from 'lib/constants' +import { UpgradePlanButton } from 'components/ui/UpgradePlanButton' + +export const LogDrainsEmpty = () => { + const items = [ + { + step: 1, + title: 'Log drain pricing', + description: + 'Log Drains are available as a project Add-On for all Team and Enterprise users. Each Log Drain costs $60 per month.', + label: 'See our pricing', + link: 'https://supabase.com/docs/guides/platform/manage-your-usage/log-drains', + }, + { + step: 2, + title: 'Connect to your drain', + description: + 'We offer support for multiple destinations including HTTPS, Datadog, Loki and Sentry.', + label: 'Read our documentation', + link: 'https://supabase.com/docs/guides/telemetry/log-drains', + }, + ] + + return ( +
+
+
+
+ +

Capture your logs, your way

+

+ Upgrade to a Team or Enterprise Plan to send your logs to your preferred platform +

+ + Upgrade plan + +
+ + {items.map((item, i) => ( +
+
+ + {item.step} + +

{item.title}

+
+

{item.description}

+ +
+ ))} +
+
+ GitHub icon +

+ Don't see your preferred drain?{' '} + + Vote here + +

+
+
+
+
+ ) +} diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx index bcb1c3f0f0b48..c68bcc420bc5f 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/BillingSettings.tsx @@ -8,13 +8,13 @@ import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-que import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { cn } from 'ui' -import InvoicesSection from '../InvoicesSettings/InvoicesSection' +import PaymentMethods from '../../Billing/Payment/PaymentMethods/PaymentMethods' +import { InvoicesSection } from '../InvoicesSettings/InvoicesSection' import BillingBreakdown from './BillingBreakdown/BillingBreakdown' import { BillingCustomerData } from './BillingCustomerData/BillingCustomerData' import BillingEmail from './BillingEmail' import CostControl from './CostControl/CostControl' import CreditBalance from './CreditBalance' -import PaymentMethods from '../../Billing/Payment/PaymentMethods/PaymentMethods' import Subscription from './Subscription/Subscription' export const BillingSettings = () => { diff --git a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx index 778d09c10d27b..22ad614dacb50 100644 --- a/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx +++ b/apps/studio/components/interfaces/Organization/BillingSettings/Subscription/SubscriptionPlanUpdateDialog.tsx @@ -672,6 +672,19 @@ export const SubscriptionPlanUpdateDialog = ({
))}
+ + {currentPlanMeta.id === 'free' && selectedTier !== 'tier_free' && ( +
+ +

+ Please note: Existing support cases will remain in the Free support + queue after your subscription is upgraded. For faster assistance under + your new plan, please open a new support case once the upgrade is + complete. +

+
+
+ )}
)} diff --git a/apps/studio/components/interfaces/Organization/Documents/Documents.tsx b/apps/studio/components/interfaces/Organization/Documents/Documents.tsx index 7190771e20761..0dcb754fb693a 100644 --- a/apps/studio/components/interfaces/Organization/Documents/Documents.tsx +++ b/apps/studio/components/interfaces/Organization/Documents/Documents.tsx @@ -10,7 +10,7 @@ import { SecurityQuestionnaire } from './SecurityQuestionnaire' import { SOC2 } from './SOC2' import { TIA } from './TIA' -const Documents = () => { +export const Documents = () => { const { organizationLegalDocuments } = useCustomContent(['organization:legal_documents']) if (Array.isArray(organizationLegalDocuments)) { @@ -67,5 +67,3 @@ const Documents = () => { ) } - -export default Documents diff --git a/apps/studio/components/interfaces/Organization/IntegrationSettings/IntegrationSettings.tsx b/apps/studio/components/interfaces/Organization/IntegrationSettings/IntegrationSettings.tsx index 5ccf2a0111a29..35672b1744b1a 100644 --- a/apps/studio/components/interfaces/Organization/IntegrationSettings/IntegrationSettings.tsx +++ b/apps/studio/components/interfaces/Organization/IntegrationSettings/IntegrationSettings.tsx @@ -43,7 +43,7 @@ const IntegrationImageHandler = ({ title }: { title: 'vercel' | 'github' }) => { ) } -const IntegrationSettings = () => { +export const IntegrationSettings = () => { const router = useRouter() const { data: org } = useSelectedOrganizationQuery() @@ -193,5 +193,3 @@ The GitHub app will watch for changes in your repository such as file changes, b ) } - -export default IntegrationSettings diff --git a/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSection.tsx b/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSection.tsx index eea5037c28399..0a077ee4aaf7f 100644 --- a/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSection.tsx +++ b/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSection.tsx @@ -6,9 +6,9 @@ import { } from 'components/layouts/Scaffold' import NoPermission from 'components/ui/NoPermission' import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions' -import InvoicesSettings from './InvoicesSettings' +import { InvoicesSettings } from './InvoicesSettings' -const InvoicesSection = () => { +export const InvoicesSection = () => { const { isSuccess: isPermissionsLoaded, can: canReadInvoices } = useAsyncCheckPermissions( PermissionAction.BILLING_READ, 'stripe.subscriptions' @@ -36,5 +36,3 @@ const InvoicesSection = () => { ) } - -export default InvoicesSection diff --git a/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx b/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx index 0cfae6a0cf08f..0eaea6050a5ea 100644 --- a/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx +++ b/apps/studio/components/interfaces/Organization/InvoicesSettings/InvoicesSettings.tsx @@ -11,14 +11,14 @@ 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 { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' +import { MANAGED_BY } from 'lib/constants/infrastructure' import { formatCurrency } from 'lib/helpers' import { ChevronLeft, ChevronRight, Download, FileText } from 'lucide-react' +import { Organization } from 'types/base' import { Button } from 'ui' import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' import InvoicePayButton from './InvoicePayButton' -import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' -import { Organization } from 'types/base' -import { MANAGED_BY } from 'lib/constants/infrastructure' const PAGE_LIMIT = 5 @@ -36,7 +36,7 @@ const getPartnerManagedResourceCta = (selectedOrganization: Organization) => { } } } -const InvoicesSettings = () => { +export const InvoicesSettings = () => { const [page, setPage] = useState(1) const { data: selectedOrganization } = useSelectedOrganizationQuery() @@ -208,5 +208,3 @@ const InvoicesSettings = () => { ) } - -export default InvoicesSettings diff --git a/apps/studio/components/interfaces/Organization/SecuritySettings/SecuritySettings.tsx b/apps/studio/components/interfaces/Organization/SecuritySettings.tsx similarity index 100% rename from apps/studio/components/interfaces/Organization/SecuritySettings/SecuritySettings.tsx rename to apps/studio/components/interfaces/Organization/SecuritySettings.tsx diff --git a/apps/studio/components/interfaces/Organization/index.ts b/apps/studio/components/interfaces/Organization/index.ts deleted file mode 100644 index ad48fc6448301..0000000000000 --- a/apps/studio/components/interfaces/Organization/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { default as Documents } from './Documents/Documents' -export { default as IntegrationSettings } from './IntegrationSettings/IntegrationSettings' -export { default as InvoicesSettings } from './InvoicesSettings/InvoicesSettings' diff --git a/apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover/index.tsx b/apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover.tsx similarity index 99% rename from apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover/index.tsx rename to apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover.tsx index f106a20a12054..108d516567bae 100644 --- a/apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover/index.tsx +++ b/apps/studio/components/interfaces/Realtime/Inspector/ChooseChannelPopover.tsx @@ -24,7 +24,7 @@ import { Popover_Shadcn_, Switch, } from 'ui' -import { RealtimeConfig } from '../useRealtimeMessages' +import { RealtimeConfig } from './useRealtimeMessages' interface ChooseChannelPopoverProps { config: RealtimeConfig diff --git a/apps/studio/components/interfaces/Realtime/Inspector/RealtimeTokensPopover/index.tsx b/apps/studio/components/interfaces/Realtime/Inspector/RealtimeTokensPopover.tsx similarity index 96% rename from apps/studio/components/interfaces/Realtime/Inspector/RealtimeTokensPopover/index.tsx rename to apps/studio/components/interfaces/Realtime/Inspector/RealtimeTokensPopover.tsx index 2ed1e7515e776..096529b7163dc 100644 --- a/apps/studio/components/interfaces/Realtime/Inspector/RealtimeTokensPopover/index.tsx +++ b/apps/studio/components/interfaces/Realtime/Inspector/RealtimeTokensPopover.tsx @@ -2,7 +2,7 @@ import { Dispatch, SetStateAction, useEffect, useRef } from 'react' import { toast } from 'sonner' import { useParams } from 'common' -import { RoleImpersonationPopover } from 'components/interfaces/RoleImpersonationSelector' +import { RoleImpersonationPopover } from 'components/interfaces/RoleImpersonationSelector/RoleImpersonationPopover' import { getKeys, useAPIKeysQuery } from 'data/api-keys/api-keys-query' import { getTemporaryAPIKey } from 'data/api-keys/temp-api-keys-query' import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query' @@ -11,7 +11,7 @@ import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization import { IS_PLATFORM } from 'lib/constants' import { getRoleImpersonationJWT } from 'lib/role-impersonation' import { useRoleImpersonationStateSnapshot } from 'state/role-impersonation-state' -import { RealtimeConfig } from '../useRealtimeMessages' +import { RealtimeConfig } from './useRealtimeMessages' interface RealtimeTokensPopoverProps { config: RealtimeConfig diff --git a/apps/studio/components/interfaces/RoleImpersonationSelector/RoleImpersonationPopover.tsx b/apps/studio/components/interfaces/RoleImpersonationSelector/RoleImpersonationPopover.tsx index 551e5ef2ac1e0..352c2a69d4b69 100644 --- a/apps/studio/components/interfaces/RoleImpersonationSelector/RoleImpersonationPopover.tsx +++ b/apps/studio/components/interfaces/RoleImpersonationSelector/RoleImpersonationPopover.tsx @@ -5,8 +5,8 @@ import { ButtonTooltip } from 'components/ui/ButtonTooltip' import type { User } from 'data/auth/users-infinite-query' import { ChevronDown, User as IconUser } from 'lucide-react' import { useRoleImpersonationStateSnapshot } from 'state/role-impersonation-state' +import { RoleImpersonationSelector } from '.' import { getAvatarUrl, getDisplayName } from '../Auth/Users/Users.utils' -import RoleImpersonationSelector from './RoleImpersonationSelector' export interface RoleImpersonationPopoverProps { portal?: boolean @@ -18,7 +18,7 @@ export interface RoleImpersonationPopoverProps { disallowAuthenticatedOption?: boolean } -const RoleImpersonationPopover = ({ +export const RoleImpersonationPopover = ({ portal = true, serviceRoleLabel, variant = 'regular', @@ -86,8 +86,6 @@ const RoleImpersonationPopover = ({ ) } -export default RoleImpersonationPopover - const UserRoleButtonSection = ({ user }: { user: User }) => { const avatarUrl = getAvatarUrl(user) const displayName = getDisplayName(user, user.email ?? user.phone ?? user.id ?? 'Unknown') diff --git a/apps/studio/components/interfaces/RoleImpersonationSelector/index.ts b/apps/studio/components/interfaces/RoleImpersonationSelector/index.ts deleted file mode 100644 index 6441dbb7572ff..0000000000000 --- a/apps/studio/components/interfaces/RoleImpersonationSelector/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default as RoleImpersonationPopover } from './RoleImpersonationPopover' -export { default as RoleImpersonationSelector } from './RoleImpersonationSelector' diff --git a/apps/studio/components/interfaces/RoleImpersonationSelector/RoleImpersonationSelector.tsx b/apps/studio/components/interfaces/RoleImpersonationSelector/index.tsx similarity index 98% rename from apps/studio/components/interfaces/RoleImpersonationSelector/RoleImpersonationSelector.tsx rename to apps/studio/components/interfaces/RoleImpersonationSelector/index.tsx index 98aff096501e1..078f91b6272fb 100644 --- a/apps/studio/components/interfaces/RoleImpersonationSelector/RoleImpersonationSelector.tsx +++ b/apps/studio/components/interfaces/RoleImpersonationSelector/index.tsx @@ -13,7 +13,7 @@ export interface RoleImpersonationSelectorProps { disallowAuthenticatedOption?: boolean } -const RoleImpersonationSelector = ({ +export const RoleImpersonationSelector = ({ serviceRoleLabel, padded = true, disallowAuthenticatedOption = false, @@ -137,5 +137,3 @@ const RoleImpersonationSelector = ({ ) } - -export default RoleImpersonationSelector diff --git a/apps/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityActions.tsx b/apps/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityActions.tsx index 8e933e22d5df7..1de2e9d886eb3 100644 --- a/apps/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityActions.tsx +++ b/apps/studio/components/interfaces/SQLEditor/UtilityPanel/UtilityActions.tsx @@ -2,7 +2,7 @@ import { AlignLeft, Check, Heart, Keyboard, MoreVertical } from 'lucide-react' import { toast } from 'sonner' import { LOCAL_STORAGE_KEYS, useParams } from 'common' -import { RoleImpersonationPopover } from 'components/interfaces/RoleImpersonationSelector' +import { RoleImpersonationPopover } from 'components/interfaces/RoleImpersonationSelector/RoleImpersonationPopover' import DatabaseSelector from 'components/ui/DatabaseSelector' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { IS_PLATFORM } from 'lib/constants' diff --git a/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx b/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx index 431f40e6522af..14254f3aef2f8 100644 --- a/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx +++ b/apps/studio/components/interfaces/Storage/StorageSettings/StorageSettings.tsx @@ -52,6 +52,7 @@ import { StorageSizeUnits, } from './StorageSettings.constants' import { convertFromBytes, convertToBytes } from './StorageSettings.utils' +import { useCheckEntitlements } from 'hooks/misc/useCheckEntitlements' const formId = 'storage-settings-form' @@ -87,6 +88,8 @@ export const StorageSettings = () => { !!config && !config.capabilities.list_v2 && config.external.upstreamTarget === 'canary' const { data: organization } = useSelectedOrganizationQuery() + const { getEntitlementNumericValue, isEntitlementUnlimited } = + useCheckEntitlements('storage.max_file_size') const isFreeTier = organization?.plan.id === 'free' const isSpendCapOn = organization?.plan.id === 'pro' && organization?.usage_billing_enabled === false @@ -109,14 +112,12 @@ export const StorageSettings = () => { }) const maxBytes = useMemo(() => { - if (organization?.plan.id === 'free') { - return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_FREE_PLAN - } else if (organization?.usage_billing_enabled) { + if (organization?.usage_billing_enabled || isEntitlementUnlimited()) { return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED } else { - return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED + return getEntitlementNumericValue() ?? STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED } - }, [organization]) + }, [organization, isEntitlementUnlimited, getEntitlementNumericValue]) const FormSchema = z .object({ diff --git a/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx b/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx index 179e3f46ee259..8edd24d71617a 100644 --- a/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/GridHeaderActions.tsx @@ -1,18 +1,18 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import { Lock, MousePointer2, PlusCircle, Unlock } from 'lucide-react' import Link from 'next/link' -import { useMemo, useState } from 'react' +import { useState } from 'react' import { toast } from 'sonner' -import { useParams, useFlag } from 'common' +import { useFlag, useParams } from 'common' import { RefreshButton } from 'components/grid/components/header/RefreshButton' import { getEntityLintDetails } from 'components/interfaces/TableGridEditor/TableEntity.utils' import { APIDocsButton } from 'components/ui/APIDocsButton' import { ButtonTooltip } from 'components/ui/ButtonTooltip' -import { useDatabaseTriggersQuery } from 'data/database-triggers/database-triggers-query' import { useDatabasePoliciesQuery } from 'data/database-policies/database-policies-query' import { useDatabasePublicationsQuery } from 'data/database-publications/database-publications-query' import { useDatabasePublicationUpdateMutation } from 'data/database-publications/database-publications-update-mutation' +import { useDatabaseTriggersQuery } from 'data/database-triggers/database-triggers-query' import { useProjectLintsQuery } from 'data/lint/lint-query' import { Entity, @@ -43,7 +43,7 @@ import { } from 'ui' import ConfirmModal from 'ui-patterns/Dialogs/ConfirmDialog' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' -import { RoleImpersonationPopover } from '../RoleImpersonationSelector' +import { RoleImpersonationPopover } from '../RoleImpersonationSelector/RoleImpersonationPopover' import ViewEntityAutofixSecurityModal from './ViewEntityAutofixSecurityModal' export interface GridHeaderActionsProps { diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/DateTimeInput/index.ts b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/DateTimeInput/index.ts deleted file mode 100644 index e1a725706ad4c..0000000000000 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/DateTimeInput/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as DateTimeInput } from './DateTimeInput' diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/DateTimeInput/DateTimeInput.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/DateTimeInput/index.tsx similarity index 97% rename from apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/DateTimeInput/DateTimeInput.tsx rename to apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/DateTimeInput/index.tsx index ae9fc6961451f..964f8db9c1480 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/DateTimeInput/DateTimeInput.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/DateTimeInput/index.tsx @@ -28,7 +28,7 @@ interface DateTimeInputProps { * e.g Yes: 2022-05-13T14:29:03 * No: 2022-05-13T14:29:03+0800 */ -const DateTimeInput = ({ +export const DateTimeInput = ({ value, onChange, name, @@ -91,5 +91,3 @@ const DateTimeInput = ({ /> ) } - -export default DateTimeInput diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/DrilldownViewer/DrilldownViewer.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/DrilldownViewer/DrilldownViewer.tsx index ac0cf89f390a8..f5208b606b169 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/DrilldownViewer/DrilldownViewer.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/DrilldownViewer/DrilldownViewer.tsx @@ -9,7 +9,7 @@ interface DrilldownViewerProps { jsonData: Dictionary } -const DrilldownViewer = ({ jsonData = {} }: DrilldownViewerProps) => { +export const DrilldownViewer = ({ jsonData = {} }: DrilldownViewerProps) => { const [activeKey, setActiveKey] = useState() const [breadCrumbs, setBreadCrumbs] = useState([]) const [jsonPane1, setJsonPane1] = useState>(jsonData) @@ -92,5 +92,3 @@ const DrilldownViewer = ({ jsonData = {} }: DrilldownViewerProps) => { ) } - -export default DrilldownViewer diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/DrilldownViewer/index.ts b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/DrilldownViewer/index.ts deleted file mode 100644 index 0122a59f72499..0000000000000 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/DrilldownViewer/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as DrilldownViewer } from './DrilldownViewer' diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/JsonCodeEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/JsonCodeEditor.tsx index 063d87d090048..49f9c13dcfe34 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/JsonCodeEditor.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/JsonCodeEditor.tsx @@ -3,13 +3,17 @@ import { noop } from 'lodash' // [Joshen] Should just use CodeEditor instead of declaring Editor here so that all the mount logic is consistent -interface JsonEditorProps { +interface JsonCodeEditorProps { value: string readOnly?: boolean onInputChange: OnChange } -const JsonEditor = ({ value = '', readOnly = false, onInputChange = noop }: JsonEditorProps) => { +export const JsonCodeEditor = ({ + value = '', + readOnly = false, + onInputChange = noop, +}: JsonCodeEditorProps) => { return ( ) } - -export default JsonEditor diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/index.ts b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/index.ts deleted file mode 100644 index b1bbc25b412f3..0000000000000 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as JsonEditor } from './JsonEditor' diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/JsonEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/index.tsx similarity index 97% rename from apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/JsonEditor.tsx rename to apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/index.tsx index f97e734544971..7736b7e885504 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/JsonEditor.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/JsonEditor/index.tsx @@ -14,8 +14,8 @@ import { minifyJSON, prettifyJSON, removeJSONTrailingComma, tryParseJson } from import { Button, SidePanel, cn } from 'ui' import ActionBar from '../../ActionBar' import { isValueTruncated } from '../RowEditor.utils' -import { DrilldownViewer } from './DrilldownViewer' -import JsonCodeEditor from './JsonCodeEditor' +import { DrilldownViewer } from './DrilldownViewer/DrilldownViewer' +import { JsonCodeEditor } from './JsonCodeEditor' interface JsonEditProps { row?: { [key: string]: any } @@ -28,7 +28,7 @@ interface JsonEditProps { onSaveJSON: (value: string | number | null, resolve: () => void) => void } -const JsonEdit = ({ +export const JsonEditor = ({ row, column, visible, @@ -203,5 +203,3 @@ const JsonEdit = ({ ) } - -export default JsonEdit diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx index 578680c777c27..b32f32ebed450 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx @@ -33,7 +33,7 @@ import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import ColumnEditor from './ColumnEditor/ColumnEditor' import type { ForeignKey } from './ForeignKeySelector/ForeignKeySelector.types' import ForeignRowSelector from './RowEditor/ForeignRowSelector/ForeignRowSelector' -import JsonEditor from './RowEditor/JsonEditor/JsonEditor' +import { JsonEditor } from './RowEditor/JsonEditor' import RowEditor from './RowEditor/RowEditor' import { convertByteaToHex } from './RowEditor/RowEditor.utils' import { TextEditor } from './RowEditor/TextEditor' diff --git a/apps/studio/components/layouts/AdvisorsLayout/AdvisorsLayout.tsx b/apps/studio/components/layouts/AdvisorsLayout/AdvisorsLayout.tsx index 3daa22f996d06..cc8738a147531 100644 --- a/apps/studio/components/layouts/AdvisorsLayout/AdvisorsLayout.tsx +++ b/apps/studio/components/layouts/AdvisorsLayout/AdvisorsLayout.tsx @@ -2,7 +2,7 @@ import { useRouter } from 'next/router' import { PropsWithChildren } from 'react' import { withAuth } from 'hooks/misc/withAuth' -import { ProjectLayout } from '../ProjectLayout/ProjectLayout' +import { ProjectLayout } from '../ProjectLayout' import { AdvisorsSidebarMenu } from './AdvisorsSidebarMenu' export interface AdvisorsLayoutProps { diff --git a/apps/studio/components/layouts/AuthLayout/AuthLayout.tsx b/apps/studio/components/layouts/AuthLayout/AuthLayout.tsx index 5e869db10b5ba..f0060141017fc 100644 --- a/apps/studio/components/layouts/AuthLayout/AuthLayout.tsx +++ b/apps/studio/components/layouts/AuthLayout/AuthLayout.tsx @@ -7,7 +7,7 @@ import { ProductMenu } from 'components/ui/ProductMenu' import { useAuthConfigPrefetch } from 'data/auth/auth-config-query' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { withAuth } from 'hooks/misc/withAuth' -import { ProjectLayout } from '../ProjectLayout/ProjectLayout' +import { ProjectLayout } from '../ProjectLayout' import { generateAuthMenu } from './AuthLayout.utils' const AuthProductMenu = () => { diff --git a/apps/studio/components/layouts/BranchLayout/BranchLayout.tsx b/apps/studio/components/layouts/BranchLayout/BranchLayout.tsx index 1f8fd8c88c3ab..29f53a3b91f28 100644 --- a/apps/studio/components/layouts/BranchLayout/BranchLayout.tsx +++ b/apps/studio/components/layouts/BranchLayout/BranchLayout.tsx @@ -5,7 +5,7 @@ import { useParams } from 'common' import { GitHubStatus } from 'components/interfaces/Settings/Integrations/GithubIntegration/GitHubStatus' import { ProductMenu } from 'components/ui/ProductMenu' import { withAuth } from 'hooks/misc/withAuth' -import { ProjectLayout } from '../ProjectLayout/ProjectLayout' +import { ProjectLayout } from '../ProjectLayout' import { generateBranchMenu } from './BranchLayout.utils' const BranchProductMenu = () => { diff --git a/apps/studio/components/layouts/DatabaseLayout/DatabaseLayout.tsx b/apps/studio/components/layouts/DatabaseLayout/DatabaseLayout.tsx index 0be07584daa25..e3ba07dc31342 100644 --- a/apps/studio/components/layouts/DatabaseLayout/DatabaseLayout.tsx +++ b/apps/studio/components/layouts/DatabaseLayout/DatabaseLayout.tsx @@ -9,7 +9,7 @@ import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { withAuth } from 'hooks/misc/withAuth' -import { ProjectLayout } from '../ProjectLayout/ProjectLayout' +import { ProjectLayout } from '../ProjectLayout' import { generateDatabaseMenu } from './DatabaseMenu.utils' export interface DatabaseLayoutProps { diff --git a/apps/studio/components/layouts/DefaultLayout.tsx b/apps/studio/components/layouts/DefaultLayout.tsx index 8763b5e1b00a1..c2ed5bcdeae41 100644 --- a/apps/studio/components/layouts/DefaultLayout.tsx +++ b/apps/studio/components/layouts/DefaultLayout.tsx @@ -9,7 +9,7 @@ import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { useCheckLatestDeploy } from 'hooks/use-check-latest-deploy' import { useAppStateSnapshot } from 'state/app-state' import { SidebarProvider } from 'ui' -import { LayoutHeader } from './ProjectLayout/LayoutHeader' +import { LayoutHeader } from './ProjectLayout/LayoutHeader/LayoutHeader' import MobileNavigationBar from './ProjectLayout/NavigationBar/MobileNavigationBar' import { ProjectContextProvider } from './ProjectLayout/ProjectContext' import { LayoutSidebarProvider } from './ProjectLayout/LayoutSidebar/LayoutSidebarProvider' diff --git a/apps/studio/components/layouts/DocsLayout/DocsLayout.tsx b/apps/studio/components/layouts/DocsLayout/DocsLayout.tsx index d34e06829756e..cbaa098120a6a 100644 --- a/apps/studio/components/layouts/DocsLayout/DocsLayout.tsx +++ b/apps/studio/components/layouts/DocsLayout/DocsLayout.tsx @@ -10,7 +10,7 @@ import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { withAuth } from 'hooks/misc/withAuth' import { PROJECT_STATUS } from 'lib/constants' -import { ProjectLayout } from '../ProjectLayout/ProjectLayout' +import { ProjectLayout } from '../ProjectLayout' import { generateDocsMenu } from './DocsLayout.utils' function DocsLayout({ title, children }: { title: string; children: ReactElement }) { diff --git a/apps/studio/components/layouts/EdgeFunctionsLayout/EdgeFunctionDetailsLayout.tsx b/apps/studio/components/layouts/EdgeFunctionsLayout/EdgeFunctionDetailsLayout.tsx index 2f9087594e34d..22cc520b71c23 100644 --- a/apps/studio/components/layouts/EdgeFunctionsLayout/EdgeFunctionDetailsLayout.tsx +++ b/apps/studio/components/layouts/EdgeFunctionsLayout/EdgeFunctionDetailsLayout.tsx @@ -27,7 +27,7 @@ import { Separator, } from 'ui' import { Input } from 'ui-patterns/DataInputs/Input' -import { ProjectLayout } from '../ProjectLayout/ProjectLayout' +import { ProjectLayout } from '../ProjectLayout' import EdgeFunctionsLayout from './EdgeFunctionsLayout' interface EdgeFunctionDetailsLayoutProps { diff --git a/apps/studio/components/layouts/EdgeFunctionsLayout/EdgeFunctionsLayout.tsx b/apps/studio/components/layouts/EdgeFunctionsLayout/EdgeFunctionsLayout.tsx index 7816e4c0e3bfe..b2967dfe84b98 100644 --- a/apps/studio/components/layouts/EdgeFunctionsLayout/EdgeFunctionsLayout.tsx +++ b/apps/studio/components/layouts/EdgeFunctionsLayout/EdgeFunctionsLayout.tsx @@ -4,7 +4,7 @@ import { PropsWithChildren } from 'react' import { useParams } from 'common' import { ProductMenu } from 'components/ui/ProductMenu' import { withAuth } from 'hooks/misc/withAuth' -import { ProjectLayout } from '../ProjectLayout/ProjectLayout' +import { ProjectLayout } from '../ProjectLayout' const EdgeFunctionsProductMenu = () => { const { ref: projectRef = 'default' } = useParams() diff --git a/apps/studio/components/layouts/Integrations/layout.tsx b/apps/studio/components/layouts/Integrations/layout.tsx index dc9444794626f..e3e3fad3f1699 100644 --- a/apps/studio/components/layouts/Integrations/layout.tsx +++ b/apps/studio/components/layouts/Integrations/layout.tsx @@ -2,7 +2,7 @@ import { useRouter } from 'next/router' import { PropsWithChildren } from 'react' import { useInstalledIntegrations } from 'components/interfaces/Integrations/Landing/useInstalledIntegrations' -import { ProjectLayout } from 'components/layouts/ProjectLayout/ProjectLayout' +import { ProjectLayout } from 'components/layouts/ProjectLayout' import AlertError from 'components/ui/AlertError' import { ProductMenu } from 'components/ui/ProductMenu' import { ProductMenuGroup } from 'components/ui/ProductMenu/ProductMenu.types' diff --git a/apps/studio/components/layouts/LogsLayout/LogsLayout.tsx b/apps/studio/components/layouts/LogsLayout/LogsLayout.tsx index cba2574d6fad1..18eca1e0c14e0 100644 --- a/apps/studio/components/layouts/LogsLayout/LogsLayout.tsx +++ b/apps/studio/components/layouts/LogsLayout/LogsLayout.tsx @@ -5,7 +5,7 @@ import { PropsWithChildren } from 'react' import NoPermission from 'components/ui/NoPermission' import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions' import { withAuth } from 'hooks/misc/withAuth' -import { ProjectLayout } from '../ProjectLayout/ProjectLayout' +import { ProjectLayout } from '../ProjectLayout' import { LogsSidebarMenuV2 } from './LogsSidebarMenuV2' interface LogsLayoutProps { diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx index f4142e8ae02d9..8c7765ca2aaae 100644 --- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx +++ b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx @@ -57,7 +57,7 @@ interface LayoutHeaderProps { backToDashboardURL?: string } -const LayoutHeader = ({ +export const LayoutHeader = ({ customHeaderComponents, breadcrumbs = [], headerTitle, @@ -248,5 +248,3 @@ const LayoutHeader = ({ ) } - -export default LayoutHeader diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/index.ts b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/index.ts deleted file mode 100644 index fee21125467a9..0000000000000 --- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as LayoutHeader } from './LayoutHeader' diff --git a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx index c3f6fc7885f99..e5059034da25b 100644 --- a/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx +++ b/apps/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.utils.tsx @@ -195,16 +195,12 @@ export const generateOtherRoutes = ( export const generateSettingsRoutes = (ref?: string, project?: Project): Route[] => { const settingsMenu = generateSettingsMenu(ref as string) return [ - ...(IS_PLATFORM - ? [ - { - key: 'settings', - label: 'Project Settings', - icon: , - link: ref && `/project/${ref}/settings/general`, - items: settingsMenu, - }, - ] - : []), + { + key: 'settings', + label: 'Project Settings', + icon: , + link: ref && `/project/${ref}/settings/general`, + items: settingsMenu, + }, ] } diff --git a/apps/studio/components/layouts/ProjectLayout/UpgradingState/index.ts b/apps/studio/components/layouts/ProjectLayout/UpgradingState/index.ts deleted file mode 100644 index 7264caf00decf..0000000000000 --- a/apps/studio/components/layouts/ProjectLayout/UpgradingState/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as UpgradingState } from './UpgradingState' diff --git a/apps/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx b/apps/studio/components/layouts/ProjectLayout/UpgradingState/index.tsx similarity index 99% rename from apps/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx rename to apps/studio/components/layouts/ProjectLayout/UpgradingState/index.tsx index b73cd169847a2..42ed5734c167a 100644 --- a/apps/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx +++ b/apps/studio/components/layouts/ProjectLayout/UpgradingState/index.tsx @@ -23,7 +23,7 @@ import { IS_PLATFORM } from 'lib/constants' import { Button, Tooltip, TooltipContent, TooltipTrigger } from 'ui' import { DATABASE_UPGRADE_MESSAGES } from './UpgradingState.constants' -const UpgradingState = () => { +export const UpgradingState = () => { const { ref } = useParams() const queryParams = useSearchParams() const { data: project } = useSelectedProjectQuery() @@ -248,5 +248,3 @@ const UpgradingState = () => { ) } - -export default UpgradingState diff --git a/apps/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/apps/studio/components/layouts/ProjectLayout/index.tsx similarity index 100% rename from apps/studio/components/layouts/ProjectLayout/ProjectLayout.tsx rename to apps/studio/components/layouts/ProjectLayout/index.tsx diff --git a/apps/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx b/apps/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx index 5dbb91b642cfb..d9595685b1540 100644 --- a/apps/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx +++ b/apps/studio/components/layouts/ProjectSettingsLayout/SettingsLayout.tsx @@ -8,7 +8,7 @@ import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { withAuth } from 'hooks/misc/withAuth' import { IS_PLATFORM } from 'lib/constants' -import { ProjectLayout } from '../ProjectLayout/ProjectLayout' +import { ProjectLayout } from '../ProjectLayout' import { generateSettingsMenu } from './SettingsMenu.utils' interface SettingsLayoutProps { @@ -21,12 +21,6 @@ const SettingsLayout = ({ title, children }: PropsWithChildren { - if (!IS_PLATFORM) { - router.push('/project/default') - } - }, [router]) - // billing pages live under /billing/invoices and /billing/subscription, etc // so we need to pass the [5]th part of the url to the menu const page = router.pathname.includes('billing') diff --git a/apps/studio/components/layouts/ProjectSettingsLayout/SettingsMenu.utils.tsx b/apps/studio/components/layouts/ProjectSettingsLayout/SettingsMenu.utils.tsx index 75a37bd46e24e..dc87caee7d66d 100644 --- a/apps/studio/components/layouts/ProjectSettingsLayout/SettingsMenu.utils.tsx +++ b/apps/studio/components/layouts/ProjectSettingsLayout/SettingsMenu.utils.tsx @@ -20,6 +20,21 @@ export const generateSettingsMenu = ( billing?: boolean } ): ProductMenuGroup[] => { + if (!IS_PLATFORM) { + return [ + { + title: 'Project Settings', + items: [ + { + name: `Log Drains`, + key: `log-drains`, + url: `/project/${ref}/settings/log-drains`, + items: [], + }, + ], + }, + ] + } const isProjectBuilding = project?.status === PROJECT_STATUS.COMING_UP const buildingUrl = `/project/${ref}` @@ -28,7 +43,6 @@ export const generateSettingsMenu = ( const edgeFunctionsEnabled = features?.edgeFunctions ?? true const storageEnabled = features?.storage ?? true const legacyJwtKeysEnabled = features?.legacyJwtKeys ?? true - const logDrainsEnabled = features?.logDrains ?? true const billingEnabled = features?.billing ?? true return [ @@ -41,64 +55,55 @@ export const generateSettingsMenu = ( url: `/project/${ref}/settings/general`, items: [], }, - ...(IS_PLATFORM - ? [ - { - name: 'Compute and Disk', - key: 'compute-and-disk', - url: `/project/${ref}/settings/compute-and-disk`, - items: [], - }, - ] - : []), + { + name: 'Compute and Disk', + key: 'compute-and-disk', + url: `/project/${ref}/settings/compute-and-disk`, + items: [], + }, { name: 'Infrastructure', key: 'infrastructure', url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/infrastructure`, items: [], }, - ...(IS_PLATFORM - ? [ - { - name: 'Integrations', - key: 'integrations', - url: `/project/${ref}/settings/integrations`, - items: [], - }, - ...(logDrainsEnabled - ? [ - { - name: `Log Drains`, - key: `log-drains`, - url: `/project/${ref}/settings/log-drains`, - items: [], - }, - ] - : []), - { - name: 'Data API', - key: 'api', - url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/api`, - items: [], - }, - { - name: 'API Keys', - key: 'api-keys', - url: `/project/${ref}/settings/api-keys`, - items: [], - label: 'NEW', - }, - { - name: 'JWT Keys', - key: 'jwt', - url: legacyJwtKeysEnabled - ? `/project/${ref}/settings/jwt` - : `/project/${ref}/settings/jwt/signing-keys`, - items: [], - label: 'NEW', - }, - ] - : []), + + { + name: 'Integrations', + key: 'integrations', + url: `/project/${ref}/settings/integrations`, + items: [], + }, + + { + name: 'Data API', + key: 'api', + url: isProjectBuilding ? buildingUrl : `/project/${ref}/settings/api`, + items: [], + }, + { + name: 'API Keys', + key: 'api-keys', + url: `/project/${ref}/settings/api-keys`, + items: [], + label: 'NEW', + }, + { + name: 'JWT Keys', + key: 'jwt', + url: legacyJwtKeysEnabled + ? `/project/${ref}/settings/jwt` + : `/project/${ref}/settings/jwt/signing-keys`, + items: [], + label: 'NEW', + }, + + { + name: `Log Drains`, + key: `log-drains`, + url: `/project/${ref}/settings/log-drains`, + items: [], + }, { name: 'Add Ons', key: 'addons', @@ -125,7 +130,7 @@ export const generateSettingsMenu = ( items: [], rightIcon: , }, - ...(IS_PLATFORM && authEnabled + ...(authEnabled ? [ { name: 'Authentication', @@ -138,7 +143,7 @@ export const generateSettingsMenu = ( }, ] : []), - ...(IS_PLATFORM && storageEnabled + ...(storageEnabled ? [ { name: 'Storage', @@ -149,7 +154,7 @@ export const generateSettingsMenu = ( }, ] : []), - ...(IS_PLATFORM && edgeFunctionsEnabled + ...(edgeFunctionsEnabled ? [ { name: 'Edge Functions', diff --git a/apps/studio/components/layouts/RealtimeLayout/RealtimeLayout.tsx b/apps/studio/components/layouts/RealtimeLayout/RealtimeLayout.tsx index 9c56dc29e0da0..3c0f8d515c04f 100644 --- a/apps/studio/components/layouts/RealtimeLayout/RealtimeLayout.tsx +++ b/apps/studio/components/layouts/RealtimeLayout/RealtimeLayout.tsx @@ -4,7 +4,7 @@ import { PropsWithChildren } from 'react' import { ProductMenu } from 'components/ui/ProductMenu' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import { withAuth } from 'hooks/misc/withAuth' -import { ProjectLayout } from '../ProjectLayout/ProjectLayout' +import { ProjectLayout } from '../ProjectLayout' import { generateRealtimeMenu } from './RealtimeMenu.utils' export interface RealtimeLayoutProps { diff --git a/apps/studio/components/layouts/ReportsLayout/ReportsLayout.tsx b/apps/studio/components/layouts/ReportsLayout/ReportsLayout.tsx index a5d5e33c7c360..af1c9695b85ff 100644 --- a/apps/studio/components/layouts/ReportsLayout/ReportsLayout.tsx +++ b/apps/studio/components/layouts/ReportsLayout/ReportsLayout.tsx @@ -4,7 +4,7 @@ import { useParams } from 'common' import { UnknownInterface } from 'components/ui/UnknownInterface' import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { withAuth } from 'hooks/misc/withAuth' -import { ProjectLayout } from '../ProjectLayout/ProjectLayout' +import { ProjectLayout } from '../ProjectLayout' import ReportsMenu from './ReportsMenu' interface ReportsLayoutProps { diff --git a/apps/studio/components/layouts/StorageLayout/StorageLayout.tsx b/apps/studio/components/layouts/StorageLayout/StorageLayout.tsx index 81be39cbbada9..a74e4fef03c1c 100644 --- a/apps/studio/components/layouts/StorageLayout/StorageLayout.tsx +++ b/apps/studio/components/layouts/StorageLayout/StorageLayout.tsx @@ -7,7 +7,7 @@ import { useSelectedBucket } from 'components/interfaces/Storage/StorageExplorer import { StorageMenu } from 'components/interfaces/Storage/StorageMenu' import { StorageMenuV2 } from 'components/interfaces/Storage/StorageMenuV2' import { withAuth } from 'hooks/misc/withAuth' -import { ProjectLayout } from '../ProjectLayout/ProjectLayout' +import { ProjectLayout } from '../ProjectLayout' export interface StorageLayoutProps { title: string diff --git a/apps/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx b/apps/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx index e7615f9f4b981..a0a400a135841 100644 --- a/apps/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx +++ b/apps/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx @@ -3,7 +3,7 @@ import { PropsWithChildren } from 'react' import NoPermission from 'components/ui/NoPermission' import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions' -import { ProjectLayoutWithAuth } from '../ProjectLayout/ProjectLayout' +import { ProjectLayoutWithAuth } from '../ProjectLayout' const TableEditorLayout = ({ children }: PropsWithChildren<{}>) => { const { can: canReadTables, isSuccess: isPermissionsLoaded } = useAsyncCheckPermissions( diff --git a/apps/studio/components/layouts/editors/EditorBaseLayout.tsx b/apps/studio/components/layouts/editors/EditorBaseLayout.tsx index 27c4b4a41bede..38c687372d04b 100644 --- a/apps/studio/components/layouts/editors/EditorBaseLayout.tsx +++ b/apps/studio/components/layouts/editors/EditorBaseLayout.tsx @@ -4,7 +4,7 @@ import { ComponentProps, ReactNode } from 'react' import { useParams } from 'common' import { useTabsStateSnapshot } from 'state/tabs' import { cn } from 'ui' -import { ProjectLayoutWithAuth } from '../ProjectLayout/ProjectLayout' +import { ProjectLayoutWithAuth } from '../ProjectLayout' import { CollapseButton } from '../Tabs/CollapseButton' import { EditorTabs } from '../Tabs/Tabs' import { useEditorType } from './EditorsLayout.hooks' diff --git a/apps/studio/components/ui/DatePicker/DatePicker.tsx b/apps/studio/components/ui/DatePicker/DatePicker.tsx deleted file mode 100644 index 52a2825fbf894..0000000000000 --- a/apps/studio/components/ui/DatePicker/DatePicker.tsx +++ /dev/null @@ -1,292 +0,0 @@ -import { format } from 'date-fns' -import dayjs from 'dayjs' -import { ArrowRight, Calendar, ChevronLeft, ChevronRight } from 'lucide-react' -import { ReactNode, useEffect, useState } from 'react' -import ReactDatePicker from 'react-datepicker' - -import type { DatePickerToFrom } from 'components/interfaces/Settings/Logs/Logs.types' -import { - Button, - Popover, - PopoverContent_Shadcn_, - PopoverTrigger_Shadcn_, - Popover_Shadcn_, -} from 'ui' -import { ButtonProps } from 'ui/src/components/Button/Button' -import { TimeSplitInput } from './TimeSplitInput' - -export interface DatePickerProps { - onChange?: (args: DatePickerToFrom) => void - to?: string // ISO string - from?: string // ISO string - triggerButtonType?: ButtonProps['type'] - triggerButtonClassName?: string - triggerButtonTitle?: string - triggerButtonSize?: 'tiny' | 'small' - contentSide?: 'bottom' | 'top' - minDate?: Date - maxDate?: Date - hideTime?: boolean - hideClear?: boolean - selectsRange?: boolean - renderFooter?: (args: DatePickerToFrom) => ReactNode | void - children?: ReactNode | ReactNode[] | null -} - -const START_DATE_DEFAULT = new Date() -const END_DATE_DEFAULT = new Date() - -const START_TIME_DEFAULT = { HH: '00', mm: '00', ss: '00' } -const END_TIME_DEFAULT = { HH: '23', mm: '59', ss: '59' } - -export function DatePicker({ - to, - from, - onChange, - triggerButtonType = 'default', - triggerButtonClassName = '', - triggerButtonTitle, - triggerButtonSize, - contentSide = 'bottom', - minDate, - maxDate, - hideTime = false, - hideClear = false, - selectsRange = true, - renderFooter = () => null, - children, -}: DatePickerProps) { - const [open, setOpen] = useState(false) - const [appliedStartDate, setAppliedStartDate] = useState(null) - const [appliedEndDate, setAppliedEndDate] = useState(null) - const [startDate, setStartDate] = useState(START_DATE_DEFAULT) - const [endDate, setEndDate] = useState(END_DATE_DEFAULT) - const [startTime, setStartTime] = useState(START_TIME_DEFAULT) - const [endTime, setEndTime] = useState(END_TIME_DEFAULT) - - useEffect(() => { - if (!from) { - setAppliedStartDate(null) - } else if (from !== appliedStartDate?.toISOString()) { - const start = dayjs(from) - const startDate = start.toDate() - setAppliedStartDate(startDate) - setStartDate(startDate) - setStartTime({ - HH: start.format('HH'), - mm: start.format('mm'), - ss: start.format('ss'), - }) - } - - if (!to) { - setAppliedEndDate(null) - } else if (to !== appliedEndDate?.toISOString()) { - const end = dayjs(to) - const endDate = end.toDate() - setAppliedEndDate(endDate) - setEndDate(endDate) - setEndTime({ - HH: end.format('HH'), - mm: end.format('mm'), - ss: end.format('ss'), - }) - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [to, from]) - - function handleDatePickerChange(dates: Date | [from: Date | null, to: Date | null] | null) { - if (!dates) { - setStartDate(null) - setEndDate(null) - } else if (dates instanceof Date) { - setStartDate(dates) - setEndDate(dates) - } else { - const [from, to] = dates - setStartDate(from) - setEndDate(to) - } - } - - function handleSubmit() { - setOpen(false) - - setAppliedStartDate(startDate) - setAppliedEndDate(endDate) - - const payload = { - from: dayjs(startDate) - .second(Number(startTime.ss)) - .minute(Number(startTime.mm)) - .hour(Number(startTime.HH)) - .toISOString(), - to: dayjs(endDate || startDate) - .second(Number(endTime.ss)) - .minute(Number(endTime.mm)) - .hour(Number(endTime.HH)) - .toISOString(), - } - if (onChange) onChange(payload) - } - - function handleClear() { - setOpen(false) - setStartDate(START_DATE_DEFAULT) - setEndDate(END_DATE_DEFAULT) - - setStartTime(START_TIME_DEFAULT) - setEndTime(END_TIME_DEFAULT) - - setAppliedStartDate(null) - setAppliedEndDate(null) - - if (onChange) onChange({ from: null, to: null }) - } - - return ( - - - - - - <> - {hideTime ? null : ( - <> -
- {!selectsRange ? null : ( - <> -
- -
-
- -
- - )} -
- -
-
- - )} -
- { - handleDatePickerChange(dates) - }} - dateFormat="MMMM d, yyyy h:mm aa" - startDate={startDate} - endDate={endDate} - minDate={minDate} - maxDate={maxDate} - dayClassName={() => 'cursor-pointer'} - renderCustomHeader={({ - date, - decreaseMonth, - increaseMonth, - prevMonthButtonDisabled, - nextMonthButtonDisabled, - }) => ( -
-
- - - {format(date, 'MMMM yyyy')} - - -
-
- )} - /> -
- {renderFooter({ - from: startDate?.toISOString() || null, - to: endDate?.toISOString() || null, - })} - -
- {!hideClear && ( - - )} - -
- -
-
- ) -} - -export default DatePicker diff --git a/apps/studio/components/ui/DatePicker/index.tsx b/apps/studio/components/ui/DatePicker/index.tsx index 772529a018d5f..4302eaac9da0b 100644 --- a/apps/studio/components/ui/DatePicker/index.tsx +++ b/apps/studio/components/ui/DatePicker/index.tsx @@ -1,2 +1,290 @@ -export { default as DatePicker } from './DatePicker' -export type { DatePickerProps } from './DatePicker' +import { format } from 'date-fns' +import dayjs from 'dayjs' +import { ArrowRight, Calendar, ChevronLeft, ChevronRight } from 'lucide-react' +import { ReactNode, useEffect, useState } from 'react' +import ReactDatePicker from 'react-datepicker' + +import type { DatePickerToFrom } from 'components/interfaces/Settings/Logs/Logs.types' +import { + Button, + Popover, + PopoverContent_Shadcn_, + PopoverTrigger_Shadcn_, + Popover_Shadcn_, +} from 'ui' +import { ButtonProps } from 'ui/src/components/Button/Button' +import { TimeSplitInput } from './TimeSplitInput' + +interface DatePickerProps { + onChange?: (args: DatePickerToFrom) => void + to?: string // ISO string + from?: string // ISO string + triggerButtonType?: ButtonProps['type'] + triggerButtonClassName?: string + triggerButtonTitle?: string + triggerButtonSize?: 'tiny' | 'small' + contentSide?: 'bottom' | 'top' + minDate?: Date + maxDate?: Date + hideTime?: boolean + hideClear?: boolean + selectsRange?: boolean + renderFooter?: (args: DatePickerToFrom) => ReactNode | void + children?: ReactNode | ReactNode[] | null +} + +const START_DATE_DEFAULT = new Date() +const END_DATE_DEFAULT = new Date() + +const START_TIME_DEFAULT = { HH: '00', mm: '00', ss: '00' } +const END_TIME_DEFAULT = { HH: '23', mm: '59', ss: '59' } + +export function DatePicker({ + to, + from, + onChange, + triggerButtonType = 'default', + triggerButtonClassName = '', + triggerButtonTitle, + triggerButtonSize, + contentSide = 'bottom', + minDate, + maxDate, + hideTime = false, + hideClear = false, + selectsRange = true, + renderFooter = () => null, + children, +}: DatePickerProps) { + const [open, setOpen] = useState(false) + const [appliedStartDate, setAppliedStartDate] = useState(null) + const [appliedEndDate, setAppliedEndDate] = useState(null) + const [startDate, setStartDate] = useState(START_DATE_DEFAULT) + const [endDate, setEndDate] = useState(END_DATE_DEFAULT) + const [startTime, setStartTime] = useState(START_TIME_DEFAULT) + const [endTime, setEndTime] = useState(END_TIME_DEFAULT) + + useEffect(() => { + if (!from) { + setAppliedStartDate(null) + } else if (from !== appliedStartDate?.toISOString()) { + const start = dayjs(from) + const startDate = start.toDate() + setAppliedStartDate(startDate) + setStartDate(startDate) + setStartTime({ + HH: start.format('HH'), + mm: start.format('mm'), + ss: start.format('ss'), + }) + } + + if (!to) { + setAppliedEndDate(null) + } else if (to !== appliedEndDate?.toISOString()) { + const end = dayjs(to) + const endDate = end.toDate() + setAppliedEndDate(endDate) + setEndDate(endDate) + setEndTime({ + HH: end.format('HH'), + mm: end.format('mm'), + ss: end.format('ss'), + }) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [to, from]) + + function handleDatePickerChange(dates: Date | [from: Date | null, to: Date | null] | null) { + if (!dates) { + setStartDate(null) + setEndDate(null) + } else if (dates instanceof Date) { + setStartDate(dates) + setEndDate(dates) + } else { + const [from, to] = dates + setStartDate(from) + setEndDate(to) + } + } + + function handleSubmit() { + setOpen(false) + + setAppliedStartDate(startDate) + setAppliedEndDate(endDate) + + const payload = { + from: dayjs(startDate) + .second(Number(startTime.ss)) + .minute(Number(startTime.mm)) + .hour(Number(startTime.HH)) + .toISOString(), + to: dayjs(endDate || startDate) + .second(Number(endTime.ss)) + .minute(Number(endTime.mm)) + .hour(Number(endTime.HH)) + .toISOString(), + } + if (onChange) onChange(payload) + } + + function handleClear() { + setOpen(false) + setStartDate(START_DATE_DEFAULT) + setEndDate(END_DATE_DEFAULT) + + setStartTime(START_TIME_DEFAULT) + setEndTime(END_TIME_DEFAULT) + + setAppliedStartDate(null) + setAppliedEndDate(null) + + if (onChange) onChange({ from: null, to: null }) + } + + return ( + + + + + + <> + {hideTime ? null : ( + <> +
+ {!selectsRange ? null : ( + <> +
+ +
+
+ +
+ + )} +
+ +
+
+ + )} +
+ { + handleDatePickerChange(dates) + }} + dateFormat="MMMM d, yyyy h:mm aa" + startDate={startDate} + endDate={endDate} + minDate={minDate} + maxDate={maxDate} + dayClassName={() => 'cursor-pointer'} + renderCustomHeader={({ + date, + decreaseMonth, + increaseMonth, + prevMonthButtonDisabled, + nextMonthButtonDisabled, + }) => ( +
+
+ + + {format(date, 'MMMM yyyy')} + + +
+
+ )} + /> +
+ {renderFooter({ + from: startDate?.toISOString() || null, + to: endDate?.toISOString() || null, + })} + +
+ {!hideClear && ( + + )} + +
+ +
+
+ ) +} diff --git a/apps/studio/components/ui/ProductMenu/index.ts b/apps/studio/components/ui/ProductMenu/index.ts deleted file mode 100644 index fd8718e853716..0000000000000 --- a/apps/studio/components/ui/ProductMenu/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as ProductMenu } from './ProductMenu' diff --git a/apps/studio/components/ui/ProductMenu/ProductMenu.tsx b/apps/studio/components/ui/ProductMenu/index.tsx similarity index 95% rename from apps/studio/components/ui/ProductMenu/ProductMenu.tsx rename to apps/studio/components/ui/ProductMenu/index.tsx index d55a15a053b17..4f5612f7ee796 100644 --- a/apps/studio/components/ui/ProductMenu/ProductMenu.tsx +++ b/apps/studio/components/ui/ProductMenu/index.tsx @@ -8,7 +8,7 @@ interface ProductMenuProps { menu: ProductMenuGroup[] } -const ProductMenu = ({ page, menu }: ProductMenuProps) => { +export const ProductMenu = ({ page, menu }: ProductMenuProps) => { return (
@@ -56,5 +56,3 @@ const ProductMenu = ({ page, menu }: ProductMenuProps) => {
) } - -export default ProductMenu diff --git a/apps/studio/data/entitlements/entitlements-query.ts b/apps/studio/data/entitlements/entitlements-query.ts index c06df0e7dc920..991539c657165 100644 --- a/apps/studio/data/entitlements/entitlements-query.ts +++ b/apps/studio/data/entitlements/entitlements-query.ts @@ -11,6 +11,7 @@ export type EntitlementsVariables = { export type EntitlementConfig = components['schemas']['ListEntitlementsResponse']['entitlements'][0]['config'] export type Entitlement = components['schemas']['ListEntitlementsResponse']['entitlements'][0] +export type EntitlementType = Entitlement['type'] export async function getEntitlements({ slug }: EntitlementsVariables, signal?: AbortSignal) { if (!slug) throw new Error('slug is required') diff --git a/apps/studio/hooks/misc/useCheckEntitlements.ts b/apps/studio/hooks/misc/useCheckEntitlements.ts index bf55549b27791..e82dbe0573c60 100644 --- a/apps/studio/hooks/misc/useCheckEntitlements.ts +++ b/apps/studio/hooks/misc/useCheckEntitlements.ts @@ -1,7 +1,57 @@ import { useEntitlementsQuery } from 'data/entitlements/entitlements-query' import { useMemo } from 'react' import { useSelectedOrganizationQuery } from './useSelectedOrganization' -import type { EntitlementConfig } from 'data/entitlements/entitlements-query' +import type { + Entitlement, + EntitlementConfig, + EntitlementType, +} from 'data/entitlements/entitlements-query' + +function isNumericConfig( + config: EntitlementConfig, + type: EntitlementType +): config is { enabled: boolean; unlimited: boolean; value: number } { + return type === 'numeric' +} + +function isSetConfig( + config: EntitlementConfig, + type: EntitlementType +): config is { enabled: boolean; set: string[] } { + return type === 'set' +} + +function isBooleanConfig( + config: EntitlementConfig, + type: EntitlementType +): config is { enabled: boolean } { + return type === 'boolean' +} + +function getEntitlementNumericValue(entitlement: Entitlement | null): number | undefined { + const entitlementConfig = entitlement?.config + return entitlementConfig && + entitlement.type && + isNumericConfig(entitlementConfig, entitlement.type) + ? entitlementConfig.value + : undefined +} + +function isEntitlementUnlimited(entitlement: Entitlement | null): boolean { + const entitlementConfig = entitlement?.config + return entitlementConfig && + entitlement.type && + isNumericConfig(entitlementConfig, entitlement.type) + ? entitlementConfig.unlimited + : false +} + +function getEntitlementSetValues(entitlement: Entitlement | null): string[] { + const entitlementConfig = entitlement?.config + return entitlementConfig && entitlement.type && isSetConfig(entitlementConfig, entitlement.type) + ? entitlementConfig.set + : [] +} export function useCheckEntitlements( featureKey: string, @@ -29,21 +79,19 @@ export function useCheckEntitlements( isSuccess: isSuccessEntitlements, } = useEntitlementsQuery({ slug: finalOrgSlug! }, { enabled }) - const { hasAccess, entitlementConfig } = useMemo((): { - hasAccess: boolean - entitlementConfig: EntitlementConfig + const { entitlement } = useMemo((): { + entitlement: Entitlement | null } => { // If no organization slug, no access - if (!finalOrgSlug) return { hasAccess: false, entitlementConfig: { enabled: false } } + if (!finalOrgSlug) return { entitlement: null } const entitlement = entitlementsData?.entitlements.find( (entitlement) => entitlement.feature.key === featureKey ) - const entitlementConfig = entitlement?.config ?? { enabled: false } - - if (!entitlement) return { hasAccess: false, entitlementConfig: { enabled: false } } - return { hasAccess: entitlement.hasAccess, entitlementConfig } + return { + entitlement: entitlement ?? null, + } }, [entitlementsData, featureKey, finalOrgSlug]) const isLoading = shouldGetSelectedOrg ? isLoadingSelectedOrg : isLoadingEntitlements @@ -51,5 +99,12 @@ export function useCheckEntitlements( ? isSuccessSelectedOrg && isSuccessEntitlements : isSuccessEntitlements - return { hasAccess, entitlementConfig, isLoading, isSuccess } + return { + hasAccess: entitlement?.hasAccess ?? false, + isLoading, + isSuccess, + getEntitlementNumericValue: () => getEntitlementNumericValue(entitlement), + isEntitlementUnlimited: () => isEntitlementUnlimited(entitlement), + getEntitlementSetValues: () => getEntitlementSetValues(entitlement), + } } diff --git a/apps/studio/pages/api/platform/projects/[ref]/analytics/log-drains.ts b/apps/studio/pages/api/platform/projects/[ref]/analytics/log-drains.ts new file mode 100644 index 0000000000000..65fed737ed089 --- /dev/null +++ b/apps/studio/pages/api/platform/projects/[ref]/analytics/log-drains.ts @@ -0,0 +1,125 @@ +import { NextApiRequest, NextApiResponse } from 'next' +import apiWrapper from 'lib/api/apiWrapper' +import { PROJECT_ANALYTICS_URL } from 'lib/constants/api' + +export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) + +async function handler(req: NextApiRequest, res: NextApiResponse) { + const { method } = req + const { ref } = req.query + + const missingEnvVars = envVarsSet() + + if (missingEnvVars !== true) { + return res + .status(500) + .json({ error: { message: `${missingEnvVars.join(', ')} env variables are not set` } }) + } + + const baseUrl = PROJECT_ANALYTICS_URL + if (!baseUrl) { + return res.status(500).json({ error: { message: `LOGFLARE_URL env variable is not set` } }) + } + + switch (method) { + case 'GET': + // list log drains + const url = new URL(baseUrl) + url.pathname = '/api/backends' + url.search = new URLSearchParams({ + 'metadata[type]': 'log-drain', + }).toString() + const resp = await fetch(url, { + method: 'GET', + headers: { + Authorization: `Bearer ${process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }).then((res) => { + return res.json() + }) + + return res.status(200).json(resp) + case 'POST': + // create the log drain + const postUrl = new URL(baseUrl) + postUrl.pathname = '/api/backends' + const postResult = await fetch(postUrl, { + body: JSON.stringify({ + ...req.body, + config: req.body.config, + metadata: { + type: 'log-drain', + }, + }), + method: 'POST', + headers: { + Authorization: `Bearer ${process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }).then(async (r) => await r.json()) + + const sourcesGetUrl = new URL(baseUrl) + sourcesGetUrl.pathname = '/api/sources' + const sources = await fetch(sourcesGetUrl, { + method: 'GET', + headers: { + Authorization: `Bearer ${process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }).then((r) => r.json()) + + const params = sources + .filter((source: { name: string; metadata: { type: string } }) => + [ + 'cloudflare.logs.prod', + 'deno-relay-logs', + 'deno-subhosting-events', + 'gotrue.logs.prod', + 'pgbouncer.logs.prod', + 'postgrest.logs.prod', + 'postgres.logs', + 'realtime.logs.prod', + 'storage.logs.prod.2', + ].includes(source.name.toLocaleLowerCase()) + ) + .map((source: { name: string; id: number }) => { + return { backend_id: postResult.id, lql_string: `~".*?"`, source_id: source.id } + }) + const rulesPostUrl = new URL(baseUrl) + rulesPostUrl.pathname = '/api/rules' + await Promise.all( + params.map((param: any) => + fetch(rulesPostUrl, { + method: 'POST', + body: JSON.stringify(param), + headers: { + Authorization: `Bearer ${process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }) + ) + ) + return res.status(201).json(postResult) + + default: + res.setHeader('Allow', ['GET', 'POST', 'PUT', 'DELETE']) + res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } }) + } +} + +const envVarsSet = () => { + const missingEnvVars = [ + process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN ? null : 'LOGFLARE_PRIVATE_ACCESS_TOKEN', + process.env.LOGFLARE_URL ? null : 'LOGFLARE_URL', + ].filter((v) => v) + if (missingEnvVars.length == 0) { + return true + } else { + return missingEnvVars + } +} diff --git a/apps/studio/pages/api/platform/projects/[ref]/analytics/log-drains/[uuid].ts b/apps/studio/pages/api/platform/projects/[ref]/analytics/log-drains/[uuid].ts new file mode 100644 index 0000000000000..2e66774baa74b --- /dev/null +++ b/apps/studio/pages/api/platform/projects/[ref]/analytics/log-drains/[uuid].ts @@ -0,0 +1,93 @@ +import { NextApiRequest, NextApiResponse } from 'next' +import apiWrapper from 'lib/api/apiWrapper' +import { PROJECT_ANALYTICS_URL } from 'lib/constants/api' + +export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) + +async function handler(req: NextApiRequest, res: NextApiResponse) { + const { method } = req + const { uuid } = req.query + + const missingEnvVars = envVarsSet() + + if (missingEnvVars !== true) { + return res + .status(500) + .json({ error: { message: `${missingEnvVars.join(', ')} env variables are not set` } }) + } + + const baseUrl = PROJECT_ANALYTICS_URL + if (!baseUrl) { + return res.status(500).json({ error: { message: `LOGFLARE_URL env variable is not set` } }) + } + + switch (method) { + case 'GET': + // get log drain + const url = new URL(baseUrl) + url.pathname = `/api/backends/${uuid}` + const result = await fetch(url, { + method: 'GET', + headers: { + Authorization: `Bearer ${process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }).then((r) => r.json()) + + return res.status(200).json(result) + case 'PUT': + // create the log drain + const putUrl = new URL(baseUrl) + putUrl.pathname = `/api/backends/${uuid}` + delete req.body['metadata'] + const putResult = await fetch(putUrl, { + body: JSON.stringify(req.body), + method: 'PUT', + headers: { + Authorization: `Bearer ${process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + }) + .then(async (r) => await r.json()) + .catch((err) => { + console.error('error updating log drain', err) + return res.status(500).json({ error: { message: 'Error updating log drain' } }) + }) + return res.status(200).json(putResult) + + case 'DELETE': + // create the log drain + const deleteUrl = new URL(baseUrl) + deleteUrl.pathname = `/api/backends/${uuid}` + + await fetch(deleteUrl, { + headers: { + Authorization: `Bearer ${process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN}`, + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + method: 'DELETE', + }).catch((err) => { + console.error('error deleting log drain', err) + return res.status(500).json({ error: { message: 'Error deleting log drain' } }) + }) + return res.status(204).json({ error: null }) + default: + res.setHeader('Allow', ['GET', 'POST']) + res.status(405).json({ data: null, error: { message: `Method ${method} Not Allowed` } }) + } +} + +const envVarsSet = () => { + const missingEnvVars = [ + process.env.LOGFLARE_PRIVATE_ACCESS_TOKEN ? null : 'LOGFLARE_PRIVATE_ACCESS_TOKEN', + process.env.LOGFLARE_URL ? null : 'LOGFLARE_URL', + ].filter((v) => v) + if (missingEnvVars.length == 0) { + return true + } else { + return missingEnvVars + } +} diff --git a/apps/studio/pages/org/[slug]/documents.tsx b/apps/studio/pages/org/[slug]/documents.tsx index 1d2fb1e44d519..c9e97ece7b6fc 100644 --- a/apps/studio/pages/org/[slug]/documents.tsx +++ b/apps/studio/pages/org/[slug]/documents.tsx @@ -1,5 +1,5 @@ import { useParams } from 'common' -import { Documents } from 'components/interfaces/Organization' +import { Documents } from 'components/interfaces/Organization/Documents/Documents' import DefaultLayout from 'components/layouts/DefaultLayout' import OrganizationLayout from 'components/layouts/OrganizationLayout' import OrganizationSettingsLayout from 'components/layouts/ProjectLayout/OrganizationSettingsLayout' diff --git a/apps/studio/pages/org/[slug]/integrations.tsx b/apps/studio/pages/org/[slug]/integrations.tsx index 69990a65094ff..0c95ded000916 100644 --- a/apps/studio/pages/org/[slug]/integrations.tsx +++ b/apps/studio/pages/org/[slug]/integrations.tsx @@ -1,4 +1,4 @@ -import { IntegrationSettings } from 'components/interfaces/Organization' +import { IntegrationSettings } from 'components/interfaces/Organization/IntegrationSettings/IntegrationSettings' import DefaultLayout from 'components/layouts/DefaultLayout' import OrganizationLayout from 'components/layouts/OrganizationLayout' import type { NextPageWithLayout } from 'types' diff --git a/apps/studio/pages/org/[slug]/security.tsx b/apps/studio/pages/org/[slug]/security.tsx index 0740972492481..a4b867621b067 100644 --- a/apps/studio/pages/org/[slug]/security.tsx +++ b/apps/studio/pages/org/[slug]/security.tsx @@ -1,5 +1,5 @@ import { useParams } from 'common' -import { SecuritySettings } from 'components/interfaces/Organization/SecuritySettings/SecuritySettings' +import { SecuritySettings } from 'components/interfaces/Organization/SecuritySettings' import DefaultLayout from 'components/layouts/DefaultLayout' import OrganizationLayout from 'components/layouts/OrganizationLayout' import OrganizationSettingsLayout from 'components/layouts/ProjectLayout/OrganizationSettingsLayout' diff --git a/apps/studio/pages/project/[ref]/api/index.tsx b/apps/studio/pages/project/[ref]/api/index.tsx index b324749bdb1bd..e3723645d315f 100644 --- a/apps/studio/pages/project/[ref]/api/index.tsx +++ b/apps/studio/pages/project/[ref]/api/index.tsx @@ -1,8 +1,10 @@ import { useParams } from 'common' import { useState } from 'react' -import { GeneralContent, ResourceContent, RpcContent } from 'components/interfaces/Docs' -import LangSelector from 'components/interfaces/Docs/LangSelector' +import { GeneralContent } from 'components/interfaces/Docs/GeneralContent' +import { LangSelector } from 'components/interfaces/Docs/LangSelector' +import { ResourceContent } from 'components/interfaces/Docs/ResourceContent' +import { RpcContent } from 'components/interfaces/Docs/RpcContent' import DefaultLayout from 'components/layouts/DefaultLayout' import DocsLayout from 'components/layouts/DocsLayout/DocsLayout' import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query' diff --git a/apps/studio/pages/project/[ref]/auth/providers.tsx b/apps/studio/pages/project/[ref]/auth/providers.tsx index e4f2dfd6a9979..ce10e26e70818 100644 --- a/apps/studio/pages/project/[ref]/auth/providers.tsx +++ b/apps/studio/pages/project/[ref]/auth/providers.tsx @@ -1,5 +1,5 @@ -import { AuthProvidersForm } from 'components/interfaces/Auth/AuthProvidersForm/AuthProvidersForm' -import { BasicAuthSettingsForm } from 'components/interfaces/Auth/BasicAuthSettingsForm/BasicAuthSettingsForm' +import { AuthProvidersForm } from 'components/interfaces/Auth/AuthProvidersForm' +import { BasicAuthSettingsForm } from 'components/interfaces/Auth/BasicAuthSettingsForm' import { AuthProvidersLayout } from 'components/layouts/AuthLayout/AuthProvidersLayout' import DefaultLayout from 'components/layouts/DefaultLayout' import { ScaffoldContainer } from 'components/layouts/Scaffold' diff --git a/apps/studio/pages/project/[ref]/building.tsx b/apps/studio/pages/project/[ref]/building.tsx index 907c27779802b..73d694b1bbc20 100644 --- a/apps/studio/pages/project/[ref]/building.tsx +++ b/apps/studio/pages/project/[ref]/building.tsx @@ -1,5 +1,5 @@ -import { ProjectLayoutWithAuth } from 'components/layouts/ProjectLayout/ProjectLayout' import DefaultLayout from 'components/layouts/DefaultLayout' +import { ProjectLayoutWithAuth } from 'components/layouts/ProjectLayout' import { useRouter } from 'next/router' import { useEffect } from 'react' import type { NextPageWithLayout } from 'types' diff --git a/apps/studio/pages/project/[ref]/index.tsx b/apps/studio/pages/project/[ref]/index.tsx index 27a1dd15c7c2e..9f9122cf52ebc 100644 --- a/apps/studio/pages/project/[ref]/index.tsx +++ b/apps/studio/pages/project/[ref]/index.tsx @@ -2,7 +2,7 @@ import { useFlag } from 'common' import { Home } from 'components/interfaces/Home/Home' import { HomeV2 } from 'components/interfaces/HomeNew/Home' import DefaultLayout from 'components/layouts/DefaultLayout' -import { ProjectLayoutWithAuth } from 'components/layouts/ProjectLayout/ProjectLayout' +import { ProjectLayoutWithAuth } from 'components/layouts/ProjectLayout' import { usePHFlag } from 'hooks/ui/useFlag' import type { NextPageWithLayout } from 'types' diff --git a/apps/studio/pages/project/[ref]/logs/index.tsx b/apps/studio/pages/project/[ref]/logs/index.tsx index b434b15c397f5..3f8c7b6854167 100644 --- a/apps/studio/pages/project/[ref]/logs/index.tsx +++ b/apps/studio/pages/project/[ref]/logs/index.tsx @@ -5,7 +5,7 @@ import { useParams } from 'common' import { useUnifiedLogsPreview } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { UnifiedLogs } from 'components/interfaces/UnifiedLogs/UnifiedLogs' import DefaultLayout from 'components/layouts/DefaultLayout' -import { ProjectLayout } from 'components/layouts/ProjectLayout/ProjectLayout' +import { ProjectLayout } from 'components/layouts/ProjectLayout' import { NextPageWithLayout } from 'types' export const LogPage: NextPageWithLayout = () => { diff --git a/apps/studio/pages/project/[ref]/merge.tsx b/apps/studio/pages/project/[ref]/merge.tsx index 5e3a9751f07a5..19f1749675791 100644 --- a/apps/studio/pages/project/[ref]/merge.tsx +++ b/apps/studio/pages/project/[ref]/merge.tsx @@ -13,7 +13,7 @@ import { ReviewWithAI } from 'components/interfaces/BranchManagement/ReviewWithA import WorkflowLogsCard from 'components/interfaces/BranchManagement/WorkflowLogsCard' import DefaultLayout from 'components/layouts/DefaultLayout' import { PageLayout } from 'components/layouts/PageLayout/PageLayout' -import { ProjectLayoutWithAuth } from 'components/layouts/ProjectLayout/ProjectLayout' +import { ProjectLayoutWithAuth } from 'components/layouts/ProjectLayout' import { ScaffoldContainer } from 'components/layouts/Scaffold' import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState' import { ButtonTooltip } from 'components/ui/ButtonTooltip' diff --git a/apps/studio/pages/project/[ref]/settings/general.tsx b/apps/studio/pages/project/[ref]/settings/general.tsx index 618016b289f99..392985ff6d278 100644 --- a/apps/studio/pages/project/[ref]/settings/general.tsx +++ b/apps/studio/pages/project/[ref]/settings/general.tsx @@ -12,6 +12,9 @@ import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' import type { NextPageWithLayout } from 'types' +import { useRouter } from 'next/router' +import { useEffect } from 'react' +import { IS_PLATFORM } from 'common' const ProjectSettings: NextPageWithLayout = () => { const { data: project } = useSelectedProjectQuery() @@ -20,6 +23,13 @@ const ProjectSettings: NextPageWithLayout = () => { const isBranch = !!project?.parent_project_ref const { projectsTransfer: projectTransferEnabled, projectSettingsCustomDomains } = useIsFeatureEnabled(['projects:transfer', 'project_settings:custom_domains']) + const router = useRouter() + + useEffect(() => { + if (!IS_PLATFORM) { + router.push(`/project/default/settings/log-drains`) + } + }, [router]) const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: selectedOrganization?.slug }) const hasHipaaAddon = subscriptionHasHipaaAddon(subscription) diff --git a/apps/studio/pages/project/[ref]/settings/log-drains.tsx b/apps/studio/pages/project/[ref]/settings/log-drains.tsx index 259adfc3569c3..e1f34d83e7091 100644 --- a/apps/studio/pages/project/[ref]/settings/log-drains.tsx +++ b/apps/studio/pages/project/[ref]/settings/log-drains.tsx @@ -81,32 +81,36 @@ const LogDrainsSettings: NextPageWithLayout = () => { return ( <> - -
- Log Drains - - Send your project logs to third party destinations - -
-
- - - {!(logDrains?.length === 0) && ( - - )} -
-
+ {(logDrainsEnabled || planLoading) && ( + +
+ Log Drains + + Send your project logs to third party destinations + +
+ +
+ + + {!(logDrains?.length === 0) && ( + + )} +
+
+ )}
+ vite-node': 3.2.4 '@redocly/respect-core>form-data': ^4.0.4 - '@supabase/supabase-js>@supabase/auth-js': 2.75.1 + '@supabase/supabase-js>@supabase/auth-js': 2.78.0 '@tanstack/directive-functions-plugin>vite': ^7.1.11 '@tanstack/react-start-plugin>vite': ^7.1.11 vinxi>vite: ^7.1.11 @@ -397,10 +397,10 @@ 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: ^10.3.0 - version: 10.3.0(@opentelemetry/context-async-hooks@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.2(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(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: 10.3.0(@opentelemetry/context-async-hooks@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(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.75.1 + version: 2.78.0 '@tailwindcss/container-queries': specifier: ^0.1.1 version: 0.1.1(tailwindcss@4.1.13) @@ -812,7 +812,7 @@ 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: ^10.3.0 - version: 10.3.0(@opentelemetry/context-async-hooks@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.2(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(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: 10.3.0(@opentelemetry/context-async-hooks@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(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' @@ -824,7 +824,7 @@ importers: version: 7.5.0 '@supabase/auth-js': specifier: 'catalog:' - version: 2.75.1 + version: 2.78.0 '@supabase/mcp-server-supabase': specifier: ^0.5.6 version: 0.5.6(supports-color@8.1.1) @@ -836,7 +836,7 @@ importers: version: link:../../packages/pg-meta '@supabase/realtime-js': specifier: 'catalog:' - version: 2.75.1 + version: 2.78.0 '@supabase/shared-types': specifier: 0.1.80 version: 0.1.80 @@ -845,7 +845,7 @@ importers: version: 0.1.6(encoding@0.1.13)(supports-color@8.1.1) '@supabase/supabase-js': specifier: 'catalog:' - version: 2.75.1 + version: 2.78.0 '@tanstack/react-query': specifier: 4.35.7 version: 4.35.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -1227,7 +1227,7 @@ importers: version: 2.11.3(@types/node@22.13.14)(typescript@5.9.2) next-router-mock: specifier: ^0.9.13 - version: 0.9.13(next@15.5.2(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(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.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1) node-mocks-http: specifier: ^1.17.2 version: 1.17.2(@types/node@22.13.14) @@ -1356,7 +1356,7 @@ importers: version: 7.4.0(@react-router/dev@7.4.0(@types/node@22.13.14)(babel-plugin-macros@3.1.0)(jiti@2.5.1)(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(vite@7.1.11(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1))(yaml@2.8.1))(typescript@5.9.2) '@supabase/postgrest-js': specifier: 'catalog:' - version: 2.75.1 + version: 2.78.0 '@supabase/supa-mdx-lint': specifier: 0.2.6-alpha version: 0.2.6-alpha @@ -1480,16 +1480,16 @@ importers: version: 1.6.0 '@supabase/ssr': specifier: ^0.7.0 - version: 0.7.0(@supabase/supabase-js@2.75.1) + version: 0.7.0(@supabase/supabase-js@2.78.0) '@supabase/supabase-js': specifier: 'catalog:' - version: 2.75.1 + version: 2.78.0 '@tanstack/react-router': specifier: ^1.114.27 version: 1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tanstack/react-start': specifier: ^1.114.25 - version: 1.114.27(@electric-sql/pglite@0.2.15)(@tanstack/react-router@1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/node@22.13.14)(aws4fetch@1.0.20)(babel-plugin-macros@3.1.0)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(vite@7.1.11(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1))(webpack@5.94.0(esbuild@0.25.2))(yaml@2.8.1) + version: 1.114.27(@electric-sql/pglite@0.2.15)(@tanstack/react-router@1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/node@22.13.14)(aws4fetch@1.0.20)(babel-plugin-macros@3.1.0)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(vite@7.1.11(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1))(webpack@5.94.0(esbuild@0.25.2))(yaml@2.8.1) '@types/common-tags': specifier: ^1.8.4 version: 1.8.4 @@ -1591,10 +1591,10 @@ importers: version: 1.0.5(@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: ^10 - version: 10.3.0(@opentelemetry/context-async-hooks@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.2(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(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: 10.3.0(@opentelemetry/context-async-hooks@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(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.75.1 + version: 2.78.0 '@vercel/og': specifier: ^0.6.2 version: 0.6.2 @@ -1823,10 +1823,10 @@ importers: dependencies: '@supabase/ssr': specifier: ^0.7.0 - version: 0.7.0(@supabase/supabase-js@2.75.1) + version: 0.7.0(@supabase/supabase-js@2.78.0) '@supabase/supabase-js': specifier: 'catalog:' - version: 2.75.1 + version: 2.78.0 h3: specifier: ^1.15.4 version: 1.15.4 @@ -1860,7 +1860,7 @@ importers: version: 0.18.5 '@supabase/supabase-js': specifier: 'catalog:' - version: 2.75.1 + version: 2.78.0 ai: specifier: ^5.0.0 version: 5.0.2(zod@3.25.76) @@ -1954,10 +1954,10 @@ importers: dependencies: '@supabase/auth-js': specifier: 'catalog:' - version: 2.75.1 + version: 2.78.0 '@supabase/supabase-js': specifier: 'catalog:' - version: 2.75.1 + version: 2.78.0 '@types/dat.gui': specifier: ^0.7.12 version: 0.7.12 @@ -2422,7 +2422,7 @@ importers: version: 0.1.6(encoding@0.1.13)(supports-color@8.1.1) '@supabase/supabase-js': specifier: 'catalog:' - version: 2.75.1 + version: 2.78.0 '@vitest/coverage-v8': specifier: ^3.2.0 version: 3.2.4(supports-color@8.1.1)(vitest@3.2.4) @@ -2576,7 +2576,7 @@ importers: version: link:../api-types next-router-mock: specifier: ^0.9.13 - version: 0.9.13(next@15.5.2(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(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.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1) tsx: specifier: 'catalog:' version: 4.20.3 @@ -8805,11 +8805,11 @@ packages: resolution: {integrity: sha512-Cq3KKe+G1o7PSBMbmrgpT2JgBeyH2THHr3RdIX2MqF7AnBuspIMgtZ3ktcCgP7kZsTMvnmWymr7zZCT1zeWbMw==} engines: {node: '>=12.16'} - '@supabase/auth-js@2.75.1': - resolution: {integrity: sha512-zktlxtXstQuVys/egDpVsargD9hQtG20CMdtn+mMn7d2Ulkzy2tgUT5FUtpppvCJtd9CkhPHO/73rvi5W6Am5A==} + '@supabase/auth-js@2.78.0': + resolution: {integrity: sha512-cXDtu1U0LeZj/xfnFoV7yCze37TcbNo8FCxy1FpqhMbB9u9QxxDSW6pA5gm/07Ei7m260Lof4CZx67Cu6DPeig==} - '@supabase/functions-js@2.75.1': - resolution: {integrity: sha512-xO+01SUcwVmmo67J7Htxq8FmhkYLFdWkxfR/taxBOI36wACEUNQZmroXGPl4PkpYxBO7TaDsRHYGxUpv9zTKkg==} + '@supabase/functions-js@2.78.0': + resolution: {integrity: sha512-t1jOvArBsOINyqaRee1xJ3gryXLvkBzqnKfi6q3YRzzhJbGS6eXz0pXR5fqmJeB01fLC+1njpf3YhMszdPEF7g==} '@supabase/mcp-server-supabase@0.5.6': resolution: {integrity: sha512-pLVukhxx0oxwAcvAEaJEwNngcPIEieFyd4bWK4phpXpbxqs4xp3i3f7nwrnflkSmG5PnRhLCKcurw3fwQHnCKQ==} @@ -8829,11 +8829,11 @@ packages: resolution: {integrity: sha512-vz5gc6RKNfDVnIfRUmH2ssTMYFI0U3MYOVyQ9R4YkzOS2dKSanjC4rTEDGjlMFwGTCUPW3N3pbY7HJIW81wMyg==} engines: {node: '>=16', npm: '>=8'} - '@supabase/postgrest-js@2.75.1': - resolution: {integrity: sha512-FiYBD0MaKqGW8eo4Xqu7/100Xm3ddgh+3qHtqS18yQRoglJTFRQCJzY1xkrGS0JFHE2YnbjL6XCiOBXiG8DK4Q==} + '@supabase/postgrest-js@2.78.0': + resolution: {integrity: sha512-AwhpYlSvJ+PSnPmIK8sHj7NGDyDENYfQGKrMtpVIEzQA2ApUjgpUGxzXWN4Z0wEtLQsvv7g4y9HVad9Hzo1TNA==} - '@supabase/realtime-js@2.75.1': - resolution: {integrity: sha512-lBIJ855bUsBFScHA/AY+lxIFkubduUvmwbagbP1hq0wDBNAsYdg3ql80w8YmtXCDjkCwlE96SZqcFn7BGKKJKQ==} + '@supabase/realtime-js@2.78.0': + resolution: {integrity: sha512-rCs1zmLe7of7hj4s7G9z8rTqzWuNVtmwDr3FiCRCJFawEoa+RQO1xpZGbdeuVvVmKDyVN6b542Okci+117y/LQ==} '@supabase/shared-types@0.1.80': resolution: {integrity: sha512-U2ACit34Up5OzB53dthb50YVGcAUzkuWn0Wq9fXWDdfl4Wlp+euWKSSVUMUIQo+bf2phu3V/PmVHEWR6dpls1g==} @@ -8846,8 +8846,8 @@ packages: peerDependencies: '@supabase/supabase-js': ^2.43.4 - '@supabase/storage-js@2.75.1': - resolution: {integrity: sha512-WdGEhroflt5O398Yg3dpf1uKZZ6N3CGloY9iGsdT873uWbkQKoP0wG8mtx98dh0fhj6dAlzBqOAvnlV12cJfzA==} + '@supabase/storage-js@2.78.0': + resolution: {integrity: sha512-n17P0JbjHOlxqJpkaGFOn97i3EusEKPEbWOpuk1r4t00Wg06B8Z4GUiq0O0n1vUpjiMgJUkLIMuBVp+bEgunzQ==} '@supabase/supa-mdx-lint-darwin@0.2.6-alpha': resolution: {integrity: sha512-tDyl2SWfuFb5yckD1cyd5CfmkAbvx9onaxXFrMTP/1bB/sYM3RI2BJQ7/lfZy0jcIiMvc70LroJe5EYx37yYsQ==} @@ -8939,8 +8939,8 @@ packages: resolution: {integrity: sha512-TNbBLSofM6jQg3JwzO4lttd59dScTTzW4p504/OWcgRWghQLRNfxXRJJtdui83gBMLWpgeUZqvgtfYIwS1Flzw==} hasBin: true - '@supabase/supabase-js@2.75.1': - resolution: {integrity: sha512-GEPVBvjQimcMd9z5K1eTKTixTRb6oVbudoLQ9JKqTUJnR6GQdBU4OifFZean1AnHfsQwtri1fop2OWwsMv019w==} + '@supabase/supabase-js@2.78.0': + resolution: {integrity: sha512-xYMRNBFmKp2m1gMuwcp/gr/HlfZKqjye1Ib8kJe29XJNsgwsfO/f8skxnWiscFKTlkOKLuBexNgl5L8dzGt6vA==} '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} @@ -27614,7 +27614,7 @@ snapshots: '@sentry/core@10.3.0': {} - '@sentry/nextjs@10.3.0(@opentelemetry/context-async-hooks@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.2(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(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@10.3.0(@opentelemetry/context-async-hooks@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.0.1(@opentelemetry/api@1.9.0))(encoding@0.1.13)(next@15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(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.36.0 @@ -28405,13 +28405,15 @@ snapshots: '@stripe/stripe-js@7.5.0': {} - '@supabase/auth-js@2.75.1': + '@supabase/auth-js@2.78.0': dependencies: '@supabase/node-fetch': 2.6.15 + tslib: 2.8.1 - '@supabase/functions-js@2.75.1': + '@supabase/functions-js@2.78.0': dependencies: '@supabase/node-fetch': 2.6.15 + tslib: 2.8.1 '@supabase/mcp-server-supabase@0.5.6(supports-color@8.1.1)': dependencies: @@ -28460,15 +28462,17 @@ snapshots: - pg-native - supports-color - '@supabase/postgrest-js@2.75.1': + '@supabase/postgrest-js@2.78.0': dependencies: '@supabase/node-fetch': 2.6.15 + tslib: 2.8.1 - '@supabase/realtime-js@2.75.1': + '@supabase/realtime-js@2.78.0': dependencies: '@supabase/node-fetch': 2.6.15 '@types/phoenix': 1.6.6 '@types/ws': 8.18.1 + tslib: 2.8.1 ws: 8.18.3 transitivePeerDependencies: - bufferutil @@ -28485,14 +28489,15 @@ snapshots: - encoding - supports-color - '@supabase/ssr@0.7.0(@supabase/supabase-js@2.75.1)': + '@supabase/ssr@0.7.0(@supabase/supabase-js@2.78.0)': dependencies: - '@supabase/supabase-js': 2.75.1 + '@supabase/supabase-js': 2.78.0 cookie: 1.0.2 - '@supabase/storage-js@2.75.1': + '@supabase/storage-js@2.78.0': dependencies: '@supabase/node-fetch': 2.6.15 + tslib: 2.8.1 '@supabase/supa-mdx-lint-darwin@0.2.6-alpha': optional: true @@ -28558,14 +28563,14 @@ snapshots: '@supabase/supa-mdx-lint-win32-x64': 0.3.1 node-pty: 1.0.0 - '@supabase/supabase-js@2.75.1': + '@supabase/supabase-js@2.78.0': dependencies: - '@supabase/auth-js': 2.75.1 - '@supabase/functions-js': 2.75.1 + '@supabase/auth-js': 2.78.0 + '@supabase/functions-js': 2.78.0 '@supabase/node-fetch': 2.6.15 - '@supabase/postgrest-js': 2.75.1 - '@supabase/realtime-js': 2.75.1 - '@supabase/storage-js': 2.75.1 + '@supabase/postgrest-js': 2.78.0 + '@supabase/realtime-js': 2.78.0 + '@supabase/storage-js': 2.78.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -28678,7 +28683,7 @@ snapshots: tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - '@tanstack/react-start-client@1.114.27(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1)': + '@tanstack/react-start-client@1.114.27(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1)': dependencies: '@tanstack/react-router': 1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tanstack/router-core': 1.114.25 @@ -28689,7 +28694,7 @@ snapshots: react-dom: 18.3.1(react@18.3.1) tiny-invariant: 1.3.3 tiny-warning: 1.0.3 - vinxi: 0.5.3(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) + vinxi: 0.5.3(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -28733,7 +28738,7 @@ snapshots: - xml2js - yaml - '@tanstack/react-start-config@1.114.27(@electric-sql/pglite@0.2.15)(@tanstack/react-router@1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/node@22.13.14)(aws4fetch@1.0.20)(babel-plugin-macros@3.1.0)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(vite@7.1.11(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1))(webpack@5.94.0(esbuild@0.25.2))(yaml@2.8.1)': + '@tanstack/react-start-config@1.114.27(@electric-sql/pglite@0.2.15)(@tanstack/react-router@1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/node@22.13.14)(aws4fetch@1.0.20)(babel-plugin-macros@3.1.0)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(vite@7.1.11(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1))(webpack@5.94.0(esbuild@0.25.2))(yaml@2.8.1)': dependencies: '@tanstack/react-start-plugin': 1.114.12(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1) '@tanstack/router-core': 1.114.25 @@ -28743,11 +28748,11 @@ snapshots: '@tanstack/start-server-functions-handler': 1.114.25 '@vitejs/plugin-react': 4.3.4(supports-color@8.1.1)(vite@7.1.11(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1)) import-meta-resolve: 4.1.0 - nitropack: 2.11.7(@electric-sql/pglite@0.2.15)(aws4fetch@1.0.20)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(supports-color@8.1.1)(typescript@5.9.2) + nitropack: 2.11.7(@electric-sql/pglite@0.2.15)(aws4fetch@1.0.20)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(supports-color@8.1.1)(typescript@5.9.2) ofetch: 1.4.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - vinxi: 0.5.3(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) + vinxi: 0.5.3(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) vite: 7.1.11(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1) zod: 3.25.76 transitivePeerDependencies: @@ -28825,11 +28830,11 @@ snapshots: - tsx - yaml - '@tanstack/react-start-router-manifest@1.114.25(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1)': + '@tanstack/react-start-router-manifest@1.114.25(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1)': dependencies: '@tanstack/router-core': 1.114.25 tiny-invariant: 1.3.3 - vinxi: 0.5.3(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) + vinxi: 0.5.3(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -28888,13 +28893,13 @@ snapshots: tiny-warning: 1.0.3 unctx: 2.4.1 - '@tanstack/react-start@1.114.27(@electric-sql/pglite@0.2.15)(@tanstack/react-router@1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/node@22.13.14)(aws4fetch@1.0.20)(babel-plugin-macros@3.1.0)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(vite@7.1.11(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1))(webpack@5.94.0(esbuild@0.25.2))(yaml@2.8.1)': + '@tanstack/react-start@1.114.27(@electric-sql/pglite@0.2.15)(@tanstack/react-router@1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/node@22.13.14)(aws4fetch@1.0.20)(babel-plugin-macros@3.1.0)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(vite@7.1.11(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1))(webpack@5.94.0(esbuild@0.25.2))(yaml@2.8.1)': dependencies: - '@tanstack/react-start-client': 1.114.27(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) - '@tanstack/react-start-config': 1.114.27(@electric-sql/pglite@0.2.15)(@tanstack/react-router@1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/node@22.13.14)(aws4fetch@1.0.20)(babel-plugin-macros@3.1.0)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(vite@7.1.11(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1))(webpack@5.94.0(esbuild@0.25.2))(yaml@2.8.1) - '@tanstack/react-start-router-manifest': 1.114.25(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) + '@tanstack/react-start-client': 1.114.27(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) + '@tanstack/react-start-config': 1.114.27(@electric-sql/pglite@0.2.15)(@tanstack/react-router@1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/node@22.13.14)(aws4fetch@1.0.20)(babel-plugin-macros@3.1.0)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(vite@7.1.11(@types/node@22.13.14)(jiti@2.5.1)(sass@1.77.4)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1))(webpack@5.94.0(esbuild@0.25.2))(yaml@2.8.1) + '@tanstack/react-start-router-manifest': 1.114.25(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) '@tanstack/react-start-server': 1.114.27(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@tanstack/start-api-routes': 1.114.25(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) + '@tanstack/start-api-routes': 1.114.25(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) '@tanstack/start-server-functions-client': 1.114.25(@types/node@22.13.14)(babel-plugin-macros@3.1.0)(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1) '@tanstack/start-server-functions-handler': 1.114.25 '@tanstack/start-server-functions-server': 1.114.12(@types/node@22.13.14)(babel-plugin-macros@3.1.0)(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(yaml@2.8.1) @@ -29045,11 +29050,11 @@ snapshots: - tsx - yaml - '@tanstack/start-api-routes@1.114.25(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1)': + '@tanstack/start-api-routes@1.114.25(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1)': dependencies: '@tanstack/router-core': 1.114.25 '@tanstack/start-server-core': 1.114.25 - vinxi: 0.5.3(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) + vinxi: 0.5.3(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -29951,7 +29956,7 @@ snapshots: optionalDependencies: '@aws-sdk/credential-provider-web-identity': 3.830.0 - '@vercel/nft@0.29.2(rollup@4.50.2)(supports-color@8.1.1)': + '@vercel/nft@0.29.2(encoding@0.1.13)(rollup@4.50.2)(supports-color@8.1.1)': dependencies: '@mapbox/node-pre-gyp': 2.0.0(encoding@0.1.13)(supports-color@8.1.1) '@rollup/pluginutils': 5.1.4(rollup@4.50.2) @@ -36719,7 +36724,7 @@ snapshots: dependencies: js-yaml-loader: 1.2.2 - next-router-mock@0.9.13(next@15.5.2(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(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.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.77.4))(react@18.3.1): dependencies: next: 15.5.2(@babel/core@7.28.4(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(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 @@ -36765,7 +36770,7 @@ snapshots: nice-try@1.0.5: {} - nitropack@2.11.7(@electric-sql/pglite@0.2.15)(aws4fetch@1.0.20)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(supports-color@8.1.1)(typescript@5.9.2): + nitropack@2.11.7(@electric-sql/pglite@0.2.15)(aws4fetch@1.0.20)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(supports-color@8.1.1)(typescript@5.9.2): dependencies: '@cloudflare/kv-asset-handler': 0.4.0 '@netlify/functions': 3.0.4 @@ -36776,7 +36781,7 @@ snapshots: '@rollup/plugin-node-resolve': 16.0.1(rollup@4.50.2) '@rollup/plugin-replace': 6.0.2(rollup@4.50.2) '@rollup/plugin-terser': 0.4.4(rollup@4.50.2) - '@vercel/nft': 0.29.2(rollup@4.50.2)(supports-color@8.1.1) + '@vercel/nft': 0.29.2(encoding@0.1.13)(rollup@4.50.2)(supports-color@8.1.1) archiver: 7.0.1 c12: 3.0.2(magicast@0.3.5) chokidar: 4.0.3 @@ -41747,7 +41752,7 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vinxi@0.5.3(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1): + vinxi@0.5.3(@electric-sql/pglite@0.2.15)(@types/node@22.13.14)(aws4fetch@1.0.20)(db0@0.3.1(@electric-sql/pglite@0.2.15)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3)))(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(ioredis@5.6.0(supports-color@8.1.1))(jiti@2.5.1)(sass@1.77.4)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.20.3)(typescript@5.9.2)(yaml@2.8.1): dependencies: '@babel/core': 7.28.4(supports-color@8.1.1) '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4(supports-color@8.1.1)) @@ -41769,7 +41774,7 @@ snapshots: hookable: 5.5.3 http-proxy: 1.18.1 micromatch: 4.0.8 - nitropack: 2.11.7(@electric-sql/pglite@0.2.15)(aws4fetch@1.0.20)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(supports-color@8.1.1)(typescript@5.9.2) + nitropack: 2.11.7(@electric-sql/pglite@0.2.15)(aws4fetch@1.0.20)(drizzle-orm@0.44.2(@electric-sql/pglite@0.2.15)(@opentelemetry/api@1.9.0)(@types/pg@8.15.4)(pg@8.16.3))(encoding@0.1.13)(supports-color@8.1.1)(typescript@5.9.2) node-fetch-native: 1.6.6 path-to-regexp: 6.3.0 pathe: 1.1.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index afbe9be1a24a8..5c802c6491f4a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,10 +5,10 @@ packages: - e2e/* catalog: - '@supabase/auth-js': 2.75.1 - '@supabase/realtime-js': 2.75.1 - '@supabase/supabase-js': 2.75.1 - '@supabase/postgrest-js': 2.75.1 + '@supabase/auth-js': 2.78.0 + '@supabase/realtime-js': 2.78.0 + '@supabase/supabase-js': 2.78.0 + '@supabase/postgrest-js': 2.78.0 '@types/node': ^22.0.0 '@types/react': ^18.3.0 '@types/react-dom': ^18.3.0 diff --git a/scripts/generateLocalEnv.js b/scripts/generateLocalEnv.js index a09b81a161ff1..1b6b541b081d7 100644 --- a/scripts/generateLocalEnv.js +++ b/scripts/generateLocalEnv.js @@ -25,6 +25,7 @@ const defaultEnv = { SENTRY_IGNORE_API_RESOLUTION_ERROR: '1', LOGFLARE_URL: 'http://127.0.0.1:54327', LOGFLARE_PRIVATE_ACCESS_TOKEN: 'api-key', + LOGFLARE_API_KEY: 'api-key', NEXT_PUBLIC_SITE_URL: 'http://localhost:8082', NEXT_PUBLIC_GOTRUE_URL: '$SUPABASE_PUBLIC_URL/auth/v1', NEXT_PUBLIC_HCAPTCHA_SITE_KEY: '10000000-ffff-ffff-ffff-000000000001', diff --git a/supa-mdx-lint/Rule003Spelling.toml b/supa-mdx-lint/Rule003Spelling.toml index 272d478430520..a5cdd6f37bfb7 100644 --- a/supa-mdx-lint/Rule003Spelling.toml +++ b/supa-mdx-lint/Rule003Spelling.toml @@ -27,6 +27,8 @@ allow_list = [ "[Bb]ackend", "[Bb]ackoff", "[Bb]lockchains?", + "BootEvent", + "BootFailure", "[Bb]reakpoints?", "[Bb]uilt-ins?", "[Cc]hangelogs?", @@ -37,6 +39,7 @@ allow_list = [ "[Cc]ooldowns?", "[Cc]oroutines?", "ComposeAuth", + "CPUTime", "[Cc]ron", "[Cc]rypto", "[Cc]ryptography", @@ -49,10 +52,12 @@ allow_list = [ "[Dd]evs?", "[Dd]iff(s|ing|ed)?", "[Dd]ropdown", + "EarlyDrop", "[Ee]nqueues?", "[Ee]ntrypoints?", "[Ee]nums?", "[Ee]nv", + "EventLoopCompleted", "[Ee]x", "[Ee]xecutables?", "[Ff]atals", @@ -69,10 +74,12 @@ allow_list = [ "[KMG]bps", "[KMG]iB", "[Ll]iveness", + "LogEvent", "[Mm]atryoshka", "[Mm]essageBird", "MetaMask", "[Mm]icroservices?", + "microtasks", "[Mm]iddlewares?", "[Mm]onorepos?", "[Mm]ultimodal", @@ -113,14 +120,18 @@ allow_list = [ "[Ss]ubfolders?", "[Ss]ubmodules?", "[Ss]wappiness", + "TerminationRequested", "[Tt]imebox(ed)?", "[Tt]odos?", "[Tt]radeoffs?", + "UncaughtException", "[Uu]nlink(ing|s|ed)?", "[Uu]pserts?", "[Uu]ptime", "[Ww]aitlists?", + "WallClockTime", "[Ww]ebhooks?", + "WorkerMemoryUsed", "Airbyte", "AndroidX", "AppleAuthentication", diff --git a/supabase/config.toml b/supabase/config.toml index 62b1c81341a3e..177ecf2700031 100644 --- a/supabase/config.toml +++ b/supabase/config.toml @@ -72,17 +72,17 @@ enable_confirmations = false # Uncomment the following to use gh oAuth app locally with your own test app # to use env vars locally, run > `source ./supabase/.env && supabase [command...]` -[auth.external.github] -enabled = true -client_id = "env(GITHUB_CLIENT_ID)" -secret = "env(GITHUB_SECRET)" -# Overrides the default auth redirectUrl. -redirect_uri = "http://localhost:54321/auth/v1/callback" +# [auth.external.github] +# enabled = true +# client_id = "env(GITHUB_CLIENT_ID)" +# secret = "env(GITHUB_SECRET)" +# # Overrides the default auth redirectUrl. +# redirect_uri = "http://localhost:54321/auth/v1/callback" -[remotes.prod.auth.external.github] -enabled = true -client_id = "env(GITHUB_CLIENT_ID)" -secret = "env(GITHUB_SECRET)" +# [remotes.prod.auth.external.github] +# enabled = true +# client_id = "env(GITHUB_CLIENT_ID)" +# secret = "env(GITHUB_SECRET)" # Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, # `discord`, `facebook`, `figma`, `github`, `gitlab`, `google`, `keycloak`, `linkedin`, `linkedin_oidc`, `notion`, diff --git a/turbo.json b/turbo.json index 714bbf2161e0b..a7c1ac8706a50 100644 --- a/turbo.json +++ b/turbo.json @@ -100,8 +100,10 @@ "DEFAULT_ORGANIZATION_NAME", "OPENAI_API_KEY", "AUTH_JWT_SECRET", - "LOGFLARE_URL", + "LOGFLARE_API_KEY", + "LOGFLARE_PUBLIC_ACCESS_TOKEN", "LOGFLARE_PRIVATE_ACCESS_TOKEN", + "LOGFLARE_URL", "SENTRY_ORG", "SENTRY_PROJECT", "SENTRY_AUTH_TOKEN",