mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
feat: new marketplace db (#44574)
This PR integrates with the new marketplace db to allow Grafana (and other partners) OAuth apps to install from the integrations page. A demo of this working locally is available here: https://supabase.slack.com/archives/C01GN60J0BS/p1775551752479709. End to end flow is documented here: https://www.notion.so/supabase/Grafana-Integration-Flow-33a5004b775f80eeaf91c098beb8071f. TODO: - [ ] Make sure `NEXT_PUBLIC_MARKETPLACE_API_URL` variable is set to the new marketplace db. - [x] Test with the `marketplaceIntegrations` enabled and disabled in staging once https://github.com/supabase/platform/pull/31298 is merged and available in staging. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Add OAuth "Install integration" button that detects installed integrations and supports GET/POST install flows * Marketplace listings now include install links, installation method, partner info, and listing assets/logos * **Infrastructure** * Allow marketplace API origin for images and content in security and image config * Centralize marketplace types and switch marketplace data source for more reliable listings <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
1 parent
1cb548b2dc
commit
0cb71a2497
15 files changed
+651
-553
No files matched your search
+35
-20
@@ -1,28 +1,43 @@
|
||||
import { useState } from 'react'
|
||||
import { cn } from 'ui'
|
||||
import { cn, Dialog, DialogContent } from 'ui'
|
||||
|
||||
export const FilesViewer = ({ files }: { files: string[] }) => {
|
||||
const [selected, setSelected] = useState(files[0])
|
||||
const [showDialog, setShowDialog] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-4">
|
||||
<img alt={selected} src={selected} className="rounded-md border" />
|
||||
{files.length > 1 && (
|
||||
<div className="grid grid-cols-10 gap-x-2">
|
||||
{files.map((x) => (
|
||||
<button key={x} onClick={() => setSelected(x)}>
|
||||
<img
|
||||
alt={x}
|
||||
src={x}
|
||||
className={cn(
|
||||
'col-span-1 bg-surface-100 rounded-md object-cover aspect-square border transition',
|
||||
selected === x ? 'border-stronger' : 'border-secondary'
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<>
|
||||
<div className="flex flex-col gap-y-4">
|
||||
<button onClick={() => setShowDialog(true)}>
|
||||
<img
|
||||
alt={selected}
|
||||
src={selected}
|
||||
className="rounded-md border object-cover aspect-video"
|
||||
/>
|
||||
</button>
|
||||
|
||||
{files.length > 1 && (
|
||||
<div className="grid grid-cols-10 gap-x-2">
|
||||
{files.map((x) => (
|
||||
<button key={x} onClick={() => setSelected(x)}>
|
||||
<img
|
||||
alt={x}
|
||||
src={x}
|
||||
className={cn(
|
||||
'col-span-1 bg-surface-100 rounded-md object-cover aspect-square border transition',
|
||||
selected === x ? 'border-button-hover' : 'border-secondary'
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<Dialog open={showDialog} onOpenChange={setShowDialog}>
|
||||
<DialogContent size="xxlarge">
|
||||
<img alt={selected} src={selected} className="rounded-md border" />
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
)
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
import { useParams } from 'common'
|
||||
import { useMemo } from 'react'
|
||||
import { toast } from 'sonner'
|
||||
import { Button } from 'ui'
|
||||
|
||||
import type { IntegrationDefinition } from '@/components/interfaces/Integrations/Landing/Integrations.constants'
|
||||
import { useAPIKeysQuery } from '@/data/api-keys/api-keys-query'
|
||||
import { useInstallOAuthIntegrationMutation } from '@/data/marketplace/install-oauth-integration-mutation'
|
||||
|
||||
interface InstallOAuthIntegrationButtonProps {
|
||||
integration: IntegrationDefinition
|
||||
}
|
||||
|
||||
export function InstallOAuthIntegrationButton({ integration }: InstallOAuthIntegrationButtonProps) {
|
||||
const { ref: projectRef } = useParams()
|
||||
|
||||
const { data: apiKeys, isLoading: isApiKeysLoading } = useAPIKeysQuery(
|
||||
{ projectRef, reveal: false },
|
||||
{ enabled: !!projectRef }
|
||||
)
|
||||
|
||||
const { mutate: installOAuthIntegration, isPending: isInstalling } =
|
||||
useInstallOAuthIntegrationMutation({
|
||||
onSuccess: (data) => {
|
||||
if ('redirectUrl' in data) {
|
||||
if (!data.redirectUrl) {
|
||||
toast.error('Failed to redirect because redirect URL is invalid')
|
||||
return
|
||||
}
|
||||
window.location.href = data.redirectUrl
|
||||
} else {
|
||||
toast.error('Failed to start integration installation')
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const isLoading =
|
||||
integration.installIdentificationMethod === 'secret_key_prefix' && isApiKeysLoading
|
||||
|
||||
const isIntegrationInstalled = useMemo(() => {
|
||||
if (!integration) return false
|
||||
|
||||
const prefix = integration.secretKeyPrefix
|
||||
|
||||
if (integration.installIdentificationMethod !== 'secret_key_prefix' || !prefix) return false
|
||||
if (isApiKeysLoading || !apiKeys) return false
|
||||
|
||||
return apiKeys.some((k) => k.type === 'secret' && k.name.startsWith(prefix))
|
||||
}, [apiKeys, integration, isApiKeysLoading])
|
||||
|
||||
const handleInstallClick = async () => {
|
||||
if (!integration || !projectRef) return
|
||||
|
||||
if (integration.installUrlType === 'post') {
|
||||
if (!integration.listingId) return toast.error('Listing ID is required')
|
||||
installOAuthIntegration({ projectRef, id: integration.listingId })
|
||||
} else {
|
||||
window.location.href = integration.installUrl ?? '/'
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{isIntegrationInstalled ? (
|
||||
<Button disabled type="outline" className="shrink-0">
|
||||
Installed
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
type="primary"
|
||||
className="shrink-0"
|
||||
loading={isInstalling || isLoading}
|
||||
disabled={isLoading}
|
||||
onClick={handleInstallClick}
|
||||
>
|
||||
Install integration
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -46,6 +46,10 @@ type IntegrationStep = {
|
||||
description?: string
|
||||
}
|
||||
|
||||
type InstallUrlType = 'get' | 'post'
|
||||
|
||||
type InstallIdentificationMethod = 'secret_key_prefix'
|
||||
|
||||
/**
|
||||
* [Joshen] For marketplace, we probably need to revisit this definition
|
||||
* What properties are obsolete, what properties we need from remote source
|
||||
@@ -97,6 +101,13 @@ export type IntegrationDefinition = {
|
||||
inputs?: IntegrationInputs
|
||||
/** Purely visual, just to show what are the changes on the project from installing the integration */
|
||||
steps?: IntegrationStep[]
|
||||
|
||||
/** These are for OAuth Integrations */
|
||||
installUrl?: string | null
|
||||
installUrlType?: InstallUrlType
|
||||
installIdentificationMethod?: InstallIdentificationMethod
|
||||
secretKeyPrefix?: string
|
||||
listingId?: string
|
||||
} & (
|
||||
| { type: 'wrapper'; meta: WrapperMeta }
|
||||
| { type: 'postgres_extension' | 'custom' | 'oauth' | 'template' }
|
||||
|
||||
+96
-65
@@ -2,6 +2,7 @@ import { useQuery } from '@tanstack/react-query'
|
||||
import { FeatureFlagContext, IS_PLATFORM, useFlag } from 'common'
|
||||
import { Boxes } from 'lucide-react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import Image from 'next/image'
|
||||
import { useContext, useMemo } from 'react'
|
||||
import { cn } from 'ui'
|
||||
|
||||
@@ -10,6 +11,11 @@ import { marketplaceIntegrationsQueryOptions } from '@/data/marketplace/integrat
|
||||
import { useCLIReleaseVersionQuery } from '@/data/misc/cli-release-version-query'
|
||||
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
|
||||
|
||||
const fullImageUrl = (imagePath: string) => {
|
||||
const API_URL = process.env.NEXT_PUBLIC_MARKETPLACE_API_URL || ''
|
||||
return `${API_URL}${imagePath}`
|
||||
}
|
||||
|
||||
/**
|
||||
* [Joshen] Returns a combination of
|
||||
* - Marketplace integrations retrieved remotely (Only if feature flag enabled)
|
||||
@@ -33,69 +39,93 @@ export const useAvailableIntegrations = () => {
|
||||
|
||||
// [Joshen] Format marketplace integrations into existing ones for now
|
||||
// Likely that we might need to change, but can look into separately
|
||||
const marketplaceIntegrations: IntegrationDefinition[] = (data ?? [])?.map((integration) => {
|
||||
const {
|
||||
id,
|
||||
type,
|
||||
categories,
|
||||
title: name,
|
||||
summary: description,
|
||||
documentation_url: docsUrl,
|
||||
url: siteUrl,
|
||||
content,
|
||||
files,
|
||||
} = integration
|
||||
const marketplaceIntegrations: IntegrationDefinition[] = useMemo(
|
||||
() =>
|
||||
(data ?? [])?.map((integration) => {
|
||||
const {
|
||||
id: listingId,
|
||||
slug,
|
||||
categories,
|
||||
title,
|
||||
description,
|
||||
documentation_url: docsUrl,
|
||||
website_url: siteUrl,
|
||||
installation_url: installUrl,
|
||||
installation_url_type: installUrlType,
|
||||
installation_identification_method: installMethod,
|
||||
secret_key_prefix: secretKeyPrefix,
|
||||
images,
|
||||
content,
|
||||
partner_name: authorName,
|
||||
listing_logo: listingLogo,
|
||||
} = integration
|
||||
|
||||
const status = undefined
|
||||
const author = { name: '', websiteUrl: '' }
|
||||
const status = undefined
|
||||
const author = { name: authorName ?? '', websiteUrl: '' }
|
||||
|
||||
return {
|
||||
id: id.toString(),
|
||||
name,
|
||||
status,
|
||||
type,
|
||||
categories: categories.map((x) => x.slug),
|
||||
content,
|
||||
files,
|
||||
description,
|
||||
docsUrl,
|
||||
siteUrl,
|
||||
author,
|
||||
requiredExtensions: [],
|
||||
icon: ({ className, ...props } = {}) => (
|
||||
<Boxes className={cn('inset-0 p-2 text-black w-full h-full', className)} {...props} />
|
||||
),
|
||||
navigation: [
|
||||
{
|
||||
route: 'overview',
|
||||
label: 'Overview',
|
||||
},
|
||||
],
|
||||
navigate: ({ pageId = 'overview' }) => {
|
||||
switch (pageId) {
|
||||
case 'overview':
|
||||
return dynamic(
|
||||
() =>
|
||||
import('@/components/interfaces/Integrations/Integration/IntegrationOverviewTabV2/index').then(
|
||||
(mod) => mod.IntegrationOverviewTabV2
|
||||
),
|
||||
{
|
||||
loading: Loading,
|
||||
}
|
||||
)
|
||||
case 'secrets':
|
||||
return dynamic(
|
||||
() =>
|
||||
import('../Vault/Secrets/SecretsManagement').then((mod) => mod.SecretsManagement),
|
||||
{
|
||||
loading: Loading,
|
||||
}
|
||||
)
|
||||
return {
|
||||
id: slug ?? '',
|
||||
name: title ?? '',
|
||||
status,
|
||||
type: 'oauth' as const, // Currently marketplace only supports oauth apps
|
||||
categories: Array.isArray(categories)
|
||||
? (categories as Array<{ slug: string }>).map((x) => x.slug)
|
||||
: [],
|
||||
content,
|
||||
files: images?.map((image) => fullImageUrl(image)),
|
||||
description,
|
||||
docsUrl,
|
||||
siteUrl,
|
||||
installUrl,
|
||||
installUrlType: installUrlType ?? undefined,
|
||||
installIdentificationMethod: installMethod ?? undefined,
|
||||
secretKeyPrefix: secretKeyPrefix ?? undefined,
|
||||
listingId: listingId ?? undefined,
|
||||
author,
|
||||
requiredExtensions: [],
|
||||
icon: ({ className, ...props } = {}) => (
|
||||
<div className="relative w-full h-full">
|
||||
{listingLogo ? (
|
||||
<Image
|
||||
fill
|
||||
src={fullImageUrl(listingLogo)}
|
||||
alt=""
|
||||
className={cn('p-2', className)}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<Boxes
|
||||
className={cn('inset-0 p-2 text-black w-full h-full', className)}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
navigation: [
|
||||
{
|
||||
route: 'overview',
|
||||
label: 'Overview',
|
||||
},
|
||||
],
|
||||
navigate: ({ pageId = 'overview' }) => {
|
||||
switch (pageId) {
|
||||
case 'overview':
|
||||
return dynamic(
|
||||
() =>
|
||||
import('@/components/interfaces/Integrations/Integration/IntegrationOverviewTabV2/index').then(
|
||||
(mod) => mod.IntegrationOverviewTabV2
|
||||
),
|
||||
{
|
||||
loading: Loading,
|
||||
}
|
||||
)
|
||||
}
|
||||
return null
|
||||
},
|
||||
}
|
||||
return null
|
||||
},
|
||||
}
|
||||
})
|
||||
}),
|
||||
[data]
|
||||
)
|
||||
|
||||
// [Joshen] Existing integrations that are defined within studio
|
||||
// Available integrations are all integrations that can be installed. If an integration can't be installed (needed
|
||||
@@ -117,13 +147,14 @@ export const useAvailableIntegrations = () => {
|
||||
})
|
||||
}, [integrationsWrappers, isCLI])
|
||||
|
||||
const availableIntegrations = useMemo(
|
||||
() => allIntegrations.sort((a, b) => a.name.localeCompare(b.name)),
|
||||
[allIntegrations]
|
||||
)
|
||||
const dataWithMarketplace = useMemo(() => {
|
||||
return [...marketplaceIntegrations, ...allIntegrations].sort((a, b) =>
|
||||
a.name.localeCompare(b.name)
|
||||
)
|
||||
}, [marketplaceIntegrations, allIntegrations])
|
||||
|
||||
return {
|
||||
data: [...marketplaceIntegrations, ...availableIntegrations],
|
||||
data: dataWithMarketplace,
|
||||
error,
|
||||
isPending,
|
||||
isSuccess,
|
||||
|
||||
@@ -85,8 +85,8 @@ const IntegrationCategoriesMenu = ({ page }: { page: string }) => {
|
||||
items: [],
|
||||
},
|
||||
...categories.map((category) => ({
|
||||
name: category.title,
|
||||
key: category.slug,
|
||||
name: category.name ?? '',
|
||||
key: category.slug ?? '',
|
||||
url: `/project/${ref}/integrations?category=${category.slug}`,
|
||||
items: [],
|
||||
})),
|
||||
|
||||
@@ -5,6 +5,10 @@ const SUPABASE_URL = process.env.SUPABASE_URL ? new URL(process.env.SUPABASE_URL
|
||||
const GOTRUE_URL = process.env.NEXT_PUBLIC_GOTRUE_URL
|
||||
? new URL(process.env.NEXT_PUBLIC_GOTRUE_URL).origin
|
||||
: ''
|
||||
const MARKETPLACE_API_URL = process.env.NEXT_PUBLIC_MARKETPLACE_API_URL
|
||||
? new URL(process.env.NEXT_PUBLIC_MARKETPLACE_API_URL).origin
|
||||
: ''
|
||||
|
||||
const SUPABASE_PROJECTS_URL = 'https://*.supabase.co https://*.storage.supabase.co'
|
||||
const SUPABASE_PROJECTS_URL_WS = 'wss://*.supabase.co'
|
||||
|
||||
@@ -85,6 +89,7 @@ export function getCSP() {
|
||||
API_URL,
|
||||
SUPABASE_URL,
|
||||
GOTRUE_URL,
|
||||
MARKETPLACE_API_URL,
|
||||
SUPABASE_LOCAL_PROJECTS_URL_WS,
|
||||
SUPABASE_PROJECTS_URL,
|
||||
SUPABASE_PROJECTS_URL_WS,
|
||||
@@ -130,6 +135,7 @@ export function getCSP() {
|
||||
USERCENTRICS_APP_URL,
|
||||
STAPE_URL,
|
||||
USERCENTRICS_URLS,
|
||||
MARKETPLACE_API_URL,
|
||||
...(!!NIMBUS_PROD_PROJECTS_URL ? [NIMBUS_PROD_PROJECTS_URL, NIMBUS_PROD_PROJECTS_URL_WS] : []),
|
||||
]
|
||||
const STYLE_SRC_URLS = [CLOUDFLARE_CDN_URL, SUPABASE_ASSETS_URL]
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { useMutation } from '@tanstack/react-query'
|
||||
import { toast } from 'sonner'
|
||||
|
||||
import { handleError, post } from '@/data/fetchers'
|
||||
import type { ResponseError, UseCustomMutationOptions } from '@/types'
|
||||
|
||||
export type OAuthIntegrationInstallVariables = {
|
||||
projectRef: string
|
||||
id: string
|
||||
}
|
||||
|
||||
export async function installOAuthIntegration({
|
||||
projectRef,
|
||||
id,
|
||||
}: OAuthIntegrationInstallVariables) {
|
||||
const { data, error } = await post('/platform/integrations/partners/{ref}/{listing_id}', {
|
||||
params: { path: { ref: projectRef, listing_id: id } },
|
||||
})
|
||||
|
||||
if (error) handleError(error)
|
||||
return data
|
||||
}
|
||||
|
||||
type OAuthIntegrationInstallData = Awaited<ReturnType<typeof installOAuthIntegration>>
|
||||
|
||||
export const useInstallOAuthIntegrationMutation = ({
|
||||
onSuccess,
|
||||
onError,
|
||||
...options
|
||||
}: Omit<
|
||||
UseCustomMutationOptions<
|
||||
OAuthIntegrationInstallData,
|
||||
ResponseError,
|
||||
OAuthIntegrationInstallVariables
|
||||
>,
|
||||
'mutationFn'
|
||||
> = {}) => {
|
||||
return useMutation<OAuthIntegrationInstallData, ResponseError, OAuthIntegrationInstallVariables>({
|
||||
mutationFn: (vars) => installOAuthIntegration(vars),
|
||||
async onSuccess(data, variables, context) {
|
||||
await onSuccess?.(data, variables, context)
|
||||
},
|
||||
async onError(data, variables, context) {
|
||||
if (onError === undefined) {
|
||||
toast.error(`Failed to start OAuth integration installation: ${data.message}`)
|
||||
} else {
|
||||
onError(data, variables, context)
|
||||
}
|
||||
},
|
||||
...options,
|
||||
})
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import { queryOptions } from '@tanstack/react-query'
|
||||
import { createMarketplaceClient } from 'common/marketplace-client'
|
||||
|
||||
import { marketplaceIntegrationsKeys } from './keys'
|
||||
import { createMarketplaceClient } from './marketplace-client'
|
||||
import { handleError } from '@/data/fetchers'
|
||||
|
||||
async function getMarketplaceCategories() {
|
||||
const client = createMarketplaceClient()
|
||||
const { data, error } = await client.from('categories').select('*')
|
||||
const marketplaceClient = createMarketplaceClient()
|
||||
const { data, error } = await marketplaceClient.from('categories').select('*')
|
||||
|
||||
if (error) handleError(error)
|
||||
return data ?? []
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { queryOptions } from '@tanstack/react-query'
|
||||
import { createMarketplaceClient } from 'common/marketplace-client'
|
||||
|
||||
import { marketplaceIntegrationsKeys } from './keys'
|
||||
import { createMarketplaceClient } from './marketplace-client'
|
||||
import { handleError } from '@/data/fetchers'
|
||||
|
||||
async function getMarketplaceIntegrations() {
|
||||
const client = createMarketplaceClient()
|
||||
const { data, error } = await client
|
||||
.from('items')
|
||||
.select('*, categories:category_items(...categories(slug, title))')
|
||||
const marketplaceClient = createMarketplaceClient()
|
||||
const { data, error } = await marketplaceClient
|
||||
.from('listings')
|
||||
.select('*')
|
||||
.or('publish_location.eq.dashboard,publish_location.eq.both')
|
||||
|
||||
if (error) handleError(error)
|
||||
return data ?? []
|
||||
|
||||
@@ -1,453 +0,0 @@
|
||||
export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]
|
||||
|
||||
export type Database = {
|
||||
// Allows to automatically instantiate createClient with right options
|
||||
// instead of createClient<Database, { PostgrestVersion: 'XX' }>(URL, KEY)
|
||||
__InternalSupabase: {
|
||||
PostgrestVersion: '14.4'
|
||||
}
|
||||
graphql_public: {
|
||||
Tables: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Views: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Functions: {
|
||||
graphql: {
|
||||
Args: {
|
||||
extensions?: Json
|
||||
operationName?: string
|
||||
query?: string
|
||||
variables?: Json
|
||||
}
|
||||
Returns: Json
|
||||
}
|
||||
}
|
||||
Enums: {
|
||||
[_ in never]: never
|
||||
}
|
||||
CompositeTypes: {
|
||||
[_ in never]: never
|
||||
}
|
||||
}
|
||||
public: {
|
||||
Tables: {
|
||||
categories: {
|
||||
Row: {
|
||||
created_at: string
|
||||
description: string | null
|
||||
id: number
|
||||
slug: string
|
||||
title: string
|
||||
updated_at: string
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string
|
||||
description?: string | null
|
||||
id?: never
|
||||
slug: string
|
||||
title: string
|
||||
updated_at?: string
|
||||
}
|
||||
Update: {
|
||||
created_at?: string
|
||||
description?: string | null
|
||||
id?: never
|
||||
slug?: string
|
||||
title?: string
|
||||
updated_at?: string
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
category_items: {
|
||||
Row: {
|
||||
category_id: number
|
||||
created_at: string
|
||||
item_id: number
|
||||
}
|
||||
Insert: {
|
||||
category_id: number
|
||||
created_at?: string
|
||||
item_id: number
|
||||
}
|
||||
Update: {
|
||||
category_id?: number
|
||||
created_at?: string
|
||||
item_id?: number
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: 'category_items_category_id_fkey'
|
||||
columns: ['category_id']
|
||||
isOneToOne: false
|
||||
referencedRelation: 'categories'
|
||||
referencedColumns: ['id']
|
||||
},
|
||||
{
|
||||
foreignKeyName: 'category_items_item_id_fkey'
|
||||
columns: ['item_id']
|
||||
isOneToOne: false
|
||||
referencedRelation: 'items'
|
||||
referencedColumns: ['id']
|
||||
},
|
||||
]
|
||||
}
|
||||
item_reviews: {
|
||||
Row: {
|
||||
created_at: string
|
||||
featured: boolean
|
||||
item_id: number
|
||||
published_at: string | null
|
||||
review_notes: string | null
|
||||
reviewed_at: string | null
|
||||
reviewed_by: string | null
|
||||
status: Database['public']['Enums']['marketplace_review_status']
|
||||
updated_at: string
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string
|
||||
featured?: boolean
|
||||
item_id: number
|
||||
published_at?: string | null
|
||||
review_notes?: string | null
|
||||
reviewed_at?: string | null
|
||||
reviewed_by?: string | null
|
||||
status?: Database['public']['Enums']['marketplace_review_status']
|
||||
updated_at?: string
|
||||
}
|
||||
Update: {
|
||||
created_at?: string
|
||||
featured?: boolean
|
||||
item_id?: number
|
||||
published_at?: string | null
|
||||
review_notes?: string | null
|
||||
reviewed_at?: string | null
|
||||
reviewed_by?: string | null
|
||||
status?: Database['public']['Enums']['marketplace_review_status']
|
||||
updated_at?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: 'item_reviews_item_id_fkey'
|
||||
columns: ['item_id']
|
||||
isOneToOne: true
|
||||
referencedRelation: 'items'
|
||||
referencedColumns: ['id']
|
||||
},
|
||||
]
|
||||
}
|
||||
items: {
|
||||
Row: {
|
||||
content: string | null
|
||||
created_at: string
|
||||
documentation_url: string | null
|
||||
files: string[]
|
||||
id: number
|
||||
partner_id: number
|
||||
published: boolean
|
||||
registry_item_url: string | null
|
||||
slug: string
|
||||
submitted_by: string | null
|
||||
summary: string | null
|
||||
title: string
|
||||
type: Database['public']['Enums']['marketplace_item_type']
|
||||
updated_at: string
|
||||
url: string | null
|
||||
}
|
||||
Insert: {
|
||||
content?: string | null
|
||||
created_at?: string
|
||||
documentation_url?: string | null
|
||||
files?: string[]
|
||||
id?: never
|
||||
partner_id: number
|
||||
published?: boolean
|
||||
registry_item_url?: string | null
|
||||
slug: string
|
||||
submitted_by?: string | null
|
||||
summary?: string | null
|
||||
title: string
|
||||
type: Database['public']['Enums']['marketplace_item_type']
|
||||
updated_at?: string
|
||||
url?: string | null
|
||||
}
|
||||
Update: {
|
||||
content?: string | null
|
||||
created_at?: string
|
||||
documentation_url?: string | null
|
||||
files?: string[]
|
||||
id?: never
|
||||
partner_id?: number
|
||||
published?: boolean
|
||||
registry_item_url?: string | null
|
||||
slug?: string
|
||||
submitted_by?: string | null
|
||||
summary?: string | null
|
||||
title?: string
|
||||
type?: Database['public']['Enums']['marketplace_item_type']
|
||||
updated_at?: string
|
||||
url?: string | null
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: 'items_partner_id_fkey'
|
||||
columns: ['partner_id']
|
||||
isOneToOne: false
|
||||
referencedRelation: 'partners'
|
||||
referencedColumns: ['id']
|
||||
},
|
||||
]
|
||||
}
|
||||
partner_members: {
|
||||
Row: {
|
||||
created_at: string
|
||||
partner_id: number
|
||||
role: string
|
||||
user_id: string
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string
|
||||
partner_id: number
|
||||
role?: string
|
||||
user_id: string
|
||||
}
|
||||
Update: {
|
||||
created_at?: string
|
||||
partner_id?: number
|
||||
role?: string
|
||||
user_id?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: 'partner_members_partner_id_fkey'
|
||||
columns: ['partner_id']
|
||||
isOneToOne: false
|
||||
referencedRelation: 'partners'
|
||||
referencedColumns: ['id']
|
||||
},
|
||||
]
|
||||
}
|
||||
partners: {
|
||||
Row: {
|
||||
created_at: string
|
||||
created_by: string | null
|
||||
description: string | null
|
||||
id: number
|
||||
logo_url: string | null
|
||||
role: Database['public']['Enums']['marketplace_partner_role']
|
||||
slug: string
|
||||
title: string
|
||||
updated_at: string
|
||||
website: string | null
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string
|
||||
created_by?: string | null
|
||||
description?: string | null
|
||||
id?: never
|
||||
logo_url?: string | null
|
||||
role?: Database['public']['Enums']['marketplace_partner_role']
|
||||
slug: string
|
||||
title: string
|
||||
updated_at?: string
|
||||
website?: string | null
|
||||
}
|
||||
Update: {
|
||||
created_at?: string
|
||||
created_by?: string | null
|
||||
description?: string | null
|
||||
id?: never
|
||||
logo_url?: string | null
|
||||
role?: Database['public']['Enums']['marketplace_partner_role']
|
||||
slug?: string
|
||||
title?: string
|
||||
updated_at?: string
|
||||
website?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
}
|
||||
Views: {
|
||||
[_ in never]: never
|
||||
}
|
||||
Functions: {
|
||||
add_partner_member: {
|
||||
Args: {
|
||||
target_email: string
|
||||
target_partner_id: number
|
||||
target_role?: string
|
||||
}
|
||||
Returns: {
|
||||
created_at: string
|
||||
partner_id: number
|
||||
role: string
|
||||
user_id: string
|
||||
}
|
||||
SetofOptions: {
|
||||
from: '*'
|
||||
to: 'partner_members'
|
||||
isOneToOne: true
|
||||
isSetofReturn: false
|
||||
}
|
||||
}
|
||||
before_user_created_hook: { Args: { event: Json }; Returns: Json }
|
||||
is_admin_member: { Args: never; Returns: boolean }
|
||||
is_partner_admin: {
|
||||
Args: { target_partner_id: number }
|
||||
Returns: boolean
|
||||
}
|
||||
is_partner_member: {
|
||||
Args: { target_partner_id: number }
|
||||
Returns: boolean
|
||||
}
|
||||
is_review_manager_member: { Args: never; Returns: boolean }
|
||||
is_reviewer_member: { Args: never; Returns: boolean }
|
||||
item_latest_review_is_approved: {
|
||||
Args: { target_item_id: number }
|
||||
Returns: boolean
|
||||
}
|
||||
storage_object_item_id: { Args: { object_name: string }; Returns: number }
|
||||
storage_object_partner_id: {
|
||||
Args: { object_name: string }
|
||||
Returns: number
|
||||
}
|
||||
}
|
||||
Enums: {
|
||||
marketplace_item_type: 'oauth' | 'template'
|
||||
marketplace_partner_role: 'partner' | 'reviewer' | 'admin'
|
||||
marketplace_review_status: 'draft' | 'pending_review' | 'approved' | 'rejected'
|
||||
}
|
||||
CompositeTypes: {
|
||||
[_ in never]: never
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type DatabaseWithoutInternals = Omit<Database, '__InternalSupabase'>
|
||||
|
||||
type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, 'public'>]
|
||||
|
||||
export type Tables<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof (DefaultSchema['Tables'] & DefaultSchema['Views'])
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] &
|
||||
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Views'])
|
||||
: never = never,
|
||||
> = DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] &
|
||||
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Views'])[TableName] extends {
|
||||
Row: infer R
|
||||
}
|
||||
? R
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema['Tables'] & DefaultSchema['Views'])
|
||||
? (DefaultSchema['Tables'] & DefaultSchema['Views'])[DefaultSchemaTableNameOrOptions] extends {
|
||||
Row: infer R
|
||||
}
|
||||
? R
|
||||
: never
|
||||
: never
|
||||
|
||||
export type TablesInsert<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof DefaultSchema['Tables']
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables']
|
||||
: never = never,
|
||||
> = DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'][TableName] extends {
|
||||
Insert: infer I
|
||||
}
|
||||
? I
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables']
|
||||
? DefaultSchema['Tables'][DefaultSchemaTableNameOrOptions] extends {
|
||||
Insert: infer I
|
||||
}
|
||||
? I
|
||||
: never
|
||||
: never
|
||||
|
||||
export type TablesUpdate<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof DefaultSchema['Tables']
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables']
|
||||
: never = never,
|
||||
> = DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'][TableName] extends {
|
||||
Update: infer U
|
||||
}
|
||||
? U
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables']
|
||||
? DefaultSchema['Tables'][DefaultSchemaTableNameOrOptions] extends {
|
||||
Update: infer U
|
||||
}
|
||||
? U
|
||||
: never
|
||||
: never
|
||||
|
||||
export type Enums<
|
||||
DefaultSchemaEnumNameOrOptions extends
|
||||
| keyof DefaultSchema['Enums']
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
EnumName extends DefaultSchemaEnumNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions['schema']]['Enums']
|
||||
: never = never,
|
||||
> = DefaultSchemaEnumNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions['schema']]['Enums'][EnumName]
|
||||
: DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema['Enums']
|
||||
? DefaultSchema['Enums'][DefaultSchemaEnumNameOrOptions]
|
||||
: never
|
||||
|
||||
export type CompositeTypes<
|
||||
PublicCompositeTypeNameOrOptions extends
|
||||
| keyof DefaultSchema['CompositeTypes']
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes']
|
||||
: never = never,
|
||||
> = PublicCompositeTypeNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes'][CompositeTypeName]
|
||||
: PublicCompositeTypeNameOrOptions extends keyof DefaultSchema['CompositeTypes']
|
||||
? DefaultSchema['CompositeTypes'][PublicCompositeTypeNameOrOptions]
|
||||
: never
|
||||
|
||||
export const Constants = {
|
||||
graphql_public: {
|
||||
Enums: {},
|
||||
},
|
||||
public: {
|
||||
Enums: {
|
||||
marketplace_item_type: ['oauth', 'template'],
|
||||
marketplace_partner_role: ['partner', 'reviewer', 'admin'],
|
||||
marketplace_review_status: ['draft', 'pending_review', 'approved', 'rejected'],
|
||||
},
|
||||
},
|
||||
} as const
|
||||
@@ -29,6 +29,17 @@ function getAssetPrefix() {
|
||||
return `${SUPABASE_ASSETS_URL}/${process.env.SITE_NAME}/${process.env.VERCEL_GIT_COMMIT_SHA?.substring(0, 12) ?? 'unknown'}`
|
||||
}
|
||||
|
||||
const marketplaceApiUrl = process.env.NEXT_PUBLIC_MARKETPLACE_API_URL
|
||||
? new URL(process.env.NEXT_PUBLIC_MARKETPLACE_API_URL)
|
||||
: null
|
||||
|
||||
const marketplaceApiProtocol: 'http' | 'https' | null =
|
||||
marketplaceApiUrl?.protocol === 'https:'
|
||||
? 'https'
|
||||
: marketplaceApiUrl?.protocol === 'http:'
|
||||
? 'http'
|
||||
: null
|
||||
|
||||
// Use `satisfies` instead of `: NextConfig` so TypeScript preserves narrow
|
||||
// inferred types (e.g. async headers → Promise). This avoids TS2345 when
|
||||
// wrapper functions (bundle-analyzer, sentry) resolve their `next` peer
|
||||
@@ -576,6 +587,16 @@ const nextConfig = {
|
||||
port: '',
|
||||
pathname: '**',
|
||||
},
|
||||
...(marketplaceApiUrl
|
||||
? [
|
||||
{
|
||||
...(marketplaceApiProtocol ? { protocol: marketplaceApiProtocol } : {}),
|
||||
hostname: marketplaceApiUrl.hostname,
|
||||
port: marketplaceApiUrl.port,
|
||||
pathname: '**',
|
||||
},
|
||||
]
|
||||
: []),
|
||||
],
|
||||
},
|
||||
transpilePackages: [
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
import ShimmeringLoader, { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
|
||||
|
||||
import { InstallIntegrationSheet } from '@/components/interfaces/Integrations/Integration/IntegrationOverviewTabV2/InstallIntegrationSheet/InstallIntegrationSheet'
|
||||
import { InstallOAuthIntegrationButton } from '@/components/interfaces/Integrations/Integration/IntegrationOverviewTabV2/InstallIntegrationSheet/InstallOAuthIntegrationButton'
|
||||
import { useAvailableIntegrations } from '@/components/interfaces/Integrations/Landing/useAvailableIntegrations'
|
||||
import { useInstalledIntegrations } from '@/components/interfaces/Integrations/Landing/useInstalledIntegrations'
|
||||
import { DefaultLayout } from '@/components/layouts/DefaultLayout'
|
||||
@@ -208,11 +209,7 @@ const IntegrationPage: NextPageWithLayout = () => {
|
||||
</PageHeaderSummary>
|
||||
|
||||
{integration?.type === 'oauth' ? (
|
||||
<Button asChild type="primary" className="shrink-0">
|
||||
<a target="_blank" rel="noreferrer" href={integration.siteUrl ?? '/'}>
|
||||
Install integration
|
||||
</a>
|
||||
</Button>
|
||||
<InstallOAuthIntegrationButton integration={integration} />
|
||||
) : isMarketplaceEnabled && !!integration && !isInstalled ? (
|
||||
<InstallIntegrationSheet integration={integration} />
|
||||
) : isMarketplaceEnabled && isInstalled ? (
|
||||
|
||||
+93
@@ -670,6 +670,23 @@ export interface paths {
|
||||
patch?: never
|
||||
trace?: never
|
||||
}
|
||||
'/platform/integrations/partners/{ref}/{listing_id}': {
|
||||
parameters: {
|
||||
query?: never
|
||||
header?: never
|
||||
path?: never
|
||||
cookie?: never
|
||||
}
|
||||
get?: never
|
||||
put?: never
|
||||
/** Creates a partner integration and returns the redirect URL */
|
||||
post: operations['PartnerIntegrationsController_createIntegration']
|
||||
delete?: never
|
||||
options?: never
|
||||
head?: never
|
||||
patch?: never
|
||||
trace?: never
|
||||
}
|
||||
'/platform/integrations/private-link/{slug}': {
|
||||
parameters: {
|
||||
query?: never
|
||||
@@ -7918,6 +7935,23 @@ export interface components {
|
||||
organization_id: number
|
||||
overdue_invoice_count: number
|
||||
}
|
||||
PartnerIntegrationsResponse: {
|
||||
/**
|
||||
* Format: date-time
|
||||
* @description When the integration link expires (1 hour from creation). The user must begin the flow before this time.
|
||||
*/
|
||||
expiresAt: string
|
||||
/**
|
||||
* Format: uuid
|
||||
* @description Unique identifier for the integration record
|
||||
*/
|
||||
integrationId: string
|
||||
/**
|
||||
* Format: uri
|
||||
* @description URL to redirect the user's browser to
|
||||
*/
|
||||
redirectUrl: string
|
||||
}
|
||||
PauseStatusResponse: {
|
||||
can_restore: boolean
|
||||
last_paused_on: string | null
|
||||
@@ -13210,6 +13244,65 @@ export interface operations {
|
||||
}
|
||||
}
|
||||
}
|
||||
PartnerIntegrationsController_createIntegration: {
|
||||
parameters: {
|
||||
query?: never
|
||||
header?: never
|
||||
path: {
|
||||
/** @description The id of the listing in the marketplace database */
|
||||
listing_id: string
|
||||
/** @description Supabase project ref */
|
||||
ref: string
|
||||
}
|
||||
cookie?: never
|
||||
}
|
||||
requestBody?: never
|
||||
responses: {
|
||||
201: {
|
||||
headers: {
|
||||
[name: string]: unknown
|
||||
}
|
||||
content: {
|
||||
'application/json': components['schemas']['PartnerIntegrationsResponse']
|
||||
}
|
||||
}
|
||||
/** @description Unauthorized */
|
||||
401: {
|
||||
headers: {
|
||||
[name: string]: unknown
|
||||
}
|
||||
content?: never
|
||||
}
|
||||
/** @description Forbidden action */
|
||||
403: {
|
||||
headers: {
|
||||
[name: string]: unknown
|
||||
}
|
||||
content?: never
|
||||
}
|
||||
/** @description Project or listing not found */
|
||||
404: {
|
||||
headers: {
|
||||
[name: string]: unknown
|
||||
}
|
||||
content?: never
|
||||
}
|
||||
/** @description Rate limit exceeded */
|
||||
429: {
|
||||
headers: {
|
||||
[name: string]: unknown
|
||||
}
|
||||
content?: never
|
||||
}
|
||||
/** @description Failed to retrieve listing or project, or failed to create partner integration */
|
||||
500: {
|
||||
headers: {
|
||||
[name: string]: unknown
|
||||
}
|
||||
content?: never
|
||||
}
|
||||
}
|
||||
}
|
||||
PrivateLinkController_getPrivateLinkConfig: {
|
||||
parameters: {
|
||||
query?: never
|
||||
|
||||
File renamed without changes.
@@ -0,0 +1,243 @@
|
||||
export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]
|
||||
|
||||
export type Database = {
|
||||
public: {
|
||||
Tables: {
|
||||
v_secret_id: {
|
||||
Row: {
|
||||
create_secret: string | null
|
||||
}
|
||||
Insert: {
|
||||
create_secret?: string | null
|
||||
}
|
||||
Update: {
|
||||
create_secret?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
}
|
||||
Views: {
|
||||
categories: {
|
||||
Row: {
|
||||
description: string | null
|
||||
id: string | null
|
||||
name: string | null
|
||||
slug: string | null
|
||||
}
|
||||
Insert: {
|
||||
description?: string | null
|
||||
id?: string | null
|
||||
name?: string | null
|
||||
slug?: string | null
|
||||
}
|
||||
Update: {
|
||||
description?: string | null
|
||||
id?: string | null
|
||||
name?: string | null
|
||||
slug?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
listings: {
|
||||
Row: {
|
||||
aud: string | null
|
||||
categories: Json | null
|
||||
content: string | null
|
||||
description: string | null
|
||||
documentation_url: string | null
|
||||
featured: boolean | null
|
||||
id: string | null
|
||||
images: string[] | null
|
||||
installation_identification_method: 'secret_key_prefix' | null
|
||||
installation_url: string | null
|
||||
installation_url_type: 'get' | 'post' | null
|
||||
listing_logo: string | null
|
||||
marketplace_url: string | null
|
||||
partner_logo: string | null
|
||||
partner_name: string | null
|
||||
partner_slug: string | null
|
||||
publish_location: 'marketplace' | 'dashboard' | 'both' | null
|
||||
secret_key_prefix: string | null
|
||||
slug: string | null
|
||||
title: string | null
|
||||
website_url: string | null
|
||||
youtube_id: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
partners: {
|
||||
Row: {
|
||||
country: string | null
|
||||
description: string | null
|
||||
id: string | null
|
||||
logo: string | null
|
||||
name: string | null
|
||||
num_of_employees: number | null
|
||||
slug: string | null
|
||||
type: 'technology' | 'expert' | null
|
||||
website: string | null
|
||||
}
|
||||
Insert: {
|
||||
country?: string | null
|
||||
description?: string | null
|
||||
id?: string | null
|
||||
logo?: never
|
||||
name?: string | null
|
||||
num_of_employees?: number | null
|
||||
slug?: string | null
|
||||
type?: 'technology' | 'expert' | null
|
||||
website?: string | null
|
||||
}
|
||||
Update: {
|
||||
country?: string | null
|
||||
description?: string | null
|
||||
id?: string | null
|
||||
logo?: never
|
||||
name?: string | null
|
||||
num_of_employees?: number | null
|
||||
slug?: string | null
|
||||
type?: 'technology' | 'expert' | null
|
||||
website?: string | null
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
}
|
||||
Functions: {
|
||||
get_redirect_url: {
|
||||
Args: {
|
||||
p_listing_id: string
|
||||
p_organization_slug: string
|
||||
p_project_id: string
|
||||
}
|
||||
Returns: Json
|
||||
}
|
||||
}
|
||||
Enums: {
|
||||
[_ in never]: never
|
||||
}
|
||||
CompositeTypes: {
|
||||
[_ in never]: never
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type DatabaseWithoutInternals = Omit<Database, '__InternalSupabase'>
|
||||
|
||||
type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, 'public'>]
|
||||
|
||||
export type Tables<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof (DefaultSchema['Tables'] & DefaultSchema['Views'])
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] &
|
||||
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Views'])
|
||||
: never = never,
|
||||
> = DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'] &
|
||||
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Views'])[TableName] extends {
|
||||
Row: infer R
|
||||
}
|
||||
? R
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema['Tables'] & DefaultSchema['Views'])
|
||||
? (DefaultSchema['Tables'] & DefaultSchema['Views'])[DefaultSchemaTableNameOrOptions] extends {
|
||||
Row: infer R
|
||||
}
|
||||
? R
|
||||
: never
|
||||
: never
|
||||
|
||||
export type TablesInsert<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof DefaultSchema['Tables']
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables']
|
||||
: never = never,
|
||||
> = DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'][TableName] extends {
|
||||
Insert: infer I
|
||||
}
|
||||
? I
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables']
|
||||
? DefaultSchema['Tables'][DefaultSchemaTableNameOrOptions] extends {
|
||||
Insert: infer I
|
||||
}
|
||||
? I
|
||||
: never
|
||||
: never
|
||||
|
||||
export type TablesUpdate<
|
||||
DefaultSchemaTableNameOrOptions extends
|
||||
| keyof DefaultSchema['Tables']
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
TableName extends DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables']
|
||||
: never = never,
|
||||
> = DefaultSchemaTableNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions['schema']]['Tables'][TableName] extends {
|
||||
Update: infer U
|
||||
}
|
||||
? U
|
||||
: never
|
||||
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema['Tables']
|
||||
? DefaultSchema['Tables'][DefaultSchemaTableNameOrOptions] extends {
|
||||
Update: infer U
|
||||
}
|
||||
? U
|
||||
: never
|
||||
: never
|
||||
|
||||
export type Enums<
|
||||
DefaultSchemaEnumNameOrOptions extends
|
||||
| keyof DefaultSchema['Enums']
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
EnumName extends DefaultSchemaEnumNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions['schema']]['Enums']
|
||||
: never = never,
|
||||
> = DefaultSchemaEnumNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions['schema']]['Enums'][EnumName]
|
||||
: DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema['Enums']
|
||||
? DefaultSchema['Enums'][DefaultSchemaEnumNameOrOptions]
|
||||
: never
|
||||
|
||||
export type CompositeTypes<
|
||||
PublicCompositeTypeNameOrOptions extends
|
||||
| keyof DefaultSchema['CompositeTypes']
|
||||
| { schema: keyof DatabaseWithoutInternals },
|
||||
CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes']
|
||||
: never = never,
|
||||
> = PublicCompositeTypeNameOrOptions extends {
|
||||
schema: keyof DatabaseWithoutInternals
|
||||
}
|
||||
? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions['schema']]['CompositeTypes'][CompositeTypeName]
|
||||
: PublicCompositeTypeNameOrOptions extends keyof DefaultSchema['CompositeTypes']
|
||||
? DefaultSchema['CompositeTypes'][PublicCompositeTypeNameOrOptions]
|
||||
: never
|
||||
|
||||
export const Constants = {
|
||||
public: {
|
||||
Enums: {},
|
||||
},
|
||||
} as const
|
||||
Reference in new issue
Block a user