Skip to content

Commit a2c8f4b

Browse files
fix(litegraph): Resolve workflow corruption from subgraph serialization
This commit fixes a critical issue where saving a workflow with complex subgraphs could lead to a corrupted file, causing errors upon loading. The root cause was twofold: 1. **Unsafe Object Cloning:** The `LiteGraph.cloneObject` function used `JSON.stringify`, which cannot handle circular references that can exist in a nodes properties, especially in complex graphs with promoted widgets. This has been replaced with `_.cloneDeep` from lodash for robust and safe deep cloning. 2. **Unsafe Node Configuration:** The `LGraphNode.configure` method used a generic loop that would overwrite the `inputs` and `outputs` arrays with plain objects from the serialized data. This created a temporary but dangerous state where class instances were replaced by plain objects, leading to `TypeError` exceptions when methods or private fields were accessed on them. The `configure` method has been refactored to handle `inputs`, `outputs`, and `properties` explicitly and safely, ensuring they are always proper class instances. This fix makes the node loading process more robust and predictable, preventing data corruption and ensuring the stability of workflows with subgraphs.
1 parent 16d7436 commit a2c8f4b

File tree

2 files changed

+33
-15
lines changed

2 files changed

+33
-15
lines changed

src/lib/litegraph/src/LGraphNode.ts

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -769,12 +769,7 @@ export class LGraphNode
769769
this.graph._version++
770770
}
771771
for (const j in info) {
772-
if (j == 'properties') {
773-
// i don't want to clone properties, I want to reuse the old container
774-
for (const k in info.properties) {
775-
this.properties[k] = info.properties[k]
776-
this.onPropertyChanged?.(k, info.properties[k])
777-
}
772+
if (j === 'properties' || j === 'inputs' || j === 'outputs') {
778773
continue
779774
}
780775

@@ -798,14 +793,28 @@ export class LGraphNode
798793
}
799794
}
800795

796+
if (info.properties) {
797+
for (const k in info.properties) {
798+
this.properties[k] = info.properties[k]
799+
this.onPropertyChanged?.(k, info.properties[k])
800+
}
801+
}
802+
801803
if (!info.title) {
802804
this.title = this.constructor.title
803805
}
804806

805-
this.inputs ??= []
806-
this.inputs = this.inputs.map((input) =>
807-
toClass(NodeInputSlot, input, this)
808-
)
807+
if (info.inputs) {
808+
this.inputs = info.inputs.map((input) =>
809+
toClass(NodeInputSlot, input, this)
810+
)
811+
} else {
812+
this.inputs ??= []
813+
this.inputs = this.inputs.map((input) =>
814+
toClass(NodeInputSlot, input, this)
815+
)
816+
}
817+
809818
for (const [i, input] of this.inputs.entries()) {
810819
const link =
811820
this.graph && input.link != null
@@ -815,10 +824,17 @@ export class LGraphNode
815824
this.onInputAdded?.(input)
816825
}
817826

818-
this.outputs ??= []
819-
this.outputs = this.outputs.map((output) =>
820-
toClass(NodeOutputSlot, output, this)
821-
)
827+
if (info.outputs) {
828+
this.outputs = info.outputs.map((output) =>
829+
toClass(NodeOutputSlot, output, this)
830+
)
831+
} else {
832+
this.outputs ??= []
833+
this.outputs = this.outputs.map((output) =>
834+
toClass(NodeOutputSlot, output, this)
835+
)
836+
}
837+
822838
for (const [i, output] of this.outputs.entries()) {
823839
if (!output.links) continue
824840

src/lib/litegraph/src/LiteGraphGlobal.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import _ from 'lodash'
2+
13
import { ContextMenu } from './ContextMenu'
24
import { CurveEditor } from './CurveEditor'
35
import { DragAndScale } from './DragAndScale'
@@ -641,7 +643,7 @@ export class LiteGraphGlobal {
641643
): WhenNullish<T, null> {
642644
if (obj == null) return null as WhenNullish<T, null>
643645

644-
const r = JSON.parse(JSON.stringify(obj))
646+
const r = _.cloneDeep(obj)
645647
if (!target) return r
646648

647649
for (const i in r) {

0 commit comments

Comments
 (0)