Merge branch 'master' into feat/careers-page

This commit is contained in:
Jonathan Summers-Muir committed 2022-11-30 01:11:56 +08:00
commit 9e0525e9c5
1811 files changed
+58427 -121688

No files matched your search

+1 -1
View File
@@ -1,4 +1,4 @@
/studio/ @supabase/frontend
/apps/www/ @supabase/frontend
/apps/reference/ @supabase/docs
/apps/docs/ @supabase/docs
/spec/ @supabase/docs
@@ -1,42 +0,0 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Docs Compilation Check
on:
pull_request:
branches: [master]
paths:
- 'apps/reference/**'
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
# Ref: https://github.com/actions/setup-node/issues/214
- name: Reconfigure git to use HTTP authentication
run: >
git config --global url."https://github.com/".insteadOf
ssh://git@github.com/
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install Deps
run: npm ci
working-directory: ./apps/reference
- name: Build
run: npm run build
working-directory: ./apps/reference
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
with:
ref: master
- uses: sobolevn/misspell-fixer-action@master
- uses: peter-evans/create-pull-request@v2.4.4
- uses: peter-evans/create-pull-request@v4
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
with:
+5 -1
View File
@@ -2,6 +2,8 @@ name: Publish to Image Registry
on:
push:
branches:
- 'studio'
tags:
- '*'
workflow_dispatch:
@@ -25,6 +27,7 @@ jobs:
latest=false
tags: |
type=ref,event=tag
type=sha,prefix={{date 'YYYYMMDD'}},enable=${{ github.ref_type == 'branch' }}
type=raw,value=${{ inputs.version }},enable=${{ github.event_name != 'push' }}
- uses: docker/setup-qemu-action@v2
@@ -49,7 +52,8 @@ jobs:
- uses: docker/build-push-action@v3
with:
push: true
context: '{{defaultContext}}:studio'
context: '{{defaultContext}}'
file: studio/Dockerfile
target: production
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
+55 -153
View File
@@ -1,45 +1,47 @@
# Developing Supabase
1. [Development setup](#development-setup)
- [Install dependencies](#install-dependencies)
- [Fork the repository](#fork-the-repository)
1. [Build Supabase](#build-supabase)
- [Choose a directory](#choose-a-directory)
1. [Start a development server](#start-a-development-server)
- [Supabase Website](#supabase-website)
- [Supabase Docs](#supabase-docs)
- [Supabase Studio](#supabase-studio)
1. [Create a pull request](#create-a-pull-request)
1. [Getting started](#getting-started)
- [Install dependencies](#install-dependencies)
2. [Local development](#local-development)
- [Fork the repository](#fork-the-repository)
- [Clone the repo](#clone-the-repo)
- [Running turborepo](#running-turborepo)
- [Shared components](#shared-components)
- [Installing packages](#installing-packages)
3. [Create a pull request](#create-a-pull-request)
- [Common tasks](#common-tasks)
- [Add a redirect](#add-a-redirect)
- [Monorepo](#monorepo)
- [Getting started](#getting-started)
- [Shared components](#shared-components)
- [Installing packages](#installing-packages)
- [Development](#development)
- [Community channels](#community-channels)
## Development setup
## Getting started
Thanks for your interest in Supabase and for wanting to contribute! Before you begin, read the
Thanks for your interest in [Supabase](https://supabase.com) and for wanting to contribute! Before you begin, read the
[code of conduct](https://github.com/supabase/.github/blob/main/CODE_OF_CONDUCT.md) and check out the
[existing issues](https://github.com/supabase/supabase/issues).
This document describes how to set up your development environment to build and test Supabase.
This document describes how to set up your development environment to build and test [Supabase](https://supabase.com).
### Install dependencies
You need to install and configure the following dependencies on your machine to build Supabase:
You need to install and configure the following dependencies on your machine to build [Supabase](https://supabase.com):
- [Git](http://git-scm.com/)
- [Node.js v16.x (LTS)](http://nodejs.org)
- [npm](https://www.npmjs.com/) version 7+ or [Yarn](https://yarnpkg.com/)
## Local development
We are in the process of migrating this repository to monorepo, using [Turborepo](https://turborepo.org/docs).
Eventually, all the apps will be run using [Turborepo](https://turborepo.org/docs), which will significantly improve the developer workflow.
If you are working on the ([docs site](https://supabase.com/docs)), please refer to this [developers guide](https://github.com/supabase/supabase/tree/master/apps/reference/DEVELOPERS.md).
### Fork the repository
To contribute code to Supabase, you must fork the [Supabase Repository](https://github.com/supabase/supabase).
To contribute code to [Supabase](https://supabase.com), you must fork the [Supabase Repository](https://github.com/supabase/supabase).
## Build Supabase
### Clone the repo
1. Clone your GitHub forked repository:
@@ -52,104 +54,61 @@ To contribute code to Supabase, you must fork the [Supabase Repository](https://
cd supabase
```
### Choose a directory
### Running turborepo
Choose if you want to work on the [Supabase Website](https://supabase.com), [Supabase Docs](https://supabase.com/docs), or [Supabase Studio](https://app.supabase.com).
[Supabase](https://supabase.com) uses [Turborepo](https://turborepo.org/docs) to manage and run this monorepo.
1. Go to the [Supabase Website](https://supabase.com) directory
1. Install the dependences in the root of the repo.
```sh
cd apps/www
npm install # install dependencies
```
Go to the [Supabase Docs](https://supabase.com/docs) directory
2. You can then run the apps simultaneously with the following.
```sh
cd apps/reference
npm run dev # start all the applications
```
Go to the [Supabase Studio](https://app.supabase.com) directory
Then visit, and edit, any of the following sites:
```sh
cd studio
```
| Site | Directory | Scope name | Description | Local development server |
| ---------------------------------------------- | -------------- | ---------- | ------------------------------------ | ------------------------ |
| [supabase.com](https://supabase.com) | `/apps/www` | www | The main website | http://localhost:3000 |
| [app.supabase.com](https://app.supabase.com) | `/studio` | studio | Studio dashboard | http://localhost:8082 |
| [supabase.com/docs](https://supabase.com/docs) | `/apps/docs` | docs | Guides and Reference (Next.js based) | http://localhost:3001 |
1. Install npm/yarn dependencies:
#### Running sites individually
npm
You can run any of the sites individually by using the scope name. For example:
```sh
npm install
```
```sh
npm run dev:www
```
or with yarn
#### Shared components
```sh
yarn install
```
The monorepo has a set of shared components under `/packages`:
## Start a development server
- `/packages/common`: Common React code, shared between all sites.
- `/packages/config`: All shared config
- `/packages/spec`: Generates documentation using spec files.
- `/packages/tsconfig`: Shared Typescript settings
To debug code and to see your changes in real time, it is often useful to have a local HTTP server. Click one of the three links below to choose which development server you want to start.
#### Installing packages
- [Supabase Website](#supabase-website)
- [Supabase Docs](#supabase-docs)
- [Supabase Studio](#supabase-studio)
Installing a package with NPM workspaces requires you to add the `-w` flag to tell NPM which workspace you want to install into. Do not install dependencies in their local folder, install them from the route using the `-w` flag.
### Supabase Website
The format is: `npm install <package name> -w=<workspace to install in>`.
The website is moving to a new monorepo setup. See the [Monorepo](#monorepo) section below.
For example:
### Supabase Docs
- `npm install react -w common`: installs into `./packages/common`
- `npm install react -w www`: installs into `./apps/www`
- `npm install react -w studio`: installs into `./studio`
1. Build development server
You do not need to install `devDependencies` in each workspace. These can all be installed in the root package.
npm
```sh
npm run build
```
or with yarn
```sh
yarn build
```
1. Start development server
npm
```sh
npm run start
```
or with yarn
```sh
yarn start
```
1. Access the local server in your web browser at http://localhost:3010/docs.
### Supabase Studio
1. Start development server
npm
```sh
npm run dev
```
or with yarn
```sh
yarn dev
```
1. Access the local server in your web browser at http://localhost:8082/.
See the [Supabase Studio readme](./studio/README.md) for more information.
---
## Create a pull request
@@ -165,63 +124,6 @@ Did you have an issue, like a merge conflict, or don't know how to open a pull r
Create a new entry in the [`redirects.js`](https://github.com/supabase/supabase/blob/master/apps/www/lib/redirects.js) file in our main site.
## Monorepo
We are in the process of migrating this repository to monorepo, using Turborepo.
Eventually, the docs and the Studio will be run using Turborepo, which will significantly improve the developer workflow.
You must be using NPM 7 or higher.
### Getting started
```sh
npm install # install dependencies
npm run dev # start all the applications
```
Then edit and visit any of the following sites:
Site | Directory | Description | Local development server
---- | --------- | ----------- | ------------------------
[supabase.com](https://supabase.com) | `/apps/www` | The main website | http://localhost:3000
[supabase.com/docs](https://supabase.com/docs) | `apps/reference` | Guides and Reference documentation | http://localhost:3010/docs
[POC] Community forum | `/apps/temp-community-forum` | GitHub Discussions in a Next.js site | http://localhost:3002
[POC] DEV articles site | `/apps/temp-community-tutorials` | A Next.js site for our DEV articles (which community members can write) | http://localhost:3003
### Shared components
The monorepo has a set of shared components under `/packages`:
- `/packages/common`: Common React code, shared between all sites.
- `/packages/config`: All shared config
- `/packages/spec`: Generates documentation using spec files.
- `/packages/tsconfig`: Shared Typescript settings
- `/packages/ui`: Shared UI components (formerly @supabase/ui)
To use these 'packages', or any of their components from a Next.JS app, you must use `next-transpile-modules` in the `next.config.js` file. This looks like:
```tsx
// next.config.js
const withTM = require('next-transpile-modules')(['ui', 'common'])
module.exports = withTM({})
```
### Installing packages
Installing a package with NPM workspaces requires you to add the `-w` flag to tell NPM which workspace you want to install into.
The format is: `npm install <package name> -w=<workspace to install in>`.
For example:
- `npm install @supabase/ui -w common`: installs into `./packages/common`
- `npm install @supabase/ui -w www`: installs into `./apps/www`
You do not need to install `devDependencies` in each workspace. These can all be installed in the root package.
### Development
`npm run dev`
---
## Community channels
+24 -15
View File
@@ -107,6 +107,15 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
<td><a href="https://github.com/supabase/realtime-js" target="_blank" rel="noopener noreferrer">realtime-js</a></td>
<td><a href="https://github.com/supabase/storage-js" target="_blank" rel="noopener noreferrer">storage-js</a></td>
<td><a href="https://github.com/supabase/functions-js" target="_blank" rel="noopener noreferrer">functions-js</a></td>
</tr>
<tr>
<td>Flutter</td>
<td><a href="https://github.com/supabase/supabase-flutter" target="_blank" rel="noopener noreferrer">supabase-flutter</a></td>
<td><a href="https://github.com/supabase/postgrest-dart" target="_blank" rel="noopener noreferrer">postgrest-dart</a></td>
<td><a href="https://github.com/supabase/gotrue-dart" target="_blank" rel="noopener noreferrer">gotrue-dart</a></td>
<td><a href="https://github.com/supabase/realtime-dart" target="_blank" rel="noopener noreferrer">realtime-dart</a></td>
<td><a href="https://github.com/supabase/storage-dart" target="_blank" rel="noopener noreferrer">storage-dart</a></td>
<td><a href="https://github.com/supabase/functions-dart" target="_blank" rel="noopener noreferrer">functions-dart</a></td>
</tr>
<th colspan="7">💚 Community 💚</th>
<tr>
@@ -118,20 +127,11 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
<td><a href="https://github.com/supabase-community/storage-csharp" target="_blank" rel="noopener noreferrer">storage-csharp</a></td>
<td><a href="https://github.com/supabase-community/functions-csharp" target="_blank" rel="noopener noreferrer">functions-csharp</a></td>
</tr>
<tr>
<td>Flutter</td>
<td><a href="https://github.com/supabase/supabase-flutter" target="_blank" rel="noopener noreferrer">supabase-flutter</a></td>
<td><a href="https://github.com/supabase/postgrest-dart" target="_blank" rel="noopener noreferrer">postgrest-dart</a></td>
<td><a href="https://github.com/supabase/gotrue-dart" target="_blank" rel="noopener noreferrer">gotrue-dart</a></td>
<td><a href="https://github.com/supabase/realtime-dart" target="_blank" rel="noopener noreferrer">realtime-dart</a></td>
<td><a href="https://github.com/supabase/storage-dart" target="_blank" rel="noopener noreferrer">storage-dart</a></td>
<td><a href="https://github.com/supabase-community/functions-dart" target="_blank" rel="noopener noreferrer">functions-dart</a></td>
</tr>
<tr>
<td>Go</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/postgrest-go" target="_blank" rel="noopener noreferrer">postgrest-go</a></td>
<td>-</td>
<td><a href="https://github.com/supabase-community/gotrue-go" target="_blank" rel="noopener noreferrer">gotrue-go</a></td>
<td>-</td>
<td><a href="https://github.com/supabase-community/storage-go" target="_blank" rel="noopener noreferrer">storage-go</a></td>
<td><a href="https://github.com/supabase-community/functions-go" target="_blank" rel="noopener noreferrer">functions-go</a></td>
@@ -147,12 +147,12 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
</tr>
<tr>
<td>Kotlin</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/supabase-kt" target="_blank" rel="noopener noreferrer">supabase-kt</a></td>
<td><a href="https://github.com/supabase-community/postgrest-kt" target="_blank" rel="noopener noreferrer">postgrest-kt</a></td>
<td><a href="https://github.com/supabase-community/gotrue-kt" target="_blank" rel="noopener noreferrer">gotrue-kt</a></td>
<td>-</td>
<td>-</td>
<td>-</td>
<td><a href="https://github.com/supabase-community/supabase-kt/tree/master/Realtime" target="_blank" rel="noopener noreferrer">realtime-kt</a></td>
<td><a href="https://github.com/supabase-community/supabase-kt/tree/master/Storage" target="_blank" rel="noopener noreferrer">storage-kt</a></td>
<td><a href="https://github.com/supabase-community/supabase-kt/tree/master/Functions" target="_blank" rel="noopener noreferrer">functions-kt</a></td>
</tr>
<tr>
<td>Python</td>
@@ -188,7 +188,16 @@ Our approach for client libraries is modular. Each sub-library is a standalone i
<td><a href="https://github.com/supabase-community/gotrue-swift" target="_blank" rel="noopener noreferrer">gotrue-swift</a></td>
<td><a href="https://github.com/supabase-community/realtime-swift" target="_blank" rel="noopener noreferrer">realtime-swift</a></td>
<td><a href="https://github.com/supabase-community/storage-swift" target="_blank" rel="noopener noreferrer">storage-swift</a></td>
<td>-</td>
<td><a href="https://github.com/supabase-community/functions-swift" target="_blank" rel="noopener noreferrer">functions-swift</a></td>
</tr>
<tr>
<td>Godot Engine (GDScript)</td>
<td><a href="https://github.com/supabase-community/godot-engine.supabase" target="_blank" rel="noopener noreferrer">supabase-gdscript</a></td>
<td><a href="https://github.com/supabase-community/postgrest-gdscript" target="_blank" rel="noopener noreferrer">postgrest-gdscript</a></td>
<td><a href="https://github.com/supabase-community/gotrue-gdscript" target="_blank" rel="noopener noreferrer">gotrue-gdscript</a></td>
<td><a href="https://github.com/supabase-community/realtime-gdscript" target="_blank" rel="noopener noreferrer">realtime-gdscript</a></td>
<td><a href="https://github.com/supabase-community/storage-gdscript" target="_blank" rel="noopener noreferrer">storage-gdscript</a></td>
<td><a href="https://github.com/supabase-community/functions-gdscript" target="_blank" rel="noopener noreferrer">functions-gdscript</a></td>
</tr>
</table>
File renamed without changes.
File renamed without changes.
@@ -42,4 +42,4 @@ We use documentation specifications which can be used to generate human-readable
- CLISpec (custom to Supabase): for CLI commands and usage.
The benefit of using custom specifications is that we can generate many other types from a strict schema (eg, HTML and manpages).
It also means that we can switch any documentation system we want. On this site we use Docusaurus, but in Supabase's official website we use a custom React site and expose only a subset of the available API for each tool.
It also means that we can switch any documentation system we want. On this site we use Next.JS, but in Supabase's official website we use a custom React site and expose only a subset of the available API for each tool.
+51
View File
@@ -0,0 +1,51 @@
import { FC } from 'react'
import { IconInfo, IconHelpCircle, IconAlertTriangle } from 'ui'
interface Props {
type: 'note' | 'tip' | 'info' | 'caution' | 'danger'
label?: string
children: any
}
const Admonition: FC<Props> = ({ type = 'note', label, children }) => {
return (
<div
className={[
'shadow p-4 rounded border-l-[5px] space-y-2 my-4',
`${
type === 'note'
? 'bg-scale-400 border-scale-800'
: type === 'info'
? 'bg-scale-500 border-scale-800'
: type === 'tip'
? 'bg-brand-300 border-brand-800'
: type === 'caution'
? 'bg-yellow-400 border-yellow-800'
: type === 'danger'
? 'bg-red-500 border-red-800'
: 'bg-scale-500 border-scale-800'
}`,
].join(' ')}
>
<div className="flex items-center space-x-2">
{type === 'note' ? (
<IconInfo className="text-scale-1200" size={18} strokeWidth={1.5} />
) : type === 'info' ? (
<IconInfo className="text-scale-1200" size={18} strokeWidth={1.5} />
) : type === 'tip' ? (
<IconHelpCircle className="text-scale-1200" size={18} strokeWidth={1.5} />
) : type === 'caution' ? (
<IconAlertTriangle className="text-scale-1200" size={18} strokeWidth={1.5} />
) : type === 'danger' ? (
<IconAlertTriangle className="text-scale-1200" size={18} strokeWidth={1.5} />
) : (
<IconInfo className="text-scale-1200" size={18} strokeWidth={1.5} />
)}
<p className="text-sm text-scale-1200 uppercase my-0 font-bold">{label || type}</p>
</div>
<div className="admonition-content text-scale-1200 text-base space-y-1">{children}</div>
</div>
)
}
export default Admonition
+53
View File
@@ -0,0 +1,53 @@
import providers from '../data/authProviders'
import ButtonCard from './ButtonCard'
export default function AuthProviders() {
return (
<div className="grid md:grid-cols-12 gap-4">
{providers.map((x) => (
<div key={x.name} className="col-span-6">
<ButtonCard to={x.href} title={x.name}>
<div className="px-6 py-4">
<div className="flex justify-between">
<p className="mt-0">{x.name}</p>
<p className="mt-0">
{x.official ? (
<span className={`badge badge--official`}>Official</span>
) : (
<span className={`badge badge--unofficial`}>Unofficial</span>
)}
</p>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
<div
className="code-block"
style={{
width: '100%',
display: 'flex',
justifyContent: 'space-between',
fontSize: '0.7rem',
}}
>
<span>Platform:</span>
<span>{x.platform.toString()}</span>
</div>
<div
className="code-block"
style={{
width: '100%',
display: 'flex',
justifyContent: 'space-between',
fontSize: '0.7rem',
}}
>
<span>Self-Hosted:</span>
<span>{x.selfHosted.toString()}</span>
</div>
</div>
</div>
</ButtonCard>
</div>
))}
</div>
)
}
+55
View File
@@ -0,0 +1,55 @@
import React, { FC, ReactNode } from 'react'
import Link from 'next/link'
import Image from 'next/image'
interface Props {
title: string
description?: string
to: string
icon?: string | any
children?: any
layout?: 'vertical' | 'horizontal'
}
const ButtonCard: FC<Props> = ({
children = undefined,
icon = undefined,
title,
description = '',
to,
layout = 'vertical',
}) => {
return (
<Link href={to}>
<a
className={[
'h-full block shadow-none bg-scale-300 rounded transition',
'border border-transparent hover:border-scale-600',
].join('')}
>
{children ? (
children
) : (
<div
className={[
'px-6 py-4 gap-x-4 gap-y-2 flex',
`${layout === 'vertical' ? 'flex-col' : 'items-center'}`,
].join(' ')}
>
{icon && typeof icon == 'string' ? (
<div className="w-[24px] h-[24px]">
<Image className="m-0" src={icon} width={24} height={24} alt={title} />
</div>
) : (
icon
)}
<h3 className="my-0 text-base">{title}</h3>
<p className="my-0 text-sm">{description}</p>
</div>
)}
</a>
</Link>
)
}
export default ButtonCard
@@ -0,0 +1,137 @@
import { FC } from 'react'
import CopyToClipboard from 'react-copy-to-clipboard'
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter'
import monokaiCustomTheme from './CodeBlock.utils'
import { Button, IconCheck, IconCopy } from 'ui'
import js from 'react-syntax-highlighter/dist/cjs/languages/hljs/javascript'
import ts from 'react-syntax-highlighter/dist/cjs/languages/hljs/typescript'
import py from 'react-syntax-highlighter/dist/cjs/languages/hljs/python'
import sql from 'react-syntax-highlighter/dist/cjs/languages/hljs/sql'
import bash from 'react-syntax-highlighter/dist/cjs/languages/hljs/bash'
import dart from 'react-syntax-highlighter/dist/cjs/languages/hljs/dart'
import { useState } from 'react'
import { useTheme } from '../Providers'
interface Props {
title?: string
language: 'js' | 'jsx' | 'sql' | 'py' | 'bash' | 'ts' | 'dart'
linesToHighlight?: number[]
hideCopy?: boolean
hideLineNumbers?: boolean
className?: string
value?: string
children?: string
}
const CodeBlock: FC<Props> = ({
title,
language,
linesToHighlight = [],
className,
value,
children,
hideCopy = false,
hideLineNumbers = false,
}) => {
const { isDarkMode } = useTheme()
const monokaiTheme = monokaiCustomTheme(isDarkMode)
const [copied, setCopied] = useState(false)
const handleCopy = () => {
setCopied(true)
setTimeout(() => {
setCopied(false)
}, 1000)
}
let lang = language ? language : className ? className.replace('language-', '') : 'js'
// force jsx to be js highlighted
if (lang === 'jsx') lang = 'js'
SyntaxHighlighter.registerLanguage('js', js)
SyntaxHighlighter.registerLanguage('ts', ts)
SyntaxHighlighter.registerLanguage('py', py)
SyntaxHighlighter.registerLanguage('sql', sql)
SyntaxHighlighter.registerLanguage('bash', bash)
SyntaxHighlighter.registerLanguage('dart', dart)
const large = false
// don't show line numbers if bash == lang
const showLineNumbers = hideLineNumbers || lang !== 'bash'
return (
<div className="relative my-2 max-w-[90vw] md:max-w-none overflow-auto">
{title && (
<div className="rounded-t-md bg-scale-300 py-2 px-4 border-b border-scale-500 text-blue-1100 font-sans">
{title.replace(/%20/g, ' ')}
</div>
)}
{className ? (
<SyntaxHighlighter
language={lang}
wrapLines={true}
style={monokaiTheme}
className={[
'code-block border p-4 w-full !my-0 !bg-scale-300',
`${!title ? '!rounded-md' : '!rounded-t-none !rounded-b-md'}`,
`${!showLineNumbers ? 'pl-6' : ''}`,
].join(' ')}
customStyle={{
fontSize: large ? 18 : 13,
lineHeight: large ? 1.5 : 1.4,
}}
showLineNumbers={showLineNumbers}
lineProps={(lineNumber) => {
if (linesToHighlight.includes(lineNumber)) {
return {
style: { display: 'block', backgroundColor: 'var(--colors-scale6)' },
}
}
return {}
}}
lineNumberContainerStyle={{
paddingTop: '128px',
}}
lineNumberStyle={{
minWidth: '44px',
paddingLeft: '4px',
paddingRight: '4px',
marginRight: '12px',
color: '#828282',
textAlign: 'center',
fontSize: large ? 14 : 12,
paddingTop: '4px',
paddingBottom: '4px',
}}
>
{(value || children)?.trimEnd()}
</SyntaxHighlighter>
) : (
<code>{value || children}</code>
)}
{!hideCopy && (value || children) && className ? (
<div
className={[
'absolute right-2',
`${isDarkMode ? 'dark' : ''}`,
`${!title ? 'top-2' : 'top-[3.25rem]'}`,
].join(' ')}
>
<CopyToClipboard text={value || children}>
<Button
type="default"
icon={copied ? <IconCheck /> : <IconCopy />}
onClick={() => handleCopy()}
>
{copied ? 'Copied' : ''}
</Button>
</CopyToClipboard>
</div>
) : null}
</div>
)
}
export default CodeBlock
@@ -1,3 +1,32 @@
// https://github.com/euank/node-parse-numeric-range/blob/master/index.js
export function parseNumericRange(string) {
let res = []
let m
for (let str of string.split(',').map((str) => str.trim())) {
// just a number
if (/^-?\d+$/.test(str)) {
res.push(parseInt(str, 10))
} else if ((m = str.match(/^(-?\d+)(-|\.\.\.?|\u2025|\u2026|\u22EF)(-?\d+)$/))) {
// 1-5 or 1..5 (equivalent) or 1...5 (doesn't include 5)
let [_, lhs, sep, rhs] = m
if (lhs && rhs) {
lhs = parseInt(lhs)
rhs = parseInt(rhs)
const incr = lhs < rhs ? 1 : -1
// Make it inclusive by moving the right 'stop-point' away by one.
if (sep === '-' || sep === '..' || sep === '\u2025') rhs += incr
for (let i = lhs; i !== rhs; i += incr) res.push(i)
}
}
}
return res
}
const monokaiCustomTheme = (isDarkMode) => {
return {
hljs: {
@@ -0,0 +1,79 @@
// Check if heading has custom anchor first, before forming the anchor based on the title
export const getAnchor = (text: any): string | undefined => {
if (typeof text === 'object') {
if (Array.isArray(text)) {
const customAnchor = text.find(
(x) => typeof x === 'string' && x.includes('[#') && x.endsWith(']')
)
if (customAnchor !== undefined) return customAnchor.slice(2, customAnchor.indexOf(']'))
const formattedText = text
.map((x) => {
if (typeof x !== 'string') return x.props.children
else return x.trim()
})
.map((x) => {
if (typeof x !== 'string') return x
else
return x
.toLowerCase()
.replace(/[^a-z0-9- ]/g, '')
.replace(/[ ]/g, '-')
})
return formattedText.join('-').toLowerCase()
} else {
const anchor = text.props.children
if (typeof anchor === 'string') {
return anchor
.toLowerCase()
.replace(/[^a-z0-9- ]/g, '')
.replace(/[ ]/g, '-')
}
return anchor
}
} else if (typeof text === 'string') {
if (text.includes('[#') && text.endsWith(']')) {
return text.slice(text.indexOf('[#') + 2, text.indexOf(']'))
} else {
return text
.toLowerCase()
.replace(/[^a-z0-9- ]/g, '')
.replace(/[ ]/g, '-')
}
} else {
return undefined
}
}
export const removeAnchor = (text: any) => {
if (typeof text === 'object' && Array.isArray(text)) {
return text.filter((x) => !(typeof x === 'string' && x.includes('[#') && x.endsWith(']')))
} else if (typeof text === 'string') {
if (text.indexOf('[#') > 0) return text.slice(0, text.indexOf('[#'))
else return text
}
return text
}
export const highlightSelectedTocItem = (id: string) => {
const tocMenuItems = document.querySelectorAll('.toc-menu a')
// find any currently active items and remove them
const currentActiveItem = document.querySelector('.toc-menu .toc__menu-item--active')
currentActiveItem?.classList.remove('toc__menu-item--active')
// Add active class to the current item
tocMenuItems.forEach((item) => {
// @ts-ignore
if (item.href.split('#')[1] === id) {
item.classList.add('toc__menu-item--active')
}
})
}
// find any currently active items and remove them on route change
export const unHighlightSelectedTocItems = () => {
const currentActiveItem = document.querySelector('.toc-menu .toc__menu-item--active')
currentActiveItem?.classList.remove('toc__menu-item--active')
}
@@ -0,0 +1,36 @@
import {
getAnchor,
removeAnchor,
highlightSelectedTocItem,
unHighlightSelectedTocItems,
} from './CustomHTMLElements.utils'
import { useInView } from 'react-intersection-observer'
const Heading = ({ tag, children }) => {
const HeadingTag = `${tag}` as any
const anchor = getAnchor(children)
const link = `#${anchor}`
// check if current heading is in view, update TOC active item accordingly
// [Joshen] Ideally we highlight the section in the TOC when it's at the top of the page
// much like when we click on the item in the TOC itself, the rootMargin fix is insufficient
const { ref } = useInView({
threshold: 1,
onChange: (inView, entry) => {
if (window.scrollY === 0) unHighlightSelectedTocItems()
if (inView) highlightSelectedTocItem(entry.target.id)
},
})
return (
<HeadingTag id={anchor} ref={ref} className="group scroll-mt-24">
{removeAnchor(children)}
{anchor && (
<a href={link} className="ml-2 opacity-0 group-hover:opacity-100 transition">
#
</a>
)}
</HeadingTag>
)
}
export default Heading
@@ -0,0 +1,11 @@
const InlineCodeTag = ({ children }) => {
// If children isn't a string, just return it as is, just in case
if (typeof children !== 'string') return children
// check the length of the string inside the <code> tag
// if it's fewer than 70 characters, add a white-space: pre so it doesn't wrap
const classes = children.length < 70 ? 'short-inline-codeblock' : ''
return <code className={classes}>{children}</code>
}
export default InlineCodeTag
@@ -0,0 +1,3 @@
import Heading from './Heading'
export { Heading }
@@ -7,9 +7,12 @@ import { DocSearchModal } from '@docsearch/react'
import clsx from 'clsx'
import { useActionKey } from '~/hooks/useActionKey'
const INDEX_NAME = 'tailwindcss_dev'
const API_KEY = '5fc87cef58bb80203d2207578309fab6'
const APP_ID = 'KNPXZI5B0M'
// [Joshen] We're currently using this over Algolia search for the time being
// Refer to the comment in Navigation/AlgoliaSearch for more information about this
const INDEX_NAME = process.env.NEXT_PUBLIC_ALGOLIA_INDEX_NAME
const API_KEY = process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY
const APP_ID = process.env.NEXT_PUBLIC_ALGOLIA_APP_ID
const SearchContext = createContext(null)
@@ -64,10 +67,6 @@ export function SearchProvider({ children }: any) {
// @ts-ignore
initialQuery={initialQuery}
initialScrollY={window.scrollY}
searchParameters={{
facetFilters: 'version:v3',
distinct: 1,
}}
placeholder="Search documentation"
onClose={onClose}
indexName={INDEX_NAME}
@@ -75,11 +74,14 @@ export function SearchProvider({ children }: any) {
appId={APP_ID}
// @ts-ignore
navigator={{
navigate({ suggestionUrl }) {
navigate({ itemUrl }) {
setIsOpen(false)
router.push(suggestionUrl)
router.push(itemUrl)
},
}}
getMissingResultsUrl={({ query }) => {
return `https://github.com/supabase/supabase/issues/new?title=Unable+to+search+docs+with+query:+${query}`
}}
hitComponent={Hit}
transformItems={(items) => {
return items.map((item, index) => {
+35
View File
@@ -0,0 +1,35 @@
import React, { useState } from 'react'
import extensions from '../data/extensions.json'
export default function Extensions() {
const [filter, setFilter] = useState('')
return (
<>
<div className="mb-8 grid">
<label className="text-xs mb-2">Filter extensions</label>
<input
type="text"
className="border text-gray-200"
placeholder="Extension name"
onChange={(e) => setFilter(e.target.value)}
/>
</div>
<div className="grid sm:grid-cols-2 gap-4">
{extensions
.filter((x) => x.name.indexOf(filter) >= 0)
.map((extension) => (
<div className={' my-2 px-2'} key={extension.name}>
<div className="border rounded-sm p-4">
<h3 className="m-0">
<code className="text-sm">{extension.name}</code>
</h3>
<p className=" mt-4">
{extension.comment.charAt(0).toUpperCase() + extension.comment.slice(1)}
</p>
</div>
</div>
))}
</div>
</>
)
}
+38
View File
@@ -0,0 +1,38 @@
import Head from 'next/head'
import { useRouter } from 'next/router'
const Favicons = () => {
const { basePath } = useRouter()
return (
<Head>
<link
rel="apple-touch-icon"
sizes="180x180"
href={`${basePath}/favicon/apple-touch-icon.png`}
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href={`${basePath}/favicon/favicon-32x32.png`}
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href={`${basePath}/favicon/favicon-16x16.png`}
/>
<link rel="manifest" href={`${basePath}/favicon/site.webmanifest`} />
{/* <link rel="mask-icon" href={`${basePath}/favicon/safari-pinned-tab.svg`} color="#333333" /> */}
<link rel="shortcut icon" href={`${basePath}/favicon/favicon.ico`} />
<meta name="msapplication-TileColor" content="#1E1E1E" />
<meta name="msapplication-config" content={`${basePath}/favicon/browserconfig.xml`} />
<meta name="theme-color" content="#1E1E1E" />
<link rel="alternate" type="application/rss+xml" href={`${basePath}/feed.xml`} />
<link rel="icon" type="image/png" href={`${basePath}/favicon/favicon.ico`} />
<link rel="apple-touch-icon" href={`${basePath}/favicon/favicon.ico`} />
</Head>
)
}
export default Favicons
@@ -1,10 +1,9 @@
import FooterLinks from 'data/footer.json'
import SectionContainer from './SectionContainer'
import DarkModeToggle from './DarkModeToggle'
import Link from 'next/link'
import { useTheme } from './Providers'
import { Badge } from 'ui'
import Link from 'next/link'
import Image from 'next/image'
import FooterLinks from 'data/footer.json'
import DarkModeToggle from './DarkModeToggle'
import { useTheme } from '../Providers'
import * as supabaseLogoWordmarkDark from 'common/assets/images/supabase-logo-wordmark--dark.png'
import * as supabaseLogoWordmarkLight from 'common/assets/images/supabase-logo-wordmark--light.png'
@@ -19,7 +18,7 @@ const Footer = () => {
<h2 id="footerHeading" className="sr-only">
Footer
</h2>
<SectionContainer>
<div className="sm:py-18 container relative mx-auto px-6 py-16 md:py-24 lg:px-16 lg:py-24 xl:px-20">
<div className="xl:grid xl:grid-cols-3 xl:gap-8">
<div className="space-y-8 xl:col-span-1">
<Link href="#" as="/">
@@ -126,7 +125,7 @@ const Footer = () => {
<small className="small">&copy; Supabase Inc</small>
<DarkModeToggle />
</div>
</SectionContainer>
</div>
</footer>
)
}
+106
View File
@@ -0,0 +1,106 @@
import ButtonCard from './ButtonCard'
import { useTheme } from '~/components/Providers'
const Frameworks = () => {
const { isDarkMode } = useTheme()
const frameworks = [
{
name: 'Angular',
logo: {
light: '/docs/img/libraries/angular-icon.svg',
dark: '/docs/img/libraries/angular-icon.svg',
},
href: '/guides/with-angular',
},
{
name: 'Expo',
logo: {
light: '/docs/img/libraries/expo-icon.svg',
dark: '/docs/img/libraries/expo-icon-dark.svg',
},
href: '/guides/with-expo',
},
{
name: 'Flutter',
logo: {
light: '/docs/img/libraries/flutter-icon.svg',
dark: '/docs/img/libraries/flutter-icon.svg',
},
href: '/guides/with-flutter',
},
{
name: 'JavaScript',
logo: {
light: '/docs/img/libraries/javascript-icon.svg',
dark: '/docs/img/libraries/javascript-icon.svg',
},
href: '/reference/javascript/installing#javascript',
},
{
name: 'Next.js',
logo: {
light: '/docs/img/libraries/nextjs-light-icon.svg',
dark: '/docs/img/libraries/nextjs-dark-icon.svg',
},
href: '/guides/with-nextjs',
},
{
name: 'React',
logo: {
light: '/docs/img/libraries/react-icon.svg',
dark: '/docs/img/libraries/react-icon.svg',
},
href: '/guides/with-react',
},
{
name: 'RedwoodJS',
logo: {
light: '/docs/img/libraries/redwoodjs-icon.svg',
dark: '/docs/img/libraries/redwoodjs-icon.svg',
},
href: '/guides/with-redwoodjs',
},
{
name: 'SolidJS',
logo: {
light: '/docs/img/libraries/solidjs-icon.svg',
dark: '/docs/img/libraries/solidjs-icon.svg',
},
href: '/guides/with-solidjs',
},
{
name: 'Svelte',
logo: {
light: '/docs/img/libraries/svelte-icon.svg',
dark: '/docs/img/libraries/svelte-icon.svg',
},
href: '/guides/with-svelte',
},
{
name: 'Vue',
logo: {
light: '/docs/img/libraries/vuejs-icon.svg',
dark: '/docs/img/libraries/vuejs-icon.svg',
},
href: '/guides/with-vue-3',
},
]
return (
<div className="grid md:grid-cols-12 gap-4">
{frameworks.map((x) => (
<div key={x.name} className="col-span-3">
<ButtonCard
layout="horizontal"
to={x.href}
title={x.name}
// [Joshen] Nice to have: theming
icon={isDarkMode ? x.logo.dark : x.logo.light}
/>
</div>
))}
</div>
)
}
export default Frameworks
@@ -1,10 +1,5 @@
---
id: examples
title: Examples
description: Useful Supabase Edge Functions Examples.
---
import ButtonCard from 'components/ButtonCard'
import ButtonCard from '@site/src/components/ButtonCard'
const examples = [
{
name: 'With supabase-js',
@@ -28,36 +23,43 @@ const examples = [
},
{
name: 'Building a RESTful Service API',
description: 'Learn how to use HTTP methods and paths to build a RESTful service for managing tasks.',
description:
'Learn how to use HTTP methods and paths to build a RESTful service for managing tasks.',
href: 'https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/restful-tasks/index.ts',
},
{
name: 'Working with Supabase Storage',
description: 'An example on reading a file from Supbase Storage.',
description: 'An example on reading a file from Supabase Storage.',
href: 'https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/read-storage/index.ts',
},
{
{
name: 'OpenGraph Image Generation',
description: 'Generate Open Graph images with Deno and Supabase Edge Functions, no framework needed.',
description:
'Generate Open Graph images with Deno and Supabase Edge Functions, no framework needed.',
href: 'https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/opengraph',
},
{
name: 'OG Image Generation & Storage CDN Caching',
description: 'Cache generated images with Supabase Storage CDN.',
href: 'https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/og-image-with-storage-cdn',
},
{
name: 'Get User Location',
description: `Get user location data from user's IP address.`,
href: 'https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/location',
},
]
You can find a list of useful [Edge Function Examples](https://github.com/supabase/supabase/tree/master/examples/edge-functions) in our GitHub repository.
const FunctionsExamples = () => {
return (
<div className="grid md:grid-cols-12 gap-4">
{examples.map((x) => (
<div className="col-span-6">
<ButtonCard to={x.href} title={x.name} description={x.description} />
</div>
))}
</div>
)
}
<div class="container" style={{ padding: 0 }}>
<div class="row is-multiline">
{examples.map((x) => (
<div class="col col--6">
<ButtonCard
class="card"
to={x.href}
title={x.name}
description={x.description}
style={{ height: '100%' }}
/>
</div>
))}
</div>
</div>
export default FunctionsExamples
+17
View File
@@ -0,0 +1,17 @@
import React from 'react'
export default function GithubCard({ title, description, href, stars, handle }) {
return (
<a className="h-full" href={href}>
<div className="card__body">
<h4 className="uppercase my-2 font-semibold">{title}</h4>
<span className="text-sm">{description}</span>
</div>
<hr className="my-2" />
<div className="flex justify-between py-2 text-xs">
<div>@{handle}</div>
<div>{stars} ★</div>
</div>
</a>
)
}
@@ -0,0 +1,74 @@
import { FC } from 'react'
import { removeAnchor } from './CustomHTMLElements/CustomHTMLElements.utils'
const formatSlug = (slug: string) => {
// [Joshen] We will still provide support for headers declared like this:
// ## REST API {#rest-api-overview}
// At least for now, this was a docusaurus thing.
if (slug.includes('#')) return slug.split('#')[1]
return slug
}
const formatTOCHeader = (content: string) => {
let begin = false
const res = []
for (const x of content) {
if (x === '`') {
if (!begin) {
begin = true
res.push(`<code class="text-xs border rounded bg-scale-400 border-scale-500">`)
} else {
begin = false
res.push(`</code>`)
}
} else {
res.push(x)
}
}
return res.join('')
}
interface TOCHeader {
id: number
level: number
text: string
link: string
}
interface Props {
list: TOCHeader[]
}
const GuidesTableOfContents: FC<Props> = ({ list }) => {
return (
<ul className="toc-menu list-none pl-4 text-[0.8rem] grid gap-2 mt-12">
{list.map((item, i) => (
<li key={`${item.level}-${i}`} className={item.level === 3 ? 'ml-4' : ''}>
<a
href={`#${formatSlug(item.link)}`}
className="text-scale-1000 hover:text-brand-900 transition-colors"
dangerouslySetInnerHTML={{ __html: formatTOCHeader(removeAnchor(item.text)) }}
/>
</li>
))}
</ul>
// <ul className="toc-menu list-none pl-4 text-[0.8rem] grid gap-2 mt-12">
// {(toc.json as TOCHeader[])
// .filter((item) => item.lvl !== 1 && item.lvl <= 3)
// .map((item: any, i: number) => {
// return (
// <li key={i} id={item.lvl} style={{ marginLeft: `${(item.lvl - 2) * 1}rem` }}>
// <a
// href={`#${formatSlug(item.slug)}`}
// className="text-scale-1000 hover:text-brand-900 transition-colors"
// dangerouslySetInnerHTML={{ __html: formatTOCHeader(removeAnchor(item.content)) }}
// />
// </li>
// )
// })}
// </ul>
)
}
export default GuidesTableOfContents
@@ -1,6 +1,7 @@
import React, { useState } from 'react'
import KJUR from 'jsrsasign'
import CustomCodeBlock from './CustomCodeBlock'
import CodeBlock from './CodeBlock/CodeBlock'
import { Button } from 'ui'
const JWT_HEADER = { alg: 'HS256', typ: 'JWT' }
const now = new Date()
@@ -25,9 +26,7 @@ const serviceToken = `
`.trim()
export default function JwtGenerator({}) {
const secret = [...Array(40)]
.map(() => Math.random().toString(36)[2])
.join('')
const secret = [...Array(40)].map(() => Math.random().toString(36)[2]).join('')
const [jwtSecret, setJwtSecret] = useState(secret)
const [token, setToken] = useState(anonToken)
@@ -45,72 +44,53 @@ export default function JwtGenerator({}) {
return (
<div>
<div style={styles.inputContainer}>
<label>JWT Secret:</label>
<div className="grid mb-8">
<label for="secret">JWT Secret:</label>
<input
id="secret"
className="border rounded bg-gray-200 w-full"
type="text"
style={styles.input}
placeholder="JWT Secret (at least 32 characters)"
value={jwtSecret}
onChange={(e) => setJwtSecret(e.target.value)}
/>
</div>
<div style={styles.inputContainer}>
<label>Preconfigured Payload:</label>
<select onChange={handleKeySelection} style={styles.input}>
<div className="grid mb-8">
<label for="service">Preconfigured Payload:</label>
<select
id="service"
onChange={handleKeySelection}
className="border rounded bg-gray-200 w-full"
>
<option value="anon">ANON_KEY</option>
<option value="service">SERVICE_KEY</option>
</select>
</div>
<div style={styles.inputContainer}>
<label>Payload:</label>
<div className="grid mb-8">
<label for="token">Payload:</label>
<textarea
id="token"
type="text"
style={styles.input}
rows="5"
placeholder="A valid JWT Token"
value={token}
onChange={(e) => setToken(e.target.value)}
/>
</div>
<button className="button button--primary" onClick={generate}>
<Button type="primary" onClick={generate}>
Generate JWT
</button>
</Button>
{signedToken && (
<div style={{ marginTop: 20 }}>
<h4>Generated Token</h4>
<CustomCodeBlock code={signedToken} />
<div className="mt-8">
<h4>Generated Token:</h4>
<CodeBlock language="bash" className="relative">
{signedToken}
</CodeBlock>
</div>
)}
</div>
)
}
const styles = {
inputContainer: {
marginBottom: '15px',
},
input: {
border: '1px solid var(--ifm-panel-border-color)',
borderRadius: 4,
backgroundColor: 'var(--custom-background-color-diff)',
color: 'var(--ifm-font-color-base)',
margin: 0,
padding: '6px 8px',
width: '100%',
},
card: {
border: '1px solid var(--ifm-panel-border-color)',
borderRadius: 5,
padding: 8,
},
title: {
margin: 0,
fontSize: '0.9rem',
border: '1px solid var(--ifm-panel-border-color)',
},
description: {
fontSize: '0.8rem',
margin: 0,
},
}
@@ -9,7 +9,7 @@ export default function LinkCard({
}: {
title: string
description: string
icon: ReactElement
icon: any
link: string
}) {
return (
@@ -1,5 +1,5 @@
import { ReactElement } from 'react'
export default function LinkCardsWrapper({ children }: { children: ReactElement }) {
export default function LinkCardsWrapper({ children }: { children: any }) {
return <div className="flex w-full flex-col lg:flex-row lg:flex-wrap">{children}</div>
}
@@ -1,6 +1,4 @@
import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'
import UserManagementSQLTemplate from './user_management_quickstart_sql_template.md'
import UserManagementSQLTemplate from './user_management_quickstart_sql_template.mdx'
## Project set up
@@ -20,31 +18,27 @@ Now we are going to set up the database schema. We can use the "User Management
or you can just copy/paste the SQL from below and run it yourself.
<Tabs
groupId="language"
defaultValue="dashboard"
values={[
{label: 'Dashboard', value: 'dashboard'},
{label: 'SQL', value: 'sql'},
]}>
<TabItem value="dashboard">
scrollable
size="small"
type="underlined"
defaultActiveId="dashboard"
>
<TabPanel id="dashboard" label="Dashboard">
1. Go to the [SQL Editor](https://app.supabase.com/project/_/sql) page in the Dashboard.
2. Click **User Management Starter**.
3. Click **Run**.
<video width="99%" muted playsInline controls="true">
<source
src="/docs/videos/sql-user-management-starter.mp4"
type="video/mp4"
muted
playsInline
/>
<source src="/docs/videos/sql-user-management-starter.mp4" type="video/mp4" muted playsInline />
</video>
</TabItem>
<TabItem value="sql">
</TabPanel>
<TabPanel id="sql" label="SQL">
<UserManagementSQLTemplate />
</TabItem>
</TabPanel>
</Tabs>
### Get the API Keys
@@ -57,10 +51,5 @@ We just need to get the URL and `anon` key from the API settings.
3. Find your API `URL`, `anon`, and `service_role` keys on this page.
<video width="99%" muted playsInline controls="true">
<source
src="/docs/videos/api/api-url-and-key.mp4"
type="video/mp4"
muted
playsInline
/>
<source src="/docs/videos/api/api-url-and-key.mp4" type="video/mp4" muted playsInline />
</video>
@@ -0,0 +1,169 @@
import { render } from 'react-dom'
import { IconCommand } from 'ui'
import { createElement, FC, useEffect, useRef, Fragment } from 'react'
import algoliasearch from 'algoliasearch/lite'
import { autocomplete, getAlgoliaResults } from '@algolia/autocomplete-js'
import { createLocalStorageRecentSearchesPlugin } from '@algolia/autocomplete-plugin-recent-searches'
import { useRouter } from 'next/router'
// [Joshen] We're currently using DocSearch from Algolia as it provides a nice
// UI out of the box + some good preconfigured search settings (e.g hierarchy).
// However, we're using our own Algolia account to store the records in the indexes
// (rather than going through the DocSearch program from Algolia where they'll crawl
// our site for us). Refer to scripts/build-search on how we're saving the records.
// Using Algolia's autocomplete library gives us full flexbility in terms of customizing
// our search experience, but that will take time to figure out. Hence why for now we're just
// using DocSearch with our own records.
// Potentially for search, we could
// - Go ahead with the DocSearch program and let them crawl our site to generate the records
// - But we need to ensure that our site is semantically correct first
// - Use Algolia itself to flesh out our own search logic
// - The basics are already set up to be honest, but will take time to make it great
// - Go back to Typesense if we deem that Algolia is not helpful in the long run
const searchClient = algoliasearch(
process.env.NEXT_PUBLIC_ALGOLIA_APP_ID,
process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY
)
// [Joshen] Not working properly, but lets not get stuck on this
// Priority just to get a search working
const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({
key: 'docs-search',
limit: 3,
//@ts-ignore
transformSource({ source }) {
return {
...source,
templates: {
...source.templates,
header({ state }) {
if (state.query) return null
return (
<Fragment>
<span className="aa-SourceHeaderTitle">Your searches</span>
<div className="aa-SourceHeaderLine" />
</Fragment>
)
},
},
}
},
})
interface Props {}
const AlgoliaSearch: FC<Props> = ({}) => {
const searchRef = useRef(null)
const router = useRouter()
useEffect(() => {
if (!searchRef.current) {
return undefined
}
const search = autocomplete({
openOnFocus: true,
container: searchRef.current,
defaultActiveItemId: 0,
detachedMediaQuery: '',
// @ts-ignore
renderer: { createElement, Fragment, render },
placeholder: 'Search docs',
plugins: [recentSearchesPlugin],
renderNoResults({ state, render }, root) {
render(
<div className="text-scale-1100 py-2 text-sm px-4">
No results found for {state.query}.
</div>,
root
)
},
navigator: {
navigate({ itemUrl }) {
router.push(itemUrl)
},
},
onSelect({ item, setQuery, setIsOpen, refresh }) {
console.log('onSelect', item.url)
},
// @ts-ignore
getSources({ query }) {
return [
{
sourceId: 'pages',
templates: {
item({ item, components }) {
return (
<a
href={item.url as string}
className="aa-ItemLink truncate flex justify-between space-x-4"
>
<div className="aa-ItemContent w-full">
<div className="aa-ItemTitle flex items-center space-x-1">
{item.category && (
<p
className={`${
['cli', 'api'].includes(item.category as string)
? 'uppercase'
: 'capitalize'
}`}
>
<>
{item.category}
{item.version ? ` (${item.version})` : ''}:
</>
</p>
)}
<p>
<components.Highlight hit={item} attribute="title" />
</p>
</div>
<p className="aa-ItemContentSubtitle">{item.description as string}</p>
</div>
</a>
)
},
},
getItemUrl({ item }) {
return item.url
},
getItems() {
// if (!query) return []
return getAlgoliaResults({
searchClient,
queries: [
{
indexName: 'dev_docs',
query,
},
],
})
},
},
]
},
})
return () => {
search.destroy()
}
}, [])
return (
<div className="w-[200px] relative">
<div ref={searchRef} />
<div className="flex items-center space-x-1 absolute top-[7px] right-2">
<div className="text-scale-1200 flex items-center justify-center h-6 w-6 rounded bg-scale-500">
<IconCommand size={12} strokeWidth={1.5} />
</div>
<div className="text-xs text-scale-1200 flex items-center justify-center h-6 w-6 rounded bg-scale-500">
K
</div>
</div>
</div>
)
}
export default AlgoliaSearch
@@ -2,9 +2,10 @@ import Image from 'next/image'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useState, useEffect, FC } from 'react'
import { IconMenu, IconSearch, Input, IconCommand, Button, IconMoon, IconSun, Listbox } from 'ui'
import { IconMenu, IconMoon, IconSearch, IconSun, IconCommand, Listbox } from 'ui'
import { useTheme } from '../Providers'
import { REFERENCES } from './Navigation.constants'
import { SearchButton } from '../DocSearch'
interface Props {
currentPage: string
@@ -13,6 +14,7 @@ interface Props {
const NavBar: FC<Props> = ({ currentPage }) => {
const { isDarkMode, toggleTheme } = useTheme()
const [mounted, setMounted] = useState(false)
const [mobileMenuOpen, setMobileMenuOpen] = useState(false)
const { asPath, push } = useRouter()
const pathSegments = asPath.split('/')
@@ -52,16 +54,26 @@ const NavBar: FC<Props> = ({ currentPage }) => {
}
}
// [Joshen] Kaizen: Use UI library's SidePanel for this
const toggleMobileMenu = () => {
setMobileMenuOpen(!mobileMenuOpen)
const sidebar = document.querySelector('.sidebar-menu-container')
const contentPane = document.querySelector('.main-content-pane')
sidebar.classList.toggle('hidden')
contentPane.classList.toggle('hidden')
}
return (
<nav
className={[
'sticky top-0 z-10 flex items-center justify-between',
'h-[60px] border-b bg-white p-4 backdrop-blur backdrop-filter',
'h-[60px] border-b bg-white px-4 backdrop-blur backdrop-filter',
'dark:border-scale-400 dark:bg-scale-200',
].join(' ')}
>
<div className="flex items-center">
<button className="mr-4 block stroke-2 lg:hidden">
<button className="mr-4 block stroke-2 lg:hidden" onClick={toggleMobileMenu}>
<IconMenu className="text-scale-1100" />
</button>
{mounted && (
@@ -111,6 +123,7 @@ const NavBar: FC<Props> = ({ currentPage }) => {
</div>
)}
</div>
<div className="flex items-center space-x-4">
<div className="hidden items-center md:flex">
<ul className="flex items-center">
@@ -180,19 +193,22 @@ const NavBar: FC<Props> = ({ currentPage }) => {
</li>
</ul>
</div>
<Input
placeholder="Search"
icon={<IconSearch />}
type="search"
actions={[
<Button disabled key="icon-command" type="default" size="tiny">
<IconCommand size="tiny" />
</Button>,
<Button disabled key="icon-letter" type="default" size="tiny">
K
</Button>,
]}
/>
<SearchButton>
<div className="flex items-center justify-between space-x-6 bg-scale-300 border border-scale-700 pl-3 pr-1.5 py-1.5 rounded">
<div className="flex items-center space-x-2">
<IconSearch className="text-scale-1100" size={18} strokeWidth={2} />
<p className="text-scale-800 text-sm">Search</p>
</div>
<div className="flex items-center space-x-1">
<div className="hidden text-scale-1200 md:flex items-center justify-center h-6 w-6 rounded bg-scale-500">
<IconCommand size={12} strokeWidth={1.5} />
</div>
<div className="hidden text-xs text-scale-1200 md:flex items-center justify-center h-6 w-6 rounded bg-scale-500">
K
</div>
</div>
</div>
</SearchButton>
</div>
</nav>
)
@@ -1,3 +1,5 @@
// [Terry] Also need to look at getPageType() in ./lib/helpers
// to set a menu to a page
import SupabaseJsV1Nav from 'data/nav/supabase-js/v1'
import SupabaseJsV2Nav from 'data/nav/supabase-js/v2'
import SupabaseDartV0Nav from 'data/nav/supabase-dart/v0'
@@ -5,6 +7,7 @@ import SupabaseDartV1Nav from 'data/nav/supabase-dart/v1'
import SupabaseCLINav from 'data/nav/supabase-cli'
import SupabaseAPINav from 'data/nav/supabase-api'
import AuthServerNav from 'data/nav/auth-server'
import RealtimeServerNav from 'data/nav/realtime-server'
import StorageServerNav from 'data/nav/storage-server'
import { NavMenu, References } from './Navigation.types'
@@ -20,19 +23,19 @@ export const REFERENCES: References = {
name: 'Flutter',
library: 'supabase-dart',
versions: ['v1', 'v0'],
icon: '/docs/img/libraries/dart-icon.svg',
icon: '/docs/img/libraries/flutter-icon.svg',
},
cli: {
name: 'CLI',
library: undefined,
versions: [],
icon: '/docs/img/libraries/cli-icon.svg',
icon: '/docs/img/icons/cli-icon.svg',
},
api: {
name: 'API',
library: undefined,
versions: [],
icon: '/docs/img/libraries/api-icon.svg',
icon: '/docs/img/icons/api-icon.svg',
},
}
@@ -42,7 +45,7 @@ export const menuItems: NavMenu = {
label: 'Overview',
items: [
{ name: 'Introduction', url: '/', items: [] },
{ name: 'Features', url: '/guides/features', items: [] },
{ name: 'Features', url: '/features', items: [] },
{ name: 'Architecture', url: '/architecture', items: [] },
],
},
@@ -99,9 +102,13 @@ export const menuItems: NavMenu = {
{ name: 'Login with Twitch', url: '/guides/auth/auth-twitch', items: [] },
{ name: 'Login with Twitter', url: '/guides/auth/auth-twitter', items: [] },
{ name: 'Login with WorkOS', url: '/guides/auth/auth-workos', items: [] },
{ name: 'Phone Auth with Twilio', url: '/guides/auth/twilio', items: [] },
{ name: 'Phone Auth with Vonage', url: '/guides/auth/vonage', items: [] },
{ name: 'Phone Auth with MessageBird', url: '/guides/auth/messagebird', items: [] },
{ name: 'Phone Auth with Twilio', url: '/guides/auth/auth-twilio', items: [] },
{ name: 'Phone Auth with Vonage', url: '/guides/auth/auth-vonage', items: [] },
{
name: 'Phone Auth with MessageBird',
url: '/guides/auth/auth-messagebird',
items: [],
},
],
},
{
@@ -122,37 +129,40 @@ export const menuItems: NavMenu = {
{ name: 'Overview', url: '/guides/auth/auth-helpers', items: [] },
{ name: 'Auth UI', url: '/guides/auth/auth-helpers/auth-ui', items: [] },
{ name: 'Next.js', url: '/guides/auth/auth-helpers/nextjs', items: [] },
{
name: 'Next.js Server Components',
url: '/guides/auth/auth-helpers/nextjs-server-components',
items: [],
},
{ name: 'SvelteKit', url: '/guides/auth/auth-helpers/sveltekit', items: [] },
{ name: 'Remix', url: '/guides/auth/auth-helpers/remix', items: [] },
],
},
{
name: 'Deep Dive',
url: undefined,
items: [
{ name: 'Part One: JWTs', url: 'learn/auth-deep-dive/auth-deep-dive-jwts', items: [] },
{
name: 'Part Two: Row Level Security',
url: 'learn/auth-deep-dive/auth-row-level-security',
name: 'Part One: JWTs',
url: '/learn/auth-deep-dive/auth-deep-dive-jwts',
items: [],
},
{ name: 'Part Three: Policies', url: 'learn/auth-deep-dive/auth-policies', items: [] },
{ name: 'Part Four: GoTrue', url: 'learn/auth-deep-dive/auth-gotrue', items: [] },
{
name: 'Part Fibe: Google OAuth',
url: 'learn/auth-deep-dive/auth-google-oauth',
name: 'Part Two: Row Level Security',
url: '/learn/auth-deep-dive/auth-row-level-security',
items: [],
},
{ name: 'Part Three: Policies', url: '/learn/auth-deep-dive/auth-policies', items: [] },
{ name: 'Part Four: GoTrue', url: '/learn/auth-deep-dive/auth-gotrue', items: [] },
{
name: 'Part Five: Google OAuth',
url: '/learn/auth-deep-dive/auth-google-oauth',
items: [],
},
],
},
],
},
{
label: 'APIs',
items: [
{ name: 'Overview', url: '/guides/api', items: [] },
{ name: 'Generating Types', url: '/guides/api/generating-types', items: [] },
],
},
{
label: 'Database',
items: [
@@ -160,24 +170,39 @@ export const menuItems: NavMenu = {
{ name: 'Database Connections', url: '/guides/database/connecting-to-postgres', items: [] },
{ name: 'Tables and Data', url: '/guides/database/tables', items: [] },
{ name: 'Database Functions', url: '/guides/database/functions', items: [] },
{ name: 'Database Webhooks', url: '/guides/database/webhooks', items: [] },
{ name: 'Full Text Search', url: '/guides/database/full-text-search', items: [] },
{ name: 'Database Testing', url: '/guides/database/testing', items: [] },
{
name: 'Migrating Between Projects',
url: '/guides/platform/migrating-and-upgrading-projects',
items: [],
name: 'Serverless APIs',
url: undefined,
items: [
{ name: 'Overview', url: '/guides/api', items: [] },
{ name: 'Generating Types', url: '/guides/api/generating-types', items: [] },
],
},
{
name: 'Extensions',
url: undefined,
items: [
{ name: 'Overview', url: '/guides/database/extensions', items: [] },
{ name: 'http: RESTful Client', url: '/guides/database/extensions/http', items: [] },
{
name: 'pg_cron: Job Scheduling',
url: '/guides/database/extensions/pgcron',
items: [],
},
{
name: 'pg_net: Async Networking',
url: '/guides/database/extensions/pgnet',
items: [],
},
{ name: 'pgTAP: Unit Testing', url: '/guides/database/extensions/pgtap', items: [] },
{
name: 'plv8: Javascript Language',
url: '/guides/database/extensions/plv8',
items: [],
},
{ name: 'http: RESTful Client', url: '/guides/database/extensions/http', items: [] },
{ name: 'pgTAP: Unit Testing', url: '/guides/database/extensions/pgtap', items: [] },
{
name: 'uuid-ossp: Unique Identifiers',
url: '/guides/database/extensions/uuid-ossp',
@@ -212,6 +237,7 @@ export const menuItems: NavMenu = {
{ name: 'Overview', url: '/guides/realtime', items: [] },
{ name: 'Quickstart', url: '/guides/realtime/quickstart', items: [] },
{ name: 'Postgres CDC', url: '/guides/realtime/postgres-cdc', items: [] },
{ name: 'Rate Limits', url: '/guides/realtime/rate-limits', items: [] },
],
},
{
@@ -225,11 +251,17 @@ export const menuItems: NavMenu = {
label: 'Platform',
items: [
{ name: 'Overview', url: '/guides/hosting/platform', items: [] },
{ name: 'Compute Add-ons', url: '/guides/platform/compute-add-ons', items: [] },
{ name: 'Custom Domains', url: '/guides/platform/custom-domains', items: [] },
{ name: 'Disk Space Usage', url: '/guides/platform/disk-usage', items: [] },
{ name: 'Database Usage', url: '/guides/platform/database-usage', items: [] },
{ name: 'Logging', url: '/guides/platform/logs', items: [] },
{ name: 'Metrics', url: '/guides/platform/metrics', items: [] },
{ name: 'Performance', url: '/guides/platform/performance', items: [] },
{
name: 'Migrating and upgrading',
url: '/guides/platform/migrating-and-upgrading-projects',
items: [],
},
{ name: 'Performance Tuning', url: '/guides/platform/performance', items: [] },
{ name: 'Permissions', url: '/guides/platform/permissions', items: [] },
{ name: 'Production Readiness', url: '/guides/platform/going-into-prod', items: [] },
],
@@ -247,7 +279,7 @@ export const menuItems: NavMenu = {
{ name: 'Firebase Auth', url: '/guides/migrations/firebase-auth', items: [] },
{ name: 'Firestore Data', url: '/guides/migrations/firestore-data', items: [] },
{ name: 'Firebase Storage', url: '/guides/migrations/firebase-storage', items: [] },
{ name: 'Herokuy', url: '/guides/migrations/heroku', items: [] },
{ name: 'Heroku', url: '/guides/migrations/heroku', items: [] },
],
},
{
@@ -258,12 +290,12 @@ export const menuItems: NavMenu = {
name: 'Auth',
url: undefined,
items: [
{ name: 'Auth0', url: '/guides/integration/auth0', items: [] },
{ name: 'Authsignal', url: '/guides/integration/authsignal', items: [] },
{ name: 'Clerk', url: '/guides/integration/clerk', items: [] },
{ name: 'keyri', url: '/guides/integration/keyri', items: [] },
{ name: 'Stytch', url: '/guides/integration/stytch', items: [] },
{ name: 'SuperTokens', url: '/guides/integration/supertokens', items: [] },
{ name: 'Auth0', url: '/guides/integrations/auth0', items: [] },
{ name: 'Authsignal', url: '/guides/integrations/authsignal', items: [] },
{ name: 'Clerk', url: '/guides/integrations/clerk', items: [] },
{ name: 'keyri', url: '/guides/integrations/keyri', items: [] },
{ name: 'Stytch', url: '/guides/integrations/stytch', items: [] },
{ name: 'SuperTokens', url: '/guides/integrations/supertokens', items: [] },
],
},
{
@@ -275,6 +307,7 @@ export const menuItems: NavMenu = {
name: 'Developer Tools',
url: undefined,
items: [
{ name: 'Estuary', url: '/guides/integrations/estuary', items: [] },
{ name: 'pgMustard', url: '/guides/integrations/pgmustard', items: [] },
{ name: 'Prisma', url: '/guides/integrations/prisma', items: [] },
{ name: 'Sequin', url: '/guides/integrations/sequin', items: [] },
@@ -292,6 +325,7 @@ export const menuItems: NavMenu = {
{ name: 'Directus', url: '/guides/integrations/directus', items: [] },
{ name: 'Draftbit', url: '/guides/integrations/draftbit', items: [] },
{ name: 'Plasmic', url: '/guides/integrations/plasmic', items: [] },
{ name: 'WeWeb', url: '/guides/integrations/weweb', items: [] },
],
},
],
@@ -302,7 +336,7 @@ export const menuItems: NavMenu = {
label: 'Official',
items: [
{ name: 'Reference Documentation', url: '/reference', items: [] },
{ name: 'Supabase Javascript Library', url: '/reference/javascript', items: [] },
{ name: 'Supabase JavaScript Library', url: '/reference/javascript', items: [] },
{ name: 'Supabase Flutter Library', url: '/reference/dart', items: [] },
{ name: 'Supabase CLI', url: '/reference/cli', items: [] },
{ name: 'Management API', url: '/reference/api', items: [] },
@@ -312,6 +346,7 @@ export const menuItems: NavMenu = {
label: 'Self-hosting',
items: [
{ name: 'Auth Server', url: '/reference/auth', items: [] },
{ name: 'Realtime Server', url: '/reference/realtime', items: [] },
{ name: 'Storage Server', url: '/reference/storage', items: [] },
],
},
@@ -323,5 +358,6 @@ export const menuItems: NavMenu = {
'reference/cli': SupabaseCLINav,
'reference/api': SupabaseAPINav,
'reference/auth': AuthServerNav,
'reference/realtime': RealtimeServerNav,
'reference/storage': StorageServerNav,
}
@@ -42,8 +42,8 @@ const SideBar = ({ menuItems = [] }: { menuItems: any }) => {
return (
<div
className="dark:bg-scale-200 dark:border-scale-400 sidebar-width sticky top-0
h-screen overflow-y-scroll border-r py-8 px-6 sidebar-menu-container"
className="dark:bg-scale-200 dark:border-scale-400 sidebar-width sticky top-16
h-screen overflow-y-scroll border-r py-8 px-6 sidebar-menu-container hidden lg:block"
>
{isInReferencePages && (
<>
@@ -73,9 +73,8 @@ const SideBar = ({ menuItems = [] }: { menuItems: any }) => {
)}
{menuItems.length === 1 ? (
<div className="my-2">
<h4 className="text-scale-1200 pt-2 pb-3">{menuItems[0].label}</h4>
{menuItems[0].items.map((item) => (
<Link href={item.url}>
<Link key={item.name} href={item.url}>
<a>
<div
key={item.name}
@@ -83,7 +82,7 @@ const SideBar = ({ menuItems = [] }: { menuItems: any }) => {
'py-1.5 px-5 rounded text-sm transition',
`${
item.url === asPath
? 'bg-scale-400 text-brand-900'
? 'bg-scale-200 text-brand-900'
: 'text-scale-1100 hover:text-scale-1200'
}`,
].join(' ')}
@@ -117,15 +116,14 @@ const SideBar = ({ menuItems = [] }: { menuItems: any }) => {
{group.items.map((section: NavMenuSection) => {
if (section.items.length === 0) {
return (
<Link href={section.url}>
<Link href={section.url} key={section.name}>
<a>
<div
key={section.name}
className={[
'py-1.5 px-5 rounded text-sm transition',
`${
section.url === asPath
? 'bg-scale-400 text-brand-900'
? 'bg-scale-200 text-brand-900'
: 'text-scale-1100 hover:text-scale-1200'
}`,
].join(' ')}
@@ -157,7 +155,7 @@ const SideBar = ({ menuItems = [] }: { menuItems: any }) => {
</Accordion.Trigger>
<Accordion.Content className="my-2 data-open:animate-slide-down data-closed:animate-slide-up">
{section.items.map((item: NavMenuSection) => (
<Link href={item.url}>
<Link key={item.name} href={item.url}>
<a>
<div
key={item.name}
@@ -165,7 +163,7 @@ const SideBar = ({ menuItems = [] }: { menuItems: any }) => {
'py-1.5 ml-4 px-5 rounded text-sm transition',
`${
item.url === asPath
? 'bg-scale-400 text-brand-900'
? 'bg-scale-200 text-brand-900'
: 'text-scale-1100 hover:text-scale-1200'
}`,
].join(' ')}
@@ -1,7 +1,7 @@
import Link from 'next/link'
import { IconArrowLeft, IconArrowRight } from 'ui'
const pagination = ({ currentPage, totalCount }: { currentPage: number; totalCount: number }) => {
const Pagination = ({ currentPage, totalCount }: { currentPage: number; totalCount: number }) => {
// TODO: not sure if this is the most efficient way to do this. may need to refactor.
const totalArray = Array.from({ length: totalCount }, (_, i: number) => i + 1)
const pages = totalArray.filter((page: number) => {
@@ -50,4 +50,4 @@ const pagination = ({ currentPage, totalCount }: { currentPage: number; totalCou
)
}
export default pagination
export default Pagination
File renamed without changes.
@@ -1,5 +1,6 @@
import Image from 'next/image'
import React from 'react'
import sponsors from '@site/src/data/sponsors.json'
import sponsors from '../data/sponsors.json'
export default function Sponsors() {
const tiers = [
@@ -41,19 +42,22 @@ export default function Sponsors() {
(t, index) =>
!!t.transactions.length && (
<div key={index}>
<h4 className="">{t.heading}</h4>
<div className="row is-multiline" style={{ paddingLeft: 15 }}>
<h4 className="font-bold">{t.heading}</h4>
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-6 pl-4">
{t.transactions.map((x, index) => (
<div className="col col--4" key={index}>
<div className="" key={index}>
<a
className="avatar"
className="flex items-center gap-4 shadow-none shrink-0"
href={`https://github.com/${x.sponsor}`}
>
<img
className="avatar__photo avatar__photo--sm"
<Image
alt="Sponsor"
className="mt-4 mb-4 rounded-full w-12 h-12"
width={45}
height={45}
src={`https://github.com/${x.sponsor}.png`}
/>
<div className="avatar__intro">
<div className="flex items-center flex-col justify-center text-center">
<h5 className="avatar__name">{x.sponsor}</h5>
</div>
</a>
+70
View File
@@ -0,0 +1,70 @@
// [Terry]
// Delete this after we've implemented GuidesTableofContents and moved all guides
// and rename GuidesTableofContents to TableOfContents
import { FC } from 'react'
import { getAnchor, removeAnchor } from './CustomHTMLElements/CustomHTMLElements.utils'
interface TOCHeader {
id: number
lvl: number
seen: number
content: string
slug: string
}
interface Props {
toc: any
}
const formatSlug = (slug: string) => {
// [Joshen] We will still provide support for headers declared like this:
// ## REST API {#rest-api-overview}
// At least for now, this was a docusaurus thing.
if (slug.includes('#')) return slug.split('-#')[1]
return slug
}
const formatTOCHeader = (content: string) => {
let begin = false
const res = []
for (const x of content) {
if (x === '`') {
if (!begin) {
begin = true
res.push(`<code class="text-xs border rounded bg-scale-400 border-scale-500">`)
} else {
begin = false
res.push(`</code>`)
}
} else {
res.push(x)
}
}
return res.join('')
}
const TableOfContents: FC<Props> = ({ toc }) => {
// [Joshen] markdown-toc doesn't seem to read maxdepth from the options passed in
// Our first level headers will be H2s (H1 is ignored), and we only show up to H3
return (
<ul className="toc-menu list-none pl-4 text-[0.8rem] grid gap-2 mt-12">
{(toc.json as TOCHeader[])
.filter((item) => item.lvl !== 1 && item.lvl <= 3)
.map((item: any, i: number) => {
return (
<li key={i} id={item.lvl} style={{ marginLeft: `${(item.lvl - 2) * 1}rem` }}>
<a
href={`#${formatSlug(item.slug)}`}
className="text-scale-1000 hover:text-brand-900 transition-colors"
dangerouslySetInnerHTML={{ __html: formatTOCHeader(removeAnchor(item.content)) }}
/>
</li>
)
})}
</ul>
)
}
export default TableOfContents
+62
View File
@@ -0,0 +1,62 @@
import { Button, Tabs, Alert } from 'ui'
// Common components
import Admonition from './Admonition'
import ButtonCard from './ButtonCard'
import CodeBlock from './CodeBlock/CodeBlock'
import { parseNumericRange } from './CodeBlock/CodeBlock.utils'
import JwtGenerator from './JwtGenerator'
// Page specific components
import Frameworks from './Frameworks'
import AuthProviders from './AuthProviders'
import FunctionsExamples from './FunctionsExamples'
import Extensions from './Extensions'
// Other components
import { Heading } from './CustomHTMLElements'
import QuickstartIntro from './MDX/quickstart_intro.mdx'
import ProjectSetup from './MDX/project_setup.mdx'
import { Mermaid } from 'mdx-mermaid/lib/Mermaid'
import InlineCodeTag from './CustomHTMLElements/InlineCode'
const components = {
Admonition,
Button,
ButtonCard,
CodeBlock,
Frameworks,
AuthProviders,
FunctionsExamples,
JwtGenerator,
QuickstartIntro,
ProjectSetup,
Mermaid,
Extensions,
Alert: (props: any) => (
<Alert {...props} className="not-prose">
{props.children}
</Alert>
),
Tabs,
TabPanel: (props: any) => <Tabs.Panel {...props}>{props.children}</Tabs.Panel>,
h2: (props: any) => (
<Heading tag="h2" {...props}>
{props.children}
</Heading>
),
h3: (props: any) => (
<Heading tag="h3" {...props}>
{props.children}
</Heading>
),
code: (props: any) => {
const linesToHighlight = parseNumericRange(props.lines ?? '')
return <CodeBlock {...props} linesToHighlight={linesToHighlight} />
},
mono: (props: any) => <code className="text-sm">{props.children}</code>,
inlineCode: (props: any) => <InlineCodeTag>{props.children}</InlineCodeTag>,
}
export default components
File renamed without changes.
File renamed without changes.
+150
View File
@@ -0,0 +1,150 @@
[
{
"title": "Product",
"links": [
{
"text": "Database",
"url": "/database"
},
{
"text": "Auth",
"url": "/auth"
},
{
"text": "Functions",
"url": "/edge-functions"
},
{
"text": "Realtime",
"url": "/realtime"
},
{
"text": "Storage",
"url": "/storage"
},
{
"text": "Pricing",
"url": "/pricing"
},
{
"text": "Beta",
"url": "/beta"
}
]
},
{
"title": "Resources",
"links": [
{
"text": "Support",
"url": "/support"
},
{
"text": "System Status",
"url": "https://status.supabase.com/"
},
{
"text": "Integrations",
"url": "/partners/integrations"
},
{
"text": "Experts",
"url": "/partners/experts"
},
{
"text": "Brand Assets / Logos",
"url": "/brand-assets"
},
{
"text": "DPA",
"url": "/legal/dpa"
},
{
"text": "SOC2",
"url": "/legal/soc2"
}
]
},
{
"title": "Developers",
"links": [
{
"text": "Documentation",
"url": "/docs"
},
{
"text": "API Reference",
"url": "/docs/reference"
},
{
"text": "Changelog",
"url": "/changelog"
},
{
"text": "Contributing",
"url": "/docs/handbook/contributing"
},
{
"text": "Open Source",
"url": "/docs/oss"
},
{
"text": "SupaSquad",
"url": "/docs/handbook/supasquad"
},
{
"text": "DevTo",
"url": "https://dev.to/supabase"
},
{
"text": "RSS",
"url": "/rss.xml"
}
]
},
{
"title": "Company",
"links": [
{
"text": "Blog",
"url": "/blog"
},
{
"text": "Careers",
"url": "https://about.supabase.com/careers"
},
{
"text": "Company",
"url": "/company"
},
{
"text": "Terms of Service",
"url": "/docs/company/terms"
},
{
"text": "Privacy Policy",
"url": "/docs/company/privacy"
},
{
"text": "Acceptable Use Policy",
"url": "/docs/company/aup"
},
{
"text": "Service Level Agreement",
"url": "/docs/company/sla"
},
{
"text": "Humans.txt",
"url": "/humans.txt"
},
{
"text": "Lawyers.txt",
"url": "/lawyers.txt"
},
{
"text": "Security.txt",
"url": "/.well-known/security.txt"
}
]
}
]
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+12
View File
@@ -0,0 +1,12 @@
const Nav = [
{
label: 'Realtime',
items: [
{ name: 'Realtime Server', url: '/reference/realtime', items: [] },
{ name: 'Configuration', url: '/reference/realtime/config', items: [] },
{ name: 'Release Notes', url: '/reference/realtime/release-notes', items: [] },
],
},
]
export default Nav
@@ -2,7 +2,7 @@ const Nav = [
{
label: 'API',
items: [
{ name: 'Supabase API', url: '/reference/api', items: [] },
{ name: 'Management API', url: '/reference/api', items: [] },
{ name: 'Usage', url: '/reference/api/usage', items: [] },
{ name: 'Release Notes', url: '/reference/api/release-notes', items: [] },
],
File renamed without changes.
@@ -24,7 +24,7 @@ const Nav = [
{ name: 'currentSession', url: '/reference/dart/auth-currentsession', items: [] },
{ name: 'currentUser', url: '/reference/dart/auth-currentuser', items: [] },
{ name: 'updateUser()', url: '/reference/dart/auth-updateuser', items: [] },
{ name: 'setSession()', url: '/reference/dart/auth-setSession', items: [] },
{ name: 'onAuthStateChange()', url: '/reference/dart/auth-onauthstatechange', items: [] },
{
name: 'resetPasswordForEmail()',
url: '/reference/dart/auth-resetpasswordforemail',
@@ -118,6 +118,11 @@ const Nav = [
url: '/reference/dart/storage-from-createsignedurl',
items: [],
},
{
name: 'from.createSignedUrls()',
url: '/reference/dart/storage-from-createsignedurls',
items: [],
},
{
name: 'from.getPublicUrl()',
url: '/reference/dart/storage-from-getpublicurl',
@@ -2,7 +2,7 @@ const Nav = [
{
label: 'Getting Started',
items: [
{ name: 'Supabase Javascript Library', url: '/reference/javascript/v1', items: [] },
{ name: 'Supabase JavaScript Library', url: '/reference/javascript/v1', items: [] },
{ name: 'Installing', url: '/reference/javascript/v1/installing', items: [] },
{ name: 'Initializing', url: '/reference/javascript/v1/initializing', items: [] },
{ name: 'Generating Types', url: '/reference/javascript/v1/generating-types', items: [] },
@@ -2,7 +2,7 @@ const Nav = [
{
label: 'Getting Started',
items: [
{ name: 'Supabase Javascript Library', url: '/reference/javascript', items: [] },
{ name: 'Supabase JavaScript Library', url: '/reference/javascript', items: [] },
{ name: 'Installing', url: '/reference/javascript/installing', items: [] },
{ name: 'Initializing', url: '/reference/javascript/initializing', items: [] },
{ name: 'Typescript Support', url: '/reference/javascript/typescript-support', items: [] },
@@ -28,9 +28,15 @@ const Nav = [
{ name: 'signOut()', url: '/reference/javascript/auth-signout', items: [] },
{ name: 'verifyOtp()', url: '/reference/javascript/auth-verifyotp', items: [] },
{ name: 'getSession()', url: '/reference/javascript/auth-getsession', items: [] },
{ name: 'refreshSession()', url: '/reference/javascript/auth-refreshsession', items: [] },
{ name: 'getUser()', url: '/reference/javascript/auth-getuser', items: [] },
{ name: 'updateUser()', url: '/reference/javascript/auth-updateuser', items: [] },
{ name: 'setSession()', url: '/reference/javascript/auth-setSession', items: [] },
{ name: 'setSession()', url: '/reference/javascript/auth-setsession', items: [] },
{
name: 'onAuthStateChange()',
url: '/reference/javascript/auth-onauthstatechange',
items: [],
},
{
name: 'resetPasswordForEmail()',
url: '/reference/javascript/auth-resetpasswordforemail',
@@ -21,6 +21,8 @@ const nonGeneratedReferencePages = [
'docs/reference/auth/release-notes',
'docs/reference/storage',
'docs/reference/storage/release-notes',
'docs/reference/realtime',
'docs/reference/realtime/release-notes',
]
export default nonGeneratedReferencePages
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,11 +1,11 @@
---
slug: /
sidebar_position: 1
id: management-api
title: Management API
sidebar_label: Management API
---
# Management API
The Management API allows you to manage your projects programmatically.
## Status
@@ -1,10 +1,9 @@
---
id: release-notes
title: Release Notes
---
# Release Notes
## v1.0.0 {#1.0.0}
## v1.0.0 [#1.0.0]
Adds the following routes:
@@ -1,11 +1,11 @@
---
slug: /
sidebar_position: 1
sidebar_label: Auth Server
id: auth-server
title: Supabase Auth Server
sidebar_label: Supabase Auth Server
---
# Supabase Auth Server
The Supabase Auth Server (GoTrue) is a JSON Web Token (JWT)-based API for managing users and issuing access tokens.
GoTrue is an open-source API written in Golang, that acts as a self-standing API service for handling user registration and authentication for JAM projects. It's based on OAuth2 and JWT and handles user signup, authentication, and custom user data.
@@ -19,4 +19,4 @@ GoTrue is an open-source API written in Golang, that acts as a self-standing API
- [Source Code](https://github.com/supabase/gotrue)
- [Known bugs and issues](https://github.com/supabase/gotrue/issues)
- [Auth Guides](https://supabase.com/docs/guides/auth)
- [Auth Guides](/docs/guides/auth)
@@ -1,7 +1,6 @@
---
id: release-notes
title: Release Notes
---
# Release Notes
All release notes can be found in the [GitHub Releases](https://github.com/supabase/gotrue/releases) page.
+28
View File
@@ -0,0 +1,28 @@
---
id: intro
slug: /
sidebar_position: 1
sidebar_label: Supabase CLI
title: Supabase CLI
hide_table_of_contents: true
---
The Supabase CLI provides tools to develop your project locally and deploy to the Supabase Platform.
The CLI is still under development, but it contains all the functionality for working with your Supabase projects and the Supabase Platform.
- Run Supabase locally: [`supabase start`](/docs/reference/cli/usage#supabase-start)
- Manage database migrations: [`supabase migration`](/docs/reference/cli/usage#supabase-migration)
- CI/CD for releasing to production: [`supabase db push`](/docs/reference/cli/usage#supabase-db-push)
- Manage your Supabase projects: [`supabase projects`](/docs/reference/cli/usage#supabase-projects)
- Generate types directly from your database schema: [`supabase gen types`](/docs/reference/cli/usage#supabase-gen)
- A [community-supported GitHub Action](https://github.com/lyqht/generate-supabase-db-types-github-action) to generate TypeScript types
- Shell autocomplete: [`supabase completion`](/docs/reference/cli/usage#supabase-completion)
- A [community-supported Fig autocomplete spec](https://fig.io/manual/supabase) for macOS terminal
## Additional Links
- [Install the Supabase CLI](/docs/guides/cli)
- [Source code](https://github.com/supabase/cli)
- [Known bugs and issues](https://github.com/supabase/cli/issues)
- [Supabase CLI v1 and Management API Beta](https://supabase.com/blog/supabase-cli-v1-and-admin-api-beta)
- [The CLI team announcing CLI V1 and Management API Beta - Video](https://www.youtube.com/watch?v=OpPOaJI_Z28)
@@ -1,7 +1,6 @@
---
id: release-notes
title: Release Notes
---
# Release Notes
All release notes can be found in the [GitHub Releases page](https://github.com/supabase/cli/releases).
@@ -5,8 +5,6 @@ title: Supabase Flutter Library
sidebar_label: Supabase Flutter Library
---
# Supabase Flutter Library
This reference documents every object and method available in Supabase's isomorphic Flutter library, [`supabase-flutter`](https://pub.dev/packages/supabase_flutter).
You can use the `supabase-flutter` library to:
@@ -13,7 +13,7 @@ For `supabase-flutter`, you will be using the static `initialize()` method on `S
### Flutter `initialize()`
```dart title="main.dart"
```dart title=main.dart
Future<void> main() async {
await Supabase.initialize(url: 'https://xyzcompany.supabase.co', anonKey: 'public-anon-key');
runApp(MyApp());
@@ -28,6 +28,33 @@ Once you initialize Supabase in your `main()` method, you can access the `Supaba
final supabase = Supabase.instance.client;
```
### Call `initialize()` with custom headers
You can pass `headers` to initialize your Supabase client with customer headers.
Here is an example of passing a custom auth header to Supabase client.
```dart title=main.dart
Future<void> main() async {
await Supabase.initialize(
url: 'https://xyzcompany.supabase.co',
anonKey: 'public-anon-key',
headers: {
'Authorization': 'Bearer $accessToken',
},
);
runApp(const MyApp());
}
```
### Call `initialize()`multiple times
You can initialize Supabase multiple times in your app. When doing so, call `dispose()` each time before you initialize again.
```dart
Supabase.instance.dispose();
await Supabase.initialize(url: 'https://xyzcompany.supabase.co', anonKey: 'public-anon-key');
```
## Other Dart Projects
You can initialize a new Supabase client using the `SupabaseClient()` method.
@@ -40,4 +67,3 @@ and is the easiest way to interact with everything we offer within the Supabase
```dart
final supabase = SupabaseClient('https://xyzcompany.supabase.co', 'public-anon-key');
```
@@ -22,26 +22,24 @@ supabase_flutter: ^1.0.0
The way supabase-flutter throws error has changed in v1. In v0, errors were returned as a response. In v1, errors are thrown as exceptions. This makes it more intuitive as a Flutter developer to handle errors.
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
final res = await supabase.from('my_table').select().execute();
final error = res.error;
if(error != null) {
if (error != null) {
// handle error
}
final data = res.data;
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
try {
@@ -51,7 +49,7 @@ try {
}
```
</TabItem>
</TabPanel>
</Tabs>
## Auth classes / methods
@@ -61,13 +59,12 @@ try {
In v0, `SupabaseAuthState` and `SupabaseAuthRequiredState` were required to handle automatic token refresh and to listen to auth state change. In v1, `SupabaseAuthState` and `SupabaseAuthRequiredState` are deprecated, and token refresh will happen automatically just by initializing Supabase. [`onAuthStateChange`](/docs/reference/dart/upgrade-guide#listening-to-auth-state-change) can be used to action on auth state change.
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
await Supabase.initialize(
@@ -87,9 +84,8 @@ class AuthRequiredState<T extends StatefulWidget> extends SupabaseAuthState<T> {
}
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
await Supabase.initialize(
@@ -98,7 +94,7 @@ await Supabase.initialize(
);
```
</TabItem>
</TabPanel>
</Tabs>
### Listening to auth state change
@@ -106,13 +102,12 @@ await Supabase.initialize(
`onAuthStateChange` now returns a `Stream`.
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
final authSubscription = supabase.auth.onAuthStateChange((event, session) {
@@ -123,9 +118,8 @@ final authSubscription = supabase.auth.onAuthStateChange((event, session) {
authSubscription.data?.unsubscribe();
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
final authSubscription = supabase.auth.onAuthStateChange.listen((data) {
@@ -138,7 +132,7 @@ final authSubscription = supabase.auth.onAuthStateChange.listen((data) {
authSubscription.cancel();
```
</TabItem>
</TabPanel>
</Tabs>
### Sign in with email and password
@@ -146,65 +140,60 @@ authSubscription.cancel();
The signIn() method has been deprecated in favor of more explicit method signatures to help with type hinting. Previously it was difficult for developers to know what they were missing (e.g., a lot of developers didn't realize they could use passwordless magic links).
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
await supabase.auth.signIn(email: email, password: password);
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
await supabase.auth.signInWithPassword(email: email, password: password);
```
</TabItem>
</TabPanel>
</Tabs>
### Sign in with magic link
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
await supabase.auth.signIn(email: email);
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
await supabase.auth.signInWithOtp(email: email);
```
</TabItem>
</TabPanel>
</Tabs>
### Sign in with a third-party OAuth provider
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
await supabase.auth.signInWithProvider(
@@ -216,9 +205,8 @@ await supabase.auth.signInWithProvider(
);
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
await supabase.auth.signInWithOAuth(
@@ -227,19 +215,18 @@ await supabase.auth.signInWithOAuth(
);
```
</TabItem>
</TabPanel>
</Tabs>
### Sign in with phone
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
await supabase.auth.signIn(
@@ -248,9 +235,8 @@ await supabase.auth.signIn(
);
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
await supabase.auth.signInWithPassword(
@@ -259,27 +245,25 @@ await supabase.auth.signInWithPassword(
);
```
</TabItem>
</TabPanel>
</Tabs>
### Sign in with phone using OTP
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
final res = await supabase.auth.signIn(phone: phone);
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
await supabase.auth.signInWithOtp(
@@ -294,19 +278,18 @@ await supabase.auth.verifyOTP(
);
```
</TabItem>
</TabPanel>
</Tabs>
### Reset password for email
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
await supabase.auth.api.resetPasswordForEmail(
@@ -316,9 +299,8 @@ await supabase.auth.api.resetPasswordForEmail(
);
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
await supabase.auth.resetPasswordForEmail(
@@ -327,71 +309,66 @@ await supabase.auth.resetPasswordForEmail(
);
```
</TabItem>
</TabPanel>
</Tabs>
### Get the user's current session
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
final session = supabase.auth.session();
final session = supabase.auth.session();
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
final Session? session = supabase.auth.currentSession;
```
</TabItem>
</TabPanel>
</Tabs>
### Get the logged-in user
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
final user = supabase.auth.user();
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
final User? user = supabase.auth.currentUser;
```
</TabItem>
</TabPanel>
</Tabs>
### Update user data for a logged-in user
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
await supabase.auth.update(
@@ -399,9 +376,8 @@ await supabase.auth.update(
);
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
await supabase.updateUser(
@@ -411,7 +387,7 @@ await supabase.updateUser(
);
```
</TabItem>
</TabPanel>
</Tabs>
## Data methods
@@ -425,60 +401,56 @@ Also, calling `.execute()` at the end of the query was a requirement in v0, but
### Insert without returning inserted data
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
await supabase
.from('my_table')
.insert(data, returning: ReturningOption.minimal)
.execute();
.from('my_table')
.insert(data, returning: ReturningOption.minimal)
.execute();
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
await supabase.from('my_table').insert(data);
```
</TabItem>
</TabPanel>
</Tabs>
### Insert with returning inserted data
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
final res = await supabase
.from('my_table')
.insert(data)
.execute();
.from('my_table')
.insert(data)
.execute();
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
final insertedData = await supabase.from('my_table').insert(data).select();
```
</TabItem>
</TabPanel>
</Tabs>
## Realtime methods
@@ -488,13 +460,12 @@ final insertedData = await supabase.from('my_table').insert(data).select();
`.stream()` no longer needs the `.execute()` at the end. Also, filtering by `eq` is a lot easier now. `primaryKey` is now a named parameter to make it more obvious what to pass.
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
supabase.from('my_table:id=eq.120')
@@ -502,9 +473,8 @@ supabase.from('my_table:id=eq.120')
.listen();
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
supabase.from('my_table')
@@ -513,19 +483,18 @@ supabase.from('my_table')
.listen();
```
</TabItem>
</TabPanel>
</Tabs>
### Subscribe
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
final subscription = supabase
@@ -536,9 +505,8 @@ final subscription = supabase
.subscribe();
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
final channel = supabase.channel('*');
@@ -551,31 +519,29 @@ channel.on(
).subscribe();
```
</TabItem>
</TabPanel>
</Tabs>
### Unsubscribe
<Tabs
groupId="version"
values={[
{label: 'Before', value: '0.x'},
{label: 'After', value: '1.x'},
]}>
<TabItem value="0.x">
scrollable
size="small"
type="underlined"
defaultActiveId="0.x"
>
<TabPanel id="0.x" label="Before">
```dart
supabase.removeSubscription(subscription);
```
</TabItem>
<TabItem value="1.x">
</TabPanel>
<TabPanel id="1.0x" label="After">
```dart
await supabase.removeChannel(channel);
```
</TabItem>
</TabPanel>
</Tabs>
@@ -2,13 +2,14 @@
id: intro
slug: /
sidebar_label: Supabase Dart Library
title: Supabase Dart Library
---
# Supabase Dart Library
<Admonition type="caution">
:::caution
You're viewing the docs for an older version of the `supabase-flutter` library.
:::
</Admonition>
This reference documents every object and method available in Supabase's isomorphic Dart library, `supabase-dart`.
Loaded 100 of 1811 files, more files were not shown because too many files have changed in this diff. Show more