mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-27 10:14:06 +00:00
Fix all unknown slot props are serialised (#732)
Ensures only specified properties are cloned for serialisation.
This commit is contained in:
@@ -32,7 +32,7 @@ import { LGraphCanvas } from "./LGraphCanvas"
|
||||
import { type LGraphNodeConstructor, LiteGraph } from "./litegraph"
|
||||
import { LLink } from "./LLink"
|
||||
import { createBounds, isInRect, isInRectangle, isPointInRect, snapPoint } from "./measure"
|
||||
import { ConnectionColorContext, isINodeInputSlot, isWidgetInputSlot, NodeInputSlot, NodeOutputSlot, serializeSlot, toNodeSlotClass } from "./NodeSlot"
|
||||
import { ConnectionColorContext, inputAsSerialisable, isINodeInputSlot, isWidgetInputSlot, NodeInputSlot, NodeOutputSlot, outputAsSerialisable, toNodeSlotClass } from "./NodeSlot"
|
||||
import {
|
||||
LGraphEventMode,
|
||||
NodeSlotType,
|
||||
@@ -703,8 +703,8 @@ export class LGraphNode implements Positionable, IPinnable, IColorable {
|
||||
if (this.constructor === LGraphNode && this.last_serialization)
|
||||
return this.last_serialization
|
||||
|
||||
if (this.inputs) o.inputs = this.inputs.map(input => serializeSlot(input))
|
||||
if (this.outputs) o.outputs = this.outputs.map(output => serializeSlot(output))
|
||||
if (this.inputs) o.inputs = this.inputs.map(input => inputAsSerialisable(input))
|
||||
if (this.outputs) o.outputs = this.outputs.map(output => outputAsSerialisable(output))
|
||||
|
||||
if (this.title && this.title != this.constructor.title) o.title = this.title
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import type { CanvasColour, Dictionary, INodeInputSlot, INodeOutputSlot, INodeSlot, ISlotType, IWidgetInputSlot, Point } from "./interfaces"
|
||||
import type { CanvasColour, Dictionary, INodeInputSlot, INodeOutputSlot, INodeSlot, ISlotType, IWidgetInputSlot, Point, SharedIntersection } from "./interfaces"
|
||||
import type { LinkId } from "./LLink"
|
||||
import type { IWidget } from "./types/widgets"
|
||||
|
||||
import { LabelPosition, SlotShape, SlotType } from "./draw"
|
||||
import { LiteGraph } from "./litegraph"
|
||||
import { LinkDirection, RenderShape } from "./types/globalEnums"
|
||||
import { ISerialisedNodeOutputSlot } from "./types/serialisation"
|
||||
import { ISerialisedNodeInputSlot } from "./types/serialisation"
|
||||
import { omitBy } from "./utils/object"
|
||||
import { ISerialisableNodeOutput } from "./types/serialisation"
|
||||
import { ISerialisableNodeInput } from "./types/serialisation"
|
||||
|
||||
export interface ConnectionColorContext {
|
||||
default_connection_color: {
|
||||
@@ -31,16 +30,32 @@ interface IDrawOptions {
|
||||
highlight?: boolean
|
||||
}
|
||||
|
||||
export function serializeSlot(slot: INodeInputSlot): ISerialisedNodeInputSlot
|
||||
export function serializeSlot(slot: INodeOutputSlot): ISerialisedNodeOutputSlot
|
||||
export function serializeSlot(slot: INodeInputSlot | INodeOutputSlot): ISerialisedNodeInputSlot | ISerialisedNodeOutputSlot {
|
||||
return omitBy({
|
||||
...slot,
|
||||
_layoutElement: undefined,
|
||||
_data: undefined,
|
||||
pos: isWidgetInputSlot(slot) ? undefined : slot.pos,
|
||||
widget: isWidgetInputSlot(slot) ? { name: slot.widget.name } : undefined,
|
||||
}, value => value === undefined) as ISerialisedNodeInputSlot | ISerialisedNodeOutputSlot
|
||||
type CommonIoSlotProps = SharedIntersection<ISerialisableNodeInput, ISerialisableNodeOutput>
|
||||
|
||||
export function shallowCloneCommonProps(slot: CommonIoSlotProps): CommonIoSlotProps {
|
||||
const { color_off, color_on, dir, label, localized_name, locked, name, nameLocked, removable, shape, type } = slot
|
||||
return { color_off, color_on, dir, label, localized_name, locked, name, nameLocked, removable, shape, type }
|
||||
}
|
||||
|
||||
export function inputAsSerialisable(slot: INodeInputSlot): ISerialisableNodeInput {
|
||||
const widgetInputProps = slot.widget
|
||||
? { widget: { name: slot.widget.name } }
|
||||
: { pos: slot.pos }
|
||||
|
||||
return {
|
||||
...shallowCloneCommonProps(slot),
|
||||
...widgetInputProps,
|
||||
link: slot.link,
|
||||
}
|
||||
}
|
||||
|
||||
export function outputAsSerialisable(slot: INodeOutputSlot): ISerialisableNodeOutput {
|
||||
return {
|
||||
...shallowCloneCommonProps(slot),
|
||||
pos: slot.pos,
|
||||
slot_index: slot.slot_index,
|
||||
links: slot.links,
|
||||
}
|
||||
}
|
||||
|
||||
export function toNodeSlotClass(slot: INodeSlot): NodeSlot {
|
||||
|
||||
@@ -22,6 +22,13 @@ export type WhenNullish<T, Result> = T & {} | (T extends null ? Result : T exten
|
||||
/** A type with each of the {@link Properties} made optional. */
|
||||
export type OptionalProps<T, Properties extends keyof T> = Omit<T, Properties> & { [K in Properties]?: T[K] }
|
||||
|
||||
/** Bitwise AND intersection of two types; returns a new, non-union type that includes only properties that exist on both types. */
|
||||
export type SharedIntersection<T1, T2> = {
|
||||
[P in keyof T1 as P extends keyof T2 ? P : never]: T1[P]
|
||||
} & {
|
||||
[P in keyof T2 as P extends keyof T1 ? P : never]: T2[P]
|
||||
}
|
||||
|
||||
export type CanvasColour = string | CanvasGradient | CanvasPattern
|
||||
|
||||
/** An object containing a set of child objects */
|
||||
|
||||
@@ -40,10 +40,10 @@ export interface SerialisableGraph {
|
||||
extra?: Record<any, any>
|
||||
}
|
||||
|
||||
export type ISerialisedNodeInputSlot = Omit<INodeInputSlot, "_layoutElement" | "widget"> & {
|
||||
export type ISerialisableNodeInput = Omit<INodeInputSlot, "_layoutElement" | "widget"> & {
|
||||
widget?: { name: string }
|
||||
}
|
||||
export type ISerialisedNodeOutputSlot = Omit<INodeOutputSlot, "_layoutElement" | "_data">
|
||||
export type ISerialisableNodeOutput = Omit<INodeOutputSlot, "_layoutElement" | "_data">
|
||||
|
||||
/** Serialised LGraphNode */
|
||||
export interface ISerialisedNode {
|
||||
@@ -55,8 +55,8 @@ export interface ISerialisedNode {
|
||||
flags?: INodeFlags
|
||||
order?: number
|
||||
mode?: number
|
||||
outputs?: ISerialisedNodeOutputSlot[]
|
||||
inputs?: ISerialisedNodeInputSlot[]
|
||||
outputs?: ISerialisableNodeOutput[]
|
||||
inputs?: ISerialisableNodeInput[]
|
||||
properties?: Dictionary<NodeProperty | undefined>
|
||||
shape?: RenderShape
|
||||
boxcolor?: string
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import { INodeInputSlot, INodeOutputSlot } from "@/interfaces"
|
||||
import { serializeSlot } from "@/NodeSlot"
|
||||
import { inputAsSerialisable, outputAsSerialisable } from "@/NodeSlot"
|
||||
|
||||
describe("NodeSlot", () => {
|
||||
describe("serializeSlot", () => {
|
||||
describe("inputAsSerialisable", () => {
|
||||
it("removes _data from serialized slot", () => {
|
||||
const slot: INodeOutputSlot = {
|
||||
_data: "test data",
|
||||
@@ -12,7 +12,7 @@ describe("NodeSlot", () => {
|
||||
type: "STRING",
|
||||
links: [],
|
||||
}
|
||||
const serialized = serializeSlot(slot)
|
||||
const serialized = outputAsSerialisable(slot)
|
||||
expect(serialized).not.toHaveProperty("_data")
|
||||
})
|
||||
|
||||
@@ -32,7 +32,7 @@ describe("NodeSlot", () => {
|
||||
},
|
||||
}
|
||||
|
||||
const serialized = serializeSlot(widgetInputSlot)
|
||||
const serialized = inputAsSerialisable(widgetInputSlot)
|
||||
expect(serialized).not.toHaveProperty("pos")
|
||||
})
|
||||
|
||||
@@ -43,7 +43,7 @@ describe("NodeSlot", () => {
|
||||
pos: [10, 20],
|
||||
link: null,
|
||||
}
|
||||
const serialized = serializeSlot(normalSlot)
|
||||
const serialized = inputAsSerialisable(normalSlot)
|
||||
expect(serialized).toHaveProperty("pos")
|
||||
})
|
||||
|
||||
@@ -62,7 +62,7 @@ describe("NodeSlot", () => {
|
||||
},
|
||||
}
|
||||
|
||||
const serialized = serializeSlot(widgetInputSlot)
|
||||
const serialized = inputAsSerialisable(widgetInputSlot)
|
||||
expect(serialized.widget).toEqual({ name: "test-widget" })
|
||||
expect(serialized.widget).not.toHaveProperty("type")
|
||||
expect(serialized.widget).not.toHaveProperty("value")
|
||||
|
||||
Reference in New Issue
Block a user