onConnectionsChange, onConnectInput,
onConnectOutput, and onBeforeConnectInput keep their existing arguments
and timing. For link registration and LinkNetwork API changes, see
Link registration migration.
Check for SubgraphIO at subgraph boundaries
At a subgraph boundary (aSubgraphInput or SubgraphOutput),
onConnectionsChange still fires, but its fifth argument is a SubgraphIO,
not the usual INodeInputSlot/INodeOutputSlot. Check for it before
treating the argument as a normal slot. The frontend’s own isSubgraphInput
and isSubgraphOutput guards are not exposed to extensions, so check for the
linkIds array and parent node that every SubgraphIO has and node slots
do not:
Subscribe to node lifecycle events with graph.events
graph.events dispatches node:added, node:before-removed, and
node:removed. Use these when more than one part of your extension needs to
observe a node being added or removed:
node.onAdded, node.onRemoved, and the single-slot
graph.onNodeAdded/graph.onNodeRemoved still work, but assigning
graph.onNodeAdded replaces any existing handler: a second subscriber
silently drops the first. Prefer graph.events unless you need to veto or
mutate during add/remove. There’s no multi-subscriber equivalent for
connect/disconnect; onConnectionsChange is still the only per-connection
hook.
Node removal fires callbacks in this order (partially tested)
Removing a node fires, in order:node:before-removed: the node is still fully attached.onConnectionsChange(..., false, ...)on the removed node and on each connected peer, while the removed node is still in the graph.node.onRemoved():node.graphis still set.- The node detaches from graph indexes and stores;
node.graphbecomesnull. onNodeRemoved(node), thennode:removed, both seeing an already-detached node.
node:before-removed → node.onRemoved() → onNodeRemoved(node) and
separately verify that node:removed sees a detached node. The complete
sequence, including step 2, is observed from source rather than guaranteed by
one end-to-end test.
To resolve the removed node from a peer’s onConnectionsChange handler, do it
at step 2 via graph.getNodeById(). By node:removed, the node is gone.
Workflow load resolves peer nodes before onConnectionsChange runs
graph.configure() creates every node in the workflow before any node’s own
configure() runs and fires onConnectionsChange for its restored links.
So when your onConnectionsChange callback fires during load, every peer
node already exists and its link is already registered, so graph.getNodeById()
and link lookups from inside that callback resolve correctly.
API prompt import does not reconnect resolved inputs
app.loadApiJson() connects each resolvable input without running a second
disconnect/reconnect pass. Extensions should not rely on duplicate
onConnectionsChange notifications during API prompt import. Make each
connection handler perform its work on the first notification; move work that
requires the complete imported graph to afterConfigureGraph or afterLoadGraph.
Inputs whose source output or destination slot does not exist yet are retried
while a pass makes progress. This preserves dynamic-input behavior: a successful
connection can create the slot needed by a later pending connection. Already
resolved connections are not repeated, and a connection rejected by an
extension is not retried once its endpoints are available.
This changes API prompt import only. Regular workflow loading still uses
graph.configure() as described above, and connection callback signatures are
unchanged.
Reject connections before they are made
To refuse a connection, returnfalse from onConnectOutput on the source
node or onConnectInput on the target node. Both run before the graph changes
anything, so an existing link on the target input stays in place:
onConnectionsChange. By the time it runs, the new link has
already replaced any link the target input had, so removing the new link
leaves the input disconnected.
Removing a link inside the output callback skips the input callback
Connecting a link calls the output side’sonConnectionsChange first, then
the input side’s. If the output-side handler removes that same link before
returning, the input side gets an isConnected === false callback from the
removal, and its isConnected === true callback for the new link is skipped.
Don’t assume both sides always see the connection; check that the link you
were handed is still connected if your handler re-enters the graph. This is
observed behavior, not a tested contract: no test currently exercises the skip
path.
Replace removed slot events
node:slot-links:changed and node:slot-errors:changed were removed, with
no replacement event of the same name. Migrate to:
node.isInputConnected(),node.isOutputConnected(), ornode.getOutputNodes()for a reactive read of connectivity state, oronConnectionsChangewhen you need to run code at the exact moment a slot connects or disconnects.
Read connection state from callback arguments or node methods
input.link and output.links are deprecated, and reads can drift from the
current graph. Writes only remove links: assigning null to input.link
disconnects the input, and assigning or mutating output.links disconnects
every link missing from the new array. Writes never create links; use
node.connect(), node.disconnectInput() or node.disconnectOutput(). Use the
link_info argument your callback already receives, or the LGraphNode
instance methods that read the link store directly:
node.isInputConnected(), node.getInputLink(), node.isOutputConnected()
and node.getOutputNodes(). Don’t read input.link or output.links.
The store functions behind these methods are internal and not part of the
extension API. Use the node methods.