mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
fix(ui): fix canvas crash by rolling back swagger-parser (#3611)
The node polyfills needed to run the `swagger-parser` library (used to dereference the OpenAPI schema) cause the canvas tab to immediately crash when the package build is used in another react application. I'm sure this is fixable but it's not clear what is causing the issue and troubleshooting is very time consuming. Selectively rolling back the implementation of `swagger-parser`.
This commit is contained in:
commit
2b6c9c93e0
@ -3,12 +3,10 @@ import { visualizer } from 'rollup-plugin-visualizer';
|
|||||||
import { PluginOption, UserConfig } from 'vite';
|
import { PluginOption, UserConfig } from 'vite';
|
||||||
import eslint from 'vite-plugin-eslint';
|
import eslint from 'vite-plugin-eslint';
|
||||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||||
import { nodePolyfills } from 'vite-plugin-node-polyfills';
|
|
||||||
|
|
||||||
export const commonPlugins: UserConfig['plugins'] = [
|
export const commonPlugins: UserConfig['plugins'] = [
|
||||||
react(),
|
react(),
|
||||||
eslint(),
|
eslint(),
|
||||||
tsconfigPaths(),
|
tsconfigPaths(),
|
||||||
visualizer() as unknown as PluginOption,
|
visualizer() as unknown as PluginOption,
|
||||||
nodePolyfills(),
|
|
||||||
];
|
];
|
||||||
|
@ -53,7 +53,6 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apidevtools/swagger-parser": "^10.1.0",
|
|
||||||
"@chakra-ui/anatomy": "^2.1.1",
|
"@chakra-ui/anatomy": "^2.1.1",
|
||||||
"@chakra-ui/icons": "^2.0.19",
|
"@chakra-ui/icons": "^2.0.19",
|
||||||
"@chakra-ui/react": "^2.7.1",
|
"@chakra-ui/react": "^2.7.1",
|
||||||
@ -155,7 +154,6 @@
|
|||||||
"vite-plugin-css-injected-by-js": "^3.1.1",
|
"vite-plugin-css-injected-by-js": "^3.1.1",
|
||||||
"vite-plugin-dts": "^2.3.0",
|
"vite-plugin-dts": "^2.3.0",
|
||||||
"vite-plugin-eslint": "^1.8.1",
|
"vite-plugin-eslint": "^1.8.1",
|
||||||
"vite-plugin-node-polyfills": "^0.9.0",
|
|
||||||
"vite-tsconfig-paths": "^4.2.0",
|
"vite-tsconfig-paths": "^4.2.0",
|
||||||
"yarn": "^1.22.19"
|
"yarn": "^1.22.19"
|
||||||
}
|
}
|
||||||
|
@ -333,7 +333,7 @@ export type TypeHints = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type InvocationSchemaExtra = {
|
export type InvocationSchemaExtra = {
|
||||||
output: OpenAPIV3.SchemaObject; // the output of the invocation
|
output: OpenAPIV3.ReferenceObject; // the output of the invocation
|
||||||
ui?: {
|
ui?: {
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
type_hints?: TypeHints;
|
type_hints?: TypeHints;
|
||||||
|
@ -349,11 +349,21 @@ export const getFieldType = (
|
|||||||
|
|
||||||
if (typeHints && name in typeHints) {
|
if (typeHints && name in typeHints) {
|
||||||
rawFieldType = typeHints[name];
|
rawFieldType = typeHints[name];
|
||||||
} else if (!schemaObject.type && schemaObject.allOf) {
|
} else if (!schemaObject.type) {
|
||||||
// if schemaObject has no type, then it should have one of allOf
|
// if schemaObject has no type, then it should have one of allOf, anyOf, oneOf
|
||||||
rawFieldType =
|
if (schemaObject.allOf) {
|
||||||
(schemaObject.allOf[0] as OpenAPIV3.SchemaObject).title ??
|
rawFieldType = refObjectToFieldType(
|
||||||
'Missing Field Type';
|
schemaObject.allOf![0] as OpenAPIV3.ReferenceObject
|
||||||
|
);
|
||||||
|
} else if (schemaObject.anyOf) {
|
||||||
|
rawFieldType = refObjectToFieldType(
|
||||||
|
schemaObject.anyOf![0] as OpenAPIV3.ReferenceObject
|
||||||
|
);
|
||||||
|
} else if (schemaObject.oneOf) {
|
||||||
|
rawFieldType = refObjectToFieldType(
|
||||||
|
schemaObject.oneOf![0] as OpenAPIV3.ReferenceObject
|
||||||
|
);
|
||||||
|
}
|
||||||
} else if (schemaObject.enum) {
|
} else if (schemaObject.enum) {
|
||||||
rawFieldType = 'enum';
|
rawFieldType = 'enum';
|
||||||
} else if (schemaObject.type) {
|
} else if (schemaObject.type) {
|
||||||
|
@ -5,154 +5,127 @@ import {
|
|||||||
InputFieldTemplate,
|
InputFieldTemplate,
|
||||||
InvocationSchemaObject,
|
InvocationSchemaObject,
|
||||||
InvocationTemplate,
|
InvocationTemplate,
|
||||||
|
isInvocationSchemaObject,
|
||||||
OutputFieldTemplate,
|
OutputFieldTemplate,
|
||||||
} from '../types/types';
|
} from '../types/types';
|
||||||
import { buildInputFieldTemplate, getFieldType } from './fieldTemplateBuilders';
|
import {
|
||||||
import { O } from 'ts-toolbelt';
|
buildInputFieldTemplate,
|
||||||
|
buildOutputFieldTemplates,
|
||||||
// recursively exclude all properties of type U from T
|
} from './fieldTemplateBuilders';
|
||||||
type DeepExclude<T, U> = T extends U
|
|
||||||
? never
|
|
||||||
: T extends object
|
|
||||||
? {
|
|
||||||
[K in keyof T]: DeepExclude<T[K], U>;
|
|
||||||
}
|
|
||||||
: T;
|
|
||||||
|
|
||||||
// The schema from swagger-parser is dereferenced, and we know `components` and `components.schemas` exist
|
|
||||||
type DereferencedOpenAPIDocument = DeepExclude<
|
|
||||||
O.Required<OpenAPIV3.Document, 'schemas' | 'components', 'deep'>,
|
|
||||||
OpenAPIV3.ReferenceObject
|
|
||||||
>;
|
|
||||||
|
|
||||||
const RESERVED_FIELD_NAMES = ['id', 'type', 'is_intermediate'];
|
const RESERVED_FIELD_NAMES = ['id', 'type', 'is_intermediate'];
|
||||||
|
|
||||||
const invocationDenylist = ['Graph', 'InvocationMeta'];
|
const invocationDenylist = ['Graph', 'InvocationMeta'];
|
||||||
|
|
||||||
const nodeFilter = (
|
export const parseSchema = (openAPI: OpenAPIV3.Document) => {
|
||||||
schema: DereferencedOpenAPIDocument['components']['schemas'][string],
|
|
||||||
key: string
|
|
||||||
) =>
|
|
||||||
key.includes('Invocation') &&
|
|
||||||
!key.includes('InvocationOutput') &&
|
|
||||||
!invocationDenylist.some((denylistItem) => key.includes(denylistItem));
|
|
||||||
|
|
||||||
export const parseSchema = (openAPI: DereferencedOpenAPIDocument) => {
|
|
||||||
// filter out non-invocation schemas, plus some tricky invocations for now
|
// filter out non-invocation schemas, plus some tricky invocations for now
|
||||||
const filteredSchemas = filter(openAPI.components.schemas, nodeFilter);
|
const filteredSchemas = filter(
|
||||||
|
openAPI.components!.schemas,
|
||||||
|
(schema, key) =>
|
||||||
|
key.includes('Invocation') &&
|
||||||
|
!key.includes('InvocationOutput') &&
|
||||||
|
!invocationDenylist.some((denylistItem) => key.includes(denylistItem))
|
||||||
|
) as (OpenAPIV3.ReferenceObject | InvocationSchemaObject)[];
|
||||||
|
|
||||||
const invocations = filteredSchemas.reduce<
|
const invocations = filteredSchemas.reduce<
|
||||||
Record<string, InvocationTemplate>
|
Record<string, InvocationTemplate>
|
||||||
>((acc, s) => {
|
>((acc, schema) => {
|
||||||
// cast to InvocationSchemaObject, we know the shape
|
// only want SchemaObjects
|
||||||
const schema = s as InvocationSchemaObject;
|
if (isInvocationSchemaObject(schema)) {
|
||||||
|
const type = schema.properties.type.default;
|
||||||
|
|
||||||
const type = schema.properties.type.default;
|
const title = schema.ui?.title ?? schema.title.replace('Invocation', '');
|
||||||
|
|
||||||
const title = schema.ui?.title ?? schema.title.replace('Invocation', '');
|
const typeHints = schema.ui?.type_hints;
|
||||||
|
|
||||||
const typeHints = schema.ui?.type_hints;
|
const inputs: Record<string, InputFieldTemplate> = {};
|
||||||
|
|
||||||
const inputs: Record<string, InputFieldTemplate> = {};
|
if (type === 'collect') {
|
||||||
|
const itemProperty = schema.properties[
|
||||||
if (type === 'collect') {
|
'item'
|
||||||
// Special handling for the Collect node
|
] as InvocationSchemaObject;
|
||||||
const itemProperty = schema.properties['item'] as InvocationSchemaObject;
|
// Handle the special Collect node
|
||||||
inputs.item = {
|
inputs.item = {
|
||||||
type: 'item',
|
type: 'item',
|
||||||
name: 'item',
|
|
||||||
description: itemProperty.description ?? '',
|
|
||||||
title: 'Collection Item',
|
|
||||||
inputKind: 'connection',
|
|
||||||
inputRequirement: 'always',
|
|
||||||
default: undefined,
|
|
||||||
};
|
|
||||||
} else if (type === 'iterate') {
|
|
||||||
// Special handling for the Iterate node
|
|
||||||
const itemProperty = schema.properties[
|
|
||||||
'collection'
|
|
||||||
] as InvocationSchemaObject;
|
|
||||||
|
|
||||||
inputs.collection = {
|
|
||||||
type: 'array',
|
|
||||||
name: 'collection',
|
|
||||||
title: itemProperty.title ?? '',
|
|
||||||
default: [],
|
|
||||||
description: itemProperty.description ?? '',
|
|
||||||
inputRequirement: 'always',
|
|
||||||
inputKind: 'connection',
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// All other nodes
|
|
||||||
reduce(
|
|
||||||
schema.properties,
|
|
||||||
(inputsAccumulator, property, propertyName) => {
|
|
||||||
if (
|
|
||||||
// `type` and `id` are not valid inputs/outputs
|
|
||||||
!RESERVED_FIELD_NAMES.includes(propertyName) &&
|
|
||||||
isSchemaObject(property)
|
|
||||||
) {
|
|
||||||
const field: InputFieldTemplate | undefined =
|
|
||||||
buildInputFieldTemplate(property, propertyName, typeHints);
|
|
||||||
|
|
||||||
if (field) {
|
|
||||||
inputsAccumulator[propertyName] = field;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return inputsAccumulator;
|
|
||||||
},
|
|
||||||
inputs
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let outputs: Record<string, OutputFieldTemplate>;
|
|
||||||
|
|
||||||
if (type === 'iterate') {
|
|
||||||
// Special handling for the Iterate node output
|
|
||||||
const iterationOutput =
|
|
||||||
openAPI.components.schemas['IterateInvocationOutput'];
|
|
||||||
|
|
||||||
outputs = {
|
|
||||||
item: {
|
|
||||||
name: 'item',
|
name: 'item',
|
||||||
title: iterationOutput.title ?? '',
|
description: itemProperty.description ?? '',
|
||||||
description: iterationOutput.description ?? '',
|
title: 'Collection Item',
|
||||||
|
inputKind: 'connection',
|
||||||
|
inputRequirement: 'always',
|
||||||
|
default: undefined,
|
||||||
|
};
|
||||||
|
} else if (type === 'iterate') {
|
||||||
|
const itemProperty = schema.properties[
|
||||||
|
'collection'
|
||||||
|
] as InvocationSchemaObject;
|
||||||
|
|
||||||
|
inputs.collection = {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
},
|
name: 'collection',
|
||||||
|
title: itemProperty.title ?? '',
|
||||||
|
default: [],
|
||||||
|
description: itemProperty.description ?? '',
|
||||||
|
inputRequirement: 'always',
|
||||||
|
inputKind: 'connection',
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// All other nodes
|
||||||
|
reduce(
|
||||||
|
schema.properties,
|
||||||
|
(inputsAccumulator, property, propertyName) => {
|
||||||
|
if (
|
||||||
|
// `type` and `id` are not valid inputs/outputs
|
||||||
|
!RESERVED_FIELD_NAMES.includes(propertyName) &&
|
||||||
|
isSchemaObject(property)
|
||||||
|
) {
|
||||||
|
const field: InputFieldTemplate | undefined =
|
||||||
|
buildInputFieldTemplate(property, propertyName, typeHints);
|
||||||
|
|
||||||
|
if (field) {
|
||||||
|
inputsAccumulator[propertyName] = field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return inputsAccumulator;
|
||||||
|
},
|
||||||
|
inputs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawOutput = (schema as InvocationSchemaObject).output;
|
||||||
|
|
||||||
|
let outputs: Record<string, OutputFieldTemplate>;
|
||||||
|
|
||||||
|
// some special handling is needed for collect, iterate and range nodes
|
||||||
|
if (type === 'iterate') {
|
||||||
|
// this is guaranteed to be a SchemaObject
|
||||||
|
const iterationOutput = openAPI.components!.schemas![
|
||||||
|
'IterateInvocationOutput'
|
||||||
|
] as OpenAPIV3.SchemaObject;
|
||||||
|
|
||||||
|
outputs = {
|
||||||
|
item: {
|
||||||
|
name: 'item',
|
||||||
|
title: iterationOutput.title ?? '',
|
||||||
|
description: iterationOutput.description ?? '',
|
||||||
|
type: 'array',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
outputs = buildOutputFieldTemplates(rawOutput, openAPI, typeHints);
|
||||||
|
}
|
||||||
|
|
||||||
|
const invocation: InvocationTemplate = {
|
||||||
|
title,
|
||||||
|
type,
|
||||||
|
tags: schema.ui?.tags ?? [],
|
||||||
|
description: schema.description ?? '',
|
||||||
|
inputs,
|
||||||
|
outputs,
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
// All other node outputs
|
|
||||||
outputs = reduce(
|
|
||||||
schema.output.properties as OpenAPIV3.SchemaObject,
|
|
||||||
(outputsAccumulator, property, propertyName) => {
|
|
||||||
if (!['type', 'id'].includes(propertyName)) {
|
|
||||||
const fieldType = getFieldType(property, propertyName, typeHints);
|
|
||||||
|
|
||||||
outputsAccumulator[propertyName] = {
|
Object.assign(acc, { [type]: invocation });
|
||||||
name: propertyName,
|
|
||||||
title: property.title ?? '',
|
|
||||||
description: property.description ?? '',
|
|
||||||
type: fieldType,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return outputsAccumulator;
|
|
||||||
},
|
|
||||||
{} as Record<string, OutputFieldTemplate>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const invocation: InvocationTemplate = {
|
|
||||||
title,
|
|
||||||
type,
|
|
||||||
tags: schema.ui?.tags ?? [],
|
|
||||||
description: schema.description ?? '',
|
|
||||||
inputs,
|
|
||||||
outputs,
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.assign(acc, { [type]: invocation });
|
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import SwaggerParser from '@apidevtools/swagger-parser';
|
|
||||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||||
import { log } from 'app/logging/useLogger';
|
import { log } from 'app/logging/useLogger';
|
||||||
import { OpenAPIV3 } from 'openapi-types';
|
|
||||||
|
|
||||||
const schemaLog = log.child({ namespace: 'schema' });
|
const schemaLog = log.child({ namespace: 'schema' });
|
||||||
|
|
||||||
@ -29,12 +27,13 @@ export const receivedOpenAPISchema = createAsyncThunk(
|
|||||||
'nodes/receivedOpenAPISchema',
|
'nodes/receivedOpenAPISchema',
|
||||||
async (_, { dispatch, rejectWithValue }) => {
|
async (_, { dispatch, rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
const dereferencedSchema = (await SwaggerParser.dereference(
|
const response = await fetch(`openapi.json`);
|
||||||
'openapi.json'
|
const openAPISchema = await response.json();
|
||||||
)) as OpenAPIV3.Document;
|
|
||||||
|
schemaLog.info({ openAPISchema }, 'Received OpenAPI schema');
|
||||||
|
|
||||||
const schemaJSON = JSON.parse(
|
const schemaJSON = JSON.parse(
|
||||||
JSON.stringify(dereferencedSchema, getCircularReplacer())
|
JSON.stringify(openAPISchema, getCircularReplacer())
|
||||||
);
|
);
|
||||||
|
|
||||||
return schemaJSON;
|
return schemaJSON;
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user