Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4760c1a
feat(studio): polish PrivateLink connection UI (#49164)
dnywh Aug 18, 2026
b6e4331
Reorganize explorer folder structure (#49172)
joshenlim Aug 18, 2026
5f9ce72
fix(ui): systematise control surface fills and hover borders (#48887)
dnywh Aug 18, 2026
7adc83e
Implement run notebook functionality (#49178)
joshenlim Aug 18, 2026
6073c7a
[FE-4193] fix(studio): show proper names for custom identity provider…
alaister Aug 18, 2026
cb8a609
feat(studio): focus column name input after adding a column (#49183)
dnywh Aug 18, 2026
81507e3
fix(Search): Add server sources for search (#49148)
jeremenichelli Aug 18, 2026
c80f8ad
chore(studio): upgrade AI SDK to v7 (#49167)
joshenlim Aug 18, 2026
e0ee774
fix(docs): point the Management API nav entry at the Management API r…
ayaangazali Aug 18, 2026
dbb1530
feat(studio): cleaned up view permissions sheet (#49144)
kemaldotearth Aug 18, 2026
2440b06
fix(docs/oauth-server): add `plain` for code_challenge_method (#49180)
cemalkilic Aug 18, 2026
ce27b4e
chore(studio): scoped pat mcp tool ui improvement (#49188)
kemaldotearth Aug 18, 2026
fff5928
fix: scroll to required fields on support form submit (#49147)
monicakh Aug 18, 2026
9587eee
Fix TS issue (#49187)
joshenlim Aug 18, 2026
7107a22
docs(functions): update Pro and Team function limits (#49173)
satyarohith Aug 18, 2026
071a071
feat(studio): add NotebookPreview diff component (#49112)
charislam Aug 18, 2026
0a677ac
feat(www): add client-side trace propagation to Logs & Analytics (#49…
ana1337x Aug 18, 2026
79fbe46
feat(studio): wire notebook create/update proposals into assistant pa…
charislam Aug 18, 2026
ce2ed77
Add clickhouse migration banner to QueryEditor (#49184)
joshenlim Aug 18, 2026
8c409e2
Fix eval scorer truncation via local transcript capture (#49151)
charislam Aug 18, 2026
cb93942
blog(www): connect client traces to your logs (#49200)
ana1337x Aug 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3365,7 +3365,7 @@ export const reference = {
},
{
name: 'Management API',
url: '/reference/javascript',
url: '/reference/api/introduction',
icon: '/img/icons/menu/reference-api' as `/${string}`,
},
],
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/guides/auth/oauth-server/oauth-flows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ https://<project-ref>.supabase.co/auth/v1/oauth/authorize?
| `client_id` | The client ID from registration |
| `redirect_uri` | Must exactly match a registered redirect URI |
| `code_challenge` | The generated code challenge |
| `code_challenge_method` | Must be `S256` (SHA-256) |
| `code_challenge_method` | `S256` (SHA-256, recommended) or `plain` |

#### Optional parameters

Expand Down
4 changes: 2 additions & 2 deletions apps/docs/content/guides/functions/limits.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ subtitle: "Limits applied Edge Functions in Supabase's hosted platform."
- Maximum Function Size: 20MB (bundled locally via the CLI) or 5MB (bundled server-side, e.g. via the Management API or Dashboard)
- Maximum no. of Functions per project:
- Free: 100
- Pro: 500
- Team: 1000
- Pro: 1000
- Team: 2000
- Enterprise: Unlimited
- Maximum log message length: 10,000 characters
- Log event threshold: 100 events per 10 seconds
Expand Down
14 changes: 14 additions & 0 deletions apps/docs/scripts/search/sources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ export async function fetchJsLibReferenceSource() {
})
}

export async function fetchServerLibReferenceSource() {
// Server SDK is driven by the new reference pipeline. Ingest search sources
// from the generated `content/reference/server/v1/` outputs so embeddings
// never drift from what the renderer shows.
return loadClientLibReferenceFromNewPipeline({
source: 'server-lib',
path: '/reference/server',
meta: { title: 'Server Reference', language: 'TypeScript' },
contentDir: 'content/reference/server/v1',
})
}

export async function fetchDartLibReferenceSource() {
// Dart v2 is driven by the new reference pipeline. Ingest search sources from
// the generated `content/reference/dart/v2/` outputs so embeddings never
Expand Down Expand Up @@ -132,6 +144,7 @@ export async function fetchAllSources(fullIndex: boolean) {
const lintWarningsGuideSources = fetchLintWarningsGuideSources()
const openApiReferenceSource = fetchOpenApiReferenceSource()
const jsLibReferenceSource = fetchJsLibReferenceSource()
const serverLibReferenceSource = fullIndex ? fetchServerLibReferenceSource() : []
const dartLibReferenceSource = fullIndex ? fetchDartLibReferenceSource() : []
const pythonLibReferenceSource = fullIndex ? fetchPythonLibReferenceSource() : []
const cSharpLibReferenceSource = fullIndex ? fetchCSharpLibReferenceSource() : []
Expand Down Expand Up @@ -165,6 +178,7 @@ export async function fetchAllSources(fullIndex: boolean) {
lintWarningsGuideSources,
openApiReferenceSource,
jsLibReferenceSource,
serverLibReferenceSource,
dartLibReferenceSource,
pythonLibReferenceSource,
cSharpLibReferenceSource,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { describe, expect, it } from 'vitest'

import {
computeOverallRisk,
countConfigured,
getCatalogEntry,
scopesToSelection,
Expand Down Expand Up @@ -145,37 +144,6 @@ describe('selectionToScopes', () => {
})
})

describe('computeOverallRisk', () => {
it('is Minimal with no capabilities', () => {
expect(computeOverallRisk({}, 'project').level).toBe('Minimal')
})

it('account-level read-only is still Elevated', () => {
const risk = computeOverallRisk({ 'project:advisors': 'read' }, 'account')
expect(risk.level).toBe('Elevated')
expect(risk.tone).toBe('medium')
})

it('account-level with any write is High', () => {
const risk = computeOverallRisk({ 'project:realtime_config': 'readwrite' }, 'account')
expect(risk.level).toBe('High')
})

it('project high-risk write is High', () => {
expect(computeOverallRisk({ 'project:database': 'readwrite' }, 'project').level).toBe('High')
})

it('project medium write is Medium', () => {
expect(computeOverallRisk({ 'project:realtime_config': 'readwrite' }, 'project').level).toBe(
'Medium'
)
})

it('read-only project is Low', () => {
expect(computeOverallRisk({ 'project:database': 'read' }, 'project').level).toBe('Low')
})
})

describe('countConfigured', () => {
it('counts only non-none modes', () => {
expect(countConfigured({ a: 'read', b: 'none', c: 'readwrite' })).toBe(2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -629,72 +629,10 @@ export const PERMISSION_MODE_LABEL: Record<PermissionMode, string> = {
readwrite: 'Read-write',
}

export const RISK_DOT_CLASS: Record<RiskLevel, string> = {
low: 'bg-brand-600',
medium: 'bg-warning-600',
high: 'bg-destructive-600',
}

export type ResourceAccessMode = 'project' | 'organization' | 'account'

export interface OverallRisk {
/** Minimal | Low | Medium | Elevated | High */
level: string
text: string
tone: 'default' | 'low' | 'medium' | 'high'
}

export const RISK_TONE_VARIANT: Record<
OverallRisk['tone'],
'default' | 'success' | 'warning' | 'destructive'
> = {
default: 'default',
export const RISK_TONE_VARIANT: Record<RiskLevel, 'success' | 'warning' | 'destructive'> = {
low: 'success',
medium: 'warning',
high: 'destructive',
}

/**
* Computes the overall token risk from the selected capabilities and the resource-access breadth.
* Account-level tokens are never below "Elevated", even when read-only.
*/
export const computeOverallRisk = (
selection: PermissionSelection,
resourceAccess: ResourceAccessMode
): OverallRisk => {
const active = Object.entries(selection).filter(([, mode]) => mode !== 'none')
if (active.length === 0) {
return { level: 'Minimal', text: 'Minimal — No capabilities', tone: 'default' }
}

const anyWrite = active.some(([, mode]) => mode === 'readwrite')
const anyHighWrite = active.some(
([key, mode]) => mode === 'readwrite' && CATALOG_BY_KEY.get(key)?.risk === 'high'
)

const scopeWord =
resourceAccess === 'account'
? 'Account-wide'
: resourceAccess === 'organization'
? 'Organization-wide'
: 'Single-project'
const accessWord = anyWrite ? 'read-write' : 'read-only'

let level: string
let tone: OverallRisk['tone']
if (resourceAccess === 'account') {
level = anyWrite ? 'High' : 'Elevated'
tone = anyWrite ? 'high' : 'medium'
} else if (anyHighWrite) {
level = 'High'
tone = 'high'
} else if (anyWrite) {
level = 'Medium'
tone = 'medium'
} else {
level = 'Low'
tone = 'low'
}

return { level, text: `${level} — ${scopeWord} ${accessWord} access`, tone }
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { ChevronRight } from 'lucide-react'
import { ChevronRight, X } from 'lucide-react'
import { useEffect, useRef, useState } from 'react'
import { useForm, useWatch } from 'react-hook-form'
import { toast } from 'sonner'
import { Button, Form, ScrollArea, Separator, SheetClose, SheetFooter } from 'ui'
import {
Button,
Form,
InfoIcon,
Popover,
PopoverAnchor,
PopoverContent,
ScrollArea,
Separator,
SheetClose,
SheetFooter,
} from 'ui'
import { Admonition } from 'ui-patterns/Admonition'

import { CLASSIC_TOKEN_WARNING } from '../../AccessToken.constants'
Expand Down Expand Up @@ -46,6 +57,8 @@ export const NewScopedTokenForm = ({
})
const [step, setStep] = useState<'form' | 'review'>('form')
const [formValues, setFormValues] = useState<TokenFormValues>(DEFAULT_VALUES)
// Dismissal sticks for the sheet's lifetime, so bouncing between steps doesn't resurface it.
const [isCreateHintDismissed, setIsCreateHintDismissed] = useState(false)
const [showMissingPermissionsWarning, setShowMissingPermissionsWarning] = useState(false)
const resourceSectionRef = useRef<HTMLDivElement>(null)
const resourceAccess = useWatch({ control: form.control, name: 'resourceAccess' })
Expand Down Expand Up @@ -111,7 +124,10 @@ export const NewScopedTokenForm = ({

return (
<>
<ScrollArea className="flex-1">
{/* Radix wraps viewport children in an inline-styled display:table div that grows to fit
the widest child, which would let one long endpoint path expand the sheet instead of
clipping — force it back to block so widths are bounded and rows can truncate. */}
<ScrollArea className="flex-1 [&>[data-radix-scroll-area-viewport]>div]:block!">
{step === 'form' ? (
<Form {...form}>
<form id={FORM_ID} onSubmit={form.handleSubmit(handleReviewAccess)}>
Expand Down Expand Up @@ -154,7 +170,6 @@ export const NewScopedTokenForm = ({
<PermissionsAccordion
selection={selection}
onChange={handlePermissionChange}
permissionScopeMap={permissionScopeMap}
access={access}
/>
{showMissingPermissionsWarning && (
Expand Down Expand Up @@ -187,16 +202,11 @@ export const NewScopedTokenForm = ({
) : (
<StepIndicator step={step === 'form' ? 1 : 2} total={2} label="Configure" />
)}
<div className="flex items-center gap-3">
<div className="flex items-center gap-2">
{step === 'review' && (
<>
<span className="text-xs text-foreground-lighter">
Access can't be changed after creation
</span>
<Button variant="default" disabled={isPending} onClick={() => setStep('form')}>
Back
</Button>
</>
<Button variant="default" disabled={isPending} onClick={() => setStep('form')}>
Back
</Button>
)}
<SheetClose asChild disabled={isPending}>
<Button variant="default">Cancel</Button>
Expand All @@ -212,9 +222,35 @@ export const NewScopedTokenForm = ({
</Button>
)}
{step === 'review' && (
<Button loading={isPending} onClick={() => onCreateToken(formValues)}>
Create token
</Button>
<Popover open={!isCreateHintDismissed}>
<PopoverAnchor asChild>
<Button loading={isPending} onClick={() => onCreateToken(formValues)}>
Create token
</Button>
</PopoverAnchor>
<PopoverContent
side="top"
align="end"
sideOffset={8}
className="flex w-auto items-center gap-2 py-1.5 pl-3 pr-1.5"
onOpenAutoFocus={(event) => event.preventDefault()}
onInteractOutside={() => setIsCreateHintDismissed(true)}
onEscapeKeyDown={() => setIsCreateHintDismissed(true)}
>
<InfoIcon className="h-5 w-5 shrink-0" />
<p className="text-xs text-foreground-light">
Access can't be changed after creation
</p>
<Button
variant="text"
size="tiny"
icon={<X />}
aria-label="Dismiss"
className="px-1 text-foreground-lighter"
onClick={() => setIsCreateHintDismissed(true)}
/>
</PopoverContent>
</Popover>
)}
</div>
</SheetFooter>
Expand Down
Loading
Loading