From 1c41db75f8e89e83665a1e028e9ab4f483d96ec2 Mon Sep 17 00:00:00 2001 From: Chenlei Hu Date: Sat, 12 Apr 2025 11:34:02 -0400 Subject: [PATCH] [Type] Enable strict schema type for setting entries (#3425) --- src/schemas/apiSchema.ts | 229 +++++++++++----------- src/stores/workspaceStore.ts | 8 +- tests-ui/tests/store/settingStore.test.ts | 8 +- 3 files changed, 126 insertions(+), 119 deletions(-) diff --git a/src/schemas/apiSchema.ts b/src/schemas/apiSchema.ts index 7c4e19979..0fd0383f5 100644 --- a/src/schemas/apiSchema.ts +++ b/src/schemas/apiSchema.ts @@ -332,119 +332,122 @@ const zNodeBadgeMode = z.enum( Object.values(NodeBadgeMode) as [string, ...string[]] ) -const zSettings = z.record(z.any()).and( - z - .object({ - 'Comfy.ColorPalette': z.string(), - 'Comfy.CustomColorPalettes': colorPalettesSchema, - 'Comfy.ConfirmClear': z.boolean(), - 'Comfy.DevMode': z.boolean(), - 'Comfy.Workflow.ShowMissingNodesWarning': z.boolean(), - 'Comfy.Workflow.ShowMissingModelsWarning': z.boolean(), - 'Comfy.DisableFloatRounding': z.boolean(), - 'Comfy.DisableSliders': z.boolean(), - 'Comfy.DOMClippingEnabled': z.boolean(), - 'Comfy.EditAttention.Delta': z.number(), - 'Comfy.EnableTooltips': z.boolean(), - 'Comfy.EnableWorkflowViewRestore': z.boolean(), - 'Comfy.FloatRoundingPrecision': z.number(), - 'Comfy.Graph.CanvasInfo': z.boolean(), - 'Comfy.Graph.CanvasMenu': z.boolean(), - 'Comfy.Graph.CtrlShiftZoom': z.boolean(), - 'Comfy.Graph.LinkMarkers': z.nativeEnum(LinkMarkerShape), - 'Comfy.Graph.ZoomSpeed': z.number(), - 'Comfy.Group.DoubleClickTitleToEdit': z.boolean(), - 'Comfy.GroupSelectedNodes.Padding': z.number(), - 'Comfy.InvertMenuScrolling': z.boolean(), - 'Comfy.Locale': z.string(), - 'Comfy.Logging.Enabled': z.boolean(), - 'Comfy.NodeLibrary.Bookmarks': z.array(z.string()), - 'Comfy.NodeLibrary.Bookmarks.V2': z.array(z.string()), - 'Comfy.NodeLibrary.BookmarksCustomization': z.record( - z.string(), - zBookmarkCustomization - ), - 'Comfy.LinkRelease.Action': zLinkReleaseTriggerAction, - 'Comfy.LinkRelease.ActionShift': zLinkReleaseTriggerAction, - 'Comfy.ModelLibrary.AutoLoadAll': z.boolean(), - 'Comfy.ModelLibrary.NameFormat': z.enum(['filename', 'title']), - 'Comfy.NodeSearchBoxImpl.NodePreview': z.boolean(), - 'Comfy.NodeSearchBoxImpl': z.enum(['default', 'simple']), - 'Comfy.NodeSearchBoxImpl.ShowCategory': z.boolean(), - 'Comfy.NodeSearchBoxImpl.ShowIdName': z.boolean(), - 'Comfy.NodeSearchBoxImpl.ShowNodeFrequency': z.boolean(), - 'Comfy.NodeSuggestions.number': z.number(), - 'Comfy.Node.BypassAllLinksOnDelete': z.boolean(), - 'Comfy.Node.Opacity': z.number(), - 'Comfy.Node.MiddleClickRerouteNode': z.boolean(), - 'Comfy.Node.ShowDeprecated': z.boolean(), - 'Comfy.Node.ShowExperimental': z.boolean(), - 'Comfy.Pointer.ClickBufferTime': z.number(), - 'Comfy.Pointer.ClickDrift': z.number(), - 'Comfy.Pointer.DoubleClickTime': z.number(), - 'Comfy.PreviewFormat': z.string(), - 'Comfy.PromptFilename': z.boolean(), - 'Comfy.Sidebar.Location': z.enum(['left', 'right']), - 'Comfy.Sidebar.Size': z.enum(['small', 'normal']), - 'Comfy.SwitchUser': z.any(), - 'Comfy.SnapToGrid.GridSize': z.number(), - 'Comfy.TextareaWidget.FontSize': z.number(), - 'Comfy.TextareaWidget.Spellcheck': z.boolean(), - 'Comfy.UseNewMenu': z.enum(['Disabled', 'Top', 'Bottom']), - 'Comfy.TreeExplorer.ItemPadding': z.number(), - 'Comfy.Validation.Workflows': z.boolean(), - 'Comfy.Validation.NodeDefs': z.boolean(), - 'Comfy.Workflow.SortNodeIdOnSave': z.boolean(), - 'Comfy.Queue.ImageFit': z.enum(['contain', 'cover']), - 'Comfy.Workflow.WorkflowTabsPosition': z.enum([ - 'Sidebar', - 'Topbar', - 'Topbar (2nd-row)' - ]), - 'Comfy.Node.DoubleClickTitleToEdit': z.boolean(), - 'Comfy.WidgetControlMode': z.enum(['before', 'after']), - 'Comfy.Window.UnloadConfirmation': z.boolean(), - 'Comfy.NodeBadge.NodeSourceBadgeMode': zNodeBadgeMode, - 'Comfy.NodeBadge.NodeIdBadgeMode': zNodeBadgeMode, - 'Comfy.NodeBadge.NodeLifeCycleBadgeMode': zNodeBadgeMode, - 'Comfy.QueueButton.BatchCountLimit': z.number(), - 'Comfy.Queue.MaxHistoryItems': z.number(), - 'Comfy.Keybinding.UnsetBindings': z.array(zKeybinding), - 'Comfy.Keybinding.NewBindings': z.array(zKeybinding), - 'Comfy.Extension.Disabled': z.array(z.string()), - 'Comfy.Settings.ExtensionPanel': z.boolean(), - 'Comfy.LinkRenderMode': z.number(), - 'Comfy.Node.AutoSnapLinkToSlot': z.boolean(), - 'Comfy.Node.SnapHighlightsNode': z.boolean(), - 'Comfy.Server.ServerConfigValues': z.record(z.string(), z.any()), - 'Comfy.Server.LaunchArgs': z.record(z.string(), z.string()), - 'LiteGraph.Canvas.MaximumFps': z.number(), - 'Comfy.Workflow.ConfirmDelete': z.boolean(), - 'Comfy.Workflow.AutoSaveDelay': z.number(), - 'Comfy.Workflow.AutoSave': z.enum(['off', 'after delay']), - 'Comfy.RerouteBeta': z.boolean(), - 'LiteGraph.Canvas.LowQualityRenderingZoomThreshold': z.number(), - 'Comfy.Canvas.SelectionToolbox': z.boolean(), - 'LiteGraph.Node.TooltipDelay': z.number(), - 'Comfy.ComfirmClear': z.boolean(), - 'LiteGraph.ContextMenu.Scaling': z.boolean(), - 'LiteGraph.Reroute.SplineOffset': z.number(), - 'Comfy.Toast.DisableReconnectingToast': z.boolean(), - 'Comfy.Workflow.Persist': z.boolean(), - 'Comfy.TutorialCompleted': z.boolean(), - 'Comfy.Node.AllowImageSizeDraw': z.boolean(), - 'Comfy-Desktop.AutoUpdate': z.boolean(), - 'Comfy-Desktop.SendStatistics': z.boolean(), - 'Comfy-Desktop.WindowStyle': z.string(), - 'Comfy-Desktop.UV.PythonInstallMirror': z.string(), - 'Comfy-Desktop.UV.PypiInstallMirror': z.string(), - 'Comfy-Desktop.UV.TorchInstallMirror': z.string(), - 'Comfy.MaskEditor.UseNewEditor': z.boolean(), - 'Comfy.MaskEditor.BrushAdjustmentSpeed': z.number(), - 'Comfy.MaskEditor.UseDominantAxis': z.boolean() - }) - .optional() -) +const zSettings = z.object({ + 'Comfy.ColorPalette': z.string(), + 'Comfy.CustomColorPalettes': colorPalettesSchema, + 'Comfy.ConfirmClear': z.boolean(), + 'Comfy.DevMode': z.boolean(), + 'Comfy.Workflow.ShowMissingNodesWarning': z.boolean(), + 'Comfy.Workflow.ShowMissingModelsWarning': z.boolean(), + 'Comfy.DisableFloatRounding': z.boolean(), + 'Comfy.DisableSliders': z.boolean(), + 'Comfy.DOMClippingEnabled': z.boolean(), + 'Comfy.EditAttention.Delta': z.number(), + 'Comfy.EnableTooltips': z.boolean(), + 'Comfy.EnableWorkflowViewRestore': z.boolean(), + 'Comfy.FloatRoundingPrecision': z.number(), + 'Comfy.Graph.CanvasInfo': z.boolean(), + 'Comfy.Graph.CanvasMenu': z.boolean(), + 'Comfy.Graph.CtrlShiftZoom': z.boolean(), + 'Comfy.Graph.LinkMarkers': z.nativeEnum(LinkMarkerShape), + 'Comfy.Graph.ZoomSpeed': z.number(), + 'Comfy.Group.DoubleClickTitleToEdit': z.boolean(), + 'Comfy.GroupSelectedNodes.Padding': z.number(), + 'Comfy.InvertMenuScrolling': z.boolean(), + 'Comfy.Locale': z.string(), + 'Comfy.Logging.Enabled': z.boolean(), + 'Comfy.NodeLibrary.Bookmarks': z.array(z.string()), + 'Comfy.NodeLibrary.Bookmarks.V2': z.array(z.string()), + 'Comfy.NodeLibrary.BookmarksCustomization': z.record( + z.string(), + zBookmarkCustomization + ), + 'Comfy.LinkRelease.Action': zLinkReleaseTriggerAction, + 'Comfy.LinkRelease.ActionShift': zLinkReleaseTriggerAction, + 'Comfy.ModelLibrary.AutoLoadAll': z.boolean(), + 'Comfy.ModelLibrary.NameFormat': z.enum(['filename', 'title']), + 'Comfy.NodeSearchBoxImpl.NodePreview': z.boolean(), + 'Comfy.NodeSearchBoxImpl': z.enum(['default', 'simple']), + 'Comfy.NodeSearchBoxImpl.ShowCategory': z.boolean(), + 'Comfy.NodeSearchBoxImpl.ShowIdName': z.boolean(), + 'Comfy.NodeSearchBoxImpl.ShowNodeFrequency': z.boolean(), + 'Comfy.NodeSuggestions.number': z.number(), + 'Comfy.Node.BypassAllLinksOnDelete': z.boolean(), + 'Comfy.Node.Opacity': z.number(), + 'Comfy.Node.MiddleClickRerouteNode': z.boolean(), + 'Comfy.Node.ShowDeprecated': z.boolean(), + 'Comfy.Node.ShowExperimental': z.boolean(), + 'Comfy.Pointer.ClickBufferTime': z.number(), + 'Comfy.Pointer.ClickDrift': z.number(), + 'Comfy.Pointer.DoubleClickTime': z.number(), + 'Comfy.PreviewFormat': z.string(), + 'Comfy.PromptFilename': z.boolean(), + 'Comfy.Sidebar.Location': z.enum(['left', 'right']), + 'Comfy.Sidebar.Size': z.enum(['small', 'normal']), + 'Comfy.SwitchUser': z.any(), + 'Comfy.SnapToGrid.GridSize': z.number(), + 'Comfy.TextareaWidget.FontSize': z.number(), + 'Comfy.TextareaWidget.Spellcheck': z.boolean(), + 'Comfy.UseNewMenu': z.enum(['Disabled', 'Top', 'Bottom']), + 'Comfy.TreeExplorer.ItemPadding': z.number(), + 'Comfy.Validation.Workflows': z.boolean(), + 'Comfy.Validation.NodeDefs': z.boolean(), + 'Comfy.Workflow.SortNodeIdOnSave': z.boolean(), + 'Comfy.Queue.ImageFit': z.enum(['contain', 'cover']), + 'Comfy.Workflow.WorkflowTabsPosition': z.enum([ + 'Sidebar', + 'Topbar', + 'Topbar (2nd-row)' + ]), + 'Comfy.Node.DoubleClickTitleToEdit': z.boolean(), + 'Comfy.WidgetControlMode': z.enum(['before', 'after']), + 'Comfy.Window.UnloadConfirmation': z.boolean(), + 'Comfy.NodeBadge.NodeSourceBadgeMode': zNodeBadgeMode, + 'Comfy.NodeBadge.NodeIdBadgeMode': zNodeBadgeMode, + 'Comfy.NodeBadge.NodeLifeCycleBadgeMode': zNodeBadgeMode, + 'Comfy.QueueButton.BatchCountLimit': z.number(), + 'Comfy.Queue.MaxHistoryItems': z.number(), + 'Comfy.Keybinding.UnsetBindings': z.array(zKeybinding), + 'Comfy.Keybinding.NewBindings': z.array(zKeybinding), + 'Comfy.Extension.Disabled': z.array(z.string()), + 'Comfy.Settings.ExtensionPanel': z.boolean(), + 'Comfy.LinkRenderMode': z.number(), + 'Comfy.Node.AutoSnapLinkToSlot': z.boolean(), + 'Comfy.Node.SnapHighlightsNode': z.boolean(), + 'Comfy.Server.ServerConfigValues': z.record(z.string(), z.any()), + 'Comfy.Server.LaunchArgs': z.record(z.string(), z.string()), + 'LiteGraph.Canvas.MaximumFps': z.number(), + 'Comfy.Workflow.ConfirmDelete': z.boolean(), + 'Comfy.Workflow.AutoSaveDelay': z.number(), + 'Comfy.Workflow.AutoSave': z.enum(['off', 'after delay']), + 'Comfy.RerouteBeta': z.boolean(), + 'LiteGraph.Canvas.LowQualityRenderingZoomThreshold': z.number(), + 'Comfy.Canvas.SelectionToolbox': z.boolean(), + 'LiteGraph.Node.TooltipDelay': z.number(), + 'Comfy.ComfirmClear': z.boolean(), + 'LiteGraph.ContextMenu.Scaling': z.boolean(), + 'LiteGraph.Reroute.SplineOffset': z.number(), + 'Comfy.Toast.DisableReconnectingToast': z.boolean(), + 'Comfy.Workflow.Persist': z.boolean(), + 'Comfy.TutorialCompleted': z.boolean(), + 'Comfy.Node.AllowImageSizeDraw': z.boolean(), + 'Comfy-Desktop.AutoUpdate': z.boolean(), + 'Comfy-Desktop.SendStatistics': z.boolean(), + 'Comfy-Desktop.WindowStyle': z.string(), + 'Comfy-Desktop.UV.PythonInstallMirror': z.string(), + 'Comfy-Desktop.UV.PypiInstallMirror': z.string(), + 'Comfy-Desktop.UV.TorchInstallMirror': z.string(), + 'Comfy.MaskEditor.UseNewEditor': z.boolean(), + 'Comfy.MaskEditor.BrushAdjustmentSpeed': z.number(), + 'Comfy.MaskEditor.UseDominantAxis': z.boolean(), + 'pysssss.SnapToGrid': z.boolean(), + /** VHS setting is used for queue video preview support. */ + 'VHS.AdvancedPreviews': z.boolean(), + /** Settings used for testing */ + 'test.setting': z.any(), + 'main.sub.setting.name': z.any(), + 'single.setting': z.any() +}) export type EmbeddingsResponse = z.infer export type ExtensionsResponse = z.infer diff --git a/src/stores/workspaceStore.ts b/src/stores/workspaceStore.ts index 6fedd3568..33086ef65 100644 --- a/src/stores/workspaceStore.ts +++ b/src/stores/workspaceStore.ts @@ -1,6 +1,7 @@ import { defineStore } from 'pinia' import { computed, ref } from 'vue' +import type { Settings } from '@/schemas/apiSchema' import { useColorPaletteService } from '@/services/colorPaletteService' import { useDialogService } from '@/services/dialogService' import type { SidebarTabExtension, ToastManager } from '@/types/extensionTypes' @@ -31,8 +32,11 @@ export const useWorkspaceStore = defineStore('workspace', () => { const sidebarTab = computed(() => useSidebarTabStore()) const setting = computed(() => ({ settings: useSettingStore().settingsById, - get: useSettingStore().get, - set: useSettingStore().set + // Allow generic key access to settings as custom nodes may add their + // own settings which is not tracked by the `Setting` schema. + get: (key: string) => useSettingStore().get(key as keyof Settings), + set: (key: string, value: unknown) => + useSettingStore().set(key as keyof Settings, value) })) const workflow = computed(() => useWorkflowStore()) const colorPalette = useColorPaletteService() diff --git a/tests-ui/tests/store/settingStore.test.ts b/tests-ui/tests/store/settingStore.test.ts index 3abb67d48..c9de09ec3 100644 --- a/tests-ui/tests/store/settingStore.test.ts +++ b/tests-ui/tests/store/settingStore.test.ts @@ -189,11 +189,11 @@ describe('useSettingStore', () => { expect(storedValue).toEqual({ foo: 'bar', nested: { value: 123 } }) }) - it('should prevent mutations of retrieved objects', () => { + it('should prevent mutations of retrieved objects', async () => { const initialValue = { foo: 'bar', nested: { value: 123 } } // Set initial value - store.set('test.setting', initialValue) + await store.set('test.setting', initialValue) // Get the value and try to mutate it const retrievedValue = store.get('test.setting') @@ -230,11 +230,11 @@ describe('useSettingStore', () => { expect(storedValue).toEqual([1, 2, { value: 3 }]) }) - it('should prevent mutations of retrieved arrays', () => { + it('should prevent mutations of retrieved arrays', async () => { const initialArray = [1, 2, { value: 3 }] // Set initial value - store.set('test.setting', initialArray) + await store.set('test.setting', initialArray) // Get the value and try to mutate it const retrievedArray = store.get('test.setting')