mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 18:52:19 +00:00
Transform ComfyNodeDef to ComfyNodeDefImpl (#224)
This commit is contained in:
@@ -121,6 +121,61 @@ export class ComfyInputsSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ComfyOutputSpec {
|
||||||
|
type: string
|
||||||
|
is_list: boolean
|
||||||
|
display_name: string
|
||||||
|
name?: string
|
||||||
|
comboOptions?: any[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ComfyOutputsSpec {
|
||||||
|
[key: string]: ComfyOutputSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ComfyNodeDefImpl {
|
||||||
|
name: string
|
||||||
|
display_name: string
|
||||||
|
category: string
|
||||||
|
python_module: string
|
||||||
|
description: string
|
||||||
|
|
||||||
|
@Type(() => ComfyInputsSpec)
|
||||||
|
input: ComfyInputsSpec
|
||||||
|
|
||||||
|
@Transform(({ obj }) => ComfyNodeDefImpl.transformOutputSpec(obj))
|
||||||
|
output: ComfyOutputsSpec
|
||||||
|
|
||||||
|
private static transformOutputSpec(obj: any): ComfyOutputsSpec {
|
||||||
|
const { output, output_is_list, output_name } = obj
|
||||||
|
const result: ComfyOutputsSpec = {}
|
||||||
|
|
||||||
|
output.forEach((type: string | any[], index: number) => {
|
||||||
|
const typeString = Array.isArray(type) ? 'COMBO' : type
|
||||||
|
const display_name = output_name[index]
|
||||||
|
const name = display_name === typeString ? index.toString() : display_name
|
||||||
|
|
||||||
|
const outputSpec = {
|
||||||
|
name,
|
||||||
|
display_name,
|
||||||
|
type: typeString,
|
||||||
|
is_list: output_is_list[index]
|
||||||
|
} as ComfyOutputSpec
|
||||||
|
|
||||||
|
if (Array.isArray(type)) {
|
||||||
|
outputSpec.comboOptions = type
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name in result) {
|
||||||
|
throw new Error(`Duplicate output name: ${name}`)
|
||||||
|
}
|
||||||
|
result[name] = outputSpec
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const SYSTEM_NODE_DEFS: ComfyNodeDef[] = [
|
export const SYSTEM_NODE_DEFS: ComfyNodeDef[] = [
|
||||||
{
|
{
|
||||||
name: 'PrimitiveNode',
|
name: 'PrimitiveNode',
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import {
|
|||||||
BooleanInputSpec,
|
BooleanInputSpec,
|
||||||
FloatInputSpec,
|
FloatInputSpec,
|
||||||
CustomInputSpec,
|
CustomInputSpec,
|
||||||
ComboInputSpec
|
ComboInputSpec,
|
||||||
|
ComfyNodeDefImpl
|
||||||
} from '@/stores/nodeDefStore' // Adjust the import path as needed
|
} from '@/stores/nodeDefStore' // Adjust the import path as needed
|
||||||
|
|
||||||
describe('ComfyInputsSpec', () => {
|
describe('ComfyInputsSpec', () => {
|
||||||
@@ -140,3 +141,181 @@ describe('ComfyInputsSpec', () => {
|
|||||||
expect(result.hidden).toBeUndefined()
|
expect(result.hidden).toBeUndefined()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('ComfyNodeDefImpl', () => {
|
||||||
|
it('should transform a basic node definition correctly', () => {
|
||||||
|
const plainObject = {
|
||||||
|
name: 'TestNode',
|
||||||
|
display_name: 'Test Node',
|
||||||
|
category: 'Testing',
|
||||||
|
python_module: 'test_module',
|
||||||
|
description: 'A test node',
|
||||||
|
input: {
|
||||||
|
required: {
|
||||||
|
intInput: ['INT', { min: 0, max: 100, default: 50 }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
output: ['INT'],
|
||||||
|
output_is_list: [false],
|
||||||
|
output_name: ['intOutput']
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = plainToClass(ComfyNodeDefImpl, plainObject)
|
||||||
|
|
||||||
|
expect(result).toBeInstanceOf(ComfyNodeDefImpl)
|
||||||
|
expect(result.name).toBe('TestNode')
|
||||||
|
expect(result.display_name).toBe('Test Node')
|
||||||
|
expect(result.category).toBe('Testing')
|
||||||
|
expect(result.python_module).toBe('test_module')
|
||||||
|
expect(result.description).toBe('A test node')
|
||||||
|
expect(result.input).toBeInstanceOf(ComfyInputsSpec)
|
||||||
|
expect(result.output).toEqual({
|
||||||
|
intOutput: {
|
||||||
|
name: 'intOutput',
|
||||||
|
display_name: 'intOutput',
|
||||||
|
type: 'INT',
|
||||||
|
is_list: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle multiple outputs including COMBO type', () => {
|
||||||
|
const plainObject = {
|
||||||
|
name: 'MultiOutputNode',
|
||||||
|
display_name: 'Multi Output Node',
|
||||||
|
category: 'Advanced',
|
||||||
|
python_module: 'advanced_module',
|
||||||
|
description: 'A node with multiple outputs',
|
||||||
|
input: {},
|
||||||
|
output: ['STRING', ['COMBO', 'option1', 'option2'], 'FLOAT'],
|
||||||
|
output_is_list: [true, false, false],
|
||||||
|
output_name: ['stringOutput', 'comboOutput', 'floatOutput']
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = plainToClass(ComfyNodeDefImpl, plainObject)
|
||||||
|
|
||||||
|
expect(result.output).toEqual({
|
||||||
|
stringOutput: {
|
||||||
|
name: 'stringOutput',
|
||||||
|
display_name: 'stringOutput',
|
||||||
|
type: 'STRING',
|
||||||
|
is_list: true
|
||||||
|
},
|
||||||
|
comboOutput: {
|
||||||
|
name: 'comboOutput',
|
||||||
|
display_name: 'comboOutput',
|
||||||
|
type: 'COMBO',
|
||||||
|
is_list: false,
|
||||||
|
comboOptions: ['COMBO', 'option1', 'option2']
|
||||||
|
},
|
||||||
|
floatOutput: {
|
||||||
|
name: 'floatOutput',
|
||||||
|
display_name: 'floatOutput',
|
||||||
|
type: 'FLOAT',
|
||||||
|
is_list: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should use index for output names if matches type', () => {
|
||||||
|
const plainObject = {
|
||||||
|
name: 'MissingNamesNode',
|
||||||
|
display_name: 'Missing Names Node',
|
||||||
|
category: 'Test',
|
||||||
|
python_module: 'test_module',
|
||||||
|
description: 'A node with missing output names',
|
||||||
|
input: {},
|
||||||
|
output: ['INT', 'FLOAT', 'FLOAT'],
|
||||||
|
output_is_list: [false, true, true],
|
||||||
|
output_name: ['INT', 'FLOAT', 'FLOAT']
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = plainToClass(ComfyNodeDefImpl, plainObject)
|
||||||
|
|
||||||
|
expect(result.output).toEqual({
|
||||||
|
'0': {
|
||||||
|
name: '0',
|
||||||
|
display_name: 'INT',
|
||||||
|
type: 'INT',
|
||||||
|
is_list: false
|
||||||
|
},
|
||||||
|
'1': {
|
||||||
|
name: '1',
|
||||||
|
display_name: 'FLOAT',
|
||||||
|
type: 'FLOAT',
|
||||||
|
is_list: true
|
||||||
|
},
|
||||||
|
'2': {
|
||||||
|
name: '2',
|
||||||
|
display_name: 'FLOAT',
|
||||||
|
type: 'FLOAT',
|
||||||
|
is_list: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should throw an error for duplicate output names', () => {
|
||||||
|
const plainObject = {
|
||||||
|
name: 'DuplicateOutputNode',
|
||||||
|
display_name: 'Duplicate Output Node',
|
||||||
|
category: 'Test',
|
||||||
|
python_module: 'test_module',
|
||||||
|
description: 'A node with duplicate output names',
|
||||||
|
input: {},
|
||||||
|
output: ['INT', 'FLOAT', 'STRING'],
|
||||||
|
output_is_list: [false, false, false],
|
||||||
|
output_name: ['output', 'output', 'uniqueOutput']
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
plainToClass(ComfyNodeDefImpl, plainObject)
|
||||||
|
}).toThrow('Duplicate output name: output')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle empty output', () => {
|
||||||
|
const plainObject = {
|
||||||
|
name: 'EmptyOutputNode',
|
||||||
|
display_name: 'Empty Output Node',
|
||||||
|
category: 'Test',
|
||||||
|
python_module: 'test_module',
|
||||||
|
description: 'A node with no outputs',
|
||||||
|
input: {},
|
||||||
|
output: [],
|
||||||
|
output_is_list: [],
|
||||||
|
output_name: []
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = plainToClass(ComfyNodeDefImpl, plainObject)
|
||||||
|
|
||||||
|
expect(result.output).toEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle complex input specifications', () => {
|
||||||
|
const plainObject = {
|
||||||
|
name: 'ComplexInputNode',
|
||||||
|
display_name: 'Complex Input Node',
|
||||||
|
category: 'Advanced',
|
||||||
|
python_module: 'advanced_module',
|
||||||
|
description: 'A node with complex input',
|
||||||
|
input: {
|
||||||
|
required: {
|
||||||
|
intInput: ['INT', { min: 0, max: 100, default: 50 }],
|
||||||
|
stringInput: ['STRING', { multiline: true }]
|
||||||
|
},
|
||||||
|
optional: {
|
||||||
|
booleanInput: ['BOOLEAN', { default: true }],
|
||||||
|
floatInput: ['FLOAT', { step: 0.1 }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
output: ['INT'],
|
||||||
|
output_is_list: [false],
|
||||||
|
output_name: ['result']
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = plainToClass(ComfyNodeDefImpl, plainObject)
|
||||||
|
|
||||||
|
expect(result.input).toBeInstanceOf(ComfyInputsSpec)
|
||||||
|
expect(result.input.required).toBeDefined()
|
||||||
|
expect(result.input.optional).toBeDefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user