diff --git a/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx b/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx
index aacca36a58a..ba2a47bf189 100644
--- a/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx
+++ b/studio/components/interfaces/Organization/TeamSettings/MemberActions.tsx
@@ -5,7 +5,8 @@ import { FC } from 'react'
import { Button, Dropdown, IconMoreHorizontal, IconTrash } from 'ui'
import { confirmAlert } from 'components/to-be-cleaned/ModalsDeprecated/ConfirmModal'
-import { checkPermissions, useParams, useStore } from 'hooks'
+import { checkPermissions, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { Member, Role } from 'types'
import { useOrganizationMemberDeleteMutation } from 'data/organizations/organization-member-delete-mutation'
diff --git a/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx b/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx
index ffbb54d6f65..3dca2403e09 100644
--- a/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx
+++ b/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx
@@ -5,7 +5,8 @@ import { observer } from 'mobx-react-lite'
import { Badge, Button, Loading, Listbox, IconUser, Modal, IconAlertCircle, IconLoader } from 'ui'
import { Member } from 'types'
-import { useStore, useParams } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
import Table from 'components/to-be-cleaned/Table'
diff --git a/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx b/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx
index 42140783088..bb5f155102d 100644
--- a/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx
+++ b/studio/components/interfaces/Organization/TeamSettings/TeamSettings.tsx
@@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite'
import { Button, Input, IconSearch } from 'ui'
import * as Tooltip from '@radix-ui/react-tooltip'
-import { useStore, useParams } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { post } from 'lib/common/fetch'
import { API_URL } from 'lib/constants'
import { useProfileQuery } from 'data/profile/profile-query'
diff --git a/studio/components/interfaces/Reports/Reports.tsx b/studio/components/interfaces/Reports/Reports.tsx
index ecf0223e60c..4b41c46826b 100644
--- a/studio/components/interfaces/Reports/Reports.tsx
+++ b/studio/components/interfaces/Reports/Reports.tsx
@@ -16,7 +16,8 @@ import {
} from 'ui'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { checkPermissions, useParams } from 'hooks'
+import { checkPermissions } from 'hooks'
+import { useParams } from 'common/hooks'
import { uuidv4 } from 'lib/helpers'
import { METRIC_CATEGORIES, METRICS, TIME_PERIODS_REPORTS } from 'lib/constants'
import { useProjectContentStore } from 'stores/projectContentStore'
diff --git a/studio/components/interfaces/Settings/API/JWTSettings.tsx b/studio/components/interfaces/Settings/API/JWTSettings.tsx
index 8a0c66ed22a..231c445e6b1 100644
--- a/studio/components/interfaces/Settings/API/JWTSettings.tsx
+++ b/studio/components/interfaces/Settings/API/JWTSettings.tsx
@@ -23,7 +23,8 @@ import {
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { uuidv4 } from 'lib/helpers'
-import { useStore, checkPermissions, useParams } from 'hooks'
+import { useStore, checkPermissions } from 'hooks'
+import { useParams } from 'common/hooks'
import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query'
import { useJwtSecretUpdateMutation } from 'data/config/jwt-secret-update-mutation'
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
diff --git a/studio/components/interfaces/Settings/API/PostgrestConfig.tsx b/studio/components/interfaces/Settings/API/PostgrestConfig.tsx
index 1d3f6d74d74..2ed7d977be9 100644
--- a/studio/components/interfaces/Settings/API/PostgrestConfig.tsx
+++ b/studio/components/interfaces/Settings/API/PostgrestConfig.tsx
@@ -4,7 +4,8 @@ import { observer } from 'mobx-react-lite'
import { Input, Form, IconAlertCircle, InputNumber } from 'ui'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { checkPermissions, useStore, useParams } from 'hooks'
+import { checkPermissions, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { useProjectPostgrestConfigQuery } from 'data/config/project-postgrest-config-query'
import { useProjectPostgrestConfigUpdateMutation } from 'data/config/project-postgrest-config-update-mutation'
import MultiSelect from 'components/ui/MultiSelect'
diff --git a/studio/components/interfaces/Settings/API/ServiceList.tsx b/studio/components/interfaces/Settings/API/ServiceList.tsx
index 3209ae9ae15..f22b65c0215 100644
--- a/studio/components/interfaces/Settings/API/ServiceList.tsx
+++ b/studio/components/interfaces/Settings/API/ServiceList.tsx
@@ -3,7 +3,8 @@ import { JwtSecretUpdateError, JwtSecretUpdateStatus } from '@supabase/shared-ty
import { useEffect, useRef } from 'react'
import { IconAlertCircle, Input } from 'ui'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query'
import { useProjectApiQuery } from 'data/config/project-api-query'
diff --git a/studio/components/interfaces/Settings/Database/DatabaseSettings/ResetDbPassword.tsx b/studio/components/interfaces/Settings/Database/DatabaseSettings/ResetDbPassword.tsx
index 40c076a6b10..c3d88b9915f 100644
--- a/studio/components/interfaces/Settings/Database/DatabaseSettings/ResetDbPassword.tsx
+++ b/studio/components/interfaces/Settings/Database/DatabaseSettings/ResetDbPassword.tsx
@@ -5,7 +5,8 @@ import generator from 'generate-password'
import { Input, Button, Modal } from 'ui'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { checkPermissions, useParams, useStore } from 'hooks'
+import { checkPermissions, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { patch } from 'lib/common/fetch'
import { passwordStrength } from 'lib/helpers'
import { API_URL, DEFAULT_MINIMUM_PASSWORD_STRENGTH } from 'lib/constants'
diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx
index b79d99fa3c6..44fcec66a5f 100644
--- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx
+++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AddRestrictionModal.tsx
@@ -3,7 +3,8 @@ import { Address4 } from 'ip-address'
import { Button, Form, IconHelpCircle, Input, Modal } from 'ui'
import * as Tooltip from '@radix-ui/react-tooltip'
-import { useStore, useParams } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import InformationBox from 'components/ui/InformationBox'
import { checkIfPrivate, getAddressEndRange } from './NetworkRestrictions.utils'
import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation'
diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AllowAllModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AllowAllModal.tsx
index 084d12c3d89..880c176f9bb 100644
--- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/AllowAllModal.tsx
+++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/AllowAllModal.tsx
@@ -1,7 +1,8 @@
import { FC, useState } from 'react'
import { Modal, Button } from 'ui'
-import { useStore, useParams } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation'
interface Props {
diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx
index 537d509515e..c843de5dcbc 100644
--- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx
+++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/DisallowAllModal.tsx
@@ -1,7 +1,8 @@
import { FC, useState } from 'react'
import { Modal, Button } from 'ui'
-import { useStore, useParams } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation'
import InformationBox from 'components/ui/InformationBox'
diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/NetworkRestrictions.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/NetworkRestrictions.tsx
index 35242df3cb8..31e465530f8 100644
--- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/NetworkRestrictions.tsx
+++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/NetworkRestrictions.tsx
@@ -4,7 +4,8 @@ import * as Tooltip from '@radix-ui/react-tooltip'
import { Button, IconAlertCircle, IconExternalLink, IconGlobe, IconLock } from 'ui'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { checkPermissions, useParams } from 'hooks'
+import { checkPermissions } from 'hooks'
+import { useParams } from 'common/hooks'
import Panel from 'components/ui/Panel'
import { FormPanel, FormHeader } from 'components/ui/Forms'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
diff --git a/studio/components/interfaces/Settings/Database/NetworkRestrictions/RemoveRestrictionModal.tsx b/studio/components/interfaces/Settings/Database/NetworkRestrictions/RemoveRestrictionModal.tsx
index cd2a4f7f654..c0aa9253ea7 100644
--- a/studio/components/interfaces/Settings/Database/NetworkRestrictions/RemoveRestrictionModal.tsx
+++ b/studio/components/interfaces/Settings/Database/NetworkRestrictions/RemoveRestrictionModal.tsx
@@ -1,7 +1,8 @@
import { FC, useState } from 'react'
import { Alert, Button, Form, Input, Modal } from 'ui'
-import { useStore, useParams } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { useNetworkRestrictionsApplyMutation } from 'data/network-restrictions/network-retrictions-apply-mutation'
interface Props {
diff --git a/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx b/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx
index 92e146e911e..c764afb0a4a 100644
--- a/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx
+++ b/studio/components/interfaces/Settings/Database/SSLConfiguration.tsx
@@ -4,7 +4,8 @@ import * as Tooltip from '@radix-ui/react-tooltip'
import { Button, IconDownload, Toggle, IconLoader, Alert } from 'ui'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { checkPermissions, useParams, useStore, useFlag } from 'hooks'
+import { checkPermissions, useStore, useFlag } from 'hooks'
+import { useParams } from 'common/hooks'
import {
FormHeader,
FormPanel,
diff --git a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx
index 6ce9eb610cb..9e6192fab4b 100644
--- a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx
+++ b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainConfig.tsx
@@ -2,7 +2,8 @@ import Link from 'next/link'
import { observer } from 'mobx-react-lite'
import { IconAlertCircle } from 'ui'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { useProjectApiQuery } from 'data/config/project-api-query'
import { useCustomDomainsQuery } from 'data/custom-domains/custom-domains-query'
import Panel from 'components/ui/Panel'
diff --git a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx
index 398f62b9ec3..c41ae2dd05f 100644
--- a/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx
+++ b/studio/components/interfaces/Settings/General/CustomDomainConfig/CustomDomainsConfigureHostname.tsx
@@ -4,7 +4,8 @@ import { observer } from 'mobx-react-lite'
import { Button, Form, IconExternalLink, Input } from 'ui'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { checkPermissions, useParams, useStore } from 'hooks'
+import { checkPermissions, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import {
FormActions,
FormPanel,
diff --git a/studio/components/interfaces/Settings/General/Infrastructure/Infrastructure.tsx b/studio/components/interfaces/Settings/General/Infrastructure/Infrastructure.tsx
index c437f5fb6e0..52e13db0717 100644
--- a/studio/components/interfaces/Settings/General/Infrastructure/Infrastructure.tsx
+++ b/studio/components/interfaces/Settings/General/Infrastructure/Infrastructure.tsx
@@ -4,7 +4,8 @@ import { Alert, Badge, Button, IconPackage, Input } from 'ui'
import { observer } from 'mobx-react-lite'
import * as Tooltip from '@radix-ui/react-tooltip'
-import { useFlag, useParams, useStore } from 'hooks'
+import { useFlag, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
import {
FormHeader,
diff --git a/studio/components/interfaces/Settings/General/Infrastructure/ProjectUpgradeAlert/ProjectUpgradeAlert.tsx b/studio/components/interfaces/Settings/General/Infrastructure/ProjectUpgradeAlert/ProjectUpgradeAlert.tsx
index 453770469a2..0af657cae0e 100644
--- a/studio/components/interfaces/Settings/General/Infrastructure/ProjectUpgradeAlert/ProjectUpgradeAlert.tsx
+++ b/studio/components/interfaces/Settings/General/Infrastructure/ProjectUpgradeAlert/ProjectUpgradeAlert.tsx
@@ -12,7 +12,8 @@ import {
IconExternalLink,
} from 'ui'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { post } from 'lib/common/fetch'
import { API_ADMIN_URL, PROJECT_STATUS } from 'lib/constants'
import InformationBox from 'components/ui/InformationBox'
diff --git a/studio/components/interfaces/Settings/Logs/Logs.utils.ts b/studio/components/interfaces/Settings/Logs/Logs.utils.ts
index 24e42b749fa..fa076635e7c 100644
--- a/studio/components/interfaces/Settings/Logs/Logs.utils.ts
+++ b/studio/components/interfaces/Settings/Logs/Logs.utils.ts
@@ -8,7 +8,6 @@ import BackwardIterator from 'components/to-be-cleaned/SqlEditor/BackwardIterato
import { uniqBy } from 'lodash'
import { useEffect } from 'react'
-
/**
* Convert a micro timestamp from number/string to iso timestamp
*/
@@ -271,11 +270,10 @@ export const ensureNoTimestampConflict = (
}
}
-
/**
* Adds SQL code hints to logs explorer code editor
*/
-export const useEditorHints = ()=>{
+export const useEditorHints = () => {
const monaco = useMonaco()
useEffect(() => {
@@ -337,7 +335,7 @@ export const useEditorHints = ()=>{
suggestions: uniqBy(suggestions, 'label'),
}
},
- }
+ } as any
// register completion item provider for pgsql
const completeProvider = monaco.languages.registerCompletionItemProvider(
@@ -350,4 +348,4 @@ export const useEditorHints = ()=>{
}
}
}, [monaco])
-}
\ No newline at end of file
+}
diff --git a/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx b/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx
index 543c32821d8..1d718bcfb33 100644
--- a/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx
+++ b/studio/components/interfaces/Settings/Logs/LogsPreviewer.tsx
@@ -22,7 +22,7 @@ import ShimmerLine from 'components/ui/ShimmerLine'
import LoadingOpacity from 'components/ui/LoadingOpacity'
import { useUpgradePrompt } from 'hooks/misc/useUpgradePrompt'
import UpgradePrompt from './UpgradePrompt'
-import { useParams } from 'hooks'
+import { useParams } from 'common/hooks'
import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query'
/**
diff --git a/studio/components/interfaces/Settings/Vault/Keys/EncryptionKeysManagement.tsx b/studio/components/interfaces/Settings/Vault/Keys/EncryptionKeysManagement.tsx
index 6010ec432ca..135381a6600 100644
--- a/studio/components/interfaces/Settings/Vault/Keys/EncryptionKeysManagement.tsx
+++ b/studio/components/interfaces/Settings/Vault/Keys/EncryptionKeysManagement.tsx
@@ -18,7 +18,8 @@ import {
IconExternalLink,
} from 'ui'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { useParams, useStore, checkPermissions } from 'hooks'
+import { useStore, checkPermissions } from 'hooks'
+import { useParams } from 'common/hooks'
import Divider from 'components/ui/Divider'
const DEFAULT_KEY_NAME = 'No description provided'
diff --git a/studio/components/interfaces/Settings/Vault/Secrets/SecretRow.tsx b/studio/components/interfaces/Settings/Vault/Secrets/SecretRow.tsx
index 9ff849dd7ae..1b17adc55e7 100644
--- a/studio/components/interfaces/Settings/Vault/Secrets/SecretRow.tsx
+++ b/studio/components/interfaces/Settings/Vault/Secrets/SecretRow.tsx
@@ -16,7 +16,8 @@ import {
import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { VaultSecret } from 'types'
-import { useParams, useStore, checkPermissions } from 'hooks'
+import { useStore, checkPermissions } from 'hooks'
+import { useParams } from 'common/hooks'
interface Props {
secret: VaultSecret
diff --git a/studio/components/interfaces/Settings/Vault/Secrets/SecretsManagement.tsx b/studio/components/interfaces/Settings/Vault/Secrets/SecretsManagement.tsx
index b3adab646d4..614e6e09b19 100644
--- a/studio/components/interfaces/Settings/Vault/Secrets/SecretsManagement.tsx
+++ b/studio/components/interfaces/Settings/Vault/Secrets/SecretsManagement.tsx
@@ -5,7 +5,8 @@ import { FC, Fragment, useState, useEffect } from 'react'
import { IconSearch, Input, Button, Listbox, IconLoader, IconExternalLink, IconX } from 'ui'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { useStore, useParams, checkPermissions } from 'hooks'
+import { useStore, checkPermissions } from 'hooks'
+import { useParams } from 'common/hooks'
import SecretRow from './SecretRow'
import EditSecretModal from './EditSecretModal'
import DeleteSecretModal from './DeleteSecretModal'
diff --git a/studio/components/interfaces/Settings/Vault/VaultToggle.tsx b/studio/components/interfaces/Settings/Vault/VaultToggle.tsx
index f03f88633c7..510e6d66e70 100644
--- a/studio/components/interfaces/Settings/Vault/VaultToggle.tsx
+++ b/studio/components/interfaces/Settings/Vault/VaultToggle.tsx
@@ -5,7 +5,8 @@ import * as Tooltip from '@radix-ui/react-tooltip'
import { Button, IconExternalLink } from 'ui'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { checkPermissions, useParams, useStore } from 'hooks'
+import { checkPermissions, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { BASE_PATH } from 'lib/constants'
interface Props {}
diff --git a/studio/components/interfaces/TableGridEditor/APIDocumentationPanel.tsx b/studio/components/interfaces/TableGridEditor/APIDocumentationPanel.tsx
index 0557b5e4d02..4565ba6ee28 100644
--- a/studio/components/interfaces/TableGridEditor/APIDocumentationPanel.tsx
+++ b/studio/components/interfaces/TableGridEditor/APIDocumentationPanel.tsx
@@ -1,7 +1,8 @@
import { useState } from 'react'
import { SidePanel, IconBookOpen } from 'ui'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { GeneralContent, ResourceContent } from '../Docs'
import LangSelector from '../Docs/LangSelector'
import GeneratingTypes from '../Docs/GeneratingTypes'
diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnEditor.tsx b/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnEditor.tsx
index c5097858d28..79d492764e6 100644
--- a/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnEditor.tsx
+++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/ColumnEditor/ColumnEditor.tsx
@@ -10,7 +10,8 @@ import type {
PostgresType,
} from '@supabase/postgres-meta'
-import { useFlag, useParams, useStore } from 'hooks'
+import { useFlag, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import ActionBar from '../ActionBar'
import HeaderTitle from './HeaderTitle'
import ColumnType from './ColumnType'
diff --git a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx
index 99ce84b03f4..43296223414 100644
--- a/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx
+++ b/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.tsx
@@ -5,7 +5,7 @@ import { Dictionary } from 'components/grid'
import { Modal } from 'ui'
import type { PostgresTable, PostgresColumn } from '@supabase/postgres-meta'
-import { useStore } from 'hooks'
+import { useStore, useUrlState } from 'hooks'
import { entityTypeKeys } from 'data/entity-types/keys'
import { useTableRowCreateMutation } from 'data/table-rows/table-row-create-mutation'
import { useTableRowUpdateMutation } from 'data/table-rows/table-row-update-mutation'
@@ -66,6 +66,7 @@ const SidePanelEditor = ({
onTableCreated = noop,
onColumnSaved = noop,
}: SidePanelEditorProps) => {
+ const [_, setParams] = useUrlState({ arrayKeys: ['filter', 'sort'] })
const { meta, ui } = useStore()
const queryClient = useQueryClient()
@@ -263,12 +264,28 @@ const SidePanelEditor = ({
if (response?.error) {
ui.setNotification({ category: 'error', message: response.error.message })
} else {
+ if (
+ !isNewRecord &&
+ payload.name &&
+ selectedColumnToEdit &&
+ selectedColumnToEdit.name !== payload.name
+ ) {
+ reAddRenamedColumnSortAndFilter(selectedColumnToEdit.name, payload.name)
+ }
queryClient.invalidateQueries(sqlKeys.query(project?.ref, ['foreign-key-constraints']))
await Promise.all([
meta.tables.loadById(selectedTable!.id),
queryClient.invalidateQueries(
sqlKeys.query(project?.ref, [selectedTable!.schema, selectedTable!.name])
),
+ queryClient.invalidateQueries(
+ sqlKeys.query(project?.ref, [
+ 'table-definition',
+ selectedTable!.schema,
+ selectedTable!.name,
+ ])
+ ),
+ queryClient.invalidateQueries(entityTypeKeys.list(project?.ref)),
])
onColumnSaved(configuration.isEncrypted)
setIsEdited(false)
@@ -282,6 +299,28 @@ const SidePanelEditor = ({
resolve()
}
+ /**
+ * Adds the renamed column's filter and/or sort rules.
+ */
+ const reAddRenamedColumnSortAndFilter = (oldColumnName: string, newColumnName: string) => {
+ setParams((prevParams) => {
+ const existingFilters = (prevParams?.filter ?? []) as string[]
+ const existingSorts = (prevParams?.sort ?? []) as string[]
+
+ return {
+ ...prevParams,
+ filter: existingFilters.map((filter: string) => {
+ const [column] = filter.split(':')
+ return column === oldColumnName ? filter.replace(column, newColumnName) : filter
+ }),
+ sort: existingSorts.map((sort: string) => {
+ const [column] = sort.split(':')
+ return column === oldColumnName ? sort.replace(column, newColumnName) : sort
+ }),
+ }
+ })
+ }
+
const saveTable = async (
payload: any,
columns: ColumnField[],
@@ -370,7 +409,20 @@ const SidePanelEditor = ({
message: `Table ${table.name} has been updated, but there were some errors`,
})
} else {
- await queryClient.invalidateQueries(entityTypeKeys.list(project?.ref))
+ queryClient.invalidateQueries(sqlKeys.query(project?.ref, ['foreign-key-constraints']))
+ await Promise.all([
+ queryClient.invalidateQueries(
+ sqlKeys.query(project?.ref, [selectedTable!.schema, selectedTable!.name])
+ ),
+ queryClient.invalidateQueries(
+ sqlKeys.query(project?.ref, [
+ 'table-definition',
+ selectedTable!.schema,
+ selectedTable!.name,
+ ])
+ ),
+ queryClient.invalidateQueries(entityTypeKeys.list(project?.ref)),
+ ])
ui.setNotification({
id: toastId,
@@ -379,8 +431,6 @@ const SidePanelEditor = ({
})
}
}
-
- queryClient.invalidateQueries(sqlKeys.query(project?.ref, ['foreign-key-constraints']))
} catch (error: any) {
saveTableError = true
ui.setNotification({ id: toastId, category: 'error', message: error.message })
diff --git a/studio/components/interfaces/TableGridEditor/ViewDefinition.tsx b/studio/components/interfaces/TableGridEditor/TableDefinition.tsx
similarity index 52%
rename from studio/components/interfaces/TableGridEditor/ViewDefinition.tsx
rename to studio/components/interfaces/TableGridEditor/TableDefinition.tsx
index 6df9807aa53..a7bf539c849 100644
--- a/studio/components/interfaces/TableGridEditor/ViewDefinition.tsx
+++ b/studio/components/interfaces/TableGridEditor/TableDefinition.tsx
@@ -1,28 +1,72 @@
-import { useRef } from 'react'
import Editor from '@monaco-editor/react'
-import { timeout } from 'lib/helpers'
-import { useStore } from 'hooks'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
-import { useViewDefinitionQuery } from 'data/database/view-definition-query'
import ShimmeringLoader from 'components/ui/ShimmeringLoader'
+import { useTableDefinitionQuery } from 'data/database/table-definition-query'
+import { useViewDefinitionQuery } from 'data/database/view-definition-query'
+import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
+import { useStore } from 'hooks'
+import useEntityType from 'hooks/misc/useEntityType'
+import { timeout } from 'lib/helpers'
+import { observer } from 'mobx-react-lite'
+import { useMemo, useRef } from 'react'
+import { format } from 'sql-formatter'
-export interface ViewDefinitionProps {
- name: string
+export interface TableDefinitionProps {
+ id?: number
}
-const ViewDefinition = ({ name }: ViewDefinitionProps) => {
+const TableDefinition = ({ id }: TableDefinitionProps) => {
const editorRef = useRef(null)
const monacoRef = useRef(null)
const { ui } = useStore()
const { isDarkTheme } = ui
+ const entityType = useEntityType(id)
+
const { project } = useProjectContext()
- const { data: definition, isLoading } = useViewDefinitionQuery({
- name,
- projectRef: project?.ref,
- connectionString: project?.connectionString,
- })
+ const viewResult = useViewDefinitionQuery(
+ {
+ schema: entityType?.schema,
+ name: entityType?.name,
+ projectRef: project?.ref,
+ connectionString: project?.connectionString,
+ },
+ {
+ enabled: entityType?.type === ENTITY_TYPE.VIEW,
+ }
+ )
+
+ const tableResult = useTableDefinitionQuery(
+ {
+ schema: entityType?.schema,
+ name: entityType?.name,
+ projectRef: project?.ref,
+ connectionString: project?.connectionString,
+ },
+ {
+ enabled: entityType?.type === ENTITY_TYPE.TABLE,
+ }
+ )
+
+ const { data: definition, isLoading } =
+ entityType?.type === ENTITY_TYPE.VIEW ? viewResult : tableResult
+
+ const prepend =
+ entityType?.type === ENTITY_TYPE.VIEW
+ ? `create view ${entityType.schema}.${entityType.name} as\n`
+ : ''
+
+ const formattedDefinition = useMemo(
+ () =>
+ definition
+ ? format(prepend + definition, {
+ language: 'postgresql',
+ keywordCase: 'lower',
+ })
+ : undefined,
+ [definition]
+ )
const handleEditorOnMount = async (editor: any, monaco: any) => {
editorRef.current = editor
@@ -59,7 +103,7 @@ const ViewDefinition = ({ name }: ViewDefinitionProps) => {
theme={isDarkTheme ? 'vs-dark' : 'vs'}
onMount={handleEditorOnMount}
defaultLanguage="pgsql"
- defaultValue={definition}
+ value={formattedDefinition}
path={''}
options={{
domReadOnly: true,
@@ -75,4 +119,4 @@ const ViewDefinition = ({ name }: ViewDefinitionProps) => {
)
}
-export default ViewDefinition
+export default observer(TableDefinition)
diff --git a/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx b/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
index 7014e0fe8e4..e7daf73a3ae 100644
--- a/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
+++ b/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx
@@ -7,7 +7,9 @@ import { PermissionAction } from '@supabase/shared-types/out/constants'
import { QueryKey, useQueryClient } from '@tanstack/react-query'
import { SchemaView } from 'types'
-import { checkPermissions, useFlag, useStore, useParams } from 'hooks'
+import { checkPermissions, useFlag, useStore, useUrlState } from 'hooks'
+import useEntityType from 'hooks/misc/useEntityType'
+import { useParams } from 'common/hooks'
import GridHeaderActions from './GridHeaderActions'
import NotFoundState from './NotFoundState'
import SidePanelEditor from './SidePanelEditor'
@@ -23,6 +25,7 @@ import { useProjectJsonSchemaQuery } from 'data/docs/project-json-schema-query'
import { useTableRowUpdateMutation } from 'data/table-rows/table-row-update-mutation'
import { JsonEditValue } from './SidePanelEditor/RowEditor/RowEditor.types'
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
+import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
import {
ForeignKeyConstraint,
useForeignKeyConstraintsQuery,
@@ -30,7 +33,7 @@ import {
import { FOREIGN_KEY_DELETION_ACTION } from 'data/database/database-query-constants'
import { ForeignRowSelectorProps } from './SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector'
import TwoOptionToggle from 'components/ui/TwoOptionToggle'
-import ViewDefinition from './ViewDefinition'
+import TableDefinition from './TableDefinition'
import APIDocumentationPanel from './APIDocumentationPanel'
export interface TableGridEditorProps {
@@ -101,8 +104,14 @@ const TableGridEditor = ({
const [encryptedColumns, setEncryptedColumns] = useState([])
const [apiPreviewPanelOpen, setApiPreviewPanelOpen] = useState(false)
- // When rendering a view in the grid
- const [selectedView, setSelectedView] = useState<'data' | 'definition'>('data')
+ const [{ view: selectedView = 'data' }, setUrlState] = useUrlState()
+ const setSelectedView = (view: string) => {
+ if (view === 'data') {
+ setUrlState({ view: undefined })
+ } else {
+ setUrlState({ view })
+ }
+ }
const isReadOnly =
!checkPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables') &&
@@ -188,6 +197,8 @@ const TableGridEditor = ({
}
}, [selectedTable?.id])
+ const entityType = useEntityType(selectedTable?.id)
+
// NOTE: DO NOT PUT HOOKS AFTER THIS LINE
if (isUndefined(selectedTable)) {
return
@@ -195,13 +206,12 @@ const TableGridEditor = ({
const tableId = selectedTable?.id
- // @ts-ignore
- const schema = meta.schemas.list().find((schema) => schema.name === selectedSchema)
- const isViewSelected = !Object.keys(selectedTable).includes('rls_enabled')
- const isForeignTableSelected = meta.foreignTables.byId(selectedTable.id) !== undefined
- const isLocked = meta.excludedSchemas.includes(schema?.name ?? '')
+ const isViewSelected = entityType?.type === ENTITY_TYPE.VIEW
+ const isTableSelected = entityType?.type === ENTITY_TYPE.TABLE
+ const isForeignTableSelected = entityType?.type === ENTITY_TYPE.FOREIGN_TABLE
+ const isLocked = meta.excludedSchemas.includes(entityType?.schema ?? '')
const canUpdateTables = checkPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'tables')
- const canEditViaTableEditor = !isViewSelected && !isForeignTableSelected && !isLocked
+ const canEditViaTableEditor = isTableSelected && !isLocked
// [Joshen] We can tweak below to eventually support composite keys as the data
// returned from foreignKeyMeta should be easy to deal with, rather than pg-meta
@@ -336,23 +346,33 @@ const TableGridEditor = ({
table={gridTable}
refreshDocs={refreshDocs}
headerActions={
- isViewSelected ? (
-
+ isTableSelected || isViewSelected || canEditViaTableEditor ? (
+ <>
+ {canEditViaTableEditor && (
+
+ )}
+ {(isTableSelected || isViewSelected) && (
+ <>
+ {canEditViaTableEditor && (
+
+ >
+ )}
+ >
) : null
}
onAddColumn={onAddColumn}
@@ -365,9 +385,9 @@ const TableGridEditor = ({
onSqlQuery={onSqlQuery}
onExpandJSONEditor={onExpandJSONEditor}
onEditForeignKeyColumnValue={onEditForeignKeyColumnValue}
- showCustomChildren={isViewSelected && selectedView === 'definition'}
+ showCustomChildren={(isViewSelected || isTableSelected) && selectedView === 'definition'}
customHeader={
- isViewSelected && selectedView === 'definition' ? (
+ (isViewSelected || isTableSelected) && selectedView === 'definition' ? (
SQL Definition of {selectedTable.name}{' '}
@@ -377,7 +397,7 @@ const TableGridEditor = ({
) : null
}
>
- {isViewSelected && }
+ {(isViewSelected || isTableSelected) && }
{!isUndefined(selectedSchema) && (
diff --git a/studio/components/layouts/AccountLayout/AccountLayout.tsx b/studio/components/layouts/AccountLayout/AccountLayout.tsx
index 7f627e5f9fe..59b733ba78e 100644
--- a/studio/components/layouts/AccountLayout/AccountLayout.tsx
+++ b/studio/components/layouts/AccountLayout/AccountLayout.tsx
@@ -106,7 +106,7 @@ const AccountLayout: FC = ({ children, title, breadcrumbs }) => {
key: 'ext-guides',
icon: `${router.basePath}/img/book-open.svg`,
label: 'API Reference',
- href: 'https://supabase.com/docs/guides/database/api',
+ href: 'https://supabase.com/docs/guides/api',
isExternal: true,
},
],
diff --git a/studio/components/layouts/DocsLayout/DocsLayout.utils.tsx b/studio/components/layouts/DocsLayout/DocsLayout.utils.tsx
index 8f74b748afe..99d4ae82d85 100644
--- a/studio/components/layouts/DocsLayout/DocsLayout.utils.tsx
+++ b/studio/components/layouts/DocsLayout/DocsLayout.utils.tsx
@@ -93,7 +93,7 @@ export const generateDocsMenu = (
{
name: 'API Reference',
key: 'api-reference',
- url: `https://supabase.com/docs/guides/database/api`,
+ url: `https://supabase.com/docs/guides/api`,
icon: ,
items: [],
isExternal: true,
diff --git a/studio/components/layouts/FunctionsLayout.tsx b/studio/components/layouts/FunctionsLayout.tsx
index d7f4cc6a7de..c7271b1d877 100644
--- a/studio/components/layouts/FunctionsLayout.tsx
+++ b/studio/components/layouts/FunctionsLayout.tsx
@@ -3,7 +3,8 @@ import { FC, ReactNode, useState } from 'react'
import { observer } from 'mobx-react-lite'
import { Button, IconExternalLink, IconCode, Modal, IconTerminal } from 'ui'
-import { checkPermissions, useParams, withAuth } from 'hooks'
+import { checkPermissions, withAuth } from 'hooks'
+import { useParams } from 'common/hooks'
import FunctionsNav from '../interfaces/Functions/FunctionsNav'
import BaseLayout from 'components/layouts'
import NoPermission from 'components/ui/NoPermission'
@@ -44,11 +45,11 @@ const FunctionsLayout: FC = ({ title, children }) => {
<>
diff --git a/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx b/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx
index c7c599a6073..3abe0aead41 100644
--- a/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx
+++ b/studio/components/layouts/ProjectLayout/LayoutHeader/LayoutHeader.tsx
@@ -2,7 +2,8 @@ import Link from 'next/link'
import { observer } from 'mobx-react-lite'
import { IS_PLATFORM, PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
-import { useFlag, useParams, useStore } from 'hooks'
+import { useFlag, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import BreadcrumbsView from './BreadcrumbsView'
import OrgDropdown from './OrgDropdown'
import ProjectDropdown from './ProjectDropdown'
diff --git a/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx b/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx
index 599436d22ac..3d534b80502 100644
--- a/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx
+++ b/studio/components/layouts/ProjectLayout/NavigationBar/NavigationBar.tsx
@@ -13,7 +13,7 @@ import {
generateToolRoutes,
} from './NavigationBar.utils'
import NavigationIconButton from './NavigationIconButton'
-import { useParams } from 'hooks/misc/useParams'
+import { useParams } from 'common/hooks'
interface Props {}
diff --git a/studio/components/layouts/ProjectLayout/ProjectContext.tsx b/studio/components/layouts/ProjectLayout/ProjectContext.tsx
index 0c7b2144f31..aa78d0ac3ad 100644
--- a/studio/components/layouts/ProjectLayout/ProjectContext.tsx
+++ b/studio/components/layouts/ProjectLayout/ProjectContext.tsx
@@ -1,4 +1,5 @@
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { observer } from 'mobx-react-lite'
import { createContext, PropsWithChildren, useContext, useMemo } from 'react'
import { Project } from 'types'
diff --git a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx
index 8169fd225be..71a37032b0f 100644
--- a/studio/components/layouts/ProjectLayout/ProjectLayout.tsx
+++ b/studio/components/layouts/ProjectLayout/ProjectLayout.tsx
@@ -2,7 +2,8 @@ import Head from 'next/head'
import { FC, ReactNode, PropsWithChildren, Fragment } from 'react'
import { observer } from 'mobx-react-lite'
import { useRouter } from 'next/router'
-import { useStore, withAuth, useFlag, useParams } from 'hooks'
+import { useStore, withAuth, useFlag } from 'hooks'
+import { useParams } from 'common/hooks'
import { PROJECT_STATUS } from 'lib/constants'
import Connecting from 'components/ui/Loading'
diff --git a/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx b/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx
index 561f596c4c0..fbfacac365a 100644
--- a/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx
+++ b/studio/components/layouts/ProjectLayout/UpgradingState/UpgradingState.tsx
@@ -15,7 +15,8 @@ import {
} from 'ui'
import { DatabaseUpgradeStatus } from '@supabase/shared-types/out/events'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { IS_PLATFORM } from 'lib/constants'
import { DATABASE_UPGRADE_MESSAGES } from './UpgradingState.constants'
import { useProjectUpgradingStatusQuery } from 'data/config/project-upgrade-status-query'
diff --git a/studio/components/layouts/StorageLayout/StorageLayout.tsx b/studio/components/layouts/StorageLayout/StorageLayout.tsx
index cb46bbb60d2..45cc14d9aa1 100644
--- a/studio/components/layouts/StorageLayout/StorageLayout.tsx
+++ b/studio/components/layouts/StorageLayout/StorageLayout.tsx
@@ -3,7 +3,8 @@ import { useRouter } from 'next/router'
import { find, filter, get as _get } from 'lodash'
import { observer } from 'mobx-react-lite'
-import { useParams, useStore, withAuth } from 'hooks'
+import { useStore, withAuth } from 'hooks'
+import { useParams } from 'common/hooks'
import { AutoApiService, useProjectApiQuery } from 'data/config/project-api-query'
import BaseLayout from 'components/layouts'
import ProjectLayout from '../ProjectLayout/ProjectLayout'
diff --git a/studio/components/layouts/StorageLayout/StorageMenu.tsx b/studio/components/layouts/StorageLayout/StorageMenu.tsx
index be1575b9caa..ab998bb3a17 100644
--- a/studio/components/layouts/StorageLayout/StorageMenu.tsx
+++ b/studio/components/layouts/StorageLayout/StorageMenu.tsx
@@ -6,7 +6,8 @@ import * as Tooltip from '@radix-ui/react-tooltip'
import { Button, Menu, IconLoader, Alert, IconEdit } from 'ui'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { checkPermissions, useParams } from 'hooks'
+import { checkPermissions } from 'hooks'
+import { useParams } from 'common/hooks'
import BucketRow from './BucketRow'
import { useStorageStore } from 'localStores/storageExplorer/StorageExplorerStore'
diff --git a/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx b/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx
index bd25b07380e..09e50969b71 100644
--- a/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx
+++ b/studio/components/layouts/TableEditorLayout/TableEditorLayout.tsx
@@ -4,7 +4,8 @@ import { noop } from 'lodash'
import { observer } from 'mobx-react-lite'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { checkPermissions, useParams, useStore } from 'hooks'
+import { checkPermissions, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { Entity } from 'data/entity-types/entity-type-query'
import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
import ProjectLayout from '../ProjectLayout/ProjectLayout'
diff --git a/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx b/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx
index 2ad3668090f..3622dc6ad62 100644
--- a/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx
+++ b/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx
@@ -25,8 +25,9 @@ import * as Tooltip from '@radix-ui/react-tooltip'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import type { PostgresSchema } from '@supabase/postgres-meta'
+import { useParams } from 'common/hooks'
import { BASE_PATH } from 'lib/constants'
-import { checkPermissions, useStore, useParams, useLocalStorage } from 'hooks'
+import { checkPermissions, useStore, useLocalStorage } from 'hooks'
import ProductMenuItem from 'components/ui/ProductMenu/ProductMenuItem'
import { useEntityTypesQuery } from 'data/entity-types/entity-types-infinite-query'
import { ENTITY_TYPE } from 'data/entity-types/entity-type-constants'
@@ -464,7 +465,10 @@ const EntityListItem = ({
}
- onClick={() => onEditTable(entity)}
+ onClick={(e) => {
+ e.stopPropagation()
+ onEditTable(entity)
+ }}
disabled={isLoadingTableMetadata}
>
Edit Table
@@ -472,7 +476,10 @@ const EntityListItem = ({
}
- onClick={() => onDuplicateTable(entity)}
+ onClick={(e) => {
+ e.stopPropagation()
+ onDuplicateTable(entity)
+ }}
disabled={isLoadingTableMetadata}
>
Duplicate Table
@@ -488,7 +495,10 @@ const EntityListItem = ({
}
- onClick={() => onDeleteTable(entity)}
+ onClick={(e) => {
+ e.stopPropagation()
+ onDeleteTable(entity)
+ }}
disabled={isLoadingTableMetadata}
>
Delete Table
diff --git a/studio/components/to-be-cleaned/Docs/Pages/Tables/Introduction.tsx b/studio/components/to-be-cleaned/Docs/Pages/Tables/Introduction.tsx
index ba2441ad011..28723badb85 100644
--- a/studio/components/to-be-cleaned/Docs/Pages/Tables/Introduction.tsx
+++ b/studio/components/to-be-cleaned/Docs/Pages/Tables/Introduction.tsx
@@ -1,6 +1,6 @@
import { FC } from 'react'
import Link from 'next/link'
-import { useParams } from 'hooks'
+import { useParams } from 'common/hooks'
import CodeSnippet from '../../CodeSnippet'
import GeneratingTypes from 'components/interfaces/Docs/GeneratingTypes'
diff --git a/studio/components/ui/InfiniteList.tsx b/studio/components/ui/InfiniteList.tsx
index 60619509629..615cbfcf929 100644
--- a/studio/components/ui/InfiniteList.tsx
+++ b/studio/components/ui/InfiniteList.tsx
@@ -82,8 +82,8 @@ function InfiniteList
({
{({ onItemsRendered, ref }) => (
{
diff --git a/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx b/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx
index 70f1c5eba57..5c36f018f4f 100644
--- a/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx
+++ b/studio/components/ui/ProjectSettings/DisplayConfigSettings.tsx
@@ -2,7 +2,7 @@ import { JwtSecretUpdateStatus } from '@supabase/shared-types/out/events'
import Panel from 'components/ui/Panel'
import { useJwtSecretUpdatingStatusQuery } from 'data/config/jwt-secret-updating-status-query'
import { useProjectSettingsQuery } from 'data/config/project-settings-query'
-import { useParams } from 'hooks'
+import { useParams } from 'common/hooks'
import { DEFAULT_PROJECT_API_SERVICE_ID } from 'lib/constants'
import { FC } from 'react'
import { IconAlertCircle, IconLoader, Input } from 'ui'
diff --git a/studio/components/ui/ProjectUpgradeFailedBanner.tsx b/studio/components/ui/ProjectUpgradeFailedBanner.tsx
index 19bab87189a..807b17f7620 100644
--- a/studio/components/ui/ProjectUpgradeFailedBanner.tsx
+++ b/studio/components/ui/ProjectUpgradeFailedBanner.tsx
@@ -2,7 +2,7 @@ import dayjs from 'dayjs'
import Link from 'next/link'
import { useState } from 'react'
import { Alert, Button, IconX } from 'ui'
-import { useParams } from 'hooks'
+import { useParams } from 'common/hooks'
import { IS_PLATFORM } from 'lib/constants'
import { useProjectUpgradingStatusQuery } from 'data/config/project-upgrade-status-query'
import { DatabaseUpgradeStatus } from '@supabase/shared-types/out/events'
diff --git a/studio/components/ui/UpgradeToPro.tsx b/studio/components/ui/UpgradeToPro.tsx
index 81ba2eedb2d..47259e9f7b7 100644
--- a/studio/components/ui/UpgradeToPro.tsx
+++ b/studio/components/ui/UpgradeToPro.tsx
@@ -4,7 +4,8 @@ import Link from 'next/link'
import { FC, ReactNode } from 'react'
import * as Tooltip from '@radix-ui/react-tooltip'
-import { checkPermissions, useStore, useFlag, useParams } from 'hooks'
+import { checkPermissions, useStore, useFlag } from 'hooks'
+import { useParams } from 'common/hooks'
import { PRICING_TIER_PRODUCT_IDS } from 'lib/constants'
import { PermissionAction } from '@supabase/shared-types/out/constants'
diff --git a/studio/data/database/database-query-constants.ts b/studio/data/database/database-query-constants.ts
index b3879a878a5..1030afd2d68 100644
--- a/studio/data/database/database-query-constants.ts
+++ b/studio/data/database/database-query-constants.ts
@@ -1,3 +1,5 @@
+import minify from 'pg-minify'
+
// https://www.postgresql.org/docs/current/catalog-pg-constraint.html
export enum FOREIGN_KEY_DELETION_ACTION {
@@ -16,3 +18,365 @@ export enum CONSTRAINT_TYPE {
CONSTRAINT_TRIGGER = 't',
EXCLUSION_CONSTRAINT = 'x',
}
+
+// Derived from https://github.com/MichaelDBA/pg_get_tabledef
+
+export const CREATE_PG_GET_TABLEDEF_SQL = minify(
+ /* SQL */ `
+ do $$
+ <>
+ DECLARE
+ cnt int;
+ BEGIN
+ SELECT count(*) into cnt
+ FROM pg_catalog.pg_type t LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
+ AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
+ AND n.nspname <> 'pg_catalog' AND n.nspname <> 'information_schema' AND pg_catalog.pg_type_is_visible(t.oid)
+ AND pg_catalog.format_type(t.oid, NULL) in ('tabledefs');
+ IF cnt = 0 THEN
+ CREATE TYPE pg_temp.tabledefs AS ENUM ('FKEYS_INTERNAL', 'FKEYS_EXTERNAL', 'FKEYS_COMMENTED', 'FKEYS_NONE', 'INCLUDE_TRIGGERS', 'NO_TRIGGERS');
+ END IF;
+ end first_block $$;
+
+ create
+ or replace function pg_temp.pg_get_tabledef (
+ in_schema varchar,
+ in_table varchar,
+ _verbose boolean,
+ variadic arr pg_temp.tabledefs[] default '{}'::pg_temp.tabledefs[]
+ ) returns text language plpgsql volatile as $$
+ DECLARE
+ v_table_ddl text;
+ v_table_oid int;
+ v_colrec record;
+ v_constraintrec record;
+ v_indexrec record;
+ v_primary boolean := False;
+ v_constraint_name text;
+ v_fkey_defs text;
+ v_trigger text := '';
+ v_partition_key text := '';
+ v_partbound text;
+ v_parent text;
+ v_persist text;
+ v_temp text := '';
+ v_relopts text;
+ v_tablespace text;
+ v_pgversion int;
+ bPartition boolean;
+ bInheritance boolean;
+ bRelispartition boolean;
+ constraintarr text[] := '{}';
+ constraintelement text;
+ bSkip boolean;
+ bVerbose boolean := False;
+
+ -- assume defaults for ENUMs at the getgo
+ fkcnt int := 0;
+ trigcnt int := 0;
+ fktype pg_temp.tabledefs := 'FKEYS_INTERNAL';
+ trigtype pg_temp.tabledefs := 'NO_TRIGGERS';
+ arglen integer;
+ vargs text;
+ avarg pg_temp.tabledefs;
+
+ -- exception variables
+ v_ret text;
+ v_diag1 text;
+ v_diag2 text;
+ v_diag3 text;
+ v_diag4 text;
+ v_diag5 text;
+ v_diag6 text;
+
+ BEGIN
+ IF _verbose THEN bVerbose = True; END IF;
+
+ arglen := array_length($4, 1);
+ IF arglen IS NULL THEN
+ -- nothing to do, so assume defaults
+ NULL;
+ ELSE
+ -- loop thru args
+ -- IF 'NO_TRIGGERS' = ANY ($4)
+ -- select array_to_string($4, ',', '***') INTO vargs;
+ IF bVerbose THEN RAISE NOTICE 'arguments=%', $4; END IF;
+ FOREACH avarg IN ARRAY $4 LOOP
+ IF bVerbose THEN RAISE INFO 'arg=%', avarg; END IF;
+ IF avarg = 'FKEYS_INTERNAL' OR avarg = 'FKEYS_EXTERNAL' OR avarg = 'FKEYS_COMMENTED' THEN
+ fkcnt = fkcnt + 1;
+ fktype = avarg;
+ ELSEIF avarg = 'INCLUDE_TRIGGERS' OR avarg = 'NO_TRIGGERS' THEN
+ trigcnt = trigcnt + 1;
+ trigtype = avarg;
+ END IF;
+ END LOOP;
+ IF fkcnt > 1 THEN
+ RAISE WARNING 'Only one foreign key option can be provided. You provided %', fkcnt;
+ RETURN '';
+ ELSEIF trigcnt > 1 THEN
+ RAISE WARNING 'Only one trigger option can be provided. You provided %', trigcnt;
+ RETURN '';
+ END IF;
+ END IF;
+
+ SELECT c.oid, (select setting from pg_settings where name = 'server_version_num') INTO v_table_oid, v_pgversion FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind in ('r','p') AND c.relname = in_table AND n.nspname = in_schema;
+ IF bVerbose THEN RAISE NOTICE 'version=%', v_pgversion; END IF;
+
+ -- throw an error if table was not found
+ IF (v_table_oid IS NULL) THEN
+ RAISE EXCEPTION 'table does not exist';
+ END IF;
+
+ -- get user-defined tablespaces if applicable
+ SELECT tablespace INTO v_temp FROM pg_tables WHERE schemaname = in_schema and tablename = in_table and tablespace IS NOT NULL;
+ IF v_temp IS NULL THEN
+ v_tablespace := 'TABLESPACE pg_default';
+ ELSE
+ v_tablespace := 'TABLESPACE ' || v_temp;
+ END IF;
+
+ -- also see if there are any SET commands for this table, ie, autovacuum_enabled=off, fillfactor=70
+ WITH relopts AS (SELECT unnest(c.reloptions) relopts FROM pg_class c, pg_namespace n WHERE n.nspname = in_schema and n.oid = c.relnamespace and c.relname = in_table)
+ SELECT string_agg(r.relopts, ', ') as relopts INTO v_temp from relopts r;
+ IF v_temp IS NULL THEN
+ v_relopts := '';
+ ELSE
+ v_relopts := ' WITH (' || v_temp || ')';
+ END IF;
+
+ -- -----------------------------------------------------------------------------------
+ -- Create table defs for partitions/children using inheritance or declarative methods.
+ -- inheritance: pg_class.relkind = 'r' pg_class.relispartition=false pg_class.relpartbound is NULL
+ -- declarative: pg_class.relkind = 'r' pg_class.relispartition=true pg_class.relpartbound is NOT NULL
+ -- -----------------------------------------------------------------------------------
+ v_partbound := '';
+ bPartition := False;
+ bInheritance := False;
+ IF v_pgversion < 100000 THEN
+ SELECT c2.relname parent INTO v_parent from pg_class c1, pg_namespace n, pg_inherits i, pg_class c2
+ WHERE n.nspname = in_schema and n.oid = c1.relnamespace and c1.relname = in_table and c1.oid = i.inhrelid and i.inhparent = c2.oid and c1.relkind = 'r';
+ IF (v_parent IS NOT NULL) THEN
+ bPartition := True;
+ bInheritance := True;
+ END IF;
+ ELSE
+ SELECT c2.relname parent, c1.relispartition, pg_get_expr(c1.relpartbound, c1.oid, true) INTO v_parent, bRelispartition, v_partbound from pg_class c1, pg_namespace n, pg_inherits i, pg_class c2
+ WHERE n.nspname = in_schema and n.oid = c1.relnamespace and c1.relname = in_table and c1.oid = i.inhrelid and i.inhparent = c2.oid and c1.relkind = 'r';
+ IF (v_parent IS NOT NULL) THEN
+ bPartition := True;
+ IF bRelispartition THEN
+ bInheritance := False;
+ ELSE
+ bInheritance := True;
+ END IF;
+ END IF;
+ END IF;
+
+ IF bPartition THEN
+ IF bInheritance THEN
+ -- inheritance-based
+ v_table_ddl := 'CREATE TABLE ' || in_schema || '.' || in_table || '( '|| E'\\n';
+ -- Jump to constraints section to add the check constraints
+ ELSE
+ -- declarative-based
+ IF v_relopts <> '' THEN
+ v_table_ddl := 'CREATE TABLE ' || in_schema || '.' || in_table || ' PARTITION OF ' || in_schema || '.' || v_parent || ' ' || v_partbound || v_relopts || ' ' || v_tablespace || '; ' || E'\\n';
+ ELSE
+ v_table_ddl := 'CREATE TABLE ' || in_schema || '.' || in_table || ' PARTITION OF ' || in_schema || '.' || v_parent || ' ' || v_partbound || ' ' || v_tablespace || '; ' || E'\\n';
+ END IF;
+ -- Jump to constraints and index section to add the check constraints and indexes and perhaps FKeys
+ END IF;
+ END IF;
+ IF bVerbose THEN RAISE INFO '(1)tabledef so far: %', v_table_ddl; END IF;
+
+ IF NOT bPartition THEN
+ -- see if this is unlogged or temporary table
+ select c.relpersistence into v_persist from pg_class c, pg_namespace n where n.nspname = in_schema and n.oid = c.relnamespace and c.relname = in_table and c.relkind = 'r';
+ IF v_persist = 'u' THEN
+ v_temp := 'UNLOGGED';
+ ELSIF v_persist = 't' THEN
+ v_temp := 'TEMPORARY';
+ ELSE
+ v_temp := '';
+ END IF;
+ END IF;
+
+ -- start the create definition for regular tables unless we are in progress creating an inheritance-based child table
+ IF NOT bPartition THEN
+ v_table_ddl := 'CREATE ' || v_temp || ' TABLE ' || in_schema || '.' || in_table || ' (' || E'\\n';
+ END IF;
+ -- RAISE INFO 'DEBUG2: tabledef so far: %', v_table_ddl;
+ -- define all of the columns in the table unless we are in progress creating an inheritance-based child table
+ IF NOT bPartition THEN
+ FOR v_colrec IN
+ SELECT c.column_name, c.data_type, c.udt_name, c.character_maximum_length, c.is_nullable, c.column_default, c.numeric_precision, c.numeric_scale, c.is_identity, c.identity_generation
+ FROM information_schema.columns c WHERE (table_schema, table_name) = (in_schema, in_table) ORDER BY ordinal_position
+ LOOP
+ v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
+ || v_colrec.column_name || ' '
+ -- || CASE WHEN v_colrec.data_type = 'USER-DEFINED' THEN in_schema || '.' || v_colrec.udt_name ELSE v_colrec.data_type END
+ || CASE WHEN v_colrec.udt_name in ('geometry', 'box2d', 'box2df', 'box3d', 'geography', 'geometry_dump', 'gidx', 'spheroid', 'valid_detail')
+ THEN v_colrec.udt_name WHEN v_colrec.data_type = 'USER-DEFINED' THEN in_schema || '.' || v_colrec.udt_name ELSE v_colrec.data_type END
+ || CASE WHEN v_colrec.is_identity = 'YES' THEN CASE WHEN v_colrec.identity_generation = 'ALWAYS' THEN ' GENERATED ALWAYS AS IDENTITY' ELSE ' GENERATED BY DEFAULT AS IDENTITY' END ELSE '' END
+ || CASE WHEN v_colrec.character_maximum_length IS NOT NULL THEN ('(' || v_colrec.character_maximum_length || ')')
+ WHEN v_colrec.numeric_precision > 0 AND v_colrec.numeric_scale > 0 THEN '(' || v_colrec.numeric_precision || ',' || v_colrec.numeric_scale || ')'
+ ELSE '' END || ' '
+ || CASE WHEN v_colrec.is_nullable = 'NO' THEN 'NOT NULL' ELSE 'NULL' END
+ || CASE WHEN v_colrec.column_default IS NOT null THEN (' DEFAULT ' || v_colrec.column_default) ELSE '' END
+ || ',' || E'\n';
+ END LOOP;
+ END IF;
+ IF bVerbose THEN RAISE INFO '(2)tabledef so far: %', v_table_ddl; END IF;
+
+ -- define all the constraints
+ FOR v_constraintrec IN
+ SELECT con.conname as constraint_name, con.contype as constraint_type,
+ CASE
+ WHEN con.contype = 'p' THEN 1 -- primary key constraint
+ WHEN con.contype = 'u' THEN 2 -- unique constraint
+ WHEN con.contype = 'f' THEN 3 -- foreign key constraint
+ WHEN con.contype = 'c' THEN 4
+ ELSE 5
+ END as type_rank,
+ pg_get_constraintdef(con.oid) as constraint_definition
+ FROM pg_catalog.pg_constraint con JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace
+ WHERE nsp.nspname = in_schema AND rel.relname = in_table ORDER BY type_rank
+ LOOP
+ IF v_constraintrec.type_rank = 1 THEN
+ v_primary := True;
+ v_constraint_name := v_constraintrec.constraint_name;
+ IF bPartition THEN
+ continue;
+ END IF;
+ END IF;
+ -- RAISE INFO 'DEBUG4: constraint name= %', v_constraintrec.constraint_name;
+ constraintarr := constraintarr || v_constraintrec.constraint_name;
+
+ IF fktype <> 'FKEYS_INTERNAL' AND v_constraintrec.constraint_type = 'f' THEN
+ continue;
+ END IF;
+
+ v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
+ || 'CONSTRAINT' || ' '
+ || v_constraintrec.constraint_name || ' '
+ || v_constraintrec.constraint_definition
+ || ',' || E'\\n';
+ END LOOP;
+ IF bVerbose THEN RAISE INFO '(3)tabledef so far: %', v_table_ddl; END IF;
+
+ -- drop the last comma before ending the create statement
+ v_table_ddl = substr(v_table_ddl, 0, length(v_table_ddl) - 1) || E'\\n';
+
+ -- ---------------------------------------------------------------------------
+ -- at this point we have everything up to the last table-enclosing parenthesis
+ -- ---------------------------------------------------------------------------
+ IF bVerbose THEN RAISE INFO '(4)tabledef so far: %', v_table_ddl; END IF;
+
+ -- See if this is an inheritance-based child table and finish up the table create.
+ IF bPartition and bInheritance THEN
+ v_table_ddl := v_table_ddl || ') INHERITS (' || in_schema || '.' || v_parent || ') ' || E'\\n' || v_relopts || ' ' || v_tablespace || ';' || E'\\n';
+ END IF;
+
+ IF v_pgversion >= 100000 AND NOT bPartition and NOT bInheritance THEN
+ -- See if this is a partitioned table (pg_class.relkind = 'p') and add the partitioned key
+ SELECT pg_get_partkeydef(c1.oid) as partition_key INTO v_partition_key FROM pg_class c1 JOIN pg_namespace n ON (n.oid = c1.relnamespace) LEFT JOIN pg_partitioned_table p ON (c1.oid = p.partrelid)
+ WHERE n.nspname = in_schema and n.oid = c1.relnamespace and c1.relname = in_table and c1.relkind = 'p';
+
+ IF v_partition_key IS NOT NULL AND v_partition_key <> '' THEN
+ -- add partition clause
+ -- NOTE: cannot specify default tablespace for partitioned relations
+ -- v_table_ddl := v_table_ddl || ') PARTITION BY ' || v_partition_key || ' ' || v_tablespace || ';' || E'\\n';
+ v_table_ddl := v_table_ddl || ') PARTITION BY ' || v_partition_key || ';' || E'\\n';
+ ELSEIF v_relopts <> '' THEN
+ v_table_ddl := v_table_ddl || ') ' || v_relopts || ' ' || v_tablespace || ';' || E'\\n';
+ ELSE
+ -- end the create definition
+ v_table_ddl := v_table_ddl || ') ' || v_tablespace || ';' || E'\\n';
+ END IF;
+ END IF;
+
+ IF bVerbose THEN RAISE INFO '(5)tabledef so far: %', v_table_ddl; END IF;
+
+ -- Add closing paren for regular tables
+ -- IF NOT bPartition THEN
+ -- v_table_ddl := v_table_ddl || ') ' || v_relopts || ' ' || v_tablespace || E';\\n';
+ -- END IF;
+ -- RAISE NOTICE 'ddlsofar3: %', v_table_ddl;
+
+ -- create indexes
+ FOR v_indexrec IN
+ SELECT indexdef, COALESCE(tablespace, 'pg_default') as tablespace, indexname FROM pg_indexes WHERE (schemaname, tablename) = (in_schema, in_table)
+ LOOP
+ -- RAISE INFO 'DEBUG6: indexname=%', v_indexrec.indexname;
+ -- loop through constraints and skip ones already defined
+ bSkip = False;
+ FOREACH constraintelement IN ARRAY constraintarr
+ LOOP
+ IF constraintelement = v_indexrec.indexname THEN
+ -- RAISE INFO 'DEBUG7: skipping index, %', v_indexrec.indexname;
+ bSkip = True;
+ EXIT;
+ END IF;
+ END LOOP;
+ if bSkip THEN CONTINUE; END IF;
+
+ -- Add IF NOT EXISTS clause so partition index additions will not be created if declarative partition in effect and index already created on parent
+ v_indexrec.indexdef := REPLACE(v_indexrec.indexdef, 'CREATE INDEX', 'CREATE INDEX IF NOT EXISTS');
+ -- RAISE INFO 'DEBUG8: adding index, %', v_indexrec.indexname;
+
+ -- NOTE: cannot specify default tablespace for partitioned relations
+ IF v_partition_key IS NOT NULL AND v_partition_key <> '' THEN
+ v_table_ddl := v_table_ddl || v_indexrec.indexdef || ';' || E'\\n';
+ ELSE
+ v_table_ddl := v_table_ddl || v_indexrec.indexdef || ' TABLESPACE ' || v_indexrec.tablespace || ';' || E'\\n';
+ END IF;
+
+ END LOOP;
+ IF bVerbose THEN RAISE INFO '(6)tabledef so far: %', v_table_ddl; END IF;
+
+ -- Handle external foreign key defs here if applicable.
+ IF fktype = 'FKEYS_EXTERNAL' THEN
+ SELECT 'ALTER TABLE ONLY ' || n.nspname || '.' || c2.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' into v_fkey_defs
+ FROM pg_constraint r, pg_class c1, pg_namespace n, pg_class c2 where r.conrelid = c1.oid and r.contype = 'f' and n.nspname = in_schema and n.oid = r.connamespace and r.conrelid = c2.oid and c2.relname = in_table;
+ IF v_fkey_defs IS NOT NULL THEN
+ v_table_ddl := v_table_ddl || v_fkey_defs;
+ END IF;
+ ELSIF fktype = 'FKEYS_COMMENTED' THEN
+ SELECT '-- ALTER TABLE ONLY ' || n.nspname || '.' || c2.relname || ' ADD CONSTRAINT ' || r.conname || ' ' || pg_catalog.pg_get_constraintdef(r.oid, true) || ';' into v_fkey_defs
+ FROM pg_constraint r, pg_class c1, pg_namespace n, pg_class c2 where r.conrelid = c1.oid and r.contype = 'f' and n.nspname = in_schema and n.oid = r.connamespace and r.conrelid = c2.oid and c2.relname = in_table;
+ IF v_fkey_defs IS NOT NULL THEN
+ v_table_ddl := v_table_ddl || v_fkey_defs;
+ END IF;
+ END IF;
+ IF bVerbose THEN RAISE INFO '(7)tabledef so far: %', v_table_ddl; END IF;
+
+ IF trigtype = 'INCLUDE_TRIGGERS' THEN
+ select pg_get_triggerdef(t.oid, True) || ';' INTO v_trigger FROM pg_trigger t, pg_class c, pg_namespace n
+ WHERE n.nspname = in_schema and n.oid = c.relnamespace and c.relname = in_table and c.relkind = 'r' and t.tgrelid = c.oid and NOT t.tgisinternal;
+ IF v_trigger <> '' THEN
+ v_table_ddl := v_table_ddl || v_trigger;
+ END IF;
+ END IF;
+
+ -- add empty line
+ v_table_ddl := v_table_ddl || E'\\n';
+
+ RETURN v_table_ddl;
+
+ EXCEPTION
+ WHEN others THEN
+ BEGIN
+ GET STACKED DIAGNOSTICS v_diag1 = MESSAGE_TEXT, v_diag2 = PG_EXCEPTION_DETAIL, v_diag3 = PG_EXCEPTION_HINT, v_diag4 = RETURNED_SQLSTATE, v_diag5 = PG_CONTEXT, v_diag6 = PG_EXCEPTION_CONTEXT;
+ -- v_ret := 'line=' || v_diag6 || '. '|| v_diag4 || '. ' || v_diag1 || ' .' || v_diag2 || ' .' || v_diag3;
+ v_ret := 'line=' || v_diag6 || '. '|| v_diag4 || '. ' || v_diag1;
+ RAISE EXCEPTION '%', v_ret;
+ -- put additional coding here if necessarY
+ RETURN '';
+ END;
+
+ END;
+ $$;`,
+ { compress: true, removeAll: true }
+)
diff --git a/studio/data/database/foreign-key-constraints-query.ts b/studio/data/database/foreign-key-constraints-query.ts
index d7ec6f5b339..4a218079e88 100644
--- a/studio/data/database/foreign-key-constraints-query.ts
+++ b/studio/data/database/foreign-key-constraints-query.ts
@@ -2,7 +2,7 @@ import { UseQueryOptions } from '@tanstack/react-query'
import { useCallback } from 'react'
import { ExecuteSqlData, useExecuteSqlPrefetch, useExecuteSqlQuery } from '../sql/execute-sql-query'
-type getForeignKeyConstraintsVariables = {
+type GetForeignKeyConstraintsVariables = {
schema?: string
}
@@ -20,7 +20,7 @@ export type ForeignKeyConstraint = {
target_columns: string
}
-export const getForeignKeyConstraintsQuery = ({ schema }: getForeignKeyConstraintsVariables) => {
+export const getForeignKeyConstraintsQuery = ({ schema }: GetForeignKeyConstraintsVariables) => {
const sql = /* SQL */ `
SELECT
con.oid as id,
@@ -72,7 +72,7 @@ WHERE
return sql
}
-export type ForeignKeyConstraintsVariables = getForeignKeyConstraintsVariables & {
+export type ForeignKeyConstraintsVariables = GetForeignKeyConstraintsVariables & {
projectRef?: string
connectionString?: string
}
diff --git a/studio/data/database/table-definition-query.ts b/studio/data/database/table-definition-query.ts
new file mode 100644
index 00000000000..1590c7f072c
--- /dev/null
+++ b/studio/data/database/table-definition-query.ts
@@ -0,0 +1,68 @@
+import { UseQueryOptions } from '@tanstack/react-query'
+import { useCallback } from 'react'
+import { ExecuteSqlData, useExecuteSqlPrefetch, useExecuteSqlQuery } from '../sql/execute-sql-query'
+import { CREATE_PG_GET_TABLEDEF_SQL } from './database-query-constants'
+
+type GetTableDefinition = {
+ schema?: string
+ name?: string
+}
+
+export const getTableDefinitionQuery = ({ schema = 'public', name }: GetTableDefinition) => {
+ const sql = /* SQL */ `
+ ${CREATE_PG_GET_TABLEDEF_SQL}
+
+ select pg_temp.pg_get_tabledef (
+ '${schema}',
+ '${name}',
+ false,
+ 'FKEYS_INTERNAL',
+ 'INCLUDE_TRIGGERS'
+ ) as definition
+ `.trim()
+
+ return sql
+}
+
+export type TableDefinitionVariables = GetTableDefinition & {
+ projectRef?: string
+ connectionString?: string
+}
+
+export type TableDefinitionData = string
+export type TableDefinitionError = unknown
+
+export const useTableDefinitionQuery = (
+ { projectRef, connectionString, schema, name }: TableDefinitionVariables,
+ options: UseQueryOptions = {}
+) => {
+ return useExecuteSqlQuery(
+ {
+ projectRef,
+ connectionString,
+ sql: getTableDefinitionQuery({ schema, name }),
+ queryKey: ['table-definition', schema, name],
+ },
+ {
+ select(data) {
+ return data.result[0].definition.trim()
+ },
+ ...options,
+ }
+ )
+}
+
+export const useTableDefinitionQueryPrefetch = () => {
+ const prefetch = useExecuteSqlPrefetch()
+
+ return useCallback(
+ ({ projectRef, connectionString, schema, name }: TableDefinitionVariables) =>
+ prefetch({
+ projectRef,
+ connectionString,
+ sql: getTableDefinitionQuery({ schema, name }),
+ queryKey: ['table-definition', schema, name],
+ }),
+ [prefetch]
+ )
+}
diff --git a/studio/data/database/view-definition-query.ts b/studio/data/database/view-definition-query.ts
index 051b012aac3..102f887347f 100644
--- a/studio/data/database/view-definition-query.ts
+++ b/studio/data/database/view-definition-query.ts
@@ -2,7 +2,8 @@ import { UseQueryOptions } from '@tanstack/react-query'
import { useCallback } from 'react'
import { ExecuteSqlData, useExecuteSqlPrefetch, useExecuteSqlQuery } from '../sql/execute-sql-query'
-type getViewDefinition = {
+type GetViewDefinition = {
+ schema?: string
name?: string
}
@@ -20,31 +21,34 @@ type ViewDefinition = {
target_columns: string
}
-export const getViewDefinitionQuery = ({ name }: getViewDefinition) => {
+export const getViewDefinitionQuery = ({ schema, name }: GetViewDefinition) => {
+ const fullName = [schema, name].filter(Boolean).join('.')
+
const sql = /* SQL */ `
-select pg_get_viewdef('${name}', true) as definition
-`.trim()
+ select pg_get_viewdef('${fullName}', true) as definition
+ `.trim()
+
return sql
}
-export type ViewDefinitionVariables = getViewDefinition & {
+export type ViewDefinitionVariables = GetViewDefinition & {
projectRef?: string
connectionString?: string
}
-export type ViewDefinitionData = any
+export type ViewDefinitionData = string
export type ViewDefinitionError = unknown
export const useViewDefinitionQuery = (
- { projectRef, connectionString, name }: ViewDefinitionVariables,
+ { projectRef, connectionString, schema, name }: ViewDefinitionVariables,
options: UseQueryOptions = {}
) => {
return useExecuteSqlQuery(
{
projectRef,
connectionString,
- sql: getViewDefinitionQuery({ name }),
- queryKey: ['view-definition', name],
+ sql: getViewDefinitionQuery({ schema, name }),
+ queryKey: ['view-definition', schema, name],
},
{
select(data) {
@@ -59,12 +63,12 @@ export const useViewDefinitionQueryPrefetch = () => {
const prefetch = useExecuteSqlPrefetch()
return useCallback(
- ({ projectRef, connectionString, name }: ViewDefinitionVariables) =>
+ ({ projectRef, connectionString, schema, name }: ViewDefinitionVariables) =>
prefetch({
projectRef,
connectionString,
- sql: getViewDefinitionQuery({ name }),
- queryKey: ['view-definition', name],
+ sql: getViewDefinitionQuery({ schema, name }),
+ queryKey: ['view-definition', schema, name],
}),
[prefetch]
)
diff --git a/studio/hooks/index.ts b/studio/hooks/index.ts
index bcfb55c58e7..83e5a772641 100644
--- a/studio/hooks/index.ts
+++ b/studio/hooks/index.ts
@@ -2,7 +2,6 @@ export * from './misc/useStore'
export * from './misc/withAuth'
export * from './misc/useOptimisticSqlSnippetCreate'
export * from './misc/useImmutableValue'
-export * from './misc/useParams'
export * from './misc/useCheckPermissions'
export * from './misc/useLocalStorage'
export * from './deprecated'
diff --git a/studio/hooks/misc/useUpgradePrompt.tsx b/studio/hooks/misc/useUpgradePrompt.tsx
index f19f090c302..f58aca0c45d 100644
--- a/studio/hooks/misc/useUpgradePrompt.tsx
+++ b/studio/hooks/misc/useUpgradePrompt.tsx
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
-import { useParams } from 'hooks'
+import { useParams } from 'common/hooks'
import { maybeShowUpgradePrompt } from 'components/interfaces/Settings/Logs'
import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query'
diff --git a/studio/hooks/misc/withAuth.tsx b/studio/hooks/misc/withAuth.tsx
index e2c0ddcc643..35c36197825 100644
--- a/studio/hooks/misc/withAuth.tsx
+++ b/studio/hooks/misc/withAuth.tsx
@@ -3,7 +3,8 @@ import { useRouter } from 'next/router'
import { ComponentType, useEffect } from 'react'
import { usePermissionsQuery } from 'data/permissions/permissions-query'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { useAuth } from 'lib/auth'
import { IS_PLATFORM } from 'lib/constants'
import { getReturnToPath, STORAGE_KEY } from 'lib/gotrue'
diff --git a/studio/package.json b/studio/package.json
index b6af6c9187b..919e962cd5b 100644
--- a/studio/package.json
+++ b/studio/package.json
@@ -65,6 +65,7 @@
"papaparse": "^5.3.1",
"parse-numeric-range": "^1.3.0",
"path-to-regexp": "^6.2.0",
+ "pg-minify": "^1.6.3",
"prism-react-renderer": "^1.2.1",
"react": "^17.0.2",
"react-beautiful-dnd": "^13.1.0",
@@ -88,6 +89,7 @@
"scheduler": "^0.20.2",
"semver": "^6.3.0",
"shared-data": "*",
+ "sql-formatter": "^12.2.0",
"sqlstring": "^2.3.2",
"ui": "*",
"uniforms-bootstrap4": "^3.6.2",
@@ -142,6 +144,7 @@
"postcss": "^8.4.6",
"prettier": "^2.7.1",
"storybook-dark-mode": "^1.0.4",
+ "superjson": "^1.12.2",
"tailwindcss": "^3.0.15",
"typescript": "4.8.4"
}
diff --git a/studio/pages/_app.tsx b/studio/pages/_app.tsx
index 054c4fe8cfa..8598acb612d 100644
--- a/studio/pages/_app.tsx
+++ b/studio/pages/_app.tsx
@@ -40,7 +40,7 @@ import { PortalToast, RouteValidationWrapper, AppBannerWrapper } from 'component
import PageTelemetry from 'components/ui/PageTelemetry'
import FlagProvider from 'components/ui/Flag/FlagProvider'
import useAutoAuthRedirect from 'hooks/misc/useAutoAuthRedirect'
-// import CommandMenuProvider from 'ui/src/components/Command/CommandMenuProvider'
+
import { TooltipProvider } from '@radix-ui/react-tooltip'
dayjs.extend(customParseFormat)
@@ -117,11 +117,7 @@ function CustomApp({ Component, pageProps }: AppPropsWithLayout) {
-
- {/* */}
- {getLayout()}
- {/* */}
-
+ {getLayout()}
diff --git a/studio/pages/new/[slug].tsx b/studio/pages/new/[slug].tsx
index 52b630ad0a2..67305391615 100644
--- a/studio/pages/new/[slug].tsx
+++ b/studio/pages/new/[slug].tsx
@@ -21,7 +21,8 @@ import {
PRICING_TIER_FREE_KEY,
PRICING_TIER_PRODUCT_IDS,
} from 'lib/constants'
-import { useStore, useFlag, withAuth, checkPermissions, useParams } from 'hooks'
+import { useStore, useFlag, withAuth, checkPermissions } from 'hooks'
+import { useParams } from 'common/hooks'
import { useFreeProjectLimitCheckQuery } from 'data/organizations/free-project-limit-check-query'
import { WizardLayoutWithoutAuth } from 'components/layouts'
diff --git a/studio/pages/org/[slug]/billing.tsx b/studio/pages/org/[slug]/billing.tsx
index b8510f913f3..83c44735b40 100644
--- a/studio/pages/org/[slug]/billing.tsx
+++ b/studio/pages/org/[slug]/billing.tsx
@@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite'
import { Tabs } from 'ui'
import { NextPageWithLayout } from 'types'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import Loading from 'components/ui/Loading'
import { OrganizationLayout } from 'components/layouts'
import { BillingSettings } from 'components/interfaces/Organization'
diff --git a/studio/pages/org/[slug]/general.tsx b/studio/pages/org/[slug]/general.tsx
index 43cd730e8a2..f07415ba47e 100644
--- a/studio/pages/org/[slug]/general.tsx
+++ b/studio/pages/org/[slug]/general.tsx
@@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite'
import { Tabs } from 'ui'
import { NextPageWithLayout } from 'types'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import Loading from 'components/ui/Loading'
import { OrganizationLayout } from 'components/layouts'
import { GeneralSettings } from 'components/interfaces/Organization'
diff --git a/studio/pages/org/[slug]/invoices.tsx b/studio/pages/org/[slug]/invoices.tsx
index f6ee9ee7eae..5ccf1ebc825 100644
--- a/studio/pages/org/[slug]/invoices.tsx
+++ b/studio/pages/org/[slug]/invoices.tsx
@@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite'
import { Tabs } from 'ui'
import { NextPageWithLayout } from 'types'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import Loading from 'components/ui/Loading'
import { OrganizationLayout } from 'components/layouts'
import { InvoicesSettings } from 'components/interfaces/Organization'
diff --git a/studio/pages/org/[slug]/team.tsx b/studio/pages/org/[slug]/team.tsx
index f90d16a6486..7f96623cd61 100644
--- a/studio/pages/org/[slug]/team.tsx
+++ b/studio/pages/org/[slug]/team.tsx
@@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite'
import { Tabs } from 'ui'
import { NextPageWithLayout } from 'types'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import Loading from 'components/ui/Loading'
import { OrganizationLayout } from 'components/layouts'
import { TeamSettings } from 'components/interfaces/Organization'
diff --git a/studio/pages/project/[ref]/api/graphiql.tsx b/studio/pages/project/[ref]/api/graphiql.tsx
index 5317b8e57eb..a719540f4c8 100644
--- a/studio/pages/project/[ref]/api/graphiql.tsx
+++ b/studio/pages/project/[ref]/api/graphiql.tsx
@@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite'
import { createGraphiQLFetcher, Fetcher } from '@graphiql/toolkit'
import { NextPageWithLayout } from 'types'
-import { useParams, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
+import { useStore } from 'hooks'
import { API_URL, IS_PLATFORM } from 'lib/constants'
import ExtensionCard from 'components/interfaces/Database/Extensions/ExtensionCard'
import GraphiQL from 'components/interfaces/GraphQL/GraphiQL'
diff --git a/studio/pages/project/[ref]/api/index.tsx b/studio/pages/project/[ref]/api/index.tsx
index 3ceb55c3383..a890cfd8de3 100644
--- a/studio/pages/project/[ref]/api/index.tsx
+++ b/studio/pages/project/[ref]/api/index.tsx
@@ -3,7 +3,8 @@ import { FC, createContext, useContext, useEffect, useState } from 'react'
import { observer, useLocalObservable } from 'mobx-react-lite'
import { NextPageWithLayout } from 'types'
-import { checkPermissions, useParams, useStore } from 'hooks'
+import { checkPermissions, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { snakeToCamel } from 'lib/helpers'
import { useProjectApiQuery } from 'data/config/project-api-query'
import { DocsLayout } from 'components/layouts'
diff --git a/studio/pages/project/[ref]/auth/policies.tsx b/studio/pages/project/[ref]/auth/policies.tsx
index 3d4b440d580..5fe4fc3171b 100644
--- a/studio/pages/project/[ref]/auth/policies.tsx
+++ b/studio/pages/project/[ref]/auth/policies.tsx
@@ -6,7 +6,8 @@ import { PostgresTable, PostgresPolicy } from '@supabase/postgres-meta'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { NextPageWithLayout } from 'types'
-import { checkPermissions, useParams, useStore } from 'hooks'
+import { checkPermissions, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { AuthLayout } from 'components/layouts'
import { Policies } from 'components/interfaces/Auth/Policies'
import NoPermission from 'components/ui/NoPermission'
diff --git a/studio/pages/project/[ref]/auth/url-configuration.tsx b/studio/pages/project/[ref]/auth/url-configuration.tsx
index fd7ced97616..b0415afb0c9 100644
--- a/studio/pages/project/[ref]/auth/url-configuration.tsx
+++ b/studio/pages/project/[ref]/auth/url-configuration.tsx
@@ -7,7 +7,7 @@ import { useStore, checkPermissions } from 'hooks'
import { AuthLayout } from 'components/layouts'
import NoPermission from 'components/ui/NoPermission'
-import RedirectDomains from 'components/interfaces/Auth/RedirectDomains/RedirectDomains'
+import RedirectUrls from 'components/interfaces/Auth/RedirectUrls/RedirectUrls'
import { FormsContainer } from 'components/ui/Forms'
import SiteUrl from 'components/interfaces/Auth/SiteUrl/SiteUrl'
@@ -26,7 +26,7 @@ const URLConfiguration: NextPageWithLayout = () => {
return (
-
+
)
} else {
diff --git a/studio/pages/project/[ref]/database/hooks/index.tsx b/studio/pages/project/[ref]/database/hooks/index.tsx
index 9b96f0f9390..d95c83a440d 100644
--- a/studio/pages/project/[ref]/database/hooks/index.tsx
+++ b/studio/pages/project/[ref]/database/hooks/index.tsx
@@ -92,7 +92,6 @@ const HooksPage: NextPageWithLayout = () => {
disabled={!canCreateWebhooks}
disabledMessage="You need additional permissions to enable webhooks"
>
-
Database Webhooks can be used to trigger serverless functions or send requests to an
HTTP endpoint.
diff --git a/studio/pages/project/[ref]/database/tables.tsx b/studio/pages/project/[ref]/database/tables.tsx
index fa709fc8e06..5ce9684374e 100644
--- a/studio/pages/project/[ref]/database/tables.tsx
+++ b/studio/pages/project/[ref]/database/tables.tsx
@@ -1,3 +1,4 @@
+import { useQueryClient } from '@tanstack/react-query'
import { useState, useEffect } from 'react'
import { observer } from 'mobx-react-lite'
import { isUndefined } from 'lodash'
@@ -5,16 +6,22 @@ import type { PostgresColumn, PostgresTable } from '@supabase/postgres-meta'
import { Modal } from 'ui'
import { useStore } from 'hooks'
+import { sqlKeys } from 'data/sql/keys'
import { DatabaseLayout } from 'components/layouts'
import ConfirmationModal from 'components/ui/ConfirmationModal'
import { TableList, ColumnList } from 'components/interfaces/Database'
import { SidePanelEditor } from 'components/interfaces/TableGridEditor'
+import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
import { NextPageWithLayout } from 'types'
const DatabaseTables: NextPageWithLayout = () => {
const { meta, ui } = useStore()
+ const { project } = useProjectContext()
+
+ const queryClient = useQueryClient()
+
const [isDeleting, setIsDeleting] = useState(false)
const [selectedSchema, setSelectedSchema] = useState('public')
const [selectedTable, setSelectedTable] = useState()
@@ -108,6 +115,21 @@ const DatabaseTables: NextPageWithLayout = () => {
category: 'success',
message: `Successfully removed ${selectedColumnToDelete.name}.`,
})
+
+ queryClient.invalidateQueries(sqlKeys.query(project?.ref, ['foreign-key-constraints']))
+ await Promise.all([
+ meta.tables.loadById(selectedColumnToDelete!.table_id),
+ queryClient.invalidateQueries(
+ sqlKeys.query(project?.ref, [selectedTable!.schema, selectedTable!.name])
+ ),
+ queryClient.invalidateQueries(
+ sqlKeys.query(project?.ref, [
+ 'table-definition',
+ selectedTable!.schema,
+ selectedTable!.name,
+ ])
+ ),
+ ])
}
} catch (error: any) {
ui.setNotification({
diff --git a/studio/pages/project/[ref]/editor/[id].tsx b/studio/pages/project/[ref]/editor/[id].tsx
index 9fd9ca0e2dc..fd6e82e003d 100644
--- a/studio/pages/project/[ref]/editor/[id].tsx
+++ b/studio/pages/project/[ref]/editor/[id].tsx
@@ -7,16 +7,21 @@ import { isUndefined, isNaN } from 'lodash'
import { Alert, Button, Checkbox, IconExternalLink, Modal } from 'ui'
import type { PostgresTable, PostgresColumn } from '@supabase/postgres-meta'
-import { useStore, withAuth, useUrlState, useParams } from 'hooks'
+import { useStore, withAuth, useUrlState } from 'hooks'
+import { useParams } from 'common/hooks'
import { entityTypeKeys } from 'data/entity-types/keys'
import { Entity } from 'data/entity-types/entity-type-query'
+import { sqlKeys } from 'data/sql/keys'
import { Dictionary } from 'components/grid'
import { TableEditorLayout } from 'components/layouts'
import { TableGridEditor } from 'components/interfaces'
import ConfirmationModal from 'components/ui/ConfirmationModal'
import { NextPageWithLayout, SchemaView } from 'types'
import { JsonEditValue } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/RowEditor.types'
-import { ProjectContextFromParamsProvider } from 'components/layouts/ProjectLayout/ProjectContext'
+import {
+ ProjectContextFromParamsProvider,
+ useProjectContext,
+} from 'components/layouts/ProjectLayout/ProjectContext'
import { ForeignRowSelectorProps } from 'components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector'
const TableEditorPage: NextPageWithLayout = () => {
@@ -24,6 +29,8 @@ const TableEditorPage: NextPageWithLayout = () => {
const { id, ref: projectRef } = useParams()
const [_, setParams] = useUrlState({ arrayKeys: ['filter', 'sort'] })
+ const { project } = useProjectContext()
+
const queryClient = useQueryClient()
const { meta, ui } = useStore()
@@ -184,7 +191,21 @@ const TableEditorPage: NextPageWithLayout = () => {
message: `Successfully deleted column "${selectedColumnToDelete.name}"`,
})
- await meta.tables.loadById(selectedColumnToDelete!.table_id)
+ queryClient.invalidateQueries(sqlKeys.query(project?.ref, ['foreign-key-constraints']))
+ await Promise.all([
+ meta.tables.loadById(selectedColumnToDelete!.table_id),
+ queryClient.invalidateQueries(
+ sqlKeys.query(project?.ref, [selectedTable!.schema, selectedTable!.name])
+ ),
+ queryClient.invalidateQueries(
+ sqlKeys.query(project?.ref, [
+ 'table-definition',
+ selectedTable!.schema,
+ selectedTable!.name,
+ ])
+ ),
+ ])
+
if (selectedSchema) await meta.views.loadBySchema(selectedSchema)
} catch (error: any) {
ui.setNotification({
diff --git a/studio/pages/project/[ref]/functions/[functionSlug]/index.tsx b/studio/pages/project/[ref]/functions/[functionSlug]/index.tsx
index 156a1da2897..403f83ceec8 100644
--- a/studio/pages/project/[ref]/functions/[functionSlug]/index.tsx
+++ b/studio/pages/project/[ref]/functions/[functionSlug]/index.tsx
@@ -5,7 +5,8 @@ import { Button } from 'ui'
import { observer } from 'mobx-react-lite'
import { PermissionAction } from '@supabase/shared-types/out/constants'
-import { checkPermissions, useParams } from 'hooks'
+import { checkPermissions } from 'hooks'
+import { useParams } from 'common/hooks'
import { ChartIntervals, NextPageWithLayout } from 'types'
import { DATE_FORMAT } from 'lib/constants'
import Panel from 'components/ui/Panel'
diff --git a/studio/pages/project/[ref]/functions/[functionSlug]/invocations.tsx b/studio/pages/project/[ref]/functions/[functionSlug]/invocations.tsx
index 3fc638aa7c2..16e179787e9 100644
--- a/studio/pages/project/[ref]/functions/[functionSlug]/invocations.tsx
+++ b/studio/pages/project/[ref]/functions/[functionSlug]/invocations.tsx
@@ -1,7 +1,7 @@
import { observer } from 'mobx-react-lite'
import { NextPageWithLayout } from 'types'
-import { useParams } from 'hooks'
+import { useParams } from 'common/hooks'
import LogsPreviewer from 'components/interfaces/Settings/Logs/LogsPreviewer'
import FunctionsLayout from 'components/layouts/FunctionsLayout'
import { useEdgeFunctionQuery } from 'data/edge-functions/edge-function-query'
diff --git a/studio/pages/project/[ref]/functions/[functionSlug]/logs.tsx b/studio/pages/project/[ref]/functions/[functionSlug]/logs.tsx
index fd9510aee15..404985c654a 100644
--- a/studio/pages/project/[ref]/functions/[functionSlug]/logs.tsx
+++ b/studio/pages/project/[ref]/functions/[functionSlug]/logs.tsx
@@ -1,6 +1,6 @@
import { observer } from 'mobx-react-lite'
import { NextPageWithLayout } from 'types'
-import { useParams } from 'hooks'
+import { useParams } from 'common/hooks'
import LogsPreviewer from 'components/interfaces/Settings/Logs/LogsPreviewer'
import FunctionsLayout from 'components/layouts/FunctionsLayout'
import { useEdgeFunctionQuery } from 'data/edge-functions/edge-function-query'
diff --git a/studio/pages/project/[ref]/functions/index.tsx b/studio/pages/project/[ref]/functions/index.tsx
index e78b41e0996..0673931084f 100644
--- a/studio/pages/project/[ref]/functions/index.tsx
+++ b/studio/pages/project/[ref]/functions/index.tsx
@@ -1,6 +1,6 @@
import { observer } from 'mobx-react-lite'
-import { useParams } from 'hooks'
+import { useParams } from 'common/hooks'
import { NextPageWithLayout } from 'types'
import Table from 'components/to-be-cleaned/Table'
import FunctionsLayout from 'components/layouts/FunctionsLayout'
diff --git a/studio/pages/project/[ref]/index.tsx b/studio/pages/project/[ref]/index.tsx
index b6bd9ded124..44249b8cb22 100644
--- a/studio/pages/project/[ref]/index.tsx
+++ b/studio/pages/project/[ref]/index.tsx
@@ -1,6 +1,7 @@
import { observer } from 'mobx-react-lite'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { NextPageWithLayout } from 'types'
import { IS_PLATFORM, PROJECT_STATUS } from 'lib/constants'
import { ProjectLayoutWithAuth } from 'components/layouts'
diff --git a/studio/pages/project/[ref]/logs/explorer/index.tsx b/studio/pages/project/[ref]/logs/explorer/index.tsx
index a781fb52c3b..a45ba94cdc3 100644
--- a/studio/pages/project/[ref]/logs/explorer/index.tsx
+++ b/studio/pages/project/[ref]/logs/explorer/index.tsx
@@ -4,7 +4,8 @@ import { useRouter } from 'next/router'
import { observer } from 'mobx-react-lite'
import { Input, Modal, Form, Button } from 'ui'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import useLogsQuery from 'hooks/analytics/useLogsQuery'
import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query'
import { NextPageWithLayout, UserContent } from 'types'
diff --git a/studio/pages/project/[ref]/reports/api-overview.tsx b/studio/pages/project/[ref]/reports/api-overview.tsx
index 00c45d7888b..9a2a9f8a60c 100644
--- a/studio/pages/project/[ref]/reports/api-overview.tsx
+++ b/studio/pages/project/[ref]/reports/api-overview.tsx
@@ -18,10 +18,10 @@ import ReportHeader from 'components/interfaces/Reports/ReportHeader'
import { DatePickerToFrom, LogsEndpointParams } from 'components/interfaces/Settings/Logs'
import ReportFilterBar from 'components/interfaces/Reports/ReportFilterBar'
import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query'
-import { useParams } from 'hooks'
+import { useParams } from 'common/hooks'
import { isEqual } from 'lodash'
import ShimmerLine from 'components/ui/ShimmerLine'
-import ReportPadding from "components/interfaces/Reports/ReportPadding"
+import ReportPadding from 'components/interfaces/Reports/ReportPadding'
export const ApiReport: NextPageWithLayout = () => {
const { ref: projectRef } = useParams()
diff --git a/studio/pages/project/[ref]/reports/database.tsx b/studio/pages/project/[ref]/reports/database.tsx
index 51ff8dfb946..ae7c16394cb 100644
--- a/studio/pages/project/[ref]/reports/database.tsx
+++ b/studio/pages/project/[ref]/reports/database.tsx
@@ -3,7 +3,8 @@ import { observer } from 'mobx-react-lite'
import { FC, useEffect, useState } from 'react'
import { Badge, Button, IconArrowRight, IconExternalLink } from 'ui'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import {
PRICING_TIER_PRODUCT_IDS,
TIME_PERIODS_INFRA,
diff --git a/studio/pages/project/[ref]/reports/index.tsx b/studio/pages/project/[ref]/reports/index.tsx
index aece220eb06..dfda77f3db6 100644
--- a/studio/pages/project/[ref]/reports/index.tsx
+++ b/studio/pages/project/[ref]/reports/index.tsx
@@ -4,7 +4,8 @@ import { useRouter } from 'next/router'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { NextPageWithLayout } from 'types'
-import { checkPermissions, useFlag, useParams, useStore } from 'hooks'
+import { checkPermissions, useFlag, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { post } from 'lib/common/fetch'
import { API_URL, PROJECT_STATUS } from 'lib/constants'
import { useProjectContentStore } from 'stores/projectContentStore'
diff --git a/studio/pages/project/[ref]/settings/api.tsx b/studio/pages/project/[ref]/settings/api.tsx
index 288127c97a6..0446e3d361a 100644
--- a/studio/pages/project/[ref]/settings/api.tsx
+++ b/studio/pages/project/[ref]/settings/api.tsx
@@ -2,7 +2,8 @@ import { createContext } from 'react'
import { useRouter } from 'next/router'
import { observer, useLocalObservable } from 'mobx-react-lite'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { NextPageWithLayout } from 'types'
import { SettingsLayout } from 'components/layouts'
import ServiceList from 'components/interfaces/Settings/API/ServiceList'
diff --git a/studio/pages/project/[ref]/settings/database.tsx b/studio/pages/project/[ref]/settings/database.tsx
index 026f211e571..d9b90a1cecb 100644
--- a/studio/pages/project/[ref]/settings/database.tsx
+++ b/studio/pages/project/[ref]/settings/database.tsx
@@ -1,6 +1,7 @@
import { observer } from 'mobx-react-lite'
import { NextPageWithLayout } from 'types'
-import { useFlag, useParams } from 'hooks'
+import { useFlag } from 'hooks'
+import { useParams } from 'common/hooks'
import { SettingsLayout } from 'components/layouts'
import {
ConnectionPooling,
diff --git a/studio/pages/project/[ref]/settings/storage.tsx b/studio/pages/project/[ref]/settings/storage.tsx
index 7f2f2c15180..0ac1df2ea45 100644
--- a/studio/pages/project/[ref]/settings/storage.tsx
+++ b/studio/pages/project/[ref]/settings/storage.tsx
@@ -2,7 +2,8 @@ import React, { useEffect } from 'react'
import { observer } from 'mobx-react-lite'
import { API_URL, PROJECT_STATUS } from 'lib/constants'
-import { useFlag, useParams, useStore } from 'hooks'
+import { useFlag, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { post } from 'lib/common/fetch'
import { SettingsLayout } from 'components/layouts'
import { StorageSettings } from 'components/to-be-cleaned/Storage'
diff --git a/studio/pages/project/[ref]/settings/vault/keys.tsx b/studio/pages/project/[ref]/settings/vault/keys.tsx
index 530c5b2fb3d..1b14ead4e9a 100644
--- a/studio/pages/project/[ref]/settings/vault/keys.tsx
+++ b/studio/pages/project/[ref]/settings/vault/keys.tsx
@@ -3,7 +3,8 @@ import { useRouter } from 'next/router'
import { observer } from 'mobx-react-lite'
import { Tabs } from 'ui'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { NextPageWithLayout } from 'types'
import { SettingsLayout } from 'components/layouts'
import { VaultToggle, EncryptionKeysManagement } from 'components/interfaces/Settings/Vault'
diff --git a/studio/pages/project/[ref]/settings/vault/secrets.tsx b/studio/pages/project/[ref]/settings/vault/secrets.tsx
index 4a7a92ebb33..8b50c447ac0 100644
--- a/studio/pages/project/[ref]/settings/vault/secrets.tsx
+++ b/studio/pages/project/[ref]/settings/vault/secrets.tsx
@@ -3,7 +3,8 @@ import { useRouter } from 'next/router'
import { observer } from 'mobx-react-lite'
import { Tabs } from 'ui'
-import { useParams, useStore } from 'hooks'
+import { useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { NextPageWithLayout } from 'types'
import { SettingsLayout } from 'components/layouts'
import { VaultToggle, SecretsManagement } from 'components/interfaces/Settings/Vault'
diff --git a/studio/pages/project/[ref]/storage/buckets/index.tsx b/studio/pages/project/[ref]/storage/buckets/index.tsx
index 396a914ba2a..8f4ad3bba21 100644
--- a/studio/pages/project/[ref]/storage/buckets/index.tsx
+++ b/studio/pages/project/[ref]/storage/buckets/index.tsx
@@ -3,7 +3,8 @@ import { useRouter } from 'next/router'
import { observer } from 'mobx-react-lite'
import { API_URL } from 'lib/constants'
-import { useFlag, useParams, useStore } from 'hooks'
+import { useFlag, useStore } from 'hooks'
+import { useParams } from 'common/hooks'
import { post } from 'lib/common/fetch'
import { PROJECT_STATUS } from 'lib/constants'
import { StorageLayout } from 'components/layouts'
diff --git a/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx b/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx
index 084c21c0fcf..a5d6d7118be 100644
--- a/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx
+++ b/studio/stores/authConfig/schema/AuthProviders/AuthProvidersFormValidation.tsx
@@ -1,5 +1,5 @@
import { boolean, number, object, string } from 'yup'
-import { domainRegex } from 'components/interfaces/Auth/Auth.constants'
+import { urlRegex } from 'components/interfaces/Auth/Auth.constants'
const JSON_SCHEMA_VERSION = 'http://json-schema.org/draft-07/schema#'
@@ -305,7 +305,15 @@ const EXTERNAL_PROVIDER_APPLE = {
title: 'Services ID',
description: `
Client identifier when authenticating or validating users.
-[learn more](https://developer.apple.com/documentation/sign_in_with_apple/configuring_your_environment_for_sign_in_with_apple)`,
+[Learn more](https://developer.apple.com/documentation/sign_in_with_apple/configuring_your_environment_for_sign_in_with_apple)`,
+ type: 'string',
+ },
+ EXTERNAL_IOS_BUNDLE_ID: {
+ /**
+ * to do: change docs
+ */
+ title: 'IOS Bundle ID',
+ description: `The iOS app's unique identifier. [Learn more](https://developer.apple.com/documentation/appstoreconnectapi/bundle_ids)`,
type: 'string',
},
EXTERNAL_APPLE_SECRET: {
@@ -320,19 +328,38 @@ The secret key is a JWT token that must be generated.
isSecret: true,
},
},
- validationSchema: object().shape({
- EXTERNAL_APPLE_ENABLED: boolean().required(),
- EXTERNAL_APPLE_CLIENT_ID: string().when('EXTERNAL_APPLE_ENABLED', {
- is: true,
- then: (schema) => schema.required('Services ID is required'),
- otherwise: (schema) => schema,
- }),
- EXTERNAL_APPLE_SECRET: string().when('EXTERNAL_APPLE_ENABLED', {
- is: true,
- then: (schema) => schema.required('Secret key is required'),
- otherwise: (schema) => schema,
- }),
- }),
+ validationSchema: object().shape(
+ {
+ EXTERNAL_APPLE_ENABLED: boolean().required(),
+ EXTERNAL_APPLE_SECRET: string().when(['EXTERNAL_APPLE_ENABLED'], {
+ is: true,
+ then: (schema) => schema.required('Secret key is required'),
+ otherwise: (schema) => schema,
+ }),
+ EXTERNAL_APPLE_CLIENT_ID: string().when(
+ ['EXTERNAL_APPLE_ENABLED', 'EXTERNAL_IOS_BUNDLE_ID'],
+ {
+ is: (EXTERNAL_APPLE_ENABLED: boolean, EXTERNAL_IOS_BUNDLE_ID: string) => {
+ return EXTERNAL_APPLE_ENABLED && !EXTERNAL_IOS_BUNDLE_ID
+ },
+ then: (schema) => schema.required('Either the Services ID or iOS Bundle ID is required'),
+ otherwise: (schema) => schema,
+ }
+ ),
+ EXTERNAL_IOS_BUNDLE_ID: string().when(
+ ['EXTERNAL_APPLE_ENABLED', 'EXTERNAL_APPLE_CLIENT_ID'],
+ {
+ is: (EXTERNAL_APPLE_ENABLED: boolean, EXTERNAL_APPLE_CLIENT_ID: string) => {
+ return EXTERNAL_APPLE_ENABLED && !EXTERNAL_APPLE_CLIENT_ID
+ },
+ then: (schema) => schema.required('Either the Services ID or iOS Bundle ID is required'),
+ otherwise: (schema) => schema,
+ }
+ ),
+ },
+ // this is necessary for the "either or" validation on EXTERNAL_APPLE_CLIENT_ID and EXTERNAL_IOS_BUNDLE_ID
+ [['EXTERNAL_APPLE_CLIENT_ID', 'EXTERNAL_IOS_BUNDLE_ID']]
+ ),
misc: {
iconKey: 'apple-icon',
requiresRedirect: true,
@@ -385,7 +412,7 @@ const EXTERNAL_PROVIDER_AZURE = {
then: (schema) => schema.required('Secret ID is required'),
otherwise: (schema) => schema,
}),
- EXTERNAL_AZURE_URL: string().matches(domainRegex, 'Must be a valid URL').optional(),
+ EXTERNAL_AZURE_URL: string().matches(urlRegex, 'Must be a valid URL').optional(),
}),
misc: {
iconKey: 'microsoft-icon',
@@ -583,7 +610,7 @@ const EXTERNAL_PROVIDER_GITLAB = {
then: (schema) => schema.required('Client Secret is required'),
otherwise: (schema) => schema,
}),
- EXTERNAL_GITLAB_URL: string().matches(domainRegex, 'Must be a valid URL').optional(),
+ EXTERNAL_GITLAB_URL: string().matches(urlRegex, 'Must be a valid URL').optional(),
}),
misc: {
iconKey: 'gitlab-icon',
@@ -671,8 +698,8 @@ const EXTERNAL_PROVIDER_KEYCLOAK = {
EXTERNAL_KEYCLOAK_URL: string().when('EXTERNAL_KEYCLOAK_ENABLED', {
is: true,
then: (schema) =>
- schema.matches(domainRegex, 'Must be a valid URL').required('Realm URL is required'),
- otherwise: (schema) => schema.matches(domainRegex, 'Must be a valid URL'),
+ schema.matches(urlRegex, 'Must be a valid URL').required('Realm URL is required'),
+ otherwise: (schema) => schema.matches(urlRegex, 'Must be a valid URL'),
}),
}),
misc: {
@@ -937,7 +964,7 @@ const EXTERNAL_PROVIDER_WORKOS = {
validationSchema: object().shape({
EXTERNAL_WORKOS_ENABLED: boolean().required(),
EXTERNAL_WORKOS_URL: string()
- .matches(domainRegex, 'Must be a valid URL')
+ .matches(urlRegex, 'Must be a valid URL')
.when('EXTERNAL_WORKOS_ENABLED', {
is: true,
then: (schema) => schema.required('WorkOS URL is required'),
diff --git a/studio/stores/common/PostgresMetaInterface.ts b/studio/stores/common/PostgresMetaInterface.ts
index c11e782498a..2d03eb347b7 100644
--- a/studio/stores/common/PostgresMetaInterface.ts
+++ b/studio/stores/common/PostgresMetaInterface.ts
@@ -140,7 +140,7 @@ export default class PostgresMetaInterface implements IPostgresMetaInterface<
.filter((item: any) => item.schema !== schema)
const formattedPurgedData = keyBy(purgedData, this.identifier)
- this.data = { ...formattedPurgedData, ...formattedData }
+ this.data = { ...formattedPurgedData, ...formattedData } as any
this.setState(LOADED)
return data
@@ -154,7 +154,7 @@ export default class PostgresMetaInterface implements IPostgresMetaInterface<
initialDataArray(value: T[]) {
if (this.state === this.STATES.INITIAL) {
- this.data = keyBy(value, this.identifier)
+ this.data = keyBy(value, this.identifier) as any
this.state = this.STATES.LOADED
}
}
@@ -164,7 +164,7 @@ export default class PostgresMetaInterface implements IPostgresMetaInterface<
}
setDataArray(value: T[]) {
- this.data = keyBy(value, this.identifier)
+ this.data = keyBy(value, this.identifier) as any
}
setError(value: any) {
diff --git a/studio/tests/components/Auth/Auth.constants.test.js b/studio/tests/components/Auth/Auth.constants.test.js
index 4c8a3fe62f3..ab7504014dc 100644
--- a/studio/tests/components/Auth/Auth.constants.test.js
+++ b/studio/tests/components/Auth/Auth.constants.test.js
@@ -1,87 +1,87 @@
-import { domainRegex } from 'components/interfaces/Auth/Auth.constants'
+import { urlRegex } from 'components/interfaces/Auth/Auth.constants'
describe('Auth.constants: domainRegex', () => {
test('should validate typical URLs', () => {
const mockInput1 = 'http://domain.com'
- const output1 = domainRegex.test(mockInput1)
+ const output1 = urlRegex.test(mockInput1)
expect(output1).toBe(true)
const mockInput2 = 'https://supabase.io'
- const output2 = domainRegex.test(mockInput2)
+ const output2 = urlRegex.test(mockInput2)
expect(output2).toBe(true)
const mockInput3 = 'https://new-domain-vercel.com'
- const output3 = domainRegex.test(mockInput3)
+ const output3 = urlRegex.test(mockInput3)
expect(output3).toBe(true)
const mockInput4 = 'www.test-domain.com'
- const output4 = domainRegex.test(mockInput4)
+ const output4 = urlRegex.test(mockInput4)
expect(output4).toBe(true)
})
test('should validate app-based domains', () => {
const mockInput1 = 'exp://exp.host/some-app'
- const output1 = domainRegex.test(mockInput1)
+ const output1 = urlRegex.test(mockInput1)
expect(output1).toBe(true)
const mockInput2 = 'exp://exp.host/some-app?release-channel=default'
- const output2 = domainRegex.test(mockInput2)
+ const output2 = urlRegex.test(mockInput2)
expect(output2).toBe(true)
})
test('should validate subdomains', () => {
const mockInput1 = 'https://app.supabase.com'
- const output1 = domainRegex.test(mockInput1)
+ const output1 = urlRegex.test(mockInput1)
expect(output1).toBe(true)
})
test('should validate localhost URLs', () => {
const mockInput1 = 'http://localhost:3000'
- const output1 = domainRegex.test(mockInput1)
+ const output1 = urlRegex.test(mockInput1)
expect(output1).toBe(true)
})
test('should validate URLs with query params', () => {
const mockInput1 = 'https://supabase.com?name=test'
- const output1 = domainRegex.test(mockInput1)
+ const output1 = urlRegex.test(mockInput1)
expect(output1).toBe(true)
const mockInput2 = 'https://supabase.com?name=test&description=hello&page=2'
- const output2 = domainRegex.test(mockInput2)
+ const output2 = urlRegex.test(mockInput2)
expect(output2).toBe(true)
})
test('should validate URLs with wildcards', () => {
const mockInput1 = 'https://supabase*.com'
- const output1 = domainRegex.test(mockInput1)
+ const output1 = urlRegex.test(mockInput1)
expect(output1).toBe(true)
const mockInput2 = 'https://supabase.com/*'
- const output2 = domainRegex.test(mockInput2)
+ const output2 = urlRegex.test(mockInput2)
expect(output2).toBe(true)
const mockInput3 = 'https://new-*-domain.com/*'
- const output3 = domainRegex.test(mockInput3)
+ const output3 = urlRegex.test(mockInput3)
expect(output3).toBe(true)
const mockInput4 = 'https://new-*-domain.com/*/*/*'
- const output4 = domainRegex.test(mockInput4)
+ const output4 = urlRegex.test(mockInput4)
expect(output4).toBe(true)
const mockInput5 = 'https://sub-*-domain.new-*-domain.com/*/*/*'
- const output5 = domainRegex.test(mockInput5)
+ const output5 = urlRegex.test(mockInput5)
expect(output5).toBe(true)
})
test('should invalidate invalid URLs', () => {
const mockInput1 = 'supabase'
- const output1 = domainRegex.test(mockInput1)
+ const output1 = urlRegex.test(mockInput1)
expect(output1).toBe(false)
const mockInput2 = 'mailto:test@gmail.com'
- const output2 = domainRegex.test(mockInput2)
+ const output2 = urlRegex.test(mockInput2)
expect(output2).toBe(false)
const mockInput4 = 'hello world.com'
- const output4 = domainRegex.test(mockInput4)
+ const output4 = urlRegex.test(mockInput4)
expect(output4).toBe(false)
const mockInput5 = 'email@domain.com'
- const output5 = domainRegex.test(mockInput5)
+ const output5 = urlRegex.test(mockInput5)
expect(output5).toBe(false)
})
})
diff --git a/studio/tests/pages/projects/LogsPreviewer.test.js b/studio/tests/pages/projects/LogsPreviewer.test.js
index 15774d0b66c..780a82f9d4a 100644
--- a/studio/tests/pages/projects/LogsPreviewer.test.js
+++ b/studio/tests/pages/projects/LogsPreviewer.test.js
@@ -23,7 +23,8 @@ import userEvent from '@testing-library/user-event'
import { wait } from '@testing-library/user-event/dist/utils'
import { logDataFixture } from '../../fixtures'
import { LogsTableName } from 'components/interfaces/Settings/Logs'
-import { useParams } from 'hooks'
+jest.mock('common/hooks')
+import { useParams } from 'common/hooks'
beforeEach(() => {
// reset mocks between tests
diff --git a/studio/tests/pages/projects/logs-query.test.js b/studio/tests/pages/projects/logs-query.test.js
index 378b62c6f02..eb1ce4d5c50 100644
--- a/studio/tests/pages/projects/logs-query.test.js
+++ b/studio/tests/pages/projects/logs-query.test.js
@@ -8,7 +8,8 @@ import { logDataFixture } from '../../fixtures'
import { clickDropdown } from 'tests/helpers'
import dayjs from 'dayjs'
import { useProjectSubscriptionQuery } from 'data/subscriptions/project-subscription-query'
-import { useParams } from 'hooks'
+jest.mock('common/hooks')
+import { useParams } from 'common/hooks'
const defaultRouterMock = () => {
const router = jest.fn()
diff --git a/supabase/functions/og-images/README.md b/supabase/functions/og-images/README.md
index 886b9f69b9e..c2a093815df 100644
--- a/supabase/functions/og-images/README.md
+++ b/supabase/functions/og-images/README.md
@@ -38,3 +38,11 @@ supabase functions serve og-images
```
Now we can visit [localhost:54321/functions/v1/og-images/?site=docs&title=Title&description=Description&type=Auth](http://localhost:54321/functions/v1/og-images/?site=docs&title=Title&description=Description&type=Auth) to see your changes localy.
+
+## Deploy
+
+To deploy this function, you currently need to depoy it locally. To do this follow the steps below.
+
+```bash
+supabase functions deploy og-images --no-verify-jwt
+```
diff --git a/turbo.json b/turbo.json
index 176ef881d41..3ca755f081d 100644
--- a/turbo.json
+++ b/turbo.json
@@ -4,6 +4,43 @@
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
+ "www#build": {
+ "dependsOn": ["^build"],
+ "env": [
+ "SUPABASE_SERVICE_ROLE_SECRET",
+ "EMAIL_TO_ID_SECRET",
+ "SUPABASE_URL",
+ "GITHUB_OAUTH_CLIENT_SECRET"
+ ],
+ "outputs": [".next/**", "!.next/cache/**"]
+ },
+ "docs#build": {
+ "dependsOn": ["^build"],
+ "env": ["SUPABASE_SERVICE_ROLE_KEY", "OPENAI_KEY", "ALGOLIA_SEARCH_ADMIN_KEY"],
+ "outputs": [".next/**", "!.next/cache/**"]
+ },
+ "studio#build": {
+ "dependsOn": ["^build"],
+ "env": [
+ "SITE_URL",
+ "AUTH0_AUDIENCE",
+ "AUTH0_CLIENT_ID",
+ "AUTH0_CLIENT_SECRET",
+ "AUTH0_DOMAIN",
+ "COOKIE_DOMAIN",
+ "INTERCOM_API_KEY",
+ "READ_ONLY_API_KEY",
+ "READ_ONLY_URL",
+ "SENTRY_AUTH_TOKEN",
+ "SENTRY_ORG",
+ "SENTRY_PROJECT",
+ "SENTRY_URL",
+ "SESSION_COOKIE_SECRET",
+ "SUPABASE_ENCRYPTION_KEY",
+ "PLATFORM_PG_META_URL"
+ ],
+ "outputs": [".next/**", "!.next/cache/**"]
+ },
"lint": {
"outputs": []
},