mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
chore(deps): upgrade valtio to v2 (#48031)
Audited all proxy()/useSnapshot() usage against the v1→v2 migration guide; no breaking changes apply (no reused proxy() inputs, no promise-valued state, all consumers already client components). <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Updated the Valtio dependency to a newer version for improved compatibility. * **Bug Fixes** * Improved AI assistant persistence in IndexedDB so chat sessions reliably save (while keeping only the most recent 20 messages per chat). * Hardened tabs restoration from storage to fall back to fresh defaults when data is missing, invalid, or fails validation. * **Refactor** * Switched multiple studio panels to use fresh initial-state factories for initialization and reset reliability. * Updated advisor state so the derived notification filter count is no longer exposed. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
1 parent
06c2039d0b
commit
dc3c8684cc
9 files changed
+70
-80
No files matched your search
+3
-3
@@ -52,13 +52,13 @@ function createBucketFilePickerState({
|
||||
|
||||
export type BucketFilePickerState = ReturnType<typeof createBucketFilePickerState>
|
||||
|
||||
const DEFAULT_STATE_CONFIG = {
|
||||
const createDefaultStateConfig = () => ({
|
||||
bucket: {} as Bucket,
|
||||
maxFiles: 1 as const,
|
||||
}
|
||||
})
|
||||
|
||||
const BucketFilePickerStateContext = createContext<BucketFilePickerState>(
|
||||
createBucketFilePickerState(DEFAULT_STATE_CONFIG)
|
||||
createBucketFilePickerState(createDefaultStateConfig())
|
||||
)
|
||||
|
||||
export const BucketFilePickerStateContextProvider = ({
|
||||
|
||||
@@ -4,7 +4,7 @@ export type AdvisorTab = 'all' | 'security' | 'performance' | 'messages'
|
||||
export type AdvisorSeverity = 'critical' | 'warning' | 'info'
|
||||
export type AdvisorItemSource = 'lint' | 'notification' | 'signal'
|
||||
|
||||
const initialState = {
|
||||
const createInitialState = () => ({
|
||||
activeTab: 'all' as AdvisorTab,
|
||||
severityFilters: ['critical', 'warning'] as AdvisorSeverity[],
|
||||
selectedItemId: undefined as string | undefined,
|
||||
@@ -12,13 +12,10 @@ const initialState = {
|
||||
// Notification filters
|
||||
notificationFilterStatuses: [] as string[],
|
||||
notificationFilterPriorities: [] as string[],
|
||||
get numNotificationFiltersApplied() {
|
||||
return [...this.notificationFilterStatuses, ...this.notificationFilterPriorities].length
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
export const advisorState = proxy({
|
||||
...initialState,
|
||||
...createInitialState(),
|
||||
setActiveTab(tab: AdvisorTab) {
|
||||
advisorState.activeTab = tab
|
||||
},
|
||||
@@ -68,7 +65,7 @@ export const advisorState = proxy({
|
||||
advisorState.notificationFilterPriorities = []
|
||||
},
|
||||
reset() {
|
||||
Object.assign(advisorState, initialState)
|
||||
Object.assign(advisorState, createInitialState())
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ type StoredAiAssistantState = {
|
||||
model?: AssistantModel
|
||||
}
|
||||
|
||||
const INITIAL_AI_ASSISTANT: AiAssistantData = {
|
||||
const createInitialAiAssistantData = (): AiAssistantData => ({
|
||||
initialInput: '',
|
||||
sqlSnippets: undefined,
|
||||
suggestions: undefined,
|
||||
@@ -97,7 +97,7 @@ const INITIAL_AI_ASSISTANT: AiAssistantData = {
|
||||
activeChatId: undefined,
|
||||
model: undefined,
|
||||
context: {},
|
||||
}
|
||||
})
|
||||
|
||||
const DB_NAME = 'ai-assistant-db'
|
||||
const DB_VERSION = 1
|
||||
@@ -210,7 +210,7 @@ async function tryMigrateFromLocalStorage(
|
||||
projectRef: projectRef,
|
||||
activeChatId: parsedFromLocalStorage.activeChatId,
|
||||
chats: parsedFromLocalStorage.chats,
|
||||
model: parsedFromLocalStorage.model ?? INITIAL_AI_ASSISTANT.model,
|
||||
model: parsedFromLocalStorage.model ?? createInitialAiAssistantData().model,
|
||||
}
|
||||
} else {
|
||||
console.warn('Data in localStorage is not in the expected format, ignoring.')
|
||||
@@ -356,7 +356,7 @@ function createChatInstance(
|
||||
|
||||
export const createAiAssistantState = (): AiAssistantState => {
|
||||
// Initialize with defaults, loading happens asynchronously in the provider
|
||||
const initialState = { ...INITIAL_AI_ASSISTANT }
|
||||
const initialState = createInitialAiAssistantData()
|
||||
|
||||
const state: AiAssistantState = proxy({
|
||||
...initialState, // Spread initial values directly
|
||||
@@ -369,7 +369,7 @@ export const createAiAssistantState = (): AiAssistantState => {
|
||||
},
|
||||
|
||||
resetAiAssistantPanel: () => {
|
||||
Object.assign(state, INITIAL_AI_ASSISTANT)
|
||||
Object.assign(state, createInitialAiAssistantData())
|
||||
},
|
||||
|
||||
setModel: (model: AssistantModel) => {
|
||||
@@ -414,10 +414,11 @@ export const createAiAssistantState = (): AiAssistantState => {
|
||||
}
|
||||
|
||||
// Update non-chat related state based on options, falling back to current state, then initial
|
||||
state.initialInput = options?.initialInput ?? INITIAL_AI_ASSISTANT.initialInput
|
||||
state.sqlSnippets = options?.sqlSnippets ?? INITIAL_AI_ASSISTANT.sqlSnippets
|
||||
state.suggestions = options?.suggestions ?? INITIAL_AI_ASSISTANT.suggestions
|
||||
state.tables = options?.tables ?? INITIAL_AI_ASSISTANT.tables
|
||||
const initialAiAssistantData = createInitialAiAssistantData()
|
||||
state.initialInput = options?.initialInput ?? initialAiAssistantData.initialInput
|
||||
state.sqlSnippets = options?.sqlSnippets ?? initialAiAssistantData.sqlSnippets
|
||||
state.suggestions = options?.suggestions ?? initialAiAssistantData.suggestions
|
||||
state.tables = options?.tables ?? initialAiAssistantData.tables
|
||||
|
||||
return chatId
|
||||
},
|
||||
@@ -561,7 +562,7 @@ export const createAiAssistantState = (): AiAssistantState => {
|
||||
state.model =
|
||||
storedModel && isKnownAssistantModelId(storedModel)
|
||||
? storedModel
|
||||
: INITIAL_AI_ASSISTANT.model
|
||||
: createInitialAiAssistantData().model
|
||||
|
||||
// Reset sync guards on any support chats (can't be mid-sync after reload)
|
||||
Object.values(state.chats).forEach((chat) => {
|
||||
@@ -698,22 +699,25 @@ export const AiAssistantStateContextProvider = ({ children }: PropsWithChildren)
|
||||
|
||||
const unsubscribe = subscribe(state, () => {
|
||||
const snap = snapshot(state)
|
||||
|
||||
// Prepare state for IndexedDB
|
||||
const stateToSave: StoredAiAssistantState = {
|
||||
projectRef: project?.ref,
|
||||
activeChatId: snap.activeChatId,
|
||||
model: snap.model,
|
||||
chats: snap.chats
|
||||
? Object.entries(snap.chats).reduce((acc, [chatId, chat]) => {
|
||||
// Limit messages before saving
|
||||
return {
|
||||
...acc,
|
||||
[chatId]: {
|
||||
...chat,
|
||||
messages: chat.messages?.slice(-20) || [],
|
||||
},
|
||||
}
|
||||
}, {})
|
||||
? (Object.entries(snap.chats) as Array<[string, ChatSession]>).reduce(
|
||||
(acc, [chatId, chat]) => {
|
||||
return {
|
||||
...acc,
|
||||
[chatId]: {
|
||||
...chat,
|
||||
messages: chat.messages?.slice(-20) || [],
|
||||
},
|
||||
}
|
||||
},
|
||||
{} as Record<string, ChatSession>
|
||||
)
|
||||
: {},
|
||||
}
|
||||
debouncedSaveAiState(stateToSave)
|
||||
|
||||
@@ -24,7 +24,7 @@ type EditorPanelState = {
|
||||
pendingReset: boolean
|
||||
}
|
||||
|
||||
const initialState: EditorPanelState = {
|
||||
const createInitialState = (): EditorPanelState => ({
|
||||
value: safeSql``,
|
||||
templates: [],
|
||||
results: undefined,
|
||||
@@ -33,10 +33,10 @@ const initialState: EditorPanelState = {
|
||||
onChange: undefined,
|
||||
activeSnippetId: null,
|
||||
pendingReset: false,
|
||||
}
|
||||
})
|
||||
|
||||
export const editorPanelState = proxy({
|
||||
...initialState,
|
||||
...createInitialState(),
|
||||
setValue(value: DisplayableSqlFragment) {
|
||||
editorPanelState.value = value
|
||||
editorPanelState.onChange?.(value)
|
||||
@@ -65,7 +65,7 @@ export const editorPanelState = proxy({
|
||||
editorPanelState.pendingReset = true
|
||||
},
|
||||
reset() {
|
||||
Object.assign(editorPanelState, initialState)
|
||||
Object.assign(editorPanelState, createInitialState())
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
@@ -36,16 +36,16 @@ type SidebarManagerState = SidebarManagerData & {
|
||||
toggleMaximise: () => void
|
||||
}
|
||||
|
||||
const INITIAL_SIDEBAR_MANAGER_DATA: SidebarManagerData = {
|
||||
const createInitialSidebarManagerData = (): SidebarManagerData => ({
|
||||
sidebars: {},
|
||||
activeSidebar: undefined,
|
||||
pendingSidebarOpen: undefined,
|
||||
isMaximised: false,
|
||||
}
|
||||
})
|
||||
|
||||
const createSidebarManagerState = () => {
|
||||
const state: SidebarManagerState = proxy({
|
||||
...INITIAL_SIDEBAR_MANAGER_DATA,
|
||||
...createInitialSidebarManagerData(),
|
||||
|
||||
registerSidebar(
|
||||
id: string,
|
||||
|
||||
@@ -1839,16 +1839,16 @@ function createStorageExplorerState({
|
||||
|
||||
export type StorageExplorerState = ReturnType<typeof createStorageExplorerState>
|
||||
|
||||
const DEFAULT_STATE_CONFIG = {
|
||||
const createDefaultStateConfig = () => ({
|
||||
projectRef: '',
|
||||
connectionString: '',
|
||||
resumableUploadUrl: '',
|
||||
clientEndpoint: '',
|
||||
bucket: {} as Bucket,
|
||||
}
|
||||
})
|
||||
|
||||
const StorageExplorerStateContext = createContext<StorageExplorerState>(
|
||||
createStorageExplorerState(DEFAULT_STATE_CONFIG)
|
||||
createStorageExplorerState(createDefaultStateConfig())
|
||||
)
|
||||
|
||||
export const StorageExplorerStateContextProvider = ({ children }: PropsWithChildren) => {
|
||||
@@ -1856,7 +1856,7 @@ export const StorageExplorerStateContextProvider = ({ children }: PropsWithChild
|
||||
const { data: bucket } = useSelectedBucket()
|
||||
const isPaused = project?.status === PROJECT_STATUS.INACTIVE
|
||||
|
||||
const [state, setState] = useState(() => createStorageExplorerState(DEFAULT_STATE_CONFIG))
|
||||
const [state, setState] = useState(() => createStorageExplorerState(createDefaultStateConfig()))
|
||||
const stateRef = useLatest(state)
|
||||
|
||||
const {
|
||||
|
||||
@@ -116,27 +116,27 @@ function getSavedRecentItems(ref: string): RecentItem[] {
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_TABS_STATE = {
|
||||
const createDefaultTabsState = () => ({
|
||||
activeTab: null as string | null,
|
||||
openTabs: [] as string[],
|
||||
tabsMap: {} as Record<string, Tab>,
|
||||
previewTabId: undefined as string | undefined,
|
||||
recentItems: [],
|
||||
}
|
||||
})
|
||||
const TABS_STORAGE_KEY = 'supabase_studio_tabs'
|
||||
const getTabsStorageKey = (ref: string) => `${TABS_STORAGE_KEY}_${ref}`
|
||||
|
||||
function getSavedTabs(ref: string) {
|
||||
if (!ref) return DEFAULT_TABS_STATE
|
||||
if (!ref) return createDefaultTabsState()
|
||||
|
||||
const stored = safeLocalStorage.getItem(getTabsStorageKey(ref))
|
||||
|
||||
if (!stored) return DEFAULT_TABS_STATE
|
||||
if (!stored) return createDefaultTabsState()
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(
|
||||
stored ?? JSON.stringify(DEFAULT_TABS_STATE)
|
||||
) as typeof DEFAULT_TABS_STATE
|
||||
const parsed = JSON.parse(stored ?? JSON.stringify(createDefaultTabsState())) as ReturnType<
|
||||
typeof createDefaultTabsState
|
||||
>
|
||||
|
||||
if (
|
||||
!parsed.openTabs ||
|
||||
@@ -144,12 +144,12 @@ function getSavedTabs(ref: string) {
|
||||
!parsed.tabsMap ||
|
||||
typeof parsed.tabsMap !== 'object'
|
||||
) {
|
||||
return DEFAULT_TABS_STATE
|
||||
return createDefaultTabsState()
|
||||
}
|
||||
|
||||
return parsed
|
||||
} catch (error) {
|
||||
return DEFAULT_TABS_STATE
|
||||
return createDefaultTabsState()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
+18
-29
@@ -88,8 +88,8 @@ catalogs:
|
||||
specifier: ~6.0.2
|
||||
version: 6.0.2
|
||||
valtio:
|
||||
specifier: ^1.12.0
|
||||
version: 1.12.0
|
||||
specifier: ^2.3.2
|
||||
version: 2.3.2
|
||||
vite:
|
||||
specifier: ^8.0.16
|
||||
version: 8.0.16
|
||||
@@ -581,7 +581,7 @@ importers:
|
||||
version: 14.0.0
|
||||
valtio:
|
||||
specifier: 'catalog:'
|
||||
version: 1.12.0(@types/react@19.2.14)(react@19.2.6)
|
||||
version: 2.3.2(@types/react@19.2.14)(react@19.2.6)
|
||||
yaml:
|
||||
specifier: ^2.8.1
|
||||
version: 2.9.0
|
||||
@@ -1229,7 +1229,7 @@ importers:
|
||||
version: 14.0.0
|
||||
valtio:
|
||||
specifier: 'catalog:'
|
||||
version: 1.12.0(@types/react@19.2.14)(react@19.2.6)
|
||||
version: 2.3.2(@types/react@19.2.14)(react@19.2.6)
|
||||
zod:
|
||||
specifier: 'catalog:'
|
||||
version: 3.25.76
|
||||
@@ -2180,7 +2180,7 @@ importers:
|
||||
version: 17.6.1(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
|
||||
valtio:
|
||||
specifier: 'catalog:'
|
||||
version: 1.12.0(@types/react@19.2.14)(react@19.2.6)
|
||||
version: 2.3.2(@types/react@19.2.14)(react@19.2.6)
|
||||
devDependencies:
|
||||
'@types/lodash':
|
||||
specifier: 4.17.5
|
||||
@@ -2742,7 +2742,7 @@ importers:
|
||||
version: 5.0.0
|
||||
valtio:
|
||||
specifier: 'catalog:'
|
||||
version: 1.12.0(@types/react@19.2.14)(react@19.2.6)
|
||||
version: 2.3.2(@types/react@19.2.14)(react@19.2.6)
|
||||
zod:
|
||||
specifier: 'catalog:'
|
||||
version: 3.25.76
|
||||
@@ -10605,11 +10605,6 @@ packages:
|
||||
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
derive-valtio@0.1.0:
|
||||
resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==}
|
||||
peerDependencies:
|
||||
valtio: '*'
|
||||
|
||||
destr@2.0.5:
|
||||
resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==}
|
||||
|
||||
@@ -14988,8 +14983,8 @@ packages:
|
||||
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
proxy-compare@2.5.1:
|
||||
resolution: {integrity: sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA==}
|
||||
proxy-compare@3.0.1:
|
||||
resolution: {integrity: sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q==}
|
||||
|
||||
proxy-from-env@1.1.0:
|
||||
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
|
||||
@@ -17042,12 +17037,12 @@ packages:
|
||||
validate.io-function@1.0.2:
|
||||
resolution: {integrity: sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==}
|
||||
|
||||
valtio@1.12.0:
|
||||
resolution: {integrity: sha512-co8NkCHeY0NsL0XsL/cSICt5VhTjwZlYT8mi50dYY5thx3r3w1D15A04Lvs9WL/y/Rf98vUKY5PAAJCTLHvkJw==}
|
||||
valtio@2.3.2:
|
||||
resolution: {integrity: sha512-YXhWQei9IN/ZDce9rhL3trCq9+vVq8M1gWmKVdP3YSZ2gxsmmNWVbxXwf9yG6ffu/dAvAD91nevg8xirGr4Dhg==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
peerDependencies:
|
||||
'@types/react': '>=16.8'
|
||||
react: '>=16.8'
|
||||
'@types/react': '>=18.0.0'
|
||||
react: '>=18.0.0'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
@@ -24143,7 +24138,7 @@ snapshots:
|
||||
srvx: 0.11.16
|
||||
tinyglobby: 0.2.17
|
||||
ufo: 1.6.4
|
||||
vitefu: 1.1.1(vite@8.0.16(@types/node@22.13.14)(jiti@2.7.0)(sass@1.77.4)(terser@5.39.0)(tsx@4.22.4)(yaml@2.9.0))
|
||||
vitefu: 1.1.1(vite@8.0.16(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.39.0)(tsx@4.22.4)(yaml@2.9.0))
|
||||
xmlbuilder2: 4.0.3
|
||||
zod: 4.4.3
|
||||
optionalDependencies:
|
||||
@@ -24176,7 +24171,7 @@ snapshots:
|
||||
srvx: 0.11.16
|
||||
tinyglobby: 0.2.17
|
||||
ufo: 1.6.4
|
||||
vitefu: 1.1.1(vite@8.0.16(@types/node@22.13.14)(jiti@2.7.0)(sass@1.77.4)(terser@5.39.0)(tsx@4.22.4)(yaml@2.9.0))
|
||||
vitefu: 1.1.1(vite@8.0.16(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.39.0)(tsx@4.22.4)(yaml@2.9.0))
|
||||
xmlbuilder2: 4.0.3
|
||||
zod: 4.4.3
|
||||
optionalDependencies:
|
||||
@@ -26886,10 +26881,6 @@ snapshots:
|
||||
|
||||
dequal@2.0.3: {}
|
||||
|
||||
derive-valtio@0.1.0(valtio@1.12.0(@types/react@19.2.14)(react@19.2.6)):
|
||||
dependencies:
|
||||
valtio: 1.12.0(@types/react@19.2.14)(react@19.2.6)
|
||||
|
||||
destr@2.0.5: {}
|
||||
|
||||
destroy@1.2.0: {}
|
||||
@@ -32412,7 +32403,7 @@ snapshots:
|
||||
forwarded: 0.2.0
|
||||
ipaddr.js: 1.9.1
|
||||
|
||||
proxy-compare@2.5.1: {}
|
||||
proxy-compare@3.0.1: {}
|
||||
|
||||
proxy-from-env@1.1.0: {}
|
||||
|
||||
@@ -34893,11 +34884,9 @@ snapshots:
|
||||
|
||||
validate.io-function@1.0.2: {}
|
||||
|
||||
valtio@1.12.0(@types/react@19.2.14)(react@19.2.6):
|
||||
valtio@2.3.2(@types/react@19.2.14)(react@19.2.6):
|
||||
dependencies:
|
||||
derive-valtio: 0.1.0(valtio@1.12.0(@types/react@19.2.14)(react@19.2.6))
|
||||
proxy-compare: 2.5.1
|
||||
use-sync-external-store: 1.2.0(react@19.2.6)
|
||||
proxy-compare: 3.0.1
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.14
|
||||
react: 19.2.6
|
||||
@@ -35129,7 +35118,7 @@ snapshots:
|
||||
tsx: 4.22.4
|
||||
yaml: 2.9.0
|
||||
|
||||
vitefu@1.1.1(vite@8.0.16(@types/node@22.13.14)(jiti@2.7.0)(sass@1.77.4)(terser@5.39.0)(tsx@4.22.4)(yaml@2.9.0)):
|
||||
vitefu@1.1.1(vite@8.0.16(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.39.0)(tsx@4.22.4)(yaml@2.9.0)):
|
||||
optionalDependencies:
|
||||
vite: 8.0.16(@types/node@22.13.14)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.77.4)(terser@5.39.0)(tsx@4.22.4)(yaml@2.9.0)
|
||||
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ catalog:
|
||||
tailwindcss: ^4.2.4
|
||||
tsx: ^4.22.0
|
||||
typescript: ~6.0.2
|
||||
valtio: ^1.12.0
|
||||
valtio: ^2.3.2
|
||||
vite: ^8.0.16
|
||||
vite-tsconfig-paths: ^6.1.1
|
||||
vitest: ^4.1.4
|
||||
|
||||
Reference in new issue
Block a user