Merge branch 'master' into feat/mermaid-blog

This commit is contained in:
Jonathan Summers-Muir authored and GitHub committed 2025-12-01 14:24:06 +08:00
commit 4f077c78eb
18 files changed
+120 -17

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ A [JSON Web Token](https://jwt.io/introduction) is a type of data structure, rep
Supabase Auth continuously issues a new JWT for each user session, for as long as the user remains signed in. Check the comprehensive guide on [Sessions](/docs/guides/auth/sessions) to find out how you can tailor this process for your needs.
JWTs provide the foundation for [Row Level Security](/docs/guides/database/row-level-security). Each Supabase product is able to securely decode and verify the validity of a JWT it receives before using Postgres policies and roles to authorize access to the project's data.
JWTs provide the foundation for [Row Level Security](/docs/guides/database/postgres/row-level-security). Each Supabase product is able to securely decode and verify the validity of a JWT it receives before using Postgres policies and roles to authorize access to the project's data.
Supabase provides a comprehensive system of managing [JWT Signing Keys](/docs/guides/auth/signing-keys) used to create and verify JSON Web Tokens.
@@ -572,7 +572,7 @@ npx expo start --tunnel
And add it to the **Redirect URLs** field in [your Supabase dashboard Authentication configuration](/dashboard/project/_/auth/url-configuration).
For more information, follow the [Supabase Login with Apple](docs/guides/auth/social-login/auth-apple) guide.
For more information, follow the [Supabase Login with Apple](/docs/guides/auth/social-login/auth-apple) guide.
</Admonition>
@@ -74,7 +74,7 @@ async function signInWithFigma() {
</TabPanel>
<TabPanel id="flutter" label="Flutter">
When your user signs in, call [`signInWithOAuth()`](/docs/reference/flutter/auth-signinwithoauth) with `figma` as the `provider`:
When your user signs in, call [`signInWithOAuth()`](/docs/reference/dart/auth-signinwithoauth) with `figma` as the `provider`:
```dart
Future<void> signInWithFigma() async {
@@ -571,7 +571,7 @@ Future<void> _nativeGoogleSignIn() async {
<TabPanel id="flutter-other" label="Flutter (web, macOS, Windows, Linux)">
Google sign-in with Supabase on Web, macOS, Windows, and Linux is done through the [`signInWithOAuth`](docs/reference/dart/auth-signinwithoauth) method.
Google sign-in with Supabase on Web, macOS, Windows, and Linux is done through the [`signInWithOAuth`](/docs/reference/dart/auth-signinwithoauth) method.
This method of signing in is web based, and will open a browser window to perform the sign in. For non-web platforms, the user is brought back to the app via [deep linking](/docs/guides/auth/native-mobile-deep-linking?platform=flutter).
@@ -702,7 +702,7 @@ When using [Compose Multiplatform](https://www.jetbrains.com/lp/compose-multipla
**Initialize the Supabase Client**
**Note:** You have to create OAuth credentials for both a Web and Android application. [Learn more](https://developers.google.com/identity/one-tap/android/get-started#api-console)
**Note:** You have to create OAuth credentials for both a Web and Android application. [Learn more](https://developer.android.com/identity/legacy/one-tap/legacy-get-started)
```kotlin
val supabaseClient = createSupabaseClient(
@@ -93,4 +93,4 @@ create table contacts (
## Resources
- [Choosing a Postgres Primary Key](/blog/choosing-a-postgres-primary-key)
- [The Basics Of Postgres `UUID` Data Type](https://www.postgresqltutorial.com/postgresql-uuid/)
- [The Basics Of Postgres `UUID` Data Type](https://www.pgtutorial.com/postgresql-tutorial/postgresql-uuid/)
@@ -244,4 +244,4 @@ Use the value from the `BRANCH PROJECT ID` column as your `project_id` in the re
- Explore [branching integrations](/docs/guides/deployment/branching/integrations)
- Learn about [troubleshooting branches](/docs/guides/deployment/branching/troubleshooting)
- Review [branching pricing](/docs/guides/deployment/branching/pricing)
- Review [branching pricing](/docs/guides/platform/manage-your-usage/branching#pricing)
@@ -55,7 +55,7 @@ After developing your project and deciding it's Production Ready, you should run
- You can restore paused projects from the Supabase dashboard.
- Upgrade to Pro to guarantee that your project will not be paused for inactivity.
- Database backups are not available for download on the Free Plan.
- You can set up your own backup systems using tools like [pg_dump](https://www.postgresqltutorial.com/postgresql-backup-database/) or [wal-g](https://github.com/wal-g/wal-g).
- You can set up your own backup systems using tools like [pg_dump](https://www.postgresql.org/docs/current/app-pgdump.html) or [wal-g](https://github.com/wal-g/wal-g).
- Nightly backups for Pro Plan projects are available on the Supabase dashboard for up to 7 days.
- Point in Time Recovery (PITR) allows a project to be backed up at much shorter intervals. This provides users an option to restore to any chosen point of up to seconds in granularity. In terms of Recovery Point Objective (RPO), Daily Backups would be suitable for projects willing to lose up to 24 hours worth of data. If a lower RPO is required, enable PITR.
- Supabase Projects use disks that offer 99.8-99.9% durability by default.
@@ -92,7 +92,7 @@ You can use Supabase to store and process Protected Health Information (PHI). Yo
- Signing a Business Associate Agreement (BAA) with Supabase. Submit a [HIPAA add-on request](https://forms.supabase.com/hipaa2) to get started. You will need to be at least on the [Team Plan](/pricing) to sign a BAA with us.
- [Marking specific projects as HIPAA projects](/docs/guides/platform/hipaa-projects) and addressing security issues raised by the advisor.
- Ensuring [MFA is enabled](/docs/guides/platform/multi-factor-authentication) on all Supabase accounts.
- [Enforce MFA](/docs/guides/platform/org-mfa-enforcement) as a requirement to access the organization
- [Enforce MFA](/docs/guides/platform/mfa/org-mfa-enforcement) as a requirement to access the organization
- Enabling [Point in Time Recovery](/docs/guides/platform/backups#point-in-time-recovery) which requires at least a [small compute add-on](/docs/guides/platform/compute-add-ons).
- Turning on [SSL Enforcement](/docs/guides/platform/ssl-enforcement).
- Enabling [Network Restrictions](/docs/guides/platform/network-restrictions).
@@ -36,7 +36,7 @@ For security reasons, we will not be able to restore access to your account if y
Once you've enabled MFA for your Supabase user account, you will be prompted to enter your second factor challenge code as seen in your preferred TOTP app.
If you are an organization owner and on the Pro, Team or Enterprise plan, you can enforce that all organization members [must have MFA enabled](/docs/guides/platform/org-mfa-enforcement).
If you are an organization owner and on the Pro, Team or Enterprise plan, you can enforce that all organization members [must have MFA enabled](/docs/guides/platform/mfa/org-mfa-enforcement).
## Disable MFA
@@ -10,6 +10,25 @@ Let's explore how to implement Realtime Presence to track state between multiple
You can use the Supabase client libraries to track Presence state between users.
### How Presence works
Presence lets each connected client publish a small piece of state—called a “presence payload”—to a shared channel. Supabase stores each client’s payload under a unique presence key and keeps a merged view of all connected clients.
When any client subscribes, disconnects, or updates their presence payload, Supabase triggers one of three events:
- **`sync`** — the full presence state has been updated
- **`join`** — a new client has started tracking presence
- **`leave`** — a client has stopped tracking presence
The complete presence state returned by `presenceState()` looks like this:
```json
{
"client_key_1": [{ "userId": 1, "typing": false }],
"client_key_2": [{ "userId": 2, "typing": true }]
}
```
### Initialize the client
Go to your Supabase project's [API Settings](/dashboard/project/_/settings/api) and grab the `URL` and `anon` public API key.
@@ -80,7 +80,7 @@ Here's a list of the most common error codes and their potential resolutions:
Indicates that the resource is not found or you don't have the correct permission to access it
**Resolution:**
- Add a RLS policy to grant permission to the resource. See our [Access Control docs](/docs/guides/storage/uploads/access-control) for more information.
- Add a RLS policy to grant permission to the resource. See our [Access Control docs](/docs/guides/storage/security/access-control) for more information.
- Ensure you include the user `Authorization` header
- Verify the object exists
@@ -22,7 +22,7 @@ export const RowContextMenu = ({ rows }: RowContextMenuProps) => {
function onDeleteRow(p: RowContextMenuItemProps) {
const rowIdx = p.props?.rowIdx
if (!rowIdx) return
if (rowIdx === undefined || rowIdx === null) return
const row = rows[rowIdx]
if (row) tableEditorSnap.onDeleteRows([row])
@@ -30,7 +30,7 @@ export const RowContextMenu = ({ rows }: RowContextMenuProps) => {
function onEditRowClick(p: RowContextMenuItemProps) {
const rowIdx = p.props?.rowIdx
if (!rowIdx) return
if (rowIdx === undefined || rowIdx === null) return
const row = rows[rowIdx]
tableEditorSnap.onEditRow(row)
@@ -39,7 +39,7 @@ export const RowContextMenu = ({ rows }: RowContextMenuProps) => {
const onCopyCellContent = useCallback(
(p: RowContextMenuItemProps) => {
const rowIdx = p.props?.rowIdx
if (!snap.selectedCellPosition || !rowIdx) return
if (!snap.selectedCellPosition || rowIdx === undefined || rowIdx === null) return
const row = rows[rowIdx]
const columnKey = snap.gridColumns[snap.selectedCellPosition.idx as number].key
@@ -56,7 +56,7 @@ export const RowContextMenu = ({ rows }: RowContextMenuProps) => {
const onCopyRowContent = useCallback(
(p: RowContextMenuItemProps) => {
const rowIdx = p.props?.rowIdx
if (!rowIdx) return
if (rowIdx === undefined || rowIdx === null) return
const row = rows[rowIdx]
copyToClipboard(JSON.stringify(row))
@@ -48,7 +48,7 @@ test('renders dates in local time', async () => {
)
// renders time locally
await userEvent.click(await screen.findByText(RegExp(from.format('DD MMM'))))
await screen.findByText(RegExp(from.format('MMMM YYYY')))
await screen.findByText(RegExp(from.format('DD MMM')))
})
test('renders datepicker selected dates in local time', async () => {
@@ -20,6 +20,19 @@ description: Real-time chat component for collaborative applications
The Realtime Chat component provides a complete chat interface that enables users to exchange messages in real-time within a shared room.
## How it works under the hood
This chat component uses **Supabase Realtime Broadcast** to send and receive messages between connected clients.
Messages sent through Broadcast are:
- delivered in real time to other connected clients
- **not stored** unless you handle persistence yourself
- **not guaranteed** to arrive if the client disconnects
- scoped to a specific `roomName`, which corresponds to a broadcast channel
This design keeps latency extremely low, but it means you should use the `onMessage` callback if you want to store messages permanently or show chat history on page load.
## Usage
### Basic usage
+2
View File
@@ -4,6 +4,7 @@ import { Image } from 'ui'
import dynamic from 'next/dynamic'
import MainProducts from './MainProducts'
import { PRODUCT_SHORTNAMES } from 'shared-data/products'
import { SITE_ORIGIN } from '~/lib/constants'
const AuthVisual = dynamic(() => import('components/Products/AuthVisual'))
const FunctionsVisual = dynamic(() => import('components/Products/FunctionsVisual'))
@@ -53,6 +54,7 @@ const data = {
metaTitle: 'Supabase at AWS re:Invent 2025 | December 1-4, Las Vegas',
metaDescription:
'Meet Supabase at AWS re:Invent 2025. Visit us at Booth #1857, join our exclusive events, and learn how to build in a weekend and scale to millions.',
ogImage: `${SITE_ORIGIN}/images/og/re_invent.png`,
},
heroSection: {
id: 'hero',
+8 -1
View File
@@ -21,7 +21,14 @@ const AWSReInvent2025: NextPage = () => {
openGraph={{
title: data.metadata.metaTitle,
description: data.metadata.metaDescription,
url: `https://supabase.com/aws-reinvent-2025`,
images: [
{
url: data.metadata.ogImage,
width: 1280,
height: 685,
alt: 'Supabase AWS Re:Invent 2025',
},
],
}}
/>
<Layout className="overflow-visible">
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+62
View File
@@ -717,4 +717,66 @@ test.describe('table editor', () => {
await deleteTable(page, ref, tableNameDataActions)
})
test('copying cell values from first and second row works', async ({ page, ref }) => {
const tableName = 'pw_table_copy_rows'
const colName = 'pw_column'
// Ensure we're on editor
if (!page.url().includes('/editor')) {
await page.goto(toUrl(`/project/${ref}/editor?schema=public`))
await waitForTableToLoad(page, ref)
}
// Create table and add two rows
await createTable(page, ref, tableName)
await page.getByRole('button', { name: `View ${tableName}`, exact: true }).click()
await page.waitForURL(/\/editor\/\d+\?schema=public$/)
// Insert first row with value 'first_row_value'
await page.getByTestId('table-editor-insert-new-row').click()
await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
await page.getByTestId(`${colName}-input`).fill('first_row_value')
await page.getByTestId('action-bar-save-row').click()
await waitForApiResponse(page, 'pg-meta', ref, 'query?key=', { method: 'POST' })
// Insert second row with value 'second_row_value'
await page.getByTestId('table-editor-insert-new-row').click()
await page.getByRole('menuitem', { name: 'Insert row Insert a new row' }).click()
await page.getByTestId(`${colName}-input`).fill('second_row_value')
await page.getByTestId('action-bar-save-row').click()
await waitForApiResponse(page, 'pg-meta', ref, 'query?key=', { method: 'POST' })
// Wait for grid to be visible
await expect(page.getByRole('grid')).toBeVisible()
// Right-click on the first row's cell to open context menu
const firstRowCell = page.getByRole('gridcell', { name: 'first_row_value' })
await expect(firstRowCell).toBeVisible()
await firstRowCell.click({ button: 'right' })
// Click "Copy cell" from context menu
await page.getByRole('menuitem', { name: 'Copy cell' }).click()
await page.waitForTimeout(500)
// Verify first row value was copied
const firstCopiedValue = await page.evaluate(() => navigator.clipboard.readText())
expect(firstCopiedValue).toBe('first_row_value')
// Right-click on the second row's cell to open context menu
const secondRowCell = page.getByRole('gridcell', { name: 'second_row_value' })
await expect(secondRowCell).toBeVisible()
await secondRowCell.click({ button: 'right' })
// Click "Copy cell" from context menu
await page.getByRole('menuitem', { name: 'Copy cell' }).click()
await page.waitForTimeout(500)
// Verify second row value was copied
const secondCopiedValue = await page.evaluate(() => navigator.clipboard.readText())
expect(secondCopiedValue).toBe('second_row_value')
// Cleanup
await deleteTable(page, ref, tableName)
})
})