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
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:
parent
877483093a
commit
30464f4fe1
@ -3,12 +3,10 @@ import { visualizer } from 'rollup-plugin-visualizer';
|
||||
import { PluginOption, UserConfig } from 'vite';
|
||||
import eslint from 'vite-plugin-eslint';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
import { nodePolyfills } from 'vite-plugin-node-polyfills';
|
||||
|
||||
export const commonPlugins: UserConfig['plugins'] = [
|
||||
react(),
|
||||
eslint(),
|
||||
tsconfigPaths(),
|
||||
visualizer() as unknown as PluginOption,
|
||||
nodePolyfills(),
|
||||
];
|
||||
|
@ -53,7 +53,6 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@apidevtools/swagger-parser": "^10.1.0",
|
||||
"@chakra-ui/anatomy": "^2.1.1",
|
||||
"@chakra-ui/icons": "^2.0.19",
|
||||
"@chakra-ui/react": "^2.7.1",
|
||||
@ -155,7 +154,6 @@
|
||||
"vite-plugin-css-injected-by-js": "^3.1.1",
|
||||
"vite-plugin-dts": "^2.3.0",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-node-polyfills": "^0.9.0",
|
||||
"vite-tsconfig-paths": "^4.2.0",
|
||||
"yarn": "^1.22.19"
|
||||
}
|
||||
|
@ -333,7 +333,7 @@ export type TypeHints = {
|
||||
};
|
||||
|
||||
export type InvocationSchemaExtra = {
|
||||
output: OpenAPIV3.SchemaObject; // the output of the invocation
|
||||
output: OpenAPIV3.ReferenceObject; // the output of the invocation
|
||||
ui?: {
|
||||
tags?: string[];
|
||||
type_hints?: TypeHints;
|
||||
|
@ -349,11 +349,21 @@ export const getFieldType = (
|
||||
|
||||
if (typeHints && name in typeHints) {
|
||||
rawFieldType = typeHints[name];
|
||||
} else if (!schemaObject.type && schemaObject.allOf) {
|
||||
// if schemaObject has no type, then it should have one of allOf
|
||||
rawFieldType =
|
||||
(schemaObject.allOf[0] as OpenAPIV3.SchemaObject).title ??
|
||||
'Missing Field Type';
|
||||
} else if (!schemaObject.type) {
|
||||
// if schemaObject has no type, then it should have one of allOf, anyOf, oneOf
|
||||
if (schemaObject.allOf) {
|
||||
rawFieldType = refObjectToFieldType(
|
||||
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) {
|
||||
rawFieldType = 'enum';
|
||||
} else if (schemaObject.type) {
|
||||
|
@ -5,154 +5,127 @@ import {
|
||||
InputFieldTemplate,
|
||||
InvocationSchemaObject,
|
||||
InvocationTemplate,
|
||||
isInvocationSchemaObject,
|
||||
OutputFieldTemplate,
|
||||
} from '../types/types';
|
||||
import { buildInputFieldTemplate, getFieldType } from './fieldTemplateBuilders';
|
||||
import { O } from 'ts-toolbelt';
|
||||
|
||||
// recursively exclude all properties of type U from T
|
||||
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
|
||||
>;
|
||||
import {
|
||||
buildInputFieldTemplate,
|
||||
buildOutputFieldTemplates,
|
||||
} from './fieldTemplateBuilders';
|
||||
|
||||
const RESERVED_FIELD_NAMES = ['id', 'type', 'is_intermediate'];
|
||||
|
||||
const invocationDenylist = ['Graph', 'InvocationMeta'];
|
||||
|
||||
const nodeFilter = (
|
||||
schema: DereferencedOpenAPIDocument['components']['schemas'][string],
|
||||
key: string
|
||||
) =>
|
||||
key.includes('Invocation') &&
|
||||
!key.includes('InvocationOutput') &&
|
||||
!invocationDenylist.some((denylistItem) => key.includes(denylistItem));
|
||||
|
||||
export const parseSchema = (openAPI: DereferencedOpenAPIDocument) => {
|
||||
export const parseSchema = (openAPI: OpenAPIV3.Document) => {
|
||||
// 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<
|
||||
Record<string, InvocationTemplate>
|
||||
>((acc, s) => {
|
||||
// cast to InvocationSchemaObject, we know the shape
|
||||
const schema = s as InvocationSchemaObject;
|
||||
>((acc, schema) => {
|
||||
// only want SchemaObjects
|
||||
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') {
|
||||
// Special handling for the Collect node
|
||||
const itemProperty = schema.properties['item'] as InvocationSchemaObject;
|
||||
inputs.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: {
|
||||
if (type === 'collect') {
|
||||
const itemProperty = schema.properties[
|
||||
'item'
|
||||
] as InvocationSchemaObject;
|
||||
// Handle the special Collect node
|
||||
inputs.item = {
|
||||
type: 'item',
|
||||
name: 'item',
|
||||
title: iterationOutput.title ?? '',
|
||||
description: iterationOutput.description ?? '',
|
||||
description: itemProperty.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',
|
||||
},
|
||||
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] = {
|
||||
name: propertyName,
|
||||
title: property.title ?? '',
|
||||
description: property.description ?? '',
|
||||
type: fieldType,
|
||||
};
|
||||
}
|
||||
|
||||
return outputsAccumulator;
|
||||
},
|
||||
{} as Record<string, OutputFieldTemplate>
|
||||
);
|
||||
Object.assign(acc, { [type]: invocation });
|
||||
}
|
||||
|
||||
const invocation: InvocationTemplate = {
|
||||
title,
|
||||
type,
|
||||
tags: schema.ui?.tags ?? [],
|
||||
description: schema.description ?? '',
|
||||
inputs,
|
||||
outputs,
|
||||
};
|
||||
|
||||
Object.assign(acc, { [type]: invocation });
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
|
@ -1,7 +1,5 @@
|
||||
import SwaggerParser from '@apidevtools/swagger-parser';
|
||||
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||
import { log } from 'app/logging/useLogger';
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
|
||||
const schemaLog = log.child({ namespace: 'schema' });
|
||||
|
||||
@ -29,12 +27,13 @@ export const receivedOpenAPISchema = createAsyncThunk(
|
||||
'nodes/receivedOpenAPISchema',
|
||||
async (_, { dispatch, rejectWithValue }) => {
|
||||
try {
|
||||
const dereferencedSchema = (await SwaggerParser.dereference(
|
||||
'openapi.json'
|
||||
)) as OpenAPIV3.Document;
|
||||
const response = await fetch(`openapi.json`);
|
||||
const openAPISchema = await response.json();
|
||||
|
||||
schemaLog.info({ openAPISchema }, 'Received OpenAPI schema');
|
||||
|
||||
const schemaJSON = JSON.parse(
|
||||
JSON.stringify(dereferencedSchema, getCircularReplacer())
|
||||
JSON.stringify(openAPISchema, getCircularReplacer())
|
||||
);
|
||||
|
||||
return schemaJSON;
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user