From 5359176187ac43fe1c02f55e0bf35767590bb7c6 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 29 Apr 2025 11:04:26 +0800 Subject: [PATCH 01/32] Add guards in table editor table state (#35320) --- apps/studio/state/table-editor-table.tsx | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/apps/studio/state/table-editor-table.tsx b/apps/studio/state/table-editor-table.tsx index ea62ab755d0..f35aaccdaf3 100644 --- a/apps/studio/state/table-editor-table.tsx +++ b/apps/studio/state/table-editor-table.tsx @@ -93,21 +93,29 @@ export const createTableEditorTableState = ({ state.gridColumns.splice(toIdx, 0, moveItem) }, updateColumnSize: (index: number, width: number) => { - ;(state.gridColumns[index] as CalculatedColumn & { width?: number }).width = width + if (state.gridColumns[index]) { + ;(state.gridColumns[index] as CalculatedColumn & { width?: number }).width = width + } }, freezeColumn: (columnKey: string) => { const index = state.gridColumns.findIndex((x) => x.key === columnKey) - ;(state.gridColumns[index] as CalculatedColumn & { frozen?: boolean }).frozen = true + if (state.gridColumns[index]) { + ;(state.gridColumns[index] as CalculatedColumn & { frozen?: boolean }).frozen = + true + } }, unfreezeColumn: (columnKey: string) => { const index = state.gridColumns.findIndex((x) => x.key === columnKey) - ;(state.gridColumns[index] as CalculatedColumn & { frozen?: boolean }).frozen = - false + if (state.gridColumns[index]) { + ;(state.gridColumns[index] as CalculatedColumn & { frozen?: boolean }).frozen = + false + } }, updateColumnIdx: (columnKey: string, columnIdx: number) => { const index = state.gridColumns.findIndex((x) => x.key === columnKey) - ;(state.gridColumns[index] as CalculatedColumn & { idx?: number }).idx = columnIdx - + if (state.gridColumns[index]) { + ;(state.gridColumns[index] as CalculatedColumn & { idx?: number }).idx = columnIdx + } state.gridColumns.sort((a, b) => a.idx - b.idx) }, From 270935bec89a1d596619fbcde3a3efc317fe5786 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 29 Apr 2025 11:15:29 +0800 Subject: [PATCH 02/32] Chore/update tab labels when entering sql or table editor (#35219) * Update table editor tab labels when entering table editor if labels dont match entity names * Update sqlEditorTabsCleanup to also update tab labels if they dont match snippet names * Update table editor tabs clean up logic to be consistent with sql editgor * Clean up --- .../layouts/SQLEditorLayout/SQLEditorMenu.tsx | 11 ++++---- .../SQLEditorNavV2/SQLEditorNav.tsx | 10 +++---- .../SQLEditorNavV2/SearchList.tsx | 2 +- .../TableEditorLayout/TableEditorMenu.tsx | 12 ++++++-- .../components/layouts/Tabs/Tabs.utils.ts | 28 +++++++++++++++++-- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorMenu.tsx b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorMenu.tsx index 52b458484e9..15b3be04104 100644 --- a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorMenu.tsx +++ b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorMenu.tsx @@ -1,15 +1,16 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import { useDebounce } from '@uidotdev/usehooks' +import { FilePlus, FolderPlus, Plus, X } from 'lucide-react' +import { useRouter } from 'next/router' +import { useEffect, useState } from 'react' +import { toast } from 'sonner' + import { useParams } from 'common' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useLocalStorage } from 'hooks/misc/useLocalStorage' import { useSelectedProject } from 'hooks/misc/useSelectedProject' import { LOCAL_STORAGE_KEYS } from 'lib/constants' import { useProfile } from 'lib/profile' -import { FilePlus, FolderPlus, Plus, X } from 'lucide-react' -import { useRouter } from 'next/router' -import { useEffect, useState } from 'react' -import { toast } from 'sonner' import { getAppStateSnapshot } from 'state/app-state' import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2' import { @@ -37,8 +38,8 @@ export const SQLEditorMenu = () => { const { profile } = useProfile() const project = useSelectedProject() const { ref } = useParams() - const snapV2 = useSqlEditorV2StateSnapshot() + const [search, setSearch] = useState('') const [showSearch, setShowSearch] = useState(false) const [sort, setSort] = useLocalStorage<'name' | 'inserted_at'>( diff --git a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.tsx b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.tsx index 31a02e6c9fe..cb26ba3cd0c 100644 --- a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.tsx +++ b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SQLEditorNav.tsx @@ -29,7 +29,7 @@ import { useSnippetFolders, useSqlEditorV2StateSnapshot, } from 'state/sql-editor-v2' -import { createTabId, getTabsStore, makeTabPermanent, removeTabs } from 'state/tabs' +import { createTabId, getTabsStore, makeTabPermanent, removeTabs, useTabsStore } from 'state/tabs' import { SqlSnippets } from 'types' import { Separator, TreeView } from 'ui' import { @@ -40,7 +40,6 @@ import { InnerSideMenuSeparator, } from 'ui-patterns' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' -import { useSnapshot } from 'valtio' import SQLEditorLoadingSnippets from './SQLEditorLoadingSnippets' import { formatFolderResponseForTreeView, getLastItemIds, ROOT_NODE } from './SQLEditorNav.utils' import { SQLEditorTreeViewItem } from './SQLEditorTreeViewItem' @@ -61,7 +60,7 @@ export const SQLEditorNav = ({ sort = 'inserted_at' }: SQLEditorNavProps) => { const snapV2 = useSqlEditorV2StateSnapshot() const tabStore = getTabsStore(projectRef) - const tabs = useSnapshot(tabStore) + const tabs = useTabsStore(projectRef) const isSQLEditorTabsEnabled = useIsSQLEditorTabsEnabled() const [sectionVisibility, setSectionVisibility] = useLocalStorage( @@ -540,11 +539,10 @@ export const SQLEditorNav = ({ sort = 'inserted_at' }: SQLEditorNavProps) => { }, [projectRef, sharedSqlSnippetsData?.pages]) useEffect(() => { - if (projectRef && isSuccess && isSharedSqlSnippetsSuccess && isSQLEditorTabsEnabled) { + if (projectRef && isSuccess && isSQLEditorTabsEnabled) { sqlEditorTabsCleanup({ ref: projectRef, snippets: allSnippetsInView as any }) } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isSuccess, isSharedSqlSnippetsSuccess, allSnippetsInView]) + }, [isSuccess, isSharedSqlSnippetsSuccess, allSnippetsInView, isSQLEditorTabsEnabled]) return ( <> diff --git a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SearchList.tsx b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SearchList.tsx index 3a7305a4bcc..9c74944dbc9 100644 --- a/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SearchList.tsx +++ b/apps/studio/components/layouts/SQLEditorLayout/SQLEditorNavV2/SearchList.tsx @@ -29,7 +29,7 @@ export const SearchList = ({ search }: SearchListProps) => { { keepPreviousData: true } ) - const { data: count, isLoading: isLoadingCount } = useContentCountQuery( + const { data: count } = useContentCountQuery( { projectRef, cumulative: true, diff --git a/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx b/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx index 1474176cd1a..df73b7f6ade 100644 --- a/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx +++ b/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx @@ -40,16 +40,17 @@ import { InnerSideBarFilters, } from 'ui-patterns/InnerSideMenu' import { useProjectContext } from '../ProjectLayout/ProjectContext' +import { tableEditorTabsCleanUp } from '../Tabs/Tabs.utils' import EntityListItem from './EntityListItem' import { TableMenuEmptyState } from './TableMenuEmptyState' const TableEditorMenu = () => { const { ref, id: _id } = useParams() + const isMobile = useBreakpoint() const id = _id ? Number(_id) : undefined const snap = useTableEditorStateSnapshot() - const { selectedSchema, setSelectedSchema } = useQuerySchemaState() - const isMobile = useBreakpoint() const isTableEditorTabsEnabled = useIsTableEditorTabsEnabled() + const { selectedSchema, setSelectedSchema } = useQuerySchemaState() const [showModal, setShowModal] = useState(false) const [searchText, setSearchText] = useState('') @@ -114,6 +115,13 @@ const TableEditorMenu = () => { } }, [selectedTable?.schema]) + useEffect(() => { + // Clean up tabs + recent items for any tables that might have been removed outside of the dashboard session + if (isTableEditorTabsEnabled && ref && entityTypes && !searchText) { + tableEditorTabsCleanUp({ ref, schemas: [selectedSchema], entities: entityTypes }) + } + }, [entityTypes]) + return ( <>
diff --git a/apps/studio/components/layouts/Tabs/Tabs.utils.ts b/apps/studio/components/layouts/Tabs/Tabs.utils.ts index e9636ab5177..9fc1e1225c3 100644 --- a/apps/studio/components/layouts/Tabs/Tabs.utils.ts +++ b/apps/studio/components/layouts/Tabs/Tabs.utils.ts @@ -5,7 +5,7 @@ import { RecentItem, removeRecentItems, } from 'state/recent-items' -import { createTabId, getTabsStore, removeTabs } from 'state/tabs' +import { createTabId, editorEntityTypes, getTabsStore, removeTabs, updateTab } from 'state/tabs' export const tableEditorTabsCleanUp = ({ ref, @@ -58,6 +58,18 @@ export const tableEditorTabsCleanUp = ({ // perform tabs cleanup removeTabs(ref, tableEditorTabsToBeCleaned) + + // [Joshen] Validate for opened tabs, if their label matches the entity's name - update label if not + // As the entity could've been renamed outside of the table editor + // e.g Using the SQL editor to rename the entity + const openTabs = tabsStore.openTabs + .map((id) => tabsStore.tabsMap[id]) + .filter((tab) => editorEntityTypes['table']?.includes(tab.type)) + + openTabs.forEach((tab) => { + const entity = entities?.find((x) => tab.metadata?.tableId === x.id) + if (!!entity && entity.name !== tab.label) updateTab(ref, tab.id, { label: entity.name }) + }) } export const sqlEditorTabsCleanup = ({ @@ -65,7 +77,7 @@ export const sqlEditorTabsCleanup = ({ snippets, }: { ref: string - snippets: { id: string; type: string }[] + snippets: { id: string; type: string; name: string }[] }) => { // these are tabs that are static content // these canot be removed from localstorage based on this query request @@ -95,4 +107,16 @@ export const sqlEditorTabsCleanup = ({ ? recentItems.filter((item) => !currentContentIds.includes(item.id)).map((item) => item.id) : [] ) + + // [Joshen] Validate for opened tabs, if their label matches the snippet's name - update label if not + // As the snippets name could've been updated outside of the SQL Editor session + // e.g for a shared snippet, the owner could've updated the name of the snippet + const openSqlTabs = tabsStore.openTabs + .map((id) => tabsStore.tabsMap[id]) + .filter((tab) => editorEntityTypes['sql']?.includes(tab.type)) + + openSqlTabs.forEach((tab) => { + const snippet = snippets?.find((x) => tab.metadata?.sqlId === x.id) + if (!!snippet && snippet.name !== tab.label) updateTab(ref, tab.id, { label: snippet.name }) + }) } From 37ee1c0ca2591d8974836f5b3c99d1afca3e002b Mon Sep 17 00:00:00 2001 From: Jordi Enric <37541088+jordienr@users.noreply.github.com> Date: Tue, 29 Apr 2025 05:17:29 +0200 Subject: [PATCH 03/32] fix logs filter popover position (#35334) --- .../components/interfaces/Settings/Logs/LogsFilterPopover.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/studio/components/interfaces/Settings/Logs/LogsFilterPopover.tsx b/apps/studio/components/interfaces/Settings/Logs/LogsFilterPopover.tsx index 6ddc6bf66f5..0bb61c5628f 100644 --- a/apps/studio/components/interfaces/Settings/Logs/LogsFilterPopover.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/LogsFilterPopover.tsx @@ -69,7 +69,7 @@ const LogsFilterPopover = ({ {options.label} - +
{ e.preventDefault() From 4976a8de8202a78eef2093df8fe125099ebf6ff4 Mon Sep 17 00:00:00 2001 From: Jordi Enric <37541088+jordienr@users.noreply.github.com> Date: Tue, 29 Apr 2025 11:12:10 +0200 Subject: [PATCH 04/32] fix Logs Date Pickers tests (#35351) * fix tests * fix naming * fix names 2 * undo logspreviewer --- .../pages/projects/Logs.Datepickers.test.tsx | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/studio/tests/pages/projects/Logs.Datepickers.test.tsx b/apps/studio/tests/pages/projects/Logs.Datepickers.test.tsx index c4ea241af56..a5bd1aa636c 100644 --- a/apps/studio/tests/pages/projects/Logs.Datepickers.test.tsx +++ b/apps/studio/tests/pages/projects/Logs.Datepickers.test.tsx @@ -94,17 +94,16 @@ test('datepicker onSubmit will return ISO string of selected dates', async () => // open the datepicker userEvent.click(screen.getByText(/13:00/i)) - // Get today's date and tomorrow's date - const today = dayjs() - const tomorrow = today.add(1, 'day') + const day15 = dayjs().date(15) + const day16 = day15.add(1, 'day') - // Find and click on today's date - const todayElement = await screen.findByText(today.format('D')) - userEvent.click(todayElement) + // Find and click on first date + const day15Element = await screen.findByText(day15.format('D')) + userEvent.click(day15Element) - // Find and click on tomorrow's date - const tomorrowElement = await screen.findByText(tomorrow.format('D')) - userEvent.click(tomorrowElement) + // Find and click on second date + const day16Element = await screen.findByText(day16.format('D')) + userEvent.click(day16Element) await userEvent.click(await screen.findByText('Apply')) expect(mockFn).toBeCalled() @@ -112,8 +111,8 @@ test('datepicker onSubmit will return ISO string of selected dates', async () => const call = mockFn.mock.calls[0][0] expect(call).toMatchObject({ - from: dayjs().date(today.date()).hour(13).minute(0).second(0).millisecond(0).toISOString(), - to: dayjs().date(tomorrow.date()).hour(23).minute(59).second(59).millisecond(0).toISOString(), + from: dayjs().date(day15.date()).hour(13).minute(0).second(0).millisecond(0).toISOString(), + to: dayjs().date(day16.date()).hour(23).minute(59).second(59).millisecond(0).toISOString(), }) }) From 34147c3bd221e12dc381daa1f9404593f5047298 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Tue, 29 Apr 2025 18:06:24 +0800 Subject: [PATCH 05/32] Fix theme mode radio item large (#35350) * Fix theme mode radio item large * Attempt to fix Logs.DatePickers.test by fixing a date for the test * Revert --- .../Account/Preferences/ThemeSettings.tsx | 4 ++-- .../interfaces/Settings/Logs/Logs.DatePickers.tsx | 15 +++++++-------- .../interfaces/Settings/Logs/LogsQueryPanel.tsx | 8 ++++---- .../pages/projects/Logs.Datepickers.test.tsx | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/apps/studio/components/interfaces/Account/Preferences/ThemeSettings.tsx b/apps/studio/components/interfaces/Account/Preferences/ThemeSettings.tsx index 73ae2129770..b05c8c5cf28 100644 --- a/apps/studio/components/interfaces/Account/Preferences/ThemeSettings.tsx +++ b/apps/studio/components/interfaces/Account/Preferences/ThemeSettings.tsx @@ -2,6 +2,7 @@ import { useTheme } from 'next-themes' import { useEffect, useState } from 'react' import SVG from 'react-inlinesvg' +import { DEFAULT_SIDEBAR_BEHAVIOR } from 'components/interfaces/Sidebar' import Panel from 'components/ui/Panel' import { useLocalStorageQuery } from 'hooks/misc/useLocalStorage' import { BASE_PATH, LOCAL_STORAGE_KEYS } from 'lib/constants' @@ -19,7 +20,6 @@ import { Theme, } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' -import { DEFAULT_SIDEBAR_BEHAVIOR } from 'components/interfaces/Sidebar' export const ThemeSettings = () => { const [mounted, setMounted] = useState(false) @@ -51,7 +51,7 @@ export const ThemeSettings = () => { > {singleThemes.map((theme: Theme) => ( Date: Tue, 29 Apr 2025 12:19:15 -0230 Subject: [PATCH 06/32] Update og image for ui lib (#35357) --- .../public/img/supabase-og-image.png | Bin 72642 -> 195614 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/ui-library/public/img/supabase-og-image.png b/apps/ui-library/public/img/supabase-og-image.png index c2f4d0ea806de10886bca49aa35a59982e3cbd96..cba27d698598a2fd1a93e52bf0fadb8d8d57f147 100644 GIT binary patch literal 195614 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 ur;g)D)jPzHMC>QZiNW0;Pc6T2NRDCd=wh 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 z�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 Y$$5^EOG8WJ z>=UT@jOoYs@3BwB(5OOi{(Lc!%liA>-FtYZ&)?ww_i3!Q`2COn`?QM~L;K≈6IJ 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 Date: Tue, 29 Apr 2025 12:52:08 -0400 Subject: [PATCH 07/32] db (docs): new search functions (#35229) Add some new search functions. Should behave roughly the same as the search functions that are currently in use, but formatted to work better for the new search endpoint(s). --- .../20250423133137_improve_vector_search.sql | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 supabase/migrations/20250423133137_improve_vector_search.sql diff --git a/supabase/migrations/20250423133137_improve_vector_search.sql b/supabase/migrations/20250423133137_improve_vector_search.sql new file mode 100644 index 00000000000..8563164e5bd --- /dev/null +++ b/supabase/migrations/20250423133137_improve_vector_search.sql @@ -0,0 +1,103 @@ +create index if not exists +idx_page_section_embedding +on public.page_section +using hnsw (embedding vector_ip_ops); + +create or replace function match_embedding( + embedding vector(1536), + match_threshold float default 0.78, + max_results int default 30 +) +returns setof page_section +language plpgsql +set search_path = '' +as $$ +#variable_conflict use_variable +begin + return query + select * + from public.page_section + where (page_section.embedding operator(public.<#>) embedding) <= -match_threshold + order by page_section.embedding operator(public.<#>) embedding + limit max_results; +end; +$$; + +create or replace function get_full_content_url( + type text, + path text, + slug text +) +returns text +language sql +set search_path = '' +as $$ + select case + when type = 'github-discussions' + then path + when type = 'partner-integration' + then concat('https://supabase.com', path) + else + concat( + 'https://supabase.com/docs', + path, + case + when slug is null + then '' + else + concat('#', slug) + end + ) + end; +$$; + +create or replace function search_content( + embedding vector(1536), + include_full_content boolean default false, + match_threshold float default 0.78, + max_result int default 30 +) +returns table ( + id bigint, + page_title text, + type text, + href text, + content text, + subsections json[] +) +language sql +set search_path = '' +as $$ + with matched_section as ( + select + *, + row_number() over () as ranking + from public.match_embedding( + embedding, + match_threshold, + max_result + ) + ) + select + page.id, + meta ->> 'title' as page_title, + type, + public.get_full_content_url(type, path, null) as href, + case + when include_full_content + then page.content + else + null + end as content, + array_agg( + json_build_object( + 'title', heading, + 'href', public.get_full_content_url(type, path, slug), + 'content', matched_section.content + ) + ) + from matched_section + join public.page on matched_section.page_id = page.id + group by page.id + order by min(ranking); +$$; From a0f5c798dfee8a187ca29628a39d8b5d3da92930 Mon Sep 17 00:00:00 2001 From: Charis <26616127+charislam@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:02:36 -0400 Subject: [PATCH 08/32] feat(content api): add dummy api/graphql route (#35141) The first PR in a series to build out the Content API. Adds a dummy GraphQL route with some tests: for now all this does is validate the request body and return a 404. --- apps/docs/app/api/graphql/route.test.ts | 56 +++++++++++++++++++++++++ apps/docs/app/api/graphql/route.ts | 49 ++++++++++++++++++++++ apps/docs/app/api/utils.ts | 29 +++++++++++++ apps/docs/package.json | 1 + pnpm-lock.yaml | 46 ++++++++++++-------- 5 files changed, 163 insertions(+), 18 deletions(-) create mode 100644 apps/docs/app/api/graphql/route.test.ts create mode 100644 apps/docs/app/api/graphql/route.ts create mode 100644 apps/docs/app/api/utils.ts diff --git a/apps/docs/app/api/graphql/route.test.ts b/apps/docs/app/api/graphql/route.test.ts new file mode 100644 index 00000000000..77d12a09ac4 --- /dev/null +++ b/apps/docs/app/api/graphql/route.test.ts @@ -0,0 +1,56 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' +import { POST } from './route' + +describe('GraphQL API endpoint', () => { + beforeEach(() => { + vi.clearAllMocks() + vi.spyOn(console, 'error').mockImplementation(() => {}) + }) + + it('should return 400 if request body is not valid JSON', async () => { + const request = new Request('http://localhost/api/graphql', { + method: 'POST', + body: 'not json', + }) + + const response = await POST(request) + expect(response.status).toBe(400) + expect(await response.text()).toBe('Invalid request: Request body must be valid JSON') + }) + + it('should return 400 if request body is missing required fields', async () => { + const request = new Request('http://localhost/api/graphql', { + method: 'POST', + body: JSON.stringify({ variables: {} }), + }) + + const response = await POST(request) + expect(response.status).toBe(400) + expect(await response.text()).toContain('Request body must be a valid GraphQL request object') + }) + + it('should return 400 if query is not a string', async () => { + const request = new Request('http://localhost/api/graphql', { + method: 'POST', + body: JSON.stringify({ query: 123 }), + }) + + const response = await POST(request) + expect(response.status).toBe(400) + }) + + it('should return 500 for internal server errors', async () => { + const request = new Request('http://localhost/api/graphql', { + method: 'POST', + body: JSON.stringify({ query: 'query { hello }' }), + }) + + vi.spyOn(request, 'json').mockImplementation(() => { + throw new Error('Unexpected error') + }) + + const response = await POST(request) + expect(response.status).toBe(500) + expect(await response.text()).toBe('Internal Server Error') + }) +}) diff --git a/apps/docs/app/api/graphql/route.ts b/apps/docs/app/api/graphql/route.ts new file mode 100644 index 00000000000..7c5e8c9fdde --- /dev/null +++ b/apps/docs/app/api/graphql/route.ts @@ -0,0 +1,49 @@ +// import { graphql } from 'graphql' +import { NextResponse } from 'next/server' +import { z } from 'zod' + +import { ApiError, InvalidRequestError } from '~/app/api/utils' + +export const runtime = 'edge' + +const graphQLRequestSchema = z.object({ + query: z.string(), + variables: z.record(z.any()).optional(), + operationName: z.string().optional(), +}) + +async function handleGraphQLRequest(request: Request): Promise { + const body = await request.json().catch(() => { + throw new InvalidRequestError('Request body must be valid JSON') + }) + const parsedBody = graphQLRequestSchema.safeParse(body) + if (!parsedBody.success) { + const errorMessage = parsedBody.error?.message + throw new InvalidRequestError( + `Request body must be a valid GraphQL request object${errorMessage ? `: ${errorMessage}` : ''}` + ) + } + + // const { query, variables, operationName } = parsedBody.data + // When endpoint is complete, this should return a response with the GraphQL + // query result + + // For now, just return a 404 response + return new NextResponse('Not Found', { status: 404 }) +} + +export async function POST(request: Request): Promise { + try { + return await handleGraphQLRequest(request) + } catch (error: unknown) { + console.error(error) + + if (error instanceof ApiError) { + return new NextResponse(error.isPrivate() ? 'Internal Server Error' : error.message, { + status: error.statusCode(), + }) + } else { + return new NextResponse('Internal Server Error', { status: 500 }) + } + } +} diff --git a/apps/docs/app/api/utils.ts b/apps/docs/app/api/utils.ts new file mode 100644 index 00000000000..5b3f93ea15b --- /dev/null +++ b/apps/docs/app/api/utils.ts @@ -0,0 +1,29 @@ +import { z } from 'zod' + +export class ApiError extends Error { + constructor(message: string) { + super(message) + } + + isPrivate() { + return true + } + + statusCode() { + return 500 + } +} + +export class InvalidRequestError extends ApiError { + constructor(message: string) { + super(`Invalid request: ${message}`) + } + + isPrivate() { + return false + } + + statusCode() { + return 400 + } +} diff --git a/apps/docs/package.json b/apps/docs/package.json index 4208f151170..dd046d93891 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -55,6 +55,7 @@ "eslint-config-supabase": "workspace:*", "framer-motion": "^11.0.3", "github-slugger": "^2.0.0", + "graphql": "^16.10.0", "gray-matter": "^4.0.3", "hast-util-has-property": "^2.0.1", "hast-util-to-html": "^9.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae1312c5e06..b21aa326339 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -282,6 +282,9 @@ importers: github-slugger: specifier: ^2.0.0 version: 2.0.0 + graphql: + specifier: ^16.10.0 + version: 16.10.0 gray-matter: specifier: ^4.0.3 version: 4.0.3 @@ -564,10 +567,10 @@ importers: version: 0.2.15 '@graphiql/react': specifier: ^0.19.4 - version: 0.19.4(@codemirror/language@6.11.0)(@types/node@20.12.11)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@5.14.1(graphql@16.8.1))(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 0.19.4(@codemirror/language@6.11.0)(@types/node@20.12.11)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@5.14.1(graphql@16.10.0))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@graphiql/toolkit': specifier: ^0.9.1 - version: 0.9.1(@types/node@20.12.11)(graphql-ws@5.14.1(graphql@16.8.1))(graphql@16.8.1) + version: 0.9.1(@types/node@20.12.11)(graphql-ws@5.14.1(graphql@16.10.0))(graphql@16.10.0) '@gregnr/postgres-meta': specifier: ^0.82.0-dev.2 version: 0.82.0-dev.2(encoding@0.1.13)(supports-color@8.1.1) @@ -979,7 +982,7 @@ importers: version: 2.0.7(eslint@8.57.0(supports-color@8.1.1)) graphql-ws: specifier: 5.14.1 - version: 5.14.1(graphql@16.8.1) + version: 5.14.1(graphql@16.10.0) import-in-the-middle: specifier: ^1.13.1 version: 1.13.1 @@ -8675,6 +8678,7 @@ packages: resolution: {integrity: sha512-t0q23FIpvHDTtnORW+bDJziGsal5uh9RJTJ1fyH8drd4lICOoXhJ5pLMUZ5C0VQei6dNmwTzzoTRgMkO9JgHEQ==} peerDependencies: eslint: '>= 5' + bundledDependencies: [] eslint-plugin-import@2.29.1: resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} @@ -9311,6 +9315,10 @@ packages: peerDependencies: graphql: '>=0.11 <=16' + graphql@16.10.0: + resolution: {integrity: sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + graphql@16.8.1: resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} @@ -16075,9 +16083,9 @@ snapshots: '@gar/promisify@1.1.3': {} - '@graphiql/react@0.19.4(@codemirror/language@6.11.0)(@types/node@20.12.11)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@5.14.1(graphql@16.8.1))(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@graphiql/react@0.19.4(@codemirror/language@6.11.0)(@types/node@20.12.11)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@5.14.1(graphql@16.10.0))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@graphiql/toolkit': 0.9.1(@types/node@20.12.11)(graphql-ws@5.14.1(graphql@16.8.1))(graphql@16.8.1) + '@graphiql/toolkit': 0.9.1(@types/node@20.12.11)(graphql-ws@5.14.1(graphql@16.10.0))(graphql@16.10.0) '@headlessui/react': 1.7.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-dropdown-menu': 2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -16086,11 +16094,11 @@ snapshots: '@types/codemirror': 5.60.10 clsx: 1.2.1 codemirror: 5.65.15 - codemirror-graphql: 2.0.10(@codemirror/language@6.11.0)(codemirror@5.65.15)(graphql@16.8.1) + codemirror-graphql: 2.0.10(@codemirror/language@6.11.0)(codemirror@5.65.15)(graphql@16.10.0) copy-to-clipboard: 3.3.3 framer-motion: 6.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - graphql: 16.8.1 - graphql-language-service: 5.2.0(graphql@16.8.1) + graphql: 16.10.0 + graphql-language-service: 5.2.0(graphql@16.10.0) markdown-it: 12.3.2 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -16102,13 +16110,13 @@ snapshots: - '@types/react-dom' - graphql-ws - '@graphiql/toolkit@0.9.1(@types/node@20.12.11)(graphql-ws@5.14.1(graphql@16.8.1))(graphql@16.8.1)': + '@graphiql/toolkit@0.9.1(@types/node@20.12.11)(graphql-ws@5.14.1(graphql@16.10.0))(graphql@16.10.0)': dependencies: '@n1ru4l/push-pull-async-iterable-iterator': 3.2.0 - graphql: 16.8.1 + graphql: 16.10.0 meros: 1.3.0(@types/node@20.12.11) optionalDependencies: - graphql-ws: 5.14.1(graphql@16.8.1) + graphql-ws: 5.14.1(graphql@16.10.0) transitivePeerDependencies: - '@types/node' @@ -21894,13 +21902,13 @@ snapshots: estree-walker: 3.0.3 periscopic: 3.1.0 - codemirror-graphql@2.0.10(@codemirror/language@6.11.0)(codemirror@5.65.15)(graphql@16.8.1): + codemirror-graphql@2.0.10(@codemirror/language@6.11.0)(codemirror@5.65.15)(graphql@16.10.0): dependencies: '@codemirror/language': 6.11.0 '@types/codemirror': 0.0.90 codemirror: 5.65.15 - graphql: 16.8.1 - graphql-language-service: 5.2.0(graphql@16.8.1) + graphql: 16.10.0 + graphql-language-service: 5.2.0(graphql@16.10.0) codemirror@5.65.15: {} @@ -23719,15 +23727,17 @@ snapshots: graphemer@1.4.0: {} - graphql-language-service@5.2.0(graphql@16.8.1): + graphql-language-service@5.2.0(graphql@16.10.0): dependencies: - graphql: 16.8.1 + graphql: 16.10.0 nullthrows: 1.1.1 vscode-languageserver-types: 3.17.5 - graphql-ws@5.14.1(graphql@16.8.1): + graphql-ws@5.14.1(graphql@16.10.0): dependencies: - graphql: 16.8.1 + graphql: 16.10.0 + + graphql@16.10.0: {} graphql@16.8.1: {} From e41d90926ba0716c4beae489cfe91fa96919aac8 Mon Sep 17 00:00:00 2001 From: Charis <26616127+charislam@users.noreply.github.com> Date: Tue, 29 Apr 2025 15:27:21 -0400 Subject: [PATCH 09/32] feat (content api): graphql validation (#35191) Continue building out the docs/api/graphql endpoint by adding more validation: -A query depth limiter - A query complexity limiter Also added dummy (empty) root resolver and schema, plus a dev-only GraphiQL page for better DX. Endpoint should still return a 404 for now as there is no resolver. --- apps/docs/app/api/graphql/route.ts | 56 ++++- apps/docs/app/api/graphql/validators.test.ts | 247 +++++++++++++++++++ apps/docs/app/api/graphql/validators.ts | 157 ++++++++++++ apps/docs/app/graphiql/page.tsx | 33 +++ apps/docs/package.json | 4 + apps/docs/resources/rootResolver.ts | 1 + apps/docs/resources/rootSchema.ts | 8 + pnpm-lock.yaml | 189 +++++++++++++- 8 files changed, 688 insertions(+), 7 deletions(-) create mode 100644 apps/docs/app/api/graphql/validators.test.ts create mode 100644 apps/docs/app/api/graphql/validators.ts create mode 100644 apps/docs/app/graphiql/page.tsx create mode 100644 apps/docs/resources/rootResolver.ts create mode 100644 apps/docs/resources/rootSchema.ts diff --git a/apps/docs/app/api/graphql/route.ts b/apps/docs/app/api/graphql/route.ts index 7c5e8c9fdde..7efda7c254a 100644 --- a/apps/docs/app/api/graphql/route.ts +++ b/apps/docs/app/api/graphql/route.ts @@ -1,8 +1,11 @@ -// import { graphql } from 'graphql' +import { type DocumentNode, graphql, GraphQLError, parse, specifiedRules, validate } from 'graphql' +import { createComplexityLimitRule } from 'graphql-validation-complexity' import { NextResponse } from 'next/server' import { z } from 'zod' - import { ApiError, InvalidRequestError } from '~/app/api/utils' +import { rootGraphQLResolver } from '~/resources/rootResolver' +import { rootGraphQLSchema } from '~/resources/rootSchema' +import { createQueryDepthLimiter } from './validators' export const runtime = 'edge' @@ -24,14 +27,57 @@ async function handleGraphQLRequest(request: Request): Promise { ) } - // const { query, variables, operationName } = parsedBody.data - // When endpoint is complete, this should return a response with the GraphQL - // query result + const { query, variables, operationName } = parsedBody.data + const validationErrors = validateGraphQLRequest(query) + if (validationErrors.length > 0) { + return NextResponse.json({ + errors: validationErrors.map((error) => ({ + message: error.message, + locations: error.locations, + path: error.path, + })), + }) + } + + const result = await graphql({ + schema: rootGraphQLSchema, + rootValue: rootGraphQLResolver, + contextValue: { request }, + source: query, + variableValues: variables, + operationName, + }) + // return NextResponse.json(result) // For now, just return a 404 response return new NextResponse('Not Found', { status: 404 }) } +function validateGraphQLRequest(query: string): ReadonlyArray { + const MAX_DEPTH = 9 + + let documentAST: DocumentNode + try { + documentAST = parse(query) + } catch (error: unknown) { + if (error instanceof GraphQLError) { + return [error] + } else { + throw error + } + } + const validationRules = [ + ...specifiedRules, + createQueryDepthLimiter(MAX_DEPTH), + createComplexityLimitRule(1500, { + scalarCost: 1, + objectCost: 2, + listFactor: 10, + }), + ] + return validate(rootGraphQLSchema, documentAST, validationRules) +} + export async function POST(request: Request): Promise { try { return await handleGraphQLRequest(request) diff --git a/apps/docs/app/api/graphql/validators.test.ts b/apps/docs/app/api/graphql/validators.test.ts new file mode 100644 index 00000000000..0659623848d --- /dev/null +++ b/apps/docs/app/api/graphql/validators.test.ts @@ -0,0 +1,247 @@ +import { buildSchema, parse, validate } from 'graphql' +import { describe, expect, test } from 'vitest' +import { createQueryDepthLimiter } from './validators' + +describe('createQueryDepthLimiter', () => { + // Create a test schema + const schema = buildSchema(` + type Post { + id: ID! + title: String + content: String + author: User + comments: [Comment] + } + + type User { + id: ID! + name: String + email: String + posts: [Post] + friends: [User] + } + + type Comment { + id: ID! + text: String + author: User + post: Post + replies: [Comment] + } + + type Query { + post(id: ID!): Post + user(id: ID!): User + posts: [Post] + users: [User] + } + `) + + test('should not report error for queries under max depth', () => { + const query = ` + query { + posts { + title + author { + name + } + } + } + ` + const queryAST = parse(query) + const maxDepth = 3 + const errors = validate(schema, queryAST, [createQueryDepthLimiter(maxDepth)]) + expect(errors).toHaveLength(0) + }) + + test('should report error for queries exceeding max depth', () => { + const query = ` + query { + posts { + title + author { + name + posts { + title + } + } + } + } + ` + const queryAST = parse(query) + const maxDepth = 3 + const errors = validate(schema, queryAST, [createQueryDepthLimiter(maxDepth)]) + expect(errors.length).toBeGreaterThan(0) + expect(errors[0].message).toContain('Query exceeds maximum depth of 3') + }) + + test('should ignore __typename fields when calculating depth', () => { + const query = ` + query { + posts { + __typename + title + author { + __typename + name + posts { + __typename + } + } + } + } + ` + const queryAST = parse(query) + const maxDepth = 3 + const errors = validate(schema, queryAST, [createQueryDepthLimiter(maxDepth)]) + expect(errors).toHaveLength(0) + }) + + test('should handle fragments correctly', () => { + const query = ` + fragment UserDetails on User { + id + name + posts { + title + } + } + + query { + users { + ...UserDetails + } + } + ` + const queryAST = parse(query) + const maxDepth = 3 + const errors = validate(schema, queryAST, [createQueryDepthLimiter(maxDepth)]) + expect(errors).toHaveLength(0) + }) + + test('should report error for fragments exceeding max depth', () => { + const query = ` + fragment UserDetails on User { + id + name + posts { + title + author { + name + } + } + } + + query { + users { + ...UserDetails + } + } + ` + const queryAST = parse(query) + const maxDepth = 3 + const errors = validate(schema, queryAST, [createQueryDepthLimiter(maxDepth)]) + expect(errors.length).toBeGreaterThan(0) + expect(errors[0].message).toContain( + 'Fragment spread "UserDetails" causes query to exceed maximum depth' + ) + }) + + test('should report error for fragment spreads causing excessive depth', () => { + const query = ` + fragment PostDetails on Post { + title + content + } + + fragment UserDetails on User { + name + posts { + ...PostDetails + } + } + + query { + users { + friends { + ...UserDetails + } + } + } + ` + const queryAST = parse(query) + const maxDepth = 3 + const errors = validate(schema, queryAST, [createQueryDepthLimiter(maxDepth)]) + expect(errors.length).toBeGreaterThan(0) + }) + + test('should handle nested fragments correctly', () => { + const query = ` + fragment CommentDetails on Comment { + text + author { + name + } + } + + fragment PostDetails on Post { + title + comments { + ...CommentDetails + } + } + + query { + posts { + ...PostDetails + } + } + ` + const queryAST = parse(query) + const maxDepth = 3 + const errors = validate(schema, queryAST, [createQueryDepthLimiter(maxDepth)]) + expect(errors.length).toBeGreaterThan(0) + }) + + test('should handle inline fragments', () => { + const query = ` + query { + posts { + ... on Post { + title + author { + name + } + } + } + } + ` + const queryAST = parse(query) + const maxDepth = 3 + const errors = validate(schema, queryAST, [createQueryDepthLimiter(maxDepth)]) + expect(errors).toHaveLength(0) + }) + + test('should detect circular fragment references', () => { + const query = ` + fragment UserPosts on User { + posts { + author { + ...UserPosts + } + } + } + + query { + users { + ...UserPosts + } + } + ` + const queryAST = parse(query) + const maxDepth = 5 + // This should not go into infinite recursion + const errors = validate(schema, queryAST, [createQueryDepthLimiter(maxDepth)]) + expect(errors.length).toBeGreaterThan(0) + }) +}) diff --git a/apps/docs/app/api/graphql/validators.ts b/apps/docs/app/api/graphql/validators.ts new file mode 100644 index 00000000000..f4ca8e68df9 --- /dev/null +++ b/apps/docs/app/api/graphql/validators.ts @@ -0,0 +1,157 @@ +import { + type FragmentDefinitionNode, + GraphQLError, + type SelectionNode, + type ValidationContext, + type ValidationRule, +} from 'graphql' + +/** + * Creates a validator that limits the depth of a GraphQL query. + * + * @param maxDepth The maximum allowed depth + * @returns A validation rule that enforces depth limits + */ +export function createQueryDepthLimiter(maxDepth: number): ValidationRule { + const fragmentDepthMap = new Map() + + return function limitQueryDepth(context) { + return { + Document: { + leave() { + fragmentDepthMap.clear() + }, + }, + Field: { + enter(node, _key, _parent, path, ancestors) { + // Skip __typename and introspection fields + if (node.name.value === '__typename' || node.name.value.startsWith('__')) { + return + } + + // Calculate the depth by counting the ancestors that are field nodes + const fieldAncestors = ancestors.filter( + (ancestor) => ancestor && 'kind' in ancestor && ancestor.kind === 'Field' + ) + const depth = fieldAncestors.length + 1 + + // Check if this field exceeds the maximum maxDepth + if (depth > maxDepth) { + const pathStr = path.join('.') + context.reportError( + new GraphQLError( + `Query exceeds maximum depth of ${maxDepth}. Got depth ${depth} for ${pathStr}.`, + { nodes: node } + ) + ) + } + }, + }, + FragmentDefinition: { + enter(node) { + calculateFragmentDepth(node, context, 0, fragmentDepthMap, maxDepth) + const depth = fragmentDepthMap.get(node.name.value)! + + if (depth > maxDepth) { + context.reportError( + new GraphQLError( + `Fragment "${node.name.value}" exceeds maximum depth of ${maxDepth}.`, + { nodes: node } + ) + ) + } + }, + }, + FragmentSpread: { + enter(node, _key, _parent, _path, ancestors) { + const fragmentName = node.name.value + const fragmentDepth = fragmentDepthMap.get(fragmentName) || 0 + + const fieldAncestors = ancestors.filter( + (ancestor) => ancestor && 'kind' in ancestor && ancestor.kind === 'Field' + ) + const currentDepth = fieldAncestors.length + + const totalDepth = currentDepth + fragmentDepth + + if (totalDepth > maxDepth) { + context.reportError( + new GraphQLError( + `Fragment spread "${fragmentName}" causes query to exceed maximum depth of ${maxDepth}.`, + { nodes: node } + ) + ) + } + }, + }, + } + } +} + +/** + * Helper function to calculate the depth of a fragment + */ +function calculateFragmentDepth( + node: FragmentDefinitionNode, + context: ValidationContext, + currentDepth: number, + visitedFragments: Map, + maxDepth: number +) { + const fragmentName = node.name.value + if (visitedFragments.has(fragmentName)) { + return + } + + let maxFragmentDepth = 0 + // Process all selections in the fragment + function processSelections(selections: ReadonlyArray, depth: number) { + for (const selection of selections) { + if (selection.kind === 'Field') { + // Skip __typename and introspection fields + if (selection.name.value === '__typename' || selection.name.value.startsWith('__')) { + continue + } + + const newDepth = depth + 1 + if (newDepth > maxFragmentDepth) { + maxFragmentDepth = newDepth + } + if (maxFragmentDepth > maxDepth) { + // Do not recurse anymore because we might go on indefinitely + continue + } + + // If there are nested selections, process them + if (selection.selectionSet) { + processSelections(selection.selectionSet.selections, newDepth) + } + } else if (selection.kind === 'FragmentSpread') { + // Process fragment spreads + const spreadName = selection.name.value + const fragment = context.getFragment(spreadName) + if (!fragment) continue + + const fragmentName = fragment.name.value + if (!visitedFragments.has(fragmentName)) { + calculateFragmentDepth(fragment, context, depth, visitedFragments, maxDepth) + } + const fragmentDepth = visitedFragments.get(fragmentName)! + const totalDepth = fragmentDepth + depth + + if (totalDepth > maxFragmentDepth) { + maxFragmentDepth = totalDepth + } + } else if (selection.kind === 'InlineFragment' && selection.selectionSet) { + // Process inline fragments + processSelections(selection.selectionSet.selections, depth) + } + } + } + + if (node.selectionSet) { + processSelections(node.selectionSet.selections, currentDepth) + } + + visitedFragments.set(fragmentName, maxFragmentDepth) +} diff --git a/apps/docs/app/graphiql/page.tsx b/apps/docs/app/graphiql/page.tsx new file mode 100644 index 00000000000..6443554bbbd --- /dev/null +++ b/apps/docs/app/graphiql/page.tsx @@ -0,0 +1,33 @@ +'use client' + +import dynamic from 'next/dynamic' +import { notFound } from 'next/navigation' +import { type ComponentProps } from 'react' +import { IS_DEV } from '~/lib/constants' + +const LazyGraphiQL = dynamic( + async () => { + const { GraphiQL: GraphiQLPrimitive } = await import('graphiql') + const { createGraphiQLFetcher } = await import('@graphiql/toolkit') + await import('graphiql/graphiql.css') + + return function GraphiQL(props: Omit, 'fetcher'>) { + const fetcher = createGraphiQLFetcher({ + url: 'http://localhost:3001/docs/api/graphql', + }) + return + } + }, + { + loading: () =>
Loading GraphiQL...
, + ssr: false, + } +) + +export default function GraphiQLPage() { + if (!IS_DEV) { + notFound() + } + + return +} diff --git a/apps/docs/package.json b/apps/docs/package.json index dd046d93891..b145e54337f 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -56,6 +56,7 @@ "framer-motion": "^11.0.3", "github-slugger": "^2.0.0", "graphql": "^16.10.0", + "graphql-validation-complexity": "^0.4.2", "gray-matter": "^4.0.3", "hast-util-has-property": "^2.0.1", "hast-util-to-html": "^9.0.0", @@ -112,9 +113,11 @@ }, "devDependencies": { "@aws-sdk/client-secrets-manager": "^3.410.0", + "@graphiql/toolkit": "^0.9.1", "@supabase/supa-mdx-lint": "0.2.6-alpha", "@types/common-tags": "^1.8.4", "@types/estree": "1.0.5", + "@types/graphql-validation-complexity": "^0.4.4", "@types/hast": "^2.3.4", "@types/jsrsasign": "^10.5.12", "@types/mdast": "^3.0.0", @@ -131,6 +134,7 @@ "dotenv": "^16.0.3", "ejs": "^3.1.10", "globby": "^13.2.2", + "graphiql": "^3.8.3", "openapi-types": "^12.1.3", "shiki": "^3.2.1", "simple-git": "^3.24.0", diff --git a/apps/docs/resources/rootResolver.ts b/apps/docs/resources/rootResolver.ts new file mode 100644 index 00000000000..c65cd01a5b6 --- /dev/null +++ b/apps/docs/resources/rootResolver.ts @@ -0,0 +1 @@ +export const rootGraphQLResolver = {} diff --git a/apps/docs/resources/rootSchema.ts b/apps/docs/resources/rootSchema.ts new file mode 100644 index 00000000000..7dd25048a82 --- /dev/null +++ b/apps/docs/resources/rootSchema.ts @@ -0,0 +1,8 @@ +import { GraphQLObjectType, GraphQLSchema } from 'graphql' + +export const rootGraphQLSchema = new GraphQLSchema({ + query: new GraphQLObjectType({ + name: 'RootQueryType', + fields: {}, + }), +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b21aa326339..b5b8299a5f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -285,6 +285,9 @@ importers: graphql: specifier: ^16.10.0 version: 16.10.0 + graphql-validation-complexity: + specifier: ^0.4.2 + version: 0.4.2(graphql@16.10.0) gray-matter: specifier: ^4.0.3 version: 4.0.3 @@ -448,6 +451,9 @@ importers: '@aws-sdk/client-secrets-manager': specifier: ^3.410.0 version: 3.468.0 + '@graphiql/toolkit': + specifier: ^0.9.1 + version: 0.9.1(@types/node@20.12.11)(graphql-ws@5.14.1(graphql@16.10.0))(graphql@16.10.0) '@supabase/supa-mdx-lint': specifier: 0.2.6-alpha version: 0.2.6-alpha @@ -457,6 +463,9 @@ importers: '@types/estree': specifier: 1.0.5 version: 1.0.5 + '@types/graphql-validation-complexity': + specifier: ^0.4.4 + version: 0.4.4 '@types/hast': specifier: ^2.3.4 version: 2.3.6 @@ -502,6 +511,9 @@ importers: globby: specifier: ^13.2.2 version: 13.2.2 + graphiql: + specifier: ^3.8.3 + version: 3.8.3(@codemirror/language@6.11.0)(@types/node@20.12.11)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@5.14.1(graphql@16.10.0))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) openapi-types: specifier: ^12.1.3 version: 12.1.3 @@ -3170,6 +3182,22 @@ packages: react: ^16.8.0 || ^17 || ^18 react-dom: ^16.8.0 || ^17 || ^18 + '@graphiql/react@0.28.2': + resolution: {integrity: sha512-6PE2Ff9dXpyQMHy3oKzCjT738kY2+wdQ4Xce8+1K+G2yMGZ716Fo0i4vW3S6ErHVI4S/C76gFTQlv/pzxU5ylg==} + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + + '@graphiql/toolkit@0.11.1': + resolution: {integrity: sha512-G02te70/oYYna5UhbH6TXwNxeQyWa+ChlPonUrKwC5Ot9ItraGJ9yUU4sS+YRaA+EvkzNoHG79XcW2k1QaAMiw==} + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + graphql-ws: '>= 4.5.0' + peerDependenciesMeta: + graphql-ws: + optional: true + '@graphiql/toolkit@0.9.1': resolution: {integrity: sha512-LVt9pdk0830so50ZnU2Znb2rclcoWznG8r8asqAENzV0U1FM1kuY0sdPpc/rBc9MmmNgnB6A+WZzDhq6dbhTHA==} peerDependencies: @@ -6519,6 +6547,9 @@ packages: '@types/geojson@7946.0.12': resolution: {integrity: sha512-uK2z1ZHJyC0nQRbuovXFt4mzXDwf27vQeUWNhfKGwRcWW429GOhP8HxUHlM6TLH4bzmlv/HlEjpvJh3JfmGsAA==} + '@types/graphql-validation-complexity@0.4.4': + resolution: {integrity: sha512-/0AmF5HYHmLErXRky/be86E4hAVO2LE5ejHdy3kf1rASgzBDG3Yb1yqoHxrZz+Nk72jt2UWCfa6DSfdpHDkuzg==} + '@types/hast@2.3.6': resolution: {integrity: sha512-47rJE80oqPmFdVDCD7IheXBrVdwuBgsYwoczFvKmwfo2Mzsnt+V9OONsYauFmICb6lQPpCuXYJWejBNs4pDJRg==} @@ -7670,6 +7701,13 @@ packages: codemirror: ^5.65.3 graphql: ^15.5.0 || ^16.0.0 + codemirror-graphql@2.2.0: + resolution: {integrity: sha512-egIiewf5zEH5LLSkJpJDpYxO1OkNruD0gTWiBrS1JmXk7yjt5WPw7jSmDRkWJx8JheHONltaJPNPWdTUT5LRIQ==} + peerDependencies: + '@codemirror/language': 6.0.0 + codemirror: ^5.65.3 + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + codemirror@5.65.15: resolution: {integrity: sha512-YC4EHbbwQeubZzxLl5G4nlbLc1T21QTrKGaOal/Pkm9dVDMZXMH7+ieSPEOZCtO9I68i8/oteJKOxzHC2zR+0g==} @@ -9214,6 +9252,10 @@ packages: get-tsconfig@4.7.2: resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + get-value@3.0.1: + resolution: {integrity: sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA==} + engines: {node: '>=6.0'} + giget@2.0.0: resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} hasBin: true @@ -9303,12 +9345,30 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphiql@3.8.3: + resolution: {integrity: sha512-cuPDYtXVKV86Pu5PHBX642Odi/uVEE2y1Jxq5rGO/Qy1G2lRp7ZZ7a/T30RzxhuLSWo9zUbzq0P3U49//H0Ugw==} + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + graphql-language-service@5.2.0: resolution: {integrity: sha512-o/ZgTS0pBxWm3hSF4+6GwiV1//DxzoLWEbS38+jqpzzy1d/QXBidwQuVYTOksclbtOJZ3KR/tZ8fi/tI6VpVMg==} hasBin: true peerDependencies: graphql: ^15.5.0 || ^16.0.0 + graphql-language-service@5.3.0: + resolution: {integrity: sha512-gCQIIy7lM9CB1KPLEb+DNZLczA9zuTLEOJE2hEQZTFYInogdmMDRa6RAkvM4LL0LcgcS+3uPs6KtHlcjCqRbUg==} + hasBin: true + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 || ^17.0.0-alpha.2 + + graphql-validation-complexity@0.4.2: + resolution: {integrity: sha512-4tzmN/70a06c2JH5fvISkoLX6oBDpqK22cvr2comge3HZHtBLD3n5Sl6MnQYMVhQqKGlpZWcCgD00MnyKNzYYg==} + peerDependencies: + graphql: '>=0.9.5' + graphql-ws@5.14.1: resolution: {integrity: sha512-aqkls1espsygP1PfkAuuLIV96IbztQ6EaADse97pw8wRIMT3+AL/OYfS8V2iCRkc0gzckitoDRGCQEdnySggiA==} engines: {node: '>=10'} @@ -10285,6 +10345,9 @@ packages: linkify-it@3.0.3: resolution: {integrity: sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + list-item@1.1.1: resolution: {integrity: sha512-S3D0WZ4J6hyM8o5SNKWaMYB1ALSacPZ2nHGEuCjmHZ+dc03gFeNZoNDcqfcnO4vDhTZmNrqrpYZCdXsRh22bzw==} engines: {node: '>=0.10.0'} @@ -10482,6 +10545,10 @@ packages: resolution: {integrity: sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==} hasBin: true + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + markdown-link@0.1.1: resolution: {integrity: sha512-TurLymbyLyo+kAUUAV9ggR9EPcDjP/ctlv9QAFiqUH7c+t6FlsbivPo9OKTU8xdOx9oNd2drW/Fi5RRElQbUqA==} engines: {node: '>=0.10.0'} @@ -10630,6 +10697,9 @@ packages: mdurl@1.0.1: resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + mdx-bundler@10.0.2: resolution: {integrity: sha512-0wF0zoCv+Ms4G+eSlk/jaKYoJHc0oXBaOma3kYlFJiKq9H8h41Dd66ioDBGF4noy80Pf7KTBQlyHfEpTqVml7A==} engines: {node: '>=18', npm: '>=6'} @@ -12150,6 +12220,10 @@ packages: psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + punycode@2.3.0: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} @@ -12222,6 +12296,11 @@ packages: react: ^16.8.5 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0 + react-compiler-runtime@19.0.0-beta-37ed2a7-20241206: + resolution: {integrity: sha512-9e6rCpVylr9EnVocgYAjft7+2v01BDpajeHKRoO+oc9pKcAMTpstHtHvE/TSVbyf4FvzCGjfKcfHM9XGTXI6Tw==} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-contexify@5.0.0: resolution: {integrity: sha512-2FIp7lxJ6dtfGr8EZ4uVV5p5TQjd0n2h/JU7PrejNIMiCeZWvSVPFh4lj1ZvjXosglBvP7q5JQQ8yUCdSaMSaw==} engines: {node: '>=10'} @@ -13916,6 +13995,9 @@ packages: uc.micro@1.0.6: resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.5.4: resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} @@ -16110,6 +16192,45 @@ snapshots: - '@types/react-dom' - graphql-ws + '@graphiql/react@0.28.2(@codemirror/language@6.11.0)(@types/node@20.12.11)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@5.14.1(graphql@16.10.0))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@graphiql/toolkit': 0.11.1(@types/node@20.12.11)(graphql-ws@5.14.1(graphql@16.10.0))(graphql@16.10.0) + '@headlessui/react': 1.7.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dropdown-menu': 2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-tooltip': 1.1.8(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/codemirror': 5.60.10 + clsx: 1.2.1 + codemirror: 5.65.15 + codemirror-graphql: 2.2.0(@codemirror/language@6.11.0)(codemirror@5.65.15)(graphql@16.10.0) + copy-to-clipboard: 3.3.3 + framer-motion: 6.5.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + get-value: 3.0.1 + graphql: 16.10.0 + graphql-language-service: 5.3.0(graphql@16.10.0) + markdown-it: 14.1.0 + react: 18.3.1 + react-compiler-runtime: 19.0.0-beta-37ed2a7-20241206(react@18.3.1) + react-dom: 18.3.1(react@18.3.1) + set-value: 4.1.0 + transitivePeerDependencies: + - '@codemirror/language' + - '@types/node' + - '@types/react' + - '@types/react-dom' + - graphql-ws + + '@graphiql/toolkit@0.11.1(@types/node@20.12.11)(graphql-ws@5.14.1(graphql@16.10.0))(graphql@16.10.0)': + dependencies: + '@n1ru4l/push-pull-async-iterable-iterator': 3.2.0 + graphql: 16.10.0 + meros: 1.3.0(@types/node@20.12.11) + optionalDependencies: + graphql-ws: 5.14.1(graphql@16.10.0) + transitivePeerDependencies: + - '@types/node' + '@graphiql/toolkit@0.9.1(@types/node@20.12.11)(graphql-ws@5.14.1(graphql@16.10.0))(graphql@16.10.0)': dependencies: '@n1ru4l/push-pull-async-iterable-iterator': 3.2.0 @@ -20467,6 +20588,10 @@ snapshots: '@types/geojson@7946.0.12': {} + '@types/graphql-validation-complexity@0.4.4': + dependencies: + graphql: 16.10.0 + '@types/hast@2.3.6': dependencies: '@types/unist': 2.0.8 @@ -21910,6 +22035,14 @@ snapshots: graphql: 16.10.0 graphql-language-service: 5.2.0(graphql@16.10.0) + codemirror-graphql@2.2.0(@codemirror/language@6.11.0)(codemirror@5.65.15)(graphql@16.10.0): + dependencies: + '@codemirror/language': 6.11.0 + '@types/codemirror': 0.0.90 + codemirror: 5.65.15 + graphql: 16.10.0 + graphql-language-service: 5.3.0(graphql@16.10.0) + codemirror@5.65.15: {} coffee-script@1.12.7: {} @@ -23599,6 +23732,10 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-value@3.0.1: + dependencies: + isobject: 3.0.1 + giget@2.0.0: dependencies: citty: 0.1.6 @@ -23727,12 +23864,37 @@ snapshots: graphemer@1.4.0: {} + graphiql@3.8.3(@codemirror/language@6.11.0)(@types/node@20.12.11)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@5.14.1(graphql@16.10.0))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@graphiql/react': 0.28.2(@codemirror/language@6.11.0)(@types/node@20.12.11)(@types/react-dom@18.3.0)(@types/react@18.3.3)(graphql-ws@5.14.1(graphql@16.10.0))(graphql@16.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + graphql: 16.10.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@codemirror/language' + - '@types/node' + - '@types/react' + - '@types/react-dom' + - graphql-ws + graphql-language-service@5.2.0(graphql@16.10.0): dependencies: graphql: 16.10.0 nullthrows: 1.1.1 vscode-languageserver-types: 3.17.5 + graphql-language-service@5.3.0(graphql@16.10.0): + dependencies: + debounce-promise: 3.1.2 + graphql: 16.10.0 + nullthrows: 1.1.1 + vscode-languageserver-types: 3.17.5 + + graphql-validation-complexity@0.4.2(graphql@16.10.0): + dependencies: + graphql: 16.10.0 + warning: 4.0.3 + graphql-ws@5.14.1(graphql@16.10.0): dependencies: graphql: 16.10.0 @@ -24865,6 +25027,10 @@ snapshots: dependencies: uc.micro: 1.0.6 + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + list-item@1.1.1: dependencies: expand-range: 1.8.2 @@ -25107,6 +25273,15 @@ snapshots: mdurl: 1.0.1 uc.micro: 1.0.6 + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + markdown-link@0.1.1: {} markdown-table@3.0.3: {} @@ -25490,6 +25665,8 @@ snapshots: mdurl@1.0.1: {} + mdurl@2.0.0: {} + mdx-bundler@10.0.2(esbuild@0.25.2)(supports-color@8.1.1): dependencies: '@babel/runtime': 7.26.10 @@ -26281,7 +26458,7 @@ snapshots: '@open-draft/until': 2.1.0 '@types/cookie': 0.6.0 '@types/statuses': 2.0.5 - graphql: 16.8.1 + graphql: 16.10.0 headers-polyfill: 4.0.3 is-node-process: 1.2.0 outvariant: 1.4.3 @@ -26307,7 +26484,7 @@ snapshots: '@open-draft/until': 2.1.0 '@types/cookie': 0.6.0 '@types/statuses': 2.0.5 - graphql: 16.8.1 + graphql: 16.10.0 headers-polyfill: 4.0.3 is-node-process: 1.2.0 outvariant: 1.4.3 @@ -27563,6 +27740,8 @@ snapshots: psl@1.9.0: {} + punycode.js@2.3.1: {} + punycode@2.3.0: {} punycode@2.3.1: @@ -27636,6 +27815,10 @@ snapshots: transitivePeerDependencies: - react-native + react-compiler-runtime@19.0.0-beta-37ed2a7-20241206(react@18.3.1): + dependencies: + react: 18.3.1 + react-contexify@5.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: clsx: 1.2.1 @@ -29768,6 +29951,8 @@ snapshots: uc.micro@1.0.6: {} + uc.micro@2.1.0: {} + ufo@1.5.4: {} uglify-js@3.17.4: From f3583a05bc33ca7302830d1285d2381c71356f94 Mon Sep 17 00:00:00 2001 From: Charis <26616127+charislam@users.noreply.github.com> Date: Tue, 29 Apr 2025 15:39:46 -0400 Subject: [PATCH 10/32] fix(docs): branch for wrappers docs (#35340) Wrappers docs should not be pulled from the main branch, which contains unreleased content, but instead from the latest release tag. --- .../extensions/wrappers/[[...slug]]/page.tsx | 87 +++++++++++++++++-- apps/docs/lib/octokit.ts | 2 +- 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx b/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx index 0bbbee54595..f5c6712dfcc 100644 --- a/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx @@ -4,29 +4,96 @@ import { readFile } from 'node:fs/promises' import { join, relative } from 'node:path' import rehypeSlug from 'rehype-slug' import emoji from 'remark-emoji' - +import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' import { genGuideMeta, genGuidesStaticParams, removeRedundantH1, } from '~/features/docs/GuidesMdx.utils' -import { GuideTemplate, newEditLink } from '~/features/docs/GuidesMdx.template' +import { REVALIDATION_TAGS } from '~/features/helpers.fetch' import { GUIDES_DIRECTORY, isValidGuideFrontmatter } from '~/lib/docs' import { UrlTransformFunction, linkTransform } from '~/lib/mdx/plugins/rehypeLinkTransform' import remarkMkDocsAdmonition from '~/lib/mdx/plugins/remarkAdmonition' import { removeTitle } from '~/lib/mdx/plugins/remarkRemoveTitle' import remarkPyMdownTabs from '~/lib/mdx/plugins/remarkTabs' -import { REVALIDATION_TAGS } from '~/features/helpers.fetch' +import { octokit } from '~/lib/octokit' export const dynamicParams = false // We fetch these docs at build time from an external repo const org = 'supabase' const repo = 'wrappers' -const branch = 'main' const docsDir = 'docs/catalog' const externalSite = 'https://supabase.github.io/wrappers' +type TagQueryResponse = { + repository: { + releases: { + nodes: + | { + tagName: string + }[] + | null + pageInfo: { + hasNextPage: boolean + endCursor: string | null + } + } + } +} + +const tagQuery = ` + query TagQuery($owner: String!, $name: String!, $after: String) { + repository(owner: $owner, name: $name) { + releases( + orderBy: { + field: CREATED_AT, + direction: DESC + }, + first: 5, + after: $after + ) { + nodes { + tagName + } + pageInfo { + hasNextPage + endCursor + } + } + } + } + ` + +async function getLatestRelease(after: string | null = null) { + try { + const { + repository: { + releases: { + nodes, + pageInfo: { hasNextPage, endCursor }, + }, + }, + } = await octokit().graphql(tagQuery, { + owner: org, + name: repo, + after, + request: { + fetch: (url: RequestInfo | URL, options?: RequestInit) => + fetch(url, { ...options, next: { tags: [REVALIDATION_TAGS.WRAPPERS] } }), + }, + }) + + return ( + nodes?.find((node) => node?.tagName?.match(/^v\d+\.\d+\.\d+/)).tagName ?? + (hasNextPage && endCursor ? await getLatestRelease(endCursor) : null) + ) + } catch (error) { + console.error(`Error fetching release tags for wrappers federated pages: ${error}`) + return null + } +} + // Each external docs page is mapped to a local page const pageMap = [ { @@ -193,8 +260,14 @@ const getContent = async (params: Params) => { isExternal = true let remoteFile: string ;({ remoteFile, meta } = federatedPage) - const repoPath = `${org}/${repo}/${branch}/${docsDir}/${remoteFile}` - editLink = `${org}/${repo}/blob/${branch}/${docsDir}/${remoteFile}` + + const tag = await getLatestRelease() + if (!tag) { + throw new Error('No latest release found for federated wrappers pages') + } + + const repoPath = `${org}/${repo}/${tag}/${docsDir}/${remoteFile}` + editLink = `${org}/${repo}/blob/${tag}/${docsDir}/${remoteFile}` const response = await fetch(`https://raw.githubusercontent.com/${repoPath}`, { next: { tags: [REVALIDATION_TAGS.WRAPPERS] }, @@ -259,4 +332,4 @@ const generateStaticParams = async () => { const generateMetadata = genGuideMeta(getContent) export default WrappersDocs -export { generateStaticParams, generateMetadata } +export { generateMetadata, generateStaticParams } diff --git a/apps/docs/lib/octokit.ts b/apps/docs/lib/octokit.ts index bfe5a0f1b88..edad471ae79 100644 --- a/apps/docs/lib/octokit.ts +++ b/apps/docs/lib/octokit.ts @@ -8,7 +8,7 @@ import { fetchRevalidatePerDay } from '~/features/helpers.fetch' let octokitInstance: Octokit -function octokit() { +export function octokit() { if (!octokitInstance) { // https://github.com/gr2m/universal-github-app-jwt?tab=readme-ov-file#converting-pkcs1-to-pkcs8 const privateKeyPkcs8 = crypto From ca1cd24a079700a7badf0e5b321219979d55284b Mon Sep 17 00:00:00 2001 From: Charis <26616127+charislam@users.noreply.github.com> Date: Tue, 29 Apr 2025 15:51:07 -0400 Subject: [PATCH 11/32] fix (content api): return graphql-spec compliant error (#35202) GraphQL errors were occasionally returned as normal HTTP status code errors, switched to using only GraphQL-spec compliant errors for the /docs/api/graphql endpoint. --- apps/docs/app/api/graphql/route.test.ts | 27 +++++++++++++++---------- apps/docs/app/api/graphql/route.ts | 25 +++++++++++------------ apps/docs/app/api/utils.ts | 15 ++++++++++++-- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/apps/docs/app/api/graphql/route.test.ts b/apps/docs/app/api/graphql/route.test.ts index 77d12a09ac4..c60bd1d9a62 100644 --- a/apps/docs/app/api/graphql/route.test.ts +++ b/apps/docs/app/api/graphql/route.test.ts @@ -7,39 +7,44 @@ describe('GraphQL API endpoint', () => { vi.spyOn(console, 'error').mockImplementation(() => {}) }) - it('should return 400 if request body is not valid JSON', async () => { + it('should return error if request body is not valid JSON', async () => { const request = new Request('http://localhost/api/graphql', { method: 'POST', body: 'not json', }) const response = await POST(request) - expect(response.status).toBe(400) - expect(await response.text()).toBe('Invalid request: Request body must be valid JSON') + const json = await response.json() + expect(json.errors[0].message).toBe('Invalid request: Request body must be valid JSON') }) - it('should return 400 if request body is missing required fields', async () => { + it('should return error if request body is missing required fields', async () => { const request = new Request('http://localhost/api/graphql', { method: 'POST', body: JSON.stringify({ variables: {} }), }) const response = await POST(request) - expect(response.status).toBe(400) - expect(await response.text()).toContain('Request body must be a valid GraphQL request object') + const json = await response.json() + expect(json.errors[0].message).toContain( + 'Invalid request: Request body must be valid GraphQL request object' + ) }) - it('should return 400 if query is not a string', async () => { + it('should return error if query is not a string', async () => { const request = new Request('http://localhost/api/graphql', { method: 'POST', body: JSON.stringify({ query: 123 }), }) const response = await POST(request) - expect(response.status).toBe(400) + const json = await response.json() + expect(json.errors[0].message).toContain( + 'Invalid request: Request body must be valid GraphQL request object' + ) }) - it('should return 500 for internal server errors', async () => { + it('should return error for internal server errors', async () => { const request = new Request('http://localhost/api/graphql', { method: 'POST', body: JSON.stringify({ query: 'query { hello }' }), @@ -50,7 +55,7 @@ describe('GraphQL API endpoint', () => { }) const response = await POST(request) - expect(response.status).toBe(500) - expect(await response.text()).toBe('Internal Server Error') + const json = await response.json() + expect(json.errors[0].message).toBe('Internal Server Error') }) }) diff --git a/apps/docs/app/api/graphql/route.ts b/apps/docs/app/api/graphql/route.ts index 7efda7c254a..2655de05625 100644 --- a/apps/docs/app/api/graphql/route.ts +++ b/apps/docs/app/api/graphql/route.ts @@ -2,7 +2,7 @@ import { type DocumentNode, graphql, GraphQLError, parse, specifiedRules, valida import { createComplexityLimitRule } from 'graphql-validation-complexity' import { NextResponse } from 'next/server' import { z } from 'zod' -import { ApiError, InvalidRequestError } from '~/app/api/utils' +import { ApiError, convertZodToInvalidRequestError, InvalidRequestError } from '~/app/api/utils' import { rootGraphQLResolver } from '~/resources/rootResolver' import { rootGraphQLSchema } from '~/resources/rootSchema' import { createQueryDepthLimiter } from './validators' @@ -16,14 +16,14 @@ const graphQLRequestSchema = z.object({ }) async function handleGraphQLRequest(request: Request): Promise { - const body = await request.json().catch(() => { - throw new InvalidRequestError('Request body must be valid JSON') + const body = await request.json().catch((error) => { + throw new InvalidRequestError('Request body must be valid JSON', error) }) const parsedBody = graphQLRequestSchema.safeParse(body) if (!parsedBody.success) { - const errorMessage = parsedBody.error?.message - throw new InvalidRequestError( - `Request body must be a valid GraphQL request object${errorMessage ? `: ${errorMessage}` : ''}` + throw convertZodToInvalidRequestError( + parsedBody.error, + 'Request body must be valid GraphQL request object' ) } @@ -47,10 +47,7 @@ async function handleGraphQLRequest(request: Request): Promise { variableValues: variables, operationName, }) - // return NextResponse.json(result) - - // For now, just return a 404 response - return new NextResponse('Not Found', { status: 404 }) + return NextResponse.json(result) } function validateGraphQLRequest(query: string): ReadonlyArray { @@ -85,11 +82,13 @@ export async function POST(request: Request): Promise { console.error(error) if (error instanceof ApiError) { - return new NextResponse(error.isPrivate() ? 'Internal Server Error' : error.message, { - status: error.statusCode(), + return NextResponse.json({ + errors: [{ message: error.isPrivate() ? 'Internal Server Error' : error.message }], }) } else { - return new NextResponse('Internal Server Error', { status: 500 }) + return NextResponse.json({ + errors: [{ message: 'Internal Server Error' }], + }) } } } diff --git a/apps/docs/app/api/utils.ts b/apps/docs/app/api/utils.ts index 5b3f93ea15b..12e23bb605e 100644 --- a/apps/docs/app/api/utils.ts +++ b/apps/docs/app/api/utils.ts @@ -1,4 +1,4 @@ -import { z } from 'zod' +import { z, type ZodError } from 'zod' export class ApiError extends Error { constructor(message: string) { @@ -15,7 +15,7 @@ export class ApiError extends Error { } export class InvalidRequestError extends ApiError { - constructor(message: string) { + constructor(message: string, source?: unknown) { super(`Invalid request: ${message}`) } @@ -27,3 +27,14 @@ export class InvalidRequestError extends ApiError { return 400 } } + +export function convertZodToInvalidRequestError( + error: ZodError, + prelude?: string +): InvalidRequestError { + const issue = error.issues[0] + const pathStr = issue.path.join('.') + const message = `${prelude ? `${prelude}: ` : ''}${issue.message} at key "${pathStr}"` + + return new InvalidRequestError(message, error) +} From 9e8107c07055346a3c9dbe6205fe34bf51d8494d Mon Sep 17 00:00:00 2001 From: Charis <26616127+charislam@users.noreply.github.com> Date: Tue, 29 Apr 2025 16:00:34 -0400 Subject: [PATCH 12/32] fix (content api): local dev introspection (#35279) * feat (content api): add top-level schema query Adds a top-level schema query that returns the stringified GraphQL schema for this endpoint. Useful for LLMs to access the stringified schema for prompting and tool calls. Query shape: ``` query Search { schema } ``` Query response: ``` { "data": { "schema": "schema {\n query: RootQueryType\n}\n\ntype RootQueryType {\n \"\"\"Get the GraphQL schema for this endpoint\"\"\"\n schema: String\n}" } } ``` Towards DOCS-214 * test (content api): add schema snapshot Add schema snapshot test to prevent unintentional changes to public API for this endpoint * fixup! feat (content api): add top-level schema query * fix (content api): local dev introspection The validation rules that are used for security in production are preventing GraphiQL from introspecting the schema in dev. Since this DX feature is incredibly useful, I'm turning off the complexity and depth validations for dev GraphiQL only. Local curls, etc., will still have production-standard validation so it can be tested locally. --- .../graphql/__snapshots__/route.test.ts.snap | 12 ++++++ apps/docs/app/api/graphql/route.test.ts | 25 ++++++++++- apps/docs/app/api/graphql/route.ts | 43 ++++++++++++------- apps/docs/resources/rootResolver.ts | 1 - apps/docs/resources/rootSchema.ts | 27 +++++++++++- 5 files changed, 88 insertions(+), 20 deletions(-) create mode 100644 apps/docs/app/api/graphql/__snapshots__/route.test.ts.snap delete mode 100644 apps/docs/resources/rootResolver.ts diff --git a/apps/docs/app/api/graphql/__snapshots__/route.test.ts.snap b/apps/docs/app/api/graphql/__snapshots__/route.test.ts.snap new file mode 100644 index 00000000000..92aa9a51c06 --- /dev/null +++ b/apps/docs/app/api/graphql/__snapshots__/route.test.ts.snap @@ -0,0 +1,12 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`/api/graphql schema snapshot > should match snapshot 1`] = ` +"schema { + query: RootQueryType +} + +type RootQueryType { + """Get the GraphQL schema for this endpoint""" + schema: String! +}" +`; diff --git a/apps/docs/app/api/graphql/route.test.ts b/apps/docs/app/api/graphql/route.test.ts index c60bd1d9a62..f77a823659e 100644 --- a/apps/docs/app/api/graphql/route.test.ts +++ b/apps/docs/app/api/graphql/route.test.ts @@ -1,7 +1,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import { POST } from './route' -describe('GraphQL API endpoint', () => { +describe('/api/graphql basic error statuses', () => { beforeEach(() => { vi.clearAllMocks() vi.spyOn(console, 'error').mockImplementation(() => {}) @@ -59,3 +59,26 @@ describe('GraphQL API endpoint', () => { expect(json.errors[0].message).toBe('Internal Server Error') }) }) + +describe('/api/graphql schema snapshot', () => { + it('should match snapshot', async () => { + const schemaQuery = ` + query { + schema + } + ` + const request = new Request('http://localhost/api/graphql', { + method: 'POST', + body: JSON.stringify({ query: schemaQuery }), + }) + + const response = await POST(request) + const json = await response.json() + expect(json.errors).toBeUndefined() + + const { + data: { schema }, + } = json + expect(schema).toMatchSnapshot() + }) +}) diff --git a/apps/docs/app/api/graphql/route.ts b/apps/docs/app/api/graphql/route.ts index 2655de05625..c786b1c92fa 100644 --- a/apps/docs/app/api/graphql/route.ts +++ b/apps/docs/app/api/graphql/route.ts @@ -3,12 +3,34 @@ import { createComplexityLimitRule } from 'graphql-validation-complexity' import { NextResponse } from 'next/server' import { z } from 'zod' import { ApiError, convertZodToInvalidRequestError, InvalidRequestError } from '~/app/api/utils' -import { rootGraphQLResolver } from '~/resources/rootResolver' +import { BASE_PATH, IS_DEV } from '~/lib/constants' import { rootGraphQLSchema } from '~/resources/rootSchema' import { createQueryDepthLimiter } from './validators' export const runtime = 'edge' +const MAX_DEPTH = 9 + +const validationRules = [ + ...specifiedRules, + createQueryDepthLimiter(MAX_DEPTH), + createComplexityLimitRule(1500, { + scalarCost: 1, + objectCost: 2, + listFactor: 10, + }), +] + +function isDevGraphiQL(request: Request) { + const origin = request.headers.get('Origin') + const referrer = request.headers.get('Referer') + return ( + IS_DEV && + origin.startsWith('http://localhost') && + referrer === `${origin}${BASE_PATH ?? ''}/graphiql` + ) +} + const graphQLRequestSchema = z.object({ query: z.string(), variables: z.record(z.any()).optional(), @@ -28,7 +50,7 @@ async function handleGraphQLRequest(request: Request): Promise { } const { query, variables, operationName } = parsedBody.data - const validationErrors = validateGraphQLRequest(query) + const validationErrors = validateGraphQLRequest(query, isDevGraphiQL(request)) if (validationErrors.length > 0) { return NextResponse.json({ errors: validationErrors.map((error) => ({ @@ -41,7 +63,6 @@ async function handleGraphQLRequest(request: Request): Promise { const result = await graphql({ schema: rootGraphQLSchema, - rootValue: rootGraphQLResolver, contextValue: { request }, source: query, variableValues: variables, @@ -50,9 +71,7 @@ async function handleGraphQLRequest(request: Request): Promise { return NextResponse.json(result) } -function validateGraphQLRequest(query: string): ReadonlyArray { - const MAX_DEPTH = 9 - +function validateGraphQLRequest(query: string, isDevGraphiQL = false): ReadonlyArray { let documentAST: DocumentNode try { documentAST = parse(query) @@ -63,16 +82,8 @@ function validateGraphQLRequest(query: string): ReadonlyArray { throw error } } - const validationRules = [ - ...specifiedRules, - createQueryDepthLimiter(MAX_DEPTH), - createComplexityLimitRule(1500, { - scalarCost: 1, - objectCost: 2, - listFactor: 10, - }), - ] - return validate(rootGraphQLSchema, documentAST, validationRules) + const rules = isDevGraphiQL ? specifiedRules : validationRules + return validate(rootGraphQLSchema, documentAST, rules) } export async function POST(request: Request): Promise { diff --git a/apps/docs/resources/rootResolver.ts b/apps/docs/resources/rootResolver.ts deleted file mode 100644 index c65cd01a5b6..00000000000 --- a/apps/docs/resources/rootResolver.ts +++ /dev/null @@ -1 +0,0 @@ -export const rootGraphQLResolver = {} diff --git a/apps/docs/resources/rootSchema.ts b/apps/docs/resources/rootSchema.ts index 7dd25048a82..05c5d6d0f1b 100644 --- a/apps/docs/resources/rootSchema.ts +++ b/apps/docs/resources/rootSchema.ts @@ -1,8 +1,31 @@ -import { GraphQLObjectType, GraphQLSchema } from 'graphql' +import { + GraphQLNonNull, + GraphQLObjectType, + GraphQLSchema, + GraphQLString, + printSchema, +} from 'graphql' + +const GRAPHQL_FIELD_INTROSPECT = 'schema' + +async function resolveIntrospect() { + const schema = printSchema(rootGraphQLSchema) + return schema +} + +const introspectRoot = { + [GRAPHQL_FIELD_INTROSPECT]: { + description: 'Get the GraphQL schema for this endpoint', + type: new GraphQLNonNull(GraphQLString), + resolve: resolveIntrospect, + }, +} export const rootGraphQLSchema = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'RootQueryType', - fields: {}, + fields: { + ...introspectRoot, + }, }), }) From 65d0a3cd9b1492cf4a490d5eb6ebd0b1c21edae8 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Tue, 29 Apr 2025 20:09:42 -0230 Subject: [PATCH 13/32] Remove logo (#35366) --- apps/www/components/logos.tsx | 5 ----- apps/www/public/images/logos/publicity/krea.svg | 10 ---------- 2 files changed, 15 deletions(-) delete mode 100644 apps/www/public/images/logos/publicity/krea.svg diff --git a/apps/www/components/logos.tsx b/apps/www/components/logos.tsx index 12f0e8b9440..b2ef09c79c7 100644 --- a/apps/www/components/logos.tsx +++ b/apps/www/components/logos.tsx @@ -78,11 +78,6 @@ const logos = [ alt: 'humata', name: 'humata', }, - { - image: `/images/logos/publicity/krea.svg`, - alt: 'krea', - name: 'krea', - }, { image: `/images/logos/publicity/udio.svg`, alt: 'udio', diff --git a/apps/www/public/images/logos/publicity/krea.svg b/apps/www/public/images/logos/publicity/krea.svg deleted file mode 100644 index 76079718c47..00000000000 --- a/apps/www/public/images/logos/publicity/krea.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - From f08e5a3c179e36caf8e066ccf8870a1030dff56d Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 30 Apr 2025 13:58:20 +0800 Subject: [PATCH 14/32] Tiny update to delete confirmation modal (#35321) --- .../StorageExplorer/ConfirmDeleteModal.tsx | 34 +++++++------------ .../StorageExplorer/StorageExplorer.tsx | 2 +- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/ConfirmDeleteModal.tsx b/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/ConfirmDeleteModal.tsx index 6f041a1de0b..1aefdb6456d 100644 --- a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/ConfirmDeleteModal.tsx +++ b/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/ConfirmDeleteModal.tsx @@ -1,6 +1,7 @@ import { noop } from 'lodash' import { useEffect, useState } from 'react' -import { Alert, Button, Modal } from 'ui' + +import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' import { StorageItem } from '../Storage.types' interface ConfirmDeleteModalProps { @@ -10,7 +11,7 @@ interface ConfirmDeleteModalProps { onSelectDelete: () => void } -const ConfirmDeleteModal = ({ +export const ConfirmDeleteModal = ({ visible = false, selectedItemsToDelete = [], onSelectCancel = noop, @@ -42,28 +43,19 @@ const ConfirmDeleteModal = ({ } return ( - {title}} + title={{title}} size="medium" onCancel={onSelectCancel} - customFooter={ -
- - -
- } - > - - - {description} - - -
+ onConfirm={onConfirmDelete} + variant="destructive" + alert={{ + base: { variant: 'destructive' }, + title: 'This action cannot be undone', + description, + }} + /> ) } diff --git a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/StorageExplorer.tsx b/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/StorageExplorer.tsx index a6d131379d2..020eacb45ab 100644 --- a/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/StorageExplorer.tsx +++ b/apps/studio/components/to-be-cleaned/Storage/StorageExplorer/StorageExplorer.tsx @@ -9,7 +9,7 @@ import { IS_PLATFORM } from 'lib/constants' import { useStorageStore } from 'localStores/storageExplorer/StorageExplorerStore' import { useStorageExplorerStateSnapshot } from 'state/storage-explorer' import { STORAGE_ROW_TYPES, STORAGE_VIEWS } from '../Storage.constants' -import ConfirmDeleteModal from './ConfirmDeleteModal' +import { ConfirmDeleteModal } from './ConfirmDeleteModal' import CustomExpiryModal from './CustomExpiryModal' import FileExplorer from './FileExplorer' import FileExplorerHeader from './FileExplorerHeader' From a5f9142fd406ca1bd9f79647dd1b9517e1e2b2bd Mon Sep 17 00:00:00 2001 From: Jordi Enric <37541088+jordienr@users.noreply.github.com> Date: Wed, 30 Apr 2025 08:07:59 +0200 Subject: [PATCH 15/32] fix logs datepicker handling global copy events (#35361) --- .../interfaces/Settings/Logs/Logs.DatePickers.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx b/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx index a705ec69390..a113cd1539f 100644 --- a/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx +++ b/apps/studio/components/interfaces/Settings/Logs/Logs.DatePickers.tsx @@ -10,6 +10,7 @@ import TimeSplitInput from 'components/ui/DatePicker/TimeSplitInput' import { Button, PopoverContent_Shadcn_, PopoverTrigger_Shadcn_, Popover_Shadcn_, cn } from 'ui' import { LOGS_LARGE_DATE_RANGE_DAYS_THRESHOLD } from './Logs.constants' import type { DatetimeHelper } from './Logs.types' +import { copyToClipboard } from 'lib/helpers' export type DatePickerValue = { to: string @@ -175,12 +176,13 @@ export const LogsDatePicker = ({ onSubmit, helpers, value }: PropsWithChildren

{ - document.addEventListener('paste', handlePaste) - document.addEventListener('copy', handleCopy) + if (open) { + document.addEventListener('paste', handlePaste) + document.addEventListener('copy', handleCopy) + } return () => { document.removeEventListener('paste', handlePaste) document.removeEventListener('copy', handleCopy) } - }, [startDate, endDate]) + }, [open, startDate, endDate]) const isLargeRange = Math.abs(dayjs(startDate).diff(dayjs(endDate), 'days')) > From 21bbc93afa4fbd7269e4d1ee9b78384060ad07aa Mon Sep 17 00:00:00 2001 From: Jonathan Summers-Muir Date: Wed, 30 Apr 2025 14:19:21 +0800 Subject: [PATCH 16/32] Chore/table editor filter sorts logic moved to hooks (#35138) * init * update Popovers to use new hooks * Update Header.tsx * made primitive components for filter and sorts * Delete FilterPopoverWrapper.tsx * Delete SortPopoverWrapper.tsx * remove * Create README.md * Update README.md * fix sort popover issues * Update SupabaseGrid.tsx * move DeleteConfirmationDialogs into context * fix issue with * more stuff for alaister * fix ts and tables pages * First round of clean up * Update README.md * Smol fix * Fix issues identified * Smol fix * Fix updating table name in database/tables not invalidating * Improve SQL editor invalidation logic * Add fix to reopen last opened table when landing on table editor * Smol fix --------- Co-authored-by: Alaister Young Co-authored-by: Joshen Lim --- apps/studio/components/grid/SupabaseGrid.tsx | 67 ++--- .../grid/components/header/Header.tsx | 103 ++------ .../header/filter/FilterPopover.tsx | 143 +---------- .../header/filter/FilterPopoverPrimitive.tsx | 136 ++++++++++ .../components/header/sort/SortPopover.tsx | 164 +----------- .../header/sort/SortPopoverPrimitive.tsx | 236 ++++++++++++++++++ .../grid/components/header/sort/index.ts | 1 - apps/studio/components/grid/hooks/README.md | 101 ++++++++ .../grid/hooks/useSaveTableEditorState.ts | 58 +++++ .../components/grid/hooks/useTableFilter.ts | 32 +++ .../components/grid/hooks/useTableSort.ts | 50 ++++ .../DeleteConfirmationDialogs.tsx | 87 ++----- .../ForeignRowSelector/ForeignRowSelector.tsx | 36 ++- .../SidePanelEditor/SidePanelEditor.utils.tsx | 1 + .../TableGridEditor/TableGridEditor.tsx | 40 ++- .../TableEditorLayout/TableEditorMenu.tsx | 4 +- apps/studio/data/sql/execute-sql-mutation.ts | 91 ++----- apps/studio/data/table-editor/keys.ts | 4 +- .../data/table-editor/table-editor-types.ts | 37 ++- .../table-row-delete-all-mutation.ts | 5 +- .../data/table-rows/table-rows-query.ts | 45 ++-- apps/studio/data/table-rows/utils.ts | 12 +- .../project/[ref]/database/tables/[id].tsx | 16 +- .../project/[ref]/database/tables/index.tsx | 29 ++- .../pages/project/[ref]/editor/[id].tsx | 28 +-- .../pages/project/[ref]/editor/index.tsx | 4 +- apps/studio/state/table-editor-table.tsx | 5 +- apps/studio/state/table-editor.tsx | 1 - 28 files changed, 872 insertions(+), 664 deletions(-) create mode 100644 apps/studio/components/grid/components/header/filter/FilterPopoverPrimitive.tsx create mode 100644 apps/studio/components/grid/components/header/sort/SortPopoverPrimitive.tsx delete mode 100644 apps/studio/components/grid/components/header/sort/index.ts create mode 100644 apps/studio/components/grid/hooks/README.md create mode 100644 apps/studio/components/grid/hooks/useSaveTableEditorState.ts create mode 100644 apps/studio/components/grid/hooks/useTableFilter.ts create mode 100644 apps/studio/components/grid/hooks/useTableSort.ts diff --git a/apps/studio/components/grid/SupabaseGrid.tsx b/apps/studio/components/grid/SupabaseGrid.tsx index d24332ae8f5..d6dfe395600 100644 --- a/apps/studio/components/grid/SupabaseGrid.tsx +++ b/apps/studio/components/grid/SupabaseGrid.tsx @@ -1,4 +1,4 @@ -import { PropsWithChildren, useCallback, useEffect, useRef, useState } from 'react' +import { PropsWithChildren, useEffect, useRef, useState } from 'react' import { DataGridHandle } from 'react-data-grid' import { DndProvider } from 'react-dnd' import { HTML5Backend } from 'react-dnd-html5-backend' @@ -7,24 +7,21 @@ import { createPortal } from 'react-dom' import { useParams } from 'common' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { useTableRowsQuery } from 'data/table-rows/table-rows-query' -import { useTableEditorFiltersSort } from 'hooks/misc/useTableEditorFiltersSort' import { RoleImpersonationState } from 'lib/role-impersonation' import { EMPTY_ARR } from 'lib/void' import { useRoleImpersonationStateSnapshot } from 'state/role-impersonation-state' import { useTableEditorStateSnapshot } from 'state/table-editor' import { useTableEditorTableStateSnapshot } from 'state/table-editor-table' -import { - filtersToUrlParams, - formatFilterURLParams, - formatSortURLParams, - saveTableEditorStateToLocalStorage, -} from './SupabaseGrid.utils' + import { Shortcuts } from './components/common/Shortcuts' import Footer from './components/footer/Footer' import { Grid } from './components/grid/Grid' import Header, { HeaderProps } from './components/header/Header' import { RowContextMenu } from './components/menu' -import { Filter, GridProps } from './types' +import { GridProps } from './types' + +import { useTableFilter } from './hooks/useTableFilter' +import { useTableSort } from './hooks/useTableSort' export const SupabaseGrid = ({ customHeader, @@ -39,43 +36,15 @@ export const SupabaseGrid = ({ const tableId = _id ? Number(_id) : undefined const { project } = useProjectContext() + const tableEditorSnap = useTableEditorStateSnapshot() const snap = useTableEditorTableStateSnapshot() const gridRef = useRef(null) const [mounted, setMounted] = useState(false) - const { filters: filter, sorts: sort, setParams } = useTableEditorFiltersSort() - - const sorts = formatSortURLParams(snap.table.name, sort as string[] | undefined) - const filters = formatFilterURLParams(filter as string[]) - - const onApplyFilters = useCallback( - (appliedFilters: Filter[]) => { - snap.setEnforceExactCount(false) - // Reset page to 1 when filters change - snap.setPage(1) - - const filters = filtersToUrlParams(appliedFilters) - - setParams((prevParams) => { - return { - ...prevParams, - filter: filters, - } - }) - - if (project?.ref) { - saveTableEditorStateToLocalStorage({ - projectRef: project.ref, - tableName: snap.table.name, - schema: snap.table.schema, - filters: filters, - }) - } - }, - [project?.ref, snap.table.name, snap.table.schema] - ) + const { filters, onApplyFilters } = useTableFilter() + const { sorts, onApplySorts } = useTableSort() const roleImpersonationState = useRoleImpersonationStateSnapshot() @@ -93,15 +62,13 @@ export const SupabaseGrid = ({ { keepPreviousData: true, retryDelay: (retryAttempt, error: any) => { - if (error && error.message?.includes('does not exist')) { - setParams((prevParams) => { - return { - ...prevParams, - ...{ sort: undefined }, - } - }) - } - if (retryAttempt > 3) { + const doesNotExistError = error && error.message?.includes('does not exist') + const tooManyRequestsError = error.message?.includes('Too Many Requests') + const vaultError = error.message?.includes('query vault failed') + + if (doesNotExistError) onApplySorts([]) + + if (retryAttempt > 3 || doesNotExistError || tooManyRequestsError || vaultError) { return Infinity } return 5000 @@ -118,7 +85,7 @@ export const SupabaseGrid = ({ return (

-
+
{children || ( <> diff --git a/apps/studio/components/grid/components/header/Header.tsx b/apps/studio/components/grid/components/header/Header.tsx index f88b172e15f..55847f98d3e 100644 --- a/apps/studio/components/grid/components/header/Header.tsx +++ b/apps/studio/components/grid/components/header/Header.tsx @@ -3,16 +3,12 @@ import saveAs from 'file-saver' import { ArrowUp, ChevronDown, FileText, Trash } from 'lucide-react' import Link from 'next/link' import Papa from 'papaparse' -import { ReactNode, useCallback, useState } from 'react' +import { ReactNode, useState } from 'react' import { toast } from 'sonner' import { useParams } from 'common' -import { - filtersToUrlParams, - saveTableEditorStateToLocalStorage, - sortsToUrlParams, -} from 'components/grid/SupabaseGrid.utils' -import type { Filter, Sort } from 'components/grid/types' +import { useTableFilter } from 'components/grid/hooks/useTableFilter' +import { useTableSort } from 'components/grid/hooks/useTableSort' import GridHeaderActions from 'components/interfaces/TableGridEditor/GridHeaderActions' import { formatTableRowsToSQL } from 'components/interfaces/TableGridEditor/TableEntity.utils' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' @@ -22,7 +18,6 @@ import { fetchAllTableRows, useTableRowsQuery } from 'data/table-rows/table-rows import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' -import { useTableEditorFiltersSort } from 'hooks/misc/useTableEditorFiltersSort' import { RoleImpersonationState } from 'lib/role-impersonation' import { useRoleImpersonationStateSnapshot, @@ -40,8 +35,8 @@ import { Separator, SonnerProgress, } from 'ui' -import FilterPopover from './filter/FilterPopover' -import { SortPopover } from './sort' +import { FilterPopover } from './filter/FilterPopover' +import { SortPopover } from './sort/SortPopover' // [Joshen] CSV exports require this guard as a fail-safe if the table is // just too large for a browser to keep all the rows in memory before // exporting. Either that or export as multiple CSV sheets with max n rows each @@ -58,27 +53,21 @@ export const MAX_EXPORT_ROW_COUNT_MESSAGE = ( ) export type HeaderProps = { - sorts: Sort[] - filters: Filter[] customHeader: ReactNode } -const Header = ({ sorts, filters, customHeader }: HeaderProps) => { +const Header = ({ customHeader }: HeaderProps) => { const snap = useTableEditorTableStateSnapshot() return (
{customHeader ? ( - <>{customHeader} + customHeader + ) : snap.selectedRows.size > 0 ? ( + ) : ( - <> - {snap.selectedRows.size > 0 ? ( - - ) : ( - - )} - + )}
@@ -93,6 +82,8 @@ const DefaultHeader = () => { const tableEditorSnap = useTableEditorStateSnapshot() const snap = useTableEditorTableStateSnapshot() const org = useSelectedOrganization() + const canCreateColumns = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'columns') + const { mutate: sendEvent } = useSendEventMutation() const onAddRow = snap.editable && (snap.table.columns ?? []).length > 0 ? tableEditorSnap.onAddRow : undefined @@ -101,68 +92,11 @@ const DefaultHeader = () => { const canAddNew = onAddRow !== undefined || onAddColumn !== undefined - // [Joshen] Using this logic to block both column and row creation/update/delete - const canCreateColumns = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'columns') - - const { filters, sorts, setParams } = useTableEditorFiltersSort() - - const onApplyFilters = useCallback( - (appliedFilters: Filter[]) => { - snap.setEnforceExactCount(false) - // Reset page to 1 when filters change - snap.setPage(1) - - const newFilters = filtersToUrlParams(appliedFilters) - - setParams((prevParams) => { - return { - filter: newFilters, - sort: prevParams.sort, - } - }) - - if (projectRef) { - saveTableEditorStateToLocalStorage({ - projectRef, - tableName: snap.table.name, - schema: snap.table.schema, - filters: newFilters, - }) - } - }, - [projectRef, snap.table.name, snap.table.schema, setParams] - ) - - const onApplySorts = useCallback( - (appliedSorts: Sort[]) => { - const newSorts = sortsToUrlParams(appliedSorts) - - setParams((prevParams) => { - return { - filter: prevParams.filter, - sort: newSorts, - } - }) - - if (projectRef) { - saveTableEditorStateToLocalStorage({ - projectRef, - tableName: snap.table.name, - schema: snap.table.schema, - sorts: newSorts, - }) - } - }, - [projectRef, snap.table.name, snap.table.schema, setParams] - ) - - const { mutate: sendEvent } = useSendEventMutation() - return (
- - + +
{canAddNew && ( <> @@ -284,11 +218,7 @@ const DefaultHeader = () => { ) } -type RowHeaderProps = { - sorts: Sort[] - filters: Filter[] -} -const RowHeader = ({ sorts, filters }: RowHeaderProps) => { +const RowHeader = () => { const { project } = useProjectContext() const tableEditorSnap = useTableEditorStateSnapshot() const snap = useTableEditorTableStateSnapshot() @@ -296,6 +226,9 @@ const RowHeader = ({ sorts, filters }: RowHeaderProps) => { const roleImpersonationState = useRoleImpersonationStateSnapshot() const isImpersonatingRole = roleImpersonationState.role !== undefined + const { filters } = useTableFilter() + const { sorts } = useTableSort() + const [isExporting, setIsExporting] = useState(false) const { data } = useTableRowsQuery({ diff --git a/apps/studio/components/grid/components/header/filter/FilterPopover.tsx b/apps/studio/components/grid/components/header/filter/FilterPopover.tsx index ccc6b91295c..cab1b6e51b1 100644 --- a/apps/studio/components/grid/components/header/filter/FilterPopover.tsx +++ b/apps/studio/components/grid/components/header/filter/FilterPopover.tsx @@ -1,145 +1,22 @@ -import update from 'immutability-helper' -import { isEqual } from 'lodash' -import { FilterIcon, Plus } from 'lucide-react' -import { KeyboardEvent, useCallback, useMemo, useState } from 'react' +import { useMemo } from 'react' +import { useTableFilter } from 'components/grid/hooks/useTableFilter' import { formatFilterURLParams } from 'components/grid/SupabaseGrid.utils' -import type { Filter } from 'components/grid/types' -import { useTableEditorTableStateSnapshot } from 'state/table-editor-table' -import { - Button, - PopoverContent_Shadcn_, - PopoverSeparator_Shadcn_, - PopoverTrigger_Shadcn_, - Popover_Shadcn_, -} from 'ui' -import FilterRow from './FilterRow' +import { FilterPopoverPrimitive } from './FilterPopoverPrimitive' export interface FilterPopoverProps { - filters: string[] portal?: boolean - onApplyFilters: (filters: Filter[]) => void } -const FilterPopover = ({ filters, portal = true, onApplyFilters }: FilterPopoverProps) => { - const [open, setOpen] = useState(false) +export const FilterPopover = ({ portal = true }: FilterPopoverProps) => { + const { urlFilters, onApplyFilters } = useTableFilter() - const btnText = - (filters || []).length > 0 - ? `Filtered by ${filters.length} rule${filters.length > 1 ? 's' : ''}` - : 'Filter' + // Convert string[] to Filter[] + const filters = useMemo(() => { + return formatFilterURLParams(urlFilters ?? []) + }, [urlFilters]) return ( - - - - - - - - - ) -} - -export default FilterPopover - -interface FilterOverlayProps { - filters: string[] - onApplyFilters: (filter: Filter[]) => void -} - -const FilterOverlay = ({ filters: filtersFromUrl, onApplyFilters }: FilterOverlayProps) => { - const snap = useTableEditorTableStateSnapshot() - - const initialFilters = useMemo( - () => formatFilterURLParams((filtersFromUrl as string[]) ?? []), - [filtersFromUrl] - ) - const [filters, setFilters] = useState(initialFilters) - - function onAddFilter() { - const column = snap.table.columns[0]?.name - - if (column) { - setFilters([ - ...filters, - { - column, - operator: '=', - value: '', - }, - ]) - } - } - - const onChangeFilter = useCallback((index: number, filter: Filter) => { - setFilters((currentFilters) => - update(currentFilters, { - [index]: { - $set: filter, - }, - }) - ) - }, []) - - const onDeleteFilter = useCallback((index: number) => { - setFilters((currentFilters) => - update(currentFilters, { - $splice: [[index, 1]], - }) - ) - }, []) - - const onSelectApplyFilters = () => { - // [Joshen] Trim empty spaces in input for only UUID type columns - const formattedFilters = filters.map((f) => { - const column = snap.table.columns.find((c) => c.name === f.column) - if (column?.format === 'uuid') return { ...f, value: f.value.trim() } - else return f - }) - setFilters(formattedFilters) - onApplyFilters(formattedFilters) - } - - function handleEnterKeyDown(event: KeyboardEvent) { - if (event.key === 'Enter') onSelectApplyFilters() - } - - return ( -
-
- {filters.map((filter, index) => ( - - ))} - {filters.length == 0 && ( -
-
No filters applied to this view
-

Add a column below to filter the view

-
- )} -
- -
- - -
-
+ ) } diff --git a/apps/studio/components/grid/components/header/filter/FilterPopoverPrimitive.tsx b/apps/studio/components/grid/components/header/filter/FilterPopoverPrimitive.tsx new file mode 100644 index 00000000000..8891ff8dcef --- /dev/null +++ b/apps/studio/components/grid/components/header/filter/FilterPopoverPrimitive.tsx @@ -0,0 +1,136 @@ +import { isEqual } from 'lodash' +import { Filter as FilterIcon, Plus } from 'lucide-react' +import { KeyboardEvent, useCallback, useMemo, useState } from 'react' + +import type { Filter } from 'components/grid/types' +import { useTableEditorTableStateSnapshot } from 'state/table-editor-table' +import { + Button, + PopoverContent_Shadcn_, + PopoverSeparator_Shadcn_, + PopoverTrigger_Shadcn_, + Popover_Shadcn_, +} from 'ui' +import FilterRow from './FilterRow' + +export interface FilterPopoverPrimitiveProps { + buttonText?: string + filters: Filter[] + onApplyFilters: (filters: Filter[]) => void + portal?: boolean +} + +export const FilterPopoverPrimitive = ({ + buttonText, + filters, + onApplyFilters, + portal = true, +}: FilterPopoverPrimitiveProps) => { + const [open, setOpen] = useState(false) + const snap = useTableEditorTableStateSnapshot() + + // Internal state management + const [localFilters, setLocalFilters] = useState(filters) + + // Update local state when filters prop changes + useMemo(() => { + setLocalFilters(filters) + }, [filters]) + + const displayButtonText = + buttonText ?? + (filters.length > 0 + ? `Filtered by ${filters.length} rule${filters.length > 1 ? 's' : ''}` + : 'Filter') + + const onAddFilter = () => { + const column = snap.table.columns[0]?.name + if (column) { + setLocalFilters([ + ...localFilters, + { + column, + operator: '=', + value: '', + }, + ]) + } + } + + const onChangeFilter = useCallback((index: number, filter: Filter) => { + setLocalFilters((currentFilters) => [ + ...currentFilters.slice(0, index), + filter, + ...currentFilters.slice(index + 1), + ]) + }, []) + + const onDeleteFilter = useCallback((index: number) => { + setLocalFilters((currentFilters) => [ + ...currentFilters.slice(0, index), + ...currentFilters.slice(index + 1), + ]) + }, []) + + const onSelectApplyFilters = () => { + // [Joshen] Trim empty spaces in input for only UUID type columns + const formattedFilters = localFilters.map((f) => { + const column = snap.table.columns.find((c) => c.name === f.column) + if (column?.format === 'uuid') return { ...f, value: f.value.trim() } + else return f + }) + setLocalFilters(formattedFilters) + onApplyFilters(formattedFilters) + } + + function handleEnterKeyDown(event: KeyboardEvent) { + if (event.key === 'Enter') onSelectApplyFilters() + } + + return ( + + + + + +
+
+ {localFilters.map((filter, index) => ( + + ))} + {localFilters.length == 0 && ( +
+
No filters applied to this view
+

+ Add a column below to filter the view +

+
+ )} +
+ +
+ + +
+
+
+
+ ) +} diff --git a/apps/studio/components/grid/components/header/sort/SortPopover.tsx b/apps/studio/components/grid/components/header/sort/SortPopover.tsx index cbd90ecb274..f7cc7e20400 100644 --- a/apps/studio/components/grid/components/header/sort/SortPopover.tsx +++ b/apps/studio/components/grid/components/header/sort/SortPopover.tsx @@ -1,163 +1,23 @@ -import update from 'immutability-helper' -import { isEqual } from 'lodash' -import { ChevronDown, List } from 'lucide-react' -import { useCallback, useMemo, useState } from 'react' +import { useMemo } from 'react' import { formatSortURLParams } from 'components/grid/SupabaseGrid.utils' -import { DropdownControl } from 'components/grid/components/common/DropdownControl' -import type { Sort } from 'components/grid/types' +import { useTableSort } from 'components/grid/hooks/useTableSort' import { useTableEditorTableStateSnapshot } from 'state/table-editor-table' -import { - Button, - PopoverContent_Shadcn_, - PopoverSeparator_Shadcn_, - PopoverTrigger_Shadcn_, - Popover_Shadcn_, -} from 'ui' -import SortRow from './SortRow' +import { SortPopoverPrimitive } from './SortPopoverPrimitive' export interface SortPopoverProps { - sorts: string[] portal?: boolean - onApplySorts: (sorts: Sort[]) => void } -const SortPopover = ({ sorts, portal = true, onApplySorts }: SortPopoverProps) => { - const [open, setOpen] = useState(false) +export const SortPopover = ({ portal = true }: SortPopoverProps) => { + const { urlSorts, onApplySorts } = useTableSort() + const tableState = useTableEditorTableStateSnapshot() + const tableName = tableState?.table?.name || '' - const btnText = - (sorts || []).length > 0 - ? `Sorted by ${sorts.length} rule${sorts.length > 1 ? 's' : ''}` - : 'Sort' + // Convert string[] to Sort[] + const sorts = useMemo(() => { + return tableName && urlSorts ? formatSortURLParams(tableName, urlSorts) : [] + }, [tableName, urlSorts]) - return ( - - - - - - - - - ) -} - -export default SortPopover - -export interface SortOverlayProps { - sorts: string[] - onApplySorts: (sorts: Sort[]) => void -} - -const SortOverlay = ({ sorts: sortsFromUrl, onApplySorts }: SortOverlayProps) => { - const snap = useTableEditorTableStateSnapshot() - - const initialSorts = useMemo( - () => formatSortURLParams(snap.table.name, sortsFromUrl ?? []), - [snap.table.name, sortsFromUrl] - ) - const [sorts, setSorts] = useState(initialSorts) - - const columns = snap.table.columns.filter((x) => { - // exclude json/jsonb columns from sorting. Sorting by json fields in PG is only possible if you provide key from - // the JSON object. - if (x.dataType === 'json' || x.dataType === 'jsonb') { - return false - } - const found = sorts.find((y) => y.column == x.name) - return !found - }) - - const dropdownOptions = - columns?.map((x) => { - return { value: x.name, label: x.name } - }) || [] - - function onAddSort(columnName: string | number) { - setSorts([...sorts, { table: snap.table.name, column: columnName as string, ascending: true }]) - } - - const onDeleteSort = useCallback((column: string) => { - setSorts((currentSorts) => currentSorts.filter((sort) => sort.column !== column)) - }, []) - - const onToggleSort = useCallback((column: string, ascending: boolean) => { - setSorts((currentSorts) => { - const idx = currentSorts.findIndex((x) => x.column === column) - - return update(currentSorts, { - [idx]: { - $merge: { ascending }, - }, - }) - }) - }, []) - - const onDragSort = useCallback((dragIndex: number, hoverIndex: number) => { - setSorts((currentSort) => - update(currentSort, { - $splice: [ - [dragIndex, 1], - [hoverIndex, 0, currentSort[dragIndex]], - ], - }) - ) - }, []) - - return ( -
- {sorts.map((sort, index) => ( - - ))} - {sorts.length === 0 && ( -
-
No sorts applied to this view
-

Add a column below to sort the view

-
- )} - - -
- {columns && columns.length > 0 ? ( - - - - ) : ( -

All columns have been added

- )} -
- -
-
-
- ) + return } diff --git a/apps/studio/components/grid/components/header/sort/SortPopoverPrimitive.tsx b/apps/studio/components/grid/components/header/sort/SortPopoverPrimitive.tsx new file mode 100644 index 00000000000..7cba3696a96 --- /dev/null +++ b/apps/studio/components/grid/components/header/sort/SortPopoverPrimitive.tsx @@ -0,0 +1,236 @@ +import { isEqual } from 'lodash' +import { ChevronDown, List } from 'lucide-react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' + +import type { Sort } from 'components/grid/types' +import { useTableEditorTableStateSnapshot } from 'state/table-editor-table' +import { + Button, + PopoverContent_Shadcn_, + PopoverSeparator_Shadcn_, + PopoverTrigger_Shadcn_, + Popover_Shadcn_, +} from 'ui' +import { DropdownControl } from '../../common/DropdownControl' +import SortRow from './SortRow' + +export interface SortPopoverPrimitiveProps { + buttonText?: string + sorts: Sort[] + onApplySorts: (sorts: Sort[]) => void + portal?: boolean +} + +/** + * SortPopoverPrimitive - A component for sorting table columns + * + * This component maintains a draft state of sorts while editing, then applies + * them to the parent component when "Apply" is clicked. + * + * To avoid issues with drag-and-drop reconciliation, we use a special sync mechanism + * that properly detects external vs. internal updates. + */ +export const SortPopoverPrimitive = ({ + buttonText, + sorts, + onApplySorts, + portal = true, +}: SortPopoverPrimitiveProps) => { + const [open, setOpen] = useState(false) + const snap = useTableEditorTableStateSnapshot() + + // Local state for draft sorts + const [localSorts, setLocalSorts] = useState(sorts) + + // Track the last props we received for comparison + const lastSortsRef = useRef(sorts) + // Track if we're in the middle of applying our own changes + const isApplyingRef = useRef(false) + + // Sync with props when they change, but in a smarter way + useEffect(() => { + // If we're in the middle of applying changes, don't sync from props + if (isApplyingRef.current) { + isApplyingRef.current = false + return + } + + // If the props changed unexpectedly (not due to our own actions) + // then we should update our local state + if (!isEqual(sorts, lastSortsRef.current)) { + setLocalSorts(sorts) + lastSortsRef.current = sorts + } + }, [sorts]) + + // Fix: Use localSorts for button text, not sorts + const displayButtonText = + buttonText ?? + (localSorts.length > 0 + ? `Sorted by ${localSorts.length} rule${localSorts.length > 1 ? 's' : ''}` + : 'Sort') + + // Filter available columns to exclude columns already in sorts + const columns = useMemo(() => { + if (!snap?.table?.columns) return [] + return snap.table.columns.filter((x) => { + if (x.dataType === 'json' || x.dataType === 'jsonb') return false + const found = localSorts.find((y) => y.column == x.name) + return !found + }) + }, [snap?.table?.columns, localSorts]) + + // Format the columns for the dropdown + const dropdownOptions = useMemo(() => { + return columns?.map((x) => ({ value: x.name, label: x.name })) || [] + }, [columns]) + + // Add a new sort + const onAddSort = (columnName: string | number) => { + const currentTableName = snap.table?.name + if (currentTableName) { + setLocalSorts([ + ...localSorts, + { table: currentTableName, column: columnName as string, ascending: true }, + ]) + } + } + + // Remove a sort by column name + const onDeleteSort = useCallback((column: string) => { + setLocalSorts((currentSorts) => currentSorts.filter((sort) => sort.column !== column)) + }, []) + + // Toggle ascending/descending for a column + const onToggleSort = useCallback((column: string, ascending: boolean) => { + setLocalSorts((currentSorts) => { + const index = currentSorts.findIndex((x) => x.column === column) + if (index === -1) return currentSorts + const updatedSort = { ...currentSorts[index], ascending } + return [...currentSorts.slice(0, index), updatedSort, ...currentSorts.slice(index + 1)] + }) + }, []) + + // Handle drag-and-drop reordering + const onDragSort = useCallback((dragIndex: number, hoverIndex: number) => { + setLocalSorts((currentSort) => { + if ( + dragIndex < 0 || + dragIndex >= currentSort.length || + hoverIndex < 0 || + hoverIndex >= currentSort.length + ) { + return currentSort + } + const itemToMove = currentSort[dragIndex] + const remainingItems = [ + ...currentSort.slice(0, dragIndex), + ...currentSort.slice(dragIndex + 1), + ] + return [ + ...remainingItems.slice(0, hoverIndex), + itemToMove, + ...remainingItems.slice(hoverIndex), + ] + }) + }, []) + + // Fix: Compare for meaningful changes (only column order and ascending) + const hasChanges = useMemo(() => { + if (localSorts.length !== sorts.length) return true + + // Compare each sort by relevant properties + return localSorts.some((localSort, index) => { + const propSort = sorts[index] + return ( + !propSort || + localSort.column !== propSort.column || + localSort.ascending !== propSort.ascending + ) + }) + }, [localSorts, sorts]) + + // Apply the sorts to the parent component + const onSelectApplySorts = () => { + // Mark that we're applying our changes to prevent re-syncing + isApplyingRef.current = true + + // Update our last sorts ref to the current local state + lastSortsRef.current = [...localSorts] + + // Create deep copies to avoid reference issues + const sortsCopy = localSorts.map((sort) => ({ ...sort })) + + // Apply the sorts + onApplySorts(sortsCopy) + } + + // Generate stable keys for SortRow components to avoid reconciliation issues + const getSortRowKey = (sort: Sort, index: number) => { + return `sort-${sort.table}-${sort.column}-${index}` + } + + const content = ( +
+ {localSorts.map((sort, index) => ( + + ))} + {localSorts.length === 0 && ( +
+
No sorts applied to this view
+

Add a column below to sort the view

+
+ )} + + +
+ {dropdownOptions && dropdownOptions.length > 0 ? ( + + + + ) : ( +

All columns have been added

+ )} +
+ +
+
+
+ ) + + return ( + + + + + + {content} + + + ) +} diff --git a/apps/studio/components/grid/components/header/sort/index.ts b/apps/studio/components/grid/components/header/sort/index.ts deleted file mode 100644 index 8f4094016a6..00000000000 --- a/apps/studio/components/grid/components/header/sort/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as SortPopover } from './SortPopover' diff --git a/apps/studio/components/grid/hooks/README.md b/apps/studio/components/grid/hooks/README.md new file mode 100644 index 00000000000..559e08b58dc --- /dev/null +++ b/apps/studio/components/grid/hooks/README.md @@ -0,0 +1,101 @@ +# Table Filtering and Sorting Developer notes + +## Overview + +The table filtering and sorting system uses a URL-based state persistence pattern combined with custom hooks that abstract the implementation details from consuming components. This architecture provides several benefits: + +- **Persistent state**: Filters and sorts are stored in URL parameters, enabling bookmarking and sharing +- **Separation of concerns**: Logic is separated from UI components +- **Draft-then-apply pattern**: UI components maintain draft state until explicitly applied + +## Core Hooks + +### `useTableFilter` + +```typescript +// Returns: { filters, urlFilters, onApplyFilters } +``` + +The `useTableFilter` hook manages filter state with these responsibilities: + +- Retrieves raw filter parameters from URL +- Formats filter parameters into usable Filter[] objects +- Provides a callback to apply new filters +- Persists changes to URL parameters +- Triggers side effects through `saveFiltersAndTriggerSideEffects` + +Key design aspects: + +- No direct snapshot interaction, keeping it focused solely on filter management +- Uses URL parameters as the source of truth +- Forwards filter changes to URL and triggers application-specific side effects + +### `useTableSort` + +```typescript +// Returns: { sorts, urlSorts, onApplySorts } +``` + +The `useTableSort` hook manages sort state with these responsibilities: + +- Retrieves raw filter parameters from URL +- Formats sort parameters into usable Sort[] objects (needs table name) +- Provides a callback to apply new sorts +- Persists changes to URL parameters +- Triggers side effects through `saveSortsAndTriggerSideEffects` + +Key design aspects: + +- Handles applying table name to sort objects +- Maintains URL parameters as source of truth +- Forwards sort changes to URL and triggers application-specific side effects + +## Component Implementation + +### FilterPopoverPrimitive and SortPopoverPrimitive + +These components follow a "draft and apply" pattern: + +1. **Local state management**: Both components maintain a local state copy of filters/sorts +2. **Edit operations**: Changes like adding, modifying, or deleting are made to the local state +3. **Apply operations**: Only when the user clicks "Apply" are the changes committed via the callback +4. **Synchronization**: Local state is synchronized with props when external changes occur + +## Data Flow + +1. URL parameters store the raw filter/sort state +2. Hooks read and format these parameters into usable objects +3. UI components receive formatted objects and callbacks +4. Components maintain draft state for editing +5. When "Apply" is clicked, callbacks update URL parameters +6. Side effects are triggered via dedicated save hooks + +## Component Usage + +Components using these hooks should follow this pattern: + +```tsx +function TableComponent() { + // Get filter data and callbacks + const { filters, onApplyFilters } = useTableFilter() + + // Get sort data and callbacks + const { sorts, onApplySorts } = useTableSort() + + return ( + <> + + + + + {/* Table rendering with filters and sorts applied */} + + ) +} +``` + +## Implementation Notes + +- Filter and sort parameters are stored in URL using specific formats +- Conversion utilities (`formatFilterURLParams`, `formatSortURLParams`, etc.) handle translation between URL strings and typed objects +- Side effect hooks manage database persistence and related operations diff --git a/apps/studio/components/grid/hooks/useSaveTableEditorState.ts b/apps/studio/components/grid/hooks/useSaveTableEditorState.ts new file mode 100644 index 00000000000..9157100956c --- /dev/null +++ b/apps/studio/components/grid/hooks/useSaveTableEditorState.ts @@ -0,0 +1,58 @@ +import { useCallback } from 'react' + +import { saveTableEditorStateToLocalStorage } from 'components/grid/SupabaseGrid.utils' +import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' +import { useTableEditorTableStateSnapshot } from 'state/table-editor-table' + +/** + * Hook for saving state and triggering side effects. + */ +export function useSaveTableEditorState() { + const { project } = useProjectContext() + const snap = useTableEditorTableStateSnapshot() + + const saveDataAndTriggerSideEffects = useCallback( + (dataToSave: { filters?: string[]; sorts?: string[] }) => { + const projectRef = project?.ref + + if (!projectRef) { + return console.warn( + '[useSaveTableEditorState] ProjectRef missing, cannot save or trigger side effects.' + ) + } + + try { + snap.setPage(1) + snap.setEnforceExactCount(false) + + const tableName = snap.table?.name + const schema = snap.table?.schema + + if (tableName) { + saveTableEditorStateToLocalStorage({ + projectRef, + tableName, + schema, + ...dataToSave, + }) + } else { + console.warn('[useSaveTableEditorState] Table name missing, skipping localStorage save.') + } + } catch (error) { + console.error('[useSaveTableEditorState] Error during interaction with snapshot:', error) + } + }, + [snap, project] + ) + + const saveFiltersAndTriggerSideEffects = useCallback( + (urlFilters: string[]) => saveDataAndTriggerSideEffects({ filters: urlFilters }), + [saveDataAndTriggerSideEffects] + ) + const saveSortsAndTriggerSideEffects = useCallback( + (urlSorts: string[]) => saveDataAndTriggerSideEffects({ sorts: urlSorts }), + [saveDataAndTriggerSideEffects] + ) + + return { saveFiltersAndTriggerSideEffects, saveSortsAndTriggerSideEffects } +} diff --git a/apps/studio/components/grid/hooks/useTableFilter.ts b/apps/studio/components/grid/hooks/useTableFilter.ts new file mode 100644 index 00000000000..0181bbce9ca --- /dev/null +++ b/apps/studio/components/grid/hooks/useTableFilter.ts @@ -0,0 +1,32 @@ +import { useCallback } from 'react' + +import { filtersToUrlParams, formatFilterURLParams } from 'components/grid/SupabaseGrid.utils' +import type { Filter } from 'components/grid/types' +import { useTableEditorFiltersSort } from 'hooks/misc/useTableEditorFiltersSort' +import { useSaveTableEditorState } from './useSaveTableEditorState' + +/** + * Hook for managing table filter URL parameters and saving. + * NO direct snapshot interaction. + */ +export function useTableFilter() { + const { filters: urlFilters, setParams } = useTableEditorFiltersSort() + const { saveFiltersAndTriggerSideEffects } = useSaveTableEditorState() + + const filters = formatFilterURLParams(urlFilters) + + const onApplyFilters = useCallback( + (appliedFilters: Filter[]) => { + const newUrlFilters = filtersToUrlParams(appliedFilters) + setParams((prevParams) => ({ ...prevParams, filter: newUrlFilters })) + saveFiltersAndTriggerSideEffects(newUrlFilters) + }, + [setParams, saveFiltersAndTriggerSideEffects] + ) + + return { + filters, // Formatted Filter[] object array + urlFilters, // Raw string[] from URL + onApplyFilters, // Callback to apply changes + } +} diff --git a/apps/studio/components/grid/hooks/useTableSort.ts b/apps/studio/components/grid/hooks/useTableSort.ts new file mode 100644 index 00000000000..9cbd1c70a7a --- /dev/null +++ b/apps/studio/components/grid/hooks/useTableSort.ts @@ -0,0 +1,50 @@ +import { useCallback, useMemo } from 'react' + +import { formatSortURLParams, sortsToUrlParams } from 'components/grid/SupabaseGrid.utils' +import type { Sort } from 'components/grid/types' +import { useTableEditorFiltersSort } from 'hooks/misc/useTableEditorFiltersSort' +import { useTableEditorTableStateSnapshot } from 'state/table-editor-table' +import { useSaveTableEditorState } from './useSaveTableEditorState' + +/** + * Hook for managing table sort URL parameters and saving. + * Uses snapshot ONLY to get table name for formatting/mapping. + * Uses useSaveTableEditorState for saving and side effects. + * Does NOT format initial sorts (needs table name externally). + * Does NOT interact with snapshot directly. + */ +export function useTableSort() { + const { sorts: urlSorts, setParams } = useTableEditorFiltersSort() + const snap = useTableEditorTableStateSnapshot() + const { saveSortsAndTriggerSideEffects } = useSaveTableEditorState() + + const tableName = useMemo(() => snap.table?.name || '', [snap]) + + const sorts = useMemo(() => { + return formatSortURLParams(tableName, urlSorts) + }, [tableName, urlSorts]) + + const onApplySorts = useCallback( + (appliedSorts: Sort[]) => { + if (!tableName) { + return console.warn( + '[useTableSort] Table name missing in callback, cannot apply sort correctly.' + ) + } + + const sortsWithTable = appliedSorts.map((sort) => ({ ...sort, table: tableName })) + const newUrlSorts = sortsToUrlParams(sortsWithTable) + + setParams((prevParams) => ({ ...prevParams, sort: newUrlSorts })) + + saveSortsAndTriggerSideEffects(newUrlSorts) + }, + [snap, setParams, saveSortsAndTriggerSideEffects] + ) + + return { + sorts, + urlSorts, + onApplySorts, + } +} diff --git a/apps/studio/components/interfaces/TableGridEditor/DeleteConfirmationDialogs.tsx b/apps/studio/components/interfaces/TableGridEditor/DeleteConfirmationDialogs.tsx index 62bb1fd0627..361f79ea9fe 100644 --- a/apps/studio/components/interfaces/TableGridEditor/DeleteConfirmationDialogs.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/DeleteConfirmationDialogs.tsx @@ -1,106 +1,50 @@ -import type { PostgresTable } from '@supabase/postgres-meta' import { ExternalLink } from 'lucide-react' import Link from 'next/link' import { toast } from 'sonner' -import { - formatFilterURLParams, - saveTableEditorStateToLocalStorageDebounced, -} from 'components/grid/SupabaseGrid.utils' +import { useTableFilter } from 'components/grid/hooks/useTableFilter' import type { SupaRow } from 'components/grid/types' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { useDatabaseColumnDeleteMutation } from 'data/database-columns/database-column-delete-mutation' -import { Entity } from 'data/table-editor/table-editor-types' +import { TableLike } from 'data/table-editor/table-editor-types' import { useTableRowDeleteAllMutation } from 'data/table-rows/table-row-delete-all-mutation' import { useTableRowDeleteMutation } from 'data/table-rows/table-row-delete-mutation' import { useTableRowTruncateMutation } from 'data/table-rows/table-row-truncate-mutation' import { useTableDeleteMutation } from 'data/tables/table-delete-mutation' -import { TablesData, useGetTables } from 'data/tables/tables-query' -import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState' -import { noop } from 'lib/void' import { useGetImpersonatedRoleState } from 'state/role-impersonation-state' import { useTableEditorStateSnapshot } from 'state/table-editor' import { AlertDescription_Shadcn_, AlertTitle_Shadcn_, Alert_Shadcn_, Button, Checkbox } from 'ui' import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal' -import { useTableEditorFiltersSort } from 'hooks/misc/useTableEditorFiltersSort' export type DeleteConfirmationDialogsProps = { - selectedTable?: Entity | PostgresTable - onAfterDeleteTable?: (tables: TablesData) => void + selectedTable?: TableLike + onTableDeleted?: () => void } const DeleteConfirmationDialogs = ({ selectedTable, - onAfterDeleteTable = noop, + onTableDeleted, }: DeleteConfirmationDialogsProps) => { const { project } = useProjectContext() const snap = useTableEditorStateSnapshot() - const { selectedSchema } = useQuerySchemaState() - - const { filters: filter, setParams } = useTableEditorFiltersSort() - const filters = formatFilterURLParams(filter as string[]) - - const getTables = useGetTables({ - projectRef: project?.ref, - connectionString: project?.connectionString, - }) + const { filters, onApplyFilters } = useTableFilter() const removeDeletedColumnFromFiltersAndSorts = ({ - ref, - tableName, - schema, columnName, }: { - ref: string - tableName: string - schema: string + ref?: string + tableName?: string + schema?: string columnName: string }) => { - setParams((prevParams) => { - const existingFilters = (prevParams?.filter ?? []) as string[] - const existingSorts = (prevParams?.sort ?? []) as string[] - - const newFiltersAndSorts = { - filter: existingFilters.filter((filter: string) => { - const [column] = filter.split(':') - if (column !== columnName) return filter - }), - sort: existingSorts.filter((sort: string) => { - const [column] = sort.split(':') - if (column !== columnName) return sort - }), - } - - // Overwrite local storage without the deleted column - saveTableEditorStateToLocalStorageDebounced({ - projectRef: ref, - tableName, - schema, - filters: newFiltersAndSorts.filter, - sorts: newFiltersAndSorts.sort, - }) - - return { - ...prevParams, - ...newFiltersAndSorts, - } - }) + onApplyFilters(filters.filter((filter) => filter.column !== columnName)) } const { mutate: deleteColumn } = useDatabaseColumnDeleteMutation({ onSuccess: () => { if (!(snap.confirmationDialog?.type === 'column')) return const selectedColumnToDelete = snap.confirmationDialog.column - if (!project?.ref) return - if (!selectedTable?.name) return - - removeDeletedColumnFromFiltersAndSorts({ - ref: project?.ref, - tableName: selectedTable?.name, - schema: selectedColumnToDelete.schema, - columnName: selectedColumnToDelete.name, - }) - + removeDeletedColumnFromFiltersAndSorts({ columnName: selectedColumnToDelete.name }) toast.success(`Successfully deleted column "${selectedColumnToDelete.name}"`) }, onError: (error) => { @@ -114,9 +58,8 @@ const DeleteConfirmationDialogs = ({ }) const { mutate: deleteTable } = useTableDeleteMutation({ onSuccess: async () => { - const tables = await getTables(selectedSchema) - onAfterDeleteTable(tables) toast.success(`Successfully deleted table "${selectedTable?.name}"`) + onTableDeleted?.() }, onError: (error) => { toast.error(`Failed to delete ${selectedTable?.name}: ${error.message}`) @@ -231,13 +174,13 @@ const DeleteConfirmationDialogs = ({ truncateRows({ projectRef: project.ref, connectionString: project.connectionString, - table: selectedTable as any, + table: selectedTable, }) } else { deleteAllRows({ projectRef: project.ref, connectionString: project.connectionString, - table: selectedTable as any, + table: selectedTable, filters, roleImpersonationState: getImpersonatedRoleState(), }) @@ -246,7 +189,7 @@ const DeleteConfirmationDialogs = ({ deleteRows({ projectRef: project.ref, connectionString: project.connectionString, - table: selectedTable as any, + table: selectedTable, rows: selectedRowsToDelete as SupaRow[], roleImpersonationState: getImpersonatedRoleState(), }) diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx index 221e6bfa353..92eab845314 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/RowEditor/ForeignRowSelector/ForeignRowSelector.tsx @@ -4,15 +4,9 @@ import { DndProvider } from 'react-dnd' import { HTML5Backend } from 'react-dnd-html5-backend' import { useParams } from 'common' -import { - filtersToUrlParams, - formatFilterURLParams, - formatSortURLParams, - sortsToUrlParams, -} from 'components/grid/SupabaseGrid.utils' import RefreshButton from 'components/grid/components/header/RefreshButton' -import FilterPopover from 'components/grid/components/header/filter/FilterPopover' -import { SortPopover } from 'components/grid/components/header/sort' +import { FilterPopoverPrimitive } from 'components/grid/components/header/filter/FilterPopoverPrimitive' +import { SortPopoverPrimitive } from 'components/grid/components/header/sort/SortPopoverPrimitive' import type { Filter } from 'components/grid/types' import { Sort } from 'components/grid/types' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' @@ -65,9 +59,9 @@ const ForeignRowSelector = ({ id: tableId, }) - const [{ sort: sorts, filter: filters }, setParams] = useState<{ - filter: string[] - sort: string[] + const [{ sort: sorts, filter: filters }, setFiltersAndSorts] = useState<{ + filter: Filter[] + sort: Sort[] }>({ filter: [], sort: [] }) const onApplyFilters = (appliedFilters: Filter[]) => { @@ -76,19 +70,19 @@ const ForeignRowSelector = ({ setPage(1) } - setParams((prevParams) => { + setFiltersAndSorts((prevParams) => { return { ...prevParams, - filter: filtersToUrlParams(appliedFilters), + filter: appliedFilters, } }) } const onApplySorts = (appliedSorts: Sort[]) => { - setParams((prevParams) => { + setFiltersAndSorts((prevParams) => { return { ...prevParams, - sort: sortsToUrlParams(appliedSorts), + sort: appliedSorts, } }) } @@ -103,8 +97,8 @@ const ForeignRowSelector = ({ projectRef: project?.ref, connectionString: project?.connectionString, tableId: table?.id, - sorts: formatSortURLParams(table?.name || '', sorts), - filters: formatFilterURLParams(filters), + sorts, + filters, page, limit: rowsPerPage, roleImpersonationState: roleImpersonationState as RoleImpersonationState, @@ -160,13 +154,17 @@ const ForeignRowSelector = ({
- - +
diff --git a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.utils.tsx b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.utils.tsx index 5b9799e38ea..af57fb9decb 100644 --- a/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.utils.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/SidePanelEditor.utils.tsx @@ -764,6 +764,7 @@ export const updateTable = async ({ queryClient.invalidateQueries(databaseKeys.foreignKeyConstraints(projectRef, table.schema)), queryClient.invalidateQueries(databaseKeys.tableDefinition(projectRef, table.id)), queryClient.invalidateQueries(entityTypeKeys.list(projectRef)), + queryClient.invalidateQueries(tableKeys.list(projectRef, table.schema, true)), ]) // We need to invalidate tableRowsAndCount after tableEditor diff --git a/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx b/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx index df3d071c0cb..2ad8c0c54ca 100644 --- a/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx +++ b/apps/studio/components/interfaces/TableGridEditor/TableGridEditor.tsx @@ -13,16 +13,19 @@ import { isTableLike, isView, } from 'data/table-editor/table-editor-types' +import { useGetTables } from 'data/tables/tables-query' import { useCheckPermissions } from 'hooks/misc/useCheckPermissions' +import { useQuerySchemaState } from 'hooks/misc/useSchemaQueryState' import { useSelectedProject } from 'hooks/misc/useSelectedProject' import { useUrlState } from 'hooks/ui/useUrlState' import { PROTECTED_SCHEMAS } from 'lib/constants/schemas' import { useAppStateSnapshot } from 'state/app-state' import { TableEditorTableStateContextProvider } from 'state/table-editor-table' -import { handleTabClose, makeActiveTabPermanent, useTabsStore } from 'state/tabs' +import { createTabId, handleTabClose, makeActiveTabPermanent, useTabsStore } from 'state/tabs' import { Button } from 'ui' import { Admonition, GenericSkeletonLoader } from 'ui-patterns' import { useIsTableEditorTabsEnabled } from '../App/FeaturePreview/FeaturePreviewContext' +import DeleteConfirmationDialogs from './DeleteConfirmationDialogs' import SidePanelEditor from './SidePanelEditor/SidePanelEditor' import TableDefinition from './TableDefinition' @@ -40,8 +43,10 @@ export const TableGridEditor = ({ const project = useSelectedProject() const appSnap = useAppStateSnapshot() const { ref: projectRef, id } = useParams() + const tabs = useTabsStore(projectRef) const isTableEditorTabsEnabled = useIsTableEditorTabsEnabled() + const { selectedSchema } = useQuerySchemaState() useLoadTableEditorStateFromLocalStorageIntoUrl({ projectRef, @@ -55,6 +60,17 @@ export const TableGridEditor = ({ const isReadOnly = !canEditTables && !canEditColumns const tabId = !!id ? tabs.openTabs.find((x) => x.endsWith(id)) : undefined + const getTables = useGetTables({ + projectRef: project?.ref, + connectionString: project?.connectionString, + }) + + const onClearDashboardHistory = () => { + if (projectRef && editor) { + appSnap.setDashboardHistory(projectRef, editor === 'table' ? 'editor' : editor, undefined) + } + } + const onTableCreated = useCallback( (table: { id: number }) => { router.push(`/project/${projectRef}/editor/${table.id}`) @@ -62,11 +78,21 @@ export const TableGridEditor = ({ [projectRef, router] ) - const onClearDashboardHistory = () => { - if (projectRef && editor) { - appSnap.setDashboardHistory(projectRef, editor === 'table' ? 'editor' : editor, undefined) + const onTableDeleted = useCallback(async () => { + // For simplicity for now, we just open the first table within the same schema + if (isTableEditorTabsEnabled && selectedTable) { + // Close tab + const tabId = createTabId(selectedTable.entity_type, { id: selectedTable.id }) + handleTabClose({ ref: projectRef, id: tabId, router, editor, onClearDashboardHistory }) + } else { + const tables = await getTables(selectedSchema) + if (tables.length > 0) { + router.push(`/project/${projectRef}/editor/${tables[0].id}`) + } else { + router.push(`/project/${projectRef}/editor`) + } } - } + }, [getTables, isTableEditorTabsEnabled, projectRef, router, selectedSchema]) // NOTE: DO NOT PUT HOOKS AFTER THIS LINE if (isLoadingSelectedTable || !projectRef) { @@ -161,6 +187,10 @@ export const TableGridEditor = ({ selectedTable={isTableLike(selectedTable) ? selectedTable : undefined} onTableCreated={onTableCreated} /> +
) diff --git a/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx b/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx index df73b7f6ade..3c4ea8c51bb 100644 --- a/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx +++ b/apps/studio/components/layouts/TableEditorLayout/TableEditorMenu.tsx @@ -46,11 +46,11 @@ import { TableMenuEmptyState } from './TableMenuEmptyState' const TableEditorMenu = () => { const { ref, id: _id } = useParams() - const isMobile = useBreakpoint() const id = _id ? Number(_id) : undefined const snap = useTableEditorStateSnapshot() - const isTableEditorTabsEnabled = useIsTableEditorTabsEnabled() const { selectedSchema, setSelectedSchema } = useQuerySchemaState() + const isTableEditorTabsEnabled = useIsTableEditorTabsEnabled() + const isMobile = useBreakpoint() const [showModal, setShowModal] = useState(false) const [searchText, setSearchText] = useState('') diff --git a/apps/studio/data/sql/execute-sql-mutation.ts b/apps/studio/data/sql/execute-sql-mutation.ts index 01df31c8bc1..b5ddceaf427 100644 --- a/apps/studio/data/sql/execute-sql-mutation.ts +++ b/apps/studio/data/sql/execute-sql-mutation.ts @@ -1,16 +1,13 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' import { toast } from 'sonner' -import { databaseExtensionsKeys } from 'data/database-extensions/keys' -import { databasePoliciesKeys } from 'data/database-policies/keys' -import { databaseRoleKeys } from 'data/database-roles/keys' -import { databaseTriggerKeys } from 'data/database-triggers/keys' -import { databaseKeys } from 'data/database/keys' -import { entityTypeKeys } from 'data/entity-types/keys' -import { enumeratedTypesKeys } from 'data/enumerated-types/keys' -import { tableKeys } from 'data/tables/keys' import { executeSql, ExecuteSqlData, ExecuteSqlVariables } from './execute-sql-query' +// [Joshen] Intention is that we invalidate all database related keys whenever running a mutation related query +// So we attempt to ignore all the non-related query keys. We could probably look into grouping our query keys better +// actually to not make this too hacky here +const INVALIDATION_KEYS_IGNORE = ['branches', 'settings-v2', 'addons', 'custom-domains', 'content'] + export type QueryResponseError = { code: string message: string @@ -41,9 +38,16 @@ export const useExecuteSqlMutation = ({ const { contextualInvalidation, sql, projectRef } = variables // [Joshen] Default to false for now, only used for SQL editor to dynamically invalidate - if (contextualInvalidation && projectRef) { - const invalidationKeys = inferInvalidationKeys(projectRef, sql) - await Promise.all(invalidationKeys.map((key) => queryClient.invalidateQueries(key))) + const sqlLower = sql.toLowerCase() + const isMutationSQL = + sqlLower.includes('create ') || sqlLower.includes('alter ') || sqlLower.includes('drop ') + if (contextualInvalidation && projectRef && isMutationSQL) { + const databaseRelatedKeys = queryClient + .getQueryCache() + .findAll(['projects', projectRef]) + .map((x) => x.queryKey) + .filter((x) => !INVALIDATION_KEYS_IGNORE.some((a) => x.includes(a))) + await Promise.all(databaseRelatedKeys.map((key) => queryClient.invalidateQueries(key))) } await onSuccess?.(data, variables, context) }, @@ -58,68 +62,3 @@ export const useExecuteSqlMutation = ({ } ) } - -// [Joshen] Can expand this further eventually, but just covering certain easy cases for now -const inferInvalidationKeys = (ref: string, sql: string) => { - const keys = [] - const sqlLower = sql.toLowerCase() - - if ( - sqlLower.includes('create table') || - sqlLower.includes('alter table') || - sqlLower.includes('drop table') - ) { - keys.push(entityTypeKeys.list(ref)) - keys.push(tableKeys.list(ref)) - } - if ( - sqlLower.includes('create schema') || - sqlLower.includes('alter schema') || - sqlLower.includes('drop schema') - ) { - keys.push(databaseKeys.schemas(ref)) - } - if ( - sqlLower.includes('create function') || - sqlLower.includes('alter function') || - sqlLower.includes('drop function') - ) { - keys.push(databaseKeys.databaseFunctions(ref)) - } - if ( - sqlLower.includes('create trigger') || - sqlLower.includes('alter trigger') || - sqlLower.includes('drop trigger') - ) { - keys.push(databaseTriggerKeys.list(ref)) - } - if ( - sqlLower.includes('create policy') || - sqlLower.includes('alter policy') || - sqlLower.includes('drop policy') - ) { - keys.push(databasePoliciesKeys.list(ref)) - } - if ( - sqlLower.includes('create type') || - sqlLower.includes('alter type') || - sqlLower.includes('drop type') - ) { - keys.push(enumeratedTypesKeys.list(ref)) - } - if ( - sqlLower.includes('create role') || - sqlLower.includes('alter role') || - sqlLower.includes('drop role') - ) { - keys.push(databaseRoleKeys.databaseRoles(ref)) - } - if (sqlLower.includes('create index') || sqlLower.includes('drop index')) { - keys.push(databaseKeys.indexes(ref)) - } - if (sqlLower.includes('create extension') || sqlLower.includes('drop extension')) { - keys.push(databaseExtensionsKeys.list(ref)) - } - - return keys -} diff --git a/apps/studio/data/table-editor/keys.ts b/apps/studio/data/table-editor/keys.ts index 6ce556c9754..851eefd9838 100644 --- a/apps/studio/data/table-editor/keys.ts +++ b/apps/studio/data/table-editor/keys.ts @@ -1,4 +1,4 @@ export const tableEditorKeys = { - tableEditor: (projectRef: string | undefined, id: number | undefined) => - ['projects', projectRef, 'table-editor', id] as const, + tableEditor: (projectRef: string | undefined, id?: number) => + ['projects', projectRef, 'table-editor', id].filter(Boolean), } diff --git a/apps/studio/data/table-editor/table-editor-types.ts b/apps/studio/data/table-editor/table-editor-types.ts index 1014acac8cd..b793f43111d 100644 --- a/apps/studio/data/table-editor/table-editor-types.ts +++ b/apps/studio/data/table-editor/table-editor-types.ts @@ -45,6 +45,8 @@ export interface ForeignTable { export type Entity = Table | PartitionedTable | View | MaterializedView | ForeignTable +export type TableLike = Table | PartitionedTable + export function isTable(entity?: Entity): entity is Table { return entity?.entity_type === ENTITY_TYPE.TABLE } @@ -57,7 +59,7 @@ export function isPartitionedTable(entity?: Entity): entity is PartitionedTable * Returns true if the entity is a Table or a PartitionedTable. * Foreign tables are not considered table-like. */ -export function isTableLike(entity?: Entity): entity is Table | PartitionedTable { +export function isTableLike(entity?: Entity): entity is TableLike { return isTable(entity) || isPartitionedTable(entity) } @@ -79,3 +81,36 @@ export function isMaterializedView(entity?: Entity): entity is MaterializedView export function isViewLike(entity?: Entity): entity is View | MaterializedView { return isView(entity) || isMaterializedView(entity) } + +export function postgresTableToEntity(table: PostgresTable): Entity | undefined { + if (table.columns === undefined || table.relationships === undefined) { + console.error( + 'Unable to convert PostgresTable to Entity type: columns and relationships must not be undefined.' + ) + return undefined + } + + const tableRelationships: TableRelationship[] = table.relationships.map((rel) => ({ + deletion_action: 'a', + update_action: 'a', + ...rel, + })) + + return { + id: table.id, + schema: table.schema, + name: table.name, + comment: table.comment, + rls_enabled: table.rls_enabled, + rls_forced: table.rls_forced, + replica_identity: table.replica_identity, + bytes: table.bytes, + size: table.size, + live_rows_estimate: table.live_rows_estimate, + dead_rows_estimate: table.dead_rows_estimate, + columns: table.columns, + relationships: tableRelationships, + primary_keys: table.primary_keys, + entity_type: ENTITY_TYPE.TABLE, + } +} diff --git a/apps/studio/data/table-rows/table-row-delete-all-mutation.ts b/apps/studio/data/table-rows/table-row-delete-all-mutation.ts index 0fc55394fb1..bfd96398af4 100644 --- a/apps/studio/data/table-rows/table-row-delete-all-mutation.ts +++ b/apps/studio/data/table-rows/table-row-delete-all-mutation.ts @@ -2,8 +2,9 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react import { toast } from 'sonner' import { Query } from '@supabase/pg-meta/src/query' -import type { Filter, SupaTable } from 'components/grid/types' +import type { Filter } from 'components/grid/types' import { executeSql } from 'data/sql/execute-sql-query' +import { Entity } from 'data/table-editor/table-editor-types' import { RoleImpersonationState, wrapWithRoleImpersonation } from 'lib/role-impersonation' import { isRoleImpersonationEnabled } from 'state/role-impersonation-state' import type { ResponseError } from 'types' @@ -13,7 +14,7 @@ import { formatFilterValue } from './utils' export type TableRowDeleteAllVariables = { projectRef: string connectionString?: string - table: SupaTable + table: Entity filters: Filter[] roleImpersonationState?: RoleImpersonationState } diff --git a/apps/studio/data/table-rows/table-rows-query.ts b/apps/studio/data/table-rows/table-rows-query.ts index 56825ec17d7..1241ab9e4cf 100644 --- a/apps/studio/data/table-rows/table-rows-query.ts +++ b/apps/studio/data/table-rows/table-rows-query.ts @@ -1,3 +1,5 @@ +import { Query } from '@supabase/pg-meta/src/query' +import { getTableRowsSql } from '@supabase/pg-meta/src/query/table-row-query' import { useQuery, useQueryClient, @@ -5,8 +7,6 @@ import { type UseQueryOptions, } from '@tanstack/react-query' -import { Query } from '@supabase/pg-meta/src/query' -import { getTableRowsSql } from '@supabase/pg-meta/src/query/table-row-query' import { IS_PLATFORM } from 'common' import { parseSupaTable } from 'components/grid/SupabaseGrid.utils' import { Filter, Sort, SupaRow, SupaTable } from 'components/grid/types' @@ -142,9 +142,9 @@ export const fetchAllTableRows = async ({ ) try { - const { result } = await executeWithRetry(async () => + const { result } = await executeWithRetry(async () => { executeSql({ projectRef, connectionString, sql: query }) - ) + }) rows.push(...result) progressCallback?.(rows.length) @@ -153,7 +153,7 @@ export const fetchAllTableRows = async ({ await sleep(THROTTLE_DELAY) } catch (error) { throw new Error( - `Error fetching table rows: ${error instanceof Error ? error.message : 'Unknown error'}` + `Error fetching all table rows: ${error instanceof Error ? error.message : 'Unknown error'}` ) } } @@ -202,23 +202,28 @@ export async function getTableRows( getTableRowsSql({ table: entity, filters, sorts, limit, page }), roleImpersonationState ) - const { result } = await executeSql( - { - projectRef, - connectionString, - sql, - queryKey: ['table-rows', table?.id], - isRoleImpersonationEnabled: isRoleImpersonationEnabled(roleImpersonationState?.role), - }, - signal - ) - const rows = result.map((x: any, index: number) => { - return { idx: index, ...x } - }) as SupaRow[] + try { + const { result } = await executeSql( + { + projectRef, + connectionString, + sql, + queryKey: ['table-rows', table?.id], + isRoleImpersonationEnabled: isRoleImpersonationEnabled(roleImpersonationState?.role), + }, + signal + ) - return { - rows, + const rows = result.map((x: any, index: number) => { + return { idx: index, ...x } + }) as SupaRow[] + + return { rows } + } catch (error) { + throw new Error( + `Error fetching table rows: ${error instanceof Error ? error.message : 'Unknown error'}` + ) } } diff --git a/apps/studio/data/table-rows/utils.ts b/apps/studio/data/table-rows/utils.ts index 1cd131be63a..bb6e78a811f 100644 --- a/apps/studio/data/table-rows/utils.ts +++ b/apps/studio/data/table-rows/utils.ts @@ -1,4 +1,4 @@ -import type { Filter, ServiceError, SupaTable } from 'components/grid/types' +import type { Filter, ServiceError } from 'components/grid/types' import { isNumericalColumn } from 'components/grid/utils/types' import { Entity, isTableLike } from 'data/table-editor/table-editor-types' @@ -6,7 +6,15 @@ import { Entity, isTableLike } from 'data/table-editor/table-editor-types' * temporary fix until we implement a better filter UI * which validate input value base on the column type */ -export function formatFilterValue(table: SupaTable, filter: Filter) { +export function formatFilterValue( + table: { + columns: { + name: string + format: string + }[] + }, + filter: Filter +) { const column = table.columns.find((x) => x.name == filter.column) if (column && isNumericalColumn(column.format)) { const numberValue = Number(filter.value) diff --git a/apps/studio/pages/project/[ref]/database/tables/[id].tsx b/apps/studio/pages/project/[ref]/database/tables/[id].tsx index c4f3da0e276..9c834807105 100644 --- a/apps/studio/pages/project/[ref]/database/tables/[id].tsx +++ b/apps/studio/pages/project/[ref]/database/tables/[id].tsx @@ -11,6 +11,7 @@ import { useTableEditorQuery } from 'data/table-editor/table-editor-query' import { isTableLike } from 'data/table-editor/table-editor-types' import { ChevronRight } from 'lucide-react' import { useTableEditorStateSnapshot } from 'state/table-editor' +import { TableEditorTableStateContextProvider } from 'state/table-editor-table' import type { NextPageWithLayout } from 'types' import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' @@ -50,11 +51,16 @@ const DatabaseTables: NextPageWithLayout = () => { - - + {project?.ref !== undefined && selectedTable !== undefined && isTableLike(selectedTable) && ( + + + + + )} ) } diff --git a/apps/studio/pages/project/[ref]/database/tables/index.tsx b/apps/studio/pages/project/[ref]/database/tables/index.tsx index 512e29931cf..c56310f5d36 100644 --- a/apps/studio/pages/project/[ref]/database/tables/index.tsx +++ b/apps/studio/pages/project/[ref]/database/tables/index.tsx @@ -1,6 +1,7 @@ -import type { PostgresTable } from '@supabase/postgres-meta' import { useState } from 'react' +import { PostgresTable } from '@supabase/postgres-meta' +import { useParams } from 'common' import { TableList } from 'components/interfaces/Database' import { SidePanelEditor } from 'components/interfaces/TableGridEditor' import DeleteConfirmationDialogs from 'components/interfaces/TableGridEditor/DeleteConfirmationDialogs' @@ -8,12 +9,15 @@ import DatabaseLayout from 'components/layouts/DatabaseLayout/DatabaseLayout' import DefaultLayout from 'components/layouts/DefaultLayout' import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold' import { FormHeader } from 'components/ui/Forms/FormHeader' +import { Entity, isTableLike, postgresTableToEntity } from 'data/table-editor/table-editor-types' import { useTableEditorStateSnapshot } from 'state/table-editor' +import { TableEditorTableStateContextProvider } from 'state/table-editor-table' import type { NextPageWithLayout } from 'types' const DatabaseTables: NextPageWithLayout = () => { + const { ref: projectRef } = useParams() const snap = useTableEditorStateSnapshot() - const [selectedTableToEdit, setSelectedTableToEdit] = useState() + const [selectedTableToEdit, setSelectedTableToEdit] = useState() return ( <> @@ -24,15 +28,15 @@ const DatabaseTables: NextPageWithLayout = () => { { - setSelectedTableToEdit(table) + setSelectedTableToEdit(postgresTableToEntity(table)) snap.onEditTable() }} onDeleteTable={(table) => { - setSelectedTableToEdit(table) + setSelectedTableToEdit(postgresTableToEntity(table)) snap.onDeleteTable() }} onDuplicateTable={(table) => { - setSelectedTableToEdit(table) + setSelectedTableToEdit(postgresTableToEntity(table)) snap.onDuplicateTable() }} /> @@ -40,8 +44,19 @@ const DatabaseTables: NextPageWithLayout = () => { - - + {projectRef !== undefined && + selectedTableToEdit !== undefined && + isTableLike(selectedTableToEdit) && ( + + + + )} + + ) } diff --git a/apps/studio/pages/project/[ref]/editor/[id].tsx b/apps/studio/pages/project/[ref]/editor/[id].tsx index c1f16f54964..da2077944bf 100644 --- a/apps/studio/pages/project/[ref]/editor/[id].tsx +++ b/apps/studio/pages/project/[ref]/editor/[id].tsx @@ -1,9 +1,7 @@ -import { useRouter } from 'next/router' -import { useCallback, useEffect } from 'react' +import { useEffect } from 'react' import { useParams } from 'common' import { useIsTableEditorTabsEnabled } from 'components/interfaces/App/FeaturePreview/FeaturePreviewContext' -import DeleteConfirmationDialogs from 'components/interfaces/TableGridEditor/DeleteConfirmationDialogs' import { TableGridEditor } from 'components/interfaces/TableGridEditor/TableGridEditor' import DefaultLayout from 'components/layouts/DefaultLayout' import { EditorBaseLayout } from 'components/layouts/editors/EditorBaseLayout' @@ -11,7 +9,6 @@ import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectConte import TableEditorLayout from 'components/layouts/TableEditorLayout/TableEditorLayout' import TableEditorMenu from 'components/layouts/TableEditorLayout/TableEditorMenu' import { useTableEditorQuery } from 'data/table-editor/table-editor-query' -import { TablesData } from 'data/tables/tables-query' import { addTab, createTabId, getTabsStore } from 'state/tabs' import type { NextPageWithLayout } from 'types' @@ -27,19 +24,6 @@ const TableEditorPage: NextPageWithLayout = () => { id, }) - const router = useRouter() - const onAfterDeleteTable = useCallback( - (tables: TablesData) => { - // For simplicity for now, we just open the first table within the same schema - if (tables.length > 0) { - router.push(`/project/${projectRef}/editor/${tables[0].id}`) - } else { - router.push(`/project/${projectRef}/editor`) - } - }, - [router, projectRef] - ) - /** * Effect: Creates or updates tab when table is loaded * Runs when: @@ -70,15 +54,7 @@ const TableEditorPage: NextPageWithLayout = () => { } }, [selectedTable, id, projectRef, isTableEditorTabsEnabled]) - return ( - <> - - - - ) + return } TableEditorPage.getLayout = (page) => ( diff --git a/apps/studio/pages/project/[ref]/editor/index.tsx b/apps/studio/pages/project/[ref]/editor/index.tsx index 2f3bebfa86f..e1a129c3808 100644 --- a/apps/studio/pages/project/[ref]/editor/index.tsx +++ b/apps/studio/pages/project/[ref]/editor/index.tsx @@ -25,8 +25,8 @@ const TableEditorPage: NextPageWithLayout = () => { } useEffect(() => { + const lastOpenedTab = appSnap.dashboardHistory.editor if (isTableEditorTabsEnabled) { - const lastOpenedTab = appSnap.dashboardHistory.editor const lastTabId = tabStore.openTabs.find((id) => editorEntityTypes.table.includes(tabStore.tabsMap[id]?.type) ) @@ -37,6 +37,8 @@ const TableEditorPage: NextPageWithLayout = () => { const lastTab = tabStore.tabsMap[lastTabId] if (lastTab) router.push(`/project/${projectRef}/editor/${lastTab.metadata?.tableId}`) } + } else if (lastOpenedTab) { + router.push(`/project/${projectRef}/editor/${appSnap.dashboardHistory.editor}`) } }, [isTableEditorTabsEnabled]) diff --git a/apps/studio/state/table-editor-table.tsx b/apps/studio/state/table-editor-table.tsx index f35aaccdaf3..60c133eecea 100644 --- a/apps/studio/state/table-editor-table.tsx +++ b/apps/studio/state/table-editor-table.tsx @@ -17,14 +17,15 @@ import { useTableEditorStateSnapshot } from './table-editor' export const createTableEditorTableState = ({ projectRef, table: originalTable, - editable, + editable = true, onAddColumn, onExpandJSONEditor, onExpandTextEditor, }: { projectRef: string table: Entity - editable: boolean + /** If set to true, render an additional "+" column to support adding a new column in the grid editor */ + editable?: boolean onAddColumn: () => void onExpandJSONEditor: (column: string, row: SupaRow) => void onExpandTextEditor: (column: string, row: SupaRow) => void diff --git a/apps/studio/state/table-editor.tsx b/apps/studio/state/table-editor.tsx index 6230e27716d..be940f4135e 100644 --- a/apps/studio/state/table-editor.tsx +++ b/apps/studio/state/table-editor.tsx @@ -1,7 +1,6 @@ import type { PostgresColumn } from '@supabase/postgres-meta' import { PropsWithChildren, createContext, useContext, useRef } from 'react' import { proxy, useSnapshot } from 'valtio' -import { proxySet } from 'valtio/utils' import type { SupaRow } from 'components/grid/types' import { ForeignKey } from 'components/interfaces/TableGridEditor/SidePanelEditor/ForeignKeySelector/ForeignKeySelector.types' From 915af35334c0c27e5771a4bec52c855fa14243b7 Mon Sep 17 00:00:00 2001 From: Daniel Gabriel <56942108+revogabe@users.noreply.github.com> Date: Wed, 30 Apr 2025 03:52:23 -0300 Subject: [PATCH 17/32] fix(ui): enable portal for popover components (#35370) --- apps/studio/components/interfaces/Database/Tables/TableList.tsx | 2 +- apps/studio/components/ui/FilterPopover.tsx | 2 +- apps/studio/components/ui/SchemaSelector.tsx | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/studio/components/interfaces/Database/Tables/TableList.tsx b/apps/studio/components/interfaces/Database/Tables/TableList.tsx index 798720c3dcd..ab2bc61582b 100644 --- a/apps/studio/components/interfaces/Database/Tables/TableList.tsx +++ b/apps/studio/components/interfaces/Database/Tables/TableList.tsx @@ -221,7 +221,7 @@ const TableList = ({ icon={} /> - +

Show entity types

diff --git a/apps/studio/components/ui/FilterPopover.tsx b/apps/studio/components/ui/FilterPopover.tsx index d3eed31644a..186d4c3c652 100644 --- a/apps/studio/components/ui/FilterPopover.tsx +++ b/apps/studio/components/ui/FilterPopover.tsx @@ -83,7 +83,7 @@ export const FilterPopover = >({
- +
{title ?? `Select ${name.toLowerCase()}`} diff --git a/apps/studio/components/ui/SchemaSelector.tsx b/apps/studio/components/ui/SchemaSelector.tsx index df05ba0ceba..a8103d0d931 100644 --- a/apps/studio/components/ui/SchemaSelector.tsx +++ b/apps/studio/components/ui/SchemaSelector.tsx @@ -126,6 +126,7 @@ const SchemaSelector = ({ className="p-0 min-w-[200px]" side="bottom" align="start" + portal={true} sameWidthAsTrigger > From 14562e9c8abe793bd428eba3a1da196bf03f3e41 Mon Sep 17 00:00:00 2001 From: angelico Date: Wed, 30 Apr 2025 15:29:48 +0800 Subject: [PATCH 18/32] chore: init useIsOrioleDbInAws (#35325) * chore: check for cloud provider in useIsOrioleDb * chore: introduce useIsOrioleDbInAwsNew instead * chore: consolidate to using useIsOrioleDb * chore: selectively include useIsOrioleDbInAwsNew in conditionals * fix: prettier * fix: incorrect cloud provider * fix: update conditional instead * fix: prettier * Simplify logic * Smol fix --------- Co-authored-by: Joshen Lim --- .../PostgresVersionSelector.tsx | 2 +- .../interfaces/Settings/Addons/Addons.tsx | 5 ++-- .../Infrastructure/InfrastructureInfo.tsx | 25 +++++++++---------- apps/studio/data/database/backups-query.ts | 9 ++++--- apps/studio/hooks/misc/useSelectedProject.ts | 11 +++++++- .../project/[ref]/database/backups/pitr.tsx | 8 +++--- .../[ref]/database/backups/scheduled.tsx | 8 +++--- 7 files changed, 40 insertions(+), 28 deletions(-) diff --git a/apps/studio/components/interfaces/ProjectCreation/PostgresVersionSelector.tsx b/apps/studio/components/interfaces/ProjectCreation/PostgresVersionSelector.tsx index dd5bb1a399c..cfdcb09cfee 100644 --- a/apps/studio/components/interfaces/ProjectCreation/PostgresVersionSelector.tsx +++ b/apps/studio/components/interfaces/ProjectCreation/PostgresVersionSelector.tsx @@ -76,7 +76,7 @@ export const PostgresVersionSelector = ({ const defaultValue = gaVersion ? formatValue(gaVersion) : formatValue(availableVersions[0]) form.setValue('postgresVersionSelection', defaultValue) } - }, [isSuccess, form]) + }, [isSuccess, availableVersions, form]) return ( diff --git a/apps/studio/components/interfaces/Settings/Addons/Addons.tsx b/apps/studio/components/interfaces/Settings/Addons/Addons.tsx index 16cc9ddf870..9d426018192 100644 --- a/apps/studio/components/interfaces/Settings/Addons/Addons.tsx +++ b/apps/studio/components/interfaces/Settings/Addons/Addons.tsx @@ -32,7 +32,7 @@ import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-que import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query' import type { ProjectAddonVariantMeta } from 'data/subscriptions/types' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' -import { useIsOrioleDb, useProjectByRef } from 'hooks/misc/useSelectedProject' +import { useIsOrioleDb, useIsOrioleDbInAws, useProjectByRef } from 'hooks/misc/useSelectedProject' import { useFlag } from 'hooks/ui/useFlag' import { getCloudProviderArchitecture } from 'lib/cloudprovider-utils' import { BASE_PATH, INSTANCE_MICRO_SPECS, INSTANCE_NANO_SPECS } from 'lib/constants' @@ -54,6 +54,7 @@ const Addons = () => { const isBranch = parentProject !== undefined const isProjectActive = useIsProjectActive() const isOrioleDb = useIsOrioleDb() + const isOrioleDbInAws = useIsOrioleDbInAws() const { data: settings } = useProjectSettingsV2Query({ projectRef }) const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: selectedOrg?.slug }) @@ -486,7 +487,7 @@ const Addons = () => { - ) : isOrioleDb ? ( + ) : isOrioleDbInAws ? ( { current_app_version_release_channel && current_app_version_release_channel !== 'ga' ? current_app_version_release_channel : undefined - const isOrioleDb = (current_app_version ?? '').includes('orioledb') + const isOrioleDb = useIsOrioleDb() const latestPgVersion = (latest_app_version ?? '').split('supabase-postgres-')[1] const isInactive = project?.status === 'INACTIVE' @@ -147,18 +148,16 @@ const InfrastructureInfo = () => { ), isOrioleDb && ( - <> - - - - OrioleDB - - - - This project uses OrioleDB - - - + + + + OrioleDB + + + + This project uses OrioleDB + + ), isOnLatestVersion && ( diff --git a/apps/studio/data/database/backups-query.ts b/apps/studio/data/database/backups-query.ts index 626ffef968d..7cef5a9462a 100644 --- a/apps/studio/data/database/backups-query.ts +++ b/apps/studio/data/database/backups-query.ts @@ -2,7 +2,7 @@ import { useQuery, UseQueryOptions } from '@tanstack/react-query' import type { components } from 'data/api' import { get, handleError } from 'data/fetchers' -import { useIsOrioleDb } from 'hooks/misc/useSelectedProject' +import { useIsOrioleDbInAws } from 'hooks/misc/useSelectedProject' import type { ResponseError } from 'types' import { databaseKeys } from './keys' @@ -32,11 +32,14 @@ export const useBackupsQuery = ( { enabled = true, ...options }: UseQueryOptions = {} ) => { // [Joshen] Check for specifically false to account for project not loaded yet - const isOrioleDb = useIsOrioleDb() + const isOrioleDbInAws = useIsOrioleDbInAws() return useQuery( databaseKeys.backups(projectRef), ({ signal }) => getBackups({ projectRef }, signal), - { enabled: enabled && isOrioleDb === false && typeof projectRef !== 'undefined', ...options } + { + enabled: enabled && !isOrioleDbInAws && typeof projectRef !== 'undefined', + ...options, + } ) } diff --git a/apps/studio/hooks/misc/useSelectedProject.ts b/apps/studio/hooks/misc/useSelectedProject.ts index 7fb9b5d60e8..c973f22e083 100644 --- a/apps/studio/hooks/misc/useSelectedProject.ts +++ b/apps/studio/hooks/misc/useSelectedProject.ts @@ -1,7 +1,9 @@ +import { useMemo } from 'react' + import { useIsLoggedIn, useParams } from 'common' import { useProjectDetailQuery } from 'data/projects/project-detail-query' import { ProjectInfo, useProjectsQuery } from 'data/projects/projects-query' -import { useMemo } from 'react' +import { PROVIDERS } from 'lib/constants' export function useSelectedProject({ enabled = true } = {}) { const { ref } = useParams() @@ -37,3 +39,10 @@ export const useIsOrioleDb = () => { const isOrioleDb = project?.dbVersion?.endsWith('orioledb') return isOrioleDb } + +export const useIsOrioleDbInAws = () => { + const project = useSelectedProject() + const isOrioleDbInAws = + project?.dbVersion?.endsWith('orioledb') && project?.cloud_provider === PROVIDERS.AWS.id + return isOrioleDbInAws +} diff --git a/apps/studio/pages/project/[ref]/database/backups/pitr.tsx b/apps/studio/pages/project/[ref]/database/backups/pitr.tsx index 166c05189bb..275ae658474 100644 --- a/apps/studio/pages/project/[ref]/database/backups/pitr.tsx +++ b/apps/studio/pages/project/[ref]/database/backups/pitr.tsx @@ -5,6 +5,7 @@ import { useParams } from 'common' import DatabaseBackupsNav from 'components/interfaces/Database/Backups/DatabaseBackupsNav' import { PITRNotice, PITRSelection } from 'components/interfaces/Database/Backups/PITR' import DatabaseLayout from 'components/layouts/DatabaseLayout/DatabaseLayout' +import DefaultLayout from 'components/layouts/DefaultLayout' import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold' import AlertError from 'components/ui/AlertError' @@ -16,12 +17,11 @@ import UpgradeToPro from 'components/ui/UpgradeToPro' import { useBackupsQuery } from 'data/database/backups-query' import { useCheckPermissions, usePermissionsLoaded } from 'hooks/misc/useCheckPermissions' import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' -import { useIsOrioleDb } from 'hooks/misc/useSelectedProject' +import { useIsOrioleDbInAws } from 'hooks/misc/useSelectedProject' import { PROJECT_STATUS } from 'lib/constants' import type { NextPageWithLayout } from 'types' import { Alert_Shadcn_, AlertDescription_Shadcn_, AlertTitle_Shadcn_ } from 'ui' import { Admonition } from 'ui-patterns' -import DefaultLayout from 'components/layouts/DefaultLayout' const DatabasePhysicalBackups: NextPageWithLayout = () => { return ( @@ -51,7 +51,7 @@ const PITR = () => { const { ref: projectRef } = useParams() const { project } = useProjectContext() const organization = useSelectedOrganization() - const isOrioleDb = useIsOrioleDb() + const isOrioleDbInAws = useIsOrioleDbInAws() const { data: backups, error, isLoading, isError, isSuccess } = useBackupsQuery({ projectRef }) const plan = organization?.plan?.id @@ -65,7 +65,7 @@ const PITR = () => { return } - if (isOrioleDb) { + if (isOrioleDbInAws) { return ( { const { ref: projectRef } = useParams() const { data: backups, error, isLoading, isError, isSuccess } = useBackupsQuery({ projectRef }) - const isOrioleDb = useIsOrioleDb() + const isOrioleDbInAws = useIsOrioleDbInAws() const isPitrEnabled = backups?.pitr_enabled const isPermissionsLoaded = usePermissionsLoaded() @@ -39,7 +39,7 @@ const DatabaseScheduledBackups: NextPageWithLayout = () => { - {isOrioleDb ? ( + {isOrioleDbInAws ? ( Date: Wed, 30 Apr 2025 21:24:22 +1200 Subject: [PATCH 19/32] chore: add Eduardo to humans.txt (#35365) --- apps/docs/public/humans.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/docs/public/humans.txt b/apps/docs/public/humans.txt index ef470c4a57e..d69b658b8c8 100644 --- a/apps/docs/public/humans.txt +++ b/apps/docs/public/humans.txt @@ -32,6 +32,7 @@ David A. Ventimiglia Deji I Div Arora Divit D +Eduardo Gurgel Egor Romanov Eleftheria Trivyzaki Etienne Stalmans From 4a5e1a8bd4e6931e2b1590309d3863426ce84c52 Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 30 Apr 2025 17:47:47 +0800 Subject: [PATCH 20/32] Update query block bar hover behaviour (#35374) * Update query block bar hover behaviour * Smol fix --- .../components/ui/Charts/ComposedChart.tsx | 20 +++++------ .../ui/Charts/ComposedChart.utils.tsx | 7 ++-- .../components/ui/QueryBlock/QueryBlock.tsx | 35 ++++++++++++++----- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/apps/studio/components/ui/Charts/ComposedChart.tsx b/apps/studio/components/ui/Charts/ComposedChart.tsx index 7ebfa5db0f6..1e6c62bbf1f 100644 --- a/apps/studio/components/ui/Charts/ComposedChart.tsx +++ b/apps/studio/components/ui/Charts/ComposedChart.tsx @@ -1,21 +1,21 @@ 'use client' -import { useState, ComponentProps, useEffect } from 'react' import dayjs from 'dayjs' +import { useTheme } from 'next-themes' +import { ComponentProps, useEffect, useState } from 'react' import { Area, Bar, - ComposedChart as RechartComposedChart, - XAxis, - YAxis, CartesianGrid, Line, + ComposedChart as RechartComposedChart, ReferenceArea, Tooltip, + XAxis, + YAxis, } from 'recharts' import { CategoricalChartState } from 'recharts/types/chart/types' import { cn } from 'ui' -import { useTheme } from 'next-themes' import ChartHeader from './ChartHeader' import ChartHighlightActions from './ChartHighlightActions' import { @@ -24,17 +24,17 @@ import { STACKED_CHART_COLORS, updateStackedChartColors, } from './Charts.constants' -import { Datum, CommonChartProps } from './Charts.types' -import { useChartSize, numberFormatter } from './Charts.utils' -import { ChartHighlight } from './useChartHighlight' -import NoDataPlaceholder from './NoDataPlaceholder' -import { MultiAttribute } from './ComposedChartHandler' +import { CommonChartProps, Datum } from './Charts.types' +import { numberFormatter, useChartSize } from './Charts.utils' import { calculateTotalChartAggregate, CustomLabel, CustomTooltip, formatBytes, } from './ComposedChart.utils' +import { MultiAttribute } from './ComposedChartHandler' +import NoDataPlaceholder from './NoDataPlaceholder' +import { ChartHighlight } from './useChartHighlight' export interface BarChartProps extends CommonChartProps { attributes: MultiAttribute[] diff --git a/apps/studio/components/ui/Charts/ComposedChart.utils.tsx b/apps/studio/components/ui/Charts/ComposedChart.utils.tsx index 3a6e83663bc..bacb440bf9b 100644 --- a/apps/studio/components/ui/Charts/ComposedChart.utils.tsx +++ b/apps/studio/components/ui/Charts/ComposedChart.utils.tsx @@ -1,12 +1,11 @@ 'use client' -import { useState } from 'react' import dayjs from 'dayjs' +import { useState } from 'react' +import { cn, Tooltip, TooltipContent, TooltipTrigger } from 'ui' import { DateTimeFormats } from './Charts.constants' import { numberFormatter } from './Charts.utils' import { MultiAttribute } from './ComposedChartHandler' -import { Tooltip, TooltipContent, TooltipTrigger } from 'ui' -import { cn } from 'ui' interface CustomIconProps { color: string @@ -232,4 +231,4 @@ const CustomLabel = ({ payload, attributes, showMaxValue, onLabelHover }: Custom ) } -export { CustomTooltip, CustomLabel } +export { CustomLabel, CustomTooltip } diff --git a/apps/studio/components/ui/QueryBlock/QueryBlock.tsx b/apps/studio/components/ui/QueryBlock/QueryBlock.tsx index 9424b39c1cc..b2d5fa639c4 100644 --- a/apps/studio/components/ui/QueryBlock/QueryBlock.tsx +++ b/apps/studio/components/ui/QueryBlock/QueryBlock.tsx @@ -1,6 +1,6 @@ import { Code, Play } from 'lucide-react' import { DragEvent, ReactNode, useEffect, useMemo, useState } from 'react' -import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from 'recharts' +import { Bar, BarChart, CartesianGrid, Cell, Tooltip, XAxis, YAxis } from 'recharts' import { toast } from 'sonner' import { useParams } from 'common' @@ -12,9 +12,10 @@ import { QueryResponseError, useExecuteSqlMutation } from 'data/sql/execute-sql- import dayjs from 'dayjs' import { Parameter, parseParameters } from 'lib/sql-parameters' import { Dashboards } from 'types' -import { ChartContainer, ChartTooltip, ChartTooltipContent, cn, CodeBlock, SQL_ICON } from 'ui' +import { ChartContainer, ChartTooltipContent, cn, CodeBlock, SQL_ICON } from 'ui' import ShimmeringLoader from 'ui-patterns/ShimmeringLoader' import { ButtonTooltip } from '../ButtonTooltip' +import { CHART_COLORS } from '../Charts/Charts.constants' import SqlWarningAdmonition from '../SqlWarningAdmonition' import { BlockViewConfiguration } from './BlockViewConfiguration' import { EditQueryButton } from './EditQueryButton' @@ -130,6 +131,7 @@ export const QueryBlock = ({ const [readOnlyError, setReadOnlyError] = useState(false) const [queryError, setQueryError] = useState() const [queryResult, setQueryResult] = useState(results) + const [focusDataIndex, setFocusDataIndex] = useState() const formattedQueryResult = useMemo(() => { // Make sure Y axis values are numbers @@ -379,10 +381,9 @@ export const QueryBlock = ({

Select columns for the X and Y axes

) : ( -
+
{ + if (e.activeTooltipIndex !== focusDataIndex) { + setFocusDataIndex(e.activeTooltipIndex) + } + }} + onMouseLeave={() => setFocusDataIndex(undefined)} > - + - } /> - + } /> + + {chartData?.map((_: any, index: number) => ( + + ))} +
From 5e0629cc09cca0a86f3003ee138a0d1762fc14db Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 30 Apr 2025 18:06:20 +0800 Subject: [PATCH 21/32] Fix local version popover version (#35375) --- .../LayoutHeader/LocalVersionPopover.tsx | 15 +++++++++------ apps/studio/pages/api/cli-release-version.ts | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LocalVersionPopover.tsx b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LocalVersionPopover.tsx index 1fdd245e201..6adaa0abf2b 100644 --- a/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LocalVersionPopover.tsx +++ b/apps/studio/components/layouts/ProjectLayout/LayoutHeader/LocalVersionPopover.tsx @@ -28,15 +28,18 @@ import { getSemver, semverGte, semverLte } from './LocalVersionPopover.utils' export const LocalVersionPopover = () => { const { data, isSuccess } = useCLIReleaseVersionQuery() const currentCliVersion = data?.current - const hasLatestCLIVersion = isSuccess && !!data?.latest + const latestCliVersion = data?.latest + const hasLatestCLIVersion = isSuccess && !!latestCliVersion - const current = getSemver(data?.current) - const latest = getSemver(data?.latest) + const current = getSemver(currentCliVersion) + const latest = getSemver(latestCliVersion) const hasUpdate = - !!current && !!latest ? data?.current !== data?.latest && semverLte(current, latest) : false + !!current && !!latest + ? currentCliVersion !== latestCliVersion && semverLte(current, latest) + : false const isBeta = - !!current && !!latest && data?.current !== data?.latest && semverGte(current, latest) + !!current && !!latest && currentCliVersion !== latestCliVersion && semverGte(current, latest) const approximateNextRelease = !!data?.published_at ? dayjs(data?.published_at).utc().add(14, 'day').format('DD MMM YYYY') @@ -180,7 +183,7 @@ export const LocalVersionPopover = () => { {hasLatestCLIVersion && hasUpdate && !isBeta && (

Available version:

-

{data.latest}

+

{latestCliVersion}

)}
diff --git a/apps/studio/pages/api/cli-release-version.ts b/apps/studio/pages/api/cli-release-version.ts index 0c67461cb35..7c6ce96b38a 100644 --- a/apps/studio/pages/api/cli-release-version.ts +++ b/apps/studio/pages/api/cli-release-version.ts @@ -8,7 +8,7 @@ type GitHubRepositoryRelease = { published_at: string } -const current = process.env.CURRENT_CLI_VERSION +const current = `v${process.env.CURRENT_CLI_VERSION}` const handler = async (req: NextApiRequest, res: NextApiResponse) => { try { From a343e864a02b3feb765d41b2fbb2825e17650fca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Apr 2025 10:33:11 +0000 Subject: [PATCH 22/32] build(deps-dev): bump react-router from 7.4.0 to 7.5.2 (#35263) * build(deps-dev): bump react-router from 7.4.0 to 7.5.2 Bumps [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router) from 7.4.0 to 7.5.2. - [Release notes](https://github.com/remix-run/react-router/releases) - [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md) - [Commits](https://github.com/remix-run/react-router/commits/react-router@7.5.2/packages/react-router) --- updated-dependencies: - dependency-name: react-router dependency-version: 7.5.2 dependency-type: direct:development ... Signed-off-by: dependabot[bot] * Rerun the version bump. --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Ivan Vasilov --- apps/ui-library/package.json | 2 +- pnpm-lock.yaml | 35 +++++++++++++++++------------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/apps/ui-library/package.json b/apps/ui-library/package.json index 8157c99e4e7..8523c160c0f 100644 --- a/apps/ui-library/package.json +++ b/apps/ui-library/package.json @@ -70,7 +70,7 @@ "mdast-util-toc": "^6.1.1", "postcss": "^8", "react-dropzone": "^14.3.8", - "react-router": "^7.4.0", + "react-router": "^7.5.2", "rimraf": "^4.1.3", "shadcn": "2.4.0-canary.16", "shiki": "^1.1.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5b8299a5f0..9b294548e12 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -747,7 +747,7 @@ importers: version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nuqs: specifier: ^2.4.1 - version: 2.4.1(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.72.0))(react-router@7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 2.4.1(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.72.0))(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) openai: specifier: ^4.20.1 version: 4.71.1(encoding@0.1.13)(zod@3.23.8) @@ -1057,7 +1057,7 @@ importers: version: 1.1.8(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@react-router/fs-routes': specifier: ^7.4.0 - version: 7.4.0(@react-router/dev@7.4.0(@types/node@22.13.14)(jiti@2.4.2)(react-router@7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(typescript@5.5.2)(vite@6.2.6(@types/node@22.13.14)(jiti@2.4.2)(sass@1.72.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))(yaml@2.4.5))(typescript@5.5.2) + version: 7.4.0(@react-router/dev@7.4.0(@types/node@22.13.14)(jiti@2.4.2)(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(typescript@5.5.2)(vite@6.2.6(@types/node@22.13.14)(jiti@2.4.2)(sass@1.72.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))(yaml@2.4.5))(typescript@5.5.2) '@supabase/postgrest-js': specifier: '*' version: 1.19.2 @@ -1148,7 +1148,7 @@ importers: devDependencies: '@react-router/dev': specifier: ^7.1.5 - version: 7.4.0(@types/node@22.13.14)(jiti@2.4.2)(react-router@7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(typescript@5.5.2)(vite@6.2.6(@types/node@22.13.14)(jiti@2.4.2)(sass@1.72.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))(yaml@2.4.5) + version: 7.4.0(@types/node@22.13.14)(jiti@2.4.2)(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(typescript@5.5.2)(vite@6.2.6(@types/node@22.13.14)(jiti@2.4.2)(sass@1.72.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))(yaml@2.4.5) '@shikijs/compat': specifier: ^1.1.7 version: 1.6.0 @@ -1192,8 +1192,8 @@ importers: specifier: ^14.3.8 version: 14.3.8(react@18.3.1) react-router: - specifier: ^7.4.0 - version: 7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^7.5.2 + version: 7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) rimraf: specifier: ^4.1.3 version: 4.4.1 @@ -12518,8 +12518,8 @@ packages: peerDependencies: react: '>= 16.3' - react-router@7.4.0: - resolution: {integrity: sha512-Y2g5ObjkvX3VFeVt+0CIPuYd9PpgqCslG7ASSIdN73LwA1nNWzcMLaoMRJfP3prZFI92svxFwbn7XkLJ+UPQ6A==} + react-router@7.5.3: + resolution: {integrity: sha512-3iUDM4/fZCQ89SXlDa+Ph3MevBrozBAI655OAfWQlTm9nBR0IKlrmNwFow5lPHttbwvITZfkeeeZFP6zt3F7pw==} engines: {node: '>=20.0.0'} peerDependencies: react: '>=18' @@ -18651,7 +18651,7 @@ snapshots: dependencies: react: 18.3.1 - '@react-router/dev@7.4.0(@types/node@22.13.14)(jiti@2.4.2)(react-router@7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(typescript@5.5.2)(vite@6.2.6(@types/node@22.13.14)(jiti@2.4.2)(sass@1.72.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))(yaml@2.4.5)': + '@react-router/dev@7.4.0(@types/node@22.13.14)(jiti@2.4.2)(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(typescript@5.5.2)(vite@6.2.6(@types/node@22.13.14)(jiti@2.4.2)(sass@1.72.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))(yaml@2.4.5)': dependencies: '@babel/core': 7.26.10(supports-color@8.1.1) '@babel/generator': 7.27.0 @@ -18662,7 +18662,7 @@ snapshots: '@babel/traverse': 7.27.0(supports-color@8.1.1) '@babel/types': 7.27.0 '@npmcli/package-json': 4.0.1 - '@react-router/node': 7.4.0(react-router@7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.5.2) + '@react-router/node': 7.4.0(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.5.2) arg: 5.0.2 babel-dead-code-elimination: 1.0.9(supports-color@8.1.1) chokidar: 4.0.3 @@ -18676,7 +18676,7 @@ snapshots: picocolors: 1.1.1 prettier: 2.8.8 react-refresh: 0.14.2 - react-router: 7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router: 7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) semver: 7.7.1 set-cookie-parser: 2.7.1 valibot: 0.41.0(typescript@5.5.2) @@ -18700,17 +18700,17 @@ snapshots: - tsx - yaml - '@react-router/fs-routes@7.4.0(@react-router/dev@7.4.0(@types/node@22.13.14)(jiti@2.4.2)(react-router@7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(typescript@5.5.2)(vite@6.2.6(@types/node@22.13.14)(jiti@2.4.2)(sass@1.72.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))(yaml@2.4.5))(typescript@5.5.2)': + '@react-router/fs-routes@7.4.0(@react-router/dev@7.4.0(@types/node@22.13.14)(jiti@2.4.2)(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(typescript@5.5.2)(vite@6.2.6(@types/node@22.13.14)(jiti@2.4.2)(sass@1.72.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))(yaml@2.4.5))(typescript@5.5.2)': dependencies: - '@react-router/dev': 7.4.0(@types/node@22.13.14)(jiti@2.4.2)(react-router@7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(typescript@5.5.2)(vite@6.2.6(@types/node@22.13.14)(jiti@2.4.2)(sass@1.72.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))(yaml@2.4.5) + '@react-router/dev': 7.4.0(@types/node@22.13.14)(jiti@2.4.2)(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(sass@1.72.0)(supports-color@8.1.1)(terser@5.39.0)(tsx@4.19.3)(typescript@5.5.2)(vite@6.2.6(@types/node@22.13.14)(jiti@2.4.2)(sass@1.72.0)(terser@5.39.0)(tsx@4.19.3)(yaml@2.4.5))(yaml@2.4.5) minimatch: 9.0.5 optionalDependencies: typescript: 5.5.2 - '@react-router/node@7.4.0(react-router@7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.5.2)': + '@react-router/node@7.4.0(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(typescript@5.5.2)': dependencies: '@mjackson/node-fetch-server': 0.2.0 - react-router: 7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router: 7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) source-map-support: 0.5.21 stream-slice: 0.1.2 undici: 6.21.2 @@ -26945,13 +26945,13 @@ snapshots: mitt: 3.0.1 next: 14.2.26(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.72.0) - nuqs@2.4.1(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.72.0))(react-router@7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): + nuqs@2.4.1(next@15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.72.0))(react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): dependencies: mitt: 3.0.1 react: 18.3.1 optionalDependencies: next: 15.3.1(@babel/core@7.26.10(supports-color@8.1.1))(@opentelemetry/api@1.9.0)(@playwright/test@1.49.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.72.0) - react-router: 7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-router: 7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nwsapi@2.2.20: optional: true @@ -28074,9 +28074,8 @@ snapshots: transitivePeerDependencies: - react-dom - react-router@7.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-router@7.5.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@types/cookie': 0.6.0 cookie: 1.0.2 react: 18.3.1 set-cookie-parser: 2.7.1 From 4466b9e262c887b9d6a35684996af3ca4fccae63 Mon Sep 17 00:00:00 2001 From: Charis <26616127+charislam@users.noreply.github.com> Date: Wed, 30 Apr 2025 13:14:49 -0400 Subject: [PATCH 23/32] revert(ui-patterns): admonition link underline (#35387) Admonition link underline was added when admonitions were not "prose", but admonitions are now "prose" again so it is redundant --- packages/ui-patterns/admonition.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ui-patterns/admonition.tsx b/packages/ui-patterns/admonition.tsx index 5c546acbedd..6c046091cc0 100644 --- a/packages/ui-patterns/admonition.tsx +++ b/packages/ui-patterns/admonition.tsx @@ -103,7 +103,6 @@ export const Admonition = forwardRef< {...props} className={cn( 'mb-2', - '[&_a]:underline', admonitionSVG({ type: typeMapped }), admonitionBase({ type: typeMapped }), props.className From c35fe03ab6972a2658de619169e2c54392b8f20e Mon Sep 17 00:00:00 2001 From: Prashant Sridharan <914007+CoolAssPuppy@users.noreply.github.com> Date: Wed, 30 Apr 2025 18:45:56 +0100 Subject: [PATCH 24/32] Added Euka case study (#35385) --- apps/www/_customers/bree.mdx | 14 +-- apps/www/_customers/euka.mdx | 112 ++++++++++++++++++ apps/www/data/CustomerStories.ts | 11 ++ apps/www/public/customers-rss.xml | 9 +- .../images/blog/avatars/kevin-wang-euka.jpeg | Bin 0 -> 52131 bytes .../public/images/customers/logos/euka.png | Bin 0 -> 3373 bytes .../images/customers/logos/light/euka.png | Bin 0 -> 2422 bytes 7 files changed, 138 insertions(+), 8 deletions(-) create mode 100644 apps/www/_customers/euka.mdx create mode 100644 apps/www/public/images/blog/avatars/kevin-wang-euka.jpeg create mode 100644 apps/www/public/images/customers/logos/euka.png create mode 100644 apps/www/public/images/customers/logos/light/euka.png diff --git a/apps/www/_customers/bree.mdx b/apps/www/_customers/bree.mdx index 411ea8e9e03..8e70581f263 100644 --- a/apps/www/_customers/bree.mdx +++ b/apps/www/_customers/bree.mdx @@ -30,7 +30,7 @@ region: 'North America' supabase_products: ['database', 'auth', 'functions', 'vector'] --- - + We hit scalability limits with Fauna, and Supabase was a step up across the board. The developer experience, ecosystem, support for AI. It’s the complete package. @@ -41,7 +41,7 @@ supabase_products: ['database', 'auth', 'functions', 'vector'] Bree had originally built their infrastructure on Fauna, a NoSQL database with a proprietary query language (FQL). The limitations quickly surfaced. - + Fauna’s custom language, FQL, introduced friction in our development process. It took time to learn and made it harder for our team to iterate and ship at the speed we needed. @@ -54,7 +54,7 @@ And when Fauna announced its end of life, it only added urgency. Bree needed a r Bree had long been aware of Supabase. Supabase had earned its reputation among builders and indie developers for its ease of use and instant setup. But what sold Bree was how well Supabase aligned with their long-term technical goals. - + Builders around the world use Supabase for exciting projects. It is so intuitive that developers can start building immediately and ship production-ready features in a day. @@ -65,7 +65,7 @@ After a thorough evaluation of vendors, Bree chose Supabase for several reasons: - A tightly integrated suite of tools: [Auth](/auth), [Edge Functions](/edge-functions), [Vectors](/modules/vector), and more. - A rich ecosystems of partners and plugins - + It’s almost the default database for AI-first development. It’s integrated with Lovable, with V0, and with Cursor—which we use heavily. Supabase just fits. @@ -94,14 +94,14 @@ Phase two, now underway, is focused on modularizing the data model, splitting la The results of Bree’s migration to Supabase have been immediate and measurable. Engineers can ship faster, performance is up across the board, and Supabase has become a foundational part of the team’s AI-driven development workflow. - + After migrating to Supabase, we saw immediate lift in developer velocity. Engineers can iterate on features much faster by writing Postgres SQL queries. Latency improvements are notable. Bree has observed double-digit reductions in response times across core functions and expects further gains as their architecture is optimized for Supabase. - + It already has double-digit latency improvements. With further optimization, some functions were 10x faster. @@ -117,7 +117,7 @@ Bree’s journey reflects the reasons developers and fast-moving teams choose Su - **AI-native infrastructure** – Supabase supports tools like Cursor and `pgvector` out of the box, making it ideal for AI-powered development - **Built to scale** – With high availability, backups, and a flexible Postgres foundation, Supabase is ready for growth - + Supabase puts your mind at ease. It has the best support experience I’ve ever seen. It’s a disruptive technology to bring to any organization. It’s very worth switching to. diff --git a/apps/www/_customers/euka.mdx b/apps/www/_customers/euka.mdx new file mode 100644 index 00000000000..7a8f2b4728a --- /dev/null +++ b/apps/www/_customers/euka.mdx @@ -0,0 +1,112 @@ +--- +name: Euka +title: 'Euka used Supabase to unlock faster growth' +# Use meta_title to add a custom meta title. Otherwise it defaults to '{name} | Supabase Customer Stories': +# meta_title: +description: Supabase helped Euka accelerate development, simplify AI feature rollout, and scale creator marketing faster than ever. A Postgres-based platform gave them the structure, speed, and flexibility they needed to grow. +# Use meta_description to add a custom meta description. Otherwise it defaults to {description}: +meta_description: Discover how Supabase helped Euka accelerate development. +author: prashant +author_title: Prashant Sridharan +author_url: https://github.com/CoolAssPuppy +author_image_url: https://avatars.githubusercontent.com/u/914007?v=4 +logo: /images/customers/logos/euka.png +logo_inverse: /images/customers/logos/light/euka.png +og_image: /images/customers/og/euka.jpg +tags: + - supabase +date: '2025-04-29' +company_url: https://www.euka.ai +stats: [{ stat: '00,000', label: Example stat }] +misc: [{ label: 'Founded', text: 'Canada' }] +about: Turning TikTok affiliates into a revenue-generating engine. +# "healthcare" | "fintech" | "ecommerce" | "education" | "gaming" | "media" | "real-estate" | "saas" | "social" | "analytics" | "ai" | "developer-tools" +industry: ['media', 'social'] +# "startup" | "enterprise" | "indie_dev" +company_size: 'startup' +# "Asia" | "Europe" | "North America" | "South America" | "Africa" | "Oceania" +region: 'North America' +# "database" | "auth" | "storage" | "realtime" | "functions" | "vector" +supabase_products: ['database', 'auth', 'storage', 'functions', 'vector'] +--- + + + Supabase gave us the tools we needed to move fast, scale quickly, and stay flexible as we grow — + all without the heavy lifting. + + +Euka is reimagining how brands scale their presence on social media. Their AI-powered platform helps businesses find, manage, and scale creator partnerships, primarily on TikTok Shop. By automating the most time-consuming parts of user-generated content (UGC) campaigns, Euka allows brands to build awareness and drive sales faster than ever before. + +As they expanded beyond ecommerce into SaaS and consumer software, Euka needed a backend platform that could scale with them without slowing them down. + +## The challenge + +Euka’s vision required a backend that could support rapid iteration, structured data analytics, seamless AI integration, and scalable storage. To succeed, Euka needed: + +- A relational database that supported structured queries. +- Integrated services like authentication, storage, and AI search. +- A platform that scaled easily as their business grew. +- A developer-first experience to enable faster shipping. + +## Choosing Supabase + +When it came time to choose a permanent foundation for the business, Kevin recalled a side project where Supabase had impressed him with its simplicity and speed. This time, for Euka, it was not just about familiarity. It was about finding the right platform to unlock their next phase of growth. + +Supabase provided everything they needed in one place: + +- A fully managed Postgres database with standard SQL support. +- Built-in authentication, storage, edge functions, and vector search. +- Native AI features like pgvector for semantic search. +- An intuitive dashboard and first-class developer experience. + + + It just worked. Supabase gave us a zero-to-one experience that let us build quickly without + worrying about the backend. + + +Supabase allowed the team to move faster without compromising on performance or scalability. It also future-proofed their stack for AI development without needing costly rewrites later. + +## How Euka built with Supabase + +Euka’s architecture focuses on simplicity and rapid iteration. They designed their system to get the most out of Supabase while maintaining flexibility for future growth. + +Their stack includes: + +- **Frontend**: Two Next.js apps deployed on Vercel, one for brands and one for creators. +- **Backend**: Python scripts for data scraping, trend analysis, and creator search, connected directly to Supabase. +- **Storage**: Supabase Storage for managing creator profile pictures, videos, and ecommerce product images. +- **Authentication**: Supabase Auth with ongoing fine-tuning around OAuth flows. +- **AI search**: Supabase’s `pgvector` extension for trending video discovery, using cosine similarity searches. + + + Setting up the database and storage was one of the easiest parts of building our MVP. + + +Supabase’s dashboard also became a powerful internal tool. Engineers could debug quickly by inspecting real-time logs and queries. Non-technical teams like marketing and sales used the dashboard to run simple queries, inspect user data, and export reports without needing custom-built admin panels. + +## The results + +Since launching on Supabase, Euka has accelerated every part of their development lifecycle. They can build quickly and are able to deliver new AI capabilities faster than expected. + + + With Supabase, we saw an immediate lift in developer velocity. Engineers can iterate on features + much faster by writing Postgres SQL queries. + + +Supabase helped Euka achieve: + +- Faster developer cycles by standardizing on SQL. +- Easier scaling through integrated, high-performance services. +- Simplified AI expansion using `pgvector` and seamless embedding storage. +- Greater internal collaboration through an intuitive, accessible dashboard. + +Supabase gave Euka a faster way to ship, a stronger foundation for AI growth, and a platform that can evolve alongside them as they expand into new industries. + + + Honestly, it’s a no-brainer these days. If you’re starting something new and need structured data, + Supabase is the obvious choice. + + +## Ready to build and scale with Supabase? + +Start your journey today at [www.supabase.com](/) diff --git a/apps/www/data/CustomerStories.ts b/apps/www/data/CustomerStories.ts index cdca1608ba2..93350249549 100644 --- a/apps/www/data/CustomerStories.ts +++ b/apps/www/data/CustomerStories.ts @@ -18,6 +18,17 @@ export type CustomerStoryType = { } export const data: CustomerStoryType[] = [ + { + type: 'Customer Story', + title: 'Euka used Supabase to unlock faster growth', + description: + 'Supabase helped Euka accelerate development, simplify AI feature rollout, and scale creator marketing faster than ever.', + organization: 'Euka', + imgUrl: 'images/customers/logos/euka.png', + logo: '/images/customers/logos/euka.png', + logo_inverse: '/images/customers/logos/light/euka.png', + url: '/customers/euka', + }, { type: 'Customer Story', title: "Bree's Migration to Supabase from Fauna", diff --git a/apps/www/public/customers-rss.xml b/apps/www/public/customers-rss.xml index fb6ea467bda..48b0f075937 100644 --- a/apps/www/public/customers-rss.xml +++ b/apps/www/public/customers-rss.xml @@ -5,9 +5,16 @@ https://supabase.com Latest news from Supabase en - Tue, 08 Apr 2025 00:00:00 -0700 + Tue, 29 Apr 2025 00:00:00 -0700 + https://supabase.com/customers/euka + Euka used Supabase to unlock faster growth + https://supabase.com/customers/euka + Supabase helped Euka accelerate development, simplify AI feature rollout, and scale creator marketing faster than ever. A Postgres-based platform gave them the structure, speed, and flexibility they needed to grow. + Tue, 29 Apr 2025 00:00:00 -0700 + + https://supabase.com/customers/bree Bree's Migration to Supabase from Fauna https://supabase.com/customers/bree diff --git a/apps/www/public/images/blog/avatars/kevin-wang-euka.jpeg b/apps/www/public/images/blog/avatars/kevin-wang-euka.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..365a2020ba293e6ed8c24f534c36ea6e0467f51c GIT binary patch literal 52131 zcmb5VWmFs87d}db7A@{plHgX{p@k3}iaWf8;6aNNZz&D|65NXyFTu5fBteT4v{;L` zxWnal|LcCZ>wdb=oDZ|so;iEZnKjQj^PIi!X77ICk!wKIA$SiS;Nd;EKk)A6@s#l% zJ$(4Tc8|yR4W9rX|M6pdqNh)v5Red&kPs6Q6O%rpBqx1F@r;<5oR*w|ikgOohJ=ic zo|c-PlA4D4f1Nydbbrrdd_sJDLTXZCQtJQTxch-eLGTdr5c22&0Pi8igGUq(?t1Z< z@8iUO@V{{XA3VgncZL7tDZ#y!=>guuNB=J?-h+q#xAkrgkL1z4mHZL;eGeyw+C>p5 zAGwsM)x%?75H8@u2`FA<$=GpuLbuAmYjhxXZ}vxOS&S0@i6y96EilMpsMYY{5hO5n zo%g26M4*RxnMhAs5T_27(}yJQLuOV&-Xp+ELA96b9}v$G@CGMpCw$5W;8i$Q81bBX zD?AntNFiE8#pS@|@0>RdQ(rL4#mgj&z?kk1iN1L-5O9j)6Rpq8j466a0(%(yUhf5Q z`vcpT+{uwLxT~mG0le+gBi-f9if>E1~-SOafK&_{spT)13)Se5fBZ?e2y~&G| zD0K*9BaqIV;qO#4lvUvZJA>bjuptjSbz(vY?SAteS#M;FIQ9M&3%2M?l^GvNoB0$D z7G-*I?~e+v=syR}C*Tj6$wXXukl{xiu_>+hXW(BvD(XkR5nGuby z)WDIX^iig+!NQB>YO+rf!F;8@fyFZ-RHEd>lrM$vpZSUUcP@Q~pM{ez0NW)$I*r`K za#P?jd1a!b@28gvqjsxCW8p4jSh&^`h=meXa&XA?xFD?*2)>lz`o`N zDtYM=+y4?JDt}vG!lPw0d`B2XdL_E@p8HxoWg6FELK!zfAKTM@pn5FDl;acWw6l0L)2L`<0s3_7R;*|6XX$1bv} zJ*X>rX13brxmPeHCtJO~uaU@e5w|~Yrc&~%42fLY{S9Zt1uaM(%$F?%zZoqK@NyBp zY$}a^Ird#$vaciUsZLt<3*SEQT)nk%^QBJTEpRg+O-P{*#kl)Uyrt-J|OJ z%H=c3M_*RkbFVqPXO!9GbKY`69z>+2{V;Z9R`%`!V3$cfrUt zDc)P%1Ut3+g1?+6!Hv-rV`Va>2ncLrE8G3qV)#f>8Bng2J%BCxsi)x3Gu))gas1!A zraxK6iL-91$$`$W=t6FT3IPVR8RT=A$@(+*=Ba#owI%5?QrUmva0_!3d*kY$iO3() zVQbenwb^?tgi0;IiGOmn3-7Fqp6S|+23U`BRSorh&v;rfTxfi;-KR9X2G7d)yJI4^ zCvU-UVn07wZ+XPOX-;xD&B3n`!pp{!H|O}2DR$|Xg>L@X&KJqIM`vze3AXgb{6e=P*3r>783;-nfF=`uYdM#0DT zuG%=&X|hd!+ZA4%>z7)^S!Z99#r_65Y}{u#v>#dH?*6Tv1_21lxaeYFqJ(x>v5vA= zCzh4zy!KwmP=ycgp*u#Dt(JU;FFEP#lCV1xix*8|u;regqxF8h_8Lcn+_-$=N-)&z zyC}Qb`nJ(NIGUe}l5eVw$aJ9~B!%IEQ?lrQh1EfGl(jqTC(9jPqW{885X1RO6sF#{ z!uaUde9XH&+uT^!H0K|mrlPiw0aet+;_Xapl8fV5cb6?%=_$swnd&Kjhsf$d+XfKw z)r4cwV7ui>1>@11L0}*(M%h3X#4b1?TB5*KxZ>4eS!MSpt-Us^^kd#O->w;P%KRFl zUO8!SqM#%6F&CTW*r6NI-pM@kQ{^Pbmp+MAFq87p5n1Vl4FTyu*lv*IL}_uFNXSqw z?P&;Y@ZV`^nT?J5#b;=;$q-HS4OdpM5C5yg9?`hAKC^~!k{bFy%r4nxb1tPU$*6js zKe0BYql-n0+A>VcdsG$ELDEu*ikfkpO8Bl)t1ZT_0D(?T@Q?CxMZ2#f@)CaHuzV{SCi=WbwAR1H4gRzWX>W)bteabBmDWiD zbtlEVcYAhFLVx^MP>0dO#{2~qX@7%~zbg1#|1JH+S6f>r8c5dAGBW(pI!exrkzHSE z@9%(k6Zhxek(sclKLOtY$s|=@`RZb43CPbe93RJzwv9U;8JR{o#a06(eiF#F){fN^ z#3g?qA}RLVldr?*j$ETr-!oeFx^3yx zmjvwa$lkrz2qvnHO#>;f7H>{e@tbZDb|0pG#F7G;N8laHcGWUeE?ISTqjfSH%+_c5I3?(CSx+TP( zM{EKOG;ZU)A(bh|8nul&Zfs3bYUIXR)^>5}7br}J9Mpui=t4zAI)Xpf`dj*o>ZG6K zu{vade%58r@wT_i$l=^6GQ0afm-Ugeh-! zVR-jlf&4M+*U3)Z@n?$v6{nbFrwrT(VVX4%QyEo;;Xk=7a=V6&VDe8K^*CIn`B!lT zoV)yH<}ZFMJ++Ww+?e0{$0CsCgU)gpfPJ2P)xWgPClz-%p251C`W@n;(N!|NZBx%1 zXeL4K8?181x}!*|Q5sY^vK?T~-sw0p=JY(A@^h9;)u?@i;^#gPWj}A)Uhk9_Qj(nR z$uL2g`?JS_NZW@RRxmqwfzn}X$Q@qJ+4cS{XQ}b!xxD%HS^(YW03jtKPHf23mSIg6 z1KgP~>#P`^3*CAb`$lk|pU|b9rOD4Lyt$8#HOPTvdt+|ra9-VY9`n<8ZX?h}I9-N5 z@R)13Qi5@ArzlRPhD83s@<@w%*0)zMfnx@F!62E?crMk*cJoN3N5A>=QfEyE!91GO zi`nE@auw=&B5oZ9PUz6f2tqJI^bx@T@SZ_pZ{J@mmkduFchKxzj22&>$gGe7SXH;I zt$s#9y=SKA-_|%H^A}mP8AkZ(haabobkxocX;@J&bVZZyUjFN?QaYY8Bhwx971!18 zbFJ(w5dSOHWm);-0Y7pE`J{3zKW(Zi;LMT9H$QU<6Zqom%=B*XGNi*z@Au}w;CRi% zC|tRyxUBn)lYrsTkzql-RZJ9!S_2@g0&;sq{sr$FURE!-KZTL|eLo&IL&sdEd@18i zMv3f#AR1}AyRwyMy3h9}N7;e>P>QJ&vYTgldHaECu&K);AfG90N^If~@1)k8@*lsD z`gCXEc;~s#8{bB8Q7ifF^t9k8ooSVYV2uo(WiYvU1^0LTVb|>_mr-Zi){jB*{&Y(Y z4PUmtw8%vpL2Z6-BbpgbP(Fy)^J=W=j5q&~j59AXX603h3pzCSTe(p7|)Sn|IQ zXGcw%Z7amRQ1z@Rzz-^F!RUm@q8Fg&$ppQW&l6&^3g_O2j5ueEv6}2$=O-qtW|mbp0PFZko#iKE=Mq2^Cx&F>onA>Bgyq` z#zceQv{sE_r7nMjmLQsNCDv$0>twEJtf1p)X@{e0zu$N{Z5#b~_Dia?=bw{9i-y=I zaW_xgWgW6C40u!reYm$TiTKw3u84g6C^c04ayN_X`AZn+S#sokKY%)nYO~CR7g^aTExr!@A3Xe}^%Zf6dCjpm9$Z@gVb#@$bc--=_xM3K_*m&3Q>VbV}y}UFsfJZr7elEf&aH zpPoX#W^X-H7&i1~$cs|6kYvjkTlW!l{A<6xS=<4s-#=o>C9UnZpbwJj8MLD#821E^ zkZiG!zuZ#8^KX~ZL<$uJezrO+_lu;aHta7}1| zgjPtkv)p$Ib^x4*i6CZy1me99F`dtwX`6Cn*g9*YAN5c9D84-QXQgLo+q`Sk0G7~_nYZF67Og?7Zwtv}$KIN#=Jt*c z?!Rf+%vG(jcE4W&lzmrf?Ah4!Fe%Mv^^ITj3VbutY3TyL* zm4RX}Jgbw;hJH^@SPm1L7TVQ}ZZ-uJjry8re9{|=y8(Zoti>!rI3Ah%Y^F7Pg{zmw z*NH`xfuB%fo~*E|f_A*=Ccs7M3-h9rDISWdPc^{Tp>?PLRW=jxS_S88(^Wp%_L0rE zu)gxo5Nz)0Ai+sbnXXMgqR_4B8t}$oRcv4zLW89dL!s*H_bL*lqfEaM%SS;{NRK67 ze{ThG()vMAMy*%eIBAtY=kj~Pk~6V_`UA+3M-`Am*jm+JivL`Okid-4PM%bH%T>-o zc<5kIW&}Xkw;mn6Z7J|wQtM?xST!3~d_6yVC`VgL!nBQWaoc6*M-ChEk`I?`qwPs{1)sP- z5(Ye>{to>l^3rPrkMi48DMdswRczmPjW^B@@mlDMJY$TPI=_+Q$ZJpE14JK_AO^lY zcrN|`k^CN{LAXH))}o9FXDG_$pip9{_*!fTpZVLQevkdbf7HcN(cpSKx0Z0fy;gJtL%363} zoJ_?xIhS=>r#}Pyw9a9Pm(yT}5l~CkX+gV;}C+b#g?16@nM|z(%M^`u4<5&IQAB+g2h?gQi+!5fo7T!D)PO|2NF~9`XZWDWKmg2t&iUd2@~M~1_;D(3 z*POX4QKFsskNvP)(F>`|wjlxMAC@PT)4K_d3@;YJv})UtF}p8+=1NjfM|}Gk zORB>E`b(c2huxP|S!?%VhDSMH^p%g&HOCIZ$Mp%31l1+H)A}ybNWcdk5~MBYLzx=i zlDhhuhJcCM!_`;R&P09Cs8Y4Z`anAGbe0!-n!T{mSRu1$yCTiBs+ii0(m%(i$X^~S zEJ8-3`lTQ*5S31{R0+9-dL*T~2?DhAxT;=658;RXZvslW?m>+Zx>vP@$?+cE<6h=| z_~7xqE`P6l|4-GXpk&7IDr@^d;yuty6IW9T=`5G)Dq9J6o4H3BQ%O!&?y1`YK>o`up=ja2h2? z&SPDy$%6nHl{~#+tzmSI;f2{+XYi~IjeL&e9bVPUx$ik_xg>qCkj3%MN;v|-q4>4A zMR$_9I5foV-fm^4J1OzQT6TN8PV`q@QtY8{LVfT@+eUbV%}U+XqsE~(wa}vwPSV%#Sd5X#`#ws9%Z_ro31u# zWbF{_+&twDZ~X7|P>P61$ma`PMSK0>LqF(L>A$qwj23du+V$dM98W!77=c_h6Y*qT$fVTF%J=JA77}jByDkL% zxg7Y>afcUHa{YH0HH zMhpJcH^^doXccs27SJ*hi=O<@PwN`#R%GMnCd7tz-DAR+|2Z%S%5As3yzL*csRNMd z5_7&gJtyPuOq65Oyw(>86v+Io;RwtkCOQ-=Ob+=@JnHl3LP7&tBbZ^3;h6;ItEUdaQ(KNZ9FZGuM26Qh8^> z7w<3LG$)+wVc}Y>Uvi`a)i#PG5MNchA#02BqkZXWDDa;mzeH7$cp}*IvkTNr>4uOW zF6gZbO_AS>c?W>sc#jJ;&}_yc&lrl_b#fge8w(B#I@T>23eHkF7vh0Zb2Ua@VAC

;xP|v_+9gFMJm{=O)TS!!L#GubT>I`A)ZvrIat`#Rehh7Jfe`TOmYaYj@ zkE%@9tM5zJoBoDGv)exV7#=$a-W@y3z6P0E01P;>U$v9DxuJ@*>IJW%if=_FRgN+Q zc*y$_N?jryg*d5?wKJlOE~XWthwCVVOiCek1v{^#JQT5pmv=szkrJE9|#1j=j68wJ`9 z=eZ32@uv*2VS#nPz*xk<}=0v{sJlalh*9E520hi;8Yz(?pX7glBIZ<$e|bt zw85PX(g@@TjTgen5@IrMB#f_0N)fi#9Ag_O`5lq^0{%E^=B;2ynull2Yn;3wv<;Q= zae|+!o{n-xZQ@g^58qZ@tt&RyjT{QbL#<|bxkiMK?UHu|8&glU5O3;-aM~rXt;JIJ zSF*III_>%E&xvEM{9jN%-a3jRI3+i6W`*5l#{DXgf2^poJBBMYGSCE zP01ivk$7|f@`cikkskBD%9V*u0uO$uk)*)}V)KUZmIFB!2>k{VJK;7a?=Jv+aMjfZz zA(P3P?H%=@<+3QPY)U$?8Ko%u8>1Y)(DW0g^8PBe4x@kG4I(l9W;~1h=Il@NvKH>} z>=H5Kp5w1^L}ohd%x*+TYNBxJ+nl~ z^{;B2hs(Notk0>0sX7yA#1UXH<}(Om9)ZYR=0*G#j*YWMf{VPQ7{{7bP_o=S$JheK z{nYBB^B`C2Vd>T+&O1C|m^^NsNBXPZ#VPMGq#V<<13?@Y)%#x> z4YPu1ezb@ewkJ++ewCY52B!>&Z8N;*!X^GJ7#4H(ROBtkf4lsheymsE!z}wfWI9&V zK?Yi;p~^n{mb$lJosdz!VU8w{?}H&xyLP?_yAu7Gv!iZm{kXrGVRb>)q2L{P6 z1U2m@j{%nfwsfoluD>q+9_ZX9sXS`nOWeLZ8y=+Hjko?H_c0P1iWa$2v|*Bq&T=4PkFHa(l*i#r|S)u;kK z6yN>DOJoPBY-3)YP7+?-fLo96ED3Zdog5qe+L;|Q{{~mF+ zk5|q?yONSKgJEzh$2atAR}{{XEBS*Dq@!Beua2=lN&) zm@(!8?;U-GG4H|tGj~j_oS;RzgWP`_5!F5YHHgyQwaG&})9YM-q9|=x<@dAzZV~A* zz7A(om_CO>Y33*0f!>yFg71qb8mf1Az%MqhrQ3t6?RgujP(u15-0Pii*2z+q0bzyI zjJFkZlbID0r-_W>i(}i+TYCYjd@Qpzvjn7Ip0)e4IVH|0Bv6cs31acrB*6Ok7+;`y^~X$=q>dEa(xEOT|6sif1PrQP`6{eRXu%rR(j!F?$IS+b+Krp1OA~Pgd8M!3OqNU8X^{KA4KVWdLIWBh zN})+A2yHX#Twsf(8f6>@3#ALFZa>tH?S6>XTPaO%!zfp)KWn)n=x^sl*>8vM@M^9W zuE9-lUpCVB4m3XhJ4HK&VQOpQ@`#IYThRH&*(>qlPKUoYP9~s27aWl zny@BJr|fwsyp|XL%x_|KCyja)l4;-PHS8jQyV8?StH-(+Z(=%|N3;d9L9srtZ!Mi+ zaiIVhG^)RU_*Rw~hdHC)XmA+L=Uw&?u9-o$qGDrQ}!O9EERqqf)TeqNr0=A2wQlKl-XxQuWc7z!(`G?_kB#2uvd_Ys|TjsSAmV@F2W zKM<`S9BYp}?Gp8NY+g5s=~Mc^eP*H}f7h$pPjRr#;jU*%kc~SL zRA{Id4m*$wAsS*HIM4E&$5f+0wv(Th%i@!7ZAZ-YgQVc}U2+meLQ@GiPAR0ab&T3; z4l^lA$PHVi0-}KV3RP~bdYOIYPqeZw?4ou+Bn*E&md7$(x$0oVN+;y4figW%SOmO~ zex|%@BX$h{r*{Ey-)1yqrLvE06b+{C@X}UX&t7;Q*%>Ri!=N@|Ls1qitf>#v?$Z9AJtCc67#*}(lUCh0l^8IEia;_KuFUeFRmrt z1s61rV$oisU0!K?X-k4cLxIJY=u+-O`R>lrZSvexY_QXp96^lwlmiC6@VrrVRr91x zlvBUb^diW}3MDch(=E@iUOY%k^Z7q}EI_vv4x8?h|1O`j`Xg6zoWef_t?e4$coemDa^IZN;C z_Lbgc%SMfHV_5tN5)^&DdmqUi9y3Wse9F3aafWinUWQ2*SxMX4Z#7+&Blqel-njw_ zi^g5SmMX4&=^mH(u4qJVMP#T=TAF29@f$?u=kUS7G{l*EQcIEVZV)N4WHR`eEtYscJf2D!!*qJ-;IZ&d_XpAHI&{5!0T z6}&depFw6Wn+e@?;`ZNuISVux3+ar2p+3*neEYx;sC271`f6l5;7cAQi;f3$uuG4M){tJ5zSA=j1IX%@VDyDrkNo*UDU!;ip}G3Sdv5nz)~Y1N1K>VXN&XdaX(ozc0XcFezgp5Ke`U&U))sKSx2?3RVD=NFV*HgvW( z!BGzfH2clqX>05Z0VEpiehz-}{xeq2%2bRr8-MIfd}&+Fl5%p0u1%6PvASdrOz;z% z6vsRH!`z9BfPs;F?t^+ha5&Nqq?ng;TX63Vh$- ziY+r#M>OcSw6Lw!u+XlRso>Na++&Ueq3-P};?nJ_MLHqu*RG$7B?WA!h!q0|Z-uN{ z&*+I1a(*tM$L!{3JgXe2&AH8a_r%6HCEhakD}`;QBAluKH;Md)JNow|<>jBqE&FJ=hH?wl>8=d{?pku4J z?$U>0oLJP?jkOa%97b9FSYwf|1}7^PR9(*1zCX8t8aP#CLkzSMghZ zZR|r<>}H{hhn3Fjj9{67&_X%g&u?7XObU!Djm(D5eE2%f zl01#&`^R~jBZLa73hR5PV}>Fly*U)}^7_AbAsGOzdn?m+YrE*Wm<)Yz70#aHr(kv3 zqfg(^E~fu(oS}rlmWqd;<7x^#0J(hb4ZR_cQj*^-Q^c zcft00`>+Z*yXrYxTpSOy;H_(HL{c6P{i_?vy2FD6$l?2XSAx|vYmk5ua8{98Vd(6^ zSoDFR4k&6c^j;N-+g^Y3MnmOYu91y6N~Pz1=Cec5?`0}5?zX)v_@t<1Ll$X;D9UCo zGYiAthppRNeEws#Z+Znwg}Ayxx+vO+w6tR))X-`%?Z?q0WI64n1+gpF2lsO9bx2xV z9%bndRgg3cSN|2l^DmuaUKsY5HADg1MQc&CP{Y?C>vzagy7W&@KdziEb}x+vdEW8c zCh-pMam-W$)f3~v?;&&+RZMhjPb8LNpf_C&!}{>H4iS-3Q7SCVOg1spR%1uH9vQP% z-|Kb!%TucUrQY+HKz4%Li~$F&Tq2RcqdPoZlZ|N)c*~RNrk%e0dGR7gY!Vk;w8N0I z2*++W1dNq$U&Eev%JaI6F%Hw*2hi!!l_TLXMPBEi@F;7wI~_BU5qD`p)ere|JDfG| zXWv|tf*7>}shJrYTiMw;+=w?QD=Vu~FiORN^r*vZt=p*-bm%9sT8q_@v%Kr&wE&F@IclO zMkK|oU)xMfZw&U_0FBvO4VXlXD@G#n9nv~~mNuUe!Vv65YN=sth+f}Z9l-lfoSu|oVGfS4I z(O~XiXS8u0abzvzV4WK1k; zK(fym-=t)nPPC6G7XYk?QC?KWD!0YKgLJ@9IEh`7rH_*;RcxJA46ebJZUfqC0+z1~ zCEJouv?Lfj8|*2@PW(WVtZgBNcc4yw|NvY?uua6V$DIb2U5Eu%kf8>fV4 zqsCmc+VfI+IKpFpaaycN7_8V!pWopX3p9TF_by-9EEVdIMSE3Z&#_ggW`)$2cENP_ zv4U5y9_3qg5JVkBNQa9}Wjo`XI^?S4Y>S6%<*&i5aDJa;9q_oD4j~#EH&9a5qwD+5 zo{FuXn`XuL)UJpB8_qFdU9%TpO~bZ%01=dIZu!$)BWz;W!gJYJ*r(~-+4zV=}UsCrMHOdtg0{N*3Z`;mgTs#AyyWu&x=5p>|ON7;A%ERJ4}2{4>dD#K{nT4Lkz`kV7A z58-QH)3IfP#`clwrrx|Elx3>9tLwzN-g=PLEBK+ssV$ruY|TQ+ z{B5bJPAus1#5(@p*@|qJ-&=OV+3lhiOpIQ%!zVRJo4NrhUt!wqU-&IiV~j z-NxNL89-Ik#$&7Y?zHa!NvD*BIitYBRwPszy=i%oaJ>(QoZb^gXWh%TALEzzXj+fC z=53SiH^c=*FkRluV>xyFg^Fuu@_CI%{Eq( zJrU>h*I>d}@b_1b40LXl6-Oz3BZOHM6xqfo*&=znkUzZV6vWjhHk{~`jbm_UW|C?Y zGg%1XIyg*_4VhyUgW-S}+Y#iTm^XdW8ZbR#Ub1kgz$<>of^Oi36cEM-n|Dpkx$Rmi zS5OL)kvd&fi0v-sTw`en*0KHNCNan10v5mxZLoL2w?HF8${bI2G*9+e8oI<=xsRBA zxP8lFo9wnwhg0QQ%%@y_6B{|Eu%P!FFv7Jy~G3Y zPZHI|WL+1Fw!N`VdaDVG8Mn1MR7Ct+JK`$aXfAG^p`kQ)h`0V!BJIt{E*No#r_JoM zo+36dzxGwTQPmyS$L@0lWIOXM6a>YDkX!n! zLEy9Qr=;2AbK`rXC*E_Ej(2z-5$myDbk2R3J|j^loD;#R>VBM9s^cK2c8xbqYM+Q$ zJ9M--xx=&vC<0$;UD)ZN9yXjr(O5t^B37;^% z#6D}xaVGpUi-+7<6XwW1=r~}dZaOl;MD63p(LbWi`p2@1!2%>PTj1;_?NhzrCvm?d zvVNxJ*lr+iaPbZO+clT4 zj}xrJ`*QM>4xbs{0ts1H+Y@@@LRjS%h^Bf z-He`~B-W)1Zc3}33!!jbm9T?5JbN1^j-PQIbu;TTQWZhozp`jBVHJnoV>Y(w5iJ6Y zvuh~DVx+W)h$GB4*e$_$y%7+S!+u?48na_Ryd@u}D&l=SI2d3uP?@^H*3zFFf>~3L zreCN&)G1BVMgF+r6RtRm>x`J{euZXjyKX$GA-BDWE`0{GsTVIYM}|*z21v3nQBAfM z@G6qz%x1yw@W{%N?h_ycDDD#=?$aOsmjQ8K#P>fucu4VxoRa-9)$OY+ zSVUiyF7=E8g9V$Fa@#?>ArQ5fJwlq^;)nOwoy}d%u(n=D+=-%AR{vvy&6-v zIR+W;dbiewO!Jfs)G#!<&e1NiCYj?3CNl?83bWOp+x9if#)5O4boYnnq+h2b7n_>} zPEneds1TB**Ue1W?rV7#1C--9f&RZ<{ za-#;d;A7()c#nO8y>PJ8_4xBjZvA2j`4P;kGSxBrwANwxW`w+1AkevGyj#nwwXipP zU5|lBvA!K@0mL>srPwX>K`kI^72wnW0H7(bM2P92p)7rq@`8(1y9Ci#2oA4-dUE5y z-GtXM=57scRMPFq8m&JRxlE0Tnco=<}d#rh`B<{LgI zIp_dxmHp&tt5hkQ=C5F|F6)FiAe-Y>m*)43n>R(B8K7(L}4Vh#FEoWA4|3fZ_fFd2S~x?=OTo`xdnd z32d?I*vITDfc9^=8G8oXo-JG=mH_5W2T&2Ulh`AVa=5itr~f1-CccL*0KL=dkzWQi zVHQI1C*2h4uG!3P@h1mt2yVvxF!hu^+J}nt!O7sLPs=B87}-89|6@71pToh{ia>cd5Rz@ zWoKmHIR|Y#_2xcPou*7|=r(mU*1XyF0uZ$cTW~+sBQdsxi|8Pr-g@#VOPW%OnTp#$ z0scTcusT2w6zS(?d(4eMw?^w;$okktbE?zTm^BtPI+*T5i!q&EG0+lCByXg+wGW56 zZJw|?)9}_ala;{PC8VfOYkrOfcYjO@2-A;dVdnc0gVt0=_}F2c7Tl@Ek8P(C9Bkl% zF$4W*Aa!yU0K!w(c4elP`fRvB2OSsjx&AA`pi`;+W>%dpR%%)(>>b`ObQGrm+lho%XkUr6lF-0SeSF6E<~$0M z??|!Zxf?z$6{fKA2rs2uF#0++j7@2V4AXeeNqql@lko4WkMHY||391rz!!N%{qnWm z8{q$8q=)~9k$$((m5=_D)s7mMS+4SG)g7}94I2JoJ8kn%cZP>)xEJcq|MkhSQm9D5 zKX$O@5)OgL;E2tE_iZnS)1rK0AQzN1lqX$g(#W)AbB>y=(5wyZF~UM`lcHRRwyhxqq$ zK^PF0IiD9apKpXpK)_ZzobDnF(tySUa7ffLTED9B1eolXKd1sJ%#AJ=fl zv1V~Vw8GQQ^&sP&GM!c!2I0RDaZ3u}8-5sS812P$Y#^DaY^y@(6s16dBMb%GVXe#R zK7u&n>|-Zh$U3ZGVy56su-A;^y;wf5ln_K!3#rbKhayNI&F?N|J^v@R)}xs6aEx7m zH7DxWmhMNRG9~90i;fEriKB(6TyYVEaSuN(|4vE~$E%6>wwIUEfbF$aOOEI((cm8a zx6RT(9YC$}trXn=XprOXQCu^X5gg=CMFU`$2G8N`U|p@w;)>{XJ3~&90+g!J6=cDx znAU1jY2vM<$Z-ma2nTuzLjkx1J`YdoN&j__$^l>S;Us*8KDr@3+K>cP#gb=d3`^vY zHp&b4#B)%1ngt^!KHroX54m8#I7gQz>*~WfsNWlSj-H%`4M|YFCPK_z@|p5_G2I>` z%8UCodBTxMBMv^62xa6Ele&bZo)(4ZUUEosVQ_JBG^M7kl1%Y4Y~QK43!NvC9qd%p z4ERdgdF8*+?3S^sjegNFyN4X+CifF{Lhs*?oP6ivkQHGTWrzwklKy9Ra#1p>j4Sk+ zDu3et#nW5IHTC}Q<0v(HG^0nSYy$yF>F!P$4T69)5(1;UOGLUmrMtVN8>G~UfFu3# z`aM4H-}cA$$GOk$a~@~Ub6@9q1>5L;L2Twps^+&iA0{lmc4b_7Q<6|Hh1OzUvOLFy zm3*JEO#>*Kd1+(%{|Q_&w3xDKsIJsi}+su-u86I`jpvP zWR#iG2>tflf$Cy}ot0sphXn~%t2Vh0!UrfV_)ofo7&5;iVh97avcEWb!^_p|v z9ADf;<-XC#BP%bUIvTh_iHR=KAaraZeubocJu#ijlvOJ}~PaUxx~uwB-DSaZvE^2=X?h2m3sb^4sN zZ^b8zIdNO9$7ho8gtCdR)&2KK@ez5H?e}#N&T$>De`t9!-zsiupK9N0;8WFmj)8%W zh4~!w8P0#pu_uQHF!X6frp3r41&6@gFi2lZYX&oGTPEf6_mRC1sh?w!(Xnv!_vi|cPB?r*9hRQ;L=zQL@GNgD#CfbFZa*N* z*XZ)LDO|&<*?pgaH&y0s9C|r&5eofknim&h4g55vv%wJ{(X2d86z5^YUQnVuZ^@mKwO>X*L0AB{^@m!oRjj_7oVnn+yJxz}US=yf6WbVYx)B}z>9 z0lT{lUYkR6eD0q>G3RyK4lNzf=F}XNnK>TzIEgHT!E~`{wTxgnp2xg1P_a;|256M`k~ zzE;e%w1za$dQ`15KuyqF>oTQI{p720g6xGo4@Ii+jNl!&he?__p3PT1eSQ7u@l_LU zH)wN&$E}nGhxRvB7v1k-7cG@(?FycJv>l~bCL-UT7wbGB6|ywP7hC99clnp^^@+)r zPpiR>f5aznlK!YXJv!*XED{M55zChe^4|~l2mOqGMUT(4KuDOcD&C!dvB|&MZKge+ zy*(Kv;Sben2o#VlZ309PS9-?&IU*?Fbusx4J=qQX%-qLOknMtp4%9Ze{ICkwwyK!_ zrqPjgr3Z8~o2+|CS*^!obWbl4{B2z7=oQO+^wtka6vDx=W5d`IaXci(FxhN6!{ru0 zSC;8K-ejm00puhtg1H|S!NSNQqv%XEvnEA*LTV__zr-|{k} zU-b*xg~Z zh{{@CB7`V1v@ z$C+vpTH2rH;2N?=Ee}A6ON*|Oq$NwN@D2@*%hOLLhf83&-M3nFeq}Gip!pRc(idTe z^sGzD`CPOtTqZ)I6;}Hd{R^`JblV3UCtlelmNUX06eXD$&Fz6FD^}S={0W`}P08Z0 zFUg<3`?eFLw{EO(SHxCTBUQy|7mvKtO7tG%PUk;K>dW2U9KM`#xsug+_@>dKD>qv_ z^qN*HaoS&ue^Y#5%Et3eAzZQ@YDdkXU9rzEOX#7=H$p2Qr6J4C7QUE8Me_pcu+ZFqcQ<9kMK=%AA0$G(_Ul;XT- z2#2HV1gty4dONLW7Bw_*AI0&UAZ>9d|6JstY+Qr zGu4` zdv$7bQvu*mwqZ*U?e|D%P&68j+^@&{M;PH9u*i(qExn3gEsf)`+E^rqi_n~_a)biA zbWxt325-X;gCWk;YQgDms_qf(yz%ZL8~>U~Dd{892)M_7$XvlI@EbxjIMct1#ZNHp zcKn>^y6};K=v}2o8rO?#5<<#whJ=VYNd?mc0|BX^Lt&tUIg;eTj? zNt*T0fYbzsYd2#v83Y5s2SS$gCIk&fZRonDhifi49JC4YU=P2s)swVFlVxQ1z@5;r zd&Wf?I_@0{0O_y{iCgq4%Mm#E;lLnF~sc zs}%kW_@Y86@<#WT@D!Ti5gtU8I4j+gL4$Cw_7FzYE+HPbkG^RY3*&vDU7%j^M*%Mb z1whW4&c{sLj0f&_-?$&Th1Mkh@E=0D_NgIVU!0dHzSb%9-RLDHplRW&s_Qdu2!4Fd zq_yf>tmUrf=?#~ocbT7Hc4-cY@1gn|XqV5wwP2Au9 zH%-Jv^jF8nphG{l>gbA}+kc5M`DLO_Yz{XsNoInOE3XwA;MH<@(~a2{?7EXSl{A=t)&F zG@CBkLdB2%F5v>d}p3e*sc3Sc`U9F)Y>x zPEg*LZ?0r*K9Om`Lf5M2VNM2HdJPS^33znZJNZ&t27Eneqb@_0k28OG|NIz%YKCh$ z9jdzzsN;;Ejtel4dDwTB+8ly?^;h4uOpoUGelXxZh{- zXZ+%r%0O3dQsgu=+_9{xHj|6IP292Ym1dakp`KIZuMDSRVsZU_!OSzzDj~Gr4RCmp zio|v`U>-DZ>r?$=qu2haGz5m>CbSTCPpQ6>v;fOF8=-N%SS8nM4P6CRgIqqj^m(eo zglj(&N_H*wug+@)QVJ5Ev`8zL=t+}yk-gzfTN0Fkk?K~?DRZ}IZTGh4BZPK-Mg-C@ z9;()Uim6rzIrY*t82{?`{mF!B92{7Me$W0gb?CV|s2*q9g}FWK-Pdn?EoO}lcdpWx z+94dYk2kH+1WBPzXQ@BZProLX&*@gW(iV(|`@Bt5^Cs=8U9;vtv^dSY#~0cS_4@ki z=Rv<+)~EnH+(I;M!Smq$rJDPJmPm5OD-V zuz~?kJCJf6*fk^z>MePh!>Sr>&JK8*iaPsm#4eND({6x>r;VO|i;gtv-j35X9L(a_ z0F?31ExSmr8xwt|A5`bU5-4rLurp}_q6BVt=MzJ*$tWS8`blr3-EjJK|8F=D!I+ph z`weG87K|ec&c6_vWPG1!^rc&W!BR@v3;+!o!Yng@IAmvCkZ8Xq}Q(B`UYbjIxb{LPZ{nQT4wAE8+rLKFZp=B zzBgwljkvet4@dQL{&(Pl>TrMr=NthrRQBmaxDF1%XzqzQE@^503~x@azNoz8^|qmV zgl6dt?vE|NXuN8DiqopixkC5r7LR#EmU!9(xhAg(SuLC zBgU`qo~AC@U|?yol5P~lAq@-x`p6QqkK#*91GCt9ocwWjF~3flquh;kzg;l1p$K^; ziH##CawwLRk<3o9%UR4SoTNbt-VU>??W@8qVuttza}@Y*&gnNkWqUVH$(c{2QxX6_ zNihNKzn0FrtzL@{c|DRz2)_j5^aFz`>wf~i8&V9EKidws{bz-6C_1a34%m){-;#0k{w&9<*I1n566MBemkuy7cv&vATUNzPBAdyP?jj%B*7o#4=p?3xPAF z$TIL+42zT;fRcP{B??4wJ0$wOSIe=a2>@vfGK?)eQ*w$3sRH09mx!1J2ZqlwLKWwN z%nBz}-xh>5&KHb8DOv+vH1+;g?R6TajnvSH{wyDY-yIKR@8yktS(D^#BY~y^#6%|o%G1)<9%#|j5zG6kZiXzz=WzLSmbxtQm zp834WVw}SWi%u?PVAPQiKXhnY1fQd!wvPkHUtNk6Am*7b*Cnxxe01EZ>5EDu=m1LnqQ6-Q~k zRcm``0%|htEkRR`NvpIMNx%b=s?X6JzFIUp_Zs%8E2TrKMW*pu76pqG!jS@EPmJz| z_^yOVo%)+qS=x}4hj<$xd@xNCZiXKOQIzfp+1DHv&j^$AVH3FgfxY9e5v&_XGK|ov z@-ro#RqR!!uyCE`C(|Lm^f^dTiR}tG8jq_#c~Xh8o+D7z+n}~%)BUmwt}SM_0drme zYhEy(sCy8Gd>V=Vty+qJbU@3OBVEK26U{`%U&m~1DdcZOLebkS~QE*>(MYNKnp3~>oN z9f6AxcpH`H49W=grM5tvdl#co3lM+RqvxZ?J_7iDYC1i?V@@t_ZRuRkr#dCN*zr@p zM0Ko2|3d@9;sWu7N&vFrh;R1{z~6Yt*g`y&=f<;>f-$=qw*-sYO{$ev6au>_rNp^r zs;28`_S8)AfTY=c^^jd9n%!Esgd@r-VY|1teR4;eRyt1D)&M4enQ2EHk*m<%E2d$f^co?yn!I9Fo5wThyf zRPiAY2LV$*WAtkbSlUMSVAa=bqX>K*kH^eNyqtR9BNWxFSvozW$kjrXS`dhnxNRE@ zp?Zh&a4jkfP>T$@iO9LlVJ>o-Ab}ml)kgJtVG9rJ$Y{-)KTlzocmtXZ7J%);u^Qf` zO%*Jy^#p?bcDieW{WMU6Q%86+=?T@x#ytPfJ|>n3^4>?LkEEgmbBvtMz$!e-;mxV4 zUBGveKIW%4B$!c!dqE$q40Yxh#PN+zl+e5I3M?mj+nv+^n!T03NP#={j)X#kDRXB0 z=hpjj1TE}jNiuMM3?mk>7ubs#M3w_}8f27VWQ>ZWPz3WczkQZsXk}&fry>r8k^lYn zSN_n5KT-aXYtRPMulcoinUx*2)*&VkT3TthwAn;E!;(&_-@$E9{Ag+6+4;AuTrAqh zZ+AVc_{GS3d4R2Fd zjI_gT6ei?t1*?SCZG!&F2o|k_!_rIM*mbD5B>oF0FL5%UD6I`)ZSfH^>eX#j6fz@=9| z?k2AR&}j)xw$n_O%zfu*qi*8xafy-kV6&ei!CU#VXt|AP1!WszZKTS=9Bz_|TXwLQ zKI&b`<8|_ZD$Tqe;M)WR5_rPK_>?_yn^?d3leqU8kkDuTJ-C;bAl*R@4>FxcE*&_l z`a$+8)F-RRVmNY^LM4m%r7G={M`xO>>g|28yQBw|&mv@1C>#dt%^^HquJdFIYqdS zQhdwY+VCa;iG5#fIIKwA{z5XU!1+!6ea;O-zm|LHrCh3Vi~hNl5_(im!K?T0qLpl& z0oWb8F7G07^7nvd3_hH5Jmtzn(gS`dqv^4qFv@AhV{>%oX8=a7u=sD!xP^%I=2$GL zags{A25dLzBO0~p!cx$#3rWU^>dm5Pg&kny*AU=0?}k>Qh;1jeB2I&Oinm&=C}_P1 z)PwUp>{}>tQa#cJxafC}z4)H6v1H{h!8UPwHt~9)nhuI(ezeAOD%uvRX%#tGb4PEd zCKWXt4y?1JxqR1iFix+^VOMQ&ESqIruzhaMO3fEaW5W4Be8Jf)Hi(f!F-00;MJjg- ziB*ar?hpPb^koqyD=JOx+6G~<0%0FpCHS*^Q8C&AEKY{tpl07SRNe{^L~l<4%=4<0NuXD4+vCh$KbOC4Oj}R%S;e zn?$yVtTa^@PPkxWo>$!mc1=AGvU*6^>NoFL<&*(jTBYa@Ah-|M1!7E`bkAD3jG zz7D}T>|&_l%t~G>ZFg|cC;^*D;oTT6dpJken1bp|AKoI#o=Qsu4!K1mz983dPXlK{ zkoHEX7W*r{m$b?9)I~9lEDO*v)41>Gixvz!-G47Lvw^8f$XDl(TQP z4w8-5Skd*J#2A~4BF;mBJHLl9Z^=y%hwS7Ct3P~Dd~+hE!);31{9ZUQ{jcu>!?6XB@y@zh`fdA|pcQQ?L(xm8 zUWaJ>1M@JV3xL)^2gf~F9sVkzL=LQ$7cz&wz(MBMw{NfL`HJKm!JZ5_O$gC?#zi3u zuO^l55~jj|)VC^I=fE1D@3O7YV?=};e`!qqO-TtVyLYIO4jPzqn<6sh*syoX{1tRc z<6Isjdyoa*^v3#9?KkjT*Nou+ySWtgI{5NcWotU&@I=|S*(JKDUniC)yPvfHFXN&Z zQ!8XJc5<~yopj^t3q@%t+59Yz#V$qjeDZgkWcdKqy|FEL8S+p zUTTQUHyfP|R#?u62kL2zp87_T|+vRE>9U$F}jB(Zdq;RJc3`46B5k4=EOaWAL)&>XZaB^5UVL9v-1^bpJXs)5e!k0 z9T&%=weRGoRerN$!dmsRs!k7)*?__-U`ja9#`$~j(}Jv1ftFsI=_d6kW6p$kuM zVHQCA+?eV&FTrivyMD*It*Lt^EEeim@N#5}^$XR@x9Rv-%!O=RuD?Fc#Sx0`vgc>7 z1S@ORK;n?{tQOs2Ys8BobTRKUC4=jRMW2KOU#8TloJQRNn(nBGl2==wRr9Vnqk99u zyFc;Uh%c!b){#-+&6nPW-QewW%NPD4G6Cyg!2(L`bC(swUp!fox9kpJB{6PnSxm(Y z*(ZFBoWvQ~F_+B}3VBxV`$9ng7N=S^YDpV}CWjFtU@SYL+uM5#9ViKV*{AQtG3yrg zae!x0Rnti$v>+DJag~tg$M9~Qk^&)^Ynfy>*F8HWAX4n5(5Ae^^WNlfIv!i85t;OY z^dZB~GUc(1m3aP3Gx9H4w!&K*X;HGmR;3q&4lkzQPn~Xg#2|C=Oc=wCc#!cpehA5a z-NlP2Ff4+|(4tpy0WQ7mxQ9MT3K%RH7v z`ezNut^0`{Gad4gEc(&>)G!?Gj-@T$P>DD((3w|NCM8`;qQlCItwqdRb2NOe!~!er zv0BbH6M5LgWj_%-^?w~`o*b;030mZY#zA0!#NQTPgRk~XBRK#LQ27Kb;<0Xi;HMpP z?%vrte^{!iI9+6E-4Ei__8*St?uL7EwW^Q%iZ&V-;IBDBvqraZi6}ND;G*574i+^} zc3n#Ux9=FDP9x~kh)Nk0mDj^d!==j+)vRtm!3jw$s1Zg$G%vD*g|&r*&~_bkU=}}w zj};SWje-ht6*K5K4U7X7@Pz#s{I4YxVVI87dd=v zL(eD9tTu6@ByF5iw=<;($#2DP5ZV_*>kJVyFXiI!=&+;6DeowhFKjgVBRxnW-o<|9 zypXgWdYrkx;Oc=>2wli|F@!j`C9Fk}lDs6nsL)jCojoQ}9iqw~=h820FCe>o`s*VW zIYYsoaQZfoJY)ynMpI1X65JUn%UHKtD384;HXDO>U&|VhNm+P8kdx-THrA3*e;Rx4 zshJ%>6apsvF=V>u%TME(yi&p=ZrE6F+`6CNCq*GD@=kAAy`X}PL$mV%Kfr5ZHRrD?d-UqQiZWJeQI zKLe}?kov2M)Z`I-sp zm`aFY294}sEpVmbS!LMgCrwufd}X7r1}h>2sdemvZbdOz-ZxO@tqWxnw0Cw{X?vOU z#YFW>K;-Ov#rN@eA!Q|9*pI@UQiG2*mrv7zqyy(ea zPLe*z!@*fwHteL&<3J5)0U&n5>E*$Oz}aEQ6Gf@=I1Xl=rKkuzNlx@L3C@e$ph4Fo zouTvTrk@ya4-fwe38SI1k!Cw980!bchx&i=lR7VEbPln+5B-f!rOT`I?1N(` zK(Jia{#eiEI@%T%39zi)vGh%j54U;_A5d|c{wf;IOJf(7=n)n@=uP^JsX5ZOhYj0< zpp@Lsxo|up;Kxq>3Bh&3(<9TSnNBI`kp5jxW*&KooP;L9P_m02rY^5Bw!n00ZPgqc z0qc-#*5Jo}8bfAErV;{cuK*m82)$1rC{J}z!+b7DQs(Rzy|{V{Ng9H5bSIwKv}|}> za1`XVTq>0?*RSxBuX2Az)CrAlHqy5}uTsu9nrQjf`^XqA?{CY1)0f0gSLxW8tCUd61vXW2Vt zts^Bf$b#d5MYw+ZhoY-BrtAe>*7!f69QtgMUxoB+}?CkNaPIQItpOSCD7 zLVmkyaS2GpWT8AczXcC-=@}f8@&7}MmOr`!mVgoof0bD4E~Om=x!j;(RT^GD+9F+1I;aVeEb(NJ2R$qs0v}<)O+dDgbu3#nS5zUwDwPB`&gr z38x%ugnOBN%cr9PS}vwA(5fWCpt*QAYon`D*;FkMM$AM>)BEK&moH!en9nTaN{pg{ zc0T+}9e-0W3F1u5c52Ja{Z)`%Nx72gz-&f^r{^jM{M}bBzhH|tH5fLT-<=ow6Awdx ztKI(GPG6z7%%&f?@1Z+Oh94ahjs;I4PEsZa(1Oi|l*-^U>pBh15LhF2W8!eMsw^$) z53KE99#dp9EpA8A5s#7zE`zHPS$H~XR0DRC=juNt@q^t$wyEO7+c0l)2*{dPZF9@~ z@VB0%dyK53$4>3gb|_QdEoAmCK%Vi!CHgjE*`KN?@&ywSN!78`l5~0)%V>D0JT~>J zJ9Cb!77(mU48`>$WpWjiaYc1<$`uqc=BH4asIb@!x5O~{xn16>{ zT*ZQMbY`@n=wxawu7r$=;RM5{t)nzSx{PQ4^AX}ojQyGM*KzacVA$J?Adabo!6XaCU3SncZ$aB~$T@Q{K(ZUkPY zV`ZaAvWcb1sM;XZvVg?~X1Nk4-jQ$5L|h%~;H`=c&_9c&+C(-O9^?j{nZ!yD=vWFe z_;Xg)=zlS+nCq8Vdb(JHR^dfmY#J+7B?Z3t;xtjK;7;HtRuMt!E%N&$eZW67Eh2es z`)F1Qxj$HRMDc2Xbu7+*Xh(p$v%VFc*tgood<2(a z8v98Kl^GxzEUJPbMwb;?bZGaL-?Q6(fUt?-YA}V2w?4|Iu=s{bs9IJyq{2B?=1Dzo(~;95iGsfD*TIjM%PPf4k3-R>f&^R% zPPXd4%kUC0PA9XxI;U2=iV%!=e&Cy{d0OVDp75MBH;_PuAKPOkq zzYttSg-L?^+_#dI3h!9pB?`p3wSeWx7H&7>{0Wz~)IB!!KR1T_ZLgKg7o54@zG1({ zQye;us$9%YE=Xhv|G{_dmG}$0RlQ^k7iPm2s@z>ayyM>O=7)hcu`J zKka`MmAe`teNpo4o|9+nkd@igI)XU$Ilfl#5_ zpWd7DN;(T`5!v0aVB6fN5otzUE;$}S{SU3ayf430Ub6QQ<8N^Ks$n2QJv9kQnNk%_ z0u!LPv^{{&D1oW5bOf2fDR8-YPs5|MaQyZy5L(i=HcdpJcLMTtLE5cUz?s^1}40C?O0jMlIVrp)~J2@{c zxVO6FTm`o888E_pkkW>=wXxm~-;2ksSt)%3G9&K|da8;kEp*$dukn47FhXU3qko>% z!7mWT*D`a&r5^SrtVTqI(zXzalk$#(H%6GD89FUj{u1cblBklPT|wE44%n9BsK)cC zaD+K^w2??yyt9Wrp+6}+)|(#tBw(kzh$kV!pBTtWM{Y@hHO5olZhL9De_x_JREz>< zz-e~lXrntg-by9*u@?owzsu{?+h0)&sp$~d6iA!#K0xJw<%P~rB@ft3Ou-KqaaY@ND*Fqw&ERJQ|*wCC^ zR>|Wy`^0yk{MXUaWQh1LS(5WIt|$Kd6m~{NmiS^4-2^MP=zP7KNTK78Ip=y8BXgQr zJI+n>q^q~t1l6_jX-4f-XUvY%SYMrDSYo7^??DSaC6*R{R^*dC!~UUt3GCNcACSs5 zxWm3ve=dkX4o35L$_x7Zp!>o#nB_+tC>fI!^A0uiccLVV@n_Wuc zaN3e+@Oec^9i8j-n(qY52Vp3@zTGCFXo@>Chr zjBq|~@_tVD(db2!UIBXen1CgQJatJuM%WKuT8alIdR#k(l*T6vUUweto3ZoL-=Mct zXzWs^ES;mnJ{#6Wo^_sp*E{*azCBG>#VtX`YUoxF^n^Y)lFN*8NmMgw>LWsX^7|rb z*i(4nyGb9v)aGL{A-UP*u>_>m4t%E^pmODp1!!_6I(=rx-l?i9auutL&fG|46zdD< zYFAE$g?Zi`jl|x$eJqRgw`Cny6^yE8g_c^@NwGC#*1y!5~X zk?^N#1%Y`{Y)N!sl32zo5+U??$=LRueb7D0y5^hNA1`|pWEP-8Nwe}6^HleeCBRB?GCESb^U1849Vh1^b<9-9Tvg9b z2H6OHu_$t#w@sWnI>s7Y?&pR z4Sx6uM39g%BnKozbgZyg+8urQIWTUKmVu^!^WYyEgxG($pfJCB%|^u;<`QfkhS6l5 zSKqXSv0dE8R-WgO4OgAMXWGPP9^)vY)}r&0Zc;5I)r#tlLZo0q3UNba+Z>M)>UI`* zGLJ+d%GR}RRZKh=;3*!WiE0^Hg`qsTUz)*B%V{EY59XlrSRyStPSBV}`6uGmc{UbAXnjf^NVZ*~r>ogk7M8L2 z6=du#C1<`;ZbIV_rc{G-ZX%V8Gpk2P$7*Fco6%&Bo!z56NzLG)HXT51cOWX#9nUo{ zUGq}8F~RB;$eaqlyjD?ab!M*Qo}gR@YbT)vrb`>cWH!J+1ye^^WR!(CzZzMP634Gp zRq72-z`z=xD^_rn@gThonK)5b`44n6a&Ah7lK6uKtzu-lBeO9 z(X9^oN@Ol~^h>KzH?~e9@%A*pZj)u~3_9&P7;6 zi1A$^P)}l5C^iM;x;SymZY1^D`-|5YGEbh1bnX)lA-BSdy|bO;x$58;e3gZr6@c0u znHvbU&m|_7)JTjcT=pb;!77nA)!4wpcSP0X;wCC_eoBs$7+eU?JX~5yo8>Q+4`A8^ z8YP4@N$4>3{vt)x<`b``Yp~p$$3s^2Q|W1!zob#M^sZ{iw*RcJnMzXW;dM1sT9Xaz zkjGj)HniuunEipF{S#02IyOAin(VXeqZo*QUNi5&FL6K-OxuhSO%i@;@(P>0&<8|q zjg&Xm)}^6DO~nXsLNNvc8{f{ED;U3(h*TMh67^W)bxp~+CEHY;Q&KL$LJs&J`&_-E z1Et_$(Oi0(zEbdnmdHI;ODuSsUmfC9 zbq>wiErAYvlWz5}ZPjuU%nVvVI({o5NiBTYEPHDX?)t@AFJ2W$)TJ()lrtak_HEj2i7_Z#wn&0lh0=zi4#%9R1inmiZOt>s8+ID*B zC-GIkz03`esG1a=QRcD8QW6pMKrXKp8`m@ffoEy2#Qes3!d-{#`QW#7E7Oq+(kp>c z+M$~M4iU*0cA-{Sq5shM;jO1~`EhUE<;ZW@?!)_^f@6T$)bbF!`0}K0W;-MEVI;s! z(zG~{!Hd|@2J*Ofc!O(!xNkqoHhM2WuRl>&A$T1G{W0>j#0bftAHU7Lk~o(k^>aC2 zQJi{vj10QL2@oO|N)AXP%h-7~%|&B|5;eTzc){C<>5f4R?oL^@R-I(T_tUA&E2yMI z-*#09eNQ-c30@u~&y7^E542AS0UQhsZhK^4}Ls|nP5V73|mrCI&Tcwij9cKkK zU0<%9+><2k@|i`>yCITcm4wb&sx2mtzXr@Y)^X}a6IL{Prvx&Z+1TR zc_^f4ESON+yz5qyi``;qGfUmrs>!DLQ@j~75f5dETko(E%98pKW?^sjzs%bw5*!*D zu(Jaxy*C3U)e6CSb}yv#OqxScIteo=;Nim)|A|})YcN*CawzpJ#NIO`E{?-OiNhTa zp4vUYaB=4XU_@2sBwSwUIR`E>!i}WxXI07el?T3m)Cu@HyNBL_^Hck&*_qPxwV3}S z_i;7}ci21s8Cu_`UZ*&@HDdIrO!V-bTPa-()8*a4ACe&u(fGu5+=mw^g;;`#g>GZ5 zk)JzEBKAxhy` z00}wwhZ~%k1eS=yhWe2KOpPXHB4e2lr7}=z=s<_TNo(0=(<`5u)_ESa^LP6I%hzjG zVx`k!Fx~ouL6JD@z%)3cojKT&yuU~MfSiIEUznNPJM|uZvzCyhx_DaX)A?aUGjeQ! z%h?Od`dZ;L@bCH(mq1~+8-juUh=$+SwF~}sDq@_^eRr2lx|x@LCD`rhw2?MqP_3I7 zWV!^-(+rL+^m~SWXum|~&%p8W+*B+JO6Yt*OM1D-PKs+f z|1dVYQ)f-5O8PfWl*xznWx|$QoizZC`la6;w38$vKz@ozCHDGUti86f6;yk;;f#E5 z$8PvSS(A-5Aq=0$bX*p2cl%gr=#cxZ}rO0x4T6Auli4!1zq zDKi9yAp^zHif7n5#1C)bWvjueutb9d5@+Kucoia;&>dnIb%VMb2tXvk)}i z>F~$g_vm3d;EnNQP~T%XyH&LCQ;aCzemKWJw5RtV z8UyivDcS#VV*mU6@AV9g7zlHlX9`XcD%*5W#l*TSgdsA8{?1X^z8=akL!hdAgp(?&@8TAdN z8;xev>_}F~v?=qzl{fx>SVT@Bd&nI~LlfCyl)UIH%T@uevz%I*o42$E>aN>b=K3VjBp;fT> zYU4H$jnkrN!{`EL*w=kLVc}_=&`whOs}#@Rc?}hObV?;oaadSV$WwZSok5D1!?03k z;!nEGbES?Dp7s`_5PNGIvXrwx?VwDYxq^NMGk7DD2K`Qba*9vqf4^K#+Lx zB;>;YeTi5D&Z$+15)7?mYoMjK{|5~^SoE{huv~geiOt%hl=?65;0KE*JPEFE{v`h$ zFY2etE9(^);d1%M>6-8KQrO@{*T2Ib6#Y67R50dmVWuygh6cf$&M`a@bPW*6PV3Nhg5V^<^ADX_Z$CO+aYom#6ihhp*9fk!mo@2}ckwM7Q4`#_{G1$0 zT4Z3%spoU283g32G%_9M`mmVIHu!9zTtt)bchuUaRTL_rIVwmK;Y6|4fO@Zw<{3pW zpY^&UZ13`j7vCvVk@YR>?}pZ1RBzU0ugf@#zc^NRu~uqQ^|BT2LtiYSpPaey>$r>L z#j#Y?r*m!;``!oVv;K#MOa>2fd5*OuQcDa?LU zE27RQ_W2Pfx->KGqfG7G2FkruSD%`=Moe4w8WU;E;!dFtPJ&YnG^fjr`;tHEz3faa z00lp+LyQ}6=I}<|-pI1sOqg=I##ng23hD(NeC|>p@o35$$}ob|3EDpOabwN0ocGQT zHz`!>)}I1q6C?CzC}d}XK)(++#Jb+GOpels>rJceF%3y zuZ#OIb=naAU`dQFF=ePSV%N%@+GpYOk}n!2v%B08g2qhk$?dS2CCfI_V+@>uZ@_re z4p{mmwpCEUjrOpE(pqYn4>++2N*J=2?bp)pIXtG3Ma__eHs(;5{SPdPO|i~v8yVMZ z>s_zY4;sC$-qm%zKn(me`b!c`;egSG?J_s$8n~$*wP_f{>ejM-<8)@}%sTx#$mjaW z-`aa!&U>hZhoxt3R)e}V!7nlnd>VsXB!f?Mo(-haT~Jxl6IIS*i9 zdDB`lagcbp@>7Bl`yU$Lc19BY{eN*!jQ`c<|H|@znuY(>W-#!nHbb6t3$Opb;{2bT z2=KXNV{nXx?pZ$&Z){keN0#Wvp)Y3!QT+d|VqyIqjqzuF;xb-08+ z<7|Qz|33h*Ku^DpG7OEQjAIk|ZdkBF_`ize{v&)`>tppF(%0)PyA7kUGsmzn@8P~~ zS>3Yy%iodT7cCzaJ{xbFY~m%P1K9rnEZO6I^DOY=?)kQ5#%6q2#WCk{-gqsr%kXM4 z^pEO4%TC`QlF0lsvG>+X3k!BYb``kT66L#mExr*CEtcta42eKMex+kseDCVl#`tdU zjj}8DO_ofAmXM4vDIm#f)*>4y z#_XZrcfpRMoml?>zvMN4SM^&h-rsC2JZ2n_bIiMwMJvd`n+PYah1J+Q;f0|wZ=JW7 zba_fY(#kb<1+9~e<(q5~XuJ(AauzW51(vCIXy+rHi1vl#H~YXW$(WupW3JSC}+ zggbww^bNIc(b#XX57C0sas4AiJ8UC-onZR1n{ZrPc-Qd69;EALU-!dpUB6`znPd)2 z^<J2huc(u0MVB0*;U3gB8Tv=DZ-&^)ruT#~FIpD0B z;yQN08PPwhCL!d{7jo^}2Mb5BjrMIGN6WCUi>r5iOko!2k#5UsN z5a!IC!ceAO#UW#^-!NtV(!(wTo`g0*n`M$o{XReqhaLr_Lj<0N7k_Z<%Vu9BDeM8| zyvR#s2=aY(ja-7w^mQ3$#yvk|ILvAnF6c0fyKEn9lL&rFj@fzB<%`%Jm*1EK$WI#< z->{g}Ecx#G$u^J`e&r%*A$P(}ElS$ml4L_x4*5K1H+i_p9lDMES|xohiHCzL64>>R zSvE}bU-q0sv(X{h+h?&Bg@!O}7Ls3n8Oz`@Ft+fWCeNF4@j3CbKL%ZkBy_Ej!X|Rr zXUkJN{lD((yExX#lHrlEl3b4X>N9A0GYqJ?+EL@&^@*}P!INQQdwb#b$!1~<5-o+* zWW?l*+vL19{fIg(7d(c>9v_3_;Cn_B#KqU`_biwCkp0}g!&!fR#8Uf~>JW!U5iFu> zIO}{6GsEymC-L>fyVj5X2p+GuUL7ZrZ(~g)fnD|#9D@2}=No@)Ot@sFVsd0R9TqIsSnvpgXT@*(lGXHOB4*_|ic zY=y$oY2q16cF)pNksBg&!(i-U%OE(;`beF6lKJLX!D}3`JP4d*?smDdV9QR-X3P5D zvbaV$l1ciNMS?PC_9OLWmRT?y@p8`wKZkYfFp@A~2s6an4#^!K}Lo{Mc8H*PvD#26Bu64s9(~54?3B0Z>Y`?K>UotXGm%NO7SIqCww3bgu89}j9e%1 z%Pg`>BtLJ{OIiN!`;XYdzA&)^Tp{71&v#IhyG13we_#O;62PtPSiF1)MckH;*s&)^M_CoCg{tf2U1 zG3V+YU%c#o2dt*x8N+TP)5G}#ulUPS&R^I$A%pw6-R@m}hvNEv&*1+6|HJ?%5CH%K z00II60|5a60RaI40096IAu&NwVR3;Fk)g4{(c$p%AphC`2mt{A0Y4B*>J@xS-%`0I zHI$e5Qw{M^T}wc=K9gNc97-$xPgOeNU#JR&9x_tOt%0$`)F;u3hyMWY{{Zs>j3A3f z{lXkziQG$xUxH0MOv<7>LD&?td_>=K`24y~WwO~;!BL8~qI!h#<0ctip^hL*J;4un zLXOw!Rh-nxk&3>jr{QFFMOPscc)!8PoFYQdE^t(;?4}e8m~pc{>TBeNyhS8XdX8mH z?kO1NQ3BkUJ;2Du_AtUn5fi>*TuO^`2}MfZ#bh;!apUln0%jnL3WC`Z1!N@fmbk-0 za+HywT*1a>Rhp4f}=EuhNO3y$I@m0T)t`4It^Ah@)`Iz|pHX*1UrD0!F91{e;Q2;;} z8tf=d68ua=cOq6M-YO$GJ5ed~2h^>VY`8#`yic+wfjlY;i4mU?;J#3~Ml3VMjyQ5LQs-A@wm0ISx=x{J3Nu}X!`JWt$dy(zX2BsSPFAenpO zQNCisxsB0KJxofrqvJS_qEl%WPp9CYhaM$~!e^MqXAe-DaSE)Zmx>Q?RWas$rVkVC zD{k4Z!QhGJQ6f|=)Jvb-e+4hx_&}&d51CZ)0z9P)fo9^sfdvWbDQWV6kV^}}ALP}c z%AkwT`B6Wx6m@JPS0`ptWWYNnTEB0wmZ&{A!m3T(d6w2-@J9Yg4#l1s(A1P|S<-0` z!yBYs{{X?1Xb@x7;tE38bLB3B1+_A{;o*U`L)s;i9mK7Tn~ymM;*gb>GV__`#Mh}$ zQ_48;um~d+G68s-1`)^1eDWW!<71gr;$YYWt*^sJ5uqIfr^Fc7Q`;R>cSkcFmgtU| z`#?N%9mcYIv^#Z-HVPWPJwaT)T}9jr;fCr4?8uWD zU=>7O+02|xde?-fs7|OQgUK%C9tK_&UKqOPV<-y!cdPP?24dh=iEsp zRbhOdBWy5IrzIW5)<8M8t%B6n2F3;nZ^Q-nD=CRt5s1Lsk8@uzB;M;QF*R{?zCr{!hyuF|mcfg4Ifo0@op;y&TJ1i37infD+ zDp#3%A{M1~I~{FQ9uK)vJb~Y`XeyNLoXUA)L*03b@|8a)h^MGsIi-(M9EWm-sjoXL zj(WFJ_K*R?%281LwIQLo-DAO+p5WB7N6YZSKvXU23P%@eTW!f>P=4ui6#oF}90DpW zfaTl`S1>Gc=OF^wnCFNR0Ty6}HKGv7Ou1(uroyFm^DP$2$m)0tl}8fJp-7b}C|WD{ zg3th%!0e;~^l<`Q%dyE|g7=fmpdU>kG;7MGfQsYE28>j9O@ETMLIuh}{zs1)1q4-! zLv@G7#VIArpp0TENb-1NsxJL}pAi877u>p5Y%gNK0>ARPkofSIv>AKrUr!iMGyS&Mh21I5;Vt%k=fAsatMLWr5mMJeD2YRH;)oYjgO)O!&!# zk=Srl>|Q4-yOcttaNFDzrpt2m7%W4$M72HA=mOX6iq}IUP$O!2N->tbO>qD%fRzHN zsNyc>3a}NjXj6=GQMHa?Kycw!GJ&`<%L;)^8jN4&D$exK4NF+mtZLY5rXw6d&6ka+ z!hl;a^(xA%j1j~w>N#Sg;vmqdJkgG3~9KbuKN#DA`3k!&)+2FVqs5gd&cw zxS>b(C|at^gL267-6jF4@9dOs!*SPNh!Pj(Jt40%MI$gZ5Fdd#QMkx1Z%GMu#dMU; zjG;#1hOkRQb1Z69Tb0Vt##XI}>Ud2aBFWl-*&R_f4YiL7rZ$^pVk$3T$S&Z%YHDF9 z^n*i3FA>tCcqflwbrtm5+^@JT!ZuKr@=EuJyO~w7=+q(^>zJH9*;3^j0CFdf(TQ6l z>LaBin(US?%ZuSG1{>LvsmcY@k8myMTmhmxglsThBq09)A!6-#A%#J+rUHbdU(~UG zhQg45mrPAKl$(kM#}_gZgkd@w!;M=AISsOOF&JP^vohwv{Y%OXDpc_ugDWXAAq&*J zE(7C>!D}o-_}b)Qb-*>Jr+_=XynGFjIB$5YRS~r$wP-&2j?lFho6M_y#y}dxb;p@e96FC1eV!w z8x*zN@RYHY4v0Vs^m~Q{mJKjl82gnx{v5Zz*cu@%LbW11`#l>zB zD1zc#sDrmfN4UYNyyM|!WdPRG;sHahaCxXF23poK)`Nkm38I&)ub3bT^4|n3xN{W9 zKB{seUj)+tN_#bYPHEy2q}6=HXc~7d7ChBQ!8}06v0O4 z6)!k`Wg7T~(b?_->9gh`c)u|_?cK}DUoqCXh`G@O%(@q4nnXIdlUR^OC}VS-MVg3< zm?|m=2$TzQ6)F67!xeIf4sJBW_24GFBAyzzcgp^tgmn(yXC=B@2m+?qxONe>qhaM( zn|&S#n**j2qeD9J6S9MuLir+qXyfFRt5mFYC~3RUVF#s73YKUGg=`=PCsjnE4?ZEb zL*gD$ovaaob_1Au0N=`G6%Qc_{i#Yuzd^53g@&Dyb(DB{fwyRt0cd-HIPtG!Ht~q< z5WJX17)V~gTA^F&I2#Dv&9d6tj*74;-esh@Lgd8b+|a#>;-pGnT+67h?hLowrBd!Y ztFjBPk^(|nn2p%WF$n!n77ZaSsL6Y$a=w|dTupb0K#VX!BGS>^=_w9Q(`~V9zZ1Ju zapEDuz=+Qe)OS}5{-`xZwlsMNejAu+o6*MEaxPwUXYCnklLSVn+;#E$brX+z! zG(oE9XzBwYq$$A^G$Euy7a`ddTE9_LoI*PZD7b0Ve4kN|i7A>v(kaC(9HH7VYBF5w zk>R;6Fx*#~bhHqZ$XdIA5l#03w3p1MxXhD_e-fo`3EKYvW3PUtx6E^i*p?%M9Jy3Z zr!dRb&bKE4$B2Yjm}BB{d`EBtRgV*6H^d(@gj-?mv4A1_o)1+H#v#)|?KrZ4 zRUik{C-QJS%Hesp$~k4JwG49{8RV$U*DM$%P&z!s3RTGJ7$GgF=jH$?2cDpf7#JwTOo2T zr;U_20#O;iiblH6N^<~?Z)7EaP=-X#QCLTD3& zZ|*2YqE}y^#S1I`4Ijf;N(s~ycd+7OVn(GAKq^?A zh(jDf4Ez;%uR+%$`ym!%b$%n<_NCPyp@BW_wD8|G0mKt|d$0w#mhk098C-)+1U)ru z2Rl^{(22S&pY|xDmRz#WoIX+QtQtWfN0-dahUH}%!@fv%4`>~=y+LDx80i4t@;r8m zJ6t*}YsA4{7Yhd~*$wd=gFE6`JAnzo*0^_ zMfZ0zIP1s?VmB=VnB}&aXk2+*`HENjj^h6SWh!LXMSQ{+lmS!ym$9gg+|^}-0@!{? zLGuENu3+{TP;G2Bz91uV--K!B_Zq~+Q-v&pFcKPSm_um;Fr8W@0{J76(0~@O(IM4^ z)xINJMpJ{0pK|5L2Oi0yyD@GD=z}%IQB8g7I z=cYt!k5JtSeS5;OpZh zWRhO-e7taJM#wyk&0pW4!B*{ z)EZ3Ut0jw2C_1RqT-d%KUE1+c1=}xKV*#e=g9iY1kfg)zAgBTjOTk4?`iPvk*>$l~ z=2uVy+#+PP(g9I-^L1%#uN z1eXmq*vMQ&)e^4Xs1@Jrtzy9LFE(=`l>ybm>IA|kL5LAgB~7oEHI{7ZaEF*uo@6R0 zSwu~hl#8wXz)ZE|Zh*SIM+-1k%LnL-Z+_XtnEl2^%{YiFKk^x2hcc;EPzgjh0{3v# zICUH)wqJ-#X-zD5C^~Gg5Kh|-OXnujkarQ_Fv8G0tV+rr`j_t6aV06f}rYoB|4YLjI z!UPF4@fZui;VJ}kOnS(D-G&2_5x8Pv8bO6xK4Z9SE;gxCX=**p*UaV&by`$#x%r5K zN^Qb58R`5d|E!)5JKc<=c2XK~<1=7-Blh;qIkv zZk7Rr_Na}jd)Z_j)9j!%5UoAPfd2plt+;zwJy_z?%%3q!)6PK(ztp`ZJMj&-!re_? zo3p)z({-W8+@flQ=prqOya&KXh&94G3`c*)2`WE1aOi|(o_R2WLR_f3M17)LE4e&$ z+tfG&9u!Q#sD;E%C1el@aN#d7V;Lc~n#B|uFd3}urvl@Lmx$sW%Zrj7jfMaaZ%v4V zVlA_s+!P0hC@cV4)(b__SYlRzyOcvxtp_-X5l{6BF}s2m^X4-O`J6(<$4rCeVnbkC zhJxgmG(bDULj23w(begVyXvK7&zU>lxc=PG!LUrLieYSofJ(ItsH_0Bv15etF>4WA z-Nr?+r(d|Va&R&}touZ%YkQlcnA#+Dbn}c(6Cvsc58QSQjz$_-##Bf2M9TT^QoF4+ z5ZtY*;#LCQ(cFA;xCFR$ILruAIDJPOMTYWl>c?UVki0aUH#@|kB?g*}S_^Xxk!%yV zyc1k8fEDe9VvXlw?PbmeW&JQzAGq-`<}g;!SCw2a>VV0ETNOT1>Qk65HcM(bj0mSd zJVj7OgmEyVdL_;nL*j&umYR3wUL&%bZGx}NRPi+@HPoRB_R1AoEK?sa%6IW9m0>#Y zNL`m(xOremHuU7XQnb`tY;y8Og%%W3sz(B)@N40?Z3<8~W!AkJ9}Th!@IK?ZEkSAW z$imf|oLG~$Yk{A*@Z)1G^`_CxuDuz;V_5b>;SPdhLvIL8rR5j`+Ke~5c&O-GE5yQa zg+vvDN_dxP;t)EUd_b52@b@nrC{z+9HQ??jPy`q%aH6Z= zMynMDrd@AC>QWfk+42cr zt%b^5xKvP6CF5~}S#>2|=2;kJjd3$mJ?ytnFhP9807zT5RIT7d9E~0jehgR)KWM_< zItCsA7fVY$OY=2!1G4zpR7aH(DV^IYO#)?x+vtYJ5CcuCJzNrMabGvDa3WFNSH8wo z4a`na;xG*pmkb+0@tl?fc_i?*KT`bQM%=^*=5DAM`|gzhJVFYWvxrMUD0eUXm%||6nQEY4 z97k&Ifq>UnSrhd7k5yq47lah%#$&PwFo%Iq8L}BJzTz`^IB}`T>^Qs~%^KekS7ZaS z?gbSDbuRBFj^8OQk97V0+t=-XIQwxekZs zqdM@Rd5r^VrG}lQWGolEg4KxJN?p+TDlP`dz|-O^3vn)2BOt>K?)=L^gjN2-HFp|n zuor$H;_S5gmTs;&n+{ek8z5B(!0nhWB8o$mS39M~$)hPbhe=}~(}}|f$EkmF+YwyE zR;1HJ7RH=+3z4JwAM$b3p(rO0+%*FgEa$98m2+wsI|52BbaAAG-zLj|s5B{AG{_2D zQkHd8E}n=hW}-W|HsmPf_>>a56u8$D&y;J6Tv?LxrOpvxiDwnWa?4h#7T9j@%vABx z7aCKU%sUxOltq?wUCc;b>cCaY4(6-`?In|6YQ`vGmvlGGq9eG%xGZL3x1jPx8xepI z8qMwsry3j`u!pd14sIGoU0WX_&xln@sly?bAGvrjG_0Ck+3mR3N&QsW$p2gXi^5V8e!Zc)r3qh%$*jjA!Bu`Iw+2jLh_- z*$A4zaC9=@dAk+k+UI=fvB+QO>Rc1uJqAXq>02Ca*(eqf#tP|ryA9M zVQ&Y7MNYml2o(m{ZbKRlfa8T*m_$a2Ucu zQoqR6#dH~Z((vLg7W9?k-q)xeB63R!VY7B+{KFs}xyp~+2~(4nr6`IK)NBWo4-?^oT4+&>dj&@@LfQtr=+-w31 z&a3o6!(6Aw{{Wa;Q1V8QdoKaDd09e5Z8^ERtuL8OO5n_hWea{3+9g`%j~=IeFM=cv zXPcFmPxyem;4ES(3S65jA}M=|*v;&XtY*N)uH59J?!Z#RETt-NB)r#B;;_O56-gg* z{aUwTzXF$K@Im2uh5-Kn*fh4fU}^XZE}I1t(q#e^`0Ys@I}AtKaklI}T( zJjyvtV|kj3XAOU|p0_P>QI5j5o&F;Dxl=bUCLjyU4yae!gB^qyjCUXcS9jsj%yHb+}zF;)3 z2c)i&^s=xi?ev0}y7!Va3gaTxz7MFkieZi92N&}Z6z?;V0sczH>)mq%ygb?qSMUX?({^WXgCJP{Lot;Ig<;MR8GSbI*^Nhf#rD zAsRNI)nO~j1w8RGi^*VLa0|=;bt=b_a>$mZrAz%p6)`}+>}8VBcRo?=cP_>cFo%hO zGHtUZdD+9%Z39^lI~qws+_9t=Ex`(({g&?)FnR4`{xI&ERdsuX%Y)`4G3ZGx0`63LH@z5 z>sxE;TlM)aTvK7ScNWN3NJEygpl#XtdTJC{*#kEzlZss?6~p(a~N5J}WU1Z<@) zb|Njy%{Nlbwk^3B4{$+V_a6C+B`R$W35pyE6dbTvG!5Fu3X!~2 z6eVzriA}P0j>8Lp1#s~_iAq$VVeUVu+k&I|U;>n(1Qy$b<42ItioBGmVJuPG>RBM? z;}BDr)b|}#?aP_Vf#)(%ze zu`f1WMGzued4c8*VF9o^lzbw2faJ|UJ0Y($b^MpiE@}g&M_Rg;0=__*6`hNYoy+Sx zmv3^B!_vVS6^DBf;wx=i#QxW7@fk2AE>PQGv2C81&vvyw5sH+j2ksRe8d^Nm6!w<) z^SIXw0}p{vLvVcGR7!hlMJ`m`vGVF!v1u_b3<@dkQ(n6)fdfDfgJmv4`ijxRcLfdw z{Ks;JiO~t^Y_O$U0f+^g62Mz7FA}F3*@rX|p-=7_IKyC7b24GMXsJ;}Bx0+K)E*-@ zL4f2t1lS-_KNtu~;FJVfzvQN2#m^4QxsP*WQ!JH)xx*pW5YqV>@EOc{K}Y?FgHoq9 z6o$^DLV%@@R|1%XR;4*!k6(BwRj%T7J|msAYi<>-E< z(sEwMU->T0St@Z0tC$OO7IhRanTm;0Ce&jo*(=NHGDQ#Ky&j^_%@(2s-4PWNYL3-@ z9Lr88B^3gn z7Dg+x9TbqwL8?Ik2J~fqN2Eidlt18>_p?=(GKDy{C7`33r&yHujlpLm1q#2IgM`&_ z1*sk(58PsQ>Mo3XnU>f=NA>|%sOt@Qo+S8#*~6%`3a+5KLclp;5NokH#KM)6M3yNw zR9Po5|ek-G^GnDN{4P4SrYMN z7eosVZZWlux~SlEFa-TZC|#WBh(NN!n}`8QI=-dL9y64NmA?JTy`(Cus1_-!J|zVg zHR^G#x4-f*Ex?4iEw;gAhwhH@&zV^Ej6H~w%WgH~Kw|N08Mxverx_8wiYGQ0XVkNY>J%%l zGZ3KJekXz>j^H=n;9BP%V`toR<%jWMscw;~?Y#SCfXeKQ47UFOf^<2G=iJ1k^#Q)S zCGf$9NIIVrKn{$8mF!~|TehOw{snQxHt}3RP&Z!@KrJ|07$r|K7&IvAHp)@xwKh=b z^c~BgK-hSW1m`h1B`)xLoQxSU2K$B=vFS+ioYbze;0*{Q)X^a_u!8BtNB7igwG$AC z=XQ|=<_Mw-muC3fxnK4q2r$ZoZ@BLV-1Dc-mQvwTPI)fv0p>1LQ@%XMljgdQuDcb6R3<^N_?=#XT%bn-TQ?&9bG%95XORXcwbWJ ze$vV1dG2ZeHiV!;$5yR6fD~99gabhu*ARhRCafqaTuYt>_tZS>;2|7?TuX4G02QnQ zP!5r{6db`?soTQwHH?6~1AvzcyQsAIbrlN_=R-JUiC#DJD45KLUBXwOhiiVJLawP| zvrKypMpDmlX_OL?*%deY5n+p^2vlr_HsMu}SmNU+CC(--+(Ug&w1CwW_#Yg_L*`g! zlacr=T_v)noPe)#wOz|JVc96MvwNMPyUcdWnJ@AMW}x|4#Hkmimo7BI;S!I-U^1^M z{{UkAxZRHr?_FIti+e_uaSa_Z-(&#qc?i7;$ae}oDj*9^Vg*E3V{&4e z{z}j`hciIuYb@m?c&oKVa2M2;467qp68``S*UUWXJr>Xs_vE>D^|4dLYYmhIB}wTh z(NwL>a}1R z!N&s+jw2mI0Ce#kfOP|O;e|tE2R=}JM+Z*|?T>5Hw}u;F(e7XQEs^T)9wJ+sGl)O* zJ;g0Q7D1f8>RDi;+*x3G{7sr17t|{n2OB8fx~!`-zAAMZ%Y?p6YC1a!tpLdIZsvu< zqS7Jr6Jm)$Rey}E$Z)cYiE7FFZYrEgwPLV-qdt*13W-%;5zQ1=QkA?)61%|=81- zKN%C_E~^jygEUd*01vqn6&MdUD^lhFp&)2&JxU77$nXHryD>naR{&K_*y!M2FudSA z_Yqiek*j+@aZ65aBWs>x0;v&N2xQL^wAlwYW8LOmL{{$prqWusD2|eF8u^(hHdy1L zS%C^f0U=%pB-%pgpO}Hx{Kg-MFU>*@M0zC(4Q|0{Q1B5*Op$=E;8DJbZ<)i~I!ju- zM!ZDqf#$QR^RT#%1-!!&VYOu~EcZP=Wea61_ZMvz6%py0vf~vfQYROr1`9g45|zH< z11wyG2>|bC6GlAiCO`2CJwYwpS}eNQp?}xx2r(ZrrxW2dGlW z{8hxUK|cdp<~3X@$MU}skF3G{zdS`iod*jw3X1o zY)IS#)l3wpUP!J=d*zrx2at~??-*`Ji+n|--DK_XaZ;)(8a)vJak_Ni^vnFt7anpg z(4|67`%raSb7;1<$j=!m<2}HAAfsQoM)4RyvZa=B6hVFwI!J1aY`t5?|Vf0C|Z|GS({(5SwujG>Y2onrW0cf(N*)i;fwZRz=`MiU@Ns zTjoEYpmqfksiys)u6zBy6Z0~W30LegF0%5oCY)a+xmX3J`8tBXkj3>apsH#OHAajd zFPPRm`12TC7)7s=I9x&MI>S0E9mTva+Z=pXsJRv1Ou+RRA|FxC1oIsfn22)Um_*lv zvivX_4Ne313hvgEyeTJ855rnj;p z=;m@bdYi%dPh-8roC}6iPZuhwVbygqQ)k3Uq8cBvSlYA1zfkhRQk8whBe*%~mkY{h z9}#UF;PQZLG@SR|O4#55YoxH0%hiK~otnb_CCqPtjtb@$=*jF{8iNwq_XUOBm7&Tt zM`10M3jnH8-Hrkimeq(hs|NQ)nFLjp4Tv{PwBF_cG!YTZZU}y}MYr=T2^*Q*<eUziK^5TltiDX0TLFR`*vxme8NQxyhlt*I>E^Zx-;V2JsG`-6Un`e$C!v3JE z!B)gdfB-6GnW${HcQ(iJLzM^F9d|Tiq!6X~k9C9N`x;gAF4B3F!s6oN+@7(3lu-dofQNDky&)rTL`?RgTD+ES(}j{?fQbJIhRlZH66K#bg&t8ZsD4xw!Hf5 z{D1*(_9STgMDUR!450a8+(73*sAz!smP{caGR9n#1ajjI?oS;kft3IcsZr*m<;g+O z1k5@pT=BYjgbr`av2#CiSij*H1@J%%`90Vss4;!^5GP546s zf~jw|dfX-u4+@oxnSM7Ll!2> z)5M~;JVRFxaqR*_6tyA%{{XND<_TpI2Ft|(I@**~rSlP3BwZF)sgB+7L#J2M0`ow1 z6orR22agf)SEsK~)CcYYgOQ7?RW|WmECSY3<~680|?k4zKKhz%;(#5o9%9ebh* zPQqRA>0zD+qqtOA(lfIJSEZBKSo!ARca za6o`saT25tB(hk@BBKjKD36r6#yMw8n2Ih)gfXL;P~M?7xTSRnXoit{mTPAGN;gh? zN~J$hTP^TJ<$xASNaA8W$}~YaSobVz8dBm-0U24vbGZ;Cq1aK%n@6tik@+mIPQ))pZF7K!=Yo zjqL~*v4EWqIR602LKS+I=BCh^2qYB^y(9qz{6su{rMj*7i&H3E4~VOH_>RsVYA*7i zHa;S(0$=Wkhh^p^e~3$W6yj(B^HDGzfW7-6R)bcR5DQgelDdM?)S@1TC?zP}QPyCI zpD~ubfpKglP5FdGgGAx(QoBLLEO2$x}+lu@_LdQ$TNY{@@jEA;ap^@)(U9})8} z?SjC*!^|Dy!GwG<1EU42+^x=ItpD<9M{6eb(;f82a;g$o!j2o{A zVu$w-&fSW=*HW1NiB(05$fy4RW1_o=+GE>!4XTD8JwQf0>6G|9-Vxyng`U5ka8Ot* z+u}WueIogoWI8*XO7+&mN0E%F#tQ3uX)Kh%?ieFS8!rx;BhRRqy`xAN|9O72^7ZDhIgW?9{a4_3jxU8uDMLf7t1+q-SFv97R@#IasR`g3?qp zen?$|M6&P`8W-+kb@s{t8}}a)9$|$`j#WT8f~qupM1f8Djt4}w;lv*)bh@|!Xdg1M zj8f_d>4AMdHR@6+(``JjY8D|->|Rr-#Nht`1Xf@agchekj+I|9RAbx~HEQ2y5g=a6 zAp+U@OTwW!JB_wtR}V}w1LXvWgRrt#?C|Wi3Qto052%`r&6Mh+b{8yhQnnWvl^tGE zz!M6WY8#$F0Udb8XMh}Vl%mG)$!T;818MNV26ZaD^&BTm2->0Yb2S2b-lW12wcW0n zcN88fQi${~I1ndxLt=yhy37JOR7F?-7f%EdxOqSPO%zG@ zugtoNHcU6(t%1+<{+euSeM2giz z4nSLi5N;LRR>Nf%l)w%6CHh)31@g&+T?1A4jWUS-0C!x}0lQk`b~=KuI-ggu?2D`Z zg9Hm~D~kR`t5ze!0S5DY%sa0&3M%#Bf-4YT9LqsK0tDqr3AVqfilBI>5vt^4{>9*@ z=2H8aDaCSz{GjK)DT*W1^-rM=t|2XR!a!>t;SJgj9KRv}03}u^irS9)mL;~Mhvo>d z3j)C{=PyP3n(fXy5n-QF^KGu7n^D2+@er(YfdC2*7f`OX{grE<6GFlLB7kde1GuCp zZk#|gTg++vfh-xF)PBq6Fb0O#EHc@6c6ydaF@iQNU5gx!$PXTnFO+Jopm;tIhAh;t zWFSz`OiAGGTv2iq&T+;Se8LvX0`RWlqR!)`^DJdsRWiMr1#qXiR}#I#$Cw1QB}iHF z!hDH!oc^Nu1*5C2!?=GqR>94CMLf_yP4cA=)#LSgVP^3E0EE|=x-&2`-*SUuEU6NT z@R8&KEVx`1KUuKEtcZ${n(-3>2`2)fX)I*g%{kqbO*IC{NtrPln`iSBSJm%Aq&F z>QMMscbMvnJ#xm@3Gnj(m91{dz-(dW8iHCc@>KzF;^5U+pm7q#9vxHy#p9A0kjF5# zZFczbMgYAk;5`B5U{p~(lZ5HD5GYfHsh9=8`G^JOHs&)H&wVkfhN0*af+Vhe5d?^# zo>)xOn0ZcT36ux6uk{kI4&X1ljsU9Wd?8Jyg45&X1W>+t^EnSVRzOjq-OgS{^UNt= zQ=&BrjobU1G_twm-wl7ea%8d2qMDmC*kH23RpyPk>5= zLCwv>?3hCqx||H5ykp@5uy|cY4k#|1@_8W%rJqRM5yH5^R{|p8`+}=rAofb*r;(Z# z-Yt#oE+INajoV;RDiuK#qQJ=BNAe-^rC(8{^)*Pop}W3d!&eK+BNCFSMGY>mf>0?< zeELBJJHmXRiBC1M0<^)7urRQW9%4aI;-YSfJ+bj9a(HJnzW9|AtG8|d4PEf_44#q- z($mhw=qH>?a!gEu8y;mkBNXIjDyq7^B^(nRR6wOW2sIUVeX%VHn-i!|-Bsi;mhD`F z1jQED{y{jBK9 z1s+2^OEz>}@&5n_Jpz?`UPMEV$E)`^?So8FHsRNgse28IOZ>*)o0dd3=ftWMwYHe+ z?kpulcF*UC&`q>?g6y7qh-%8!t0E;a%xnVHK4EC9(5i9;c#&09==1dzMF_<+M}{E> zEo@7LOjD0eJ|I#oBl?z-hu%R@mk<=PP-XD3iz=OlEFKFm zTt|F-nV=sDREEY52Q>~NFKh~cC$44|6ut^zGr+R>g2kqTEWjQh5Gq%dHstioAsA+0 z>dwemIf^n^tOJG-Y>xVbd5p-w0%$$V-g{knjw5tYaA3L~Wm=Dj8dG%EO9|m!%Fxq; zquB|9w++jS&EAkU2e_y%8|>xtC^j2QT7|VzFO$RofF92Q{0Ia0gr!{2N~uJkcouI( z+~q69@FJdEVBlOdONNk^;QM=ky1h-xBA@DF^FpZWgUVRjK7_!Ol?L^I=)6VO(Ytm;-?@4I-M*m+uRS}X;ui6vCTjdrR9uqR~D z;f$@#x_qMRYl`wt+&r#UHwv`wTnlV?Mky5&e&MQM?dosa%ACuiCRmPW`hxT-&!%c$ zNP;3{9u>lsr!ij{%x$U{k1;8!`G@LLreMOIL=grGmIhlBkY+xirB$u|;Xy19F_36j zSEY`T@8^V5o+}g;*5M; z@TYcnGAd}N7mHsm&kE5O%;5S(na363{*1UrKP#)EXrK*#0IMIcNWNTT=5XgR9f-GK-@eX$_XDn zB66kh{{YC~WEA|)CuRr38h~Fw#`#tJx&n^GyHL$F#L&J1zTv37IC&tE-r{n1 z1<7;&0O#PKIE=*yAokQPVNYg#McB*yz#u?tA-BWk8mZS%4SUd*hnU-x32_@X)j%z0 zwlpxj%ZN}vxI{vhUl4DU*7iWBCPTzh3p(2?q8wd77pI_cE93>9UowRi8onZ=;<$Gw zk-i{UuOBc4jxg~AV8Ly8jR03y=)sFavFcNZR-{!X2ZWpIDRmk`{CTMOigA03tsBN2 z+G4@Tja7=yZ%jdFU+SpivsxOI+~?8mdgbY@Q^123cf3t7h{oT zS2z!dLlZfcz*i43Lu!j>9Y7#cqW2oAyAhhv7Vvc{8^RWDD0QdGcTa_kL`N;gL9@t+ zVN2*Tff=nl!4ybtQ5~x3*oz_+)Ny%e#m|SziC`JN*ah`bO`q1mzM{8TE64axM=8_0rLtbW^#pLrdlnbr0JpfY+AbU4lAzh6Hm5A}B7~*u|?9e~7Rxz&T;vpJatWG*r^RIP29 zT$qKxhLfL6uD&4mD%HdR=MALO+0{d>N$lURWHw5Oy7MffVjh+%ik)l-s~+Hu;P7(G zj;jc~$BYc2>HrV}RH*cFbA88Dup;7pQs%VlxRUbLfEGTQ|*1?5daX z2M)pN4iPvGWn6aD`G}^wCY{f4r9%`F6`$LbVkPu z@lC8Lhrg)Q?BePJ7`enyEnT6k^?KKmF$GXiLz4@O%|HgIczny+pnl-Ohuqd3FVp5S z0)ugN0&#Nit!>m+0OW1e92spE3jV-Ktb2hfgz(K;GT&*UQNEdI*GXMRUm`%w>NPjH zayw9l`z0?<@^6AWHnw{UoUBG|9zrv;6ZU{e8lZ{>Qz@2w(AmbQuz!$D5~E)c9ND25 zg}N2DD}_V7h=fCL+~HZL*;2~}oi8%#bkkm7F&3S$?#Igbj!QxZio1gl^hy9eRVy}u z-;9;42akzFcWT&xUG=j2fy46`Z!dC$wx=-+pw-K_$}XjsCB7hNEj-4qc`^S0U>nQw z;e$dnh?UZ>BKHLYOQGT|%$1;Tk}dF4k?uLN(}(~Ie7K0?C9gLEmq!^WB%FtdqN;4* ziAAn~g^``G5`gt`#;fc(m_(iEfDm5ahG8AecMd$KiiP1`Vgjolh*UMUmE0>Ch;WY* zh!0UMWQDu;3|3OTtjGn>e-;hytAQ(dZx2wq&D+g!0IE&P7C}|%2ivhUPAu>}K;h_8 zPLdxn3#zIhfOleir+dAzogOBiw9s(mjgirtS0)44XVN4FV6IoD~k* zDm1SM{lS67KOzuO)50~mSB@o3+l%f1dyx_J55r}s4W%Lu6!8f2f7k)QD5XLdJ;GW6 z(ddPiV>`plMkxS2!LODVEWR(yO6DA+`Jpowbr}*>Rnw$6Y4R|; zSVY$DY_>~{t;Nc(!6=#l>Io8su2X~%0m_s>YrkYjDxRVTahDQ>?-wrx(+4beoJ~jD z4^UbZcy|;F(Z*U5RpwO1YYz~L<_*7bj2zZQ76@J{1jM)DC`Dg3#Q+}H68``#8}DP$ zR9p`R-%wX)wp|RN;y$xiRn*hnF6E&~&4QsvxP#Rd%tHp@{>~U^B8?8K%oGBmRqSwI zsO(DKS%tj5aw8(NIb0yhZW7OaCz3OGJbX<Fwbusb)sc?jtN>+5{20b>fxv=W zE6&Rxs^{r~4ujMvq_0xwsyl#!v%Og>N~Sjllev347x^|l?&hW-Mt#_id8}kaE1&<_ DF~T7U literal 0 HcmV?d00001 diff --git a/apps/www/public/images/customers/logos/euka.png b/apps/www/public/images/customers/logos/euka.png new file mode 100644 index 0000000000000000000000000000000000000000..d60da3dc2a6456c7c88b16608403143492a4953b GIT binary patch literal 3373 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 Date: Wed, 30 Apr 2025 11:04:30 -0700 Subject: [PATCH 25/32] fix(docs): Fix example file name in auth/quickstarts/nextjs.mdx (#35364) fix: Fix example file name in auth/quickstarts/nextjs.mdx --- apps/docs/content/guides/auth/quickstarts/nextjs.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/content/guides/auth/quickstarts/nextjs.mdx b/apps/docs/content/guides/auth/quickstarts/nextjs.mdx index 90b0bec7d26..69e6a29ef44 100644 --- a/apps/docs/content/guides/auth/quickstarts/nextjs.mdx +++ b/apps/docs/content/guides/auth/quickstarts/nextjs.mdx @@ -52,7 +52,7 @@ hideToc: true - Rename `.env.local.example` to `.env.local` and populate with [your project's URL and Anon Key](https://supabase.com/dashboard/project/_/settings/api). + Rename `.env.example` to `.env.local` and populate with [your project's URL and Anon Key](https://supabase.com/dashboard/project/_/settings/api). From 0797b10bf34b679c45cfcf1f3715c5ac52983c11 Mon Sep 17 00:00:00 2001 From: samrose Date: Wed, 30 Apr 2025 15:48:57 -0400 Subject: [PATCH 26/32] docs: small correction in plan_filter doc for usage (#35379) * docs: small correction in plan_filter doc for usage * docs: fixing proofread comments * docs: prettier formmatting fix * docs: omit unnecessary `load` command --------- Co-authored-by: Bobbie Soedirgo --- .../guides/database/extensions/pg_plan_filter.mdx | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/apps/docs/content/guides/database/extensions/pg_plan_filter.mdx b/apps/docs/content/guides/database/extensions/pg_plan_filter.mdx index fe490755a0b..17e8782a234 100644 --- a/apps/docs/content/guides/database/extensions/pg_plan_filter.mdx +++ b/apps/docs/content/guides/database/extensions/pg_plan_filter.mdx @@ -8,19 +8,9 @@ description: 'Block queries over a total cost limit' ## Enable the extension -`pg_plan_filter` can be enabled on a per connection basis: +The extension is already enabled by default via `shared_preload_libraries` setting. -{/* prettier-ignore */} -```sql -load 'plan_filter'; -``` - -or for all connections: - -{/* prettier-ignore */} -```sql -alter database some_db set session_preload_libraries = 'plan_filter'; -``` +You can follow the instructions below. ## API @@ -66,7 +56,6 @@ Now we can choose a `statement_cost_filter` value between the total cost for the {/* prettier-ignore */} ```sql -load 'plan_filter'; set plan_filter.statement_cost_limit = 50; -- between 2.49 and 135.0 select * from book where id = 1; From c638255cb225a9c244e6364281ed9a39f6bbb092 Mon Sep 17 00:00:00 2001 From: Wen Bo Xie <5532241+w3b6x9@users.noreply.github.com> Date: Wed, 30 Apr 2025 16:54:53 -0400 Subject: [PATCH 27/32] fix: change notion to linear team mapping (#35363) --- apps/docs/components/Feedback/Feedback.tsx | 4 ++-- .../components/Feedback/Feedback.utils.ts | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/docs/components/Feedback/Feedback.tsx b/apps/docs/components/Feedback/Feedback.tsx index 16771ca6e5b..e2aa841aa43 100644 --- a/apps/docs/components/Feedback/Feedback.tsx +++ b/apps/docs/components/Feedback/Feedback.tsx @@ -18,7 +18,7 @@ import { Button, cn } from 'ui' import { IS_PLATFORM } from '~/lib/constants' import { useSendFeedbackMutation } from '~/lib/fetch/feedback' import { useSendTelemetryEvent } from '~/lib/telemetry' -import { getNotionTeam, getSanitizedTabParams } from './Feedback.utils' +import { getLinearTeam, getSanitizedTabParams } from './Feedback.utils' import { type FeedbackFields, FeedbackModal } from './FeedbackModal' const FeedbackButton = forwardRef< @@ -134,7 +134,7 @@ function Feedback({ className }: { className?: string }) { title, // @ts-expect-error -- can't click this button without having a state.response isHelpful: state.response === 'yes', - team: getNotionTeam(pathname), + team: getLinearTeam(pathname), }) setModalOpen(false) refocusButton() diff --git a/apps/docs/components/Feedback/Feedback.utils.ts b/apps/docs/components/Feedback/Feedback.utils.ts index cf9ff4c989a..2fa626ce14d 100644 --- a/apps/docs/components/Feedback/Feedback.utils.ts +++ b/apps/docs/components/Feedback/Feedback.utils.ts @@ -3,8 +3,8 @@ import { pick } from 'lodash' /** * Gets the Notion team to send feedback to based on the pathname. */ -const getNotionTeam = (pathname: string) => { - const DEFAULT_TEAM = 'team-docs' +const getLinearTeam = (pathname: string) => { + const DEFAULT_TEAM = 'Docs' // Pathname has format `/guides/(team)/**` const pathParts = pathname.split('/') @@ -13,26 +13,26 @@ const getNotionTeam = (pathname: string) => { switch (pathParts[2]) { case 'database': - return 'team-postgres' + return 'Postgres' case 'auth': - return 'team-auth' + return 'Auth' case 'storage': - return 'team-storage' + return 'Storage' case 'functions': - return 'team-functions' + return 'Functions' case 'realtime': - return 'team-realtime' + return 'Realtime' case 'ai': - return 'team-ai' + return 'AI' case 'local-development': case 'self-hosting': case 'deployment': - return 'team-dev-workflows' + return 'Dev Workflows' case 'integrations': - return 'team-api' + return 'API' case 'platform': case 'monitoring-troubleshooting': - return 'team-infra' + return 'Infra' default: return DEFAULT_TEAM } @@ -51,4 +51,4 @@ const getSanitizedTabParams = () => { return pick(Object.fromEntries(searchParams.entries()), queryGroups) } -export { getNotionTeam, getSanitizedTabParams } +export { getLinearTeam, getSanitizedTabParams } From 84ff291a090d538409dc4c208b45778a44e70c0d Mon Sep 17 00:00:00 2001 From: Charis <26616127+charislam@users.noreply.github.com> Date: Wed, 30 Apr 2025 20:24:09 -0400 Subject: [PATCH 28/32] fix(docs): wrappers tags (#35386) Pull wrappers tags from tag docs_v*, not tag v* --- .../extensions/wrappers/[[...slug]]/page.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx b/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx index f5c6712dfcc..8028d6c5625 100644 --- a/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx +++ b/apps/docs/app/guides/database/extensions/wrappers/[[...slug]]/page.tsx @@ -28,10 +28,10 @@ const externalSite = 'https://supabase.github.io/wrappers' type TagQueryResponse = { repository: { - releases: { + refs: { nodes: | { - tagName: string + name: string }[] | null pageInfo: { @@ -45,16 +45,17 @@ type TagQueryResponse = { const tagQuery = ` query TagQuery($owner: String!, $name: String!, $after: String) { repository(owner: $owner, name: $name) { - releases( + refs( + refPrefix: "refs/tags/", orderBy: { - field: CREATED_AT, + field: TAG_COMMIT_DATE, direction: DESC }, first: 5, after: $after ) { nodes { - tagName + name } pageInfo { hasNextPage @@ -69,7 +70,7 @@ async function getLatestRelease(after: string | null = null) { try { const { repository: { - releases: { + refs: { nodes, pageInfo: { hasNextPage, endCursor }, }, @@ -85,7 +86,7 @@ async function getLatestRelease(after: string | null = null) { }) return ( - nodes?.find((node) => node?.tagName?.match(/^v\d+\.\d+\.\d+/)).tagName ?? + nodes?.find((node) => node?.name?.match(/^docs_v\d+\.\d+\.\d+/))?.name ?? (hasNextPage && endCursor ? await getLatestRelease(endCursor) : null) ) } catch (error) { From 38173acc34677d0823fca5f2b99f13f87fb48e64 Mon Sep 17 00:00:00 2001 From: Pamela Chia Date: Thu, 1 May 2025 19:24:39 +0800 Subject: [PATCH 29/32] fix: error when telemetry groups unknown (#35403) --- packages/common/telemetry.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/common/telemetry.tsx b/packages/common/telemetry.tsx index 29278df8cfa..51e9295aee1 100644 --- a/packages/common/telemetry.tsx +++ b/packages/common/telemetry.tsx @@ -172,7 +172,14 @@ export function sendTelemetryEvent(API_URL: string, event: TelemetryEvent, pathn ...getSharedTelemetryData(pathname), action: event.action, custom_properties: 'properties' in event ? event.properties : {}, - groups: 'groups' in event ? event.groups : {}, + groups: 'groups' in event ? { ...event.groups } : {}, + } + + if (body.groups?.project === 'Unknown') { + delete body.groups.project + if (body.groups?.organization === 'Unknown') { + delete body.groups + } } return post(`${ensurePlatformSuffix(API_URL)}/telemetry/event`, body, { From 2ca5a1285428501cb6ea27ea836a049b10e87e18 Mon Sep 17 00:00:00 2001 From: yujiseok <83855636+yujiseok@users.noreply.github.com> Date: Fri, 2 May 2025 00:25:22 +0900 Subject: [PATCH 30/32] fix broken Apple REST API link in auth-apple.mdx (#35404) - replace broken Sign in with Apple REST API link --- apps/docs/content/guides/auth/social-login/auth-apple.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/docs/content/guides/auth/social-login/auth-apple.mdx b/apps/docs/content/guides/auth/social-login/auth-apple.mdx index 0316b186396..aa2faa122e1 100644 --- a/apps/docs/content/guides/auth/social-login/auth-apple.mdx +++ b/apps/docs/content/guides/auth/social-login/auth-apple.mdx @@ -14,7 +14,7 @@ To support Sign in with Apple, you need to configure the [Apple provider in the There are three general ways to use Sign in with Apple, depending on the application you're trying to build: - Sign in on the web or in web-based apps - - Using an OAuth flow initiated by Supabase Auth using the [Sign in with Apple REST API](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api). + - Using an OAuth flow initiated by Supabase Auth using the [Sign in with Apple REST API](https://developer.apple.com/documentation/signinwithapplerestapi). - Using [Sign in with Apple JS](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_js) directly in the browser, usually suitable for websites. - Sign in natively inside iOS, macOS, watchOS or tvOS apps using [Apple's Authentication Services](https://developer.apple.com/documentation/authenticationservices) @@ -35,7 +35,7 @@ When developing with Expo, you can test Sign in with Apple via the Expo Go app, Sign in with Apple's OAuth flow is designed for web or browser based sign in methods. It can be used on web-based apps as well as websites, though some users can benefit by using Sign in with Apple JS directly. - Behind the scenes, Supabase Auth uses the [REST APIs](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api) provided by Apple. + Behind the scenes, Supabase Auth uses the [REST APIs](https://developer.apple.com/documentation/signinwithapplerestapi) provided by Apple. <$Partial path="create_client_snippet.mdx" /> From 7307f96879923efd538c3e5c5aca47114beca1ba Mon Sep 17 00:00:00 2001 From: Julian S <64325884+hijuliancode@users.noreply.github.com> Date: Thu, 1 May 2025 10:32:15 -0500 Subject: [PATCH 31/32] Update declarative-database-schemas.mdx (#35397) The alert "Make sure your local database is stopped before diffing your schema." appears after completing the diff step. It would be more useful to display it before performing the diff, to ensure that "supabase stop" is executed beforehand and not afterward. --- .../declarative-database-schemas.mdx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/docs/content/guides/local-development/declarative-database-schemas.mdx b/apps/docs/content/guides/local-development/declarative-database-schemas.mdx index 26eccaca0a3..37b2b085342 100644 --- a/apps/docs/content/guides/local-development/declarative-database-schemas.mdx +++ b/apps/docs/content/guides/local-development/declarative-database-schemas.mdx @@ -42,6 +42,12 @@ create table "employees" ( + + +Make sure your local database is stopped before diffing your schema. + + + @@ -64,12 +70,6 @@ supabase db diff -f create_employees_table - - -Make sure your local database is stopped before diffing your schema. - - - From 263b0c1c5b734c75e4315f99f444d8ed3209f524 Mon Sep 17 00:00:00 2001 From: Terry Sutton Date: Thu, 1 May 2025 13:16:13 -0230 Subject: [PATCH 32/32] Fix pointer events (#35408) --- apps/studio/components/ui/SchemaSelector.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/studio/components/ui/SchemaSelector.tsx b/apps/studio/components/ui/SchemaSelector.tsx index a8103d0d931..7a0293ca219 100644 --- a/apps/studio/components/ui/SchemaSelector.tsx +++ b/apps/studio/components/ui/SchemaSelector.tsx @@ -123,7 +123,7 @@ const SchemaSelector = ({