feat(docs): code hike

This commit is contained in:
Greg Richardson
2023-06-04 15:15:58 -06:00
parent 8d38c2cf1f
commit b8df7d7c2b
43 changed files with 828 additions and 671 deletions

View File

@@ -0,0 +1,361 @@
{
"name": "supabase",
"type": "from-css",
"tokenColors": [
{
"scope": ["comment", "punctuation.definition.comment", "string.comment"],
"settings": {
"foreground": "var(--ch-1)"
}
},
{
"scope": [
"constant",
"entity.name.constant",
"variable.other.constant",
"variable.other.enummember",
"variable.language",
"entity"
],
"settings": {
"foreground": "var(--ch-2)"
}
},
{
"scope": ["entity.name", "meta.export.default", "meta.definition.variable"],
"settings": {
"foreground": "var(--ch-3)"
}
},
{
"scope": [
"variable.parameter.function",
"meta.jsx.children",
"meta.block",
"meta.tag.attributes",
"entity.name.constant",
"meta.object.member",
"meta.embedded.expression"
],
"settings": {
"foreground": "var(--ch-4)"
}
},
{
"scope": "entity.name.function",
"settings": {
"foreground": "var(--ch-5)"
}
},
{
"scope": ["entity.name.tag", "support.class.component"],
"settings": {
"foreground": "var(--ch-6)"
}
},
{
"scope": "keyword",
"settings": {
"foreground": "var(--ch-7)"
}
},
{
"scope": ["storage", "storage.type"],
"settings": {
"foreground": "var(--ch-7)"
}
},
{
"scope": ["storage.modifier.package", "storage.modifier.import", "storage.type.java"],
"settings": {
"foreground": "var(--ch-4)"
}
},
{
"scope": ["string", "string punctuation.section.embedded source"],
"settings": {
"foreground": "var(--ch-8)"
}
},
{
"scope": "support",
"settings": {
"foreground": "var(--ch-2)"
}
},
{
"scope": "meta.property-name",
"settings": {
"foreground": "var(--ch-2)"
}
},
{
"scope": "variable",
"settings": {
"foreground": "var(--ch-3)"
}
},
{
"scope": "variable.other",
"settings": {
"foreground": "var(--ch-4)"
}
},
{
"scope": "invalid.broken",
"settings": {
"foreground": "var(--ch-9)",
"fontStyle": "italic"
}
},
{
"scope": "invalid.deprecated",
"settings": {
"foreground": "var(--ch-9)",
"fontStyle": "italic"
}
},
{
"scope": "invalid.illegal",
"settings": {
"foreground": "var(--ch-9)",
"fontStyle": "italic"
}
},
{
"scope": "invalid.unimplemented",
"settings": {
"foreground": "var(--ch-9)",
"fontStyle": "italic"
}
},
{
"scope": "carriage-return",
"settings": {
"background": "var(--ch-7)",
"foreground": "var(--ch-10)",
"fontStyle": "italic underline"
}
},
{
"scope": "message.error",
"settings": {
"foreground": "var(--ch-9)"
}
},
{
"scope": "string variable",
"settings": {
"foreground": "var(--ch-2)"
}
},
{
"scope": ["source.regexp", "string.regexp"],
"settings": {
"foreground": "var(--ch-8)"
}
},
{
"scope": [
"string.regexp.character-class",
"string.regexp constant.character.escape",
"string.regexp source.ruby.embedded",
"string.regexp string.regexp.arbitrary-repitition"
],
"settings": {
"foreground": "var(--ch-8)"
}
},
{
"scope": "string.regexp constant.character.escape",
"settings": {
"foreground": "var(--ch-6)",
"fontStyle": "bold"
}
},
{
"scope": "support.constant",
"settings": {
"foreground": "var(--ch-2)"
}
},
{
"scope": "support.variable",
"settings": {
"foreground": "var(--ch-2)"
}
},
{
"scope": "support.type.property-name.json",
"settings": {
"foreground": "var(--ch-6)"
}
},
{
"scope": "meta.module-reference",
"settings": {
"foreground": "var(--ch-2)"
}
},
{
"scope": "punctuation.definition.list.begin.markdown",
"settings": {
"foreground": "var(--ch-3)"
}
},
{
"scope": ["markup.heading", "markup.heading entity.name"],
"settings": {
"foreground": "var(--ch-2)",
"fontStyle": "bold"
}
},
{
"scope": "markup.quote",
"settings": {
"foreground": "var(--ch-6)"
}
},
{
"scope": "markup.italic",
"settings": {
"foreground": "var(--ch-4)",
"fontStyle": "italic"
}
},
{
"scope": "markup.bold",
"settings": {
"foreground": "var(--ch-4)",
"fontStyle": "bold"
}
},
{
"scope": ["markup.underline"],
"settings": {
"fontStyle": "underline"
}
},
{
"scope": ["markup.strikethrough"],
"settings": {
"fontStyle": "strikethrough"
}
},
{
"scope": "markup.inline.raw",
"settings": {
"foreground": "var(--ch-2)"
}
},
{
"scope": ["markup.deleted", "meta.diff.header.from-file", "punctuation.definition.deleted"],
"settings": {
"background": "var(--ch-11)",
"foreground": "var(--ch-9)"
}
},
{
"scope": ["punctuation.section.embedded"],
"settings": {
"foreground": "var(--ch-7)"
}
},
{
"scope": ["markup.inserted", "meta.diff.header.to-file", "punctuation.definition.inserted"],
"settings": {
"background": "var(--ch-12)",
"foreground": "var(--ch-6)"
}
},
{
"scope": ["markup.changed", "punctuation.definition.changed"],
"settings": {
"background": "var(--ch-13)",
"foreground": "var(--ch-3)"
}
},
{
"scope": ["markup.ignored", "markup.untracked"],
"settings": {
"background": "var(--ch-2)",
"foreground": "var(--ch-14)"
}
},
{
"scope": "meta.diff.range",
"settings": {
"foreground": "var(--ch-5)",
"fontStyle": "bold"
}
},
{
"scope": "meta.diff.header",
"settings": {
"foreground": "var(--ch-2)"
}
},
{
"scope": "meta.separator",
"settings": {
"foreground": "var(--ch-2)",
"fontStyle": "bold"
}
},
{
"scope": "meta.output",
"settings": {
"foreground": "var(--ch-2)"
}
},
{
"scope": [
"brackethighlighter.tag",
"brackethighlighter.curly",
"brackethighlighter.round",
"brackethighlighter.square",
"brackethighlighter.angle",
"brackethighlighter.quote"
],
"settings": {
"foreground": "var(--ch-15)"
}
},
{
"scope": "brackethighlighter.unmatched",
"settings": {
"foreground": "var(--ch-9)"
}
},
{
"scope": ["constant.other.reference.link", "string.other.link"],
"settings": {
"foreground": "var(--ch-8)",
"fontStyle": "underline"
}
}
],
"colors": {
"editor.background": "var(--ch-16)",
"editor.foreground": "var(--ch-4)",
"editor.selectionBackground": "var(--ch-17)",
"editor.infoForeground": "var(--ch-18)",
"editor.rangeHighlightBackground": "var(--ch-19)",
"editorLineNumber.foreground": "var(--ch-20)",
"tab.activeBackground": "var(--ch-16)",
"tab.inactiveBackground": "var(--ch-21)",
"tab.activeForeground": "var(--ch-4)",
"tab.inactiveForeground": "var(--ch-15)",
"tab.border": "var(--ch-22)",
"tab.activeBorder": "var(--ch-16)",
"tab.activeBorderTop": "var(--ch-23)",
"tab.hoverBackground": "var(--ch-16)",
"tab.hoverForeground": "var(--ch-15)",
"editorGroupHeader.tabsBorder": "var(--ch-22)",
"editorGroupHeader.tabsBackground": "var(--ch-21)",
"list.inactiveSelectionBackground": "var(--ch-24)",
"list.inactiveSelectionForeground": "var(--ch-4)",
"list.hoverBackground": "var(--ch-25)",
"list.hoverForeground": "var(--ch-4)"
}
}

View File

@@ -1,372 +0,0 @@
module.exports = {
name: 'Stripe Docs Blue',
type: 'dark',
colors: {
'editor.background': '#232323',
'editor.foreground': '#fafafa',
'activityBar.background': 'var(--colors-scale2)',
'sideBar.background': 'yellow',
'editorGroupHeader.tabsBackground': 'var(--colors-scale2)',
'sideBarSectionHeader.background': 'var(--colors-scale2)',
'tab.activeBackground': 'var(--colors-scale3)',
'tab.inactiveBackground': 'var(--colors-scale2)',
'tab.border': 'var(--colors-scale2)',
'input.background': '#ffffff1a',
'panel.background': '#1A2652',
'panel.border': '#1A2652',
'editorWidget.background': '#0d0f2b',
'editorWidget.foreground': '#ffffff4d',
'editorWidget.border': 'var(--colors-scale5)',
'list.hoverBackground': '#ffffff1a',
'list.activeSelectionBackground': '#ffffff1a',
'list.inactiveSelectionBackground': '#ffffff1a',
'editor.hoverHighlightBackground': '#ffffff1a',
'editor.selectionHighlightBackground': '#ffffff1a',
'activityBarBadge.background': 'yellow',
'sideBarTitle.foreground': 'var(--colors-scale2)',
'statusBar.background': 'var(--colors-scale2)',
},
tokenColors: [
{
name: 'Comment',
scope: ['comment', 'punctuation.definition.comment'],
settings: {
foreground: '#a3acb9',
fontStyle: '',
},
},
{
name: 'Variables',
scope: ['source', 'variable', 'variable.other.object', 'string constant.other.placeholder'],
settings: {
foreground: '#f5fbff',
},
},
{
name: 'Colors',
scope: ['variable.other.constant', 'constant.other.color'],
settings: {
foreground: '#ffffff',
fontStyle: 'bold',
},
},
{
name: 'Invalid',
scope: ['invalid', 'invalid.illegal'],
settings: {
foreground: '#FF5370',
},
},
{
name: 'Keyword, Storage',
scope: ['keyword', 'storage.type', 'storage.modifier'],
settings: {
foreground: '#98C1FE',
fontStyle: 'bold',
},
},
{
name: 'Function',
scope: ['entity.name.function'],
settings: {
foreground: '#7fd3ed',
fontStyle: 'bold',
},
},
{
name: 'Tag',
scope: ['entity.name.tag', 'meta.tag.sgml', 'markup.deleted.git_gutter'],
settings: {
foreground: '#98C1FE',
fontStyle: 'bold',
},
},
{
name: 'Parameter, Property',
scope: [
'variable.parameter',
'variable.other.object.property',
'variable.other.property',
'keyword.other.unit',
'keyword.other',
],
settings: {
foreground: '#F2AFE3',
},
},
{
name: 'Number, Constant, Function Argument, Tag Attribute, Embedded',
scope: [
'constant.numeric',
'constant.language',
'support.constant',
'constant.character',
'constant.escape',
],
settings: {
foreground: '#f8b886',
},
},
{
name: 'String, Symbols, Inherited Class, Markup Heading',
scope: [
'string',
'constant.other.symbol',
'constant.other.key',
'entity.other.inherited-class',
'markup.heading',
'markup.inserted.git_gutter',
'meta.group.braces.curly constant.other.object.key.js string.unquoted.label.js',
],
settings: {
foreground: '#85d99e',
},
},
{
name: 'Entity Types',
scope: ['support.type'],
settings: {
foreground: '#B2CCD6',
},
},
{
name: 'CSS Class and Support',
scope: [
'source.css support.type.property-name',
'source.sass support.type.property-name',
'source.scss support.type.property-name',
'source.less support.type.property-name',
'source.stylus support.type.property-name',
'source.postcss support.type.property-name',
],
settings: {
foreground: '#B2CCD6',
},
},
{
name: 'Language methods',
scope: ['variable.language'],
settings: {
fontStyle: 'italic',
foreground: '#FF5370',
},
},
{
name: 'Attributes',
scope: ['entity.other.attribute-name'],
settings: {
foreground: '#98C1FE',
fontStyle: 'italic',
},
},
{
name: 'Inserted',
scope: ['markup.inserted'],
settings: {
foreground: '#C3E88D',
},
},
{
name: 'Deleted',
scope: ['markup.deleted'],
settings: {
foreground: '#FF5370',
},
},
{
name: 'Changed',
scope: ['markup.changed'],
settings: {
foreground: '#C792EA',
},
},
{
name: 'Regular Expressions',
scope: ['string.regexp'],
settings: {
foreground: '#89DDFF',
},
},
{
name: 'Escape Characters',
scope: ['constant.character.escape'],
settings: {
foreground: '#89DDFF',
},
},
{
name: 'URL',
scope: ['*url*', '*link*', '*uri*'],
settings: {
fontStyle: 'underline',
},
},
{
name: 'ES7 Bind Operator',
scope: ['source.js constant.other.object.key.js string.unquoted.label.js'],
settings: {
fontStyle: 'italic',
foreground: '#FF5370',
},
},
{
name: 'Markdown - Plain',
scope: ['text.html', 'punctuation.definition.list_item'],
settings: {
foreground: '#f5fbff',
},
},
{
name: 'Markdown - Markup Raw Inline',
scope: ['text.html.markdown markup.inline.raw.markdown'],
settings: {
foreground: '#C792EA',
},
},
{
name: 'Markdown - Markup Raw Inline Punctuation',
scope: ['text.html.markdown markup.inline.raw.markdown punctuation.definition.raw.markdown'],
settings: {
foreground: '#65737E',
},
},
{
name: 'Markdown - Heading',
scope: [
'markdown.heading',
'markup.heading | markup.heading entity.name',
'markup.heading.markdown punctuation.definition.heading.markdown',
],
settings: {
foreground: '#C3E88D',
},
},
{
name: 'Markup - Italic',
scope: ['markup.italic'],
settings: {
fontStyle: 'italic',
foreground: '#f07178',
},
},
{
name: 'Markup - Bold',
scope: ['markup.bold', 'markup.bold string'],
settings: {
fontStyle: 'bold',
foreground: '#f07178',
},
},
{
name: 'Markup - Bold-Italic',
scope: [
'markup.bold markup.italic',
'markup.italic markup.bold',
'markup.quote markup.bold',
'markup.bold markup.italic string',
'markup.italic markup.bold string',
'markup.quote markup.bold string',
],
settings: {
fontStyle: 'bold',
foreground: '#f07178',
},
},
{
name: 'Markup - Underline',
scope: ['markup.underline'],
settings: {
fontStyle: 'underline',
foreground: '#F78C6C',
},
},
{
name: 'Markdown - Blockquote',
scope: ['markup.quote punctuation.definition.blockquote.markdown'],
settings: {
foreground: '#65737E',
},
},
{
name: 'Markup - Quote',
scope: ['markup.quote'],
settings: {
fontStyle: 'italic',
},
},
{
name: 'Markdown - Link Description',
scope: ['string.other.link.description.title.markdown'],
settings: {
foreground: '#C792EA',
},
},
{
name: 'Markdown - Link Anchor',
scope: ['constant.other.reference.link.markdown'],
settings: {
foreground: '#FFCB6B',
},
},
{
name: 'Markup - Raw Block',
scope: ['markup.raw.block'],
settings: {
foreground: '#C792EA',
},
},
{
name: 'Markdown - Raw Block Fenced',
scope: ['markup.raw.block.fenced.markdown'],
settings: {
foreground: '#00000050',
},
},
{
name: 'Markdown - Fenced Bode Block',
scope: ['punctuation.definition.fenced.markdown'],
settings: {
foreground: '#00000050',
},
},
{
name: 'Markdown - Fenced Bode Block Variable',
scope: [
'markup.raw.block.fenced.markdown',
'variable.language.fenced.markdown',
'punctuation.section.class.end',
],
settings: {
foreground: '#EEFFFF',
},
},
{
name: 'Markdown - Fenced Language',
scope: ['variable.language.fenced.markdown'],
settings: {
foreground: '#65737E',
},
},
{
name: 'Markdown - Separator',
scope: ['meta.separator'],
settings: {
fontStyle: 'bold',
foreground: '#65737E',
},
},
{
name: 'Markup - Table',
scope: ['markup.table'],
settings: {
foreground: '#EEFFFF',
},
},
],
}

View File

@@ -11,7 +11,7 @@ Enable the [http extension for the `extensions` schema](https://app.supabase.com
Then, define the following SQL functions in the SQL Editor to delete
storage objects via the API:
```SQL
```sql
create or replace function delete_storage_object(bucket text, object text, out status int, out content text)
returns record
language 'plpgsql'
@@ -52,7 +52,7 @@ $$;
Next, add a trigger that removes any obsolete avatar whenever the
profile is updated or deleted:
```SQL
```sql
create or replace function delete_old_avatar()
returns trigger
language 'plpgsql'
@@ -89,7 +89,7 @@ Finally, delete the `public.profile` row before a user is deleted.
If this step is omitted, you won't be able to delete users without
first manually deleting their avatar image.
```SQL
```sql
create or replace function delete_old_profile()
returns trigger
language 'plpgsql'

View File

@@ -22,7 +22,7 @@ import QuickstartIntro from './MDX/quickstart_intro.mdx'
import SocialProviderSettingsSupabase from './MDX/social_provider_settings_supabase.mdx'
import SocialProviderSetup from './MDX/social_provider_setup.mdx'
import StorageManagement from './MDX/storage_management.mdx'
// import { CH } from '@code-hike/mdx/components'
import { CH } from '@code-hike/mdx/components'
import RefHeaderSection from './reference/RefHeaderSection'
// Ref version specific
@@ -59,6 +59,7 @@ const components = {
Admonition,
Button,
ButtonCard,
CH,
CodeBlock,
GlassPanel,
Link,

View File

@@ -41,7 +41,7 @@ _Optionally_ if you are using custom configuration with `createClient` then foll
>
<TabPanel id="1.0x" label="Before">
```ts title=src/supabaseClient.ts
```ts src/supabaseClient.ts
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
schema: 'custom',
persistSession: false,
@@ -51,7 +51,7 @@ const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
</TabPanel>
<TabPanel id="2.0x" label="After">
```ts title=src/supabaseClient.ts
```ts src/supabaseClient.ts
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
db: {
schema: 'custom',

View File

@@ -10,7 +10,7 @@ For `supabase-flutter`, you will be using the static `initialize()` method on `S
### Flutter `initialize()`
```dart title=main.dart
```dart main.dart
Future<void> main() async {
await Supabase.initialize(url: 'https://xyzcompany.supabase.co', anonKey: 'public-anon-key');
runApp(MyApp());
@@ -30,7 +30,7 @@ final supabase = Supabase.instance.client;
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
```dart main.dart
Future<void> main() async {
await Supabase.initialize(
url: 'https://xyzcompany.supabase.co',

View File

@@ -1,7 +1,9 @@
import fs from 'fs'
import { CodeHikeConfig, remarkCodeHike } from '@code-hike/mdx'
import matter from 'gray-matter'
import { serialize } from 'next-mdx-remote/serialize'
import codeHikeTheme from '~/code-hike.theme.json' assert { type: 'json' }
import { ICommonMarkdown } from '~/components/reference/Reference.types'
async function generateRefMarkdown(sections: ICommonMarkdown[], slug: string) {
@@ -31,6 +33,14 @@ async function generateRefMarkdown(sections: ICommonMarkdown[], slug: string) {
const fileContents = markdownExists ? fs.readFileSync(pathName, 'utf8') : ''
const { data, content } = matter(fileContents)
const codeHikeOptions: CodeHikeConfig = {
theme: codeHikeTheme,
lineNumbers: true,
showCopyButton: true,
skipLanguages: [],
autoImport: false,
}
markdownContent.push({
id: section.id,
title: section.title,
@@ -41,8 +51,8 @@ async function generateRefMarkdown(sections: ICommonMarkdown[], slug: string) {
// MDX's available options, see the MDX docs for more info.
// https://mdxjs.com/packages/mdx/#compilefile-options
mdxOptions: {
// remarkPlugins: [[remarkCodeHike, { autoImport: false, theme }]],
useDynamicImport: true,
remarkPlugins: [[remarkCodeHike, codeHikeOptions]],
},
// Indicates whether or not to parse the frontmatter from the mdx source
})

View File

@@ -2,22 +2,18 @@
import nextMdx from '@next/mdx'
import remarkGfm from 'remark-gfm'
import rehypeSlug from 'rehype-slug'
//import theme from 'shiki/themes/nord.json' assert { type: 'json' }
import { remarkCodeHike } from '@code-hike/mdx'
import withTM from 'next-transpile-modules'
import withYaml from 'next-plugin-yaml'
import configureBundleAnalyzer from '@next/bundle-analyzer'
import codeHikeTheme from './code-hike.theme.json' assert { type: 'json' }
const withBundleAnalyzer = configureBundleAnalyzer({
enabled: process.env.ANALYZE === 'true',
})
// import admonitions from 'remark-admonitions'
// import { remarkCodeHike } from '@code-hike/mdx'
// import codeHikeTheme from './codeHikeTheme.js'
/**
* Rewrites and redirects are handled by
* apps/www nextjs config
@@ -29,20 +25,18 @@ const withMDX = nextMdx({
extension: /\.mdx?$/,
options: {
remarkPlugins: [
// [
// remarkCodeHike,
// {
// theme: codeHikeTheme,
// autoImport: false,
// lineNumbers: true,
// showCopyButton: true,
// },
// ],
[
remarkCodeHike,
{
theme: codeHikeTheme,
lineNumbers: true,
showCopyButton: true,
},
],
remarkGfm,
],
rehypePlugins: [rehypeSlug],
// This is required for `MDXProvider` component
// providerImportSource: '@mdx-js/react',
providerImportSource: '@mdx-js/react',
},
})
@@ -62,7 +56,7 @@ const nextConfig = {
'raw.githubusercontent.com',
'weweb-changelog.ghost.io',
'img.youtube.com',
'archbee-image-uploads.s3.amazonaws.com'
'archbee-image-uploads.s3.amazonaws.com',
],
},
experimental: {

View File

@@ -45,6 +45,7 @@
"dependencies": {
"@algolia/autocomplete-js": "^1.7.2",
"@algolia/autocomplete-plugin-recent-searches": "^1.7.2",
"@code-hike/mdx": "^0.8.3",
"@docsearch/react": "^3.3.0",
"@mdx-js/loader": "^1.6.22",
"@mdx-js/react": "^1.6.22",

View File

@@ -1,7 +1,7 @@
import '../../../packages/ui/build/css/themes/light.css'
import '../../../packages/ui/build/css/themes/dark.css'
import '../styles/ch.scss'
import '../styles/code-hike.scss'
import '../styles/main.scss?v=1.0.0'
import '../styles/new-docs.scss'
import '../styles/prism-okaidia.scss'

View File

@@ -1,9 +1,12 @@
import { remarkCodeHike, CodeHikeConfig } from '@code-hike/mdx'
import { CH } from '@code-hike/mdx/components'
import { GetStaticPaths, GetStaticProps } from 'next'
import { MDXRemote, MDXRemoteSerializeResult } from 'next-mdx-remote'
import { serialize } from 'next-mdx-remote/serialize'
import { join, relative } from 'path'
import { relative } from 'path'
import rehypeSlug from 'rehype-slug'
import remarkGfm from 'remark-gfm'
import codeHikeTheme from '~/code-hike.theme.json' assert { type: 'json' }
import components from '~/components'
import Layout from '~/layouts/DefaultGuideLayout'
import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform'
@@ -60,7 +63,7 @@ interface PythonClientDocsProps {
export default function PythonClientDocs({ source, meta }: PythonClientDocsProps) {
return (
<Layout meta={meta}>
<MDXRemote {...source} components={components} />
<MDXRemote {...source} components={{ ...components, CH }} />
</Layout>
)
}
@@ -116,9 +119,25 @@ export const getStaticProps: GetStaticProps<PythonClientDocsProps> = async ({ pa
}
}
const codeHikeOptions: CodeHikeConfig = {
theme: codeHikeTheme,
lineNumbers: true,
showCopyButton: true,
skipLanguages: [],
autoImport: false,
}
const mdxSource = await serialize(source, {
scope: {
chCodeConfig: codeHikeOptions,
},
mdxOptions: {
remarkPlugins: [remarkGfm, remarkMkDocsAdmonition, [removeTitle, meta.title]],
remarkPlugins: [
remarkGfm,
remarkMkDocsAdmonition,
[removeTitle, meta.title],
[remarkCodeHike, codeHikeOptions],
],
rehypePlugins: [[linkTransform, urlTransform], rehypeSlug],
},
})

View File

@@ -90,7 +90,7 @@ const [captchaToken, setCaptchaToken] = useState()
Now lets add the HCaptcha component to the JSX section of our code
```html
```jsx
<HCaptcha />
```

View File

@@ -25,7 +25,7 @@ npm install @supabase/supabase-js @supabase/auth-ui-react @supabase/auth-ui-shar
Pass `supabaseClient` from `@supabase/supabase-js` as a prop to the component.
```js title=/src/index.js
```js /src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
@@ -38,7 +38,7 @@ This renders the Auth component without any styling.
We recommend using one of the predefined themes to style the UI.
Import the theme you want to use and pass it to the `appearance.theme` prop.
```js lines=4,16 title=/src/index.js
```js mark=4,16 /src/index.js
import { Auth } from '@supabase/auth-ui-react'
import {
// Import predefined theme
@@ -63,7 +63,7 @@ const App = () => (
The Auth component also supports login with [official social providers](../../auth#providers).
```js lines=13 title=/src/index.js
```js mark=11 /src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
import { ThemeSupa } from '@supabase/auth-ui-shared'
@@ -81,18 +81,19 @@ const App = () => (
### Options
Options are available via 'queryParams':
Options are available via `queryParams`:
```<Auth
supabaseClient={supabase}
providers={['google']}
queryParams={{
access_type: 'offline',
prompt: 'consent',
hd: 'domain.com'
}}
onlyThirdPartyProviders={true}
/>
```jsx
<Auth
supabaseClient={supabase}
providers={['google']}
queryParams={{
access_type: 'offline',
prompt: 'consent',
hd: 'domain.com',
}}
onlyThirdPartyProviders={true}
/>
```
### Supported Views
@@ -122,7 +123,7 @@ There are several ways to customize Auth UI:
Auth UI comes with several themes to customize the appearance. Each predefined theme comes with at least two variations, a `default` variation, and a `dark` variation. You can switch between these themes using the `theme` prop. Import the theme you want to use and pass it to the `appearance.theme` prop.
```js lines=2,13 title=/src/index.js
```js mark=3,14 /src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
import { ThemeSupa } from '@supabase/auth-ui-shared'
@@ -151,7 +152,7 @@ Currently there is only one predefined theme available, but we plan to add more.
Auth UI comes with two theme variations: `default` and `dark`. You can switch between these themes with the `theme` prop.
```js lines=14 title=/src/index.js
```js mark=15 /src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
import { ThemeSupa } from '@supabase/auth-ui-shared'
@@ -177,7 +178,7 @@ If you don't pass a value to `theme` it uses the `"default"` theme. You can pass
Auth UI themes can be overridden using variable tokens. See the [list of variable tokens](https://github.com/supabase/auth-ui/blob/main/packages/shared/src/theming/Themes.ts).
```js lines=14-21 title=/src/index.js
```js mark=12:19 /src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
import { ThemeSupa } from '@supabase/auth-ui-shared'
@@ -209,7 +210,7 @@ If you created your own theme, you may not need to override any of the them.
You can create your own theme by following the same structure within a `appearance.theme` property.
See the list of [tokens within a theme](https://github.com/supabase/auth-ui/blob/main/packages/shared/src/theming/Themes.ts).
```js title=/src/index.js
```js /src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
@@ -259,7 +260,7 @@ You can swich between different variations of your theme with the ["theme" prop]
You can use custom CSS classes for the following elements:
`"button"`, `"container"`, `"anchor"`, `"divider"`, `"label"`, `"input"`, `"loader"`, `"message"`.
```js title=/src/index.js
```js /src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
@@ -287,7 +288,7 @@ const App = () => (
You can use custom CSS inline styles for the following elements:
`"button"`, `"container"`, `"anchor"`, `"divider"`, `"label"`, `"input"`, `"loader"`, `"message"`.
```js title=/src/index.js
```js /src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
@@ -311,7 +312,7 @@ const App = () => (
You can use custom labels with `localization.variables`. See the [list of labels](https://github.com/supabase/auth-ui/blob/main/packages/shared/src/localization/en.json) that can be overwritten.
```js title=/src/index.js
```js mark=10:15 /src/index.js
import { createClient } from '@supabase/supabase-js'
import { Auth } from '@supabase/auth-ui-react'
@@ -320,7 +321,6 @@ const supabase = createClient('<INSERT PROJECT URL>', '<INSERT PROJECT ANON API
const App = () => (
<Auth
supabaseClient={supabase}
//highlight-start
localization={{
variables: {
sign_in: {
@@ -329,7 +329,6 @@ const App = () => (
},
},
}}
//highlight-end
/>
)
```

View File

@@ -62,7 +62,7 @@ yarn add @supabase/auth-helpers-react
Retrieve your project URL and anon key in your project's [API settings](https://app.supabase.com/project/_/settings/api) in the Dashboard to set up the following environment variables. For local development you can set them in a `.env.local` file. See an [example](https://github.com/supabase/auth-helpers/blob/main/examples/nextjs/.env.local.example).
```bash title=.env.local
```bash .env.local
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
```
@@ -79,7 +79,7 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
Wrap your `pages/_app.js` component with the `SessionContextProvider` component:
```jsx title=pages/_app.js
```jsx pages/_app.js
import { createPagesBrowserClient } from '@supabase/auth-helpers-nextjs'
import { SessionContextProvider } from '@supabase/auth-helpers-react'
import { useState } from 'react'
@@ -104,7 +104,7 @@ function MyApp({ Component, pageProps }) {
Wrap your `pages/_app.tsx` component with the `SessionContextProvider` component:
```tsx lines=2,8 title=pages/_app.tsx
```tsx mark=2,8 pages/_app.tsx
import { createPagesBrowserClient } from '@supabase/auth-helpers-nextjs'
import { SessionContextProvider, Session } from '@supabase/auth-helpers-react'
import { useState } from 'react'
@@ -148,7 +148,7 @@ The `Code Exchange` API route is required for the [server-side auth flow](https:
Create a new file at `pages/api/auth/callback.js` and populate with the following:
```jsx title="pages/api/auth/callback.js"
```jsx pages/api/auth/callback.js
import { NextApiHandler } from 'next'
import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
@@ -172,7 +172,7 @@ export default handler
Create a new file at `pages/api/auth/callback.ts` and populate with the following:
```tsx title="pages/api/auth/callback.ts"
```tsx pages/api/auth/callback.ts
import { NextApiHandler } from 'next'
import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
@@ -242,7 +242,7 @@ export default async (req: NextApiRequest, res: NextApiResponse) => {
For [row level security](/docs/learn/auth-deep-dive/auth-row-level-security) to work properly when fetching data client-side, you need to make sure to use the `supabaseClient` from the `useSupabaseClient` hook and only run your query once the user is defined client-side in the `useUser()` hook:
```jsx lines=10-17
```jsx mark=10:17
import { Auth } from '@supabase/auth-ui-react'
import { ThemeSupa } from '@supabase/auth-ui-shared'
import { useUser, useSupabaseClient } from '@supabase/auth-helpers-react'
@@ -291,7 +291,7 @@ export default LoginPage
Create a server supabase client to retrieve the logged in user's session:
```jsx title=pages/profile.js
```jsx pages/profile.js
import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
export default function Profile({ user }) {
@@ -553,7 +553,7 @@ Create a server supabase client to retrieve the logged in user's session:
>
<TabPanel id="js" label="JavaScript">
```jsx title=pages/api/protected-route.js
```jsx pages/api/protected-route.js
import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
const ProtectedRoute = async (req, res) => {
@@ -581,7 +581,7 @@ export default ProtectedRoute
</TabPanel>
<TabPanel id="ts" label="TypeScript">
```tsx title=pages/api/protected-route.ts
```tsx pages/api/protected-route.ts
import { NextApiHandler } from 'next'
import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
@@ -614,7 +614,7 @@ export default ProtectedRoute
As an alternative to protecting individual pages you can use a [Next.js Middleware](https://nextjs.org/docs/middleware) to protect the entire directory or those that match the config object. In the following example, all requests to `/middleware-protected/*` will check whether a user is signed in, if successful the request will be forwarded to the destination route, otherwise the user will be redirected:
```ts title=middleware.ts
```ts middleware.ts
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
@@ -698,7 +698,7 @@ Use `createPagesServerClient` within your `NextApiHandler`:
>
<TabPanel id="before" label="Before">
```tsx title=pages/api/protected-route.ts
```tsx pages/api/protected-route.ts
import { withApiAuth } from '@supabase/auth-helpers-nextjs'
export default withApiAuth(async function ProtectedRoute(req, res, supabase) {
@@ -711,7 +711,7 @@ export default withApiAuth(async function ProtectedRoute(req, res, supabase) {
</TabPanel>
<TabPanel id="after" label="After">
```tsx title=pages/api/protected-route.ts
```tsx pages/api/protected-route.ts
import { NextApiHandler } from 'next'
import { createPagesServerClient } from '@supabase/auth-helpers-nextjs'
@@ -752,7 +752,7 @@ Use `createPagesServerClient` within `getServerSideProps`:
>
<TabPanel id="before" label="Before">
```tsx title=pages/profile.tsx
```tsx pages/profile.tsx
import { withPageAuth, User } from '@supabase/auth-helpers-nextjs'
export default function Profile({ user }: { user: User }) {
@@ -765,7 +765,7 @@ export const getServerSideProps = withPageAuth({ redirectTo: '/' })
</TabPanel>
<TabPanel id="after" label="After">
```tsx title=pages/profile.js
```tsx pages/profile.js
import { createPagesServerClient, User } from '@supabase/auth-helpers-nextjs'
import { GetServerSidePropsContext } from 'next'
@@ -811,7 +811,7 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
>
<TabPanel id="before" label="Before">
```tsx title=middleware.ts
```tsx middleware.ts
import { withMiddlewareAuth } from '@supabase/auth-helpers-nextjs'
export const middleware = withMiddlewareAuth({
@@ -832,7 +832,7 @@ export const config = {
</TabPanel>
<TabPanel id="after" label="After">
```tsx title=middleware.ts
```tsx middleware.ts
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

View File

@@ -43,7 +43,7 @@ yarn add @supabase/auth-helpers-nextjs
Retrieve your project's URL and anon key from your [API settings](https://app.supabase.com/project/_/settings/api), and create a `.env.local` file with the following environment variables:
```bash title=".env.local"
```bash .env.local
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
```
@@ -62,7 +62,7 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
Create a new `middleware.js` file in the root of your project and populate with the following:
```jsx title="middleware.js"
```jsx middleware.js
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
@@ -80,7 +80,7 @@ export async function middleware(req) {
Create a new `middleware.ts` file in the root of your project and populate with the following:
```tsx title="middleware.ts"
```tsx middleware.ts
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
@@ -116,7 +116,7 @@ The `Code Exchange` route is required for the [server-side auth flow](https://su
Create a new file at `app/auth/callback/route.js` and populate with the following:
```jsx title="app/auth/callback.route.js"
```jsx app/auth/callback.route.js
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'
@@ -141,7 +141,7 @@ export async function GET(request) {
Create a new file at `app/auth/callback/route.ts` and populate with the following:
```tsx title="app/auth/callback.route.ts"
```tsx app/auth/callback.route.ts
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'
@@ -186,7 +186,7 @@ Client Components can be used to trigger the authentication process from event h
>
<TabPanel id="js" label="JavaScript">
```jsx title="app/login.js"
```jsx app/login.js
'use client'
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
@@ -244,7 +244,7 @@ export default function Login() {
<TabPanel id="ts" label="TypeScript">
```tsx title="app/login.ts"
```tsx app/login.ts
'use client'
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
@@ -319,7 +319,7 @@ The combination of [Server Components](https://nextjs.org/docs/getting-started/r
>
<TabPanel id="js" label="JavaScript">
```jsx title="app/login.js"
```jsx app/login.js
import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
import { revalidatePath } from 'next/cache'
import { cookies } from 'next/headers'
@@ -379,7 +379,7 @@ export default async function Login() {
<TabPanel id="ts" label="TypeScript">
```tsx title="app/login.ts"
```tsx app/login.ts
import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
import { revalidatePath } from 'next/cache'
import { cookies } from 'next/headers'
@@ -456,7 +456,7 @@ export default async function Login() {
>
<TabPanel id="js" label="JavaScript">
```jsx title="app/client/page.jsx"
```jsx app/client/page.jsx
'use client'
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'
@@ -483,7 +483,7 @@ export default function Home() {
<TabPanel id="ts" label="TypeScript">
```jsx title="app/new-post.tsx"
```jsx app/new-post.tsx
"use client";
import { createClientComponentClient } from "@supabase/auth-helpers-nextjs";
@@ -543,7 +543,7 @@ const supabase = createClientComponentClient({ isSingleton: false })
>
<TabPanel id="js" label="JavaScript">
```jsx title="app/page.jsx"
```jsx app/page.jsx
import { cookies } from 'next/headers'
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
@@ -558,7 +558,7 @@ export default async function Home() {
<TabPanel id="ts" label="TypeScript">
```tsx title="app/page.tsx"
```tsx app/page.tsx
import { cookies } from 'next/headers'
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs'
@@ -592,7 +592,7 @@ export default async function ServerComponent() {
>
<TabPanel id="js" label="JavaScript">
```jsx title="app/new-post.jsx"
```jsx app/new-post.jsx
import { cookies } from 'next/headers'
import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
import { revalidatePath } from 'next/cache'
@@ -619,7 +619,7 @@ export default async function NewTodo() {
<TabPanel id="ts" label="TypeScript">
```tsx title="app/new-post.tsx"
```tsx app/new-post.tsx
import { cookies } from 'next/headers'
import { createServerActionClient } from '@supabase/auth-helpers-nextjs'
import { revalidatePath } from 'next/cache'
@@ -661,7 +661,7 @@ export default async function NewTodo() {
>
<TabPanel id="js" label="JavaScript">
```jsx title="app/api/todos/route.jsx"
```jsx app/api/todos/route.jsx
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import { cookies } from 'next/headers'
@@ -678,7 +678,7 @@ export async function POST(request) {
<TabPanel id="ts" label="TypeScript">
```tsx title="app/api/todos/route.tsx"
```tsx app/api/todos/route.tsx
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'
import { cookies } from 'next/headers'

View File

@@ -56,7 +56,7 @@ This library supports the following tooling versions:
Retrieve your project URL and anon key in your project's [API settings](https://app.supabase.com/project/_/settings/api) in the Dashboard to set up the following environment variables. For local development you can set them in a `.env` file. See an [example](https://github.com/supabase/auth-helpers/blob/main/examples/remix/.env.example).
```bash title=.env
```bash .env
SUPABASE_URL=YOUR_SUPABASE_URL
SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
```
@@ -75,7 +75,7 @@ The `Code Exchange` route is required for the [server-side auth flow](https://su
Create a new file at `app/routes/auth.callback.jsx` and populate with the following:
```jsx title="app/routes/auth.callback.jsx"
```jsx app/routes/auth.callback.jsx
import { redirect } from '@remix-run/node'
import { createServerClient } from '@supabase/auth-helpers-remix'
@@ -105,7 +105,7 @@ export const loader = async ({ request }) => {
Create a new file at `app/routes/auth.callback.tsx` and populate with the following:
```tsx title="app/routes/auth.callback.tsx"
```tsx app/routes/auth.callback.tsx
import { redirect } from '@remix-run/node'
import { createServerClient } from '@supabase/auth-helpers-remix'
@@ -328,7 +328,7 @@ Since our environment variables are not available client-side, we need to plumb
>
<TabPanel id="js" label="JavaScript">
```jsx title=app/root.jsx
```jsx app/root.jsx
export const loader = () => {
const env = {
SUPABASE_URL: process.env.SUPABASE_URL,
@@ -343,26 +343,26 @@ export const loader = () => {
Next, we call the `useLoaderData` hook in our component to get the `env` object.
```jsx title=app/root.jsx
```jsx app/root.jsx
const { env } = useLoaderData()
```
We then want to instantiate a single instance of a Supabase browser client, to be used across our client-side components.
```jsx title=app/root.jsx
```jsx app/root.jsx
const [supabase] = useState(() => createBrowserClient(env.SUPABASE_URL, env.SUPABASE_ANON_KEY))
```
And then we can share this instance across our application with Outlet Context.
```jsx title=app/root.jsx
```jsx app/root.jsx
<Outlet context={{ supabase }} />
```
</TabPanel>
<TabPanel id="ts" label="TypeScript">
```tsx title=app/root.tsx
```tsx app/root.tsx
export const loader = ({}: LoaderArgs) => {
const env = {
SUPABASE_URL: process.env.SUPABASE_URL!,
@@ -377,13 +377,13 @@ export const loader = ({}: LoaderArgs) => {
Next, we call the `useLoaderData` hook in our component to get the `env` object.
```tsx title=app/root.tsx
```tsx app/root.tsx
const { env } = useLoaderData<typeof loader>()
```
We then want to instantiate a single instance of a Supabase browser client, to be used across our client-side components.
```tsx title=app/root.tsx
```tsx app/root.tsx
const [supabase] = useState(() =>
createBrowserClient<Database>(env.SUPABASE_URL, env.SUPABASE_ANON_KEY)
)
@@ -391,7 +391,7 @@ const [supabase] = useState(() =>
And then we can share this instance across our application with Outlet Context.
```tsx title=app/root.tsx
```tsx app/root.tsx
<Outlet context={{ supabase }} />
```
@@ -417,7 +417,7 @@ Let's pipe that through from our loader.
<TabPanel id="js" label="JavaScript">
```jsx title=app/root.jsx
```jsx app/root.jsx
export const loader = async ({ request }) => {
const env = {
SUPABASE_URL: process.env.SUPABASE_URL,
@@ -451,7 +451,7 @@ export const loader = async ({ request }) => {
<TabPanel id="ts" label="TypeScript">
```tsx title=app/root.tsx
```tsx app/root.tsx
export const loader = async ({ request }: LoaderArgs) => {
const env = {
SUPABASE_URL: process.env.SUPABASE_URL!,
@@ -496,7 +496,7 @@ And then use the revalidator, inside the `onAuthStateChange` hook.
<TabPanel id="js" label="JavaScript">
```jsx title=app/root.jsx
```jsx app/root.jsx
const { env, session } = useLoaderData()
const { revalidate } = useRevalidator()
@@ -524,7 +524,7 @@ useEffect(() => {
<TabPanel id="ts" label="TypeScript">
```tsx title=app/root.tsx
```tsx app/root.tsx
const { env, session } = useLoaderData<typeof loader>()
const { revalidate } = useRevalidator()
@@ -569,7 +569,7 @@ Now we can use our outlet context to access our single instance of Supabase and
<TabPanel id="js" label="JavaScript">
```jsx title=app/components/login.jsx
```jsx app/components/login.jsx
export default function Login() {
const { supabase } = useOutletContext()
@@ -607,7 +607,7 @@ export default function Login() {
<TabPanel id="ts" label="TypeScript">
```tsx title=app/components/login.tsx
```tsx app/components/login.tsx
export default function Login() {
const { supabase } = useOutletContext<{ supabase: SupabaseClient<Database> }>()
@@ -656,7 +656,7 @@ export default function Login() {
<TabPanel id="js" label="JavaScript">
```jsx title=app/routes/realtime.jsx
```jsx app/routes/realtime.jsx
import { useLoaderData, useOutletContext } from '@remix-run/react'
import { createServerClient } from '@supabase/auth-helpers-remix'
import { json } from '@remix-run/node'
@@ -704,7 +704,7 @@ export default function Index() {
<TabPanel id="ts" label="TypeScript">
```tsx title=app/routes/realtime.tsx
```tsx app/routes/realtime.tsx
import { useLoaderData, useOutletContext } from '@remix-run/react'
import { createServerClient } from '@supabase/auth-helpers-remix'
import { json } from '@remix-run/node'

View File

@@ -33,7 +33,7 @@ PUBLIC_SUPABASE_ANON_KEY=your-anon-key
Create a server supabase client in a handle hook:
```ts title=src/hooks.server.ts
```ts src/hooks.server.ts
// src/hooks.server.ts
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
@@ -77,7 +77,7 @@ export const handle: Handle = async ({ event, resolve }) => {
In order to make the session available to the UI (pages, layouts) we need to pass the session in the root layout server load function:
```ts title=src/routes/+layout.server.ts
```ts src/routes/+layout.server.ts
// src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types'
@@ -398,7 +398,7 @@ In version 0.9 we now setup our Supabase client for the server inside of a `hook
>
<TabPanel id="older-0.8" label="0.8.x">
```js title=src/lib/db.ts
```js src/lib/db.ts
// src/lib/db.ts
import { createClient } from '@supabase/auth-helpers-sveltekit'
import { env } from '$env/dynamic/public'
@@ -412,7 +412,7 @@ export const supabaseClient = createClient(env.PUBLIC_SUPABASE_URL, env.PUBLIC_S
</TabPanel>
<TabPanel id="0.9.0" label="0.9.0">
```js title=src/hooks.server.ts
```js src/hooks.server.ts
// src/hooks.server.ts
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
@@ -460,7 +460,7 @@ In order to use the Supabase library in your client code you will need to setup
>
<TabPanel id="older-0.8" label="0.8.x">
```html title=src/routes/+layout.svelte
```html src/routes/+layout.svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { supabaseClient } from '$lib/db'
@@ -486,7 +486,7 @@ In order to use the Supabase library in your client code you will need to setup
</TabPanel>
<TabPanel id="0.9.0" label="0.9.0">
```ts title=src/routes/+layout.ts
```ts src/routes/+layout.ts
// src/routes/+layout.ts
import { invalidate } from '$app/navigation'
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
@@ -512,7 +512,7 @@ export const load: LayoutLoad = async ({ fetch, data, depends }) => {
}
```
```svelte title=src/routes/+layout.svelte
```svelte src/routes/+layout.svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { invalidate } from '$app/navigation';
@@ -556,7 +556,7 @@ Since version 0.9 relies on `hooks.server.ts` to setup our client, we no longer
>
<TabPanel id="older-0-8" label="0.8.x">
```ts title=src/app.d.ts
```ts src/app.d.ts
// src/app.d.ts
/// <reference types="@sveltejs/kit" />
@@ -581,7 +581,7 @@ declare namespace App {
</TabPanel>
<TabPanel id="0.9.0" label="0.9.0">
```ts title=src/app.d.ts
```ts src/app.d.ts
// src/app.d.ts
import { SupabaseClient, Session } from '@supabase/supabase-js'
import { Database } from './DatabaseDefinitions'
@@ -614,7 +614,7 @@ declare global {
>
<TabPanel id="older-0-8" label="0.8.x">
```html title=src/routes/profile/+page.svelte
```html src/routes/profile/+page.svelte
<!-- src/routes/profile/+page.svelte -->
<script lang="ts">
/** @type {import('./$types').PageData} */
@@ -627,7 +627,7 @@ declare global {
<pre>{JSON.stringify(user, null, 2)}</pre>
```
```ts title=src/routes/profile/+page.ts
```ts src/routes/profile/+page.ts
// src/routes/profile/+page.ts
import type { PageLoad } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
@@ -650,7 +650,7 @@ export const load: PageLoad = async (event) => {
</TabPanel>
<TabPanel id="0.9.0" label="0.9.0">
```html title=src/routes/profile/+page.svelte
```html src/routes/profile/+page.svelte
<!-- src/routes/profile/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types'
@@ -664,7 +664,7 @@ export const load: PageLoad = async (event) => {
<pre>{JSON.stringify(user, null, 2)}</pre>
```
```ts title=src/routes/profile/+page.ts
```ts src/routes/profile/+page.ts
// src/routes/profile/+page.ts
import type { PageLoad } from './$types'
import { redirect } from '@sveltejs/kit'
@@ -696,7 +696,7 @@ export const load: PageLoad = async ({ parent }) => {
>
<TabPanel id="older-0-8" label="0.8.x">
```ts title=src/routes/api/protected-route/+server.ts
```ts src/routes/api/protected-route/+server.ts
// src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
@@ -716,7 +716,7 @@ export const GET: RequestHandler = async (event) => {
</TabPanel>
<TabPanel id="0.9.0" label="0.9.0">
```ts title=src/routes/api/protected-route/+server.ts
```ts src/routes/api/protected-route/+server.ts
// src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types'
import { json, error } from '@sveltejs/kit'
@@ -748,7 +748,7 @@ export const GET: RequestHandler = async ({ locals: { supabase, getSession } })
>
<TabPanel id="older-0.7" label="0.7.x">
```js title=src/lib/db.ts
```js src/lib/db.ts
import { createClient } from '@supabase/supabase-js'
import { setupSupabaseHelpers } from '@supabase/auth-helpers-sveltekit'
import { dev } from '$app/environment'
@@ -773,7 +773,7 @@ setupSupabaseHelpers({
</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">
```js title=src/lib/db.ts
```js src/lib/db.ts
import { createClient } from '@supabase/auth-helpers-sveltekit'
import { env } from '$env/dynamic/public'
// or use the static env
@@ -796,7 +796,7 @@ export const supabaseClient = createClient(env.PUBLIC_SUPABASE_URL, env.PUBLIC_S
>
<TabPanel id="older-0.7" label="0.7.x">
```html title=src/routes/+layout.svelte
```html src/routes/+layout.svelte
<script lang="ts">
// make sure the supabase instance is initialized on the client
import '$lib/db'
@@ -817,7 +817,7 @@ export const supabaseClient = createClient(env.PUBLIC_SUPABASE_URL, env.PUBLIC_S
</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">
```html title=src/routes/+layout.svelte
```html src/routes/+layout.svelte
<script>
import { supabaseClient } from '$lib/db'
import { invalidate } from '$app/navigation'
@@ -852,7 +852,7 @@ export const supabaseClient = createClient(env.PUBLIC_SUPABASE_URL, env.PUBLIC_S
>
<TabPanel id="older-0-7" label="0.7.x">
```ts title=src/hooks.server.ts
```ts src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
import { dev } from '$app/environment'
@@ -863,7 +863,7 @@ export const handle = auth()
**Optional** _if using additional handle methods_
```ts title=src/hooks.server.ts
```ts src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
import { dev } from '$app/environment'
@@ -876,12 +876,12 @@ export const handle = sequence(auth(), yourHandler)
</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">
```ts title=src/hooks.server.ts
```ts src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
```
```ts title=src/hooks.client.ts
```ts src/hooks.client.ts
// make sure the supabase instance is initialized on the client
import '$lib/db'
```
@@ -899,7 +899,7 @@ import '$lib/db'
>
<TabPanel id="older-0-7" label="0.7.x">
```ts title=src/app.d.ts
```ts src/app.d.ts
/// <reference types="@sveltejs/kit" />
// See https://kit.svelte.dev/docs/types#app
@@ -922,7 +922,7 @@ declare namespace App {
</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">
```ts title=src/app.d.ts
```ts src/app.d.ts
/// <reference types="@sveltejs/kit" />
// See https://kit.svelte.dev/docs/types#app
@@ -956,7 +956,7 @@ declare namespace App {
>
<TabPanel id="older-0-7" label="0.7.x">
```html title=src/routes/protected-route/+page.svelte
```html src/routes/protected-route/+page.svelte
<script lang="ts">
import type { PageData } from './$types'
@@ -971,7 +971,7 @@ declare namespace App {
<pre>{JSON.stringify(user, null, 2)}</pre>
```
```ts title=src/routes/protected-route/+page.ts
```ts src/routes/protected-route/+page.ts
import { withAuth } from '@supabase/auth-helpers-sveltekit'
import { redirect } from '@sveltejs/kit'
import type { PageLoad } from './$types'
@@ -989,7 +989,7 @@ export const load: PageLoad = withAuth(async ({ session, getSupabaseClient }) =>
</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">
```html title=src/routes/protected-route/+page.svelte
```html src/routes/protected-route/+page.svelte
<script>
/** @type {import('./$types').PageData} */
export let data
@@ -1001,7 +1001,7 @@ export const load: PageLoad = withAuth(async ({ session, getSupabaseClient }) =>
<pre>{JSON.stringify(user, null, 2)}</pre>
```
```ts title=src/routes/protected-route/+page.ts
```ts src/routes/protected-route/+page.ts
// src/routes/profile/+page.ts
import type { PageLoad } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
@@ -1034,7 +1034,7 @@ export const load: PageLoad = async (event) => {
>
<TabPanel id="older-0-7" label="0.7.x">
```ts title=src/routes/api/protected-route/+server.ts
```ts src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types'
import { withAuth } from '@supabase/auth-helpers-sveltekit'
import { json, redirect } from '@sveltejs/kit'
@@ -1058,7 +1058,7 @@ export const GET: RequestHandler = withAuth(async ({ session, getSupabaseClient
</TabPanel>
<TabPanel id="0.8.0" label="0.8.0">
```ts title=src/routes/api/protected-route/+server.ts
```ts src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types'
import { getSupabase } from '@supabase/auth-helpers-sveltekit'
import { json, redirect } from '@sveltejs/kit'
@@ -1095,7 +1095,7 @@ The environment variable prefix is now `PUBLIC_` instead of `VITE_` (e.g., `VITE
>
<TabPanel id="older" label="0.6.11 and below">
```js title=src/lib/db.ts
```js src/lib/db.ts
import { createSupabaseClient } from '@supabase/auth-helpers-sveltekit';
const { supabaseClient } = createSupabaseClient(
@@ -1109,7 +1109,7 @@ export { supabaseClient };
</TabPanel>
<TabPanel id="0.7.0" label="0.7.0">
```js title=src/lib/db.ts
```js src/lib/db.ts
import { createClient } from '@supabase/supabase-js'
import { setupSupabaseHelpers } from '@supabase/auth-helpers-sveltekit'
import { dev } from '$app/environment'
@@ -1144,7 +1144,7 @@ setupSupabaseHelpers({
>
<TabPanel id="older" label="0.6.11 and below">
```html title=src/routes/__layout.svelte
```html src/routes/__layout.svelte
<script>
import { session } from '$app/stores'
import { supabaseClient } from '$lib/db'
@@ -1161,7 +1161,7 @@ setupSupabaseHelpers({
The `@supabase/auth-helpers-svelte` library is no longer required as the `@supabase/auth-helpers-sveltekit` library handles all the client-side code.
```html title=src/routes/+layout.svelte
```html src/routes/+layout.svelte
<script lang="ts">
// make sure the supabase instance is initialized on the client
import '$lib/db'
@@ -1192,7 +1192,7 @@ The `@supabase/auth-helpers-svelte` library is no longer required as the `@supab
>
<TabPanel id="older" label="0.6.11 and below">
```ts title=src/hooks.ts
```ts src/hooks.ts
import { handleAuth } from '@supabase/auth-helpers-sveltekit'
import type { GetSession, Handle } from '@sveltejs/kit'
import { sequence } from '@sveltejs/kit/hooks'
@@ -1212,7 +1212,7 @@ export const getSession: GetSession = async (event) => {
</TabPanel>
<TabPanel id="0.7.0" label="0.7.0">
```ts title=src/hooks.server.ts
```ts src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
import { dev } from '$app/environment'
@@ -1223,7 +1223,7 @@ export const handle = auth()
**Optional** _if using additional handle methods_
```ts title=src/hooks.server.ts
```ts src/hooks.server.ts
// make sure the supabase instance is initialized on the server
import '$lib/db'
import { dev } from '$app/environment'
@@ -1246,7 +1246,7 @@ export const handle = sequence(auth(), yourHandler)
>
<TabPanel id="older" label="0.6.11 and below">
```ts title=src/app.d.ts
```ts src/app.d.ts
/// <reference types="@sveltejs/kit" />
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
@@ -1270,7 +1270,7 @@ declare namespace App {
</TabPanel>
<TabPanel id="0.7.0" label="0.7.0">
```ts title=src/app.d.ts
```ts src/app.d.ts
/// <reference types="@sveltejs/kit" />
// See https://kit.svelte.dev/docs/types#app
@@ -1303,7 +1303,7 @@ declare namespace App {
>
<TabPanel id="older" label="0.6.11 and below">
```html title=src/routes/index.svelte
```html src/routes/index.svelte
<script>
import { session } from '$app/stores'
</script>
@@ -1319,7 +1319,7 @@ declare namespace App {
</TabPanel>
<TabPanel id="0.7.0" label="0.7.0">
```html title=src/routes/+page.svelte
```html src/routes/+page.svelte
<script>
import { page } from '$app/stores'
</script>
@@ -1345,7 +1345,7 @@ declare namespace App {
>
<TabPanel id="older" label="0.6.11 and below">
```html title=src/routes/protected-route.svelte
```html src/routes/protected-route.svelte
<script lang="ts" context="module">
import { supabaseServerClient, withPageAuth } from '@supabase/auth-helpers-sveltekit'
import type { Load } from './__types/protected-page'
@@ -1378,7 +1378,7 @@ declare namespace App {
</TabPanel>
<TabPanel id="0.7.0" label="0.7.0">
```html title=src/routes/protected-route/+page.svelte
```html src/routes/protected-route/+page.svelte
<script lang="ts">
import type { PageData } from './$types'
@@ -1393,7 +1393,7 @@ declare namespace App {
<pre>{JSON.stringify(user, null, 2)}</pre>
```
```ts title=src/routes/protected-route/+page.ts
```ts src/routes/protected-route/+page.ts
import { withAuth } from '@supabase/auth-helpers-sveltekit'
import { redirect } from '@sveltejs/kit'
import type { PageLoad } from './$types'
@@ -1421,7 +1421,7 @@ export const load: PageLoad = withAuth(async ({ session, getSupabaseClient }) =>
>
<TabPanel id="older" label="0.6.11 and below">
```ts title=src/routes/api/protected-route.ts
```ts src/routes/api/protected-route.ts
import { supabaseServerClient, withApiAuth } from '@supabase/auth-helpers-sveltekit'
import type { RequestHandler } from './__types/protected-route'
@@ -1449,7 +1449,7 @@ export const GET: RequestHandler<GetOutput> = async ({ locals, request }) =>
</TabPanel>
<TabPanel id="0.7.0" label="0.7.0">
```ts title=src/routes/api/protected-route/+server.ts
```ts src/routes/api/protected-route/+server.ts
import type { RequestHandler } from './$types';
import { withAuth } from '@supabase/auth-helpers-sveltekit';
import { json, redirect } from '@sveltejs/kit';

View File

@@ -202,7 +202,7 @@ Create the following files inside the `.github/workflows` directory:
>
<TabPanel id="ci" label="ci.yaml">
```yaml title=.github/workflows/ci.yml
```yaml .github/workflows/ci.yml
name: CI
on:
@@ -233,7 +233,7 @@ jobs:
</TabPanel>
<TabPanel id="staging" label="staging.yaml">
```yaml title=.github/workflows/staging.yml
```yaml .github/workflows/staging.yml
name: Deploy Migrations to Staging
on:
@@ -264,7 +264,7 @@ jobs:
</TabPanel>
<TabPanel id="production" label="production.yaml">
```yaml title=.github/workflows/production.yml
```yaml .github/workflows/production.yml
name: Deploy Migrations to Production
on:

View File

@@ -237,7 +237,7 @@ This loads data directly from a file into a table. There are several file format
For example, if you wanted to load a CSV file into your movies table:
```csv title=./movies.csv
```text ./movies.csv
"The Empire Strikes Back", "After the Rebels are brutally overpowered by the Empire on the ice planet Hoth, Luke Skywalker begins Jedi training with Yoda."
"Return of the Jedi", "After a daring mission to rescue Han Solo from Jabba the Hutt, the Rebels dispatch to Endor to destroy the second Death Star."
```

View File

@@ -19,7 +19,7 @@ By creating a supabase client with the auth context from the function, you can d
1. Get the user object.
2. Run queries in the context of the user with [Row Level Security (RLS)](/docs/guides/auth/row-level-security) policies enforced.
```js lines=14,17-19,22-23 title=supabase/functions/select-from-table-with-auth-rls/index.ts
```js mark=14,17:19,22:23 supabase/functions/select-from-table-with-auth-rls/index.ts
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'

View File

@@ -93,13 +93,12 @@ export const meta = {
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```bash terminal
# .env
```bash .env
# PostgreSQL connection string used for migrations
DIRECT_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres"
# PostgreSQL connection string with pgBouncer config — used by Prisma Client
DATABASE_URL="postgres://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:6543/postgres?pgbouncer=true"
```
</StepHikeCompact.Code>
@@ -113,9 +112,7 @@ export const meta = {
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```file="api/prisma/schema.prisma" title="api/prisma/schema.prisma"
// api/db/schema.prisma
```prisma api/prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
@@ -132,9 +129,7 @@ export const meta = {
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```js
// api/db/schema.prisma
```prisma api/db/schema.prisma
model Country {
id Int @id @default(autoincrement())
name String @unique
@@ -153,9 +148,7 @@ export const meta = {
<StepHikeCompact.Code>
```ts
// scripts/seed.ts
```ts scripts/seed.ts
import type { Prisma } from '@prisma/client'
import { db } from 'api/src/lib/db'

View File

@@ -40,7 +40,7 @@ And finally we want to save the environment variables in the `environment.ts` fi
All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
These variables will be exposed on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
```ts title=environment.ts
```ts environment.ts
export const environment = {
production: false,
supabaseUrl: 'YOUR_SUPABASE_URL',
@@ -50,7 +50,7 @@ export const environment = {
Now that we have the API credentials in place, let's create a **SupabaseService** with `ng g s supabase` to initialize the Supabase client and implement functions to communicate with the Supabase API.
```ts title=src/app/supabase.service.ts
```ts src/app/supabase.service.ts
import { Injectable } from '@angular/core'
import {
AuthChangeEvent,
@@ -133,7 +133,7 @@ Optionally, update [src/styles.css](https://raw.githubusercontent.com/supabase/s
Let's set up an Angular component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
Create an **AuthComponent** with `ng g c auth` Angular CLI command.
```ts title=src/app/auth/auth.component.ts
```ts src/app/auth/auth.component.ts
import { Component } from '@angular/core'
import { FormBuilder } from '@angular/forms'
import { SupabaseService } from '../supabase.service'
@@ -174,7 +174,7 @@ export class AuthComponent {
}
```
```html title=src/app/auth/auth.component.html
```html src/app/auth/auth.component.html
<div class="row flex-center flex">
<div class="col-6 form-widget" aria-live="polite">
<h1 class="header">Supabase + Angular</h1>
@@ -205,7 +205,7 @@ export class AuthComponent {
Users also need a way to edit their profile details and manage their accounts after signing in.
Create an **AccountComponent** with the `ng g c account` Angular CLI command.
```ts title=src/app/account/account.component.ts
```ts src/app/account/account.component.ts
import { Component, Input, OnInit } from '@angular/core'
import { FormBuilder } from '@angular/forms'
import { AuthSession } from '@supabase/supabase-js'
@@ -295,7 +295,7 @@ export class AccountComponent implements OnInit {
}
```
```html title=src/app/account/account.component.html
```html src/app/account/account.component.html
<form [formGroup]="updateProfileForm" (ngSubmit)="updateProfile()" class="form-widget">
<div>
<label for="email">Email</label>
@@ -326,7 +326,7 @@ export class AccountComponent implements OnInit {
Now that we have all the components in place, let's update **AppComponent**:
```ts title=src/app/app.component.ts
```ts src/app/app.component.ts
import { Component, OnInit } from '@angular/core'
import { SupabaseService } from './supabase.service'
@@ -348,7 +348,7 @@ export class AppComponent implements OnInit {
}
```
```html title=src/app/app.component.html
```html src/app/app.component.html
<div class="container" style="padding: 50px 0 100px 0">
<app-account *ngIf="session; else auth" [session]="session"></app-account>
<ng-template #auth>
@@ -359,7 +359,7 @@ export class AppComponent implements OnInit {
`app.module.ts` also needs to be modified to include the `ReactiveFormsModule` from the `@angular/forms` package.
```ts title=src/app/app.module.ts
```ts src/app/app.module.ts
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
@@ -397,7 +397,7 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma
Let's create an avatar for the user so that they can upload a profile photo.
Create an **AvatarComponent** with `ng g c avatar` Angular CLI command.
```ts title=src/app/avatar/avatar.component.ts
```ts src/app/avatar/avatar.component.ts
import { Component, EventEmitter, Input, Output } from '@angular/core'
import { SafeResourceUrl, DomSanitizer } from '@angular/platform-browser'
import { SupabaseService } from '../supabase.service'
@@ -459,7 +459,7 @@ export class AvatarComponent {
}
```
```html title=src/app/avatar/avatar.component.html
```html src/app/avatar/avatar.component.html
<div>
<img
*ngIf="_avatarUrl"
@@ -489,7 +489,7 @@ export class AvatarComponent {
And then we can add the widget on top of the **AccountComponent** html template:
```html title=src/app/account.component.html
```html src/app/account.component.html
<form [formGroup]="updateProfileForm" (ngSubmit)="updateProfile()" class="form-widget">
<app-avatar [avatarUrl]="this.avatarUrl" (upload)="updateAvatar($event)"> </app-avatar>
<!-- input fields -->
@@ -498,7 +498,7 @@ And then we can add the widget on top of the **AccountComponent** html template:
And add an `updateAvatar` function along with an `avatarUrl` getter to the **AccountComponent** typescript file:
```ts title=src/app/account.component.ts
```ts src/app/account.component.ts
@Component({
selector: 'app-account',
templateUrl: './account.component.html',

View File

@@ -44,7 +44,7 @@ We need the API URL and the `anon` key that you copied [earlier](#get-the-api-ke
These variables will be exposed on the browser, and that's completely fine since we have
[Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
```ts title=lib/supabase.ts
```ts lib/supabase.ts
import 'react-native-url-polyfill/auto'
import * as SecureStore from 'expo-secure-store'
import { createClient } from '@supabase/supabase-js'
@@ -79,7 +79,7 @@ export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
Let's set up a React Native component to manage logins and sign ups.
Users would be able to sign in with their email and password.
```tsx title=components/Auth.tsx
```tsx components/Auth.tsx
import React, { useState } from 'react'
import { Alert, StyleSheet, View } from 'react-native'
import { supabase } from '../lib/supabase'
@@ -167,7 +167,7 @@ After a user is signed in we can allow them to edit their profile details and ma
Let's create a new component for that called `Account.tsx`.
```tsx title=components/Account.tsx
```tsx components/Account.tsx
import { useState, useEffect } from 'react'
import { supabase } from '../lib/supabase'
import { StyleSheet, View, Alert } from 'react-native'
@@ -294,7 +294,7 @@ const styles = StyleSheet.create({
Now that we have all the components in place, let's update `App.tsx`:
```tsx title=App.tsx
```tsx App.tsx
import 'react-native-url-polyfill/auto'
import { useState, useEffect } from 'react'
import { supabase } from './lib/supabase'
@@ -350,7 +350,7 @@ expo install react-native-document-picker
Let's create an avatar for the user so that they can upload a profile photo.
We can start by creating a new component:
```tsx title=components/Avatar.tsx
```tsx components/Avatar.tsx
import { useState, useEffect } from 'react'
import { supabase } from '../lib/supabase'
import { StyleSheet, View, Alert, Image, Button } from 'react-native'
@@ -481,7 +481,7 @@ const styles = StyleSheet.create({
And then we can add the widget to the Account page:
```tsx title=components/Account.tsx
```tsx components/Account.tsx
// Import the new component
import Avatar from './Avatar'

View File

@@ -53,7 +53,7 @@ For Android, edit the `android/app/src/main/AndroidManifest.xml` file.
Add an intent-filter to enable deep linking:
```xml title=android/app/src/main/AndroidManifest.xml
```xml android/app/src/main/AndroidManifest.xml
<manifest ...>
<!-- ... other tags -->
<application ...>
@@ -80,7 +80,7 @@ For iOS, edit the ios/Runner/Info.plist file.
Add CFBundleURLTypes to enable deep linking:
```xml title=ios/Runner/Info.plist"
```xml ios/Runner/Info.plist"
<!-- ... other tags -->
<plist>
<dict>
@@ -113,7 +113,7 @@ Now that we have deep links ready let's initialize the Supabase client inside ou
These variables will be exposed on the app, and that's completely fine since we have
[Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
```dart title=lib/main.dart
```dart lib/main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
@@ -130,7 +130,7 @@ Future<void> main() async {
Let's also create a constant file to make it easier to use Supabase client.
We will also include an extension method declaration to call `showSnackBar` with one line of code.
```dart title=lib/constants.dart
```dart lib/constants.dart
import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
@@ -158,7 +158,7 @@ extension ShowSnackBar on BuildContext {
Let's create a splash screen that will be shown to users right after they open the app.
This screen retrieves the current session and redirects the user accordingly.
```dart title=lib/pages/splash_page.dart
```dart lib/pages/splash_page.dart
import 'package:flutter/material.dart';
import 'package:supabase_quickstart/constants.dart';
@@ -209,7 +209,7 @@ We'll use Magic Links, so users can sign in with their email without using passw
Notice that this page sets up a listener on the user's auth state using `onAuthStateChange`.
A new event will fire when the user comes back to the app by clicking their magic link, which this page can catch and redirect the user accordingly.
```dart title=lib/pages/login_page.dart
```dart lib/pages/login_page.dart
import 'dart:async';
import 'package:flutter/foundation.dart';
@@ -307,7 +307,7 @@ class _LoginPageState extends State<LoginPage> {
After a user is signed in we can allow them to edit their profile details and manage their account.
Let's create a new widget called `account_page.dart` for that.
```dart title=lib/pages/account_page.dart"
```dart lib/pages/account_page.dart"
import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:supabase_quickstart/components/avatar.dart';
@@ -442,7 +442,7 @@ class _AccountPageState extends State<AccountPage> {
Now that we have all the components in place, let's update `lib/main.dart`:
```dart title=lib/main.dart
```dart lib/main.dart
import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:supabase_quickstart/pages/account_page.dart';
@@ -535,7 +535,7 @@ Once you are done with all of the above, it is time to dive into coding.
Let's create an avatar for the user so that they can upload a profile photo.
We can start by creating a new component:
```dart title=lib/components/avatar.dart
```dart lib/components/avatar.dart
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
@@ -631,7 +631,7 @@ class _AvatarState extends State<Avatar> {
And then we can add the widget to the Account page as well as some logic to update the `avatar_url` whenever the user uploads a new avatar.
```dart title=lib/pages/account_page.dart
```dart lib/pages/account_page.dart
import 'package:flutter/material.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:supabase_quickstart/components/avatar.dart';

View File

@@ -41,7 +41,7 @@ And finally we want to save the environment variables in the `environment.ts` fi
All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
These variables will be exposed on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
```ts title=environment.ts
```ts environment.ts
export const environment = {
production: false,
supabaseUrl: 'YOUR_SUPABASE_URL',
@@ -51,7 +51,7 @@ export const environment = {
Now that we have the API credentials in place, let's create a **SupabaseService** with `ionic g s supabase` to initialize the Supabase client and implement functions to communicate with the Supabase API.
```ts title=src/app/supabase.service.ts
```ts src/app/supabase.service.ts
import { Injectable } from '@angular/core'
import { LoadingController, ToastController } from '@ionic/angular'
import { AuthChangeEvent, createClient, Session, SupabaseClient } from '@supabase/supabase-js'
@@ -139,7 +139,7 @@ Create an **LoginPage** with `ionic g page login` Ionic CLI command.
> This guide will show the template inline, but the example app will have templateUrls
```ts title=src/app/login/login.page.ts
```ts src/app/login/login.page.ts
import { Component, OnInit } from '@angular/core'
import { SupabaseService } from '../supabase.service'
@@ -198,7 +198,7 @@ export class LoginPage implements OnInit {
After a user is signed in we can allow them to edit their profile details and manage their account.
Create an **AccountComponent** with `ionic g page account` Ionic CLI command.
```ts title=src/app/account.component.ts
```ts src/app/account.component.ts
import { Component, OnInit } from '@angular/core'
import { Router } from '@angular/router'
import { Profile, SupabaseService } from '../supabase.service'
@@ -292,7 +292,7 @@ export class AccountPage implements OnInit {
Now that we have all the components in place, let's update **AppComponent**:
```ts title=src/app/app.component.ts
```ts src/app/app.component.ts
import { Component } from '@angular/core'
import { Router } from '@angular/router'
import { SupabaseService } from './supabase.service'
@@ -320,7 +320,7 @@ export class AppComponent {
Then update the **AppRoutingModule**
```ts title=src/app/app.ts"
```ts src/app/app.ts"
import { NgModule } from '@angular/core'
import { PreloadAllModules, RouterModule, Routes } from '@angular/router'
@@ -376,7 +376,7 @@ Ionic PWA elements is a companion package that will polyfill certain browser API
With those packages installed we can update our `main.ts` to include an additional bootstapping call for the Ionic PWA Elements.
```ts title=src/main.ts
```ts src/main.ts
import { enableProdMode } from '@angular/core'
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'
@@ -400,7 +400,7 @@ Then create an **AvatarComponent** with this Ionic CLI command:
ionic g component avatar --module=/src/app/account/account.module.ts --create-module
```
```ts title=src/app/avatar.component.ts
```ts src/app/avatar.component.ts
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'
import { SupabaseService } from '../supabase.service'
@@ -503,7 +503,7 @@ export class AvatarComponent implements OnInit {
And then we can add the widget on top of the **AccountComponent** html template:
```ts title=src/app/account.component.ts
```ts src/app/account.component.ts
template: `
<ion-header>
<ion-toolbar>

View File

@@ -40,7 +40,7 @@ npm install @supabase/supabase-js
And finally we want to save the environment variables in a `.env`.
All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
```bash title=.env
```bash .env
REACT_APP_SUPABASE_URL=YOUR_SUPABASE_URL
REACT_APP_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
```
@@ -48,7 +48,7 @@ REACT_APP_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
Now that we have the API credentials in place, let's create a helper file to initialize the Supabase client. These variables will be exposed
on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
```js title=src/supabaseClient.js
```js src/supabaseClient.js
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = process.env.REACT_APP_SUPABASE_URL
@@ -61,7 +61,7 @@ export const supabase = createClient(supabaseUrl, supabaseAnonKey)
Let's set up a React component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
```jsx title=/src/pages/Login.tsx
```jsx /src/pages/Login.tsx
import { useState } from 'react';
import {
IonButton,
@@ -140,7 +140,7 @@ After a user is signed in we can allow them to edit their profile details and ma
Let's create a new component for that called `Account.tsx`.
```jsx title=src/pages/Account.tsx
```jsx src/pages/Account.tsx
import {
IonButton,
IonContent,
@@ -294,7 +294,7 @@ export function AccountPage() {
Now that we have all the components in place, let's update `App.tsx`:
```jsx title=src/App.tsx
```jsx src/App.tsx
import { Redirect, Route } from 'react-router-dom'
import { IonApp, IonRouterOutlet, setupIonicReact } from '@ionic/react'
import { IonReactRouter } from '@ionic/react-router'
@@ -370,7 +370,7 @@ Ionic PWA elements is a companion package that will polyfill certain browser API
With those packages installed we can update our `index.tsx` to include an additional bootstapping call for the Ionic PWA Elements.
```ts title=src/index.tsx
```ts src/index.tsx
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
@@ -393,7 +393,7 @@ reportWebVitals()
Then create an **AvatarComponent**.
```jsx title=src/components/Avatar.tsx
```jsx src/components/Avatar.tsx
import { IonIcon } from '@ionic/react';
import { person } from 'ionicons/icons';
import { Camera, CameraResultType } from '@capacitor/camera';
@@ -476,7 +476,7 @@ export function Avatar({
And then we can add the widget to the Account page:
```jsx title=src/pages/Account.tsx
```jsx src/pages/Account.tsx
// Import the new component
import { Avatar } from '../components/Avatar';

View File

@@ -40,7 +40,7 @@ npm install @supabase/supabase-js
And finally we want to save the environment variables in a `.env`.
All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
```bash title=.env
```bash .env
VUE_APP_SUPABASE_URL=YOUR_SUPABASE_URL
VUE_APP_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
```
@@ -48,7 +48,7 @@ VUE_APP_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
Now that we have the API credentials in place, let's create a helper file to initialize the Supabase client. These variables will be exposed
on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
```js title=src/supabase.ts"
```js src/supabase.ts"
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.VUE_APP_SUPABASE_URL as string;
@@ -61,7 +61,7 @@ export const supabase = createClient(supabaseUrl, supabaseAnonKey);
Let's set up a Vue component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
```html title=/src/views/Login.vue
```html /src/views/Login.vue
<template>
<ion-page>
<ion-header>
@@ -156,7 +156,7 @@ After a user is signed in we can allow them to edit their profile details and ma
Let's create a new component for that called `Account.vue`.
```html title=src/views/Account.vue
```html src/views/Account.vue
<template>
<ion-page>
<ion-header>
@@ -315,7 +315,7 @@ Let's create a new component for that called `Account.vue`.
Now that we have all the components in place, let's update `App.vue` and our routes:
```ts title=src/router.index.ts
```ts src/router.index.ts
import { createRouter, createWebHistory } from '@ionic/vue-router'
import { RouteRecordRaw } from 'vue-router'
import LoginPage from '../views/Login.vue'
@@ -341,7 +341,7 @@ const router = createRouter({
export default router
```
```html title=src/App.vue
```html src/App.vue
<template>
<ion-app>
<ion-router-outlet />
@@ -403,7 +403,7 @@ Ionic PWA elements is a companion package that will polyfill certain browser API
With those packages installed we can update our `main.ts` to include an additional bootstapping call for the Ionic PWA Elements.
```ts title=src/main.tsx"
```ts src/main.tsx"
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
@@ -426,7 +426,7 @@ router.isReady().then(() => {
Then create an **AvatarComponent**.
```html title=src/components/Avatar.vue
```html src/components/Avatar.vue
<template>
<div class="avatar">
<div class="avatar_wrapper" @click="uploadAvatar">
@@ -529,7 +529,7 @@ Then create an **AvatarComponent**.
And then we can add the widget to the Account page:
```html title=src/views/Account.vue
```html src/views/Account.vue
<template>
<ion-page>
<ion-header>

View File

@@ -71,7 +71,7 @@ npm install @supabase/supabase-js
And finally we want to save the environment variables in a `.env.local`.
All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
```bash title=.env.local
```bash .env.local
NEXT_PUBLIC_SUPABASE_URL=YOUR_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
```
@@ -103,7 +103,7 @@ npm install @supabase/auth-helpers-react @supabase/auth-helpers-nextjs
Wrap your `pages/_app.js` component with the `SessionContextProvider` component:
```jsx title=pages/_app.js
```jsx pages/_app.js
import '../styles/globals.css'
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
import { SessionContextProvider } from '@supabase/auth-helpers-react'
@@ -126,7 +126,7 @@ export default MyApp
Wrap your `pages/_app.tsx` component with the `SessionContextProvider` component:
```jsx lines=2,8 title=pages/_app.tsx
```jsx mark=2,8 pages/_app.tsx
import '../styles/globals.css'
import { useState } from 'react'
import { createBrowserSupabaseClient } from '@supabase/auth-helpers-nextjs'
@@ -167,7 +167,7 @@ npm install @supabase/auth-ui-react
Add the `Auth` component to your home page
```jsx title=pages/index.js
```jsx pages/index.js
import { Auth } from '@supabase/auth-ui-react'
import { ThemeSupa } from '@supabase/auth-ui-shared'
import { useSession, useSupabaseClient } from '@supabase/auth-helpers-react'
@@ -204,7 +204,7 @@ After a user is signed in we can allow them to edit their profile details and ma
Let's create a new component for that called `Account.js` within a `components` folder.
```tsx title=components/Account.js
```tsx components/Account.js
import { useState, useEffect } from 'react'
import { useUser, useSupabaseClient } from '@supabase/auth-helpers-react'
@@ -322,7 +322,7 @@ Let's create a new component for that called `Account.tsx` within a `components`
First, [generate type definitions from your database using the Supabase CLI](https://supabase.com/docs/guides/database/api/generating-types) to `../utils/database.types`.
```tsx title=components/Account.tsx
```tsx components/Account.tsx
import { useState, useEffect } from 'react'
import { useUser, useSupabaseClient, Session } from '@supabase/auth-helpers-react'
import { Database } from '../utils/database.types'
@@ -452,7 +452,7 @@ export default function Account({ session }: { session: Session }) {
Now that we have all the components in place, let's update `pages/index.js`:
```jsx lines=3,14 title=pages/index.js
```jsx mark=3,14 pages/index.js
import { Auth } from '@supabase/auth-ui-react'
import { ThemeSupa } from '@supabase/auth-ui-shared'
import { useSession, useSupabaseClient } from '@supabase/auth-helpers-react'
@@ -501,7 +501,7 @@ Let's create an avatar widget for the user so that they can upload a profile pho
>
<TabPanel id="js" label="JavaScript">
```jsx title=components/Avatar.js
```jsx components/Avatar.js
import React, { useEffect, useState } from 'react'
import { useSupabaseClient } from '@supabase/auth-helpers-react'
@@ -593,7 +593,7 @@ export default function Avatar({ uid, url, size, onUpload }) {
</TabPanel>
<TabPanel id="ts" label="TypeScript">
```tsx title=components/Avatar.tsx
```tsx components/Avatar.tsx
import React, { useEffect, useState } from 'react'
import { useSupabaseClient } from '@supabase/auth-helpers-react'
import { Database } from '../utils/database.types'
@@ -701,7 +701,7 @@ export default function Avatar({
And then we can add the widget to the Account page:
```jsx title=components/Account.js
```jsx components/Account.js
// Import the new component
import Avatar from './Avatar'

View File

@@ -39,7 +39,7 @@ npm install @nuxtjs/supabase --save-dev
And finally we want to save the environment variables in a `.env`.
All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
```bash title=.env
```bash .env
SUPABASE_URL="YOUR_SUPABASE_URL"
SUPABASE_KEY="YOUR_SUPABASE_ANON_KEY"
```
@@ -51,7 +51,7 @@ No need to initialize Supabase. The library will take care of it automatically.
And one optional step is to update the CSS file `assets/main.css` to make the app look nice.
You can find the full contents of this file [here](https://github.com/supabase-community/nuxt3-quickstarter/blob/main/assets/main.css).
```typescript title=nuxt.config.ts
```typescript nuxt.config.ts
import { defineNuxtConfig } from 'nuxt'
// https://v3.nuxtjs.org/api/configuration/nuxt.config
@@ -65,7 +65,7 @@ export default defineNuxtConfig({
Let's set up a Vue component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
```vue title=/components/Auth.vue
```vue /components/Auth.vue
<script setup>
const supabase = useSupabaseClient()
@@ -116,7 +116,7 @@ To access the user information, use the composable [useSupabaseUser](https://sup
After a user is signed in we can allow them to edit their profile details and manage their account.
Let's create a new component for that called `Account.vue`.
```vue title=components/Account.vue
```vue components/Account.vue
<script setup>
const supabase = useSupabaseClient()
@@ -215,7 +215,7 @@ async function signOut() {
Now that we have all the components in place, let's update `app.vue`:
```vue title=app.vue
```vue app.vue
<script setup>
const user = useSupabaseUser()
</script>
@@ -246,7 +246,7 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma
Let's create an avatar for the user so that they can upload a profile photo. We can start by creating a new component:
```vue title=components/Avatar.vue
```vue components/Avatar.vue
<script setup>
const props = defineProps(['path'])
const { path } = toRefs(props)
@@ -337,7 +337,7 @@ watch(path, () => {
And then we can add the widget to the Account page:
```vue title=components/Account.vue
```vue components/Account.vue
<script setup>
const supabase = useSupabaseClient()

View File

@@ -39,7 +39,7 @@ npm install @supabase/supabase-js
And finally we want to save the environment variables in a `.env.local` file.
All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
```bash title=.env
```bash .env
VITE_SUPABASE_URL=YOUR_SUPABASE_URL
VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
```
@@ -49,7 +49,7 @@ on the browser, and that's completely fine since we have [Row Level Security](/d
Create and edit `src/supabaseClient.js`:
```js title=src/supabaseClient.js
```js src/supabaseClient.js
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
@@ -67,7 +67,7 @@ Let's set up a React component to manage logins and sign ups. We'll use Magic Li
Create and edit `src/Auth.jsx`:
```jsx title=src/Auth.jsx
```jsx src/Auth.jsx
import { useState } from 'react'
import { supabase } from './supabaseClient'
@@ -123,7 +123,7 @@ After a user is signed in we can allow them to edit their profile details and ma
Let's create a new component for that called `src/Account.jsx`.
```jsx title=src/Account.jsx
```jsx src/Account.jsx
import { useState, useEffect } from 'react'
import { supabase } from './supabaseClient'
@@ -226,7 +226,7 @@ export default function Account({ session }) {
Now that we have all the components in place, let's update `src/App.jsx`:
```jsx title=src/App.jsx
```jsx src/App.jsx
import './App.css'
import { useState, useEffect } from 'react'
import { supabase } from './supabaseClient'
@@ -276,7 +276,7 @@ Let's create an avatar for the user so that they can upload a profile photo. We
Create and edit `src/Avatar.jsx`:
```jsx title=src/Avatar.jsx
```jsx src/Avatar.jsx
import { useEffect, useState } from 'react'
import { supabase } from './supabaseClient'
@@ -365,7 +365,7 @@ export default function Avatar({ url, size, onUpload }) {
And then we can add the widget to the Account page at `src/Account.jsx`:
```jsx title=src/Account.jsx
```jsx src/Account.jsx
// Import the new component
import Avatar from './Avatar'

View File

@@ -127,7 +127,7 @@ Say, **yes** and it will setup the Supabase client in your app and also provide
Next, we want to save the environment variables in a `.env`.
We need the `API URL` as well as the `anon` and `jwt_secret` keys that you copied [earlier](#get-the-api-keys).
```bash title=.env
```bash .env
SUPABASE_URL=YOUR_SUPABASE_URL
SUPABASE_KEY=YOUR_SUPABASE_ANON_KEY
SUPABASE_JWT_SECRET=YOUR_SUPABASE_JWT_SECRET
@@ -135,7 +135,7 @@ SUPABASE_JWT_SECRET=YOUR_SUPABASE_JWT_SECRET
And finally, you will also need to save **just** the `web side` environment variables to the `redwood.toml`.
```bash title=redwood.toml
```bash redwood.toml
[web]
title = "Supabase Redwood Tutorial"
port = 8910
@@ -153,7 +153,7 @@ since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled
You'll see these being used to configure your Supabase client in `web/src/App.js`:
```js title=web/src/App.js
```js web/src/App.js
// ... Redwood imports
import { AuthProvider } from '@redwoodjs/auth'
import { createClient } from '@supabase/supabase-js'
@@ -217,7 +217,7 @@ You can stop the `dev` server if you want; to see your changes, just be sure to
You should see the `Home` page route in `web/src/Routes.js`:
```bash title=web/src/Routes.js
```bash web/src/Routes.js
import { Router, Route } from '@redwoodjs/router'
const Routes = () => {
@@ -248,7 +248,7 @@ yarn rw g component auth
Now, update the `Auth.js` component to contain:
```jsx title=/web/src/components/Auth/Auth.js
```jsx /web/src/components/Auth/Auth.js
import { useState } from 'react'
import { useAuth } from '@redwoodjs/auth'
@@ -321,7 +321,7 @@ yarn rw g component account
And then update the file to contain:
```jsx title=web/src/components/Account/Account.js
```jsx web/src/components/Account/Account.js
import { useState, useEffect } from 'react'
import { useAuth } from '@redwoodjs/auth'
@@ -453,7 +453,7 @@ of the supabase client to interact with your API.
Now that we have all the components in place, let's update your `HomePage` page to use them:
```jsx title=web/src/pages/HomePage/HomePage.js
```jsx web/src/pages/HomePage/HomePage.js
import { useAuth } from '@redwoodjs/auth'
import { MetaTags } from '@redwoodjs/web'
@@ -506,7 +506,7 @@ yarn rw g component avatar
Now, update your Avatar component to contain the following widget:
```jsx title=web/src/components/Avatar/Avatar.js
```jsx web/src/components/Avatar/Avatar.js
import { useEffect, useState } from 'react'
import { useAuth } from '@redwoodjs/auth'
@@ -599,7 +599,7 @@ export default Avatar
And then we can add the widget to the Account component:
```jsx title=web/src/components/Account/Account.js
```jsx web/src/components/Account/Account.js
// Import the new component
import Avatar from 'src/components/Avatar'

View File

@@ -38,7 +38,7 @@ npm install @supabase/supabase-js
And finally we want to save the environment variables in a `.env`.
All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
```bash title=.env
```bash .env
VITE_SUPABASE_URL=YOUR_SUPABASE_URL
VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
```
@@ -46,7 +46,7 @@ VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
Now that we have the API credentials in place, let's create a helper file to initialize the Supabase client. These variables will be exposed
on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
```js title=src/supabaseClient.jsx
```js src/supabaseClient.jsx
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
@@ -62,7 +62,7 @@ You can find the full contents of this file [here](https://raw.githubusercontent
Let's set up a SolidJS component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
```jsx title=src/Auth.tsx
```jsx src/Auth.tsx
import { createSignal } from 'solid-js'
import { supabase } from './supabaseClient'
@@ -122,7 +122,7 @@ After a user is signed in we can allow them to edit their profile details and ma
Let's create a new component for that called `Account.tsx`.
```tsx title=src/Account.tsx
```tsx src/Account.tsx
import { AuthSession } from '@supabase/supabase-js'
import { Component, createEffect, createSignal } from 'solid-js'
import { supabase } from './supabaseClient'
@@ -241,7 +241,7 @@ export default Account
Now that we have all the components in place, let's update `App.tsx`:
```jsx title=src/App.tsx
```jsx src/App.tsx
import { Component, createEffect, createSignal } from 'solid-js'
import { supabase } from './supabaseClient'
import { AuthSession } from '@supabase/supabase-js'
@@ -289,7 +289,7 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma
Let's create an avatar for the user so that they can upload a profile photo. We can start by creating a new component:
```jsx title=src/Avatar.tsx
```jsx src/Avatar.tsx
import { Component, createEffect, createSignal, JSX } from 'solid-js'
import { supabase } from './supabaseClient'
@@ -392,7 +392,7 @@ export default Avatar
And then we can add the widget to the Account page:
```jsx title=src/Account.tsx
```jsx src/Account.tsx
// Import the new component
import Avatar from './Avatar'

View File

@@ -39,7 +39,7 @@ npm install @supabase/supabase-js
And finally we want to save the environment variables in a `.env`.
All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
```bash title=.env
```bash .env
VITE_SUPABASE_URL=YOUR_SUPABASE_URL
VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
```
@@ -47,7 +47,7 @@ VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
Now that we have the API credentials in place, let's create a helper file to initialize the Supabase client. These variables will be exposed
on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
```js title=src/supabaseClient.ts
```js src/supabaseClient.ts
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
@@ -63,7 +63,7 @@ You can find the full contents of this file [here](https://raw.githubusercontent
Let's set up a Svelte component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
```html title=src/lib/Auth.svelte
```html src/lib/Auth.svelte
<script lang="ts">
import { supabase } from 'src/supabaseClient'
@@ -116,7 +116,7 @@ Let's set up a Svelte component to manage logins and sign ups. We'll use Magic L
After a user is signed in we can allow them to edit their profile details and manage their account.
Let's create a new component for that called `Account.svelte`.
```html title=src/lib/Account.svelte
```html src/lib/Account.svelte
<script lang="ts">
import { onMount } from "svelte";
import type { AuthSession } from "@supabase/supabase-js";
@@ -213,7 +213,7 @@ Let's create a new component for that called `Account.svelte`.
Now that we have all the components in place, let's update `App.svelte`:
```html title=src/App.svelte
```html src/App.svelte
<script lang="ts">
import { onMount } from 'svelte'
import { supabase } from './supabaseClient'
@@ -263,7 +263,7 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma
Let's create an avatar for the user so that they can upload a profile photo. We can start by creating a new component:
```html title=src/lib/Avatar.svelte
```html src/lib/Avatar.svelte
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { supabase } from '../supabaseClient'
@@ -353,7 +353,7 @@ Let's create an avatar for the user so that they can upload a profile photo. We
And then we can add the widget to the Account page:
```html title=src/lib/Account.svelte
```html src/lib/Account.svelte
<script lang="ts">
// Import the new component
import Avatar from './Avatar.svelte'

View File

@@ -40,7 +40,7 @@ npm install @supabase/supabase-js
And finally we want to save the environment variables in a `.env`.
All we need are the `SUPABASE_URL` and the `SUPABASE_KEY` key that you copied [earlier](#get-the-api-keys).
```bash title=.env
```bash .env
PUBLIC_SUPABASE_URL="YOUR_SUPABASE_URL"
PUBLIC_SUPABASE_ANON_KEY="YOUR_SUPABASE_KEY"
```
@@ -61,7 +61,7 @@ npm install @supabase/auth-helpers-sveltekit
Add the code below to your `src/hooks.server.ts` to initialize the client on the server:
```ts title=src/hooks.server.ts
```ts src/hooks.server.ts
// src/hooks.server.ts
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public'
import { createSupabaseServerClient } from '@supabase/auth-helpers-sveltekit'
@@ -94,7 +94,7 @@ export const handle: Handle = async ({ event, resolve }) => {
If you are using TypeScript the compiler might complain about `event.locals.supabase` and `event.locals.getSession`, this can be fixed by updating your `src/app.d.ts` with the content below:
```ts title=src/app.d.ts
```ts src/app.d.ts
// src/app.d.ts
import { SupabaseClient, Session } from '@supabase/supabase-js'
@@ -116,7 +116,7 @@ declare global {
Create a new `src/routes/+layout.server.ts` file to handle the session on the server-side.
```ts title=src/routes/+layout.server.ts
```ts src/routes/+layout.server.ts
// src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types'
@@ -131,7 +131,7 @@ export const load: LayoutServerLoad = async ({ locals: { getSession } }) => {
Create a new `src/routes/+layout.ts` file to handle the session and the supabase object on the client-side.
```ts title=src/routes/+layout.ts
```ts src/routes/+layout.ts
// src/routes/+layout.ts
import { invalidate } from '$app/navigation'
import { PUBLIC_SUPABASE_ANON_KEY, PUBLIC_SUPABASE_URL } from '$env/static/public'
@@ -158,7 +158,7 @@ export const load: LayoutLoad = async ({ fetch, data, depends }) => {
Update your `src/routes/+layout.svelte`:
```svelte title=src/routes/+layout.svelte
```svelte src/routes/+layout.svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import '../styles.css';
@@ -203,7 +203,7 @@ npm install @supabase/auth-ui-svelte @supabase/auth-ui-shared
Add the `Auth` component to your home page
```svelte title=src/routes/+page.svelte
```svelte src/routes/+page.svelte
<!-- src/routes/+page.svelte -->
<script lang="ts">
import { Auth } from '@supabase/auth-ui-svelte';
@@ -289,7 +289,7 @@ This page is used for waiting while your client-side code inside of `src/routes/
After a user is signed in, they need to be able to edit their profile details and manage their account.
Create a new `src/routes/account/+page.svelte` file with the content below.
```svelte title=src/routes/account/+page.svelte
```svelte src/routes/account/+page.svelte
<!-- src/routes/account/+page.svelte -->
<script lang="ts">
import { enhance, type SubmitFunction } from '$app/forms';
@@ -456,7 +456,7 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma
Let's create an avatar for the user so that they can upload a profile photo. We can start by creating a new component called `Avatar.svelte` in the `src/routes/account` directory:
```svelte title=src/routes/account/Avatar.svelte
```svelte src/routes/account/Avatar.svelte
<!-- src/routes/account/Avatar.svelte -->
<script lang="ts">
import type { SupabaseClient } from '@supabase/supabase-js';
@@ -554,7 +554,7 @@ Let's create an avatar for the user so that they can upload a profile photo. We
And then we can add the widget to the Account page:
```svelte title=src/routes/account/+page.svelte
```svelte src/routes/account/+page.svelte
<!-- src/routes/account/+page.svelte -->
<script lang="ts">
// Import the new component

View File

@@ -44,7 +44,7 @@ npm install @supabase/supabase-js
And finally we want to save the environment variables in a `.env`.
All we need are the API URL and the `anon` key that you copied [earlier](#get-the-api-keys).
```bash title=.env
```bash .env
VITE_SUPABASE_URL=YOUR_SUPABASE_URL
VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
```
@@ -52,7 +52,7 @@ VITE_SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEY
With the API credentials in place, create an `src/supabase.js` helper file to initialize the Supabase client. These variables are exposed
on the browser, and that's completely fine since we have [Row Level Security](/docs/guides/auth#row-level-security) enabled on our Database.
```js title=src/supabase.js
```js src/supabase.js
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
@@ -67,7 +67,7 @@ Optionally, update [src/style.css](https://raw.githubusercontent.com/supabase/su
Set up an `src/components/Auth.vue` component to manage logins and sign ups. We'll use Magic Links, so users can sign in with their email without using passwords.
```vue title=/src/components/Auth.vue
```vue /src/components/Auth.vue
<script setup>
import { ref } from 'vue'
import { supabase } from '../supabase'
@@ -119,7 +119,7 @@ const handleLogin = async () => {
After a user is signed in we can allow them to edit their profile details and manage their account.
Create a new `src/components/Account.vue` component to handle this.
```vue title=src/components/Account.vue
```vue src/components/Account.vue
<script setup>
import { supabase } from '../supabase'
import { onMounted, ref, toRefs } from 'vue'
@@ -232,7 +232,7 @@ async function signOut() {
Now that we have all the components in place, let's update `App.vue`:
```vue title=src/App.vue
```vue src/App.vue
<script setup>
import { onMounted, ref } from 'vue'
import Account from './components/Account.vue'
@@ -278,7 +278,7 @@ Every Supabase project is configured with [Storage](/docs/guides/storage) for ma
Create a new `src/components/Avatar.vue` component that allows users to upload profile photos:
```vue title=src/components/Avatar.vue
```vue src/components/Avatar.vue
<script setup>
import { ref, toRefs, watch } from 'vue'
import { supabase } from '../supabase'
@@ -362,7 +362,7 @@ watch(path, () => {
And then we can add the widget to the Account page in `src/components/Account.vue`:
```vue title=src/components/Account.vue
```vue src/components/Account.vue
<script>
// Import the new component
import Avatar from './Avatar.vue'

View File

@@ -63,7 +63,7 @@ After that, follow the instructions [here](https://documentation.onesignal.com/d
The Next.js app will have a login form for the user to sign in, and a button that they can press to make an order once they are signed in. Update the `index.tsx` file to the following.
```tsx title="pages/index.tsx"
```tsx pages/index.tsx
import { createClient, User } from '@supabase/supabase-js'
import type { NextPage } from 'next'
import Head from 'next/head'

View File

@@ -52,7 +52,7 @@ git clone https://github.com/supabase-community/firebase-to-supabase.git
1. At the top right of the users list, open the menu (3 dots) and click **Password hash parameters**.
1. Copy and save the parameters for `base64_signer_key`, `base64_salt_separator`, `rounds`, and `mem_cost`.
```bash title=Sample%20password%20hash%20parameters
```text Sample
hash_config {
algorithm: SCRYPT,
base64_signer_key: XXXX/XXX+XXXXXXXXXXXXXXXXX+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX==,

View File

@@ -61,17 +61,17 @@ git clone https://github.com/supabase-community/firebase-to-supabase.git
You can customize the way your JSON file is written using a [custom hook](#custom-hooks). A common use for this is to "flatten" the JSON file, or to split nested data into separate, related database tables. For example, you could take a Firestore document that looks like this:
```json title=Firestore%20document
```json Firestore
[{ "user": "mark", "score": 100, "items": ["hammer", "nail", "glue"] }]
```
And split it into two files (one table for users and one table for items):
```json title=Users%20table
```json Users
[{ "user": "mark", "score": 100 }]
```
```json title=Items%20table
```json Items
[
{ "user": "mark", "item": "hammer" },
{ "user": "mark", "item": "nail" },
@@ -187,14 +187,14 @@ module.exports = (collectionName, doc, recordCounters, writeRecord) => {
The result is two separate JSON files:
```json title=users.json
```json users.json
[
{ "uid": "abc123", "name": "mark", "score": 100 },
{ "uid": "xyz789", "name": "chuck", "score": 9999999 }
]
```
```json title=weapons.json
```json weapons.json
[
{ "uid": "abc123", "weapon": "toothpick" },
{ "uid": "abc123", "weapon": "needle" },

View File

@@ -88,7 +88,7 @@ We strongly recommend [decoupling your database](../self-hosting#managing-your-d
The middleware will run with any PostgreSQL database that has logical replication enabled. The following environment variables should be updated
in the `.env` file to point to your external database:
```env title=.env
```bash .env
POSTGRES_PASSWORD=your-super-secret-and-long-postgres-password
POSTGRES_HOST=db

View File

@@ -1,3 +1,61 @@
.dark {
--ch-0: dark;
--ch-1: #8b949e;
--ch-2: #79c0ff;
--ch-3: #ffcda1;
--ch-4: #f8f8f2;
--ch-5: #bda4ff;
--ch-6: var(--colors-brand10);
--ch-7: #569cd6;
--ch-8: var(--colors-brand9);
--ch-9: #ffa198;
--ch-10: #f0f6fc;
--ch-11: #490202;
--ch-12: #04260f;
--ch-13: #5a1e02;
--ch-14: #161b22;
--ch-15: #8b949e;
--ch-16: var(--colors-scale1);
--ch-17: #264f78;
--ch-18: #3794ff;
--ch-19: #ffffff0b;
--ch-20: #6e7681;
--ch-21: #010409;
--ch-22: #30363d;
--ch-23: #f78166;
--ch-24: #6e768166;
--ch-25: #6e76811a;
}
.light {
--ch-0: light;
--ch-1: #6e7781;
--ch-2: #0550ae;
--ch-3: #953800;
--ch-4: #24292f;
--ch-5: #8250df;
--ch-6: var(--colors-brand10);
--ch-7: #cf222e;
--ch-8: var(--colors-brand9);
--ch-9: #82071e;
--ch-10: #f6f8fa;
--ch-11: #ffebe9;
--ch-12: #dafbe1;
--ch-13: #ffd8b5;
--ch-14: #eaeef2;
--ch-15: #57606a;
--ch-16: var(--colors-scale1);
--ch-17: #add6ff;
--ch-18: #1a85ff;
--ch-19: #fdff0033;
--ch-20: #8c959f;
--ch-21: #f6f8fa;
--ch-22: #d0d7de;
--ch-23: #fd8c73;
--ch-24: #afb8c133;
--ch-25: #eaeef280;
}
.ch-terminal {
font-size: 14px;
height: 100%;

6
apps/docs/types/code-hike.d.ts vendored Normal file
View File

@@ -0,0 +1,6 @@
import { remarkCodeHike } from '@code-hike/mdx'
declare module '@code-hike/mdx' {
export type CodeHikeRemarkPlugin = typeof remarkCodeHike
export type CodeHikeConfig = Parameters<CodeHikeRemarkPlugin>[0]
}

82
package-lock.json generated
View File

@@ -38,6 +38,7 @@
"dependencies": {
"@algolia/autocomplete-js": "^1.7.2",
"@algolia/autocomplete-plugin-recent-searches": "^1.7.2",
"@code-hike/mdx": "^0.8.3",
"@docsearch/react": "^3.3.0",
"@mdx-js/loader": "^1.6.22",
"@mdx-js/react": "^1.6.22",
@@ -3091,6 +3092,31 @@
"node": ">=0.1.95"
}
},
"node_modules/@code-hike/lighter": {
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/@code-hike/lighter/-/lighter-0.6.7.tgz",
"integrity": "sha512-NJJ/s5IdemXHf+D6RsCm+eCYPI5BKmJE1yL2+euKFhY5mLhYt3yljHF7ly7E0pu7R9QrY2y0spGLL+SFUOgz6g==",
"funding": {
"url": "https://github.com/code-hike/lighter?sponsor=1"
}
},
"node_modules/@code-hike/mdx": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/@code-hike/mdx/-/mdx-0.8.3.tgz",
"integrity": "sha512-pbbv7PivrU+GqPiM0ufehNyhsoge8V25fx+y89M2yKgEWMAFnNkk4E1XaW/X81QzIq3h58IoKEWnNYSJpERTvA==",
"dependencies": {
"@code-hike/lighter": "0.6.4",
"node-fetch": "^2.0.0",
"shiki": "^0.10.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/code-hike"
},
"peerDependencies": {
"react": "^16.8.3 || ^17 || ^18"
}
},
"node_modules/@codemirror/language": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.0.0.tgz",
@@ -40559,6 +40585,16 @@
"node": ">= 0.10"
}
},
"node_modules/shiki": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz",
"integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==",
"dependencies": {
"jsonc-parser": "^3.0.0",
"vscode-oniguruma": "^1.6.1",
"vscode-textmate": "5.2.0"
}
},
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
@@ -44293,6 +44329,16 @@
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz",
"integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
},
"node_modules/vscode-oniguruma": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz",
"integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA=="
},
"node_modules/vscode-textmate": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz",
"integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ=="
},
"node_modules/w3c-hr-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
@@ -48826,6 +48872,21 @@
"minimist": "^1.2.0"
}
},
"@code-hike/lighter": {
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/@code-hike/lighter/-/lighter-0.6.7.tgz",
"integrity": "sha512-NJJ/s5IdemXHf+D6RsCm+eCYPI5BKmJE1yL2+euKFhY5mLhYt3yljHF7ly7E0pu7R9QrY2y0spGLL+SFUOgz6g=="
},
"@code-hike/mdx": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/@code-hike/mdx/-/mdx-0.8.3.tgz",
"integrity": "sha512-pbbv7PivrU+GqPiM0ufehNyhsoge8V25fx+y89M2yKgEWMAFnNkk4E1XaW/X81QzIq3h58IoKEWnNYSJpERTvA==",
"requires": {
"@code-hike/lighter": "^0.6.7",
"node-fetch": "^2.0.0",
"shiki": "^0.10.1"
}
},
"@codemirror/language": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.0.0.tgz",
@@ -62954,6 +63015,7 @@
"requires": {
"@algolia/autocomplete-js": "^1.7.2",
"@algolia/autocomplete-plugin-recent-searches": "^1.7.2",
"@code-hike/mdx": "^0.8.3",
"@docsearch/react": "^3.3.0",
"@mdx-js/loader": "^1.6.22",
"@mdx-js/react": "^1.6.22",
@@ -77788,6 +77850,16 @@
}
}
},
"shiki": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/shiki/-/shiki-0.10.1.tgz",
"integrity": "sha512-VsY7QJVzU51j5o1+DguUd+6vmCmZ5v/6gYu4vyYAhzjuNQU6P/vmSy4uQaOhvje031qQMiW0d2BwgMH52vqMng==",
"requires": {
"jsonc-parser": "^3.0.0",
"vscode-oniguruma": "^1.6.1",
"vscode-textmate": "5.2.0"
}
},
"side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
@@ -81728,6 +81800,16 @@
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz",
"integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA=="
},
"vscode-oniguruma": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz",
"integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA=="
},
"vscode-textmate": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz",
"integrity": "sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ=="
},
"w3c-hr-time": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",

View File

@@ -33,6 +33,11 @@
"perf:meta": "ab -t 5 -c 20 -T application/json http://localhost:5555/tables",
"generate:types": "supabase gen types typescript --local > ./supabase/functions/common/database-types.ts"
},
"overrides": {
"@code-hike/mdx": {
"@code-hike/lighter": "^0.6.7"
}
},
"devDependencies": {
"@types/json-stringify-safe": "^5.0.0",
"aws-sdk": "^2.1354.0",