refactor: consolidate Outcome types and replace push loops with flatMap

- proxyWidgetMigration.ts: collapse three *Result discriminated unions
  into a single Outcome<TOk, TReason>; merge quarantineFor into
  makeQuarantineEntry; SnapshotLink extends PrimitiveBypassTargetRef;
  rename cohortReferencesPrimitive -> cohortDuplicatesPrimitive and
  drop mutable counter
- SubgraphNode.serialize: replace for+push+hasSerializableValue flag
  with flatMap and .some()
- appModeStore: replace nested for+continue+push with nested flatMap

Amp-Thread-ID: https://ampcode.com/threads/T-019e1f3a-a617-70b4-8945-3f0fbd34c1de
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
DrJKL
2026-05-12 22:37:06 -07:00
parent aacb3eb5c3
commit acd71a6085
3 changed files with 51 additions and 72 deletions

View File

@@ -268,32 +268,22 @@ function collectTargetsSkippingDangling(
primitiveNode: LGraphNode
): PrimitiveBypassTargetRef[] {
const subgraph = hostNode.subgraph
const output = primitiveNode.outputs?.[0]
const linkIds = output?.links ?? []
const targets: PrimitiveBypassTargetRef[] = []
for (const linkId of linkIds) {
const linkIds = primitiveNode.outputs?.[0]?.links ?? []
return linkIds.flatMap((linkId) => {
const link = subgraph.links.get(linkId)
if (!link) continue
targets.push({
targetNodeId: link.target_id,
targetSlot: link.target_slot
})
}
return targets
return link
? [{ targetNodeId: link.target_id, targetSlot: link.target_slot }]
: []
})
}
function cohortReferencesPrimitive(
function cohortDuplicatesPrimitive(
cohort: readonly LegacyProxyEntrySource[],
primitiveNodeId: string
): boolean {
let count = 0
for (const entry of cohort) {
if (entry.sourceNodeId === primitiveNodeId) {
count += 1
if (count >= 2) return true
}
}
return false
return (
cohort.filter((entry) => entry.sourceNodeId === primitiveNodeId).length >= 2
)
}
function classify(
@@ -320,7 +310,7 @@ function classify(
if (sourceNode.type === PRIMITIVE_NODE_TYPE) {
const targets = collectTargetsSkippingDangling(hostNode, sourceNode)
const cohortDuplicated = cohortReferencesPrimitive(
const cohortDuplicated = cohortDuplicatesPrimitive(
cohort,
normalized.sourceNodeId
)
@@ -382,9 +372,11 @@ function addUniqueSubgraphInput(
return subgraph.addInput(uniqueName, type)
}
type RepairValueResult =
| { ok: true; subgraphInputName: string }
| { ok: false; reason: ProxyWidgetQuarantineReason }
type Outcome<TOk, TReason = ProxyWidgetQuarantineReason> =
| ({ ok: true } & TOk)
| { ok: false; reason: TReason }
type RepairValueResult = Outcome<{ subgraphInputName: string }>
function repairValue(
hostNode: SubgraphNode,
@@ -484,19 +476,18 @@ function repairCreateSubgraphInput(
return { ok: true, subgraphInputName: newSubgraphInput.name }
}
type RepairPrimitiveResult =
| { ok: true; subgraphInputName: string; reconnectCount: number }
| { ok: false; reason: 'primitiveBypassFailed' }
type RepairPrimitiveResult = Outcome<
{ subgraphInputName: string; reconnectCount: number },
'primitiveBypassFailed'
>
const PRIMITIVE_FAILED: RepairPrimitiveResult = {
ok: false,
reason: 'primitiveBypassFailed'
}
interface SnapshotLink {
interface SnapshotLink extends PrimitiveBypassTargetRef {
primitiveSlot: number
targetNodeId: NodeId
targetSlot: number
}
interface CohortValidationOk {
@@ -665,9 +656,10 @@ function repairPrimitive(
}
}
type MigratePreviewResult =
| { ok: true; previewName: string }
| { ok: false; reason: 'missingSourceNode' | 'missingSourceWidget' }
type MigratePreviewResult = Outcome<
{ previewName: string },
'missingSourceNode' | 'missingSourceWidget'
>
function migratePreview(
hostNode: SubgraphNode,
@@ -723,15 +715,11 @@ function quarantineFor(
const originalEntry: SerializedProxyWidgetTuple = disambiguatingSourceNodeId
? [sourceNodeId, sourceWidgetName, disambiguatingSourceNodeId]
: [sourceNodeId, sourceWidgetName]
const result: ProxyWidgetErrorQuarantineEntry = {
return makeQuarantineEntry({
originalEntry,
reason,
attemptedAtVersion: QUARANTINE_VERSION
}
if (!entry.isHole && entry.hostValue !== undefined) {
result.hostValue = entry.hostValue
}
return result
hostValue: entry.isHole ? undefined : entry.hostValue
})
}
function appendQuarantine(
@@ -762,15 +750,11 @@ export function readHostQuarantine(
)
}
interface MakeQuarantineEntryArgs {
export function makeQuarantineEntry(args: {
originalEntry: SerializedProxyWidgetTuple
reason: ProxyWidgetQuarantineReason
hostValue?: TWidgetValue
}
export function makeQuarantineEntry(
args: MakeQuarantineEntryArgs
): ProxyWidgetErrorQuarantineEntry {
}): ProxyWidgetErrorQuarantineEntry {
const entry: ProxyWidgetErrorQuarantineEntry = {
originalEntry: args.originalEntry,
reason: args.reason,

View File

@@ -1169,25 +1169,22 @@ export class SubgraphNode extends LGraphNode implements BaseLGraph {
serialized.properties = serializedProperties
const widgetStore = useWidgetValueStore()
const widgetValues: TWidgetValue[] = []
let hasSerializableValue = false
for (const input of this.inputs) {
const widgetValues = this.inputs.flatMap((input) => {
const widget = input._widget
if (!widget || !isPromotedWidgetView(widget)) continue
if (!widget || !isPromotedWidgetView(widget)) return []
const state = widgetStore.getWidget(
rootGraphId,
this.id,
getPromotedWidgetHostStateName(widget)
)
const value =
state && isWidgetValue(state.value) ? state.value : undefined
widgetValues.push(value)
hasSerializableValue ||= value !== undefined
}
return [state && isWidgetValue(state.value) ? state.value : undefined]
})
if (hasSerializableValue) serialized.widgets_values = widgetValues
else delete serialized.widgets_values
if (widgetValues.some((value) => value !== undefined)) {
serialized.widgets_values = widgetValues
} else {
delete serialized.widgets_values
}
return serialized
}

View File

@@ -72,24 +72,22 @@ export const useAppModeStore = defineStore('appMode', () => {
const directNode = rootGraph.getNodeById?.(storedId)
if (directNode?.widgets?.some((w) => w.name === widgetName)) return input
const matches: LinearInput[] = []
for (const node of rootGraph.nodes) {
if (!(node instanceof SubgraphNode)) continue
for (const inputSlot of node.inputs) {
const matches: LinearInput[] = rootGraph.nodes.flatMap((node) => {
if (!(node instanceof SubgraphNode)) return []
return node.inputs.flatMap((inputSlot): LinearInput[] => {
const widget = inputSlot._widget
if (!widget || !isPromotedWidgetView(widget)) continue
if (!widget || !isPromotedWidgetView(widget)) return []
if (
widget.sourceNodeId === String(storedId) &&
widget.sourceWidgetName === widgetName
widget.sourceNodeId !== String(storedId) ||
widget.sourceWidgetName !== widgetName
) {
matches.push([
createNodeLocatorId(rootGraph.id, node.id),
inputSlot.name,
input[2]
])
return []
}
}
}
return [
[createNodeLocatorId(rootGraph.id, node.id), inputSlot.name, input[2]]
]
})
})
if (matches.length === 1) return matches[0]
if (matches.length > 1) {
console.warn(