Refactor/app router refs (#28095)
Migrates client SDK References to App Router. (Management and CLI API references aren't migrated yet, nor are self-hosting config references.) Some notes: Big changes to the way crawler pages are built and individual section URLs (e.g., javascript/select) are served. All of these used to be SSG-generated pages, but the number of heavy pages was just too much to handle -- slow as molasses and my laptop sounded like it was taking off, and CI sometimes refuses to build it all at all. Tried various tricks with caching and pre-generating data but no dice. So I changed to only building one copy of each SDK+version page, then serving the sub-URLs through a response rewrite. That's for the actual user-visible pages. For the bot pages, each sub-URL needs to be its own page, but prebuilding it doesn't work, and rendering on demand from React components is too slow (looking for super-fast response here for SEO). Instead I changed to using an API route that serves very minimal, hand-crafted HTML. It looks ugly, but it's purely for the search bots. You can test what bots see by running curl --user-agent "Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" <URL_OF_PAGE> Also added some smoke tests to run against prod for the crawler routes, since we don't keep an eye on those regularly, and Vercel config changes could surprise-break them. Tested the meta images on Open Graph and all seems to work fine. With this approach, full production builds are really fast: ~5 minutes Starts using the new type spec handling, which is better at finding params automatically, so I could remove some of the manually written ones from the spec files.
This commit is contained in:
35
.github/workflows/docs-tests-smoke.yml
vendored
Normal file
35
.github/workflows/docs-tests-smoke.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
# Smoke tests against the docs production site, run daily
|
||||
|
||||
name: Docs Production Smoke Tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 4 * * *'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
sparse-checkout: |
|
||||
apps/docs
|
||||
packages
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: Install deps
|
||||
run: npm ci
|
||||
|
||||
- name: Run tests
|
||||
run: npm --prefix="apps/docs" run test:smoke
|
||||
72
apps/docs/app/api/crawlers/route.smoke.test.ts
Normal file
72
apps/docs/app/api/crawlers/route.smoke.test.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { load } from 'cheerio'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const REFERENCE_DOCS_URL = 'https://supabase.com/docs/reference'
|
||||
// For dev testing: comment out above and uncomment below
|
||||
// const REFERENCE_DOCS_URL = 'http://localhost:3001/docs/reference'
|
||||
|
||||
describe('prod smoke test: crawler pages return correct data', () => {
|
||||
/**
|
||||
* No special tricks required to spoof the user agent. Tests are correctly
|
||||
* detected as coming from bots. If they ever aren't, the `h1` test will fail
|
||||
* as a different `h1` is served to non-bots.
|
||||
*/
|
||||
|
||||
it('metadata: title, description, canonical, image', async () => {
|
||||
const result = await fetch(REFERENCE_DOCS_URL + '/javascript/rangelte')
|
||||
const text = await result.text()
|
||||
|
||||
const $ = load(text)
|
||||
const title = $('title').text()
|
||||
expect(title).toBe('JavaScript: Less than or equal to a range | Supabase Docs')
|
||||
|
||||
const metaDescription = $('meta[name="description"]')
|
||||
expect(metaDescription.attr('content')).toBe(
|
||||
'Supabase API reference for JavaScript: Less than or equal to a range'
|
||||
)
|
||||
|
||||
const canonical = $('link[rel="canonical"]')
|
||||
expect(canonical.attr('href')).toBe('https://supabase.com/docs/reference/javascript/rangelte')
|
||||
|
||||
const ogImage = $('meta[name="og:image"]')
|
||||
expect(ogImage.attr('content')).toBe('https://supabase.com/docs/img/supabase-og-image.png')
|
||||
|
||||
const twitterImage = $('meta[name="twitter:image"]')
|
||||
expect(twitterImage.attr('content')).toBe('https://supabase.com/docs/img/supabase-og-image.png')
|
||||
})
|
||||
|
||||
it('markdown pages', async () => {
|
||||
const result = await fetch(REFERENCE_DOCS_URL + '/javascript/introduction')
|
||||
const text = await result.text()
|
||||
|
||||
const $ = load(text)
|
||||
const h1 = $('h1').text()
|
||||
expect(h1).toBe('JavaScript: Introduction')
|
||||
|
||||
const firstPara = $('h1').next().text()
|
||||
expect(/JavaScript library/.test(firstPara)).toBe(true)
|
||||
expect(/supabase-js/.test(firstPara)).toBe(true)
|
||||
})
|
||||
|
||||
it('function pages', async () => {
|
||||
const result = await fetch(REFERENCE_DOCS_URL + '/javascript/rangelte')
|
||||
const text = await result.text()
|
||||
|
||||
const $ = load(text)
|
||||
const h1 = $('h1').text()
|
||||
expect(h1).toBe('JavaScript: Less than or equal to a range')
|
||||
|
||||
const description = $('h1').next().text()
|
||||
expect(description).toBe(
|
||||
'Only relevant for range columns. Match only rows where every element in column is either contained in range or less than any element in range.'
|
||||
)
|
||||
|
||||
const headings = [] as Array<string | undefined>
|
||||
$('h2').map(function () {
|
||||
headings.push($(this).attr('id'))
|
||||
})
|
||||
|
||||
expect(headings.includes('parameters')).toBe(true)
|
||||
expect(headings.includes('examples')).toBe(true)
|
||||
})
|
||||
})
|
||||
237
apps/docs/app/api/crawlers/route.ts
Normal file
237
apps/docs/app/api/crawlers/route.ts
Normal file
@@ -0,0 +1,237 @@
|
||||
import { toHtml } from 'hast-util-to-html'
|
||||
import { fromMarkdown } from 'mdast-util-from-markdown'
|
||||
import { mdxFromMarkdown } from 'mdast-util-mdx'
|
||||
import { toHast } from 'mdast-util-to-hast'
|
||||
import { mdxjs } from 'micromark-extension-mdxjs'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { visit } from 'unist-util-visit'
|
||||
|
||||
import { REFERENCES } from '~/content/navigation.references'
|
||||
import {
|
||||
getFlattenedSections,
|
||||
getFunctionsList,
|
||||
getTypeSpec,
|
||||
} from '~/features/docs/Reference.generated.singleton'
|
||||
import { getRefMarkdown } from '~/features/docs/Reference.mdx'
|
||||
import type { MethodTypes } from '~/features/docs/Reference.typeSpec'
|
||||
import type { AbbrevCommonClientLibSection } from '~/features/docs/Reference.utils'
|
||||
import { notFoundLink } from '~/features/recommendations/NotFound.utils'
|
||||
import { BASE_PATH } from '~/lib/constants'
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const url = new URL(request.url)
|
||||
let [, , lib, maybeVersion, slug] = url.pathname.split('/')
|
||||
|
||||
const libraryMeta = REFERENCES[lib]
|
||||
|
||||
const isVersion = /^v\d+$/.test(maybeVersion)
|
||||
const version = isVersion ? maybeVersion : libraryMeta.versions[0]
|
||||
if (!isVersion) {
|
||||
slug = maybeVersion
|
||||
}
|
||||
|
||||
const flattenedSections = await getFlattenedSections(lib, version)
|
||||
const sectionsWithUrl: Array<AbbrevCommonClientLibSection & { url: URL }> =
|
||||
flattenedSections!.map((section) => {
|
||||
const url = new URL(request.url)
|
||||
url.pathname = [BASE_PATH, 'reference', lib, isVersion ? version : null, section.slug]
|
||||
.filter(Boolean)
|
||||
.join('/')
|
||||
|
||||
return {
|
||||
...section,
|
||||
url,
|
||||
}
|
||||
})
|
||||
const section = flattenedSections!.find(
|
||||
(section) =>
|
||||
(section.type === 'markdown' || section.type === 'function') && section.slug === slug
|
||||
)
|
||||
|
||||
if (!section) {
|
||||
redirect(notFoundLink(`${lib}/${slug}`))
|
||||
}
|
||||
|
||||
const html = htmlShell(
|
||||
lib,
|
||||
isVersion ? version : null,
|
||||
slug,
|
||||
section,
|
||||
libraryNav(sectionsWithUrl) + (await sectionDetails(lib, isVersion ? version : null, section))
|
||||
)
|
||||
const response = new Response(html)
|
||||
response.headers.set('Content-Type', 'text/html; charset=utf-8')
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
function htmlShell(
|
||||
lib: string,
|
||||
version: string | null,
|
||||
slug: string,
|
||||
section: AbbrevCommonClientLibSection,
|
||||
body: string
|
||||
) {
|
||||
const libraryName = REFERENCES[lib].name
|
||||
let title = libraryName + ': ' + section.title ?? ''
|
||||
|
||||
return (
|
||||
'<!doctype html><html>' +
|
||||
'<head>' +
|
||||
`<title>${title} | Supabase Docs</title>` +
|
||||
`<meta name="description" content="Supabase API reference for ${libraryName}${section.title ? ': ' + section.title : ''}">` +
|
||||
`<meta name="og:image" content="https://supabase.com/docs/img/supabase-og-image.png">` +
|
||||
`<meta name="twitter:image" content="https://supabase.com/docs/img/supabase-og-image.png">` +
|
||||
`<link rel="canonical" href="https://supabase.com/docs/reference/${lib}` +
|
||||
(version ? '/' + version : '') +
|
||||
(slug ? '/' + slug : '') +
|
||||
`">` +
|
||||
'</head>' +
|
||||
'<body>' +
|
||||
body +
|
||||
'</body></html>'
|
||||
)
|
||||
}
|
||||
|
||||
function libraryNav(sections: Array<AbbrevCommonClientLibSection & { url: URL }>) {
|
||||
return (
|
||||
'<nav><ul>' +
|
||||
sections
|
||||
.map((section) => `<li><a href="${section.url}">${section.title ?? ''}</a></li>`)
|
||||
.join('') +
|
||||
'</ul></nav>'
|
||||
)
|
||||
}
|
||||
|
||||
async function sectionDetails(lib: string, version: string, section: AbbrevCommonClientLibSection) {
|
||||
const libraryName = REFERENCES[lib].name
|
||||
let result = '<h1>' + (libraryName + ': ' + section.title ?? '') + '</h1>'
|
||||
|
||||
if (section.type === 'markdown') {
|
||||
result += await markdown(lib, version, section)
|
||||
} else {
|
||||
result += await functionDetails(lib, version, section)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async function markdown(
|
||||
lib: string,
|
||||
version: string | null,
|
||||
section: AbbrevCommonClientLibSection
|
||||
) {
|
||||
const dir = !!section.meta?.shared ? 'shared' : lib + (version ? '/' + version : '')
|
||||
|
||||
let content = await getRefMarkdown(dir + '/' + section.slug)
|
||||
content = mdxToHtml(content)
|
||||
return content
|
||||
}
|
||||
|
||||
async function functionDetails(
|
||||
lib: string,
|
||||
version: string | null,
|
||||
section: AbbrevCommonClientLibSection
|
||||
) {
|
||||
const libraryMeta = REFERENCES[lib]
|
||||
|
||||
const fns = await getFunctionsList(lib, version ?? libraryMeta.versions[0])
|
||||
const fn = fns!.find((fn) => fn.id === section.id)
|
||||
if (!fn) return ''
|
||||
|
||||
let types: MethodTypes | undefined
|
||||
if (libraryMeta.typeSpec && '$ref' in fn) {
|
||||
types = await getTypeSpec(fn['$ref'] as string)
|
||||
}
|
||||
|
||||
const fullDescription = [
|
||||
types?.comment?.shortText,
|
||||
'description' in fn && (fn.description as string),
|
||||
'notes' in fn && (fn.notes as string),
|
||||
]
|
||||
.filter((x) => typeof x === 'string')
|
||||
.map(mdxToHtml)
|
||||
.join('')
|
||||
|
||||
const parameters = parametersToHtml(fn, types)
|
||||
const examples = examplesToHtml(fn)
|
||||
|
||||
return fullDescription + parameters + examples
|
||||
}
|
||||
|
||||
function mdxToHtml(markdownUnescaped: string): string {
|
||||
const markdown = markdownUnescaped.replace(/(?<!\\)\{/g, '\\{').replace(/(?<!\\)\}/g, '\\}')
|
||||
|
||||
const mdast = fromMarkdown(markdown, {
|
||||
extensions: [mdxjs()],
|
||||
mdastExtensions: [mdxFromMarkdown()],
|
||||
})
|
||||
|
||||
visit(mdast, 'text', (node) => {
|
||||
node.value = node.value.replace(/\n/g, ' ')
|
||||
})
|
||||
if (!mdast) return ''
|
||||
|
||||
const hast = toHast(mdast)
|
||||
if (!hast) return ''
|
||||
|
||||
// @ts-ignore
|
||||
const html = toHtml(hast)
|
||||
|
||||
return html
|
||||
}
|
||||
|
||||
function parametersToHtml(fn: any, types: MethodTypes | undefined) {
|
||||
let result = '<h2 id="parameters">Parameters</h2>'
|
||||
|
||||
if ('overwriteParams' in fn || 'params' in fn) {
|
||||
const params = fn.overwriteParams ?? fn.params
|
||||
if (params.length === 0) return ''
|
||||
|
||||
result +=
|
||||
'<ul>' +
|
||||
params
|
||||
.map(
|
||||
(param) =>
|
||||
'<li>' +
|
||||
`<h3>${param.name}</h3>` +
|
||||
`<span>${param.isOptional ? '(Optional)' : '(Required)'}</span>` +
|
||||
`<p>${param.description}</p>` +
|
||||
'</li>'
|
||||
)
|
||||
.join('') +
|
||||
'</ul>'
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
if (!types?.params || types.params.length === 0) return ''
|
||||
|
||||
result +=
|
||||
'<ul>' +
|
||||
types.params
|
||||
.map(
|
||||
(param) =>
|
||||
'<li>' +
|
||||
`<h3>${String(param.name)}</h3>` +
|
||||
`<span>${param.isOptional ? '(Optional)' : '(Required)'}</span>` +
|
||||
`<p>${param.comment?.shortText ?? ''}</p>` +
|
||||
'</li>'
|
||||
)
|
||||
.join('') +
|
||||
'</ul>'
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
function examplesToHtml(fn: any) {
|
||||
if (!fn.examples || fn.examples.length === 0) return ''
|
||||
|
||||
let result = '<h2 id="examples">Examples</h2>'
|
||||
|
||||
result += fn.examples
|
||||
.map((example) => `<h3>${example.name ?? ''}</h3>` + mdxToHtml(example.code ?? ''))
|
||||
.join('')
|
||||
|
||||
return result
|
||||
}
|
||||
42
apps/docs/app/reference/[...slug]/page.tsx
Normal file
42
apps/docs/app/reference/[...slug]/page.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { redirect } from 'next/navigation'
|
||||
|
||||
import { REFERENCES } from '~/content/navigation.references'
|
||||
import { ClientSdkReferencePage } from '~/features/docs/Reference.sdkPage'
|
||||
import {
|
||||
generateReferenceMetadata,
|
||||
generateReferenceStaticParams,
|
||||
parseReferencePath,
|
||||
redirectNonexistentReferenceSection,
|
||||
} from '~/features/docs/Reference.utils'
|
||||
import { notFoundLink } from '~/features/recommendations/NotFound.utils'
|
||||
|
||||
export default async function ReferencePage({
|
||||
params: { slug },
|
||||
}: {
|
||||
params: { slug: Array<string> }
|
||||
}) {
|
||||
if (!Object.keys(REFERENCES).includes(slug[0])) {
|
||||
redirect(notFoundLink(slug.join('/')))
|
||||
}
|
||||
|
||||
const parsedPath = parseReferencePath(slug)
|
||||
const isClientSdkReference = parsedPath.__type === 'clientSdk'
|
||||
|
||||
if (isClientSdkReference) {
|
||||
const { sdkId, maybeVersion, path } = parsedPath
|
||||
|
||||
const sdkData = REFERENCES[sdkId]
|
||||
const latestVersion = sdkData.versions[0]
|
||||
const version = maybeVersion ?? latestVersion
|
||||
|
||||
await redirectNonexistentReferenceSection(sdkId, version, path, version === latestVersion)
|
||||
|
||||
return <ClientSdkReferencePage sdkId={sdkId} libVersion={version} />
|
||||
} else {
|
||||
// Unimplemented -- eventually API and CLI
|
||||
redirect(notFoundLink(slug.join('/')))
|
||||
}
|
||||
}
|
||||
|
||||
export const generateStaticParams = generateReferenceStaticParams
|
||||
export const generateMetadata = generateReferenceMetadata
|
||||
62
apps/docs/app/reference/page.tsx
Normal file
62
apps/docs/app/reference/page.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
import { REFERENCES, clientSdkIds } from '~/content/navigation.references'
|
||||
import { IconPanelWithIconPicker } from '~/features/ui/IconPanelWithIconPicker'
|
||||
import { LayoutMainContent } from '~/layouts/DefaultLayout'
|
||||
import { SidebarSkeleton } from '~/layouts/MainSkeleton'
|
||||
|
||||
export default function ReferenceIndexPage() {
|
||||
return (
|
||||
<SidebarSkeleton>
|
||||
<LayoutMainContent>
|
||||
<article className="prose">
|
||||
<h1>API References</h1>
|
||||
<p>
|
||||
The Supabase client libraries help you interact with Supabase products, such as the
|
||||
Postgres Database, Auth, and Realtime. They are available in several popular programming
|
||||
languages.
|
||||
</p>
|
||||
<p>
|
||||
Supabase also has a Management API to help with managing your Supabase Platform, and a
|
||||
CLI for local development and CI workflows.
|
||||
</p>
|
||||
<h2 className="mb-8">Client Libraries</h2>
|
||||
<div className="grid col-span-8 grid-cols-12 gap-6 not-prose">
|
||||
{clientSdkIds.map((sdkId) => {
|
||||
return (
|
||||
<Link
|
||||
key={REFERENCES[sdkId].name}
|
||||
href={`/reference/${REFERENCES[sdkId].libPath}`}
|
||||
passHref
|
||||
className="col-span-6 md:col-span-4"
|
||||
>
|
||||
<IconPanelWithIconPicker
|
||||
title={REFERENCES[sdkId].name}
|
||||
icon={REFERENCES[sdkId].icon}
|
||||
/>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<h2 className="mb-8">Management API and CLI</h2>
|
||||
<div className="grid col-span-8 grid-cols-12 gap-6 not-prose">
|
||||
<Link
|
||||
href={`/reference/api/introduction`}
|
||||
passHref
|
||||
className="col-span-6 md:col-span-4"
|
||||
>
|
||||
<IconPanelWithIconPicker title="Management API" icon={REFERENCES['api'].icon} />
|
||||
</Link>
|
||||
<Link
|
||||
href={`/reference/cli/introduction`}
|
||||
passHref
|
||||
className="col-span-6 md:col-span-4"
|
||||
>
|
||||
<IconPanelWithIconPicker title="CLI" icon={REFERENCES['cli'].icon} />
|
||||
</Link>
|
||||
</div>
|
||||
</article>
|
||||
</LayoutMainContent>
|
||||
</SidebarSkeleton>
|
||||
)
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import React, { Fragment } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import React, { Fragment } from 'react'
|
||||
|
||||
import { useBreakpoint } from 'common'
|
||||
import {
|
||||
Breadcrumb_Shadcn_ as Breadcrumb,
|
||||
BreadcrumbList_Shadcn_ as BreadcrumbList,
|
||||
@@ -23,9 +25,9 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from 'ui'
|
||||
|
||||
import * as NavItems from '~/components/Navigation/NavigationMenu/NavigationMenu.constants'
|
||||
import { getMenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu.utils'
|
||||
import { useBreakpoint } from 'common'
|
||||
import * as NavItems from './Navigation/NavigationMenu/NavigationMenu.constants'
|
||||
|
||||
const Breadcrumbs = ({ className }: { className?: string }) => {
|
||||
const pathname = usePathname()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import AuthErrorCodesTable from './auth_error_codes_table.mdx'
|
||||
import AuthRateLimits from './auth_rate_limits.mdx'
|
||||
import CreateClientSnippet from './create_client_snippet.mdx'
|
||||
import DatabaseSetup from './database_setup.mdx'
|
||||
@@ -15,6 +16,7 @@ import SocialProviderSettingsSupabase from './social_provider_settings_supabase.
|
||||
import SocialProviderSetup from './social_provider_setup.mdx'
|
||||
|
||||
export {
|
||||
AuthErrorCodesTable,
|
||||
AuthRateLimits,
|
||||
CreateClientSnippet,
|
||||
DatabaseSetup,
|
||||
|
||||
@@ -13,16 +13,6 @@ export interface NavMenuSection {
|
||||
items: Partial<NavMenuSection>[]
|
||||
}
|
||||
|
||||
export interface References {
|
||||
[key: string]: {
|
||||
name: string
|
||||
library?: string
|
||||
versions: string[]
|
||||
icon: string
|
||||
currentVersion?: string
|
||||
}
|
||||
}
|
||||
|
||||
type MenuItem = {
|
||||
label: string
|
||||
icon?: string
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import {
|
||||
IconBranching,
|
||||
IconGitHub,
|
||||
IconMenuApi,
|
||||
IconMenuAuth,
|
||||
IconMenuCli,
|
||||
@@ -26,10 +25,8 @@ import {
|
||||
IconMenuKotlin,
|
||||
IconMenuAI,
|
||||
IconMenuDevCli,
|
||||
IconGitHub,
|
||||
IconSupport,
|
||||
IconTroubleshooting,
|
||||
IconBranching,
|
||||
} from './MenuIcons'
|
||||
|
||||
function getMenuIcon(menuKey: string, width: number = 16, height: number = 16, className?: string) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { IS_DEV } from '~/lib/constants'
|
||||
import type { GlobalMenuItems, NavMenuConstant, References } from '../Navigation.types'
|
||||
import type { GlobalMenuItems, NavMenuConstant } from '../Navigation.types'
|
||||
|
||||
export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [
|
||||
[
|
||||
@@ -100,38 +99,38 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [
|
||||
{
|
||||
label: 'JavaScript',
|
||||
icon: 'reference-javascript',
|
||||
href: '/reference/javascript/introduction',
|
||||
href: '/reference/javascript',
|
||||
level: 'reference_javascript',
|
||||
},
|
||||
{
|
||||
label: 'Flutter',
|
||||
icon: 'reference-dart',
|
||||
href: '/reference/dart/introduction',
|
||||
href: '/reference/dart',
|
||||
level: 'reference_dart',
|
||||
},
|
||||
{
|
||||
label: 'Swift',
|
||||
icon: 'reference-swift',
|
||||
href: '/reference/swift/introduction',
|
||||
href: '/reference/swift',
|
||||
level: 'reference_swift',
|
||||
},
|
||||
{
|
||||
label: 'Python',
|
||||
icon: 'reference-python',
|
||||
href: '/reference/python/introduction',
|
||||
href: '/reference/python',
|
||||
level: 'reference_python',
|
||||
},
|
||||
{
|
||||
label: 'C#',
|
||||
icon: 'reference-csharp',
|
||||
href: '/reference/csharp/introduction',
|
||||
href: '/reference/csharp',
|
||||
level: 'reference_csharp',
|
||||
community: true,
|
||||
},
|
||||
{
|
||||
label: 'Kotlin',
|
||||
icon: 'reference-kotlin',
|
||||
href: '/reference/kotlin/introduction',
|
||||
href: '/reference/kotlin',
|
||||
level: 'reference_kotlin',
|
||||
community: true,
|
||||
},
|
||||
@@ -202,51 +201,6 @@ export const GLOBAL_MENU_ITEMS: GlobalMenuItems = [
|
||||
],
|
||||
]
|
||||
|
||||
export const REFERENCES: References = {
|
||||
javascript: {
|
||||
name: 'supabase-js',
|
||||
library: 'supabase-js',
|
||||
versions: ['v2', 'v1'],
|
||||
icon: '/img/libraries/javascript-icon',
|
||||
},
|
||||
dart: {
|
||||
name: 'Flutter',
|
||||
library: 'supabase-dart',
|
||||
versions: ['v2', 'v1'],
|
||||
icon: '/docs/img/libraries/flutter-icon.svg',
|
||||
},
|
||||
csharp: {
|
||||
name: 'C#',
|
||||
library: 'supabase-csharp',
|
||||
versions: ['v1', 'v0'],
|
||||
icon: '/docs/img/libraries/c-sharp-icon.svg',
|
||||
},
|
||||
swift: {
|
||||
name: 'Swift',
|
||||
library: 'supabase-swift',
|
||||
versions: ['v2', 'v1'],
|
||||
icon: '/docs/img/libraries/swift-icon.svg',
|
||||
},
|
||||
kotlin: {
|
||||
name: 'Kotlin',
|
||||
library: 'supabase-kt',
|
||||
versions: ['v2', 'v1'],
|
||||
icon: '/docs/img/libraries/kotlin-icon.svg',
|
||||
},
|
||||
cli: {
|
||||
name: 'CLI',
|
||||
library: undefined,
|
||||
versions: [],
|
||||
icon: '/docs/img/icons/cli-icon.svg',
|
||||
},
|
||||
api: {
|
||||
name: 'API',
|
||||
library: undefined,
|
||||
versions: [],
|
||||
icon: '/docs/img/icons/api-icon.svg',
|
||||
},
|
||||
}
|
||||
|
||||
export const gettingstarted: NavMenuConstant = {
|
||||
icon: 'getting-started',
|
||||
title: 'Start with Supabase',
|
||||
@@ -2123,13 +2077,6 @@ export const reference = {
|
||||
items: [],
|
||||
icon: '/img/icons/menu/reference-kotlin',
|
||||
},
|
||||
// {
|
||||
// name: 'supabase-python',
|
||||
// url: '/reference/python/start',
|
||||
// level: 'reference_python',
|
||||
//
|
||||
// icon: '/img/icons/menu/reference-javascript',
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -2155,6 +2102,10 @@ export const reference_javascript_v1 = {
|
||||
title: 'JavaScript',
|
||||
url: '/guides/reference/javascript',
|
||||
parent: '/reference',
|
||||
pkg: {
|
||||
name: '@supabase/supabase-js',
|
||||
repo: 'https://github.com/supabase/supabase-js',
|
||||
},
|
||||
}
|
||||
|
||||
export const reference_javascript_v2 = {
|
||||
@@ -2162,6 +2113,10 @@ export const reference_javascript_v2 = {
|
||||
title: 'JavaScript',
|
||||
url: '/guides/reference/javascript',
|
||||
parent: '/reference',
|
||||
pkg: {
|
||||
name: '@supabase/supabase-js',
|
||||
repo: 'https://github.com/supabase/supabase-js',
|
||||
},
|
||||
}
|
||||
|
||||
export const reference_dart_v1 = {
|
||||
@@ -2169,6 +2124,10 @@ export const reference_dart_v1 = {
|
||||
title: 'Flutter',
|
||||
url: '/guides/reference/dart',
|
||||
parent: '/reference',
|
||||
pkg: {
|
||||
name: 'supabase_flutter',
|
||||
repo: 'https://github.com/supabase/supabase-flutter',
|
||||
},
|
||||
}
|
||||
|
||||
export const reference_dart_v2 = {
|
||||
@@ -2176,6 +2135,10 @@ export const reference_dart_v2 = {
|
||||
title: 'Flutter',
|
||||
url: '/guides/reference/dart',
|
||||
parent: '/reference',
|
||||
pkg: {
|
||||
name: 'supabase_flutter',
|
||||
repo: 'https://github.com/supabase/supabase-flutter',
|
||||
},
|
||||
}
|
||||
|
||||
export const reference_csharp_v0 = {
|
||||
@@ -2183,6 +2146,10 @@ export const reference_csharp_v0 = {
|
||||
title: 'C#',
|
||||
url: 'guides/reference/csharp',
|
||||
parent: '/reference',
|
||||
pkg: {
|
||||
name: 'supabase',
|
||||
repo: 'https://github.com/supabase-community/supabase-csharp',
|
||||
},
|
||||
}
|
||||
|
||||
export const reference_csharp_v1 = {
|
||||
@@ -2190,6 +2157,10 @@ export const reference_csharp_v1 = {
|
||||
title: 'C#',
|
||||
url: 'guides/reference/csharp',
|
||||
parent: '/reference',
|
||||
pkg: {
|
||||
name: 'supabase',
|
||||
repo: 'https://github.com/supabase-community/supabase-csharp',
|
||||
},
|
||||
}
|
||||
|
||||
export const reference_python_v2 = {
|
||||
@@ -2197,6 +2168,10 @@ export const reference_python_v2 = {
|
||||
title: 'Python',
|
||||
url: '/guides/reference/python',
|
||||
parent: '/reference',
|
||||
pkg: {
|
||||
name: 'supabase-py',
|
||||
repo: 'https://github.com/supabase/supabase-py',
|
||||
},
|
||||
}
|
||||
|
||||
export const reference_swift_v1 = {
|
||||
@@ -2204,6 +2179,10 @@ export const reference_swift_v1 = {
|
||||
title: 'swift',
|
||||
url: 'guides/reference/swift',
|
||||
parent: '/reference',
|
||||
pkg: {
|
||||
name: 'supabase-swift',
|
||||
repo: 'https://github.com/supabase/supabase-swift',
|
||||
},
|
||||
}
|
||||
|
||||
export const reference_swift_v2 = {
|
||||
@@ -2211,6 +2190,10 @@ export const reference_swift_v2 = {
|
||||
title: 'swift',
|
||||
url: 'guides/reference/swift',
|
||||
parent: '/reference',
|
||||
pkg: {
|
||||
name: 'supabase-swift',
|
||||
repo: 'https://github.com/supabase/supabase-swift',
|
||||
},
|
||||
}
|
||||
|
||||
export const reference_kotlin_v1 = {
|
||||
@@ -2218,6 +2201,10 @@ export const reference_kotlin_v1 = {
|
||||
title: 'kotlin',
|
||||
url: 'guides/reference/kotlin',
|
||||
parent: '/reference',
|
||||
pkg: {
|
||||
name: '@supabase-community/supabase-kt',
|
||||
repo: 'https://github.com/supabase-community/supabase-kt',
|
||||
},
|
||||
}
|
||||
|
||||
export const reference_kotlin_v2 = {
|
||||
@@ -2225,6 +2212,10 @@ export const reference_kotlin_v2 = {
|
||||
title: 'kotlin',
|
||||
url: 'guides/reference/kotlin',
|
||||
parent: '/reference',
|
||||
pkg: {
|
||||
name: '@supabase-community/supabase-kt',
|
||||
repo: 'https://github.com/supabase-community/supabase-kt',
|
||||
},
|
||||
}
|
||||
|
||||
export const reference_cli = {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { memo } from 'react'
|
||||
|
||||
import NavigationMenuGuideList from './NavigationMenuGuideList'
|
||||
import NavigationMenuRefList from './NavigationMenuRefList'
|
||||
import { useCloseMenuOnRouteChange } from './NavigationMenu.utils'
|
||||
@@ -50,8 +51,7 @@ interface GuideMenu extends BaseMenu {
|
||||
interface ReferenceMenu extends BaseMenu {
|
||||
type: 'reference'
|
||||
path: string
|
||||
commonSectionsFile: string
|
||||
specFile?: string
|
||||
commonSectionsFile?: string
|
||||
}
|
||||
|
||||
type Menu = GuideMenu | ReferenceMenu
|
||||
@@ -115,133 +115,109 @@ const menus: Menu[] = [
|
||||
},
|
||||
{
|
||||
id: MenuId.RefJavaScriptV1,
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_js_v1.yml',
|
||||
type: 'reference',
|
||||
path: '/reference/javascript/v1',
|
||||
},
|
||||
{
|
||||
id: MenuId.RefJavaScriptV2,
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_js_v2.yml',
|
||||
type: 'reference',
|
||||
path: '/reference/javascript',
|
||||
},
|
||||
{
|
||||
id: MenuId.RefDartV1,
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_dart_v1.yml',
|
||||
type: 'reference',
|
||||
path: '/reference/dart/v1',
|
||||
},
|
||||
{
|
||||
id: MenuId.RefDartV2,
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_dart_v2.yml',
|
||||
type: 'reference',
|
||||
path: '/reference/dart',
|
||||
},
|
||||
{
|
||||
id: MenuId.RefCSharpV0,
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_csharp_v0.yml',
|
||||
type: 'reference',
|
||||
path: '/reference/csharp/v0',
|
||||
},
|
||||
{
|
||||
id: MenuId.RefCSharpV1,
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_csharp_v1.yml',
|
||||
type: 'reference',
|
||||
path: '/reference/csharp',
|
||||
},
|
||||
{
|
||||
id: MenuId.RefPythonV2,
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_py_v2.yml',
|
||||
type: 'reference',
|
||||
path: '/reference/python',
|
||||
},
|
||||
{
|
||||
id: MenuId.RefSwiftV1,
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_swift_v1.yml',
|
||||
type: 'reference',
|
||||
path: '/reference/swift',
|
||||
},
|
||||
{
|
||||
id: MenuId.RefSwiftV2,
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_swift_v2.yml',
|
||||
type: 'reference',
|
||||
path: '/reference/swift',
|
||||
},
|
||||
{
|
||||
id: MenuId.RefKotlinV1,
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_kt_v1.yml',
|
||||
type: 'reference',
|
||||
path: '/reference/kotlin/v1',
|
||||
},
|
||||
{
|
||||
id: MenuId.RefKotlinV2,
|
||||
commonSectionsFile: 'common-client-libs-sections.json',
|
||||
specFile: 'supabase_kt_v2.yml',
|
||||
type: 'reference',
|
||||
path: '/reference/kotlin',
|
||||
},
|
||||
{
|
||||
id: MenuId.RefCli,
|
||||
commonSectionsFile: 'common-cli-sections.json',
|
||||
type: 'reference',
|
||||
path: '/reference/cli',
|
||||
commonSectionsFile: 'common-cli-sections.json',
|
||||
},
|
||||
{
|
||||
id: MenuId.RefApi,
|
||||
commonSectionsFile: 'common-api-sections.json',
|
||||
type: 'reference',
|
||||
path: '/reference/api',
|
||||
commonSectionsFile: 'common-api-sections.json',
|
||||
},
|
||||
{
|
||||
id: MenuId.SelfHostingAuth,
|
||||
commonSectionsFile: 'common-self-hosting-auth-sections.json',
|
||||
type: 'reference',
|
||||
path: '/reference/self-hosting-auth',
|
||||
commonSectionsFile: 'common-self-hosting-auth-sections.json',
|
||||
},
|
||||
{
|
||||
id: MenuId.SelfHostingStorage,
|
||||
commonSectionsFile: 'common-self-hosting-storage-sections.json',
|
||||
type: 'reference',
|
||||
path: '/reference/self-hosting-storage',
|
||||
commonSectionsFile: 'common-self-hosting-storage-sections.json',
|
||||
},
|
||||
{
|
||||
id: MenuId.SelfHostingRealtime,
|
||||
commonSectionsFile: 'common-self-hosting-realtime-sections.json',
|
||||
type: 'reference',
|
||||
path: '/reference/self-hosting-realtime',
|
||||
commonSectionsFile: 'common-self-hosting-realtime-sections.json',
|
||||
},
|
||||
{
|
||||
id: MenuId.SelfHostingAnalytics,
|
||||
commonSectionsFile: 'common-self-hosting-analytics-sections.json',
|
||||
type: 'reference',
|
||||
path: '/reference/self-hosting-analytics',
|
||||
commonSectionsFile: 'common-self-hosting-analytics-sections.json',
|
||||
},
|
||||
{
|
||||
id: MenuId.SelfHostingFunctions,
|
||||
commonSectionsFile: 'common-self-hosting-functions-sections.json',
|
||||
type: 'reference',
|
||||
path: '/reference/self-hosting-functions',
|
||||
commonSectionsFile: 'common-self-hosting-functions-sections.json',
|
||||
},
|
||||
]
|
||||
|
||||
export function getMenuById(id: MenuId) {
|
||||
function getMenuById(id: MenuId) {
|
||||
return menus.find((menu) => menu.id === id)
|
||||
}
|
||||
|
||||
function getMenuElement(menu: Menu | undefined) {
|
||||
if (!menu) throw Error('No menu found for this menuId')
|
||||
|
||||
const menuType = menu.type
|
||||
const menuType = menu?.type
|
||||
switch (menuType) {
|
||||
case 'guide':
|
||||
return <NavigationMenuGuideList id={menu.id} />
|
||||
@@ -251,7 +227,6 @@ function getMenuElement(menu: Menu | undefined) {
|
||||
id={menu.id}
|
||||
basePath={menu.path}
|
||||
commonSectionsFile={menu.commonSectionsFile}
|
||||
specFile={menu.specFile}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
@@ -268,5 +243,5 @@ const NavigationMenu = ({ menuId }: { menuId: MenuId }) => {
|
||||
return getMenuElement(menu)
|
||||
}
|
||||
|
||||
export { MenuId }
|
||||
export { MenuId, getMenuById }
|
||||
export default memo(NavigationMenu)
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useEffect, useState } from 'react'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
import type { ICommonItem } from '~/components/reference/Reference.types'
|
||||
import type { Json } from '~/types'
|
||||
import type { Json } from '~/features/helpers.types'
|
||||
import { menuState } from '../../../hooks/useMenuState'
|
||||
|
||||
export function getPathWithoutHash(relativePath: string) {
|
||||
@@ -99,14 +99,6 @@ export function useSpec(specFile?: string) {
|
||||
return spec
|
||||
}
|
||||
|
||||
export const useCloseMenuOnRouteChange = () => {
|
||||
const pathname = usePathname()
|
||||
|
||||
useEffect(() => {
|
||||
menuState.setMenuMobileOpen(false)
|
||||
}, [pathname])
|
||||
}
|
||||
|
||||
export const getMenuId = (pathname: string | null) => {
|
||||
pathname = (pathname ??= '').replace(/^\/guides\//, '')
|
||||
|
||||
@@ -141,3 +133,11 @@ export const getMenuId = (pathname: string | null) => {
|
||||
return MenuId.GettingStarted
|
||||
}
|
||||
}
|
||||
|
||||
export const useCloseMenuOnRouteChange = () => {
|
||||
const pathname = usePathname()
|
||||
|
||||
useEffect(() => {
|
||||
menuState.setMenuMobileOpen(false)
|
||||
}, [pathname])
|
||||
}
|
||||
|
||||
@@ -28,12 +28,6 @@ const NavigationMenuRefList = ({
|
||||
}
|
||||
|
||||
const filteredSections = commonSections.filter((section) => {
|
||||
if (section.type === 'category') {
|
||||
section.items = section.items.filter((item) => {
|
||||
return !item.excludes?.includes(id)
|
||||
})
|
||||
}
|
||||
|
||||
return !section.excludes?.includes(id)
|
||||
})
|
||||
|
||||
|
||||
@@ -185,7 +185,6 @@ const NavigationMenuRefListItems = ({
|
||||
<div className="flex items-center gap-3 my-3">
|
||||
<MenuIconPicker icon={menu.icon} width={21} height={21} />
|
||||
<HeaderLink title={menu.title} url={menu.url} id={id} />
|
||||
<RevVersionDropdown />
|
||||
</div>
|
||||
<ul className="function-link-list flex flex-col gap-2 pb-5">
|
||||
{filteredSections.map((section) => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import Link from 'next/link'
|
||||
import Image from 'next/legacy/image'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { IconChevronRight, IconArrowLeft } from '~/../../packages/ui'
|
||||
import { REFERENCES } from './NavigationMenu/NavigationMenu.constants'
|
||||
import { REFERENCES } from '~/content/navigation.references'
|
||||
|
||||
import { NavMenuGroup, NavMenuSection } from './Navigation.types'
|
||||
import * as Accordion from '@radix-ui/react-accordion'
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { usePathname, useRouter } from 'next/navigation'
|
||||
'use client'
|
||||
|
||||
import { ChevronDown } from 'lucide-react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
import {
|
||||
Badge,
|
||||
DropdownMenu,
|
||||
@@ -6,34 +10,32 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuTrigger,
|
||||
IconChevronDown,
|
||||
} from 'ui'
|
||||
import { REFERENCES } from './Navigation/NavigationMenu/NavigationMenu.constants'
|
||||
|
||||
const RevVersionDropdown = () => {
|
||||
const pathname = usePathname()
|
||||
import { REFERENCES } from '~/content/navigation.references'
|
||||
|
||||
const RevVersionDropdown = ({
|
||||
library,
|
||||
currentVersion,
|
||||
}: {
|
||||
library: string
|
||||
currentVersion: string
|
||||
}) => {
|
||||
const { push } = useRouter()
|
||||
const pathSegments = pathname.split('/')
|
||||
|
||||
const library = pathSegments.length >= 3 ? pathSegments[2] : undefined
|
||||
const libraryMeta = REFERENCES?.[library] ?? undefined
|
||||
const versions = libraryMeta?.versions ?? []
|
||||
|
||||
const currentVersion = versions.includes(pathSegments[pathSegments.indexOf(library) + 1])
|
||||
? pathSegments[pathSegments.indexOf(library) + 1]
|
||||
: versions[0]
|
||||
|
||||
const onSelectVersion = (version: string) => {
|
||||
if (!library) return
|
||||
if (version === versions[0]) {
|
||||
push(`/reference/${library}/start`)
|
||||
} else {
|
||||
push(`/reference/${library}/${version}/start`)
|
||||
}
|
||||
if (!versions || versions.length <= 1) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!versions || versions.length === 0) {
|
||||
return <></>
|
||||
const onSelectVersion = (version: string) => {
|
||||
if (version === versions[0]) {
|
||||
push(`/reference/${library}`)
|
||||
} else {
|
||||
push(`/reference/${library}/${version}`)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -52,11 +54,10 @@ const RevVersionDropdown = () => {
|
||||
flex items-center gap-1 text-foreground-muted text-xs group-hover:text-foreground transition
|
||||
"
|
||||
>
|
||||
{/* <span>version</span> */}
|
||||
<span className="text-foreground text-sm group-hover:text-foreground transition">
|
||||
{currentVersion}.0
|
||||
</span>
|
||||
<IconChevronDown size={14} strokeWidth={2} />
|
||||
<ChevronDown size={14} strokeWidth={2} />
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" side="bottom" className="w-48">
|
||||
|
||||
123
apps/docs/content/navigation.references.ts
Normal file
123
apps/docs/content/navigation.references.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
export const REFERENCES = {
|
||||
javascript: {
|
||||
type: 'sdk',
|
||||
name: 'JavaScript',
|
||||
library: 'supabase-js',
|
||||
libPath: 'javascript',
|
||||
versions: ['v2', 'v1'],
|
||||
typeSpec: true,
|
||||
icon: 'reference-javascript',
|
||||
meta: {
|
||||
v2: {
|
||||
libId: 'reference_javascript_v2',
|
||||
specFile: 'supabase_js_v2',
|
||||
},
|
||||
v1: {
|
||||
libId: 'reference_javascript_v1',
|
||||
specFile: 'supabase_js_v1',
|
||||
},
|
||||
},
|
||||
},
|
||||
dart: {
|
||||
type: 'sdk',
|
||||
name: 'Flutter',
|
||||
library: 'supabase-dart',
|
||||
libPath: 'dart',
|
||||
versions: ['v2', 'v1'],
|
||||
icon: 'reference-dart',
|
||||
meta: {
|
||||
v2: {
|
||||
libId: 'reference_dart_v2',
|
||||
specFile: 'supabase_dart_v2',
|
||||
},
|
||||
v1: {
|
||||
libId: 'reference_dart_v1',
|
||||
specFile: 'supabase_dart_v1',
|
||||
},
|
||||
},
|
||||
},
|
||||
csharp: {
|
||||
type: 'sdk',
|
||||
name: 'C#',
|
||||
library: 'supabase-csharp',
|
||||
libPath: 'csharp',
|
||||
versions: ['v1', 'v0'],
|
||||
icon: 'reference-csharp',
|
||||
meta: {
|
||||
v1: {
|
||||
libId: 'reference_csharp_v1',
|
||||
specFile: 'supabase_csharp_v1',
|
||||
},
|
||||
v0: {
|
||||
libId: 'reference_csharp_v0',
|
||||
specFile: 'supabase_csharp_v0',
|
||||
},
|
||||
},
|
||||
},
|
||||
swift: {
|
||||
type: 'sdk',
|
||||
name: 'Swift',
|
||||
library: 'supabase-swift',
|
||||
libPath: 'swift',
|
||||
versions: ['v2', 'v1'],
|
||||
icon: 'reference-swift',
|
||||
meta: {
|
||||
v2: {
|
||||
libId: 'reference_swift_v2',
|
||||
specFile: 'supabase_swift_v2',
|
||||
},
|
||||
v1: {
|
||||
libId: 'reference_swift_v1',
|
||||
specFile: 'supabase_swift_v1',
|
||||
},
|
||||
},
|
||||
},
|
||||
kotlin: {
|
||||
type: 'sdk',
|
||||
name: 'Kotlin',
|
||||
library: 'supabase-kt',
|
||||
libPath: 'kotlin',
|
||||
versions: ['v2', 'v1'],
|
||||
icon: 'reference-kotlin',
|
||||
meta: {
|
||||
v2: {
|
||||
libId: 'reference_kotlin_v2',
|
||||
specFile: 'supabase_kt_v2',
|
||||
},
|
||||
v1: {
|
||||
libId: 'reference_kotlin_v1',
|
||||
specFile: 'supabase_kt_v1',
|
||||
},
|
||||
},
|
||||
},
|
||||
python: {
|
||||
type: 'sdk',
|
||||
name: 'Python',
|
||||
library: 'supabase-py',
|
||||
libPath: 'python',
|
||||
versions: ['v2'],
|
||||
icon: 'reference-python',
|
||||
meta: {
|
||||
v2: {
|
||||
libId: 'reference_python_v2',
|
||||
specFile: 'supabase_py_v2',
|
||||
},
|
||||
},
|
||||
},
|
||||
cli: {
|
||||
type: 'cli',
|
||||
name: 'CLI',
|
||||
versions: [],
|
||||
icon: 'reference-cli',
|
||||
},
|
||||
api: {
|
||||
type: 'api',
|
||||
name: 'API',
|
||||
versions: [],
|
||||
icon: 'reference-api',
|
||||
},
|
||||
} as const
|
||||
|
||||
export const clientSdkIds = Object.keys(REFERENCES).filter(
|
||||
(reference) => REFERENCES[reference].type === 'sdk'
|
||||
)
|
||||
@@ -1,29 +1,9 @@
|
||||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
This reference documents every object and method available in Supabase's C# library, [supabase](https://www.nuget.org/packages/supabase). You can use `Supabase` to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<IconMenuCsharp width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-foreground m-0">C# Client Library</h1>
|
||||
<h2 className="text-base font-mono text-foreground-light">
|
||||
@supabase-community/supabase-csharp
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<Admonition type="note">
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<div className="max-w-xl">
|
||||
This reference documents every object and method available in Supabase's C# library, [supabase](https://www.nuget.org/packages/supabase). You can use `Supabase` to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
</div>
|
||||
The C# client library is created and maintained by the Supabase community, and is not an official library. Please be tolerant of areas where the library is still being developed, and — as with all the libraries — feel free to contribute wherever you find issues.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<div className="max-w-xl bg-slate-300 px-4 py-2 rounded-md">
|
||||
Huge thanks to official maintainer, [Joseph Schultz](https://github.com/acupofjose). As well as [Will Iverson](https://github.com/wiverson), [Ben Randall](https://github.com/veleek), and [Rhuan Barros](https://github.com/rhuanbarros) for their help.
|
||||
|
||||
The C# client library is created and maintained by the Supabase community, and is not an official library. Please be tolerant of areas where the library is still being developed, and — as with all the libraries — feel free to contribute wherever you find issues.
|
||||
|
||||
Huge thanks to official maintainer, [Joseph Schultz](https://github.com/acupofjose). As well as [Will Iverson](https://github.com/wiverson), [Ben Randall](https://github.com/veleek), and [Rhuan Barros](https://github.com/rhuanbarros) for their help.
|
||||
|
||||
</div>
|
||||
</Admonition>
|
||||
|
||||
@@ -1,29 +1,9 @@
|
||||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
This reference documents every object and method available in Supabase's C# library, [supabase-csharp](https://www.nuget.org/packages/supabase-csharp). You can use `Supabase` to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<IconMenuCsharp width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-foreground m-0">C# Client Library</h1>
|
||||
<h2 className="text-base font-mono text-foreground-light">
|
||||
@supabase-community/supabase-csharp
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<Admonition type="note">
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<div className="max-w-xl">
|
||||
This reference documents every object and method available in Supabase's C# library, [supabase-csharp](https://www.nuget.org/packages/supabase-csharp). You can use `Supabase` to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
</div>
|
||||
The C# client library is created and maintained by the Supabase community, and is not an official library. Please be tolerant of areas where the library is still being developed, and — as with all the libraries — feel free to contribute wherever you find issues.
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<div className="max-w-xl bg-slate-300 px-4 py-2 rounded-md">
|
||||
Huge thanks to official maintainer, [Joseph Schultz](https://github.com/acupofjose). As well as [Will Iverson](https://github.com/wiverson), [Ben Randall](https://github.com/veleek), and [Rhuan Barros](https://github.com/rhuanbarros) for their help.
|
||||
|
||||
The C# client library is created and maintained by the Supabase community, and is not an official library. Please be tolerant of areas where the library is still being developed, and — as with all the libraries — feel free to contribute wherever you find issues.
|
||||
|
||||
Huge thanks to official maintainer, [Joseph Schultz](https://github.com/acupofjose). As well as [Will Iverson](https://github.com/wiverson), [Ben Randall](https://github.com/veleek), and [Rhuan Barros](https://github.com/rhuanbarros) for their help.
|
||||
|
||||
</div>
|
||||
</Admonition>
|
||||
|
||||
@@ -1,20 +1,3 @@
|
||||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
This reference documents every object and method available in Supabase's Flutter library, [supabase-flutter](https://pub.dev/packages/supabase_flutter). You can use supabase-flutter to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<IconMenuFlutter width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-foreground m-0">Flutter Client Library</h1>
|
||||
<h2 className="text-base font-mono text-foreground-light">supabase-flutter</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<div className="max-w-xl">
|
||||
This reference documents every object and method available in Supabase's Flutter library, [supabase-flutter](https://pub.dev/packages/supabase_flutter). You can use supabase-flutter to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
We also provide a [supabase](https://pub.dev/packages/supabase) package for non-Flutter projects.
|
||||
</div>
|
||||
We also provide a [supabase](https://pub.dev/packages/supabase) package for non-Flutter projects.
|
||||
|
||||
@@ -1,24 +1,3 @@
|
||||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
This reference documents every object and method available in Supabase's Flutter library, [supabase-flutter](https://pub.dev/packages/supabase_flutter). You can use supabase-flutter to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<IconMenuFlutter width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-foreground m-0">Flutter Client Library</h1>
|
||||
<h2 className="text-base font-mono text-foreground-light">supabase-flutter</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl">
|
||||
<Admonition type="caution">
|
||||
|
||||
You're viewing the docs for an older version of the `supabase-flutter` library. Learn how to [upgrade to the latest version](/docs/reference/dart/v0/upgrade-guide).
|
||||
|
||||
</Admonition>
|
||||
This reference documents every object and method available in Supabase's Flutter library, [supabase-flutter](https://pub.dev/packages/supabase_flutter). You can use supabase-flutter to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
We also provide a [supabase](https://pub.dev/packages/supabase) package for non-Flutter projects.
|
||||
</div>
|
||||
We also provide a [supabase](https://pub.dev/packages/supabase) package for non-Flutter projects.
|
||||
|
||||
@@ -1,20 +1,3 @@
|
||||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
This reference documents every object and method available in Supabase's Flutter library, [supabase-flutter](https://pub.dev/packages/supabase_flutter). You can use supabase-flutter to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<IconMenuFlutter width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-foreground m-0">Flutter Client Library</h1>
|
||||
<h2 className="text-base font-mono text-foreground-light">supabase-flutter</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<div className="max-w-xl">
|
||||
This reference documents every object and method available in Supabase's Flutter library, [supabase-flutter](https://pub.dev/packages/supabase_flutter). You can use supabase-flutter to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
We also provide a [supabase](https://pub.dev/packages/supabase) package for non-Flutter projects.
|
||||
</div>
|
||||
We also provide a [supabase](https://pub.dev/packages/supabase) package for non-Flutter projects.
|
||||
|
||||
@@ -4,15 +4,4 @@ title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<IconMenuJavascript width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-foreground m-0">JavaScript Client Library</h1>
|
||||
<h2 className="text-base font-mono text-foreground-light">@supabase/supabase-js</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<div className="max-w-xl">
|
||||
This reference documents every object and method available in Supabase's isomorphic JavaScript library, supabase-js. You can use supabase-js to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
</div>
|
||||
This reference documents every object and method available in Supabase's isomorphic JavaScript library, `supabase-js`. You can use `supabase-js` to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
@@ -1,25 +1 @@
|
||||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<IconMenuJavascript width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-foreground m-0">JavaScript Client Library</h1>
|
||||
<h2 className="text-base font-mono text-foreground-light">@supabase/supabase-js</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl">
|
||||
<Admonition type="caution">
|
||||
|
||||
You're viewing the docs for an older version of the `supabase-js` library. Learn how to [upgrade to the latest version](/docs/reference/javascript/v1/upgrade-guide).
|
||||
|
||||
</Admonition>
|
||||
|
||||
<p>
|
||||
This reference documents every object and method available in Supabase's isomorphic JavaScript library, supabase-js. You can use supabase-js to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
</p>
|
||||
</div>
|
||||
This reference documents every object and method available in Supabase's isomorphic JavaScript library, supabase-js. You can use supabase-js to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
id: release-notes
|
||||
title: Release Notes
|
||||
---
|
||||
|
||||
## js v1 this is the release notes file.
|
||||
@@ -1,30 +1,13 @@
|
||||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<IconMenuKotlin width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-foreground m-0">Kotlin Client Library</h1>
|
||||
<h2 className="text-base font-mono text-foreground-light">@supabase-community/supabase-kt</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl">
|
||||
|
||||
This reference documents every object and method available in Supabase's Kotlin Multiplatform library, [supabase-kt](https://github.com/supabase-community/supabase-kt). You can use supabase-kt to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
To see supported Kotlin targets, check the corresponding module README on [GitHub](https://github.com/supabase-community/supabase-kt).
|
||||
|
||||
To migrate from version 1.4.X to 2.0.0, see the [migration guide](https://github.com/supabase-community/supabase-kt/blob/master/MIGRATION.md)
|
||||
|
||||
</div>
|
||||
<Admonition type="note">
|
||||
|
||||
<div className="max-w-xl bg-slate-300 px-4 py-2 rounded-md">
|
||||
The Kotlin client library is created and maintained by the Supabase community, and is not an official library. Please be tolerant of areas where the library is still being developed, and — as with all the libraries — feel free to contribute wherever you find issues.
|
||||
|
||||
Huge thanks to official maintainer, [jan-tennert](https://github.com/jan-tennert).
|
||||
|
||||
</div>
|
||||
</Admonition>
|
||||
|
||||
393
apps/docs/docs/ref/kotlin/v1/installing.mdx
Normal file
393
apps/docs/docs/ref/kotlin/v1/installing.mdx
Normal file
@@ -0,0 +1,393 @@
|
||||
---
|
||||
id: installing
|
||||
title: 'Installing & Initialization'
|
||||
slug: installing
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supabase.yml
|
||||
---
|
||||
|
||||
### Add one or more modules to your project
|
||||
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
<a href="https://github.com/supabase-community/supabase-kt/releases" style={{ marginRight: '8px' }}>
|
||||
<img src="https://img.shields.io/github/release/supabase-community/supabase-kt?label=stable" alt="latest stable supabase-kt version"/>
|
||||
</a>
|
||||
<a href="https://github.com/supabase-community/supabase-kt/releases">
|
||||
<img src="https://img.shields.io/maven-central/v/io.github.jan-tennert.supabase/supabase-kt?label=experimental" alt="latest supabase-kt version"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
Add dependency to your build file using the BOM.
|
||||
|
||||
The available modules are:
|
||||
- [**gotrue-kt**](https://github.com/supabase-community/supabase-kt/tree/master/GoTrue)
|
||||
- [**realtime-kt**](https://github.com/supabase-community/supabase-kt/tree/master/Realtime)
|
||||
- [**storage-kt**](https://github.com/supabase-community/supabase-kt/tree/master/Storage)
|
||||
- [**functions-kt**](https://github.com/supabase-community/supabase-kt/tree/master/Functions)
|
||||
- [**postgrest-kt**](https://github.com/supabase-community/supabase-kt/tree/master/Postgrest)
|
||||
- Other plugins also available [here](https://github.com/supabase-community/supabase-kt/tree/master/plugins)
|
||||
|
||||
</RefSubLayout.Details>
|
||||
<RefSubLayout.Examples>
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin"
|
||||
queryGroup="build-file"
|
||||
>
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
implementation(platform("io.github.jan-tennert.supabase:bom:VERSION"))
|
||||
implementation("io.github.jan-tennert.supabase:postgrest-kt")
|
||||
implementation("io.github.jan-tennert.supabase:gotrue-kt")
|
||||
implementation("io.github.jan-tennert.supabase:realtime-kt")
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="groovy" label="build.gradle">
|
||||
|
||||
```groovy
|
||||
implementation platform("io.github.jan-tennert.supabase:bom:VERSION")
|
||||
implementation 'io.github.jan-tennert.supabase:postgrest-kt'
|
||||
implementation 'io.github.jan-tennert.supabase:gotrue-kt'
|
||||
implementation 'io.github.jan-tennert.supabase:realtime-kt'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="xml" label="pom.xml">
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>bom</artifactId>
|
||||
<version>VERSION</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>postgrest-kt</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>gotrue-kt</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>realtime-kt</artifactId>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</RefSubLayout.Examples>
|
||||
|
||||
</RefSubLayout.EducationRow>
|
||||
|
||||
### Add Ktor Client Engine to each of your Kotlin targets (required)
|
||||
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
|
||||
You can find a list of engines [here](https://ktor.io/docs/http-client-engines.html)
|
||||
|
||||
</RefSubLayout.Details>
|
||||
<RefSubLayout.Examples>
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin"
|
||||
queryGroup="build-file"
|
||||
>
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
implementation("io.ktor:ktor-client-[engine]:KTOR_VERSION")
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="groovy" label="build.gradle">
|
||||
|
||||
```groovy
|
||||
implementation 'io.ktor:ktor-client-[engine]:KTOR_VERSION'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="xml" label="pom.xml">
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.ktor</groupId>
|
||||
<artifactId>ktor-client-[engine]</artifactId>
|
||||
<version>KTOR_VERSION</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
</RefSubLayout.Examples>
|
||||
|
||||
<RefSubLayout.Details>
|
||||
|
||||
Multiplatform example:
|
||||
|
||||
</RefSubLayout.Details>
|
||||
<RefSubLayout.Examples>
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin"
|
||||
queryGroup="build-file"
|
||||
>
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
//supabase modules
|
||||
}
|
||||
}
|
||||
val jvmMain by getting {
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-client-cio:KTOR_VERSION")
|
||||
}
|
||||
}
|
||||
val androidMain by getting {
|
||||
dependsOn(jvmMain)
|
||||
}
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-client-js:KTOR_VERSION")
|
||||
}
|
||||
}
|
||||
val iosMain by getting {
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-client-darwin:KTOR_VERSION")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
|
||||
</RefSubLayout.EducationRow>
|
||||
|
||||
### Serialization
|
||||
|
||||
supabase-kt provides several different ways to encode and decode your custom objects.
|
||||
By default, [KotlinX Serialization](https://github.com/Kotlin/kotlinx.serialization) is used.
|
||||
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
|
||||
Use [KotlinX Serialization](https://github.com/Kotlin/kotlinx.serialization).
|
||||
|
||||
</RefSubLayout.Details>
|
||||
<RefSubLayout.Examples>
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin"
|
||||
queryGroup="build-file"
|
||||
>
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
plugins {
|
||||
kotlin("plugin.serialization") version "KOTLIN_VERSION"
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="groovy" label="build.gradle">
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version 'KOTLIN_VERSION'
|
||||
}
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="xml" label="pom.xml">
|
||||
|
||||
```xml
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<compilerPlugins>
|
||||
<plugin>kotlinx-serialization</plugin>
|
||||
</compilerPlugins>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-serialization</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
```kotlin
|
||||
val supabase = createSupabaseClient(supabaseUrl, supabaseKey) {
|
||||
//Already the default serializer, but you can provide a custom Json instance (optional):
|
||||
defaultSerializer = KotlinXSerializer(Json {
|
||||
//apply your custom config
|
||||
})
|
||||
}
|
||||
```
|
||||
</RefSubLayout.Examples>
|
||||
|
||||
</RefSubLayout.EducationRow>
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
|
||||
Use [Moshi](https://github.com/square/moshi).
|
||||
|
||||
</RefSubLayout.Details>
|
||||
<RefSubLayout.Examples>
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin"
|
||||
queryGroup="build-file"
|
||||
>
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
implementation("io.github.jan-tennert.supabase:serializer-moshi:VERSION")
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="groovy" label="build.gradle">
|
||||
|
||||
```groovy
|
||||
implementation 'io.github.jan-tennert.supabase:serializer-moshi:VERSION'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="xml" label="pom.xml">
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>serializer-moshi</artifactId>
|
||||
<version>VERSION</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
```kotlin
|
||||
val supabase = createSupabaseClient(supabaseUrl, supabaseKey) {
|
||||
defaultSerializer = MoshiSerializer()
|
||||
}
|
||||
```
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
|
||||
</RefSubLayout.EducationRow>
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
|
||||
Use [Jackson](https://github.com/FasterXML/jackson-module-kotlin).
|
||||
|
||||
</RefSubLayout.Details>
|
||||
<RefSubLayout.Examples>
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="kotlin"
|
||||
queryGroup="build-file"
|
||||
>
|
||||
<TabPanel id="kotlin" label="build.gradle.kts">
|
||||
|
||||
```kotlin
|
||||
implementation("io.github.jan-tennert.supabase:serializer-jackson:VERSION")
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="groovy" label="build.gradle">
|
||||
|
||||
```groovy
|
||||
implementation 'io.github.jan-tennert.supabase:serializer-jackson:VERSION'
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
<TabPanel id="xml" label="pom.xml">
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>io.github.jan-tennert.supabase</groupId>
|
||||
<artifactId>serializer-jackson</artifactId>
|
||||
<version>VERSION</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
```kotlin
|
||||
val supabase = createSupabaseClient(supabaseUrl, supabaseKey) {
|
||||
defaultSerializer = JacksonSerializer()
|
||||
}
|
||||
```
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
|
||||
</RefSubLayout.EducationRow>
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
|
||||
Use custom serializer.
|
||||
|
||||
</RefSubLayout.Details>
|
||||
<RefSubLayout.Examples>
|
||||
|
||||
```kotlin
|
||||
class CustomSerializer: SupabaseSerializer {
|
||||
|
||||
override fun <T : Any> encode(type: KType, value: T): String {
|
||||
//encode value to string
|
||||
}
|
||||
|
||||
override fun <T : Any> decode(type: KType, value: String): T {
|
||||
//decode value
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
```kotlin
|
||||
val supabase = createSupabaseClient(supabaseUrl, supabaseKey) {
|
||||
defaultSerializer = CustomSerializer()
|
||||
}
|
||||
```
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
|
||||
</RefSubLayout.EducationRow>
|
||||
13
apps/docs/docs/ref/kotlin/v1/introduction.mdx
Normal file
13
apps/docs/docs/ref/kotlin/v1/introduction.mdx
Normal file
@@ -0,0 +1,13 @@
|
||||
This reference documents every object and method available in Supabase's Kotlin Multiplatform library, [supabase-kt](https://github.com/supabase-community/supabase-kt). You can use supabase-kt to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
To see supported Kotlin targets, check the corresponding module README on [GitHub](https://github.com/supabase-community/supabase-kt).
|
||||
|
||||
To migrate from version 1.4.X to 2.0.0, see the [migration guide](https://github.com/supabase-community/supabase-kt/blob/master/MIGRATION.md)
|
||||
|
||||
<Admonition type="note">
|
||||
|
||||
The Kotlin client library is created and maintained by the Supabase community, and is not an official library. Please be tolerant of areas where the library is still being developed, and — as with all the libraries — feel free to contribute wherever you find issues.
|
||||
|
||||
Huge thanks to official maintainer, [jan-tennert](https://github.com/jan-tennert).
|
||||
|
||||
</Admonition>
|
||||
@@ -1,18 +1 @@
|
||||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<IconMenuPython width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-foreground m-0">Python Client Library</h1>
|
||||
<h2 className="text-base font-mono text-foreground-light">@supabase/supabase-py</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-xl">
|
||||
{/* prettier-ignore */}
|
||||
<p>This reference documents every object and method available in Supabase's Python library, [supabase-py](https://github.com/supabase/supabase-py). You can use supabase-py to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.</p>
|
||||
</div>
|
||||
This reference documents every object and method available in Supabase's Python library, [supabase-py](https://github.com/supabase/supabase-py). You can use supabase-py to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
@@ -1,20 +1 @@
|
||||
---
|
||||
id: introduction
|
||||
title: Introduction
|
||||
hideTitle: true
|
||||
---
|
||||
|
||||
<div className="flex items-start gap-6 not-prose" id="introduction">
|
||||
<IconMenuSwift width={35} height={35} />
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 className="text-3xl text-foreground m-0">Swift Client Library</h1>
|
||||
<h2 className="text-base font-mono text-foreground-light">@supabase/supabase-swift</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* prettier-ignore */}
|
||||
<div className="max-w-xl">
|
||||
|
||||
This reference documents every object and method available in Supabase's Swift library, [supabase-swift](https://github.com/supabase/supabase-swift). You can use supabase-swift to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
</div>
|
||||
This reference documents every object and method available in Supabase's Swift library, [supabase-swift](https://github.com/supabase/supabase-swift). You can use supabase-swift to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
|
||||
63
apps/docs/docs/ref/swift/v1/installing.mdx
Normal file
63
apps/docs/docs/ref/swift/v1/installing.mdx
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
id: installing
|
||||
title: 'Installing'
|
||||
slug: installing
|
||||
custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supabase.yml
|
||||
---
|
||||
|
||||
### Install using Swift Package Manager
|
||||
|
||||
<RefSubLayout.EducationRow>
|
||||
<RefSubLayout.Details>
|
||||
|
||||
You can install Supabase package using Swift Package Manager.
|
||||
|
||||
The package exposes multiple libraries, you can choose between adding all of them using Supabase, or some of:
|
||||
|
||||
- `Auth`
|
||||
- `Realtime`
|
||||
- `Postgrest`
|
||||
- `Functions`
|
||||
- `Storage`
|
||||
|
||||
</RefSubLayout.Details>
|
||||
|
||||
<RefSubLayout.Examples>
|
||||
|
||||
<Tabs
|
||||
size="small"
|
||||
type="underlined"
|
||||
defaultActiveId="swift"
|
||||
queryGroup="framework"
|
||||
>
|
||||
<TabPanel id="swift" label="Package.swift">
|
||||
|
||||
```swift
|
||||
let package = Package(
|
||||
...
|
||||
dependencies: [
|
||||
...
|
||||
.package(
|
||||
url: "https://github.com/supabase/supabase-swift.git",
|
||||
from: "2.0.0"
|
||||
),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "YourTargetName",
|
||||
dependencies: [
|
||||
.product(
|
||||
name: "Supabase", // Auth, Realtime, Postgrest, Functions, or Storage
|
||||
package: "supabase-swift"
|
||||
),
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
</RefSubLayout.Examples>
|
||||
</RefSubLayout.EducationRow>
|
||||
1
apps/docs/docs/ref/swift/v1/introduction.mdx
Normal file
1
apps/docs/docs/ref/swift/v1/introduction.mdx
Normal file
@@ -0,0 +1 @@
|
||||
This reference documents every object and method available in Supabase's Swift library, [supabase-swift](https://github.com/supabase/supabase-swift). You can use supabase-swift to interact with your Postgres database, listen to database changes, invoke Deno Edge Functions, build login and user management functionality, and manage large files.
|
||||
@@ -4,10 +4,12 @@ import { redirect } from 'next/navigation'
|
||||
import { readFile, readdir } from 'node:fs/promises'
|
||||
import { extname, join, sep } from 'node:path'
|
||||
|
||||
import { pluckPromise } from '~/features/helpers.fn'
|
||||
import { cache_fullProcess_withDevCacheBust, existsFile } from '~/features/helpers.fs'
|
||||
import type { OrPromise } from '~/features/helpers.types'
|
||||
import { notFoundLink } from '~/features/recommendations/NotFound.utils'
|
||||
import { BASE_PATH, MISC_URL } from '~/lib/constants'
|
||||
import { generateOpenGraphImageMeta } from '~/features/seo/openGraph'
|
||||
import { BASE_PATH } from '~/lib/constants'
|
||||
import { GUIDES_DIRECTORY, isValidGuideFrontmatter, type GuideFrontmatter } from '~/lib/docs'
|
||||
import { newEditLink } from './GuidesMdx.template'
|
||||
|
||||
@@ -108,9 +110,6 @@ const genGuidesStaticParams = (directory?: string) => async () => {
|
||||
return result
|
||||
}
|
||||
|
||||
const pluckPromise = <T, K extends keyof T>(promise: Promise<T>, key: K) =>
|
||||
promise.then((data) => data[key])
|
||||
|
||||
const genGuideMeta =
|
||||
<Params,>(
|
||||
generate: (params: Params) => OrPromise<{ meta: GuideFrontmatter; pathname: `/${string}` }>
|
||||
@@ -136,12 +135,11 @@ const genGuideMeta =
|
||||
openGraph: {
|
||||
...parentOg,
|
||||
url: `${BASE_PATH}${pathname}`,
|
||||
images: {
|
||||
url: `${MISC_URL}/functions/v1/og-images?site=docs&type=${encodeURIComponent(ogType)}&title=${encodeURIComponent(meta.title)}&description=${encodeURIComponent(meta.description ?? 'undefined')}`,
|
||||
width: 800,
|
||||
height: 600,
|
||||
alt: meta.title,
|
||||
},
|
||||
images: generateOpenGraphImageMeta({
|
||||
type: ogType,
|
||||
title: meta.title,
|
||||
description: meta.description,
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import ButtonCard from '~/components/ButtonCard'
|
||||
import { Extensions } from '~/components/Extensions'
|
||||
import { JwtGenerator } from '~/components/JwtGenerator'
|
||||
import {
|
||||
AuthErrorCodesTable,
|
||||
AuthRateLimits,
|
||||
CreateClientSnippet,
|
||||
DatabaseSetup,
|
||||
@@ -41,6 +42,7 @@ const components = {
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
Admonition,
|
||||
AuthErrorCodesTable,
|
||||
AuthRateLimits,
|
||||
AuthSmsProviderConfig,
|
||||
AppleSecretGenerator,
|
||||
|
||||
136
apps/docs/features/docs/Reference.generated.script.ts
Normal file
136
apps/docs/features/docs/Reference.generated.script.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { keyBy, isPlainObject } from 'lodash'
|
||||
import { mkdir, readFile, writeFile } from 'node:fs/promises'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { parse } from 'yaml'
|
||||
|
||||
import { REFERENCES, clientSdkIds } from '~/content/navigation.references'
|
||||
import { parseTypeSpec } from '~/features/docs/Reference.typeSpec'
|
||||
import type { AbbrevCommonClientLibSection } from '~/features/docs/Reference.utils'
|
||||
import { deepFilterRec } from '~/features/helpers.fn'
|
||||
import type { Json } from '~/features/helpers.types'
|
||||
import commonClientLibSections from '~/spec/common-client-libs-sections.json' assert { type: 'json' }
|
||||
|
||||
const DOCS_DIRECTORY = join(dirname(fileURLToPath(import.meta.url)), '../..')
|
||||
const SPEC_DIRECTORY = join(DOCS_DIRECTORY, 'spec')
|
||||
const GENERATED_DIRECTORY = join(dirname(fileURLToPath(import.meta.url)), 'generated')
|
||||
|
||||
async function getSpec(specFile: string, { ext = 'yml' }: { ext?: string } = {}) {
|
||||
const specFullPath = join(SPEC_DIRECTORY, `${specFile}.${ext}`)
|
||||
const rawSpec = await readFile(specFullPath, 'utf-8')
|
||||
return ext === 'yml' ? parse(rawSpec) : rawSpec
|
||||
}
|
||||
|
||||
async function parseFnsList(rawSpec: Json): Promise<Array<{ id: unknown }>> {
|
||||
if (isPlainObject(rawSpec) && 'functions' in (rawSpec as object)) {
|
||||
const _rawSpec = rawSpec as { functions: unknown }
|
||||
if (Array.isArray(_rawSpec.functions)) {
|
||||
return _rawSpec.functions.filter(({ id }) => !!id)
|
||||
}
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
function genClientSdkSectionTree(fns: Array<{ id: unknown }>, excludeName: string) {
|
||||
const validSections = deepFilterRec(
|
||||
commonClientLibSections as Array<AbbrevCommonClientLibSection>,
|
||||
'items',
|
||||
(section) =>
|
||||
section.type === 'markdown' || section.type === 'category'
|
||||
? !('excludes' in section && section.excludes.includes(excludeName))
|
||||
: section.type === 'function'
|
||||
? fns.some(({ id }) => section.id === id)
|
||||
: true
|
||||
)
|
||||
return validSections
|
||||
}
|
||||
|
||||
export function flattenCommonClientLibSections(tree: Array<AbbrevCommonClientLibSection>) {
|
||||
return tree.reduce((acc, elem) => {
|
||||
if ('items' in elem) {
|
||||
const prunedElem = { ...elem }
|
||||
delete prunedElem.items
|
||||
acc.push(prunedElem)
|
||||
acc.push(...flattenCommonClientLibSections(elem.items))
|
||||
} else {
|
||||
acc.push(elem)
|
||||
}
|
||||
|
||||
return acc
|
||||
}, [] as Array<AbbrevCommonClientLibSection>)
|
||||
}
|
||||
|
||||
async function writeTypes() {
|
||||
const types = await parseTypeSpec()
|
||||
|
||||
await writeFile(
|
||||
join(GENERATED_DIRECTORY, 'typeSpec.json'),
|
||||
JSON.stringify(types, (key, value) => {
|
||||
if (key === 'methods') {
|
||||
return Object.fromEntries(value.entries())
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
async function writeReferenceSections() {
|
||||
return Promise.all(
|
||||
clientSdkIds
|
||||
.flatMap((sdkId) => {
|
||||
const versions = REFERENCES[sdkId].versions
|
||||
return versions.map((version) => ({
|
||||
sdkId,
|
||||
version,
|
||||
}))
|
||||
})
|
||||
.flatMap(async ({ sdkId, version }) => {
|
||||
const spec = await getSpec(REFERENCES[sdkId].meta[version].specFile)
|
||||
|
||||
const fnsList = await parseFnsList(spec)
|
||||
const pendingFnListWrite = writeFile(
|
||||
join(GENERATED_DIRECTORY, `${sdkId}.${version}.functions.json`),
|
||||
JSON.stringify(fnsList)
|
||||
)
|
||||
|
||||
const sectionTree = genClientSdkSectionTree(fnsList, REFERENCES[sdkId].meta[version].libId)
|
||||
const pendingSectionTreeWrite = writeFile(
|
||||
join(GENERATED_DIRECTORY, `${sdkId}.${version}.sections.json`),
|
||||
JSON.stringify(sectionTree)
|
||||
)
|
||||
|
||||
const flattened = flattenCommonClientLibSections(sectionTree)
|
||||
const pendingFlattenedWrite = writeFile(
|
||||
join(GENERATED_DIRECTORY, `${sdkId}.${version}.flat.json`),
|
||||
JSON.stringify(flattened)
|
||||
)
|
||||
|
||||
const sectionsBySlug = keyBy(flattened, (section) => section.slug)
|
||||
const pendingSlugDictionaryWrite = writeFile(
|
||||
join(GENERATED_DIRECTORY, `${sdkId}.${version}.bySlug.json`),
|
||||
JSON.stringify(sectionsBySlug)
|
||||
)
|
||||
|
||||
return [
|
||||
pendingFnListWrite,
|
||||
pendingSectionTreeWrite,
|
||||
pendingFlattenedWrite,
|
||||
pendingSlugDictionaryWrite,
|
||||
]
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
await mkdir(GENERATED_DIRECTORY, { recursive: true })
|
||||
|
||||
await Promise.all([writeTypes(), writeReferenceSections()])
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
||||
100
apps/docs/features/docs/Reference.generated.singleton.ts
Normal file
100
apps/docs/features/docs/Reference.generated.singleton.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
|
||||
import type { ModuleTypes } from '~/features/docs/Reference.typeSpec'
|
||||
import type { AbbrevCommonClientLibSection } from '~/features/docs/Reference.utils'
|
||||
|
||||
let typeSpec: Array<ModuleTypes>
|
||||
|
||||
async function _typeSpecSingleton() {
|
||||
if (!typeSpec) {
|
||||
const rawJson = await readFile(
|
||||
join(process.cwd(), 'features/docs', './generated/typeSpec.json'),
|
||||
'utf-8'
|
||||
)
|
||||
typeSpec = JSON.parse(rawJson, (key, value) => {
|
||||
if (key === 'methods') {
|
||||
return new Map(Object.entries(value))
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return typeSpec
|
||||
}
|
||||
|
||||
export async function getTypeSpec(ref: string) {
|
||||
const modules = await _typeSpecSingleton()
|
||||
|
||||
const delimiter = ref.indexOf('.')
|
||||
const refMod = ref.substring(0, delimiter)
|
||||
|
||||
const mod = modules.find((mod) => mod.name === refMod)
|
||||
return mod?.methods.get(ref)
|
||||
}
|
||||
|
||||
const functionsList = new Map<string, Array<{ id: unknown }>>()
|
||||
|
||||
export async function getFunctionsList(sdkId: string, version: string) {
|
||||
const key = `${sdkId}.${version}`
|
||||
if (!functionsList.has(key)) {
|
||||
const data = await readFile(
|
||||
join(process.cwd(), 'features/docs', `./generated/${sdkId}.${version}.functions.json`),
|
||||
'utf-8'
|
||||
)
|
||||
|
||||
functionsList.set(key, JSON.parse(data))
|
||||
}
|
||||
|
||||
return functionsList.get(key)
|
||||
}
|
||||
|
||||
const referenceSections = new Map<string, Array<AbbrevCommonClientLibSection>>()
|
||||
|
||||
export async function getReferenceSections(sdkId: string, version: string) {
|
||||
const key = `${sdkId}.${version}`
|
||||
if (!referenceSections.has(key)) {
|
||||
const data = await readFile(
|
||||
join(process.cwd(), 'features/docs', `./generated/${sdkId}.${version}.sections.json`),
|
||||
'utf-8'
|
||||
)
|
||||
|
||||
referenceSections.set(key, JSON.parse(data))
|
||||
}
|
||||
|
||||
return referenceSections.get(key)
|
||||
}
|
||||
|
||||
const flatSections = new Map<string, Array<AbbrevCommonClientLibSection>>()
|
||||
|
||||
export async function getFlattenedSections(sdkId: string, version: string) {
|
||||
const key = `${sdkId}.${version}`
|
||||
if (!flatSections.has(key)) {
|
||||
const data = await readFile(
|
||||
join(process.cwd(), 'features/docs', `./generated/${sdkId}.${version}.flat.json`),
|
||||
'utf-8'
|
||||
)
|
||||
|
||||
flatSections.set(key, JSON.parse(data))
|
||||
}
|
||||
|
||||
return flatSections.get(key)
|
||||
}
|
||||
|
||||
const sectionsBySlug = new Map<string, Map<string, AbbrevCommonClientLibSection>>()
|
||||
|
||||
export async function getSectionsBySlug(sdkId: string, version: string) {
|
||||
const key = `${sdkId}.${version}`
|
||||
if (!sectionsBySlug.has(key)) {
|
||||
const data = await readFile(
|
||||
join(process.cwd(), 'features/docs', `./generated/${sdkId}.${version}.bySlug.json`),
|
||||
'utf-8'
|
||||
)
|
||||
const asObject = JSON.parse(data)
|
||||
|
||||
sectionsBySlug.set(key, new Map(Object.entries(asObject)))
|
||||
}
|
||||
|
||||
return sectionsBySlug.get(key)
|
||||
}
|
||||
54
apps/docs/features/docs/Reference.header.tsx
Normal file
54
apps/docs/features/docs/Reference.header.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Github } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { cn } from 'ui'
|
||||
|
||||
import MenuIconPicker from '~/components/Navigation/NavigationMenu/MenuIconPicker'
|
||||
|
||||
interface ClientLibHeaderProps {
|
||||
menuData: {
|
||||
title: string
|
||||
icon?: string
|
||||
pkg: {
|
||||
name: string
|
||||
repo: string
|
||||
}
|
||||
}
|
||||
className?: string
|
||||
}
|
||||
|
||||
function ClientLibHeader({ menuData, className }: ClientLibHeaderProps) {
|
||||
return (
|
||||
<div className={cn('flex items-start gap-6', className)}>
|
||||
{'icon' in menuData && (
|
||||
<MenuIconPicker
|
||||
icon={menuData.icon}
|
||||
width={35}
|
||||
height={35}
|
||||
className="text-foreground-light"
|
||||
/>
|
||||
)}
|
||||
<div className="flex flex-col gap-2">
|
||||
<h1 id="introduction" className="text-3xl text-foreground">
|
||||
{menuData.title} Client Library
|
||||
</h1>
|
||||
<span
|
||||
className={cn('text-base font-mono text-foreground-light', 'flex items-center gap-2')}
|
||||
>
|
||||
{menuData.pkg.name}
|
||||
<Link
|
||||
href={menuData.pkg.repo}
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
className="hover:text-brand focus-visible:text-brand transition-colors"
|
||||
>
|
||||
<span className="sr-only">View on GitHub</span>
|
||||
<Github size={18} />
|
||||
</Link>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { ClientLibHeader }
|
||||
67
apps/docs/features/docs/Reference.introduction.tsx
Normal file
67
apps/docs/features/docs/Reference.introduction.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import { AlertTriangle } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_, cn } from 'ui'
|
||||
|
||||
import { MDXRemoteBase } from '~/features/docs/MdxBase'
|
||||
import { getRefMarkdown } from '~/features/docs/Reference.mdx'
|
||||
import { ReferenceSectionWrapper } from '~/features/docs/Reference.ui.client'
|
||||
import commonClientLibSections from '~/spec/common-client-libs-sections.json' assert { type: 'json' }
|
||||
|
||||
function hasIntro(sections: typeof commonClientLibSections, excludeName: string) {
|
||||
return Boolean(
|
||||
sections[0]?.type === 'markdown' &&
|
||||
sections[0]?.slug === 'introduction' &&
|
||||
!(
|
||||
'excludes' in sections[0] &&
|
||||
Array.isArray(sections[0].excludes) &&
|
||||
sections[0].excludes?.includes(excludeName)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
interface ClientLibIntroductionProps {
|
||||
libPath: string
|
||||
excludeName: string
|
||||
version: string
|
||||
isLatestVersion: boolean
|
||||
}
|
||||
|
||||
export async function ClientLibIntroduction({
|
||||
libPath,
|
||||
excludeName,
|
||||
version,
|
||||
isLatestVersion,
|
||||
}: ClientLibIntroductionProps) {
|
||||
if (!hasIntro(commonClientLibSections, excludeName)) return null
|
||||
|
||||
const content = await getRefMarkdown(
|
||||
`${libPath}/${isLatestVersion ? '' : `${version}/`}introduction`
|
||||
)
|
||||
|
||||
return (
|
||||
<ReferenceSectionWrapper
|
||||
id="introduction"
|
||||
link={`/docs/reference/${libPath}/${isLatestVersion ? '' : `${version}/`}introduction`}
|
||||
className="prose"
|
||||
>
|
||||
<MDXRemoteBase source={content} />
|
||||
</ReferenceSectionWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export function OldVersionAlert({ libPath, className }: { libPath: string; className?: string }) {
|
||||
return (
|
||||
<Alert_Shadcn_ variant="warning" className={cn('not-prose', className)}>
|
||||
<AlertTriangle />
|
||||
<AlertTitle_Shadcn_ className="font-medium">Version out of date</AlertTitle_Shadcn_>
|
||||
<AlertDescription_Shadcn_>
|
||||
There's a newer version of this library! Migrate to the{' '}
|
||||
<Link href={`/reference/${libPath}`} className="underline underline-offset-2">
|
||||
newest version
|
||||
</Link>
|
||||
.
|
||||
</AlertDescription_Shadcn_>
|
||||
</Alert_Shadcn_>
|
||||
)
|
||||
}
|
||||
20
apps/docs/features/docs/Reference.mdx.client.tsx
Normal file
20
apps/docs/features/docs/Reference.mdx.client.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
'use client'
|
||||
|
||||
/**
|
||||
* The MDXProvider is necessary so that MDX partials will have access
|
||||
* to components.
|
||||
*
|
||||
* Since the reference pages are so heavy, keep the weight down by only
|
||||
* including the bare minimum.
|
||||
*/
|
||||
|
||||
import { MDXProvider } from '@mdx-js/react'
|
||||
import { type PropsWithChildren } from 'react'
|
||||
|
||||
import { Admonition } from 'ui'
|
||||
|
||||
const components = { Admonition }
|
||||
|
||||
export function MDXProviderReference({ children }: PropsWithChildren) {
|
||||
return <MDXProvider components={components}>{children}</MDXProvider>
|
||||
}
|
||||
49
apps/docs/features/docs/Reference.mdx.tsx
Normal file
49
apps/docs/features/docs/Reference.mdx.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import matter from 'gray-matter'
|
||||
import { readFile } from 'node:fs/promises'
|
||||
import { join } from 'node:path'
|
||||
|
||||
import { MDXRemoteBase } from '~/features/docs/MdxBase'
|
||||
import { components } from '~/features/docs/MdxBase.shared'
|
||||
import { RefSubLayout } from '~/features/docs/Reference.ui'
|
||||
import { cache_fullProcess_withDevCacheBust } from '~/features/helpers.fs'
|
||||
import { REF_DOCS_DIRECTORY } from '~/lib/docs'
|
||||
|
||||
async function getRefMarkdownInternal(relPath: string) {
|
||||
const fullPath = join(REF_DOCS_DIRECTORY, relPath + '.mdx')
|
||||
|
||||
try {
|
||||
if (!fullPath.startsWith(REF_DOCS_DIRECTORY)) {
|
||||
throw Error(`Accessing forbidden path outside of REF_DOCS_DIRECTORY: ${fullPath}`)
|
||||
}
|
||||
|
||||
const mdx = await readFile(fullPath, 'utf-8')
|
||||
const { content } = matter(mdx)
|
||||
return content
|
||||
} catch (err) {
|
||||
console.error(`Error fetching reference markdown from file: ${fullPath}:\n\n${err}`)
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caching this for the entire process is fine because the Markdown content is
|
||||
* baked into each deployment and cannot change. There's also nothing sensitive
|
||||
* here: this is just reading the public MDX files from the codebase.
|
||||
*/
|
||||
const getRefMarkdown = cache_fullProcess_withDevCacheBust(
|
||||
getRefMarkdownInternal,
|
||||
REF_DOCS_DIRECTORY,
|
||||
(filename: string) => JSON.stringify([filename.replace(/\.mdx$/, '')])
|
||||
)
|
||||
|
||||
interface MDXRemoteRefsProps {
|
||||
source: string
|
||||
}
|
||||
|
||||
function MDXRemoteRefs({ source }: MDXRemoteRefsProps) {
|
||||
const refComponents = { ...components, RefSubLayout }
|
||||
|
||||
return <MDXRemoteBase source={source} components={refComponents} />
|
||||
}
|
||||
|
||||
export { getRefMarkdown, MDXRemoteRefs }
|
||||
240
apps/docs/features/docs/Reference.navigation.client.tsx
Normal file
240
apps/docs/features/docs/Reference.navigation.client.tsx
Normal file
@@ -0,0 +1,240 @@
|
||||
'use client'
|
||||
|
||||
import * as Collapsible from '@radix-ui/react-collapsible'
|
||||
import { debounce } from 'lodash'
|
||||
import { ChevronUp } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import type { HTMLAttributes, MouseEvent, PropsWithChildren } from 'react'
|
||||
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
import { cn } from 'ui'
|
||||
|
||||
import { BASE_PATH } from '~/lib/constants'
|
||||
import type { AbbrevCommonClientLibSection } from '~/features/docs/Reference.navigation'
|
||||
import { isElementInViewport } from '~/features/ui/helpers.dom'
|
||||
|
||||
export const ReferenceContentInitiallyScrolledContext = createContext<boolean>(false)
|
||||
|
||||
export function ReferenceContentScrollHandler({
|
||||
libPath,
|
||||
version,
|
||||
isLatestVersion,
|
||||
children,
|
||||
}: PropsWithChildren<{
|
||||
libPath: string
|
||||
version: string
|
||||
isLatestVersion: boolean
|
||||
}>) {
|
||||
const checkedPathnameOnLoad = useRef(false)
|
||||
const [initiallyScrolled, setInitiallyScrolled] = useState(false)
|
||||
|
||||
const pathname = usePathname()
|
||||
|
||||
useEffect(() => {
|
||||
if (!checkedPathnameOnLoad.current) {
|
||||
const initialSelectedSection = pathname.replace(
|
||||
`/reference/${libPath}/${isLatestVersion ? '' : `${version}/`}`,
|
||||
''
|
||||
)
|
||||
if (initialSelectedSection) {
|
||||
const section = document.getElementById(initialSelectedSection)
|
||||
section?.scrollIntoView()
|
||||
section?.querySelector('h2')?.focus()
|
||||
}
|
||||
|
||||
checkedPathnameOnLoad.current = true
|
||||
setInitiallyScrolled(true)
|
||||
}
|
||||
}, [pathname, libPath, version, isLatestVersion])
|
||||
|
||||
return (
|
||||
<ReferenceContentInitiallyScrolledContext.Provider value={initiallyScrolled}>
|
||||
{children}
|
||||
</ReferenceContentInitiallyScrolledContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export function ReferenceNavigationScrollHandler({
|
||||
children,
|
||||
...rest
|
||||
}: PropsWithChildren & HTMLAttributes<HTMLDivElement>) {
|
||||
const ref = useRef<HTMLDivElement>()
|
||||
const initialScrollHappened = useContext(ReferenceContentInitiallyScrolledContext)
|
||||
|
||||
const scrollActiveIntoView = useCallback(() => {
|
||||
const currentLink = ref.current?.querySelector('[aria-current=page]') as HTMLElement
|
||||
if (currentLink && !isElementInViewport(currentLink)) {
|
||||
currentLink.scrollIntoView({
|
||||
block: 'center',
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (initialScrollHappened) {
|
||||
scrollActiveIntoView()
|
||||
}
|
||||
}, [initialScrollHappened, scrollActiveIntoView])
|
||||
|
||||
useEffect(() => {
|
||||
const debouncedScrollActiveIntoView = debounce(scrollActiveIntoView, 150)
|
||||
|
||||
window.addEventListener('scrollend', debouncedScrollActiveIntoView)
|
||||
return () => window.removeEventListener('scrollend', debouncedScrollActiveIntoView)
|
||||
}, [scrollActiveIntoView])
|
||||
|
||||
return (
|
||||
<div ref={ref} {...rest}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function deriveHref(basePath: string, section: AbbrevCommonClientLibSection) {
|
||||
return 'slug' in section ? `${basePath}/${section.slug}` : ''
|
||||
}
|
||||
|
||||
function getLinkStyles(isActive: boolean, className?: string) {
|
||||
return cn(
|
||||
'text-sm text-foreground-lighter',
|
||||
!isActive && 'hover:text-foreground',
|
||||
isActive && 'text-brand',
|
||||
'transition-colors',
|
||||
className
|
||||
)
|
||||
}
|
||||
|
||||
export function RefLink({
|
||||
basePath,
|
||||
section,
|
||||
skipChildren = false,
|
||||
className,
|
||||
}: {
|
||||
basePath: string
|
||||
section: AbbrevCommonClientLibSection
|
||||
skipChildren?: boolean
|
||||
className?: string
|
||||
}) {
|
||||
const ref = useRef<HTMLAnchorElement>()
|
||||
|
||||
const pathname = usePathname()
|
||||
const href = deriveHref(basePath, section)
|
||||
const isActive =
|
||||
pathname === href || (pathname === basePath && href.replace(basePath, '') === '/introduction')
|
||||
|
||||
if (!('title' in section)) return null
|
||||
|
||||
const isCompoundSection = !skipChildren && 'items' in section && section.items.length > 0
|
||||
|
||||
return (
|
||||
<>
|
||||
{isCompoundSection ? (
|
||||
<CompoundRefLink basePath={basePath} section={section} />
|
||||
) : (
|
||||
<Link
|
||||
ref={ref}
|
||||
href={href}
|
||||
aria-current={isActive ? 'page' : false}
|
||||
className={getLinkStyles(isActive, className)}
|
||||
onClick={(evt: MouseEvent) => {
|
||||
/*
|
||||
* We don't actually want to navigate or rerender anything since
|
||||
* links are all to sections on the same page.
|
||||
*/
|
||||
evt.preventDefault()
|
||||
history.pushState({}, '', `${BASE_PATH}${href}`)
|
||||
|
||||
if ('slug' in section) {
|
||||
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
|
||||
const domElement = document.getElementById(section.slug)
|
||||
domElement?.scrollIntoView({
|
||||
behavior: reduceMotion ? 'auto' : 'smooth',
|
||||
})
|
||||
domElement?.querySelector('h2')?.focus()
|
||||
}
|
||||
}}
|
||||
>
|
||||
{section.title}
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function useCompoundRefLinkActive(basePath: string, section: AbbrevCommonClientLibSection) {
|
||||
const [open, _setOpen] = useState(false)
|
||||
|
||||
const pathname = usePathname()
|
||||
const parentHref = deriveHref(basePath, section)
|
||||
const isParentActive = pathname === parentHref
|
||||
|
||||
const childHrefs = useMemo(
|
||||
() => new Set(section.items.map((item) => deriveHref(basePath, item))),
|
||||
[basePath, section]
|
||||
)
|
||||
const isChildActive = childHrefs.has(pathname)
|
||||
|
||||
const isActive = isParentActive || isChildActive
|
||||
|
||||
const setOpen = (open: boolean) => {
|
||||
// Disable closing if the section is active, to prevent the currently active
|
||||
// link disappearing
|
||||
if (open || !isActive) _setOpen(open)
|
||||
}
|
||||
|
||||
if (isActive && !open) {
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
return { open, setOpen, isActive }
|
||||
}
|
||||
|
||||
function CompoundRefLink({
|
||||
basePath,
|
||||
section,
|
||||
}: {
|
||||
basePath: string
|
||||
section: AbbrevCommonClientLibSection
|
||||
}) {
|
||||
const { open, setOpen, isActive } = useCompoundRefLinkActive(basePath, section)
|
||||
|
||||
return (
|
||||
<Collapsible.Root open={open} onOpenChange={setOpen}>
|
||||
<Collapsible.Trigger asChild disabled={isActive}>
|
||||
<button
|
||||
className={cn(
|
||||
'group',
|
||||
'cursor-pointer',
|
||||
'w-full',
|
||||
'flex items-center justify-between gap-2'
|
||||
)}
|
||||
>
|
||||
<span className={getLinkStyles(false)}>{section.title}</span>
|
||||
<ChevronUp
|
||||
width={16}
|
||||
className={cn(
|
||||
'group-disabled:cursor-not-allowed group-disabled:opacity-10',
|
||||
'data-open-parent:rotate-0 data-closed-parent:rotate-90',
|
||||
'transition'
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content
|
||||
className={cn('border-l border-control pl-3 ml-1 data-open:mt-2 grid gap-2.5')}
|
||||
>
|
||||
<ul className="space-y-2">
|
||||
<RefLink basePath={basePath} section={section} skipChildren />
|
||||
{section.items.map((item, idx) => {
|
||||
return (
|
||||
<li key={`${section.id}-${idx}`}>
|
||||
<RefLink basePath={basePath} section={item} />
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</Collapsible.Content>
|
||||
</Collapsible.Root>
|
||||
)
|
||||
}
|
||||
105
apps/docs/features/docs/Reference.navigation.tsx
Normal file
105
apps/docs/features/docs/Reference.navigation.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import { Fragment, type PropsWithChildren } from 'react'
|
||||
|
||||
import { cn } from 'ui'
|
||||
|
||||
import MenuIconPicker from '~/components/Navigation/NavigationMenu/MenuIconPicker'
|
||||
import RefVersionDropdown from '~/components/RefVersionDropdown'
|
||||
import { getReferenceSections } from '~/features/docs/Reference.generated.singleton'
|
||||
import {
|
||||
RefLink,
|
||||
ReferenceNavigationScrollHandler,
|
||||
} from '~/features/docs/Reference.navigation.client'
|
||||
import { type AbbrevCommonClientLibSection } from '~/features/docs/Reference.utils'
|
||||
|
||||
interface ClientSdkNavigationProps {
|
||||
sdkId: string
|
||||
name: string
|
||||
menuData: { icon?: string }
|
||||
libPath: string
|
||||
version: string
|
||||
isLatestVersion: boolean
|
||||
}
|
||||
|
||||
async function ClientSdkNavigation({
|
||||
sdkId,
|
||||
name,
|
||||
menuData,
|
||||
libPath,
|
||||
version,
|
||||
isLatestVersion,
|
||||
}: ClientSdkNavigationProps) {
|
||||
const navSections = await getReferenceSections(sdkId, version)
|
||||
|
||||
const basePath = `/reference/${libPath}${isLatestVersion ? '' : `/${version}`}`
|
||||
|
||||
return (
|
||||
<ReferenceNavigationScrollHandler className="w-full flex flex-col pt-3 pb-5 gap-3">
|
||||
<div className="flex items-center gap-3">
|
||||
{'icon' in menuData && <MenuIconPicker icon={menuData.icon} width={21} height={21} />}
|
||||
<span className="text-base text-brand-600">{name}</span>
|
||||
<RefVersionDropdown library={libPath} currentVersion={version} />
|
||||
</div>
|
||||
<ul className="flex flex-col gap-2">
|
||||
{navSections.map((section) => (
|
||||
<Fragment key={section.id}>
|
||||
{section.type === 'category' ? (
|
||||
<li>
|
||||
<RefCategory basePath={basePath} section={section} />
|
||||
</li>
|
||||
) : (
|
||||
<li className={topLvlRefNavItemStyles}>
|
||||
<RefLink basePath={basePath} section={section} />
|
||||
</li>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</ul>
|
||||
</ReferenceNavigationScrollHandler>
|
||||
)
|
||||
}
|
||||
|
||||
const topLvlRefNavItemStyles = 'leading-5'
|
||||
|
||||
function RefCategory({
|
||||
basePath,
|
||||
section,
|
||||
}: {
|
||||
basePath: string
|
||||
section: AbbrevCommonClientLibSection
|
||||
}) {
|
||||
if (!('items' in section && section.items.length > 0)) return null
|
||||
|
||||
return (
|
||||
<>
|
||||
<Divider />
|
||||
{'title' in section && <SideMenuTitle className="py-2">{section.title}</SideMenuTitle>}
|
||||
<ul className="space-y-2">
|
||||
{section.items.map((item) => (
|
||||
<li key={item.id} className={topLvlRefNavItemStyles}>
|
||||
<RefLink basePath={basePath} section={item} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function Divider() {
|
||||
return <hr className="w-full h-px my-3 bg-control" />
|
||||
}
|
||||
|
||||
function SideMenuTitle({ children, className }: PropsWithChildren<{ className?: string }>) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'font-mono font-medium text-xs text-foreground tracking-wider uppercase',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { ClientSdkNavigation }
|
||||
export type { AbbrevCommonClientLibSection }
|
||||
64
apps/docs/features/docs/Reference.sdkPage.tsx
Normal file
64
apps/docs/features/docs/Reference.sdkPage.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
import * as NavItems from '~/components/Navigation/NavigationMenu/NavigationMenu.constants'
|
||||
import { REFERENCES } from '~/content/navigation.references'
|
||||
import { ClientLibHeader } from '~/features/docs/Reference.header'
|
||||
import { ClientLibIntroduction, OldVersionAlert } from '~/features/docs/Reference.introduction'
|
||||
import { ClientSdkNavigation } from '~/features/docs/Reference.navigation'
|
||||
import { ReferenceContentScrollHandler } from '~/features/docs/Reference.navigation.client'
|
||||
import { ClientLibRefSections } from '~/features/docs/Reference.sections'
|
||||
import { LayoutMainContent } from '~/layouts/DefaultLayout'
|
||||
import { SidebarSkeleton } from '~/layouts/MainSkeleton'
|
||||
|
||||
interface ClientSdkReferenceProps {
|
||||
sdkId: string
|
||||
libVersion: string
|
||||
}
|
||||
|
||||
export async function ClientSdkReferencePage({ sdkId, libVersion }: ClientSdkReferenceProps) {
|
||||
const libraryMeta = REFERENCES[sdkId]
|
||||
const versions = libraryMeta?.versions ?? []
|
||||
const isLatestVersion = libVersion === versions[0]
|
||||
|
||||
const menuData = NavItems[libraryMeta.meta[libVersion].libId]
|
||||
|
||||
return (
|
||||
<ReferenceContentScrollHandler
|
||||
libPath={libraryMeta.libPath}
|
||||
version={libVersion}
|
||||
isLatestVersion={isLatestVersion}
|
||||
>
|
||||
<SidebarSkeleton
|
||||
menuId={MenuId.RefJavaScriptV2}
|
||||
NavigationMenu={
|
||||
<ClientSdkNavigation
|
||||
sdkId={sdkId}
|
||||
name={menuData.title}
|
||||
menuData={menuData}
|
||||
libPath={libraryMeta.libPath}
|
||||
version={libVersion}
|
||||
isLatestVersion={isLatestVersion}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<LayoutMainContent>
|
||||
{!isLatestVersion && (
|
||||
<OldVersionAlert
|
||||
libPath={libraryMeta.libPath}
|
||||
className="z-10 fixed top-[calc(var(--header-height)+1rem)] right-4 w-84 max-w-[calc(100vw-2rem)]"
|
||||
/>
|
||||
)}
|
||||
<article className="@container/article">
|
||||
<ClientLibHeader menuData={menuData} className="mt-4 mb-8" />
|
||||
<ClientLibIntroduction
|
||||
libPath={libraryMeta.libPath}
|
||||
excludeName={libraryMeta.meta[libVersion].libId}
|
||||
version={libVersion}
|
||||
isLatestVersion={isLatestVersion}
|
||||
/>
|
||||
<ClientLibRefSections sdkId={sdkId} version={libVersion} />
|
||||
</article>
|
||||
</LayoutMainContent>
|
||||
</SidebarSkeleton>
|
||||
</ReferenceContentScrollHandler>
|
||||
)
|
||||
}
|
||||
236
apps/docs/features/docs/Reference.sections.tsx
Normal file
236
apps/docs/features/docs/Reference.sections.tsx
Normal file
@@ -0,0 +1,236 @@
|
||||
import { Fragment } from 'react'
|
||||
|
||||
import { Tabs_Shadcn_, TabsContent_Shadcn_, TabsList_Shadcn_, TabsTrigger_Shadcn_, cn } from 'ui'
|
||||
|
||||
import { REFERENCES } from '~/content/navigation.references'
|
||||
import { MDXRemoteRefs, getRefMarkdown } from '~/features/docs/Reference.mdx'
|
||||
import { MDXProviderReference } from '~/features/docs/Reference.mdx.client'
|
||||
import type { MethodTypes } from '~/features/docs/Reference.typeSpec'
|
||||
import {
|
||||
getFlattenedSections,
|
||||
getFunctionsList,
|
||||
getTypeSpec,
|
||||
} from '~/features/docs/Reference.generated.singleton'
|
||||
import {
|
||||
CollapsibleDetails,
|
||||
FnParameterDetails,
|
||||
RefSubLayout,
|
||||
ReturnTypeDetails,
|
||||
StickyHeader,
|
||||
} from '~/features/docs/Reference.ui'
|
||||
import type { AbbrevCommonClientLibSection } from '~/features/docs/Reference.utils'
|
||||
import { normalizeMarkdown } from '~/features/docs/Reference.utils'
|
||||
|
||||
type ClientLibRefSectionsProps = {
|
||||
sdkId: string
|
||||
version: string
|
||||
}
|
||||
|
||||
async function ClientLibRefSections({ sdkId, version }: ClientLibRefSectionsProps) {
|
||||
let flattenedSections = await getFlattenedSections(sdkId, version)
|
||||
flattenedSections = trimIntro(flattenedSections)
|
||||
|
||||
return (
|
||||
<MDXProviderReference>
|
||||
<div className="flex flex-col my-16 gap-16">
|
||||
{flattenedSections
|
||||
.filter((section) => section.type !== 'category')
|
||||
.map((section, idx) => (
|
||||
<Fragment key={`${section.id}-${idx}`}>
|
||||
<SectionDivider />
|
||||
<SectionSwitch sdkId={sdkId} version={version} section={section} />
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
</MDXProviderReference>
|
||||
)
|
||||
}
|
||||
|
||||
function trimIntro(sections: Array<AbbrevCommonClientLibSection>) {
|
||||
const hasIntro = sections[0]?.type === 'markdown' && sections[0]?.slug === 'introduction'
|
||||
if (hasIntro) {
|
||||
return sections.slice(1)
|
||||
}
|
||||
return sections
|
||||
}
|
||||
|
||||
function SectionDivider() {
|
||||
return <hr />
|
||||
}
|
||||
|
||||
type SectionSwitchProps = {
|
||||
sdkId: string
|
||||
version: string
|
||||
section: AbbrevCommonClientLibSection
|
||||
}
|
||||
|
||||
export function SectionSwitch({ sdkId, version, section }: SectionSwitchProps) {
|
||||
const libPath = REFERENCES[sdkId].libPath
|
||||
const isLatestVersion = version === REFERENCES[sdkId].versions[0]
|
||||
|
||||
const sectionLink = `/docs/reference/${libPath}/${isLatestVersion ? '' : `${version}/`}${section.slug}`
|
||||
|
||||
switch (section.type) {
|
||||
case 'markdown':
|
||||
return (
|
||||
<MarkdownSection
|
||||
libPath={libPath}
|
||||
version={version}
|
||||
isLatestVersion={isLatestVersion}
|
||||
link={sectionLink}
|
||||
section={section}
|
||||
/>
|
||||
)
|
||||
case 'function':
|
||||
return (
|
||||
<FunctionSection
|
||||
sdkId={sdkId}
|
||||
version={version}
|
||||
link={sectionLink}
|
||||
section={section}
|
||||
useTypeSpec={REFERENCES[sdkId].typeSpec}
|
||||
/>
|
||||
)
|
||||
default:
|
||||
console.error(`Unhandled type in reference sections: ${section.type}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
interface MarkdownSectionProps {
|
||||
libPath: string
|
||||
version: string
|
||||
isLatestVersion: boolean
|
||||
link: string
|
||||
section: AbbrevCommonClientLibSection
|
||||
}
|
||||
|
||||
async function MarkdownSection({
|
||||
libPath,
|
||||
version,
|
||||
isLatestVersion,
|
||||
link,
|
||||
section,
|
||||
}: MarkdownSectionProps) {
|
||||
const content = await getRefMarkdown(
|
||||
section.meta?.shared
|
||||
? `shared/${section.id}`
|
||||
: `${libPath}/${isLatestVersion ? '' : `${version}/`}${section.id}`
|
||||
)
|
||||
|
||||
return (
|
||||
<RefSubLayout.EducationSection link={link} {...section}>
|
||||
<StickyHeader {...section} />
|
||||
<MDXRemoteRefs source={content} />
|
||||
</RefSubLayout.EducationSection>
|
||||
)
|
||||
}
|
||||
|
||||
interface FunctionSectionProps {
|
||||
sdkId: string
|
||||
version: string
|
||||
link: string
|
||||
section: AbbrevCommonClientLibSection
|
||||
useTypeSpec: boolean
|
||||
}
|
||||
|
||||
async function FunctionSection({
|
||||
sdkId,
|
||||
version,
|
||||
link,
|
||||
section,
|
||||
useTypeSpec,
|
||||
}: FunctionSectionProps) {
|
||||
const fns = await getFunctionsList(sdkId, version)
|
||||
|
||||
const fn = fns.find((fn) => fn.id === section.id)
|
||||
if (!fn) return null
|
||||
|
||||
let types: MethodTypes | undefined
|
||||
if (useTypeSpec && '$ref' in fn) {
|
||||
types = await getTypeSpec(fn['$ref'] as string)
|
||||
}
|
||||
|
||||
const fullDescription = [
|
||||
types?.comment?.shortText,
|
||||
'description' in fn && (fn.description as string),
|
||||
'notes' in fn && (fn.notes as string),
|
||||
]
|
||||
.filter(Boolean)
|
||||
.map(normalizeMarkdown)
|
||||
.join('\n\n')
|
||||
|
||||
return (
|
||||
<RefSubLayout.Section columns="double" link={link} {...section}>
|
||||
<StickyHeader {...section} className="col-[1_/_-1]" />
|
||||
<div className="overflow-hidden flex flex-col gap-8">
|
||||
<div className="prose break-words text-sm">
|
||||
<MDXRemoteRefs source={fullDescription} />
|
||||
</div>
|
||||
<FnParameterDetails
|
||||
parameters={
|
||||
'overwriteParams' in fn
|
||||
? (fn.overwriteParams as Array<object>).map((overwrittenParams) => ({
|
||||
...overwrittenParams,
|
||||
__overwritten: true,
|
||||
}))
|
||||
: 'params' in fn
|
||||
? (fn.params as Array<object>).map((param) => ({ ...param, __overwritten: true }))
|
||||
: types?.params
|
||||
}
|
||||
altParameters={types?.altSignatures?.map(({ params }) => params)}
|
||||
className="max-w-[80ch]"
|
||||
/>
|
||||
{!!types?.ret && <ReturnTypeDetails returnType={types.ret} />}
|
||||
</div>
|
||||
<div className="overflow-auto">
|
||||
{'examples' in fn && Array.isArray(fn.examples) && fn.examples.length > 0 && (
|
||||
<Tabs_Shadcn_ defaultValue={fn.examples[0].id}>
|
||||
<TabsList_Shadcn_ className="flex-wrap gap-2 border-0">
|
||||
{fn.examples.map((example) => (
|
||||
<TabsTrigger_Shadcn_
|
||||
key={example.id}
|
||||
value={example.id}
|
||||
className={cn(
|
||||
'px-2.5 py-1 rounded-full',
|
||||
'border-0 bg-surface-200 hover:bg-surface-300',
|
||||
'text-xs text-foreground-lighter',
|
||||
// Undoing styles from primitive component
|
||||
'data-[state=active]:border-0 data-[state=active]:shadow-0',
|
||||
'data-[state=active]:bg-foreground data-[state=active]:text-background',
|
||||
'transition'
|
||||
)}
|
||||
>
|
||||
{example.name}
|
||||
</TabsTrigger_Shadcn_>
|
||||
))}
|
||||
</TabsList_Shadcn_>
|
||||
{'examples' in fn &&
|
||||
Array.isArray(fn.examples) &&
|
||||
fn.examples.map((example) => (
|
||||
<TabsContent_Shadcn_ key={example.id} value={example.id}>
|
||||
<MDXRemoteRefs source={example.code} />
|
||||
<div className="flex flex-col gap-2">
|
||||
{!!example.data?.sql && (
|
||||
<CollapsibleDetails title="Data source" content={example.data.sql} />
|
||||
)}
|
||||
{!!example.response && (
|
||||
<CollapsibleDetails title="Response" content={example.response} />
|
||||
)}
|
||||
{!!example.description && (
|
||||
<CollapsibleDetails
|
||||
title="Notes"
|
||||
content={normalizeMarkdown(example.description)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</TabsContent_Shadcn_>
|
||||
))}
|
||||
</Tabs_Shadcn_>
|
||||
)}
|
||||
</div>
|
||||
</RefSubLayout.Section>
|
||||
)
|
||||
}
|
||||
|
||||
export { ClientLibRefSections }
|
||||
@@ -1,10 +1,10 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { __parseTypeSpec } from './Reference.typeSpec'
|
||||
import { parseTypeSpec } from './Reference.typeSpec'
|
||||
|
||||
describe('TS type spec parsing', () => {
|
||||
it('matches snapshot', async () => {
|
||||
const parsed = await __parseTypeSpec()
|
||||
const parsed = await parseTypeSpec()
|
||||
const json = JSON.stringify(
|
||||
parsed,
|
||||
(key, value) => {
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
* access to a function's type definition, given its name and module.
|
||||
*/
|
||||
|
||||
import { join } from 'node:path'
|
||||
|
||||
import { cache_fullProcess_withDevCacheBust } from '~/features/helpers.fs'
|
||||
import { SPEC_DIRECTORY } from '~/lib/docs'
|
||||
import _typeSpec from '~/spec/enrichments/tsdoc_v2/combined.json' assert { type: 'json' }
|
||||
|
||||
// [Charis] 2024-07-10
|
||||
@@ -26,7 +22,7 @@ export const TYPESPEC_NODE_ANONYMOUS = Symbol('anonymous')
|
||||
* Definitions for the methods and types defined in each Supabase JS client
|
||||
* library.
|
||||
*/
|
||||
interface ModuleTypes {
|
||||
export interface ModuleTypes {
|
||||
name: string
|
||||
methods: Map<string, MethodTypes>
|
||||
}
|
||||
@@ -37,8 +33,14 @@ interface ModuleTypes {
|
||||
export interface MethodTypes {
|
||||
name: string | typeof TYPESPEC_NODE_ANONYMOUS
|
||||
comment?: Comment
|
||||
params: Array<ParamType>
|
||||
params: Array<FunctionParameterType>
|
||||
ret: ReturnType | undefined
|
||||
altSignatures?: [
|
||||
{
|
||||
params: Array<FunctionParameterType>
|
||||
ret: ReturnType | undefined
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
interface Comment {
|
||||
@@ -46,18 +48,19 @@ interface Comment {
|
||||
text?: string
|
||||
}
|
||||
|
||||
interface ParamType {
|
||||
export interface FunctionParameterType {
|
||||
name: string | typeof TYPESPEC_NODE_ANONYMOUS
|
||||
comment?: Comment
|
||||
isOptional?: boolean
|
||||
type: Type | undefined
|
||||
type: TypeDetails | undefined
|
||||
}
|
||||
|
||||
interface ReturnType {
|
||||
type: Type | undefined
|
||||
type: TypeDetails | undefined
|
||||
comment?: Comment
|
||||
}
|
||||
|
||||
type Type = IntrinsicType | LiteralType | CustomType
|
||||
export type TypeDetails = IntrinsicType | LiteralType | CustomType
|
||||
|
||||
/**
|
||||
* Type definition for an intrinsic (built-in) TypeScript type, for example,
|
||||
@@ -91,24 +94,24 @@ interface NameOnlyType {
|
||||
}
|
||||
|
||||
interface CustomObjectType {
|
||||
type: 'customObject'
|
||||
type: 'object'
|
||||
name?: string | typeof TYPESPEC_NODE_ANONYMOUS
|
||||
comment?: Comment
|
||||
properties: Array<PropertyType>
|
||||
properties: Array<CustomTypePropertyType>
|
||||
}
|
||||
|
||||
interface CustomUnionType {
|
||||
type: 'customUnion'
|
||||
export interface CustomUnionType {
|
||||
type: 'union'
|
||||
name?: string | typeof TYPESPEC_NODE_ANONYMOUS
|
||||
comment?: Comment
|
||||
subTypes: Array<Type>
|
||||
subTypes: Array<TypeDetails>
|
||||
}
|
||||
|
||||
interface CustomFunctionType {
|
||||
type: 'function'
|
||||
name?: string | typeof TYPESPEC_NODE_ANONYMOUS
|
||||
comment?: Comment
|
||||
params: Array<ParamType>
|
||||
params: Array<FunctionParameterType>
|
||||
ret: ReturnType | undefined
|
||||
}
|
||||
|
||||
@@ -116,71 +119,43 @@ interface ArrayType {
|
||||
type: 'array'
|
||||
name?: string | typeof TYPESPEC_NODE_ANONYMOUS
|
||||
comment?: Comment
|
||||
elemType: Type | undefined
|
||||
elemType: TypeDetails | undefined
|
||||
}
|
||||
|
||||
interface RecordType {
|
||||
type: 'record'
|
||||
name?: string | typeof TYPESPEC_NODE_ANONYMOUS
|
||||
comment?: Comment
|
||||
keyType: Type | undefined
|
||||
valueType: Type | undefined
|
||||
keyType: TypeDetails | undefined
|
||||
valueType: TypeDetails | undefined
|
||||
}
|
||||
|
||||
interface IndexSignatureType {
|
||||
type: 'index signature'
|
||||
name?: string | typeof TYPESPEC_NODE_ANONYMOUS
|
||||
comment?: Comment
|
||||
keyType: Type | undefined
|
||||
valueType: Type | undefined
|
||||
keyType: TypeDetails | undefined
|
||||
valueType: TypeDetails | undefined
|
||||
}
|
||||
|
||||
interface PromiseType {
|
||||
type: 'promise'
|
||||
name?: string | typeof TYPESPEC_NODE_ANONYMOUS
|
||||
comment?: Comment
|
||||
awaited: Type | undefined
|
||||
awaited: TypeDetails | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Type definition for a property on a custom type definition.
|
||||
*/
|
||||
interface PropertyType {
|
||||
export interface CustomTypePropertyType {
|
||||
name?: string | typeof TYPESPEC_NODE_ANONYMOUS
|
||||
comment?: Comment
|
||||
isOptional?: boolean
|
||||
type: Type | undefined
|
||||
type: TypeDetails | undefined
|
||||
}
|
||||
|
||||
// The following is the API of this module, and the only externally exposed
|
||||
// piece. Given a reference to a function (method), return its type definition.
|
||||
|
||||
/**
|
||||
* Get the type definition for a function (a method).
|
||||
*
|
||||
* @param ref The method identifier, in the form `@supabase/supabase-js.index.SupabaseClient.constructor`.
|
||||
*/
|
||||
export async function getTypeSpec(ref: string) {
|
||||
const modules = await parseTypeSpec()
|
||||
|
||||
const delimiter = ref.indexOf('.')
|
||||
const refMod = ref.substring(0, delimiter)
|
||||
|
||||
const mod = modules.find((mod) => mod.name === refMod)
|
||||
return mod?.methods.get(ref)
|
||||
}
|
||||
|
||||
const parseTypeSpec = cache_fullProcess_withDevCacheBust(
|
||||
__parseTypeSpec,
|
||||
join(SPEC_DIRECTORY, 'enrichments/tsdoc_v2/combined.json'),
|
||||
() => JSON.stringify([])
|
||||
)
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Exposed for testing purposes only, PRIVATE DO NOT USE externally.
|
||||
*/
|
||||
export function __parseTypeSpec() {
|
||||
export function parseTypeSpec() {
|
||||
const modules = (typeSpec.children ?? []).map(parseMod)
|
||||
return modules as Array<ModuleTypes>
|
||||
}
|
||||
@@ -316,17 +291,23 @@ function parseMethod(
|
||||
comment,
|
||||
}
|
||||
|
||||
if (node.signatures.length > 1) {
|
||||
types.altSignatures = node.signatures
|
||||
.slice(1)
|
||||
.map((signature) => parseSignature(signature, map))
|
||||
}
|
||||
|
||||
res.methods.set($ref, types)
|
||||
}
|
||||
|
||||
function parseSignature(
|
||||
signature: any,
|
||||
map: Map<number, any>
|
||||
): { params: Array<ParamType>; ret: ReturnType; comment?: Comment } {
|
||||
const params: Array<ParamType> = (signature.parameters ?? []).map((param: any) => {
|
||||
): { params: Array<FunctionParameterType>; ret: ReturnType; comment?: Comment } {
|
||||
const params: Array<FunctionParameterType> = (signature.parameters ?? []).map((param: any) => {
|
||||
const type = parseType(param.type, map)
|
||||
|
||||
const res: ParamType = {
|
||||
const res: FunctionParameterType = {
|
||||
name: nameOrAnonymous(param),
|
||||
type,
|
||||
}
|
||||
@@ -447,6 +428,14 @@ function parseReferenceType(type: any, map: Map<number, any>) {
|
||||
return parsePromiseType(type, map)
|
||||
}
|
||||
|
||||
if (type.package === 'typescript' && type.qualifiedName === 'Extract') {
|
||||
return parseExtractType(type, map)
|
||||
}
|
||||
|
||||
if (type.package === 'typescript' && type.qualifiedName === 'Pick') {
|
||||
return parsePickType(type, map)
|
||||
}
|
||||
|
||||
if (type.package && type.qualifiedName) {
|
||||
return {
|
||||
type: 'nameOnly',
|
||||
@@ -500,7 +489,7 @@ function parseUnionType(type: any, map: Map<number, any>): CustomUnionType {
|
||||
const subTypes = type.types.filter(Boolean).map((type) => parseType(type, map))
|
||||
|
||||
return {
|
||||
type: 'customUnion',
|
||||
type: 'union',
|
||||
name: nameOrAnonymous(type),
|
||||
subTypes,
|
||||
}
|
||||
@@ -525,10 +514,44 @@ function parsePromiseType(type: any, map: Map<number, any>): PromiseType {
|
||||
}
|
||||
}
|
||||
|
||||
function parseExtractType(type: any, map: Map<number, any>): CustomUnionType {
|
||||
const extractedUnion = parseUnionType(type.typeArguments[1], map)
|
||||
return extractedUnion
|
||||
}
|
||||
|
||||
function parsePickType(type: any, map: Map<number, any>) {
|
||||
if (type.typeArguments[0].type === 'reference') {
|
||||
const dereferencedNode = map.get(type.typeArguments[0].id)
|
||||
if (!dereferencedNode) return undefined
|
||||
const dereferencedType = parseType(dereferencedNode, map)
|
||||
if (!dereferencedType?.properties) return undefined
|
||||
|
||||
switch (type.typeArguments[1].type) {
|
||||
case 'literal':
|
||||
return dereferencedType.properties.find(
|
||||
(property) => property.name === type.typeArguments[1].value
|
||||
)
|
||||
case 'union':
|
||||
default:
|
||||
const subTypes = dereferencedType.properties.filter((property) =>
|
||||
type.typeArguments[1].types.some((type) => type.value === property.name)
|
||||
)
|
||||
if (subTypes.length === 0) return undefined
|
||||
|
||||
return {
|
||||
type: 'union',
|
||||
subTypes,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
function parseReflectionType(type: any, map: Map<number, any>) {
|
||||
if (!type.declaration) return undefined
|
||||
|
||||
let res: Type
|
||||
let res: TypeDetails
|
||||
switch (type.declaration.kindString) {
|
||||
case 'Type literal':
|
||||
res = parseTypeLiteral(type, map)
|
||||
@@ -540,14 +563,14 @@ function parseReflectionType(type: any, map: Map<number, any>) {
|
||||
return res
|
||||
}
|
||||
|
||||
function parseTypeLiteral(type: any, map: Map<number, any>): Type {
|
||||
function parseTypeLiteral(type: any, map: Map<number, any>): TypeDetails {
|
||||
const name = nameOrAnonymous(type)
|
||||
|
||||
if ('children' in type.declaration) {
|
||||
const properties = type.declaration.children.map((child: any) => parseTypeInternals(child, map))
|
||||
return {
|
||||
name,
|
||||
type: 'customObject',
|
||||
type: 'object',
|
||||
properties,
|
||||
} satisfies CustomObjectType
|
||||
}
|
||||
@@ -582,7 +605,7 @@ function parseTypeLiteral(type: any, map: Map<number, any>): Type {
|
||||
function parseIndexedAccessType(type: any, map: Map<number, any>) {
|
||||
return {
|
||||
type: 'nameOnly',
|
||||
name: `${type.objectType?.name ?? ''}[${type.indexType.value ?? type.indexType.name ?? ''}]`,
|
||||
name: `${type.objectType?.name ?? ''}['${type.indexType.value ?? type.indexType.name ?? ''}']`,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,7 +622,7 @@ function parseInterface(type: any, map: Map<number, any>): CustomObjectType {
|
||||
const properties = (type.children ?? []).map((child) => parseTypeInternals(child, map))
|
||||
|
||||
return {
|
||||
type: 'customObject',
|
||||
type: 'object',
|
||||
name: nameOrAnonymous(type),
|
||||
properties,
|
||||
}
|
||||
@@ -641,7 +664,7 @@ function parseInternalProperty(elem: any, map: Map<number, any>) {
|
||||
const res = {
|
||||
name,
|
||||
type,
|
||||
} as PropertyType
|
||||
} as CustomTypePropertyType
|
||||
|
||||
if (elem.flags?.isOptional) {
|
||||
res.isOptional = true
|
||||
|
||||
51
apps/docs/features/docs/Reference.ui.client.tsx
Normal file
51
apps/docs/features/docs/Reference.ui.client.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
'use client'
|
||||
|
||||
import type { HTMLAttributes, PropsWithChildren } from 'react'
|
||||
import { useContext } from 'react'
|
||||
import { useInView } from 'react-intersection-observer'
|
||||
|
||||
import { cn } from 'ui'
|
||||
|
||||
import { ReferenceContentInitiallyScrolledContext } from '~/features/docs/Reference.navigation.client'
|
||||
|
||||
/**
|
||||
* Wrap a reference section with client-side functionality:
|
||||
*
|
||||
* - Intersection observer to auto-update the URL when the user scrolls the page
|
||||
* - An ID to scroll to programmatically. This is on the entire section rather
|
||||
* than the heading to avoid problems with scroll-to position when the heading
|
||||
* is sticky.
|
||||
*/
|
||||
export function ReferenceSectionWrapper({
|
||||
id,
|
||||
link,
|
||||
children,
|
||||
className,
|
||||
}: PropsWithChildren<{ id: string; link: string; className?: string }> &
|
||||
HTMLAttributes<HTMLElement>) {
|
||||
const initialScrollHappened = useContext(ReferenceContentInitiallyScrolledContext)
|
||||
|
||||
const { ref } = useInView({
|
||||
threshold: 0,
|
||||
rootMargin: '-10% 0% -50% 0%',
|
||||
onChange: (inView) => {
|
||||
if (
|
||||
inView &&
|
||||
initialScrollHappened &&
|
||||
window.scrollY > 0 /* Don't update on first navigation to introduction */
|
||||
) {
|
||||
window.history.replaceState(null, '', link)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<section
|
||||
ref={ref}
|
||||
id={id}
|
||||
className={cn('scroll-mt-[calc(var(--header-height)+4rem)]', className)}
|
||||
>
|
||||
{children}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
626
apps/docs/features/docs/Reference.ui.tsx
Normal file
626
apps/docs/features/docs/Reference.ui.tsx
Normal file
@@ -0,0 +1,626 @@
|
||||
import { isEqual } from 'lodash'
|
||||
import { ChevronRight, XCircle } from 'lucide-react'
|
||||
import type { PropsWithChildren } from 'react'
|
||||
|
||||
import { Collapsible_Shadcn_, CollapsibleContent_Shadcn_, CollapsibleTrigger_Shadcn_, cn } from 'ui'
|
||||
|
||||
import { MDXRemoteBase } from '~/features/docs/MdxBase'
|
||||
import { MDXRemoteRefs } from '~/features/docs/Reference.mdx'
|
||||
import type {
|
||||
CustomTypePropertyType,
|
||||
FunctionParameterType,
|
||||
MethodTypes,
|
||||
TypeDetails,
|
||||
} from '~/features/docs/Reference.typeSpec'
|
||||
import { TYPESPEC_NODE_ANONYMOUS } from '~/features/docs/Reference.typeSpec'
|
||||
import { ReferenceSectionWrapper } from '~/features/docs/Reference.ui.client'
|
||||
import { normalizeMarkdown } from '~/features/docs/Reference.utils'
|
||||
|
||||
interface SectionProps extends PropsWithChildren {
|
||||
link: string
|
||||
slug?: string
|
||||
columns?: 'single' | 'double'
|
||||
}
|
||||
|
||||
function Section({ slug, link, columns = 'single', children }: SectionProps) {
|
||||
const singleColumn = columns === 'single'
|
||||
|
||||
return (
|
||||
<ReferenceSectionWrapper
|
||||
id={slug}
|
||||
link={link}
|
||||
className={cn(
|
||||
'grid grid-cols-[1fr] gap-x-16 gap-y-8',
|
||||
singleColumn ? 'max-w-3xl' : '@4xl/article:grid-cols-[1fr,1fr]'
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</ReferenceSectionWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
function Details({ children }: PropsWithChildren) {
|
||||
/*
|
||||
* `min-w` is necessary because these are used as grid children, which have
|
||||
* default `min-w-auto`
|
||||
*/
|
||||
return <div className="w-full min-w-full">{children}</div>
|
||||
}
|
||||
|
||||
function Examples({ children }: PropsWithChildren) {
|
||||
/*
|
||||
* `min-w` is necessary because these are used as grid children, which have
|
||||
* default `min-w-auto`
|
||||
*/
|
||||
return <div className="w-full min-w-full sticky top-32 self-start">{children}</div>
|
||||
}
|
||||
|
||||
function EducationSection({ children, slug, ...props }: SectionProps) {
|
||||
return (
|
||||
<ReferenceSectionWrapper id={slug} className={'prose max-w-none'} {...props}>
|
||||
{children}
|
||||
</ReferenceSectionWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
interface EducationRowProps extends PropsWithChildren {
|
||||
className?: string
|
||||
}
|
||||
|
||||
function EducationRow({ className, children }: EducationRowProps) {
|
||||
return <div className={cn('grid lg:grid-cols-2 gap-8 lg:gap-16', className)}>{children}</div>
|
||||
}
|
||||
|
||||
export const RefSubLayout = {
|
||||
Section,
|
||||
EducationSection,
|
||||
EducationRow,
|
||||
Details,
|
||||
Examples,
|
||||
}
|
||||
|
||||
interface StickyHeaderProps {
|
||||
title?: string
|
||||
monoFont?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function StickyHeader({ title, monoFont = false, className }: StickyHeaderProps) {
|
||||
return (
|
||||
<h2
|
||||
tabIndex={-1} // For programmatic focus on auto-scroll to section
|
||||
className={cn(
|
||||
'sticky top-0 z-10',
|
||||
'w-full',
|
||||
// Enough padding to cover the background when stuck to the top,
|
||||
// then readjust with negative margin to prevent it looking too
|
||||
// spaced-out in regular position
|
||||
'pt-[calc(var(--header-height)+1rem)] -mt-[calc(var(--header-height)+1rem-2px)]',
|
||||
// Same for bottom
|
||||
'pb-8 -mb-4',
|
||||
'bg-gradient-to-b from-background from-85% to-transparent to-100%',
|
||||
'text-2xl font-medium text-foreground',
|
||||
'scroll-mt-[calc(var(--header-height)+1rem)]',
|
||||
monoFont && 'font-mono',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
)
|
||||
}
|
||||
|
||||
export function CollapsibleDetails({ title, content }: { title: string; content: string }) {
|
||||
return (
|
||||
<Collapsible_Shadcn_>
|
||||
<CollapsibleTrigger_Shadcn_
|
||||
className={cn(
|
||||
'group',
|
||||
'w-full h-8',
|
||||
'border bg-surface-100 rounded',
|
||||
'px-5',
|
||||
'flex items-center gap-3',
|
||||
'text-xs text-foreground-light',
|
||||
'data-[state=open]:bg-surface-200',
|
||||
'data-[state=open]:rounded-b-none data-[state=open]:border-b-0',
|
||||
'transition-safe-all ease-out'
|
||||
)}
|
||||
>
|
||||
<ChevronRight
|
||||
size={12}
|
||||
className="group-data-[state=open]:rotate-90 transition-transform"
|
||||
/>
|
||||
{title}
|
||||
</CollapsibleTrigger_Shadcn_>
|
||||
<CollapsibleContent_Shadcn_
|
||||
className={cn(
|
||||
'border border-default bg-surface-100 rounded-b',
|
||||
'px-5 py-2',
|
||||
'prose max-w-none text-sm'
|
||||
)}
|
||||
>
|
||||
<MDXRemoteRefs source={content} />
|
||||
</CollapsibleContent_Shadcn_>
|
||||
</Collapsible_Shadcn_>
|
||||
)
|
||||
}
|
||||
|
||||
export function FnParameterDetails({
|
||||
parameters,
|
||||
altParameters,
|
||||
className,
|
||||
}: {
|
||||
parameters: Array<object> | undefined
|
||||
altParameters?: Array<Array<FunctionParameterType>>
|
||||
className?: string
|
||||
}) {
|
||||
if (!parameters || parameters.length === 0) return
|
||||
|
||||
const combinedParameters = altParameters
|
||||
? mergeAlternateParameters(parameters, altParameters)
|
||||
: parameters
|
||||
|
||||
return (
|
||||
<div className={className ?? ''}>
|
||||
<h3 className="mb-3 text-base text-foreground">Parameters</h3>
|
||||
<ul>
|
||||
{combinedParameters.map((parameter, index) => (
|
||||
<li key={index} className="border-t last-of-type:border-b py-5 flex flex-col gap-3">
|
||||
<ParamOrTypeDetails paramOrType={parameter} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface SubContent {
|
||||
name: string
|
||||
isOptional?: boolean | 'NA' // not applicable
|
||||
type?: string
|
||||
description?: string
|
||||
subContent: Array<SubContent>
|
||||
}
|
||||
|
||||
function ParamOrTypeDetails({ paramOrType }: { paramOrType: object }) {
|
||||
if (!('name' in paramOrType)) return
|
||||
|
||||
const description: string =
|
||||
'description' in paramOrType
|
||||
? (paramOrType.description as string)
|
||||
: isFromTypespec(paramOrType)
|
||||
? paramOrType.comment?.shortText ?? ''
|
||||
: ''
|
||||
|
||||
const subContent =
|
||||
'subContent' in paramOrType
|
||||
? (paramOrType.subContent as Array<SubContent>)
|
||||
: isFromTypespec(paramOrType)
|
||||
? getSubDetails(paramOrType)
|
||||
: undefined
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-wrap items-baseline gap-3">
|
||||
<span className="font-mono text-sm font-medium text-foreground">
|
||||
{paramOrType.name === TYPESPEC_NODE_ANONYMOUS
|
||||
? '[Anonymous]'
|
||||
: (paramOrType.name as string)}
|
||||
</span>
|
||||
<RequiredBadge
|
||||
isOptional={
|
||||
'isOptional' in paramOrType ? (paramOrType.isOptional as boolean | 'NA') : false
|
||||
}
|
||||
/>
|
||||
{/* @ts-ignore */}
|
||||
{paramOrType?.comment?.tags?.some((tag) => tag.tag === 'deprecated') && (
|
||||
<span className="text-xs text-warning-600">Deprecated</span>
|
||||
)}
|
||||
<span className="text-xs text-foreground-muted">{getTypeName(paramOrType)}</span>
|
||||
</div>
|
||||
{description && (
|
||||
<div className="prose text-sm">
|
||||
<MDXRemoteBase source={normalizeMarkdown(description)} />
|
||||
</div>
|
||||
)}
|
||||
{subContent && subContent.length > 0 && <TypeSubDetails details={subContent} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function ReturnTypeDetails({ returnType }: { returnType: MethodTypes['ret'] }) {
|
||||
// These custom names that aren't defined aren't particularly meaningful, so
|
||||
// just don't display them.
|
||||
const isNameOnlyType = returnType.type.type === 'nameOnly'
|
||||
if (isNameOnlyType) return
|
||||
|
||||
const subContent = getSubDetails(returnType)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 className="mb-3 text-base text-foreground">Return Type</h3>
|
||||
<div className="border-t border-b py-5 flex flex-col gap-3">
|
||||
<div className="text-xs text-foreground-muted">{getTypeName(returnType)}</div>
|
||||
{returnType.comment?.shortText && (
|
||||
<div className="prose text-sm">
|
||||
<MDXRemoteBase source={normalizeMarkdown(returnType.comment?.shortText)} />
|
||||
</div>
|
||||
)}
|
||||
{subContent && subContent.length > 0 && <TypeSubDetails details={subContent} />}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function TypeSubDetails({
|
||||
details,
|
||||
className,
|
||||
}: {
|
||||
details: Array<SubContent> | Array<CustomTypePropertyType> | Array<TypeDetails>
|
||||
className?: string
|
||||
}) {
|
||||
return (
|
||||
<Collapsible_Shadcn_>
|
||||
<CollapsibleTrigger_Shadcn_
|
||||
className={cn(
|
||||
'group',
|
||||
'w-fit rounded-full',
|
||||
'px-5 py-1',
|
||||
'border border-default',
|
||||
'flex items-center gap-2',
|
||||
'text-left text-sm text-foreground-light',
|
||||
'hover:bg-surface-100',
|
||||
'data-[state=open]:w-full',
|
||||
'data-[state=open]:rounded-b-none data-[state=open]:rounded-tl-lg data-[state=open]:rounded-tr-lg',
|
||||
'transition [transition-property:width,background-color]',
|
||||
className
|
||||
)}
|
||||
>
|
||||
<XCircle
|
||||
size={14}
|
||||
className={cn(
|
||||
'text-foreground-muted',
|
||||
'group-data-[state=closed]:rotate-45',
|
||||
'transition-transform'
|
||||
)}
|
||||
/>
|
||||
Details
|
||||
</CollapsibleTrigger_Shadcn_>
|
||||
<CollapsibleContent_Shadcn_>
|
||||
<ul className={cn('border-b border-x border-default', 'rounded-b-lg')}>
|
||||
{details.map(
|
||||
(detail: SubContent | CustomTypePropertyType | TypeDetails, index: number) => (
|
||||
<li
|
||||
key={index}
|
||||
className={cn(
|
||||
'px-5 py-3',
|
||||
'border-t border-default first:border-t-0',
|
||||
'flex flex-col gap-3'
|
||||
)}
|
||||
>
|
||||
<ParamOrTypeDetails paramOrType={detail} />
|
||||
</li>
|
||||
)
|
||||
)}
|
||||
</ul>
|
||||
</CollapsibleContent_Shadcn_>
|
||||
</Collapsible_Shadcn_>
|
||||
)
|
||||
}
|
||||
|
||||
function RequiredBadge({ isOptional }: { isOptional: boolean | 'NA' }) {
|
||||
return isOptional === true ? (
|
||||
<span className="font-mono text-[10px] text-foreground-lighter tracking-wide">Optional</span>
|
||||
) : isOptional === false ? (
|
||||
<span
|
||||
className={cn(
|
||||
'inline-block',
|
||||
'px-2 py-0.25 rounded-full',
|
||||
'-translate-y-[0.125rem]', // retranslate to undo visual misalignment from the y-padding
|
||||
'border border-amber-700 bg-amber-300',
|
||||
'font-mono text-[10px] text-amber-900 uppercase tracking-wide'
|
||||
)}
|
||||
>
|
||||
Required
|
||||
</span>
|
||||
) : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the param comes from overwritten params in the library spec file or
|
||||
* directly from the type spec.
|
||||
*
|
||||
* We're cheating here, this isn't a full validation but rather just checking
|
||||
* that it isn't overwritten.
|
||||
*/
|
||||
function isFromTypespec(parameter: object): parameter is MethodTypes['params'][number] {
|
||||
return !('__overwritten' in parameter)
|
||||
}
|
||||
|
||||
function getTypeName(parameter: object): string {
|
||||
if (!('type' in parameter)) return ''
|
||||
|
||||
if (typeof parameter.type === 'string') {
|
||||
return parameter.type
|
||||
}
|
||||
|
||||
if (typeof parameter.type !== 'object' || !('type' in parameter.type)) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const type = parameter.type
|
||||
|
||||
switch (type.type) {
|
||||
case 'nameOnly':
|
||||
return nameOrDefault(type, '')
|
||||
case 'intrinsic':
|
||||
return nameOrDefault(type, '')
|
||||
case 'literal':
|
||||
return 'value' in type ? (type.value === null ? 'null' : `"${type.value as string}"`) : ''
|
||||
case 'record':
|
||||
// Needs an extra level of wrapping to fake the wrapping parameter
|
||||
// @ts-ignore
|
||||
return `Record<${getTypeName({ type: type.keyType })}, ${getTypeName({ type: type.valueType })}>`
|
||||
case 'object':
|
||||
return nameOrDefault(type, 'object')
|
||||
case 'function':
|
||||
return 'function'
|
||||
case 'promise':
|
||||
// Needs an extra level of wrapping to fake the wrapping parameter
|
||||
// @ts-ignore
|
||||
return `Promise<${getTypeName({ type: type.awaited })}>`
|
||||
case 'union':
|
||||
return 'Union: expand to see options'
|
||||
case 'index signature':
|
||||
// Needs an extra level of wrapping to fake the wrapping parameter
|
||||
// @ts-ignore
|
||||
return `{ [key: ${getTypeName({ type: type.keyType })}]: ${getTypeName({ type: type.valueType })} }`
|
||||
case 'array':
|
||||
// Needs an extra level of wrapping to fake the wrapping parameter
|
||||
// @ts-ignore
|
||||
return `Array<${getTypeName({ type: type.elemType })}>`
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
function nameOrDefault(node: object, fallback: string) {
|
||||
return 'name' in node && node.name !== TYPESPEC_NODE_ANONYMOUS ? (node.name as string) : fallback
|
||||
}
|
||||
|
||||
function getSubDetails(parentType: MethodTypes['params'][number] | MethodTypes['ret']) {
|
||||
let subDetails: Array<any>
|
||||
|
||||
switch (parentType.type?.type) {
|
||||
case 'object':
|
||||
subDetails = parentType.type.properties
|
||||
break
|
||||
case 'function':
|
||||
subDetails = [
|
||||
...(parentType.type.params.length === 0
|
||||
? []
|
||||
: [
|
||||
{
|
||||
name: 'Parameters',
|
||||
type: 'callback parameters',
|
||||
isOptional: 'NA',
|
||||
params: parentType.type.params.map((param) => ({
|
||||
...param,
|
||||
isOptional: 'NA',
|
||||
})),
|
||||
},
|
||||
]),
|
||||
{ name: 'Return', type: parentType.type.ret.type, isOptional: 'NA' },
|
||||
]
|
||||
break
|
||||
// @ts-ignore -- Adding these fake types to take advantage of existing recursion
|
||||
case 'callback parameters':
|
||||
// @ts-ignore -- Adding these fake types to take advantage of existing recursion
|
||||
subDetails = parentType.params
|
||||
break
|
||||
case 'union':
|
||||
subDetails = parentType.type.subTypes.map((subType, index) => ({
|
||||
name: `union option ${index + 1}`,
|
||||
type: { ...subType },
|
||||
isOptional: 'NA',
|
||||
}))
|
||||
break
|
||||
case 'promise':
|
||||
if (parentType.type.awaited.type === 'union') {
|
||||
subDetails = parentType.type.awaited.subTypes.map((subType, index) => ({
|
||||
name: `union option ${index + 1}`,
|
||||
type: { ...subType },
|
||||
isOptional: 'NA',
|
||||
}))
|
||||
} else if (parentType.type.awaited.type === 'object') {
|
||||
subDetails = parentType.type.awaited.properties.map((property) => ({
|
||||
...property,
|
||||
isOptional: 'NA',
|
||||
}))
|
||||
} else if (parentType.type.awaited.type === 'array') {
|
||||
subDetails = [
|
||||
{ name: 'array element', type: parentType.type.awaited.elemType, isOptional: 'NA' },
|
||||
]
|
||||
}
|
||||
break
|
||||
case 'array':
|
||||
if (parentType.type.elemType.type === 'union') {
|
||||
subDetails = parentType.type.elemType.subTypes.map((subType, index) => ({
|
||||
name: `union option ${index + 1}`,
|
||||
type: { ...subType },
|
||||
isOptional: 'NA',
|
||||
}))
|
||||
}
|
||||
if (parentType.type.elemType.type === 'object') {
|
||||
subDetails = parentType.type.elemType.properties
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
subDetails?.sort((a, b) => (a.isOptional === true ? 1 : 0) - (b.isOptional === true ? 1 : 0))
|
||||
|
||||
return subDetails
|
||||
}
|
||||
|
||||
function mergeAlternateParameters(
|
||||
parameters: Array<object>,
|
||||
altParameters: Array<Array<FunctionParameterType>>
|
||||
) {
|
||||
const combinedParameters = parameters.map((parameter) => {
|
||||
if (!isFromTypespec(parameter)) return parameter
|
||||
|
||||
const parameterWithoutType = { ...parameter }
|
||||
if ('type' in parameterWithoutType) {
|
||||
delete parameterWithoutType.type
|
||||
}
|
||||
|
||||
for (const alternate of altParameters) {
|
||||
const match = alternate.find((alternateParam) => {
|
||||
const alternateWithoutType = { ...alternateParam }
|
||||
if ('type' in alternateWithoutType) {
|
||||
delete alternateWithoutType.type
|
||||
}
|
||||
|
||||
return isEqual(parameterWithoutType, alternateWithoutType)
|
||||
})
|
||||
|
||||
if (match) {
|
||||
// @ts-ignore
|
||||
parameter = applyParameterMergeStrategy(parameter, match)
|
||||
}
|
||||
}
|
||||
|
||||
return parameter
|
||||
})
|
||||
|
||||
return combinedParameters
|
||||
}
|
||||
|
||||
function applyParameterMergeStrategy(
|
||||
parameter: Pick<FunctionParameterType, 'type'>,
|
||||
alternateParameter: Pick<FunctionParameterType, 'type'>
|
||||
) {
|
||||
if (!alternateParameter.type) {
|
||||
// Nothing to merge, abort
|
||||
return parameter as FunctionParameterType
|
||||
}
|
||||
|
||||
const clonedParameter = JSON.parse(JSON.stringify(parameter)) as FunctionParameterType
|
||||
|
||||
if (!clonedParameter.type) {
|
||||
clonedParameter.type = alternateParameter.type
|
||||
return clonedParameter
|
||||
}
|
||||
|
||||
switch (clonedParameter.type.type) {
|
||||
case 'nameOnly':
|
||||
mergeIntoUnion()
|
||||
break
|
||||
case 'literal':
|
||||
mergeIntoUnion()
|
||||
break
|
||||
case 'record':
|
||||
mergeIntoUnion()
|
||||
break
|
||||
case 'union':
|
||||
if (alternateParameter.type.type === 'union') {
|
||||
// Both unions, merge them
|
||||
for (const alternateSubType of alternateParameter.type.subTypes) {
|
||||
if (
|
||||
!clonedParameter.type.subTypes.some((subType) => isEqual(subType, alternateSubType))
|
||||
) {
|
||||
clonedParameter.type.subTypes.push(alternateSubType)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
!clonedParameter.type.subTypes.some((subType) =>
|
||||
isEqual(subType, alternateParameter.type)
|
||||
)
|
||||
) {
|
||||
clonedParameter.type.subTypes.push(alternateParameter.type)
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'object':
|
||||
if (alternateParameter.type.type === 'object') {
|
||||
// Check if the base and alternate parameters have different sets of
|
||||
// required properties. If so, they can't be merged without loss of
|
||||
// meaning and have to be represented as a union.
|
||||
const requiredOriginalProperties = new Set(
|
||||
clonedParameter.type.properties.filter(
|
||||
// @ts-ignore -- NA introduced as an additional flag for display logic
|
||||
(property) => property.isOptional !== true && property.isOptional !== 'NA'
|
||||
)
|
||||
)
|
||||
const requiredAlternateProperties = new Set(
|
||||
alternateParameter.type.properties.filter(
|
||||
// @ts-ignore -- NA introduced as an additional flag for display logic
|
||||
(property) => property.isOptional !== true && property.isOptional !== 'NA'
|
||||
)
|
||||
)
|
||||
if (requiredOriginalProperties.size !== requiredAlternateProperties.size) {
|
||||
mergeIntoUnion()
|
||||
break
|
||||
}
|
||||
const union = new Set([...requiredOriginalProperties, ...requiredAlternateProperties])
|
||||
if (union.size !== requiredOriginalProperties.size) {
|
||||
mergeIntoUnion()
|
||||
break
|
||||
}
|
||||
|
||||
const clonedParametersByName = new Map(
|
||||
clonedParameter.type.properties.map((property) => [property.name, property])
|
||||
)
|
||||
const alternateParametersByName = new Map(
|
||||
alternateParameter.type.properties.map((property) => [property.name, property])
|
||||
)
|
||||
|
||||
for (const [key, alternateValue] of alternateParametersByName) {
|
||||
if (clonedParametersByName.has(key)) {
|
||||
clonedParametersByName.set(
|
||||
key,
|
||||
applyParameterMergeStrategy(clonedParametersByName.get(key), alternateValue)
|
||||
)
|
||||
} else {
|
||||
clonedParametersByName.set(key, alternateValue)
|
||||
}
|
||||
}
|
||||
|
||||
clonedParameter.type.properties = [...clonedParametersByName.values()]
|
||||
} else {
|
||||
mergeIntoUnion()
|
||||
}
|
||||
}
|
||||
|
||||
return clonedParameter as FunctionParameterType
|
||||
|
||||
/*********
|
||||
* Utils *
|
||||
*********/
|
||||
|
||||
function mergeIntoUnion() {
|
||||
if (alternateParameter.type.type === 'union') {
|
||||
const originalType = clonedParameter.type
|
||||
|
||||
if (
|
||||
alternateParameter.type.subTypes.some((subType) => isEqual(subType, clonedParameter.type))
|
||||
) {
|
||||
clonedParameter.type = alternateParameter.type
|
||||
} else {
|
||||
clonedParameter.type = {
|
||||
type: 'union',
|
||||
subTypes: [originalType, ...alternateParameter.type.subTypes],
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const originalType = parameter.type
|
||||
if (!isEqual(originalType, alternateParameter.type)) {
|
||||
clonedParameter.type = {
|
||||
type: 'union',
|
||||
subTypes: [originalType, alternateParameter.type],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
169
apps/docs/features/docs/Reference.utils.ts
Normal file
169
apps/docs/features/docs/Reference.utils.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import { fromMarkdown } from 'mdast-util-from-markdown'
|
||||
import { toMarkdown } from 'mdast-util-to-markdown'
|
||||
import { mdxFromMarkdown, mdxToMarkdown } from 'mdast-util-mdx'
|
||||
import { mdxjs } from 'micromark-extension-mdxjs'
|
||||
import type { Metadata, ResolvingMetadata } from 'next'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { visit } from 'unist-util-visit'
|
||||
|
||||
import { REFERENCES, clientSdkIds } from '~/content/navigation.references'
|
||||
import { getFlattenedSections } from '~/features/docs/Reference.generated.singleton'
|
||||
import { generateOpenGraphImageMeta } from '~/features/seo/openGraph'
|
||||
import { BASE_PATH } from '~/lib/constants'
|
||||
|
||||
export interface AbbrevCommonClientLibSection {
|
||||
id: string
|
||||
type: string
|
||||
title?: string
|
||||
slug?: string
|
||||
items?: Array<AbbrevCommonClientLibSection>
|
||||
excludes?: Array<string>
|
||||
meta?: {
|
||||
shared?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export function parseReferencePath(slug: Array<string>) {
|
||||
const isClientSdkReference = clientSdkIds.includes(slug[0])
|
||||
|
||||
if (isClientSdkReference) {
|
||||
let [sdkId, maybeVersion, maybeCrawlers, ...path] = slug
|
||||
if (!/v\d+/.test(maybeVersion)) {
|
||||
maybeVersion = null
|
||||
maybeCrawlers = maybeVersion
|
||||
path = [maybeCrawlers, ...path]
|
||||
}
|
||||
if (maybeCrawlers !== 'crawlers') {
|
||||
maybeCrawlers = null
|
||||
path = [maybeCrawlers, ...path]
|
||||
}
|
||||
|
||||
return {
|
||||
__type: 'clientSdk' as const,
|
||||
sdkId,
|
||||
maybeVersion,
|
||||
maybeCrawlers,
|
||||
path,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
__type: 'UNIMPLEMENTED' as const,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function generateStaticParamsForSdkVersion(sdkId: string, version: string) {
|
||||
const flattenedSections = await getFlattenedSections(sdkId, version)
|
||||
|
||||
return flattenedSections
|
||||
.filter((section) => section.type !== 'category' && !!section.slug)
|
||||
.map((section) => ({
|
||||
slug: [
|
||||
sdkId,
|
||||
version === REFERENCES[sdkId].versions[0] ? null : version,
|
||||
'crawlers',
|
||||
section.slug,
|
||||
].filter(Boolean),
|
||||
}))
|
||||
}
|
||||
|
||||
export async function generateReferenceStaticParams() {
|
||||
const nonCrawlerPages = clientSdkIds
|
||||
.flatMap((sdkId) =>
|
||||
REFERENCES[sdkId].versions.map((version) => ({
|
||||
sdkId,
|
||||
version,
|
||||
}))
|
||||
)
|
||||
.map(({ sdkId, version }) => ({
|
||||
slug: [sdkId, version === REFERENCES[sdkId].versions[0] ? null : version].filter(Boolean),
|
||||
}))
|
||||
|
||||
return nonCrawlerPages
|
||||
}
|
||||
|
||||
export async function generateReferenceMetadata(
|
||||
{ params: { slug } }: { params: { slug: Array<string> } },
|
||||
resolvingParent: ResolvingMetadata
|
||||
): Promise<Metadata> {
|
||||
const { alternates: parentAlternates, openGraph: parentOg } = await resolvingParent
|
||||
|
||||
const parsedPath = parseReferencePath(slug)
|
||||
const isClientSdkReference = parsedPath.__type === 'clientSdk'
|
||||
|
||||
if (isClientSdkReference) {
|
||||
const { sdkId, maybeVersion } = parsedPath
|
||||
const version = maybeVersion ?? REFERENCES[sdkId].versions[0]
|
||||
|
||||
const flattenedSections = await getFlattenedSections(sdkId, version)
|
||||
|
||||
const displayName = REFERENCES[sdkId].name
|
||||
const sectionTitle =
|
||||
slug.length > 0
|
||||
? flattenedSections.find((section) => section.slug === slug[0])?.title
|
||||
: undefined
|
||||
const url = [BASE_PATH, 'reference', sdkId, maybeVersion, slug[0]].filter(Boolean).join('/')
|
||||
|
||||
const images = generateOpenGraphImageMeta({
|
||||
type: 'API Reference',
|
||||
title: `${displayName}${sectionTitle ? `: ${sectionTitle}` : ''}`,
|
||||
})
|
||||
|
||||
return {
|
||||
title: `${displayName} API Reference | Supabase Docs`,
|
||||
description: `API reference for the ${displayName} Supabase SDK`,
|
||||
...(slug.length > 0
|
||||
? {
|
||||
alternates: {
|
||||
...parentAlternates,
|
||||
canonical: url,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
openGraph: {
|
||||
...parentOg,
|
||||
url,
|
||||
images,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
export async function redirectNonexistentReferenceSection(
|
||||
sdkId: string,
|
||||
version: string,
|
||||
path: Array<string>,
|
||||
isLatestVersion: boolean
|
||||
) {
|
||||
const initialSelectedSection = path[0]
|
||||
|
||||
const validSlugs = await generateStaticParamsForSdkVersion(sdkId, version)
|
||||
|
||||
if (
|
||||
initialSelectedSection &&
|
||||
!validSlugs.some((params) => params.slug[0] === initialSelectedSection)
|
||||
) {
|
||||
redirect(`/reference/${sdkId}` + (!isLatestVersion ? '/' + version : ''))
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeMarkdown(markdownUnescaped: string): string {
|
||||
const markdown = markdownUnescaped.replaceAll(/(?<!\\)\{/g, '\\{').replaceAll(/(?<!\\)\}/g, '\\}')
|
||||
|
||||
const mdxTree = fromMarkdown(markdown, {
|
||||
extensions: [mdxjs()],
|
||||
mdastExtensions: [mdxFromMarkdown()],
|
||||
})
|
||||
|
||||
visit(mdxTree, 'text', (node) => {
|
||||
node.value = node.value.replace(/\n/g, ' ')
|
||||
})
|
||||
|
||||
const content = toMarkdown(mdxTree, {
|
||||
extensions: [mdxToMarkdown()],
|
||||
})
|
||||
|
||||
return content
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@ import { BUILD_PREVIEW_HTML, IS_PREVIEW } from '~/lib/constants'
|
||||
* rerender in prod, but this is fine because IS_PREVIEW will never change on
|
||||
* you within a single build.
|
||||
*/
|
||||
const ShortcutPreviewBuild = ({ children }: PropsWithChildren) => {
|
||||
export const ShortcutPreviewBuild = ({ children }: PropsWithChildren) => {
|
||||
if (!BUILD_PREVIEW_HTML && IS_PREVIEW) {
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const [isMounted, setIsMounted] = useState(false)
|
||||
@@ -27,5 +27,3 @@ const ShortcutPreviewBuild = ({ children }: PropsWithChildren) => {
|
||||
|
||||
return children
|
||||
}
|
||||
|
||||
export { ShortcutPreviewBuild }
|
||||
|
||||
29
apps/docs/features/helpers.fn.ts
Normal file
29
apps/docs/features/helpers.fn.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
type RecObj<T extends object, K extends keyof T> = T[K] extends Array<T> ? T : never
|
||||
|
||||
export function deepFilterRec<T extends object, K extends keyof T>(
|
||||
arr: Array<RecObj<T, K>>,
|
||||
recKey: K,
|
||||
filterFn: (item: T) => boolean
|
||||
) {
|
||||
return arr.reduce(
|
||||
(acc, elem) => {
|
||||
if (!filterFn(elem)) return acc
|
||||
|
||||
if (recKey in elem) {
|
||||
const newSubitems = deepFilterRec(elem[recKey] as Array<RecObj<T, K>>, recKey, filterFn)
|
||||
const newElem = { ...elem, [recKey]: newSubitems }
|
||||
|
||||
if (newSubitems.length > 0 || filterFn(elem)) acc.push(newElem)
|
||||
} else {
|
||||
acc.push(elem)
|
||||
}
|
||||
|
||||
return acc
|
||||
},
|
||||
[] as Array<RecObj<T, K>>
|
||||
)
|
||||
}
|
||||
|
||||
export function pluckPromise<T, K extends keyof T>(promise: Promise<T>, key: K) {
|
||||
return promise.then((data) => data[key])
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { watch } from 'node:fs'
|
||||
import { stat } from 'node:fs/promises'
|
||||
|
||||
import { IS_DEV } from '~/lib/constants'
|
||||
import type { OrPromise } from '~/features/helpers.types'
|
||||
import { IS_DEV } from '~/lib/constants'
|
||||
|
||||
/**
|
||||
* Caches a function for the length of the server process.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
type OrPromise<T> = T | Promise<T>
|
||||
|
||||
type Json = string | number | boolean | { [key: string]: Json } | Json[]
|
||||
|
||||
type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }
|
||||
|
||||
export type { OrPromise, WithRequired }
|
||||
export type { Json, OrPromise, WithRequired }
|
||||
|
||||
18
apps/docs/features/seo/openGraph.ts
Normal file
18
apps/docs/features/seo/openGraph.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { MISC_URL } from '~/lib/constants'
|
||||
|
||||
export function generateOpenGraphImageMeta({
|
||||
type,
|
||||
title,
|
||||
description,
|
||||
}: {
|
||||
type: string
|
||||
title: string
|
||||
description?: string
|
||||
}) {
|
||||
return {
|
||||
url: `${MISC_URL}/functions/v1/og-images?site=docs&type=${encodeURIComponent(type)}&title=${encodeURIComponent(title)}&description=${encodeURIComponent(description ?? 'undefined')}`,
|
||||
width: 800,
|
||||
height: 600,
|
||||
alt: title,
|
||||
}
|
||||
}
|
||||
9
apps/docs/features/ui/helpers.dom.ts
Normal file
9
apps/docs/features/ui/helpers.dom.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export function isElementInViewport(element: HTMLElement) {
|
||||
const { top, left, width, height } = element.getBoundingClientRect()
|
||||
const { innerWidth, innerHeight } = window
|
||||
|
||||
return (
|
||||
((top >= 0 && top < innerHeight) || (top < 0 && top + height > 0)) &&
|
||||
((left >= 0 && left < innerWidth) || (left < 0 && left + width > 0))
|
||||
)
|
||||
}
|
||||
@@ -13,9 +13,6 @@ import { DOCS_CONTENT_CONTAINER_ID } from '~/features/ui/helpers.constants'
|
||||
import { menuState, useMenuMobileOpen } from '~/hooks/useMenuState'
|
||||
|
||||
const Footer = dynamic(() => import('~/components/Navigation/Footer'))
|
||||
const NavigationMenu = dynamic(
|
||||
() => import('~/components/Navigation/NavigationMenu/NavigationMenu')
|
||||
)
|
||||
|
||||
const levelsData = {
|
||||
home: {
|
||||
|
||||
23
apps/docs/layouts/ReferenceLayout.tsx
Normal file
23
apps/docs/layouts/ReferenceLayout.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
'use client'
|
||||
|
||||
import 'katex/dist/katex.min.css'
|
||||
|
||||
import { type PropsWithChildren } from 'react'
|
||||
|
||||
import { type MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
import { LayoutMainContent } from '~/layouts/DefaultLayout'
|
||||
import { SidebarSkeleton } from '~/layouts/MainSkeleton'
|
||||
|
||||
interface LayoutProps extends PropsWithChildren {
|
||||
menuId: MenuId
|
||||
}
|
||||
|
||||
function ReferenceLayout({ menuId, children }: LayoutProps) {
|
||||
return (
|
||||
<SidebarSkeleton menuId={menuId}>
|
||||
<LayoutMainContent className="pb-0">{children}</LayoutMainContent>
|
||||
</SidebarSkeleton>
|
||||
)
|
||||
}
|
||||
|
||||
export { ReferenceLayout }
|
||||
@@ -10,3 +10,4 @@ export const IS_PLATFORM = process.env.NEXT_PUBLIC_IS_PLATFORM === 'true'
|
||||
export const IS_PREVIEW = process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview'
|
||||
export const LOCAL_SUPABASE = process.env.NEXT_PUBLIC_LOCAL_SUPABASE === 'true'
|
||||
export const MISC_URL = process.env.NEXT_PUBLIC_MISC_URL ?? ''
|
||||
export const SKIP_BUILD_STATIC_GENERATION = process.env.SKIP_BUILD_STATIC_GENERATION === 'true'
|
||||
|
||||
@@ -14,6 +14,7 @@ import codeHikeTheme from 'config/code-hike.theme.json' assert { type: 'json' }
|
||||
|
||||
const DOCS_DIRECTORY = join(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
export const GUIDES_DIRECTORY = join(DOCS_DIRECTORY, 'content/guides')
|
||||
export const REF_DOCS_DIRECTORY = join(DOCS_DIRECTORY, 'docs/ref')
|
||||
export const SPEC_DIRECTORY = join(DOCS_DIRECTORY, 'spec')
|
||||
|
||||
export type GuideFrontmatter = {
|
||||
|
||||
@@ -1,34 +1,45 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { isbot } from 'isbot'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
import { clientSdkIds } from '~/content/navigation.references'
|
||||
import { BASE_PATH } from '~/lib/constants'
|
||||
|
||||
const REFERENCE_PATH = `${(BASE_PATH ?? '') + '/'}reference`
|
||||
|
||||
export function middleware(request: NextRequest) {
|
||||
const specs = ['javascript', 'dart', 'csharp']
|
||||
const url = new URL(request.url)
|
||||
|
||||
let version = ''
|
||||
if (request.url.includes('/v1/')) {
|
||||
version = 'v1'
|
||||
}
|
||||
if (request.url.includes('/v0/')) {
|
||||
version = 'v0'
|
||||
if (!url.pathname.startsWith(REFERENCE_PATH)) {
|
||||
return NextResponse.next()
|
||||
}
|
||||
|
||||
if (isbot(request.headers.get('user-agent'))) {
|
||||
for (const lib of specs) {
|
||||
if (request.url.includes(`reference/${lib}`)) {
|
||||
const requestSlug = request.url.split('/').pop()
|
||||
let [, lib, maybeVersion, ...slug] = url.pathname.replace(REFERENCE_PATH, '').split('/')
|
||||
|
||||
return NextResponse.rewrite(
|
||||
new URL(
|
||||
`/docs/reference/${lib}/${version ? version + '/' : ''}crawlers/${requestSlug}`,
|
||||
request.url
|
||||
).toString()
|
||||
)
|
||||
if (clientSdkIds.includes(lib)) {
|
||||
const version = /v\d+/.test(maybeVersion) ? maybeVersion : undefined
|
||||
if (!version) {
|
||||
slug = [maybeVersion, ...slug]
|
||||
}
|
||||
|
||||
if (slug.length > 0) {
|
||||
const rewriteUrl = new URL(url)
|
||||
rewriteUrl.pathname = (BASE_PATH ?? '') + '/api/crawlers'
|
||||
|
||||
return NextResponse.rewrite(rewriteUrl)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return NextResponse.next()
|
||||
const [, lib, maybeVersion] = url.pathname.replace(REFERENCE_PATH, '').split('/')
|
||||
if (clientSdkIds.includes(lib)) {
|
||||
const version = /v\d+/.test(maybeVersion) ? maybeVersion : null
|
||||
const rewritePath = [REFERENCE_PATH, lib, version].filter(Boolean).join('/')
|
||||
return NextResponse.rewrite(new URL(rewritePath, request.url))
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.next()
|
||||
}
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -64,6 +64,11 @@ const nextConfig = {
|
||||
},
|
||||
},
|
||||
transpilePackages: ['ui', 'ui-patterns', 'common', 'dayjs', 'shared-data', 'api-types', 'icons'],
|
||||
experimental: {
|
||||
outputFileTracingIncludes: {
|
||||
'/api/crawlers': ['./features/docs/generated/**/*', './docs/ref/**/*'],
|
||||
},
|
||||
},
|
||||
/**
|
||||
* The SQL to REST API translator relies on libpg-query, which packages a
|
||||
* native Node.js module that wraps the Postgres query parser.
|
||||
|
||||
@@ -3,19 +3,23 @@
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"predev": "npm run codegen:references",
|
||||
"dev": "next dev --port 3001",
|
||||
"dev:secrets:pull": "AWS_PROFILE=supabase-dev node ../../scripts/getSecrets.js -n local/docs",
|
||||
"prebuild": "npm run codegen:references",
|
||||
"build": "next build",
|
||||
"build:analyze": "ANALYZE=true next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "vitest",
|
||||
"test": "vitest --exclude \"**/*.smoke.test.ts\"",
|
||||
"test:smoke": "npm run codegen:references && vitest -t \"prod smoke test\"",
|
||||
"build:sitemap": "node ./internals/generate-sitemap.mjs",
|
||||
"embeddings": "tsx scripts/search/generate-embeddings.ts",
|
||||
"embeddings:refresh": "npm run embeddings -- --refresh",
|
||||
"last-changed": "tsx scripts/last-changed.ts",
|
||||
"last-changed:reset": "npm run last-changed -- --reset",
|
||||
"codegen:references": "tsx features/docs/Reference.generated.script.ts",
|
||||
"codemod:frontmatter": "node ./scripts/codemod/mdx-meta.mjs && prettier --write \"content/**/*.mdx\"",
|
||||
"postbuild": "node ./internals/generate-sitemap.mjs"
|
||||
},
|
||||
@@ -80,7 +84,8 @@
|
||||
"unist-util-filter": "^4.0.1",
|
||||
"unist-util-visit": "^4.1.2",
|
||||
"uuid": "^9.0.1",
|
||||
"valtio": "^1.12.0"
|
||||
"valtio": "^1.12.0",
|
||||
"yaml": "^2.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-secrets-manager": "^3.410.0",
|
||||
@@ -91,6 +96,7 @@
|
||||
"@types/unist": "^2.0.6",
|
||||
"acorn": "^8.11.3",
|
||||
"api-types": "*",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"config": "*",
|
||||
"dotenv": "^16.0.3",
|
||||
"ejs": "^3.1.10",
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_csharp_v1.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/csharp'
|
||||
|
||||
export default function CSharpReference(props) {
|
||||
return (
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefCSharpV1}
|
||||
sections={sections}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_csharp_v1.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import RefSEO from '~/components/reference/RefSEO'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
import type { TypeSpec } from '~/components/reference/Reference.types'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/csharp'
|
||||
|
||||
export default function CSharpReference(props) {
|
||||
const router = useRouter()
|
||||
const slug = router.query.slug[0]
|
||||
const filteredSection = sections.filter((section) => section.id === slug)
|
||||
|
||||
const pageTitle = filteredSection[0]?.title
|
||||
? `${filteredSection[0]?.title} | Supabase`
|
||||
: 'Supabase'
|
||||
|
||||
return (
|
||||
<>
|
||||
<RefSEO title={pageTitle} />
|
||||
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefCSharpV1}
|
||||
sections={filteredSection}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_csharp_v0.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/csharp/v0'
|
||||
|
||||
export default function CSharpReference(props) {
|
||||
return (
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefCSharpV0}
|
||||
sections={sections}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_csharp_v0.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import RefSEO from '~/components/reference/RefSEO'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/csharp/v0'
|
||||
|
||||
export default function JSReference(props) {
|
||||
const router = useRouter()
|
||||
const slug = router.query.slug[0]
|
||||
const filteredSection = sections.filter((section) => section.id === slug)
|
||||
|
||||
const pageTitle = filteredSection[0]?.title
|
||||
? `${filteredSection[0]?.title} | Supabase`
|
||||
: 'Supabase'
|
||||
return (
|
||||
<>
|
||||
<RefSEO title={pageTitle} />
|
||||
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefCSharpV0}
|
||||
sections={filteredSection}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_dart_v2.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/dart'
|
||||
|
||||
export default function DartReference(props) {
|
||||
return (
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefDartV2}
|
||||
sections={sections}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_dart_v2.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import RefSEO from '~/components/reference/RefSEO'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/dart'
|
||||
|
||||
export default function DartReference(props) {
|
||||
const router = useRouter()
|
||||
const slug = router.query.slug[0]
|
||||
const filteredSection = sections.filter((section) => section.slug === slug)
|
||||
|
||||
const pageTitle = filteredSection[0]?.title
|
||||
? `${filteredSection[0]?.title} | Supabase`
|
||||
: 'Supabase'
|
||||
|
||||
return (
|
||||
<>
|
||||
<RefSEO title={pageTitle} />
|
||||
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefDartV2}
|
||||
sections={filteredSection}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_dart_v1.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/dart/v0'
|
||||
|
||||
export default function JSReference(props) {
|
||||
return (
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefDartV1}
|
||||
sections={sections}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
isOldVersion
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_dart_v1.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import RefSEO from '~/components/reference/RefSEO'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/dart/v0'
|
||||
|
||||
export default function DartReference(props) {
|
||||
const router = useRouter()
|
||||
const slug = router.query.slug[0]
|
||||
const filteredSection = sections.filter((section) => section.slug === slug)
|
||||
|
||||
const pageTitle = filteredSection[0]?.title
|
||||
? `${filteredSection[0]?.title} | Supabase`
|
||||
: 'Supabase'
|
||||
return (
|
||||
<>
|
||||
<RefSEO title={pageTitle} />
|
||||
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefDartV1}
|
||||
sections={filteredSection}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json' assert { type: 'json' }
|
||||
import typeSpec from '~/spec/enrichments/tsdoc_v2/combined.json' assert { type: 'json' }
|
||||
import spec from '~/spec/supabase_js_v2.yml' assert { type: 'yml' }
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/javascript'
|
||||
|
||||
export default function JSReference(props) {
|
||||
return (
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefJavaScriptV2}
|
||||
sections={sections}
|
||||
spec={spec}
|
||||
typeSpec={typeSpec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import typeSpec from '~/spec/enrichments/tsdoc_v2/combined.json'
|
||||
import spec from '~/spec/supabase_js_v2.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import RefSEO from '~/components/reference/RefSEO'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/javascript'
|
||||
|
||||
export default function JSReference(props) {
|
||||
const router = useRouter()
|
||||
const slug = router.query.slug[0]
|
||||
const filteredSection = sections.filter((section) => section.slug === slug)
|
||||
|
||||
const pageTitle = filteredSection[0]?.title
|
||||
? `${filteredSection[0]?.title} | Supabase`
|
||||
: 'Supabase'
|
||||
|
||||
return (
|
||||
<>
|
||||
<RefSEO title={pageTitle} />
|
||||
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefJavaScriptV2}
|
||||
sections={filteredSection}
|
||||
spec={spec}
|
||||
typeSpec={typeSpec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json' assert { type: 'json' }
|
||||
import typeSpec from '~/spec/enrichments/tsdoc_v1/combined.json' assert { type: 'json' }
|
||||
import spec from '~/spec/supabase_js_v1.yml' assert { type: 'yml' }
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/javascript/v1'
|
||||
|
||||
export default function JSReference(props) {
|
||||
return (
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefJavaScriptV1}
|
||||
sections={sections}
|
||||
spec={spec}
|
||||
typeSpec={typeSpec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
isOldVersion
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import typeSpec from '~/spec/enrichments/tsdoc_v1/combined.json'
|
||||
import spec from '~/spec/supabase_js_v1.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import RefSEO from '~/components/reference/RefSEO'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/javascript/v1'
|
||||
|
||||
export default function JSReference(props) {
|
||||
const router = useRouter()
|
||||
const slug = router.query.slug[0]
|
||||
const filteredSection = sections.filter((section) => section.slug === slug)
|
||||
|
||||
const pageTitle = filteredSection[0]?.title
|
||||
? `${filteredSection[0]?.title} | Supabase`
|
||||
: 'Supabase'
|
||||
|
||||
return (
|
||||
<>
|
||||
<RefSEO title={pageTitle} />
|
||||
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefJavaScriptV1}
|
||||
sections={filteredSection}
|
||||
spec={spec}
|
||||
typeSpec={typeSpec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_kt_v2.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/kotlin'
|
||||
|
||||
export default function KotlinReference(props) {
|
||||
return (
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefKotlinV2}
|
||||
sections={sections}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_kt_v2.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import RefSEO from '~/components/reference/RefSEO'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/kotlin'
|
||||
|
||||
export default function KotlinReference(props) {
|
||||
const router = useRouter()
|
||||
const slug = router.query.slug[0]
|
||||
const filteredSection = sections.filter((section) => section.slug === slug)
|
||||
|
||||
const pageTitle = filteredSection[0]?.title
|
||||
? `${filteredSection[0]?.title} | Supabase`
|
||||
: 'Supabase'
|
||||
|
||||
return (
|
||||
<>
|
||||
<RefSEO title={pageTitle} />
|
||||
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefKotlinV2}
|
||||
sections={filteredSection}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_kt_v1.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/kotlin/v1'
|
||||
|
||||
export default function KotlinReference(props) {
|
||||
return (
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefKotlinV1}
|
||||
sections={sections}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
isOldVersion
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_kt_v1.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import RefSEO from '~/components/reference/RefSEO'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/kotlin/v1'
|
||||
|
||||
export default function KotlinReference(props) {
|
||||
const router = useRouter()
|
||||
const slug = router.query.slug[0]
|
||||
const filteredSection = sections.filter((section) => section.id === slug)
|
||||
|
||||
const pageTitle = filteredSection[0]?.title
|
||||
? `${filteredSection[0]?.title} | Supabase`
|
||||
: 'Supabase'
|
||||
return (
|
||||
<>
|
||||
<RefSEO title={pageTitle} />
|
||||
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefKotlinV1}
|
||||
sections={filteredSection}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_py_v2.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/python'
|
||||
|
||||
export default function PyReference(props) {
|
||||
return (
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefPythonV2}
|
||||
sections={sections}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_py_v2.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import RefSEO from '~/components/reference/RefSEO'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/python'
|
||||
|
||||
export default function PyReference(props) {
|
||||
const router = useRouter()
|
||||
const slug = router.query.slug[0]
|
||||
const filteredSection = sections.filter((section) => section.id === slug)
|
||||
|
||||
const pageTitle = filteredSection[0]?.title
|
||||
? `${filteredSection[0]?.title} | Supabase`
|
||||
: 'Supabase'
|
||||
|
||||
return (
|
||||
<>
|
||||
<RefSEO title={pageTitle} />
|
||||
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefPythonV2}
|
||||
sections={filteredSection}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_swift_v2.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/swift'
|
||||
|
||||
export default function SwiftReference(props) {
|
||||
return (
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefSwiftV2}
|
||||
sections={sections}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_swift_v2.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import RefSEO from '~/components/reference/RefSEO'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/swift'
|
||||
|
||||
export default function SwiftReference(props) {
|
||||
const router = useRouter()
|
||||
const slug = router.query.slug[0]
|
||||
const filteredSection = sections.filter((section) => section.id === slug)
|
||||
|
||||
const pageTitle = filteredSection[0]?.title
|
||||
? `${filteredSection[0]?.title} | Supabase`
|
||||
: 'Supabase'
|
||||
|
||||
return (
|
||||
<>
|
||||
<RefSEO title={pageTitle} />
|
||||
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefSwiftV2}
|
||||
sections={filteredSection}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_swift_v2.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/swift/v2'
|
||||
|
||||
export default function SwiftReference(props) {
|
||||
return (
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefSwiftV1}
|
||||
sections={sections}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import clientLibsCommonSections from '~/spec/common-client-libs-sections.json'
|
||||
import spec from '~/spec/supabase_swift_v2.yml' assert { type: 'yml' }
|
||||
import RefSectionHandler from '~/components/reference/RefSectionHandler'
|
||||
import { flattenSections } from '~/lib/helpers'
|
||||
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
|
||||
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
|
||||
import { useRouter } from 'next/compat/router'
|
||||
import RefSEO from '~/components/reference/RefSEO'
|
||||
import { MenuId } from '~/components/Navigation/NavigationMenu/NavigationMenu'
|
||||
|
||||
const sections = flattenSections(clientLibsCommonSections)
|
||||
const libraryPath = '/swift/v2'
|
||||
|
||||
export default function SwiftReference(props) {
|
||||
const router = useRouter()
|
||||
const slug = router.query.slug[0]
|
||||
const filteredSection = sections.filter((section) => section.id === slug)
|
||||
|
||||
const pageTitle = filteredSection[0]?.title
|
||||
? `${filteredSection[0]?.title} | Supabase`
|
||||
: 'Supabase'
|
||||
return (
|
||||
<>
|
||||
<RefSEO title={pageTitle} />
|
||||
|
||||
<RefSectionHandler
|
||||
menuId={MenuId.RefSwiftV1}
|
||||
sections={filteredSection}
|
||||
spec={spec}
|
||||
pageProps={props}
|
||||
type="client-lib"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export async function getStaticProps() {
|
||||
return handleRefStaticProps(sections, libraryPath)
|
||||
}
|
||||
|
||||
export async function getStaticPaths() {
|
||||
return handleRefGetStaticPaths(sections)
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
"type": "markdown",
|
||||
"excludes": [
|
||||
"reference_javascript_v1",
|
||||
"reference_kotlin_v1",
|
||||
"reference_swift_v1"
|
||||
]
|
||||
},
|
||||
@@ -999,11 +1000,12 @@
|
||||
"excludes": [
|
||||
"reference_dart_v1",
|
||||
"reference_dart_v2",
|
||||
"reference_javascript_v1",
|
||||
"reference_kotlin_v1",
|
||||
"reference_kotlin_v2",
|
||||
"reference_python_v2",
|
||||
"reference_swift_v1",
|
||||
"reference_swift_v2",
|
||||
"reference_kotlin_v1",
|
||||
"reference_kotlin_v2"
|
||||
"reference_swift_v2"
|
||||
],
|
||||
"items": [
|
||||
{
|
||||
|
||||
@@ -3752,7 +3752,7 @@ functions:
|
||||
with advanced operators.
|
||||
|
||||
- `unquoted text`: text not inside quote marks will be converted to terms separated by & operators, as if processed by plainto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by <-> operators, as if processed by phraseto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by `<->` operators, as if processed by phraseto_tsquery.
|
||||
- `OR`: the word “or” will be converted to the | operator.
|
||||
- `-`: a dash will be converted to the ! operator.
|
||||
|
||||
|
||||
@@ -1584,7 +1584,7 @@ functions:
|
||||
with advanced operators.
|
||||
|
||||
- `unquoted text`: text not inside quote marks will be converted to terms separated by & operators, as if processed by plainto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by <-> operators, as if processed by phraseto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by `<->` operators, as if processed by phraseto_tsquery.
|
||||
- `OR`: the word “or” will be converted to the | operator.
|
||||
- `-`: a dash will be converted to the ! operator.
|
||||
|
||||
|
||||
@@ -1584,7 +1584,7 @@ functions:
|
||||
with advanced operators.
|
||||
|
||||
- `unquoted text`: text not inside quote marks will be converted to terms separated by & operators, as if processed by plainto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by <-> operators, as if processed by phraseto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by `<->` operators, as if processed by phraseto_tsquery.
|
||||
- `OR`: the word “or” will be converted to the | operator.
|
||||
- `-`: a dash will be converted to the ! operator.
|
||||
|
||||
|
||||
@@ -2847,7 +2847,7 @@ functions:
|
||||
with advanced operators.
|
||||
|
||||
- `unquoted text`: text not inside quote marks will be converted to terms separated by & operators, as if processed by plainto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by <-> operators, as if processed by phraseto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by `<->` operators, as if processed by phraseto_tsquery.
|
||||
- `OR`: the word “or” will be converted to the | operator.
|
||||
- `-`: a dash will be converted to the ! operator.
|
||||
|
||||
|
||||
@@ -6667,7 +6667,7 @@ functions:
|
||||
with advanced operators.
|
||||
|
||||
- `unquoted text`: text not inside quote marks will be converted to terms separated by & operators, as if processed by plainto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by <-> operators, as if processed by phraseto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by `<->` operators, as if processed by phraseto_tsquery.
|
||||
- `OR`: the word “or” will be converted to the | operator.
|
||||
- `-`: a dash will be converted to the ! operator.
|
||||
|
||||
|
||||
@@ -238,45 +238,6 @@ functions:
|
||||
- When both **Confirm email** and **Confirm phone** (even when phone provider is disabled) are enabled in [your project](/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
|
||||
- When either **Confirm email** or **Confirm phone** (even when phone provider is disabled) is disabled, the error message, `User already registered` is returned.
|
||||
- To fetch the currently logged-in user, refer to [`getUser()`](/docs/reference/javascript/auth-getuser).
|
||||
overwriteParams:
|
||||
- name: credentials
|
||||
type: SignUpWithPasswordCredentials
|
||||
subContent:
|
||||
- name: email
|
||||
isOptional: true
|
||||
type: string
|
||||
description: One of `email` or `phone` must be provided.
|
||||
- name: phone
|
||||
isOptional: true
|
||||
type: string
|
||||
description: One of `email` or `phone` must be provided.
|
||||
- name: password
|
||||
type: string
|
||||
- name: options
|
||||
isOptional: true
|
||||
type: object
|
||||
subContent:
|
||||
- name: emailRedirectTo
|
||||
isOptional: true
|
||||
type: string
|
||||
description: >
|
||||
Only for email signups.
|
||||
The redirect URL embedded in the email link.
|
||||
Must be a configured redirect URL for your Supabase instance.
|
||||
- name: data
|
||||
isOptional: true
|
||||
type: object
|
||||
description: >
|
||||
A custom data object to store additional user metadata.
|
||||
- name: captchaToken
|
||||
isOptional: true
|
||||
type: string
|
||||
- name: channel
|
||||
isOptional: true
|
||||
type: sms | whatsapp
|
||||
description: >
|
||||
The channel to use for sending messages.
|
||||
Only for phone signups.
|
||||
examples:
|
||||
- id: sign-up
|
||||
name: Sign up with an email and password
|
||||
@@ -710,27 +671,6 @@ functions:
|
||||
- id: sign-in-with-password
|
||||
title: 'signInWithPassword()'
|
||||
$ref: '@supabase/auth-js.GoTrueClient.signInWithPassword'
|
||||
overwriteParams:
|
||||
- name: credentials
|
||||
type: SignInWithPasswordCredentials
|
||||
subContent:
|
||||
- name: email
|
||||
isOptional: true
|
||||
type: string
|
||||
description: One of `email` or `phone` must be provided.
|
||||
- name: phone
|
||||
isOptional: true
|
||||
type: string
|
||||
description: One of `email` or `phone` must be provided.
|
||||
- name: password
|
||||
type: string
|
||||
- name: options
|
||||
isOptional: true
|
||||
type: object
|
||||
subContent:
|
||||
- name: captchaToken
|
||||
isOptional: true
|
||||
type: string
|
||||
notes: |
|
||||
- Requires either an email and password or a phone number and password.
|
||||
examples:
|
||||
@@ -844,49 +784,6 @@ functions:
|
||||
- id: sign-in-with-otp
|
||||
title: 'signInWithOtp()'
|
||||
$ref: '@supabase/auth-js.GoTrueClient.signInWithOtp'
|
||||
overwriteParams:
|
||||
- name: credentials
|
||||
type: SignInWithPasswordlessCredentials
|
||||
subContent:
|
||||
- name: email
|
||||
isOptional: true
|
||||
type: string
|
||||
description: One of `email` or `phone` must be provided.
|
||||
- name: phone
|
||||
isOptional: true
|
||||
type: string
|
||||
description: One of `email` or `phone` must be provided.
|
||||
- name: options
|
||||
isOptional: true
|
||||
type: object
|
||||
subContent:
|
||||
- name: emailRedirectTo
|
||||
isOptional: true
|
||||
type: string
|
||||
description: >
|
||||
Only for email signups.
|
||||
The redirect URL embedded in the email link.
|
||||
Must be a configured redirect URL for your Supabase instance.
|
||||
- name: shouldCreateUser
|
||||
isOptional: true
|
||||
type: boolean
|
||||
description: >
|
||||
Whether to create the user if they don't already exist.
|
||||
Defaults to true.
|
||||
- name: data
|
||||
isOptional: true
|
||||
type: object
|
||||
description: >
|
||||
A custom data object to store additional user metadata.
|
||||
- name: captchaToken
|
||||
isOptional: true
|
||||
type: string
|
||||
- name: channel
|
||||
isOptional: true
|
||||
type: sms | whatsapp
|
||||
description: >
|
||||
The channel to use for sending messages.
|
||||
Only for phone signups.
|
||||
notes: |
|
||||
- Requires either an email or phone number.
|
||||
- This method is used for passwordless sign-ins where a OTP is sent to the user's email or phone number.
|
||||
@@ -1095,33 +992,6 @@ functions:
|
||||
- id: sign-in-with-sso
|
||||
title: 'signInWithSSO()'
|
||||
$ref: '@supabase/auth-js.GoTrueClient.signInWithSSO'
|
||||
overwriteParams:
|
||||
- name: params
|
||||
type: SignInWithSSO
|
||||
subContent:
|
||||
- name: providerId
|
||||
isOptional: true
|
||||
type: string
|
||||
description: >
|
||||
UUID of the SSO provider.
|
||||
One of `providerId` or `domain` is required.
|
||||
- name: domain
|
||||
isOptional: true
|
||||
type: string
|
||||
description: >
|
||||
Domain name of the organization to use SSO with.
|
||||
One of `providerId` or `domain` is required.
|
||||
- name: options
|
||||
isOptional: true
|
||||
type: object
|
||||
subContent:
|
||||
- name: redirectTo
|
||||
type: string
|
||||
description: >
|
||||
The URL to redirect the user to after they have signed in.
|
||||
Must be a configured redirect URL for your Supabase instance.
|
||||
- name: captchaToken
|
||||
type: string
|
||||
notes: |
|
||||
- Before you can call this method you need to [establish a connection](/docs/guides/auth/sso/auth-sso-saml#managing-saml-20-connections) to an identity provider. Use the [CLI commands](/docs/reference/cli/supabase-sso) to do this.
|
||||
- If you've associated an email domain to the identity provider, you can use the `domain` property to start a sign-in flow.
|
||||
@@ -1181,44 +1051,6 @@ functions:
|
||||
- id: verify-otp
|
||||
title: 'verifyOtp()'
|
||||
$ref: '@supabase/auth-js.GoTrueClient.verifyOtp'
|
||||
overwriteParams:
|
||||
- name: params
|
||||
type: VerifyOtpParams
|
||||
subContent:
|
||||
- name: phone
|
||||
isOptional: true
|
||||
type: string
|
||||
description: One of `phone`, `email`, or `token_hash` must be provided.
|
||||
- name: email
|
||||
isOptional: true
|
||||
type: string
|
||||
description: One of `phone`, `email`, or `token_hash` must be provided.
|
||||
- name: token_hash
|
||||
isOptional: true
|
||||
type: string
|
||||
description: >
|
||||
The token hash from the user's email link.
|
||||
One of `phone`, `email`, or `token_hash` must be provided.
|
||||
- name: type
|
||||
type: sms | phone_change | signup | invite | magiclink | recovery | email_change | email
|
||||
- name: token
|
||||
isOptional: true
|
||||
type: string
|
||||
description: The OTP sent to the user. Required if using `phone` or `email`.
|
||||
- name: options
|
||||
isOptional: true
|
||||
type: object
|
||||
subContent:
|
||||
- name: redirectTo
|
||||
isOptional: true
|
||||
type: string
|
||||
description: >
|
||||
A URL to redirect the user to after they are confirmed.
|
||||
Must be in your configured redirect URLs.
|
||||
- name: captchaToken
|
||||
isOptional: true
|
||||
type: string
|
||||
description: Deprecated.
|
||||
notes: |
|
||||
- The `verifyOtp` method takes in different verification types. If a phone number is used, the type can either be `sms` or `phone_change`. If an email address is used, the type can be one of the following: `email`, `recovery`, `invite` or `email_change` (`signup` and `magiclink` types are deprecated).
|
||||
- The verification type used should be determined based on the corresponding auth method called before `verifyOtp` to sign up / sign-in a user.
|
||||
@@ -1953,6 +1785,7 @@ functions:
|
||||
const { session, user } = data
|
||||
```
|
||||
response: |
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"user": {
|
||||
@@ -2051,6 +1884,7 @@ functions:
|
||||
},
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
- id: refresh-session-using-a-passed-in-session
|
||||
name: Refresh session using a refresh token
|
||||
isSpotlight: false
|
||||
@@ -5707,7 +5541,7 @@ functions:
|
||||
with advanced operators.
|
||||
|
||||
- `unquoted text`: text not inside quote marks will be converted to terms separated by & operators, as if processed by plainto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by <-> operators, as if processed by phraseto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by `<->` operators, as if processed by phraseto_tsquery.
|
||||
- `OR`: the word “or” will be converted to the | operator.
|
||||
- `-`: a dash will be converted to the ! operator.
|
||||
|
||||
|
||||
@@ -1707,7 +1707,7 @@ functions:
|
||||
with advanced operators.
|
||||
|
||||
- `unquoted text`: text not inside quote marks will be converted to terms separated by & operators, as if processed by plainto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by <-> operators, as if processed by phraseto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by `<->` operators, as if processed by phraseto_tsquery.
|
||||
- `OR`: the word “or” will be converted to the | operator.
|
||||
- `-`: a dash will be converted to the ! operator.
|
||||
|
||||
|
||||
@@ -2163,7 +2163,7 @@ functions:
|
||||
with advanced operators.
|
||||
|
||||
- `unquoted text`: text not inside quote marks will be converted to terms separated by & operators, as if processed by plainto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by <-> operators, as if processed by phraseto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by `<->` operators, as if processed by phraseto_tsquery.
|
||||
- `OR`: the word “or” will be converted to the | operator.
|
||||
- `-`: a dash will be converted to the ! operator.
|
||||
|
||||
|
||||
@@ -3413,7 +3413,7 @@ functions:
|
||||
with advanced operators.
|
||||
|
||||
- `unquoted text`: text not inside quote marks will be converted to terms separated by & operators, as if processed by plainto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by <-> operators, as if processed by phraseto_tsquery.
|
||||
- `"quoted text"`: text inside quote marks will be converted to terms separated by `<->` operators, as if processed by phraseto_tsquery.
|
||||
- `OR`: the word “or” will be converted to the | operator.
|
||||
- `-`: a dash will be converted to the ! operator.
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"pages/guides/append-test.js",
|
||||
"jest.config.mjs",
|
||||
".next/types/**/*.ts",
|
||||
"./../../packages/ui/src/**/*.d.ts"
|
||||
],
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
export * from './next'
|
||||
|
||||
export type Json = string | number | boolean | { [key: string]: Json } | Json[]
|
||||
|
||||
200
package-lock.json
generated
200
package-lock.json
generated
@@ -988,7 +988,8 @@
|
||||
"unist-util-filter": "^4.0.1",
|
||||
"unist-util-visit": "^4.1.2",
|
||||
"uuid": "^9.0.1",
|
||||
"valtio": "^1.12.0"
|
||||
"valtio": "^1.12.0",
|
||||
"yaml": "^2.4.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@aws-sdk/client-secrets-manager": "^3.410.0",
|
||||
@@ -999,6 +1000,7 @@
|
||||
"@types/unist": "^2.0.6",
|
||||
"acorn": "^8.11.3",
|
||||
"api-types": "*",
|
||||
"cheerio": "^1.0.0-rc.12",
|
||||
"config": "*",
|
||||
"dotenv": "^16.0.3",
|
||||
"ejs": "^3.1.10",
|
||||
@@ -17684,6 +17686,44 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/cheerio": {
|
||||
"version": "1.0.0-rc.12",
|
||||
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
|
||||
"integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cheerio-select": "^2.1.0",
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.0.1",
|
||||
"htmlparser2": "^8.0.1",
|
||||
"parse5": "^7.0.0",
|
||||
"parse5-htmlparser2-tree-adapter": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/cheeriojs/cheerio?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/cheerio-select": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
|
||||
"integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"boolbase": "^1.0.0",
|
||||
"css-select": "^5.1.0",
|
||||
"css-what": "^6.1.0",
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.0.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.5.3",
|
||||
"funding": [
|
||||
@@ -18677,6 +18717,22 @@
|
||||
"hyphenate-style-name": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/css-select": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
||||
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"boolbase": "^1.0.0",
|
||||
"css-what": "^6.1.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"domutils": "^3.0.1",
|
||||
"nth-check": "^2.0.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
},
|
||||
"node_modules/css-to-react-native": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
|
||||
@@ -19415,6 +19471,20 @@
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"entities": "^4.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domelementtype": {
|
||||
"version": "2.3.0",
|
||||
"dev": true,
|
||||
@@ -19437,6 +19507,35 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
||||
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dot-case": {
|
||||
"version": "3.0.4",
|
||||
"dev": true,
|
||||
@@ -23387,6 +23486,25 @@
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/htmlparser2": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
|
||||
"integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
"https://github.com/fb55/htmlparser2?sponsor=1",
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3",
|
||||
"domutils": "^3.0.1",
|
||||
"entities": "^4.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/http-cache-semantics": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
|
||||
@@ -32817,6 +32935,19 @@
|
||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/parse5-htmlparser2-tree-adapter": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
|
||||
"integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"domhandler": "^5.0.2",
|
||||
"parse5": "^7.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/pascal-case": {
|
||||
"version": "3.1.2",
|
||||
"license": "MIT",
|
||||
@@ -38823,22 +38954,6 @@
|
||||
"url": "https://opencollective.com/svgo"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo/node_modules/css-select": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
||||
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"boolbase": "^1.0.0",
|
||||
"css-what": "^6.1.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"domutils": "^3.0.1",
|
||||
"nth-check": "^2.0.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo/node_modules/css-tree": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
@@ -38852,49 +38967,6 @@
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo/node_modules/dom-serializer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"entities": "^4.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo/node_modules/domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo/node_modules/domutils": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
||||
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo/node_modules/mdn-data": {
|
||||
"version": "2.0.30",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||
@@ -42708,8 +42780,12 @@
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.3.2",
|
||||
"license": "ISC",
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz",
|
||||
"integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==",
|
||||
"bin": {
|
||||
"yaml": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user