diff --git a/invokeai/frontend/web/package.json b/invokeai/frontend/web/package.json index 743cb1e09d..0bf236ee38 100644 --- a/invokeai/frontend/web/package.json +++ b/invokeai/frontend/web/package.json @@ -99,7 +99,6 @@ "roarr": "^7.21.0", "serialize-error": "^11.0.3", "socket.io-client": "^4.7.4", - "type-fest": "^4.10.2", "use-debounce": "^10.0.0", "use-image": "^1.1.1", "uuid": "^9.0.1", @@ -146,6 +145,7 @@ "ts-toolbelt": "^9.6.0", "tsafe": "^1.6.6", "typescript": "^5.3.3", + "utility-types": "^3.11.0", "vite": "^5.1.3", "vite-plugin-css-injected-by-js": "^3.4.0", "vite-plugin-dts": "^3.7.2", diff --git a/invokeai/frontend/web/pnpm-lock.yaml b/invokeai/frontend/web/pnpm-lock.yaml index 9e873102e6..f2abdd87bf 100644 --- a/invokeai/frontend/web/pnpm-lock.yaml +++ b/invokeai/frontend/web/pnpm-lock.yaml @@ -152,9 +152,6 @@ dependencies: socket.io-client: specifier: ^4.7.4 version: 4.7.4 - type-fest: - specifier: ^4.10.2 - version: 4.10.2 use-debounce: specifier: ^10.0.0 version: 10.0.0(react@18.2.0) @@ -271,6 +268,9 @@ devDependencies: typescript: specifier: ^5.3.3 version: 5.3.3 + utility-types: + specifier: ^3.11.0 + version: 3.11.0 vite: specifier: ^5.1.3 version: 5.1.3(@types/node@20.11.19) @@ -13827,11 +13827,6 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} - /type-fest@4.10.2: - resolution: {integrity: sha512-anpAG63wSpdEbLwOqH8L84urkL6PiVIov3EMmgIhhThevh9aiMQov+6Btx0wldNcvm4wV+e2/Rt1QdDwKHFbHw==} - engines: {node: '>=16'} - dev: false - /type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -14152,6 +14147,11 @@ packages: which-typed-array: 1.1.14 dev: true + /utility-types@3.11.0: + resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==} + engines: {node: '>= 4'} + dev: true + /utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} diff --git a/invokeai/frontend/web/src/app/store/store.ts b/invokeai/frontend/web/src/app/store/store.ts index 270662c3d2..16f1632d88 100644 --- a/invokeai/frontend/web/src/app/store/store.ts +++ b/invokeai/frontend/web/src/app/store/store.ts @@ -3,6 +3,7 @@ import { autoBatchEnhancer, combineReducers, configureStore } from '@reduxjs/too import { logger } from 'app/logging/logger'; import { idbKeyValDriver } from 'app/store/enhancers/reduxRemember/driver'; import { errorHandler } from 'app/store/enhancers/reduxRemember/errors'; +import type { JSONObject } from 'common/types'; import { canvasPersistConfig, canvasSlice } from 'features/canvas/store/canvasSlice'; import { changeBoardModalSlice } from 'features/changeBoardModal/store/slice'; import { @@ -32,7 +33,6 @@ import { rememberEnhancer, rememberReducer } from 'redux-remember'; import { serializeError } from 'serialize-error'; import { api } from 'services/api'; import { authToastMiddleware } from 'services/api/authToastMiddleware'; -import type { JsonObject } from 'type-fest'; import { STORAGE_PREFIX } from './constants'; import { actionSanitizer } from './middleware/devtools/actionSanitizer'; @@ -125,7 +125,7 @@ const unserialize: UnserializeFunction = (data, key) => { { persistedData: parsed, rehydratedData: transformed, - diff: diff(parsed, transformed) as JsonObject, // this is always serializable + diff: diff(parsed, transformed) as JSONObject, // this is always serializable }, `Rehydrated slice "${key}"` ); diff --git a/invokeai/frontend/web/src/common/types.ts b/invokeai/frontend/web/src/common/types.ts new file mode 100644 index 0000000000..29a411788d --- /dev/null +++ b/invokeai/frontend/web/src/common/types.ts @@ -0,0 +1,7 @@ +export type JSONValue = string | number | boolean | null | JSONValue[] | { [key: string]: JSONValue }; + +export interface JSONObject { + [k: string]: JSONValue; +} + +export interface JSONArray extends Array {} diff --git a/invokeai/frontend/web/src/features/nodes/util/graph/metadata.ts b/invokeai/frontend/web/src/features/nodes/util/graph/metadata.ts index 781ce57ebc..c48f54d191 100644 --- a/invokeai/frontend/web/src/features/nodes/util/graph/metadata.ts +++ b/invokeai/frontend/web/src/features/nodes/util/graph/metadata.ts @@ -1,5 +1,5 @@ +import type { JSONObject } from 'common/types'; import type { CoreMetadataInvocation, NonNullableGraph } from 'services/api/types'; -import type { JsonObject } from 'type-fest'; import { METADATA } from './constants'; @@ -30,7 +30,7 @@ export const addCoreMetadataNode = ( export const upsertMetadata = ( graph: NonNullableGraph, - metadata: Partial | JsonObject + metadata: Partial | JSONObject ): void => { const metadataNode = graph.nodes[METADATA] as CoreMetadataInvocation | undefined; diff --git a/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts b/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts index 5096e588b0..b402f2f8af 100644 --- a/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts +++ b/invokeai/frontend/web/src/features/nodes/util/workflow/validateWorkflow.ts @@ -1,3 +1,4 @@ +import type { JSONObject } from 'common/types'; import { parseify } from 'common/util/serialize'; import type { InvocationTemplate } from 'features/nodes/types/invocation'; import type { WorkflowV3 } from 'features/nodes/types/workflow'; @@ -5,14 +6,13 @@ import { isWorkflowInvocationNode } from 'features/nodes/types/workflow'; import { getNeedsUpdate } from 'features/nodes/util/node/nodeUpdate'; import { t } from 'i18next'; import { keyBy } from 'lodash-es'; -import type { JsonObject } from 'type-fest'; import { parseAndMigrateWorkflow } from './migrations'; type WorkflowWarning = { message: string; issues?: string[]; - data: JsonObject; + data: JSONObject; }; type ValidateWorkflowResult = { diff --git a/invokeai/frontend/web/src/services/api/types.ts b/invokeai/frontend/web/src/services/api/types.ts index aaa70a2684..d561173337 100644 --- a/invokeai/frontend/web/src/services/api/types.ts +++ b/invokeai/frontend/web/src/services/api/types.ts @@ -2,7 +2,7 @@ import type { UseToastOptions } from '@invoke-ai/ui-library'; import type { EntityState } from '@reduxjs/toolkit'; import type { components, paths } from 'services/api/schema'; import type { O } from 'ts-toolbelt'; -import type { SetRequired } from 'type-fest'; +import type { Overwrite } from 'utility-types'; export type S = components['schemas']; @@ -61,28 +61,60 @@ export type IPAdapterField = S['IPAdapterField']; // Model Configs // TODO(MM2): Can we make key required in the pydantic model? -type KeyRequired = SetRequired; -export type LoRAConfig = KeyRequired; +export type LoRAModelConfig = S['LoRAConfig']; // TODO(MM2): Can we rename this from Vae -> VAE -export type VAEConfig = KeyRequired | KeyRequired; -export type ControlNetConfig = - | KeyRequired - | KeyRequired; -export type IPAdapterConfig = KeyRequired; +export type VAEModelConfig = S['VaeCheckpointConfig'] | S['VaeDiffusersConfig']; +export type ControlNetModelConfig = S['ControlNetDiffusersConfig'] | S['ControlNetCheckpointConfig']; +export type IPAdapterModelConfig = S['IPAdapterConfig']; // TODO(MM2): Can we rename this to T2IAdapterConfig -export type T2IAdapterConfig = KeyRequired; -export type TextualInversionConfig = KeyRequired; -export type DiffusersModelConfig = KeyRequired; -export type CheckpointModelConfig = KeyRequired; +export type T2IAdapterModelConfig = S['T2IConfig']; +export type TextualInversionModelConfig = S['TextualInversionConfig']; +export type DiffusersModelConfig = S['MainDiffusersConfig']; +export type CheckpointModelConfig = S['MainCheckpointConfig']; export type MainModelConfig = DiffusersModelConfig | CheckpointModelConfig; +export type RefinerMainModelConfig = Overwrite; +export type NonRefinerMainModelConfig = Overwrite; export type AnyModelConfig = - | LoRAConfig - | VAEConfig - | ControlNetConfig - | IPAdapterConfig - | T2IAdapterConfig - | TextualInversionConfig - | MainModelConfig; + | LoRAModelConfig + | VAEModelConfig + | ControlNetModelConfig + | IPAdapterModelConfig + | T2IAdapterModelConfig + | TextualInversionModelConfig + | RefinerMainModelConfig + | NonRefinerMainModelConfig; + +export const isLoRAModelConfig = (config: AnyModelConfig): config is LoRAModelConfig => { + return config.type === 'lora'; +}; + +export const isVAEModelConfig = (config: AnyModelConfig): config is VAEModelConfig => { + return config.type === 'vae'; +}; + +export const isControlNetModelConfig = (config: AnyModelConfig): config is ControlNetModelConfig => { + return config.type === 'controlnet'; +}; + +export const isIPAdapterModelConfig = (config: AnyModelConfig): config is IPAdapterModelConfig => { + return config.type === 'ip_adapter'; +}; + +export const isT2IAdapterModelConfig = (config: AnyModelConfig): config is T2IAdapterModelConfig => { + return config.type === 't2i_adapter'; +}; + +export const isTextualInversionModelConfig = (config: AnyModelConfig): config is TextualInversionModelConfig => { + return config.type === 'embedding'; +}; + +export const isNonRefinerMainModelConfig = (config: AnyModelConfig): config is NonRefinerMainModelConfig => { + return config.type === 'main' && config.base !== 'sdxl-refiner'; +}; + +export const isRefinerMainModelModelConfig = (config: AnyModelConfig): config is RefinerMainModelConfig => { + return config.type === 'main' && config.base === 'sdxl-refiner'; +}; export type MergeModelConfig = S['Body_merge']; export type ImportModelConfig = S['Body_import_model'];