From a552194fb9c3e24a3566b4663738ea1cfc66e693 Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Tue, 9 Dec 2025 16:33:08 +0200 Subject: [PATCH] docs: manually surface OAuth docs (#41194) --- apps/docs/scripts/find-undocumented.ts | 259 ++++++++++++++++++ apps/docs/scripts/validate-references.ts | 103 +++++++ apps/docs/scripts/validate-typespec-refs.ts | 87 ++++++ .../spec/common-client-libs-sections.json | 126 +++++++++ apps/docs/spec/supabase_js_v2.yml | 72 ++--- 5 files changed, 596 insertions(+), 51 deletions(-) create mode 100644 apps/docs/scripts/find-undocumented.ts create mode 100644 apps/docs/scripts/validate-references.ts create mode 100644 apps/docs/scripts/validate-typespec-refs.ts diff --git a/apps/docs/scripts/find-undocumented.ts b/apps/docs/scripts/find-undocumented.ts new file mode 100644 index 00000000000..c2524cab2a1 --- /dev/null +++ b/apps/docs/scripts/find-undocumented.ts @@ -0,0 +1,259 @@ +/** + * Find methods in typeSpec.json that are NOT documented in supabase_js_v2.yml + * + * Usage: pnpm tsx scripts/find-undocumented.ts + * + * Note: Run `pnpm prebuild` first to generate typeSpec.json + */ + +import { existsSync, readFileSync } from 'fs' +import yaml from 'js-yaml' +import { dirname, join } from 'path' +import { fileURLToPath } from 'url' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const SPEC_DIR = join(__dirname, '../spec') +const GENERATED_DIR = join(__dirname, '../features/docs/generated') + +interface YamlFunction { + id: string + $ref?: string +} + +interface YamlSpec { + functions: YamlFunction[] +} + +interface TypeSpecModule { + name: string + methods: Record +} + +// Same normalization as Reference.typeSpec.ts +function normalizeRefPath(path: string) { + return path.replace(/\.index(?=\.|$)/g, '').replace(/\.+/g, '.') +} + +// Categorize a method path +function categorizeMethod(methodPath: string): 'public' | 'constructor' | 'error' | 'internal' { + const parts = methodPath.split('.') + const methodName = parts[parts.length - 1] + const className = parts[parts.length - 2] + + // Internal/private methods start with _ + if (methodName.startsWith('_')) { + return 'internal' + } + + // Error class constructors + if (className?.endsWith('Error') && methodName === 'constructor') { + return 'error' + } + + // Other constructors + if (methodName === 'constructor') { + return 'constructor' + } + + return 'public' +} + +// Dynamically detect re-exports from typeSpec data +// If a class exists in both @supabase/supabase-js and another @supabase/* package, +// prefer the other package (which is the original source) +function buildReexportMap(typeSpecModules: TypeSpecModule[]): Map { + const classToPackages = new Map>() + + for (const mod of typeSpecModules) { + for (const methodPath of Object.keys(mod.methods)) { + const parts = methodPath.split('.') + const pkg = parts[0] + const className = parts[1] + if (pkg?.startsWith('@supabase/') && className) { + if (!classToPackages.has(className)) { + classToPackages.set(className, new Set()) + } + classToPackages.get(className)!.add(pkg) + } + } + } + + // For classes in multiple packages, map supabase-js to the original package + const reexportMap = new Map() + for (const [className, packages] of classToPackages) { + if (packages.has('@supabase/supabase-js') && packages.size > 1) { + // Find the original package (not supabase-js) + for (const pkg of packages) { + if (pkg !== '@supabase/supabase-js') { + reexportMap.set(className, pkg) + break + } + } + } + } + return reexportMap +} + +// Get the "canonical" path (prefer original package over supabase-js re-exports) +function getCanonicalPath(methodPath: string, reexportMap: Map): string { + if (methodPath.startsWith('@supabase/supabase-js.')) { + const parts = methodPath.split('.') + const className = parts[1] + const originalPkg = reexportMap.get(className) + if (originalPkg) { + return methodPath.replace('@supabase/supabase-js', originalPkg) + } + } + return methodPath +} + +// Check if typeSpec.json exists +const typeSpecPath = join(GENERATED_DIR, 'typeSpec.json') +if (!existsSync(typeSpecPath)) { + console.error('ERROR: typeSpec.json not found!') + console.error('Run `pnpm prebuild` first to generate it.') + process.exit(1) +} + +// Load typeSpec.json and get all method paths +const typeSpecModules: TypeSpecModule[] = JSON.parse(readFileSync(typeSpecPath, 'utf8')) +const allMethods: string[] = [] + +for (const mod of typeSpecModules) { + for (const methodPath of Object.keys(mod.methods)) { + allMethods.push(methodPath) + } +} + +// Build re-export map dynamically from typeSpec data +const reexportMap = buildReexportMap(typeSpecModules) + +// Load YAML and get all documented $ref values (normalized) +const yamlPath = join(SPEC_DIR, 'supabase_js_v2.yml') +const spec = yaml.load(readFileSync(yamlPath, 'utf8')) as YamlSpec + +const documentedRefs = new Set() +for (const fn of spec.functions) { + if (fn.$ref) { + // Store both raw and normalized versions + documentedRefs.add(fn.$ref) + documentedRefs.add(normalizeRefPath(fn.$ref)) + } +} + +// Find undocumented methods and deduplicate +const seenCanonical = new Set() +const undocumented: string[] = [] + +for (const method of allMethods) { + // Get canonical path first (prefer original packages over supabase-js re-exports) + const canonical = getCanonicalPath(method, reexportMap) + + // Skip if already processed this canonical path + if (seenCanonical.has(canonical)) { + continue + } + seenCanonical.add(canonical) + + // Check if documented - check both raw path AND canonical path + if ( + documentedRefs.has(method) || + documentedRefs.has(normalizeRefPath(method)) || + documentedRefs.has(canonical) || + documentedRefs.has(normalizeRefPath(canonical)) + ) { + continue + } + + // Use canonical path for output + undocumented.push(canonical) +} + +// Categorize +const publicMethods: string[] = [] +const constructors: string[] = [] +const errorConstructors: string[] = [] +const internalMethods: string[] = [] + +for (const method of undocumented) { + switch (categorizeMethod(method)) { + case 'public': + publicMethods.push(method) + break + case 'constructor': + constructors.push(method) + break + case 'error': + errorConstructors.push(method) + break + case 'internal': + internalMethods.push(method) + break + } +} + +// Group public methods by package +const publicByPackage = new Map() +for (const method of publicMethods) { + const pkg = method.split('.')[0] + if (!publicByPackage.has(pkg)) { + publicByPackage.set(pkg, []) + } + publicByPackage.get(pkg)!.push(method) +} + +// Output public methods (the interesting ones) +console.log('╔═══════════════════════════════════════════════════════════════╗') +console.log('║ UNDOCUMENTED PUBLIC METHODS ║') +console.log('╚═══════════════════════════════════════════════════════════════╝') + +for (const [pkg, methods] of publicByPackage) { + console.log(`\n=== ${pkg} ===`) + methods.sort().forEach((m) => { + const shortName = m.replace(pkg + '.', '') + console.log(` - ${shortName}`) + }) +} + +// Summary sections for other categories +console.log('\n╔═══════════════════════════════════════════════════════════════╗') +console.log('║ SKIPPED CATEGORIES ║') +console.log('╚═══════════════════════════════════════════════════════════════╝') + +console.log(`\n[Constructors] (${constructors.length} items)`) +constructors + .sort() + .slice(0, 5) + .forEach((m) => console.log(` - ${m}`)) +if (constructors.length > 5) console.log(` ... and ${constructors.length - 5} more`) + +console.log(`\n[Error Constructors] (${errorConstructors.length} items)`) +errorConstructors + .sort() + .slice(0, 5) + .forEach((m) => console.log(` - ${m}`)) +if (errorConstructors.length > 5) console.log(` ... and ${errorConstructors.length - 5} more`) + +console.log(`\n[Internal/Private Methods] (${internalMethods.length} items)`) +internalMethods + .sort() + .slice(0, 5) + .forEach((m) => console.log(` - ${m}`)) +if (internalMethods.length > 5) console.log(` ... and ${internalMethods.length - 5} more`) + +// Final summary +const totalUndocumented = undocumented.length +const documentedCount = allMethods.length - totalUndocumented +const percentage = ((documentedCount / allMethods.length) * 100).toFixed(1) + +console.log('\n╔═══════════════════════════════════════════════════════════════╗') +console.log('║ SUMMARY ║') +console.log('╚═══════════════════════════════════════════════════════════════╝') +console.log(` Total methods in TypeSpec: ${allMethods.length}`) +console.log(` Documented in YAML: ${documentedCount}`) +console.log(` Undocumented (deduplicated): ${totalUndocumented}`) +console.log(` - Public methods: ${publicMethods.length} ← focus here`) +console.log(` - Constructors: ${constructors.length}`) +console.log(` - Error constructors: ${errorConstructors.length}`) +console.log(` - Internal methods: ${internalMethods.length}`) +console.log(` Coverage: ${percentage}%`) diff --git a/apps/docs/scripts/validate-references.ts b/apps/docs/scripts/validate-references.ts new file mode 100644 index 00000000000..aeb411d8a9e --- /dev/null +++ b/apps/docs/scripts/validate-references.ts @@ -0,0 +1,103 @@ +/** + * Cross-check IDs between common-client-libs-sections.json and supabase_js_v2.yml + * + * Reports: + * 1. Functions in sections but NOT in YAML + * 2. Groups (isFunc: false) in sections but NOT in YAML + * 3. IDs in YAML but NOT in sections + * + * Usage: pnpm tsx scripts/validate-references.ts + */ + +import { readFileSync } from 'fs' +import yaml from 'js-yaml' +import { dirname, join } from 'path' +import { fileURLToPath } from 'url' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const SPEC_DIR = join(__dirname, '../spec') + +interface Section { + id?: string + type: string + isFunc?: boolean + items?: Section[] +} + +interface YamlSpec { + functions: Array<{ id: string }> +} + +// Flatten sections, extracting all function-type entries +function flattenSections(sections: Section[]): { functions: string[]; groups: string[] } { + const functions: string[] = [] + const groups: string[] = [] + + function recurse(items: Section[]) { + for (const item of items) { + if (item.type === 'function' && item.id) { + if (item.isFunc === false) { + groups.push(item.id) + } else { + functions.push(item.id) + } + } + if (item.items) { + recurse(item.items) + } + } + } + + recurse(sections) + return { functions, groups } +} + +// Main +const sectionsPath = join(SPEC_DIR, 'common-client-libs-sections.json') +const yamlPath = join(SPEC_DIR, 'supabase_js_v2.yml') + +const sections: Section[] = JSON.parse(readFileSync(sectionsPath, 'utf8')) +const spec = yaml.load(readFileSync(yamlPath, 'utf8')) as YamlSpec + +const { functions: sectionFunctions, groups: sectionGroups } = flattenSections(sections) +const yamlIds = new Set(spec.functions.map((f) => f.id)) + +const sectionFunctionSet = new Set(sectionFunctions) +const sectionGroupSet = new Set(sectionGroups) +const allSectionIds = new Set([...sectionFunctions, ...sectionGroups]) + +// Find mismatches +const functionsNotInYaml = sectionFunctions.filter((id) => !yamlIds.has(id)) +const groupsNotInYaml = sectionGroups.filter((id) => !yamlIds.has(id)) +const yamlNotInSections = [...yamlIds].filter((id) => !allSectionIds.has(id)) + +// Output +console.log('=== Functions in sections but NOT in YAML ===') +if (functionsNotInYaml.length === 0) { + console.log('(none)') +} else { + functionsNotInYaml.forEach((id) => console.log(`- ${id}`)) +} + +console.log('\n=== Groups (isFunc: false) in sections but NOT in YAML ===') +if (groupsNotInYaml.length === 0) { + console.log('(none)') +} else { + groupsNotInYaml.forEach((id) => console.log(`- ${id}`)) +} + +console.log('\n=== IDs in YAML but NOT in sections ===') +if (yamlNotInSections.length === 0) { + console.log('(none)') +} else { + yamlNotInSections.forEach((id) => console.log(`- ${id}`)) +} + +console.log( + `\nSummary: ${functionsNotInYaml.length} functions missing, ${groupsNotInYaml.length} groups missing, ${yamlNotInSections.length} orphaned` +) + +// Exit with error code if any mismatches +if (functionsNotInYaml.length > 0 || groupsNotInYaml.length > 0 || yamlNotInSections.length > 0) { + process.exit(1) +} diff --git a/apps/docs/scripts/validate-typespec-refs.ts b/apps/docs/scripts/validate-typespec-refs.ts new file mode 100644 index 00000000000..3184056dec3 --- /dev/null +++ b/apps/docs/scripts/validate-typespec-refs.ts @@ -0,0 +1,87 @@ +/** + * Validate that YAML $ref values exist in typeSpec.json + * + * Checks: supabase_js_v2.yml $ref → typeSpec.json (generated from combined.json) + * + * Usage: pnpm tsx scripts/validate-typespec-refs.ts + * + * Note: Run `pnpm prebuild` first to generate typeSpec.json + */ + +import { existsSync, readFileSync } from 'fs' +import yaml from 'js-yaml' +import { dirname, join } from 'path' +import { fileURLToPath } from 'url' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const SPEC_DIR = join(__dirname, '../spec') +const GENERATED_DIR = join(__dirname, '../features/docs/generated') + +interface YamlFunction { + id: string + $ref?: string +} + +interface YamlSpec { + functions: YamlFunction[] +} + +interface TypeSpecModule { + name: string + methods: Record +} + +// Same normalization as Reference.typeSpec.ts +function normalizeRefPath(path: string) { + return path.replace(/\.index(?=\.|$)/g, '').replace(/\.+/g, '.') +} + +// Check if typeSpec.json exists +const typeSpecPath = join(GENERATED_DIR, 'typeSpec.json') +if (!existsSync(typeSpecPath)) { + console.error('ERROR: typeSpec.json not found!') + console.error('Run `pnpm prebuild` first to generate it.') + process.exit(1) +} + +// Load typeSpec.json and extract all valid method paths +const typeSpecModules: TypeSpecModule[] = JSON.parse(readFileSync(typeSpecPath, 'utf8')) +const validRefs = new Set() + +for (const mod of typeSpecModules) { + for (const methodPath of Object.keys(mod.methods)) { + validRefs.add(methodPath) + } +} + +// Load YAML and extract $ref values +const yamlPath = join(SPEC_DIR, 'supabase_js_v2.yml') +const spec = yaml.load(readFileSync(yamlPath, 'utf8')) as YamlSpec + +const yamlRefs: Array<{ id: string; ref: string }> = [] +for (const fn of spec.functions) { + if (fn.$ref) { + yamlRefs.push({ id: fn.id, ref: fn.$ref }) + } +} + +// Find invalid refs - check both raw and normalized (matches runtime behavior) +const invalidRefs = yamlRefs.filter( + ({ ref }) => !validRefs.has(ref) && !validRefs.has(normalizeRefPath(ref)) +) +const validCount = yamlRefs.length - invalidRefs.length + +// Output +console.log('=== YAML $ref NOT found in TypeSpec ===') +if (invalidRefs.length === 0) { + console.log('(none)') +} else { + invalidRefs.forEach(({ id, ref }) => console.log(`- ${ref} (id: ${id})`)) +} + +console.log(`\n=== Valid refs: ${validCount} | Invalid refs: ${invalidRefs.length} ===`) + +// Exit with error code if any invalid refs +if (invalidRefs.length > 0) { + process.exit(1) +} diff --git a/apps/docs/spec/common-client-libs-sections.json b/apps/docs/spec/common-client-libs-sections.json index 2479dd98699..d1fd284e799 100644 --- a/apps/docs/spec/common-client-libs-sections.json +++ b/apps/docs/spec/common-client-libs-sections.json @@ -658,6 +658,13 @@ "product": "auth", "type": "function" }, + { + "id": "auth-js-gotrueclient-initialize", + "title": "Initialize client session", + "slug": "auth-initialize", + "product": "auth", + "type": "function" + }, { "id": "auth-mfa-api", "title": "Auth MFA", @@ -705,6 +712,58 @@ "slug": "auth-mfa-getauthenticatorassurancelevel", "product": "auth", "type": "function" + }, + { + "id": "auth-js-gotruemfaapi-listfactors", + "title": "List all factors for current user", + "slug": "auth-mfa-listfactors", + "product": "auth", + "type": "function" + } + ] + }, + { + "id": "oauth-server-api", + "isFunc": false, + "title": "OAuth Server", + "slug": "auth-admin-oauth-server", + "product": "auth-admin", + "type": "function", + "items": [ + { + "id": "auth-js-authoauthserverapi-getauthorizationdetails", + "title": "Get authorization details", + "slug": "auth-admin-oauth-getauthorizationdetails", + "product": "auth-admin", + "type": "function" + }, + { + "id": "auth-js-authoauthserverapi-approveauthorization", + "title": "Approve authorization", + "slug": "auth-admin-oauth-approveauthorization", + "product": "auth-admin", + "type": "function" + }, + { + "id": "auth-js-authoauthserverapi-denyauthorization", + "title": "Deny authorization", + "slug": "auth-admin-oauth-denyauthorization", + "product": "auth-admin", + "type": "function" + }, + { + "id": "auth-js-authoauthserverapi-listgrants", + "title": "List grants", + "slug": "auth-admin-oauth-listgrants", + "product": "auth-admin", + "type": "function" + }, + { + "id": "auth-js-authoauthserverapi-revokegrant", + "title": "Revoke grant", + "slug": "auth-admin-oauth-revokegrant", + "product": "auth-admin", + "type": "function" } ] }, @@ -770,6 +829,13 @@ "product": "auth-admin", "type": "function" }, + { + "id": "auth-js-gotrueadminapi-signout", + "title": "Sign out a user (admin)", + "slug": "auth-admin-signout", + "product": "auth-admin", + "type": "function" + }, { "id": "mfa-list-factors", "title": "List all factors for a user", @@ -783,6 +849,66 @@ "slug": "auth-admin-mfa-deletefactor", "product": "auth-admin", "type": "function" + }, + { + "id": "mfa-list-factors-admin", + "title": "List all factors for a user (admin)", + "slug": "auth-admin-mfa-listfactors-admin", + "product": "auth-admin", + "type": "function" + }, + + { + "id": "oauth-admin-api", + "isFunc": false, + "title": "OAuth Admin", + "slug": "auth-admin-oauth-admin", + "product": "auth-admin", + "type": "function", + "items": [ + { + "id": "auth-js-gotrueadminoauthapi-listclients", + "title": "List OAuth clients", + "slug": "auth-admin-oauth-listclients", + "product": "auth-admin", + "type": "function" + }, + { + "id": "auth-js-gotrueadminoauthapi-getclient", + "title": "Get OAuth client", + "slug": "auth-admin-oauth-getclient", + "product": "auth-admin", + "type": "function" + }, + { + "id": "auth-js-gotrueadminoauthapi-createclient", + "title": "Create OAuth client", + "slug": "auth-admin-oauth-createclient", + "product": "auth-admin", + "type": "function" + }, + { + "id": "auth-js-gotrueadminoauthapi-updateclient", + "title": "Update OAuth client", + "slug": "auth-admin-oauth-updateclient", + "product": "auth-admin", + "type": "function" + }, + { + "id": "auth-js-gotrueadminoauthapi-deleteclient", + "title": "Delete OAuth client", + "slug": "auth-admin-oauth-deleteclient", + "product": "auth-admin", + "type": "function" + }, + { + "id": "auth-js-gotrueadminoauthapi-regenerateclientsecret", + "title": "Regenerate client secret", + "slug": "auth-admin-oauth-regenerateclientsecret", + "product": "auth-admin", + "type": "function" + } + ] } ] } diff --git a/apps/docs/spec/supabase_js_v2.yml b/apps/docs/spec/supabase_js_v2.yml index 3f2c41adf92..cb6e3556d97 100644 --- a/apps/docs/spec/supabase_js_v2.yml +++ b/apps/docs/spec/supabase_js_v2.yml @@ -2254,6 +2254,8 @@ functions: "error": null } ``` + - id: auth-js-gotrueclient-initialize + $ref: '@supabase/auth-js.GoTrueClient.initialize' - id: auth-mfa-api title: 'Overview' notes: | @@ -3225,8 +3227,9 @@ functions: { ban_duration: '100y' } ) ``` + - id: auth-js-gotrueadminapi-signout + $ref: '@supabase/auth-js.GoTrueAdminApi.signOut' - id: mfa-list-factors-admin - title: 'mfa.listFactors()' $ref: '@supabase/auth-js.GoTrueAdminMFAApi.listFactors' examples: - id: list-factors @@ -7645,78 +7648,45 @@ functions: - id: supabase-js-supabaseclient-schema title: SupabaseClient.schema() $ref: '@supabase/supabase-js.SupabaseClient.schema' - - id: auth-js-gotrueadminapi-constructor - title: new GoTrueAdminApi() - $ref: '@supabase/auth-js.GoTrueAdminApi.constructor' - examples: - - id: auth-js-gotrueadminapi-constructor-example-1 - name: Example 1 - code: |- - ```ts - import { GoTrueAdminApi } from '@supabase/auth-js' + - id: oauth-server-api + title: 'OAuth Server API' + notes: | + The OAuth Server API allows you to build custom OAuth consent screens for your application. + Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. - const admin = new GoTrueAdminApi({ - url: 'https://xyzcompany.supabase.co/auth/v1', - headers: { Authorization: `Bearer ${process.env.SUPABASE_SERVICE_ROLE_KEY}` }, - }) - ``` - - id: auth-js-gotrueadminapi-signout - title: GoTrueAdminApi.signOut() - $ref: '@supabase/auth-js.GoTrueAdminApi.signOut' - - id: auth-js-gotrueclient-constructor - title: new GoTrueClient() - $ref: '@supabase/auth-js.GoTrueClient.constructor' - examples: - - id: auth-js-gotrueclient-constructor-example-1 - name: Example 1 - code: |- - ```ts - import { GoTrueClient } from '@supabase/auth-js' - - const auth = new GoTrueClient({ - url: 'https://xyzcompany.supabase.co/auth/v1', - headers: { apikey: 'public-anon-key' }, - storageKey: 'supabase-auth', - }) - ``` - - id: auth-js-gotrueclient-initialize - title: GoTrueClient.initialize() - $ref: '@supabase/auth-js.GoTrueClient.initialize' - - id: auth-js-gotrueclient-isthrowonerrorenabled - title: GoTrueClient.isThrowOnErrorEnabled() - $ref: '@supabase/auth-js.GoTrueClient.isThrowOnErrorEnabled' - id: auth-js-authoauthserverapi-approveauthorization - title: AuthOAuthServerApi.approveAuthorization() $ref: '@supabase/auth-js.AuthOAuthServerApi.approveAuthorization' - id: auth-js-authoauthserverapi-denyauthorization - title: AuthOAuthServerApi.denyAuthorization() $ref: '@supabase/auth-js.AuthOAuthServerApi.denyAuthorization' - id: auth-js-authoauthserverapi-getauthorizationdetails - title: AuthOAuthServerApi.getAuthorizationDetails() $ref: '@supabase/auth-js.AuthOAuthServerApi.getAuthorizationDetails' + - id: auth-js-authoauthserverapi-listgrants + $ref: '@supabase/auth-js.AuthOAuthServerApi.listGrants' + - id: auth-js-authoauthserverapi-revokegrant + title: AuthOAuthServerApi.revokeGrant() + $ref: '@supabase/auth-js.AuthOAuthServerApi.revokeGrant' + - id: oauth-admin-api + title: 'OAuth Admin API' + notes: | + The OAuth Admin API allows you to manage OAuth clients programmatically. + Only relevant when the OAuth 2.1 server is enabled in Supabase Auth. + These functions should only be called on a server. Never expose your `service_role` key in the browser. + - id: auth-js-gotrueadminoauthapi-createclient - title: GoTrueAdminOAuthApi.createClient() $ref: '@supabase/auth-js.GoTrueAdminOAuthApi.createClient' - id: auth-js-gotrueadminoauthapi-deleteclient - title: GoTrueAdminOAuthApi.deleteClient() $ref: '@supabase/auth-js.GoTrueAdminOAuthApi.deleteClient' - id: auth-js-gotrueadminoauthapi-getclient - title: GoTrueAdminOAuthApi.getClient() $ref: '@supabase/auth-js.GoTrueAdminOAuthApi.getClient' - id: auth-js-gotrueadminoauthapi-listclients - title: GoTrueAdminOAuthApi.listClients() $ref: '@supabase/auth-js.GoTrueAdminOAuthApi.listClients' - id: auth-js-gotrueadminoauthapi-regenerateclientsecret - title: GoTrueAdminOAuthApi.regenerateClientSecret() $ref: '@supabase/auth-js.GoTrueAdminOAuthApi.regenerateClientSecret' - id: auth-js-gotrueadminoauthapi-updateclient - title: GoTrueAdminOAuthApi.updateClient() $ref: '@supabase/auth-js.GoTrueAdminOAuthApi.updateClient' - id: auth-js-gotruemfaapi-listfactors - title: GoTrueMFAApi.listFactors() $ref: '@supabase/auth-js.GoTrueMFAApi.listFactors' - id: postgrest-js-postgrestbuilder-constructor - title: new PostgrestBuilder() $ref: '@supabase/postgrest-js.PostgrestBuilder.constructor' examples: - id: postgrest-js-postgrestbuilder-constructor-example-1