Merge pull request #13993 from supabase/master

Prod deploy dashboard
This commit is contained in:
Joshen Lim authored and GitHub committed 2023-04-27 14:09:56 +08:00
commit 0120c02a30
37 files changed
+757 -164

No files matched your search

+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
env:
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
PROJECT_ID: ${{ secrets.PROJECT_ID }}
PROJECT_ID: ${{ secrets.OG_IMAGE_SUPABASE_PROJECT_ID }}
steps:
- name: Check out repo
+51
View File
@@ -0,0 +1,51 @@
name: Generate Embeddings for Search
on:
push:
branches:
- master
paths:
- '.github/workflows/search.yml'
- 'supabase/migrations/**'
- 'apps/docs/**'
- 'spec/**'
workflow_dispatch: {}
schedule:
- cron: '0 0 * * *'
jobs:
deploy:
runs-on: ubuntu-latest
env:
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}
PROJECT_ID: ${{ secrets.SEARCH_SUPABASE_PROJECT_ID }}
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.SEARCH_SUPABASE_URL }}
SUPABASE_SERVICE_ROLE_KEY: ${{ secrets.SEARCH_SUPABASE_SERVICE_ROLE_KEY }}
SUPABASE_DB_PASSWORD: ${{ secrets.SEARCH_SUPABASE_DB_PASSWORD }}
SEARCH_GITHUB_APP_ID: ${{ secrets.SEARCH_GITHUB_APP_ID }}
SEARCH_GITHUB_APP_INSTALLATION_ID: ${{ secrets.SEARCH_GITHUB_APP_INSTALLATION_ID }}
SEARCH_GITHUB_APP_PRIVATE_KEY: ${{ secrets.SEARCH_GITHUB_APP_PRIVATE_KEY }}
steps:
- name: Check out repo
uses: actions/checkout@v3
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: 18
- name: Download dependencies
run: npm ci
- name: Link Supabase project
run: npx supabase link --project-ref $PROJECT_ID
- name: Run migrations
run: npx supabase db push
- name: Refresh embeddings
working-directory: ./apps/docs
run: npm run embeddings
+3
View File
@@ -5,6 +5,9 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=
# PRIVATE
OPENAI_KEY=
SUPABASE_SERVICE_ROLE_KEY=
SEARCH_GITHUB_APP_ID=
SEARCH_GITHUB_APP_INSTALLATION_ID=
SEARCH_GITHUB_APP_PRIVATE_KEY=
# Set IS_PLATFORM to true if you have the api keys and urls
NEXT_PUBLIC_IS_PLATFORM=false
+2 -1
View File
@@ -12,6 +12,7 @@
.env.local
.env.development.local
.env.test.local
.env.staging.local
.env.production.local
npm-debug.log*
@@ -20,4 +21,4 @@ yarn-error.log*
**/*/generated/**/*
!**/*/generated/.gitkeep
!**/*/generated/**/.gitkeep
!**/*/generated/**/.gitkeep
@@ -577,7 +577,16 @@ export const functions = {
name: 'Connecting directly to Postgres',
url: '/guides/functions/connect-to-postgres',
},
],
},
{
name: 'Third-Party Tools',
url: undefined,
items: [
{ name: 'Dart Edge on Supabase', url: '/guides/functions/dart-edge' },
{ name: 'Browserless.io', url: '/guides/functions/examples/screenshots' },
{ name: 'OpenAI API', url: '/guides/functions/examples/openai' },
{ name: 'Upstash Redis', url: '/guides/functions/examples/upstash-redis' },
],
},
{
+4
View File
@@ -47,6 +47,9 @@
"@mdx-js/loader": "^1.6.22",
"@mdx-js/react": "^1.6.22",
"@next/mdx": "^12.0.4",
"@octokit/auth-app": "^4.0.9",
"@octokit/core": "^4.2.0",
"@octokit/plugin-paginate-graphql": "^2.0.1",
"@radix-ui/react-accordion": "^1.0.1",
"@supabase/auth-helpers-nextjs": "^0.5.6",
"@supabase/auth-helpers-react": "^0.3.1",
@@ -94,6 +97,7 @@
"ui": "*",
"unist-builder": "^3.0.1",
"unist-util-filter": "^4.0.1",
"uuid": "^9.0.0",
"valtio": "^1.7.6"
},
"devDependencies": {
@@ -1,4 +1,5 @@
import Layout from '~/layouts/DefaultGuideLayout'
export const meta = {
id: 'auth-twilio',
title: 'Phone Auth with Twilio',
@@ -308,33 +309,35 @@ To make use of WhatsApp OTP, please complete the following steps:
- Submit a template via the [Twilio Guide For Submitting WhatsApp templates](https://www.twilio.com/docs/whatsapp/tutorial/send-whatsapp-notification-messages-templates#creating-message-templates-and-submitting-them-for-approval)
<Admonition type="caution">
The message template submitted to Twilio must exactly match the SMS Body entered on the Supabase dashboard.
The message template submitted to Twilio must exactly match the SMS Body entered on the Supabase
dashboard.
</Admonition>
The sign in process with WhatsApp is similar to the sign in process for SMS. Do note the additional `whatsapp` parameter added:
```js
const {data, error } = await supabase.auth.signInWithOtp({
phone: '+57336567365',
options: {
channel:'whatsapp'
}
})
const { data, error } = await supabase.auth.signInWithOtp({
phone: '+57336567365',
options: {
channel: 'whatsapp',
},
})
```
You can also sign up with `whatsapp` as a channel:
```js
const {data, error }= await supabase.auth.signUp({
phone: '+57336567365',
password: 'testsupabasenow',
options: {
channel:'whatsapp',
}
const { data, error } = await supabase.auth.signUp({
phone: '+57336567365',
password: 'testsupabasenow',
options: {
channel: 'whatsapp',
},
})
```
There is no change in the verification process, you should continue to use the `sms` type for verification
```js
// After receiving a WhatsApp OTP
let { data, error } = await supabase.auth.verifyOtp({
@@ -10,6 +10,15 @@ export const meta = {
> ⚠️ WARNING: Dart Edge is an experimental project - it's probably not ready for production usage unless you're okay with living on the 'edge'. Open issues on [Dart Edge repo](https://github.com/invertase/dart_edge) if you encounter any issues.
<div class="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/53jhgrDONW4"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
## **Pre requirements**
Make sure you have [the edge CLI](https://docs.dartedge.dev/#getting-started) installed in addition to [the Supabase CLI](https://supabase.com/docs/guides/cli#installation).
@@ -6,6 +6,52 @@ export const meta = {
description: 'Run your Edge Functions locally.',
}
## Setting Up Your Environment
You can follow the [Deno guide](https://deno.com/manual@v1.32.5/getting_started/setup_your_environment) for setting up your development environment with your favorite editor/IDE.
When developing with VS Code inside of an existing application, you can utilize [multi-root workspaces](https://code.visualstudio.com/docs/editor/workspaces#_multiroot-workspaces).
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/lFhU3L8VoSQ"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
For example, see this `edge-functions.code-workspace` configuration for a CRA (create react app) client with Supabase Edge Functions. You can find the complete example on [GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions).
```json
{
"folders": [
{
"name": "project-root",
"path": "./"
},
{
"name": "client",
"path": "app"
},
{
"name": "supabase-functions",
"path": "supabase/functions"
}
],
"settings": {
"files.exclude": {
"node_modules/": true,
"app/": true,
"supabase/functions/": true
},
"deno.importMap": "./supabase/functions/import_map.json"
}
}
```
## Running Edge Functions locally
You can run your Edge Function locally using [`supabase functions serve`](/docs/reference/cli/usage#supabase-functions-serve):
```bash
@@ -15,7 +61,7 @@ supabase functions serve # start the Functions watcher
The `functions serve` command has hot-reloading capabilities. It will watch for any changes to your files and restart the Deno server.
### Invoking Edge Functions locally
## Invoking Edge Functions locally
While serving your local Edge Function, you can invoke it using curl:
@@ -88,6 +88,50 @@ const { data, error } = await supabase.functions.invoke('hello-world', {
After invoking your Edge Function you should see the response `{ "message":"Hello Functions!" }`.
## Setting Up Your Environment
You can follow the [Deno guide](https://deno.com/manual@v1.32.5/getting_started/setup_your_environment) for setting up your development environment with your favorite editor/IDE.
When developing with VS Code inside of an existing application, you can utilize [multi-root workspaces](https://code.visualstudio.com/docs/editor/workspaces#_multiroot-workspaces).
<div className="video-container">
<iframe
src="https://www.youtube-nocookie.com/embed/lFhU3L8VoSQ"
frameBorder="1"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
For example, see this `edge-functions.code-workspace` configuration for a CRA (create react app) client with Supabase Edge Functions. You can find the complete example on [GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions).
```json
{
"folders": [
{
"name": "project-root",
"path": "./"
},
{
"name": "client",
"path": "app"
},
{
"name": "supabase-functions",
"path": "supabase/functions"
}
],
"settings": {
"files.exclude": {
"node_modules/": true,
"app/": true,
"supabase/functions/": true
},
"deno.importMap": "./supabase/functions/import_map.json"
}
}
```
## Database Functions vs Edge Functions
For data-intensive operations we recommend using [Database Functions](/docs/guides/database/functions), which are executed within your database
@@ -12,7 +12,7 @@ In some cases, access to new features require upgrading or migrating your Supaba
## Upgrade your project
<Admonition type="note">This is only available for projects on the Free plan.</Admonition>
<Admonition type="note">This is only available for projects on the Free plan. For projects on the Pro plan, please contact support for assistance with upgrading.</Admonition>
When you pause and restore a project, the restored database includes the latest features. This method _does_ include downtime, so be aware that your project will be inaccessible for a short period of time.
@@ -66,9 +66,11 @@ Use your browser's developer tools to find the WebSocket initiation request and
</Admonition>
## Payload Limits
## Postgres Changes Payload Limit
Realtime has a message byte size limit of 1 megabyte.
Realtime Postgres Changes has a change payload size limit of 1 mebibyte (MiB).
When this limit is reached, the `new` and `old` record payloads only include the fields with a value size of less than or equal to 64 bytes.
## Presence Limits
+1
View File
@@ -44,6 +44,7 @@ Michel Pelletier
Monica El Khoury
Nikita Kotlyarov
Oli R
Pamela Chia
Paul Cioanca
Paul Copplestone
Pavel Borisov
@@ -3,6 +3,7 @@ import dotenv from 'dotenv'
import 'openai'
import { Configuration, OpenAIApi } from 'openai'
import { inspect } from 'util'
import { v4 as uuidv4 } from 'uuid'
import { fetchSources } from './sources'
dotenv.config()
@@ -12,7 +13,14 @@ async function generateEmbeddings() {
const args = process.argv.slice(2)
const shouldRefresh = args.includes('--refresh')
const requiredEnvVars = ['NEXT_PUBLIC_SUPABASE_URL', 'SUPABASE_SERVICE_ROLE_KEY', 'OPENAI_KEY']
const requiredEnvVars = [
'NEXT_PUBLIC_SUPABASE_URL',
'SUPABASE_SERVICE_ROLE_KEY',
'OPENAI_KEY',
'SEARCH_GITHUB_APP_ID',
'SEARCH_GITHUB_APP_INSTALLATION_ID',
'SEARCH_GITHUB_APP_PRIVATE_KEY',
]
if (requiredEnvVars.some((name) => !process.env[name])) {
throw new Error(
@@ -33,6 +41,12 @@ async function generateEmbeddings() {
}
)
// Use this version to track which pages to purge
// after the refresh
const refreshVersion = uuidv4()
const refreshDate = new Date()
const embeddingSources = await fetchSources()
console.log(`Discovered ${embeddingSources.length} pages`)
@@ -92,6 +106,24 @@ async function generateEmbeddings() {
throw updatePageError
}
}
// No content/embedding update required on this page
// Update other meta info
const { error: updatePageError } = await supabaseClient
.from('page')
.update({
type,
source,
meta,
version: refreshVersion,
last_refresh: refreshDate,
})
.filter('id', 'eq', existingPage.id)
if (updatePageError) {
throw updatePageError
}
continue
}
@@ -137,6 +169,8 @@ async function generateEmbeddings() {
source,
meta,
parent_page_id: parentPage?.id,
version: refreshVersion,
last_refresh: refreshDate,
},
{ onConflict: 'path' }
)
@@ -215,6 +249,18 @@ async function generateEmbeddings() {
}
}
console.log(`Removing old pages and their sections`)
// Delete pages that have been removed (and their sections via cascade)
const { error: deletePageError } = await supabaseClient
.from('page')
.delete()
.filter('version', 'neq', refreshVersion)
if (deletePageError) {
throw deletePageError
}
console.log('Embedding generation complete')
}
@@ -222,4 +268,9 @@ async function main() {
await generateEmbeddings()
}
main().catch((err) => console.error(err))
main().catch((err) => {
console.error(err)
// Exit with non-zero code
process.exit(1)
})
@@ -0,0 +1,119 @@
import { createAppAuth } from '@octokit/auth-app'
import { Octokit } from '@octokit/core'
import { paginateGraphql } from '@octokit/plugin-paginate-graphql'
import { createHash } from 'crypto'
import { BaseSource } from './base'
export const ExtendedOctokit = Octokit.plugin(paginateGraphql)
export type ExtendedOctokit = InstanceType<typeof ExtendedOctokit>
export type Discussion = {
id: string
updatedAt: string
url: string
title: string
body: string
databaseId: number
}
export type DiscussionsResponse = {
repository: {
discussions: {
totalCount: number
nodes: Discussion[]
}
}
}
/**
* Fetches GitHub discussions for a repository + category
*/
export async function fetchDiscussions(owner: string, repo: string, categoryId: string) {
const octokit = new ExtendedOctokit({
authStrategy: createAppAuth,
auth: {
appId: process.env.SEARCH_GITHUB_APP_ID,
installationId: process.env.SEARCH_GITHUB_APP_INSTALLATION_ID,
privateKey: process.env.SEARCH_GITHUB_APP_PRIVATE_KEY,
},
})
const {
repository: {
discussions: { nodes: discussions },
},
} = await octokit.graphql.paginate<DiscussionsResponse>(
`
query troubleshootDiscussions($cursor: String, $owner: String!, $repo: String!, $categoryId: ID!) {
repository(owner: $owner, name: $repo) {
discussions(first: 100, after: $cursor, categoryId: $categoryId) {
totalCount
nodes {
id
updatedAt
url
title
body
databaseId
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
`,
{
owner,
repo,
categoryId,
}
)
return discussions
}
export class GitHubDiscussionSource extends BaseSource {
type = 'github-discussions' as const
constructor(source: string, public discussion: Discussion) {
super(source, discussion.url)
}
async load() {
const { id, title, updatedAt, body, databaseId } = this.discussion
const checksum = createHash('sha256').update(updatedAt).digest('base64')
const meta = { id, title, updatedAt }
// Currently the discussion post itself is being considered as the answer
// (as opposed to a comment marked as answer)
// So we link the slug to the initial discussion post rather than a comment answer
const slug = `discussion-${databaseId}`
// Format the discussion title + body as markdown for better embeddings + LLM response
const content = `# ${title}\n${body}`
// For now, only a single section is created for GH discussions
// Consider adding multiple if we want to include comments/answers
const sections = [
{
heading: title,
slug,
content,
},
]
this.checksum = checksum
this.meta = meta
this.sections = sections
return {
checksum,
meta,
sections,
}
}
}
+11
View File
@@ -1,3 +1,4 @@
import { GitHubDiscussionSource, fetchDiscussions } from './github-discussion'
import { MarkdownSource } from './markdown'
import {
CliReferenceSource,
@@ -13,6 +14,7 @@ export type SearchSource =
| OpenApiReferenceSource
| ClientLibReferenceSource
| CliReferenceSource
| GitHubDiscussionSource
/**
* Fetches all the sources we want to index for search
@@ -71,6 +73,14 @@ export async function fetchSources() {
.filter(({ path }) => !ignoredFiles.includes(path))
.map((entry) => new MarkdownSource('guide', entry.path))
const githubDiscussionSources = (
await fetchDiscussions(
'supabase',
'supabase',
'DIC_kwDODMpXOc4CUvEr' // 'Troubleshooting' category
)
).map((discussion) => new GitHubDiscussionSource('supabase/supabase', discussion))
const sources: SearchSource[] = [
openApiReferenceSource,
jsLibReferenceSource,
@@ -78,6 +88,7 @@ export async function fetchSources() {
pythonLibReferenceSource,
cSharpLibReferenceSource,
cliReferenceSource,
...githubDiscussionSources,
...guideSources,
]
+2 -1
View File
@@ -110,7 +110,8 @@ export function processMdxForSearch(content: string): ProcessedMdx {
})
const meta = extractMetaExport(mdxTree)
const serializableMeta: Json = JSON.parse(JSON.stringify(meta))
const serializableMeta: Json = meta && JSON.parse(JSON.stringify(meta))
// Remove all MDX elements from markdown
const mdTree = filter(
@@ -104,7 +104,7 @@ Soon we're going to need to perform a similarity search over these embeddings. L
```sql
create or replace function match_documents (
query_embedding vector(1536),
similarity_threshold float,
match_threshold float,
match_count int
)
returns table (
@@ -117,12 +117,12 @@ as $$
begin
return query
select
id,
content,
documents.id,
documents.content,
1 - (documents.embedding <=> query_embedding) as similarity
from documents
where 1 - (documents.embedding <=> query_embedding) > similarity_threshold
order by documents.embedding <=> query_embedding
where 1 - (documents.embedding <=> query_embedding) > match_threshold
order by similarity desc
limit match_count;
end;
$$;
@@ -15,10 +15,16 @@ thumb: launch-week-6/pggraphql/og-pg-graphql.png
---
It's been 4 months since the [1.0.0 release of pg_graphql](https://supabase.com/blog/pg-graphql-v1).
Since then, we’ve pushed several features to improve the APIs that `pg_graphql` produces.
Since then, we’ve pushed several features to improve the APIs that `pg_graphql` produces.
In this article, we’ll walk through those features and show examples of each.
<div className="bg-gray-300 rounded-lg px-6 py-2 italic">
📢 These features are only available on projects with Postgres version `15.1.0.63` or higher. For help with upgrading, please review the [migrating and upgrading projects guide](https://supabase.com/docs/guides/platform/migrating-and-upgrading-projects).
</div>
### View Support
Prior to `v1.1`, `pg_graphql` would only reflect standard tables. Since then, views, materialized views, and foreign tables are now also reflected in the GraphQL schema.
@@ -26,7 +32,7 @@ Prior to `v1.1`, `pg_graphql` would only reflect standard tables. Since then, vi
For example:
```sql
create view "ProjectOwner" as
create view "ProjectOwner" as
select
acc.id,
acc.name
@@ -70,7 +76,7 @@ With associated `Edge` and `Connection` types. That enables querying via:
}
```
Additionally, simple views automatically support mutation events like inserts and updates. You might use these to migrate underlying tables while maintaining backwards compatibility with previous API versions.
Additionally, simple views automatically support mutation events like inserts and updates. You might use these to migrate underlying tables while maintaining backwards compatibility with previous API versions.
## Filtering
@@ -109,7 +115,7 @@ to return all `blog`s where the `name` is `null`.
### **`like`**, **`ilike`**, and **`startsWith`**
Text filtering options in `pg_graphql` have historically been restricted to equality checks. The hesitation was due to concerns about exposing a default filter that is difficult to index. The combination of [citext](https://www.postgresql.org/docs/current/citext.html) and [PGroonga available on the platform](https://supabase.com/docs/guides/database/extensions/pgroonga) solves those scalability risks and enabled us to expand the `StringFilter` with options for `like` `ilike` and `startsWith`.
Text filtering options in `pg_graphql` have historically been restricted to equality checks. The hesitation was due to concerns about exposing a default filter that is difficult to index. The combination of [citext](https://www.postgresql.org/docs/current/citext.html) and [PGroonga available on the platform](https://supabase.com/docs/guides/database/extensions/pgroonga) solves those scalability risks and enabled us to expand the `StringFilter` with options for `like` `ilike` and `startsWith`.
```graphql
input StringFilter {
@@ -118,7 +124,7 @@ input StringFilter {
startsWith: String
like: String
ilike: String
}
}
```
Note that `startsWith` filters should be preferred where appropriate because they can leverage simple B-Tree indexes to improve performance.
@@ -139,7 +145,7 @@ Note that `startsWith` filters should be preferred where appropriate because the
### GraphQL directives `@skip` and `@include`
The GraphQL spec has evolved over time. Although the spec is clear, it is common for GraphQL servers to selectively omit some chunks of functionality. For example, some frameworks intentionally do not expose an introspection schema as a form of security through obscurity.
The GraphQL spec has evolved over time. Although the spec is clear, it is common for GraphQL servers to selectively omit some chunks of functionality. For example, some frameworks intentionally do not expose an introspection schema as a form of security through obscurity.
`pg_graphql` aims to be unopinionated and adhere exactly to the spec. The `@skip` and `@include` directives are part of the [GraphQL core specification](https://spec.graphql.org/October2021/#sec--skip) and are now functional.
@@ -198,6 +204,6 @@ The headline features we aim to launch in coming releases of `pg_graphql` includ
## More pg_graphql
- [Introducing pg_graphql: A GraphQL extension for PostgreSQL](https://supabase.com/blog/pg-graphql)
- [Introducing pg_graphql: A GraphQL extension for PostgreSQL](https://supabase.com/blog/pg-graphql)
- [GraphQL is now available in Supabase](https://supabase.com/blog/graphql-now-available)
- [pg_graphql v1.0](https://supabase.com/blog/pg-graphql-v1)
-1
View File
@@ -42,7 +42,6 @@
"parse-numeric-range": "^1.3.0",
"react": "^17.0.2",
"react-copy-to-clipboard": "^5.0.2",
"react-countdown": "^2.3.5",
"react-dom": "^17.0.2",
"react-ga": "^3.3.0",
"react-markdown": "^8.0.3",
+1 -1
View File
@@ -57,7 +57,7 @@ services:
auth:
container_name: supabase-auth
image: supabase/gotrue:v2.47.0
image: supabase/gotrue:v2.60.7
depends_on:
db:
# Disable this if you are using an external Postgres database
+67 -12
View File
@@ -42,6 +42,9 @@
"@mdx-js/loader": "^1.6.22",
"@mdx-js/react": "^1.6.22",
"@next/mdx": "^12.0.4",
"@octokit/auth-app": "^4.0.9",
"@octokit/core": "^4.2.0",
"@octokit/plugin-paginate-graphql": "^2.0.1",
"@radix-ui/react-accordion": "^1.0.1",
"@supabase/auth-helpers-nextjs": "^0.5.6",
"@supabase/auth-helpers-react": "^0.3.1",
@@ -89,6 +92,7 @@
"ui": "*",
"unist-builder": "^3.0.1",
"unist-util-filter": "^4.0.1",
"uuid": "^9.0.0",
"valtio": "^1.7.6"
},
"devDependencies": {
@@ -593,6 +597,14 @@
"url": "https://opencollective.com/unified"
}
},
"apps/docs/node_modules/uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"apps/docs/node_modules/valtio": {
"version": "1.7.6",
"license": "MIT",
@@ -683,7 +695,6 @@
"parse-numeric-range": "^1.3.0",
"react": "^17.0.2",
"react-copy-to-clipboard": "^5.0.2",
"react-countdown": "^2.3.5",
"react-dom": "^17.0.2",
"react-ga": "^3.3.0",
"react-markdown": "^8.0.3",
@@ -5821,6 +5832,14 @@
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
"integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
},
"node_modules/@octokit/plugin-paginate-graphql": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-2.0.1.tgz",
"integrity": "sha512-ZlhrWMlkGeK7qkWMKPKb6b7SP6lowN0lsOm+eyDssTBMoEZKSfwK+mrG4YtUhM66YNrJI22L32Vt+CcjteU0Vw==",
"peerDependencies": {
"@octokit/core": ">=4"
}
},
"node_modules/@octokit/plugin-paginate-rest": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz",
@@ -9547,9 +9566,9 @@
}
},
"node_modules/@supabase/gotrue-js": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.18.1.tgz",
"integrity": "sha512-nXTQPbV1t1WRmdAfUeed0uIXiTwNplCGVEnHbbymyx0V7YZShlzgayuB5WZDRZLau5jg616NIRWf6/9VNF3hlg==",
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.24.0.tgz",
"integrity": "sha512-ZsH4K5cbMTjfMytXaDYVYs9l9igmlZFxiwXn7J2IP/CklWR5qmLCma+dvat5rccPLITVkN6oAZbKxDzW+pEgCg==",
"dependencies": {
"cross-fetch": "^3.1.5"
}
@@ -38890,7 +38909,7 @@
"license": "MIT",
"dependencies": {
"@headlessui/react": "^1.5.0",
"@supabase/gotrue-js": "^2.16.0",
"@supabase/gotrue-js": "2.24.0",
"@supabase/ui": "^0.37.0-alpha.50",
"react-use": "^17.4.0"
},
@@ -39005,6 +39024,7 @@
"postcss": "^8.4.5",
"prop-types": "^15.7.2",
"react-copy-to-clipboard": "^5.1.0",
"react-countdown": "^2.3.5",
"react-feather": "^2.0.10",
"react-markdown": "^8.0.3",
"react-syntax-highlighter": "^15.5.0",
@@ -39018,6 +39038,7 @@
"@storybook/react": "^6.5.16",
"@types/common-tags": "^1.8.1",
"@types/react": "^17.0.37",
"@types/react-copy-to-clipboard": "^5.0.4",
"@types/react-dom": "^17.0.11",
"sql-formatter": "^12.2.0",
"storybook-dark-mode": "^2.1.1",
@@ -39047,6 +39068,15 @@
"csstype": "^3.0.2"
}
},
"packages/ui/node_modules/@types/react-copy-to-clipboard": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.4.tgz",
"integrity": "sha512-otTJsJpofYAeaIeOwV5xBUGpo6exXG2HX7X4nseToCB2VgPEBxGBHCm/FecZ676doNR7HCSTVtmohxfG2b3/yQ==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"packages/ui/node_modules/clsx": {
"version": "1.2.1",
"license": "MIT",
@@ -43523,6 +43553,12 @@
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-16.0.0.tgz",
"integrity": "sha512-JbFWOqTJVLHZSUUoF4FzAZKYtqdxWu9Z5m2QQnOyEa04fOFljvyh7D3GYKbfuaSWisqehImiVIMG4eyJeP5VEA=="
},
"@octokit/plugin-paginate-graphql": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-2.0.1.tgz",
"integrity": "sha512-ZlhrWMlkGeK7qkWMKPKb6b7SP6lowN0lsOm+eyDssTBMoEZKSfwK+mrG4YtUhM66YNrJI22L32Vt+CcjteU0Vw==",
"requires": {}
},
"@octokit/plugin-paginate-rest": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-6.0.0.tgz",
@@ -46230,9 +46266,9 @@
}
},
"@supabase/gotrue-js": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.18.1.tgz",
"integrity": "sha512-nXTQPbV1t1WRmdAfUeed0uIXiTwNplCGVEnHbbymyx0V7YZShlzgayuB5WZDRZLau5jg616NIRWf6/9VNF3hlg==",
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/@supabase/gotrue-js/-/gotrue-js-2.24.0.tgz",
"integrity": "sha512-ZsH4K5cbMTjfMytXaDYVYs9l9igmlZFxiwXn7J2IP/CklWR5qmLCma+dvat5rccPLITVkN6oAZbKxDzW+pEgCg==",
"requires": {
"cross-fetch": "^3.1.5"
}
@@ -49775,12 +49811,12 @@
"version": "file:packages/common",
"requires": {
"@headlessui/react": "^1.5.0",
"@supabase/gotrue-js": "^2.16.0",
"@supabase/gotrue-js": "2.24.0",
"@supabase/ui": "^0.37.0-alpha.50",
"@types/react": "^17.0.37",
"@types/react-dom": "^17.0.11",
"config": "*",
"react-use": "*",
"react-use": "^17.4.0",
"tsconfig": "*",
"typescript": "^4.5.3"
},
@@ -51481,6 +51517,9 @@
"@mdx-js/loader": "^1.6.22",
"@mdx-js/react": "^1.6.22",
"@next/mdx": "^12.0.4",
"@octokit/auth-app": "^4.0.9",
"@octokit/core": "^4.2.0",
"@octokit/plugin-paginate-graphql": "^2.0.1",
"@radix-ui/react-accordion": "^1.0.1",
"@supabase/auth-helpers-nextjs": "^0.5.6",
"@supabase/auth-helpers-react": "^0.3.1",
@@ -51542,6 +51581,7 @@
"ui": "*",
"unist-builder": "^3.0.1",
"unist-util-filter": "^4.0.1",
"uuid": "^9.0.0",
"valtio": "^1.7.6"
},
"dependencies": {
@@ -51877,6 +51917,11 @@
"unist-util-is": "^4.0.0"
}
},
"uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg=="
},
"valtio": {
"version": "1.7.6",
"requires": {
@@ -66373,7 +66418,7 @@
"config": "*",
"configcat-js": "^7.0.0",
"dayjs": "^1.11.0",
"eslint-config-next": "*",
"eslint-config-next": "^13.3.0",
"file-saver": "^2.0.5",
"generate-password": "^1.7.0",
"graphql": "^16.6.0",
@@ -68050,6 +68095,7 @@
"@tailwindcss/typography": "^0.5.0",
"@types/common-tags": "^1.8.1",
"@types/react": "^17.0.37",
"@types/react-copy-to-clipboard": "^5.0.4",
"@types/react-dom": "^17.0.11",
"autoprefixer": "^10.4.2",
"clsx": "^1.2.1",
@@ -68063,6 +68109,7 @@
"postcss": "^8.4.5",
"prop-types": "^15.7.2",
"react-copy-to-clipboard": "^5.1.0",
"react-countdown": "^2.3.5",
"react-feather": "^2.0.10",
"react-markdown": "^8.0.3",
"react-syntax-highlighter": "^15.5.0",
@@ -68094,6 +68141,15 @@
"csstype": "^3.0.2"
}
},
"@types/react-copy-to-clipboard": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.4.tgz",
"integrity": "sha512-otTJsJpofYAeaIeOwV5xBUGpo6exXG2HX7X4nseToCB2VgPEBxGBHCm/FecZ676doNR7HCSTVtmohxfG2b3/yQ==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"clsx": {
"version": "1.2.1"
},
@@ -69822,7 +69878,6 @@
"postcss-preset-env": "^6.7.0",
"react": "^17.0.2",
"react-copy-to-clipboard": "^5.0.2",
"react-countdown": "^2.3.5",
"react-dom": "^17.0.2",
"react-ga": "^3.3.0",
"react-markdown": "^8.0.3",
+1 -1
View File
@@ -6,7 +6,7 @@
"license": "MIT",
"dependencies": {
"@headlessui/react": "^1.5.0",
"@supabase/gotrue-js": "^2.16.0",
"@supabase/gotrue-js": "2.24.0",
"@supabase/ui": "^0.37.0-alpha.50",
"react-use": "^17.4.0"
},
+4 -2
View File
@@ -42,15 +42,17 @@
"sse.js": "^0.6.1",
"tailwind-merge": "^1.10.0",
"tailwindcss": "^3.0.15",
"tailwindcss-radix": "^1.6.0"
"tailwindcss-radix": "^1.6.0",
"react-countdown": "^2.3.5"
},
"devDependencies": {
"@storybook/react": "^6.5.16",
"storybook-dark-mode": "^2.1.1",
"@types/common-tags": "^1.8.1",
"@types/react": "^17.0.37",
"@types/react-copy-to-clipboard": "^5.0.4",
"@types/react-dom": "^17.0.11",
"sql-formatter": "^12.2.0",
"storybook-dark-mode": "^2.1.1",
"typescript": "4.3.4"
}
}
@@ -7,7 +7,9 @@ import {
IconAlertTriangle,
IconBook,
IconChevronRight,
IconGitHub,
IconHash,
IconMessageSquare,
IconSearch,
useCommandMenu,
} from 'ui'
@@ -27,6 +29,7 @@ const questions = [
export enum PageType {
Markdown = 'markdown',
Reference = 'reference',
GithubDiscussion = 'github-discussions',
}
export interface PageSection {
@@ -163,14 +166,11 @@ const DocsSearch = () => {
value={`${page.meta.title}-item-index-${i}`}
type="block-link"
onSelect={() => {
// TODO: replace with Next.js router/Link when cross-project link logic solved
window.location.assign(`/docs/${page.path}`)
openLink(page.type, formatPageUrl(page))
}}
>
<div className="grow flex gap-3 items-center">
<IconContainer>
<IconBook strokeWidth={1.5} className="!mr-0 !w-4 !h-4" />
</IconContainer>
<IconContainer>{getPageIcon(page)}</IconContainer>
<div className="flex flex-col gap-0">
<CommandLabel>
<TextHighlighter text={page.meta.title} query={search} />
@@ -190,21 +190,14 @@ const DocsSearch = () => {
forceMount
className="ml-3 mb-3"
onSelect={() => {
// TODO: replace with Next.js router/Link when cross-project link logic solved
window.location.assign(
`/docs/${page.path}${page.type === PageType.Reference ? '/' : '#'}${
section.slug
}`
)
openLink(page.type, formatSectionUrl(page, section))
}}
key={`${page.meta.title}__${section.heading}-item-index-${i}`}
value={`${page.meta.title}__${section.heading}-item-index-${i}`}
type="block-link"
>
<div className="grow flex gap-3 items-center">
<IconContainer>
<IconHash strokeWidth={1.5} className="!mr-0 !w-4 !h-4" />
</IconContainer>
<IconContainer>{getPageSectionIcon(page)}</IconContainer>
<div className="flex flex-col gap-2">
<cite>
<TextHighlighter
@@ -281,3 +274,63 @@ const DocsSearch = () => {
}
export default DocsSearch
export function formatPageUrl(page: PageResult) {
switch (page.type) {
case PageType.Markdown:
case PageType.Reference:
return `/docs/${page.path}`
case PageType.GithubDiscussion:
return page.path
default:
throw new Error(`Unknown page type '${page.type}'`)
}
}
export function formatSectionUrl(page: PageResult, section: PageSection) {
switch (page.type) {
case PageType.Markdown:
case PageType.GithubDiscussion:
return `${formatPageUrl(page)}#${section.slug}`
case PageType.Reference:
return `${formatPageUrl(page)}/${section.slug}`
default:
throw new Error(`Unknown page type '${page.type}'`)
}
}
export function getPageIcon(page: PageResult) {
switch (page.type) {
case PageType.Markdown:
case PageType.Reference:
return <IconBook strokeWidth={1.5} className="!mr-0 !w-4 !h-4" />
case PageType.GithubDiscussion:
return <IconGitHub strokeWidth={1.5} className="!mr-0 !w-4 !h-4" />
default:
throw new Error(`Unknown page type '${page.type}'`)
}
}
export function getPageSectionIcon(page: PageResult) {
switch (page.type) {
case PageType.Markdown:
case PageType.Reference:
return <IconHash strokeWidth={1.5} className="!mr-0 !w-4 !h-4" />
case PageType.GithubDiscussion:
return <IconMessageSquare strokeWidth={1.5} className="!mr-0 !w-4 !h-4" />
default:
throw new Error(`Unknown page type '${page.type}'`)
}
}
export function openLink(pageType: PageType, link: string) {
switch (pageType) {
case PageType.Markdown:
case PageType.Reference:
return window.location.assign(link)
case PageType.GithubDiscussion:
return window.open(link, '_blank')
default:
throw new Error(`Unknown page type '${pageType}'`)
}
}
@@ -109,7 +109,11 @@ const SidePanel = ({
onCloseAutoFocus={props.onCloseAutoFocus}
onEscapeKeyDown={props.onEscapeKeyDown}
onPointerDownOutside={props.onPointerDownOutside}
onInteractOutside={props.onInteractOutside}
onInteractOutside={(event) => {
const isToast = (event.target as Element)?.closest('#toast')
if (isToast) event.preventDefault()
if (props.onInteractOutside) props.onInteractOutside(event)
}}
>
{header && <header className={__styles.header}>{header}</header>}
<div className={__styles.contents}>{children}</div>
+202 -18
View File
@@ -1,7 +1,7 @@
elispec: '001'
clispec: '001'
info:
id: cli
version: 1.42.0
version: 1.50.13
title: Supabase CLI
language: sh
source: https://github.com/supabase/cli
@@ -405,6 +405,25 @@ commands:
- id: supabase-test-db
title: supabase test db
summary: Tests local database with pgTAP
description: |2
Executes pgTAP tests against the local database.
Requires the local development stack to be started by running `supabase start`.
Runs `pg_prove` in a container with unit test files volume mounted from `supabase/tests` directory. The test file can be suffixed by either `.sql` or `.pg` extension.
Since each test is wrapped in its own transaction, it will be individually rolled back regardless of success or failure.
examples:
- id: basic-usage
name: Basic usage
code: supabase test db
response: |
/tmp/supabase/tests/nested/order_test.pg .. ok
/tmp/supabase/tests/pet_test.sql .......... ok
All tests successful.
Files=2, Tests=2, 6 wallclock secs ( 0.03 usr 0.01 sys + 0.05 cusr 0.02 csys = 0.11 CPU)
Result: PASS
tags: []
links: []
usage: supabase test db
@@ -460,7 +479,8 @@ commands:
response: |
supabase local development setup is running.
API URL: http://localhost:54321
API URL: http://localhost:54321
GraphQL URL: http://localhost:54321/graphql/v1
DB URL: postgresql://postgres:postgres@localhost:54322/postgres
Studio URL: http://localhost:54323
Inbucket URL: http://localhost:54324
@@ -474,6 +494,7 @@ commands:
ANON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0"
API_URL="http://localhost:54321"
DB_URL="postgresql://postgres:postgres@localhost:54322/postgres"
GRAPHQL_URL="http://localhost:54321/graphql/v1"
INBUCKET_URL="http://localhost:54324"
JWT_SECRET="super-secret-jwt-token-with-at-least-32-characters-long"
SERVICE_ROLE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU"
@@ -485,6 +506,7 @@ commands:
Stopped services: [supabase_inbucket_cli supabase_rest_cli supabase_studio_cli]
SUPABASE_ANON_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0"
DB_URL="postgresql://postgres:postgres@localhost:54322/postgres"
GRAPHQL_URL="http://localhost:54321/graphql/v1"
JWT_SECRET="super-secret-jwt-token-with-at-least-32-characters-long"
SUPABASE_SERVICE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU"
tags:
@@ -494,25 +516,25 @@ commands:
subcommands: []
flags:
- id: output
name: -o, --output <[ env | json | pretty | toml | yaml ]>
name: -o, --output <[ env | pretty | json | toml | yaml ]>
description: Output format of status variables.
default_value: pretty
accepted_values:
- id: env
name: env
type: '[ env | json | pretty | toml | yaml ]'
- id: json
name: json
type: '[ env | json | pretty | toml | yaml ]'
type: '[ env | pretty | json | toml | yaml ]'
- id: pretty
name: pretty
type: '[ env | json | pretty | toml | yaml ]'
type: '[ env | pretty | json | toml | yaml ]'
- id: json
name: json
type: '[ env | pretty | json | toml | yaml ]'
- id: toml
name: toml
type: '[ env | json | pretty | toml | yaml ]'
type: '[ env | pretty | json | toml | yaml ]'
- id: yaml
name: yaml
type: '[ env | json | pretty | toml | yaml ]'
type: '[ env | pretty | json | toml | yaml ]'
- id: override-name
name: --override-name <strings>
description: Override specific variable names.
@@ -536,6 +558,7 @@ commands:
name: Basic usage
code: supabase start
response: |
Creating custom roles supabase/roles.sql...
Applying migration 20220810154536_employee.sql...
Seeding data supabase/seed.sql...
Started supabase local development setup.
@@ -545,13 +568,11 @@ commands:
response: |
Excluding container: supabase/studio:20221214-4eecc99
Excluding container: darthsim/imgproxy:v3.8.0
Seeding data supabase/seed.sql...
Started supabase local development setup.
- id: ignore-health-check
name: Ignore service health checks
code: supabase start --ignore-health-check
response: |
Seeding data supabase/seed.sql...
service not healthy: [supabase_storage_cli]
Started supabase local development setup.
tags:
@@ -563,7 +584,7 @@ commands:
- id: exclude
name: -x, --exclude <strings>
description: |
Names of containers to not start. [gotrue,realtime,storage-api,imgproxy,kong,inbucket,postgrest,pgadmin-schema-diff,migra,postgres-meta,studio,deno-relay]
Names of containers to not start. [gotrue,realtime,storage-api,imgproxy,kong,inbucket,postgrest,pgadmin-schema-diff,migra,postgres-meta,studio,edge-runtime,logflare,vector]
default_value: '[]'
- id: ignore-health-check
name: --ignore-health-check
@@ -759,6 +780,9 @@ commands:
- id: us-west-1
name: us-west-1
type: string
- id: us-west-2
name: us-west-2
type: string
- id: supabase-orgs
title: supabase orgs
summary: Manage Supabase organizations
@@ -1089,19 +1113,19 @@ commands:
name: Basic usage
code: supabase link --project-ref ********************
response: |
Enter your database password: ********
Enter your database password (or leave blank to skip): ********
Finished supabase link.
- id: without-password
name: Link without database password
code: supabase link --project-ref ******************** <<< ""
response: |
Enter your database password:
Enter your database password (or leave blank to skip):
Finished supabase link.
- id: using-alternate-dns
name: Link using DNS-over-HTTPS resolver
code: supabase link --project-ref ******************** --dns-resolver https
response: |
Enter your database password:
Enter your database password (or leave blank to skip):
Finished supabase link.
tags:
- local-dev
@@ -1429,6 +1453,25 @@ commands:
- id: supabase-db-reset
title: supabase db reset
summary: Resets the local database to current migrations
description: |2
Resets the local database to a clean state.
Requires the local development stack to be started by running `supabase start`.
Recreates the local Postgres container and applies all local migrations found in `supabase/migrations` directory. If test data is defined in `supabase/seed.sql`, it will be seeded after the migrations are run. Any other data or schema changes made during local development will be discarded.
Note that since Postgres roles are cluster level entities, those changes will persist between resets. In order to reset custom roles, you need to restart the local development stack.
examples:
- id: basic-usage
name: Basic usage
code: supabase db reset
response: |
Resetting database...
Initializing schema...
Applying migration 20220810154537_create_employees_table.sql...
Seeding data supabase/seed.sql...
Finished supabase db reset on branch main.
tags: []
links: []
usage: supabase db reset
@@ -1469,6 +1512,37 @@ commands:
- id: supabase-db-push
title: supabase db push
summary: Push new migrations to the remote database
description: |2
Pushes all local migrations to a remote database.
Requires your local project to be linked to a remote database by running `supabase link`. For self-hosted databases, you can pass in the connection parameters using `--db-url` flag.
The first time this command is run, a migration history table will be created under `supabase_migrations.schema_migrations`. After successfully applying a migration, a new row will be inserted into the migration history table with timestamp as its unique id. Subsequent pushes will skip migrations that have already been applied.
If you need to mutate the migration history table, such as deleting existing entries or inserting new entries without actually running the migration, use the `migration repair` command.
Use the `--dry-run` flag to view the list of changes before applying.
examples:
- id: basic-usage
name: Basic usage
code: supabase db push
response: |
Linked project is up to date.
- id: self-hosted
name: Self hosted
code: supabase db push --db-url "postgres://user:pass@localhost:5432/postgres"
response: |
Pushing migration 20230410135622_create_employees_table.sql...
Finished supabase db push.
- id: dry-run
name: Dry run
code: supabase db push --dry-run
response: |
DRY RUN: migrations will *not* be pushed to the database.
Would push migration 20230410135622_create_employees_table.sql...
Would push migration 20230425064254_my_table.sql...
Finished supabase db push.
tags: []
links: []
usage: supabase db push [flags]
@@ -1490,6 +1564,40 @@ commands:
- id: supabase-db-lint
title: supabase db lint
summary: Checks local database for typing error
description: |2
Lints local database for schema errors.
Requires the local development stack to be running when linting against the local database. To lint against a remote or self-hosted database, specify the `--linked` or `--db-url` flag respectively.
Runs `plpgsql_check` extension in the local Postgres container to check for errors in all schemas. The default lint level is `warning` and can be raised to error via the `--level` flag.
To lint against specific schemas only, pass in the `--schema` flag.
examples:
- id: basic-usage
name: Basic usage
code: supabase db lint
response: |
Linting schema: public
No schema errors found
- id: schema-warnings
name: Warnings for a specific schema
code: supabase db lint --level warning --schema storage
response: |
Linting schema: storage
[
{
"function": "storage.search",
"issues": [
{
"level": "warning",
"message": "unused variable \"_bucketid\"",
"sqlState": "00000"
}
]
}
]
tags: []
links: []
usage: supabase db lint [flags]
@@ -1517,6 +1625,34 @@ commands:
- id: supabase-db-dump
title: supabase db dump
summary: Dumps data or schemas from the remote database
description: |2
Dumps contents from a remote database.
Requires your local project to be linked to a remote database by running `supabase link`. For self-hosted databases, you can pass in the connection parameters using `--db-url` flag.
Runs `pg_dump` in a container with additional flags to exclude Supabase managed schemas. The ignored schemas include auth, stroage, and those created by extensions.
The default dump does not contain any data or custom roles. To dump those contents explicitly, specify either the `--data-only` and `--role-only` flag.
examples:
- id: basic-usage
name: Basic usage
code: supabase db dump -f supabase/schema.sql
response: |
Dumping schemas from remote database...
Dumped schema to supabase/schema.sql.
- id: role-only
name: Role only
code: supabase db dump -f supabase/roles.sql --role-only
response: |
Dumping roles from remote database...
Dumped schema to supabase/roles.sql.
- id: data-only
name: Data only
code: supabase db dump -f supabase/seed.sql --data-only
response: |
Dumping data from remote database...
Dumped schema to supabase/seed.sql.
tags: []
links: []
usage: supabase db dump [flags]
@@ -1545,6 +1681,54 @@ commands:
- id: supabase-db-diff
title: supabase db diff
summary: Diffs the local database for schema changes
description: |2
Diffs schema changes made to the local or remote database.
Requires the local development stack to be running when diffing against the local database. To diff against a remote or self-hosted database, specify the `--linked` or `--db-url` flag respectively.
Runs [djrobstep/migra](https://github.com/djrobstep/migra) in a container to compare schema differences between the target database and a shadow database. The shadow database is created by applying migrations in local `supabase/migrations` directory in a separate container. Output is written to stdout by default. For convenience, you can also save the schema diff as a new migration file by passing in `-f` flag.
By default, all schemas in the target database are diffed. Use the `--schema public,extensions` flag to restrict diffing to a subset of schemas.
While the diff command is able to capture most schema changes, there are cases where it is known to fail. Currently, this could happen if you schema contains:
- Changes to publication
- Changes to storage buckets
- Views with `security_invoker` attributes
examples:
- id: basic-usage
name: Basic usage
code: supabase db diff -f my_table
response: |
Connecting to local database...
Creating shadow database...
Applying migration 20230425064254_remote_commit.sql...
Diffing schemas: auth,extensions,public,storage
Finished supabase db diff on branch main.
No schema changes found
- id: linked-project
name: Against linked project
code: supabase db diff -f my_table --linked
response: |
Connecting to local database...
Creating shadow database...
Diffing schemas: auth,extensions,public,storage
Finished supabase db diff on branch main.
WARNING: The diff tool is not foolproof, so you may need to manually rearrange and modify the generated migration.
Run supabase db reset to verify that the new migration does not generate errors.
- id: specific-schema
name: For a specific schema
code: supabase db diff -f my_table --schema auth
response: |
Connecting to local database...
Creating shadow database...
Diffing schemas: auth
Finished supabase db diff on branch main.
No schema changes found
tags: []
links: []
usage: supabase db diff [flags]
@@ -1602,7 +1786,7 @@ commands:
To load completions in your current shell session:
source <(supabase completion zsh); compdef _supabase supabase
source <(supabase completion zsh)
To load completions for every new session, execute once:
@@ -183,12 +183,6 @@ const PolicyEditorModal: FC<Props> = ({
/>,
]}
onCancel={onSelectCancel}
onInteractOutside={(event) => {
const isToast = (event.target as Element)?.closest('#toast')
if (isToast) {
event.preventDefault()
}
}}
>
<div className="">
{view === POLICY_MODAL_VIEWS.SELECTION ? (
@@ -1,44 +0,0 @@
import dayjs from 'dayjs'
import { FC } from 'react'
import { observer } from 'mobx-react-lite'
import { Badge, IconClipboard, IconExternalLink, IconGlobe } from 'ui'
import Table from 'components/to-be-cleaned/Table'
import SimpleCodeBlock from 'components/to-be-cleaned/SimpleCodeBlock'
import router, { useRouter } from 'next/router'
// import logDropdown from './logDropdown'
interface Props {
log: any
onClick: () => void
}
const FunctionLogsItem: FC<Props> = ({ log, onClick }) => {
const islogConfirmed = log.email_confirmed_at || log.phone_confirmed_at
const router = useRouter()
const { ref } = router.query
return (
<Table.tr key={log.id} onClick={onClick}>
<Table.td className="whitespace-nowrap">
<div className="flex items-center gap-2">
<div className="rounded bg-scale-500 px-2">{log.function_status}</div>
</div>
</Table.td>
<Table.td className="">
<span className="text-xs text-scale-1100">{log.method}</span>
</Table.td>
<Table.td className="">
<span className="font-mono text-xs text-scale-1100">{log.url}</span>
</Table.td>
<Table.td className="">
<span className="text-scale-1100">
{dayjs(log.created_at).format('DD MMM, YYYY HH:mm')}
</span>
</Table.td>
</Table.tr>
)
}
export default observer(FunctionLogsItem)
@@ -75,14 +75,6 @@ const Results = ({ id, rows }: { id: string; rows: readonly any[] }) => {
)
}
if (rows.length <= 0) {
return (
<div className="bg-table-header-light dark:bg-table-header-dark">
<p className="m-0 border-0 px-6 py-4 font-mono text-sm">Success. No rows returned</p>
</div>
)
}
return (
<>
<DataGrid
@@ -4,6 +4,7 @@ import { Button, IconChevronRight, IconMaximize2, IconPlay } from 'ui'
import Table from 'components/to-be-cleaned/Table'
import { useRouter } from 'next/router'
import SqlSnippetCode from './Logs.SqlSnippetCode'
import { timestampLocalFormatter, TimestampLocalFormatter } from './LogsFormatters'
interface Props {
item: any
@@ -33,12 +34,12 @@ const SavedQueriesItem: FC<Props> = ({ item }: Props) => {
</Table.td>
<Table.td className="">
<span className="text-scale-1100">
{dayjs(item.inserted_at).format('DD MMM, YYYY HH:mm')}
{timestampLocalFormatter(item.inserted_at)}
</span>
</Table.td>
<Table.td className="">
<span className="text-scale-1100">
{dayjs(item.updated_at).format('DD MMM, YYYY HH:mm')}
{timestampLocalFormatter(item.updated_at)}
</span>
</Table.td>
<Table.td className=" text-right">
@@ -235,9 +235,16 @@ export const TimestampLocalFormatter = ({
className?: string
value: string | number
}) => {
return <span className={`text-xs ${className}`}>{timestampLocalFormatter(value)}</span>
}
/**
* Formats a string to local timestamp display
* Accepts unix microsecond or iso timestamp
*/
export const timestampLocalFormatter = (value: string | number) => {
const timestamp = isUnixMicro(value) ? unixMicroToIsoTimestamp(value) : value
const formattedTimestamp = dayjs(timestamp).format('DD MMM, HH:mm:ss')
return <span className={`text-xs ${className}`}>{formattedTimestamp}</span>
return dayjs(timestamp).format('DD MMM HH:mm:ss')
}
/*
@@ -198,10 +198,6 @@ const ColumnEditor = ({
applyFunction={(resolve: () => void) => onSaveChanges(resolve)}
/>
}
onInteractOutside={(event) => {
const isToast = (event.target as Element)?.closest('#toast')
if (isToast) event.preventDefault()
}}
>
<FormSection header={<FormSectionLabel className="lg:!col-span-4">General</FormSectionLabel>}>
<FormSectionContent loading={false} className="lg:!col-span-8">
@@ -143,10 +143,6 @@ const RowEditor = ({
isEditingJson || isSelectingForeignKey ? ' mr-32' : ''
}`}
onCancel={closePanel}
onInteractOutside={(event) => {
const isToast = (event.target as Element)?.closest('#toast')
if (isToast) event.preventDefault()
}}
>
<form onSubmit={(e) => onSaveChanges(e)} className="h-full">
<div className="flex h-full flex-col">
@@ -192,10 +192,6 @@ const SpreadsheetImport: FC<Props> = ({
applyFunction={onConfirm}
/>
}
onInteractOutside={(event) => {
const isToast = (event.target as Element)?.closest('#toast')
if (isToast) event.preventDefault()
}}
>
<SidePanel.Content>
<div className="pt-6">
@@ -183,12 +183,6 @@ const TableEditor: FC<Props> = ({
applyFunction={(resolve: () => void) => onSaveChanges(resolve)}
/>
}
onInteractOutside={(event) => {
const isToast = (event.target as Element)?.closest('#toast')
if (isToast) {
event.preventDefault()
}
}}
>
<>
<SidePanel.Content>
@@ -0,0 +1,3 @@
alter table "public"."page"
add "version" uuid,
add "last_refresh" timestamptz;