knip: YOLO pass, all the unused exports enabled, YAGNI for the rest (#5313)

* knip: Enable unusedBinaries, add two exceptions

* knip: YOLO pass, all the unused exports enabled.
Paired with @christian-byrne to allow for some special cases to remain with custom knip ignore tags.

* knip: remove post-rebase
This commit is contained in:
Alexander Brown
2025-09-04 22:29:44 -07:00
committed by GitHub
parent 006e6bd57c
commit 3fbcf4aa7e
35 changed files with 22 additions and 883 deletions

View File

@@ -16,6 +16,7 @@ const config: KnipConfig = {
'tests-ui/**/*.{js,ts,vue}', 'tests-ui/**/*.{js,ts,vue}',
'*.{js,ts,mts}' '*.{js,ts,mts}'
], ],
ignoreBinaries: ['only-allow', 'openapi-typescript'],
ignoreDependencies: [ ignoreDependencies: [
'@primeuix/forms', '@primeuix/forms',
'@primeuix/styled', '@primeuix/styled',
@@ -59,7 +60,11 @@ const config: KnipConfig = {
'src/components/button/TextButton.vue', 'src/components/button/TextButton.vue',
'src/components/card/CardTitle.vue', 'src/components/card/CardTitle.vue',
'src/components/card/CardDescription.vue', 'src/components/card/CardDescription.vue',
'src/components/input/SingleSelect.vue' 'src/components/input/SingleSelect.vue',
// Used by a custom node (that should move off of this)
'src/scripts/ui/components/splitButton.ts',
// Generated file: openapi
'src/types/comfyRegistryTypes.ts'
], ],
ignoreExportsUsedInFile: true, ignoreExportsUsedInFile: true,
// Vue-specific configuration // Vue-specific configuration
@@ -68,15 +73,12 @@ const config: KnipConfig = {
// Only check for unused files, disable all other rules // Only check for unused files, disable all other rules
// TODO: Gradually enable other rules - see https://github.com/Comfy-Org/ComfyUI_frontend/issues/4888 // TODO: Gradually enable other rules - see https://github.com/Comfy-Org/ComfyUI_frontend/issues/4888
rules: { rules: {
binaries: 'off', classMembers: 'off'
classMembers: 'off',
duplicates: 'off',
enumMembers: 'off',
exports: 'off',
nsExports: 'off',
nsTypes: 'off',
types: 'off'
}, },
tags: [
'-knipIgnoreUnusedButUsedByCustomNodes',
'-knipIgnoreUnusedButUsedByVueNodesBranch'
],
// Include dependencies analysis // Include dependencies analysis
includeEntryExports: true includeEntryExports: true
} }

View File

@@ -2,7 +2,8 @@ import { type Ref, computed, ref } from 'vue'
import type { TemplateInfo } from '@/types/workflowTemplateTypes' import type { TemplateInfo } from '@/types/workflowTemplateTypes'
export interface TemplateFilterOptions { // @ts-expect-error unused (To be used later?)
interface TemplateFilterOptions {
searchQuery?: string searchQuery?: string
} }

View File

@@ -1,5 +1,4 @@
import { import {
AutoLaunch,
CrossAttentionMethod, CrossAttentionMethod,
CudaMalloc, CudaMalloc,
FloatingPointPrecision, FloatingPointPrecision,
@@ -20,32 +19,6 @@ export interface ServerConfig<T> extends FormItem {
getValue?: (value: T) => Record<string, ServerConfigValue> getValue?: (value: T) => Record<string, ServerConfigValue>
} }
export const WEB_ONLY_CONFIG_ITEMS: ServerConfig<any>[] = [
// Launch behavior
{
id: 'auto-launch',
name: 'Automatically opens in the browser on startup',
category: ['Launch'],
type: 'combo',
options: Object.values(AutoLaunch),
defaultValue: AutoLaunch.Auto,
getValue: (value: AutoLaunch) => {
switch (value) {
case AutoLaunch.Auto:
return {}
case AutoLaunch.Enable:
return {
['auto-launch']: true
}
case AutoLaunch.Disable:
return {
['disable-auto-launch']: true
}
}
}
}
]
export const SERVER_CONFIG_ITEMS: ServerConfig<any>[] = [ export const SERVER_CONFIG_ITEMS: ServerConfig<any>[] = [
// Network settings // Network settings
{ {

View File

@@ -185,12 +185,3 @@ export interface LoaderManagerInterface {
dispose(): void dispose(): void
loadModel(url: string, originalFileName?: string): Promise<void> loadModel(url: string, originalFileName?: string): Promise<void>
} }
export interface RecordingManagerInterface extends BaseManager {
startRecording(): Promise<void>
stopRecording(): void
hasRecording(): boolean
getRecordingDuration(): number
exportRecording(filename?: string): void
clearRecording(): void
}

View File

@@ -422,6 +422,7 @@ function getConfig(this: LGraphNode, widgetName: string) {
* @param node The node to convert the widget to an input slot for. * @param node The node to convert the widget to an input slot for.
* @param widget The widget to convert to an input slot. * @param widget The widget to convert to an input slot.
* @returns The input slot that was converted from the widget or undefined if the widget is not found. * @returns The input slot that was converted from the widget or undefined if the widget is not found.
* @knipIgnoreUnusedButUsedByCustomNodes
*/ */
export function convertToInput( export function convertToInput(
node: LGraphNode, node: LGraphNode,

View File

@@ -1,7 +1,7 @@
import type { Rectangle } from './infrastructure/Rectangle' import type { Rectangle } from './infrastructure/Rectangle'
import type { CanvasColour, Rect } from './interfaces' import type { CanvasColour, Rect } from './interfaces'
import { LiteGraph } from './litegraph' import { LiteGraph } from './litegraph'
import { LinkDirection, RenderShape, TitleMode } from './types/globalEnums' import { RenderShape, TitleMode } from './types/globalEnums'
const ELLIPSIS = '\u2026' const ELLIPSIS = '\u2026'
const TWO_DOT_LEADER = '\u2025' const TWO_DOT_LEADER = '\u2025'
@@ -22,12 +22,7 @@ export enum SlotShape {
} }
/** @see LinkDirection */ /** @see LinkDirection */
export enum SlotDirection { export enum SlotDirection {}
Up = LinkDirection.UP,
Right = LinkDirection.RIGHT,
Down = LinkDirection.DOWN,
Left = LinkDirection.LEFT
}
export enum LabelPosition { export enum LabelPosition {
Left = 'left', Left = 'left',

View File

@@ -278,9 +278,6 @@ export type KeysOfType<T, Match> = Exclude<
undefined undefined
> >
/** A new type that contains only the properties of T that are of type Match */
export type PickByType<T, Match> = { [P in keyof T]: Extract<T[P], Match> }
/** The names of all (optional) methods and functions in T */ /** The names of all (optional) methods and functions in T */
export type MethodNames<T> = KeysOfType<T, ((...args: any) => any) | undefined> export type MethodNames<T> = KeysOfType<T, ((...args: any) => any) | undefined>

View File

@@ -2,7 +2,7 @@ import type { ContextMenu } from './ContextMenu'
import type { LGraphNode } from './LGraphNode' import type { LGraphNode } from './LGraphNode'
import { LiteGraphGlobal } from './LiteGraphGlobal' import { LiteGraphGlobal } from './LiteGraphGlobal'
import type { ConnectingLink, Point } from './interfaces' import type { ConnectingLink, Point } from './interfaces'
import type { IContextMenuOptions, INodeSlot, Size } from './interfaces' import type { IContextMenuOptions, Size } from './interfaces'
import { loadPolyfills } from './polyfills' import { loadPolyfills } from './polyfills'
import type { CanvasEventDetail } from './types/events' import type { CanvasEventDetail } from './types/events'
import type { RenderShape, TitleMode } from './types/globalEnums' import type { RenderShape, TitleMode } from './types/globalEnums'
@@ -22,8 +22,6 @@ loadPolyfills()
// Definitions by: NateScarlet <https://github.com/NateScarlet> // Definitions by: NateScarlet <https://github.com/NateScarlet>
/** @deprecated Use {@link Point} instead. */ /** @deprecated Use {@link Point} instead. */
export type Vector2 = Point export type Vector2 = Point
/** @deprecated Use {@link Rect} instead. */
export type Vector4 = [number, number, number, number]
export interface IContextMenuItem { export interface IContextMenuItem {
content: string content: string
@@ -46,14 +44,6 @@ export type ContextMenuEventListener = (
node: LGraphNode node: LGraphNode
) => boolean | void ) => boolean | void
export interface LinkReleaseContext {
node_to?: LGraphNode
node_from?: LGraphNode
slot_from: INodeSlot
type_filter_in?: string
type_filter_out?: string
}
export interface LinkReleaseContextExtended { export interface LinkReleaseContextExtended {
links: ConnectingLink[] links: ConnectingLink[]
} }
@@ -117,7 +107,6 @@ export type {
LinkNetwork, LinkNetwork,
LinkSegment, LinkSegment,
MethodNames, MethodNames,
PickByType,
Point, Point,
Positionable, Positionable,
ReadonlyLinkNetwork, ReadonlyLinkNetwork,

View File

@@ -84,10 +84,6 @@ export function isINodeInputSlot(slot: INodeSlot): slot is INodeInputSlot {
return 'link' in slot return 'link' in slot
} }
export function isINodeOutputSlot(slot: INodeSlot): slot is INodeOutputSlot {
return 'links' in slot
}
/** /**
* Type guard: Whether this input slot is attached to a widget. * Type guard: Whether this input slot is attached to a widget.
* @param slot The slot to check. * @param slot The slot to check.

View File

@@ -50,9 +50,6 @@ export interface CanvasMouseEvent
Readonly<CanvasPointerExtensions>, Readonly<CanvasPointerExtensions>,
LegacyMouseEvent {} LegacyMouseEvent {}
/** DragEvent with canvasX/Y and deltaX/Y properties */
export interface CanvasDragEvent extends DragEvent, CanvasPointerExtensions {}
export type CanvasEventDetail = export type CanvasEventDetail =
| GenericEventDetail | GenericEventDetail
| GroupDoubleClickEventDetail | GroupDoubleClickEventDetail

View File

@@ -89,9 +89,6 @@ export enum LGraphEventMode {
} }
export enum EaseFunction { export enum EaseFunction {
LINEAR = 'linear',
EASE_IN_QUAD = 'easeInQuad',
EASE_OUT_QUAD = 'easeOutQuad',
EASE_IN_OUT_QUAD = 'easeInOutQuad' EASE_IN_OUT_QUAD = 'easeInOutQuad'
} }

View File

@@ -179,14 +179,6 @@ export interface ISerialisedGroup {
flags?: IGraphGroupFlags flags?: IGraphGroupFlags
} }
export type TClipboardLink = [
targetRelativeIndex: number,
originSlot: number,
nodeRelativeIndex: number,
targetSlot: number,
targetNodeId: NodeId
]
/** Items copied from the canvas */ /** Items copied from the canvas */
export interface ClipboardItems { export interface ClipboardItems {
nodes?: ISerialisedNode[] nodes?: ISerialisedNode[]
@@ -196,12 +188,6 @@ export interface ClipboardItems {
subgraphs?: ExportedSubgraph[] subgraphs?: ExportedSubgraph[]
} }
/** @deprecated */
export interface IClipboardContents {
nodes?: ISerialisedNode[]
links?: TClipboardLink[]
}
export interface SerialisableReroute { export interface SerialisableReroute {
id: RerouteId id: RerouteId
parentId?: RerouteId parentId?: RerouteId

View File

@@ -1,8 +1,7 @@
import { LGraphNode } from '@/lib/litegraph/src/LGraphNode' import { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
import type { LinkId } from '@/lib/litegraph/src/LLink'
import { parseSlotTypes } from '@/lib/litegraph/src/strings' import { parseSlotTypes } from '@/lib/litegraph/src/strings'
import type { ConnectingLink, ISlotType, Positionable } from '../interfaces' import type { ISlotType, Positionable } from '../interfaces'
/** /**
* Creates a flat set of all positionable items by recursively iterating through all child items. * Creates a flat set of all positionable items by recursively iterating through all child items.
@@ -45,19 +44,6 @@ export function findFirstNode(
} }
} }
/** @returns `true` if the provided link ID is currently being dragged. */
export function isDraggingLink(
linkId: LinkId,
connectingLinks: ConnectingLink[] | null | undefined
): ConnectingLink | undefined {
if (connectingLinks == null) return
for (const connectingLink of connectingLinks) {
if (connectingLink.link == null) continue
if (linkId === connectingLink.link.id) return connectingLink
}
}
type FreeSlotResult<T extends { type: ISlotType }> = type FreeSlotResult<T extends { type: ISlotType }> =
| { index: number; slot: T } | { index: number; slot: T }
| undefined | undefined

View File

@@ -1,14 +1,7 @@
import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode' import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode'
import type { import type {
IBaseWidget, IBaseWidget,
IBooleanWidget,
IButtonWidget,
IComboWidget, IComboWidget,
ICustomWidget,
IKnobWidget,
INumericWidget,
ISliderWidget,
IStringWidget,
IWidget, IWidget,
TWidgetType TWidgetType
} from '@/lib/litegraph/src/types/widgets' } from '@/lib/litegraph/src/types/widgets'
@@ -130,49 +123,9 @@ export function toConcreteWidget<TWidget extends IWidget | IBaseWidget>(
// #region Type Guards // #region Type Guards
/** Type guard: Narrow **from {@link IBaseWidget}** to {@link IButtonWidget}. */
export function isButtonWidget(widget: IBaseWidget): widget is IButtonWidget {
return widget.type === 'button'
}
/** Type guard: Narrow **from {@link IBaseWidget}** to {@link IBooleanWidget}. */
export function isBooleanWidget(widget: IBaseWidget): widget is IBooleanWidget {
return widget.type === 'toggle'
}
/** Type guard: Narrow **from {@link IBaseWidget}** to {@link ISliderWidget}. */
export function isSliderWidget(widget: IBaseWidget): widget is ISliderWidget {
return widget.type === 'slider'
}
/** Type guard: Narrow **from {@link IBaseWidget}** to {@link IKnobWidget}. */
export function isKnobWidget(widget: IBaseWidget): widget is IKnobWidget {
return widget.type === 'knob'
}
/** Type guard: Narrow **from {@link IBaseWidget}** to {@link IComboWidget}. */ /** Type guard: Narrow **from {@link IBaseWidget}** to {@link IComboWidget}. */
export function isComboWidget(widget: IBaseWidget): widget is IComboWidget { export function isComboWidget(widget: IBaseWidget): widget is IComboWidget {
return widget.type === 'combo' return widget.type === 'combo'
} }
/** Type guard: Narrow **from {@link IBaseWidget}** to {@link INumericWidget}. */
export function isNumberWidget(widget: IBaseWidget): widget is INumericWidget {
return widget.type === 'number'
}
/** Type guard: Narrow **from {@link IBaseWidget}** to {@link IStringWidget}. */
export function isStringWidget(widget: IBaseWidget): widget is IStringWidget {
return widget.type === 'string'
}
/** Type guard: Narrow **from {@link IBaseWidget}** to {@link ITextWidget}. */
export function isTextWidget(widget: IBaseWidget): widget is IStringWidget {
return widget.type === 'text'
}
/** Type guard: Narrow **from {@link IBaseWidget}** to {@link ICustomWidget}. */
export function isCustomWidget(widget: IBaseWidget): widget is ICustomWidget {
return widget.type === 'custom'
}
// #endregion Type Guards // #endregion Type Guards

View File

@@ -169,140 +169,3 @@ export const subgraphTest = test.extend<SubgraphFixtures>({
capture.cleanup() capture.cleanup()
} }
}) })
/**
* Fixtures that test edge cases and error conditions.
* These may leave the system in an invalid state and should be used carefully.
*/
export interface EdgeCaseFixtures {
/** Subgraph with circular references (for testing recursion detection) */
circularSubgraph: {
rootGraph: LGraph
subgraphA: Subgraph
subgraphB: Subgraph
nodeA: SubgraphNode
nodeB: SubgraphNode
}
/** Deeply nested subgraphs approaching the theoretical limit */
deeplyNestedSubgraph: ReturnType<typeof createNestedSubgraphs>
/** Subgraph with maximum inputs and outputs */
maxIOSubgraph: Subgraph
}
/**
* Test with edge case fixtures. Use sparingly and with caution.
* These tests may intentionally create invalid states.
*/
export const edgeCaseTest = subgraphTest.extend<EdgeCaseFixtures>({
// @ts-expect-error TODO: Fix after merge - fixture use parameter type
// eslint-disable-next-line no-empty-pattern
circularSubgraph: async ({}, use: (value: unknown) => Promise<void>) => {
const rootGraph = new LGraph()
// Create two subgraphs that will reference each other
const subgraphA = createTestSubgraph({
name: 'Subgraph A',
inputs: [{ name: 'input', type: '*' }],
outputs: [{ name: 'output', type: '*' }]
})
const subgraphB = createTestSubgraph({
name: 'Subgraph B',
inputs: [{ name: 'input', type: '*' }],
outputs: [{ name: 'output', type: '*' }]
})
// Create instances (this doesn't create circular refs by itself)
const nodeA = createTestSubgraphNode(subgraphA, { pos: [100, 100] })
const nodeB = createTestSubgraphNode(subgraphB, { pos: [300, 100] })
// Add nodes to root graph
rootGraph.add(nodeA)
rootGraph.add(nodeB)
await use({
rootGraph,
subgraphA,
subgraphB,
nodeA,
nodeB
})
},
// @ts-expect-error TODO: Fix after merge - fixture use parameter type
// eslint-disable-next-line no-empty-pattern
deeplyNestedSubgraph: async ({}, use: (value: unknown) => Promise<void>) => {
// Create a very deep nesting structure (but not exceeding MAX_NESTED_SUBGRAPHS)
const nested = createNestedSubgraphs({
depth: 50, // Deep but reasonable
nodesPerLevel: 1,
inputsPerSubgraph: 1,
outputsPerSubgraph: 1
})
await use(nested)
},
// @ts-expect-error TODO: Fix after merge - fixture use parameter type
// eslint-disable-next-line no-empty-pattern
maxIOSubgraph: async ({}, use: (value: unknown) => Promise<void>) => {
// Create a subgraph with many inputs and outputs
const inputs = Array.from({ length: 20 }, (_, i) => ({
name: `input_${i}`,
type: i % 2 === 0 ? 'number' : ('string' as const)
}))
const outputs = Array.from({ length: 20 }, (_, i) => ({
name: `output_${i}`,
type: i % 2 === 0 ? 'number' : ('string' as const)
}))
const subgraph = createTestSubgraph({
name: 'Max IO Subgraph',
inputs,
outputs,
nodeCount: 10
})
await use(subgraph)
}
})
/**
* Helper to verify fixture integrity.
* Use this in tests to ensure fixtures are properly set up.
*/
export function verifyFixtureIntegrity<T extends Record<string, unknown>>(
fixture: T,
expectedProperties: (keyof T)[]
): void {
for (const prop of expectedProperties) {
if (!(prop in fixture)) {
throw new Error(`Fixture missing required property: ${String(prop)}`)
}
if (fixture[prop] === undefined || fixture[prop] === null) {
throw new Error(`Fixture property ${String(prop)} is null or undefined`)
}
}
}
/**
* Creates a snapshot-friendly representation of a subgraph for testing.
* Useful for serialization tests and regression detection.
*/
export function createSubgraphSnapshot(subgraph: Subgraph) {
return {
id: subgraph.id,
name: subgraph.name,
inputCount: subgraph.inputs.length,
outputCount: subgraph.outputs.length,
nodeCount: subgraph.nodes.length,
linkCount: subgraph.links.size,
inputs: subgraph.inputs.map((i) => ({ name: i.name, type: i.type })),
outputs: subgraph.outputs.map((o) => ({ name: o.name, type: o.type })),
hasInputNode: !!subgraph.inputNode,
hasOutputNode: !!subgraph.outputNode
}
}

View File

@@ -382,76 +382,6 @@ export function createTestSubgraphData(
} }
} }
/**
* Creates a complex subgraph with multiple nodes and connections.
* Useful for testing realistic scenarios.
* @param nodeCount Number of internal nodes to create
* @returns Complex subgraph data structure
*/
export function createComplexSubgraphData(
nodeCount: number = 5
): ExportedSubgraph {
const nodes = []
const links: Record<
string,
{
id: number
origin_id: number
origin_slot: number
target_id: number
target_slot: number
type: string
}
> = {}
// Create internal nodes
for (let i = 0; i < nodeCount; i++) {
nodes.push({
id: i + 1, // Start from 1 to avoid conflicts with IO nodes
type: 'basic/test',
pos: [100 + i * 150, 200],
size: [120, 60],
inputs: [{ name: 'in', type: '*', link: null }],
outputs: [{ name: 'out', type: '*', links: [] }],
properties: { value: i },
flags: {},
mode: 0
})
}
// Create some internal links
for (let i = 0; i < nodeCount - 1; i++) {
const linkId = i + 1
links[linkId] = {
id: linkId,
origin_id: i + 1,
origin_slot: 0,
target_id: i + 2,
target_slot: 0,
type: '*'
}
}
return createTestSubgraphData({
// @ts-expect-error TODO: Fix after merge - nodes parameter type
nodes,
// @ts-expect-error TODO: Fix after merge - links parameter type
links,
inputs: [
// @ts-expect-error TODO: Fix after merge - input object type
{ name: 'input1', type: 'number', pos: [0, 0] },
// @ts-expect-error TODO: Fix after merge - input object type
{ name: 'input2', type: 'string', pos: [0, 1] }
],
outputs: [
// @ts-expect-error TODO: Fix after merge - output object type
{ name: 'output1', type: 'number', pos: [0, 0] },
// @ts-expect-error TODO: Fix after merge - output object type
{ name: 'output2', type: 'string', pos: [0, 1] }
]
})
}
/** /**
* Creates an event capture system for testing event sequences. * Creates an event capture system for testing event sequences.
* @param eventTarget The event target to monitor * @param eventTarget The event target to monitor
@@ -493,39 +423,5 @@ export function createEventCapture<T = unknown>(
} }
} }
/**
* Utility to log subgraph structure for debugging tests.
* @param subgraph The subgraph to inspect
* @param label Optional label for the log output
*/
export function logSubgraphStructure(
subgraph: Subgraph,
label: string = 'Subgraph'
): void {
console.log(`\n=== ${label} Structure ===`)
console.log(`Name: ${subgraph.name}`)
console.log(`ID: ${subgraph.id}`)
console.log(`Inputs: ${subgraph.inputs.length}`)
console.log(`Outputs: ${subgraph.outputs.length}`)
console.log(`Nodes: ${subgraph.nodes.length}`)
console.log(`Links: ${subgraph.links.size}`)
if (subgraph.inputs.length > 0) {
console.log(
'Input details:',
subgraph.inputs.map((i) => ({ name: i.name, type: i.type }))
)
}
if (subgraph.outputs.length > 0) {
console.log(
'Output details:',
subgraph.outputs.map((o) => ({ name: o.name, type: o.type }))
)
}
console.log('========================\n')
}
// Re-export expect from vitest for convenience // Re-export expect from vitest for convenience
export { expect } from 'vitest' export { expect } from 'vitest'

View File

@@ -1,5 +1,4 @@
import { z } from 'zod' import { z } from 'zod'
import { fromZodError } from 'zod-validation-error'
import { LinkMarkerShape } from '@/lib/litegraph/src/litegraph' import { LinkMarkerShape } from '@/lib/litegraph/src/litegraph'
import { colorPalettesSchema } from '@/schemas/colorPaletteSchema' import { colorPalettesSchema } from '@/schemas/colorPaletteSchema'
@@ -280,18 +279,6 @@ export type PendingTaskItem = z.infer<typeof zPendingTaskItem>
export type HistoryTaskItem = z.infer<typeof zHistoryTaskItem> export type HistoryTaskItem = z.infer<typeof zHistoryTaskItem>
export type TaskItem = z.infer<typeof zTaskItem> export type TaskItem = z.infer<typeof zTaskItem>
export function validateTaskItem(taskItem: unknown) {
const result = zTaskItem.safeParse(taskItem)
if (!result.success) {
const zodError = fromZodError(result.error)
// TODO accept a callback to report error.
console.warn(
`Invalid TaskItem: ${JSON.stringify(taskItem)}\n${zodError.message}`
)
}
return result
}
const zEmbeddingsResponse = z.array(z.string()) const zEmbeddingsResponse = z.array(z.string())
const zExtensionsResponse = z.array(z.string()) const zExtensionsResponse = z.array(z.string())
const zError = z.object({ const zError = z.object({

View File

@@ -447,14 +447,11 @@ export const zSubgraphDefinition = zComfyWorkflow1
.passthrough() .passthrough()
export type ModelFile = z.infer<typeof zModelFile> export type ModelFile = z.infer<typeof zModelFile>
export type NodeInput = z.infer<typeof zNodeInput>
export type NodeOutput = z.infer<typeof zNodeOutput>
export type ComfyLink = z.infer<typeof zComfyLink> export type ComfyLink = z.infer<typeof zComfyLink>
export type ComfyLinkObject = z.infer<typeof zComfyLinkObject> export type ComfyLinkObject = z.infer<typeof zComfyLinkObject>
export type ComfyNode = z.infer<typeof zComfyNode> export type ComfyNode = z.infer<typeof zComfyNode>
export type Reroute = z.infer<typeof zReroute> export type Reroute = z.infer<typeof zReroute>
export type WorkflowJSON04 = z.infer<typeof zComfyWorkflow> export type WorkflowJSON04 = z.infer<typeof zComfyWorkflow>
export type WorkflowJSON10 = z.infer<typeof zComfyWorkflow1>
export type ComfyWorkflowJSON = z.infer< export type ComfyWorkflowJSON = z.infer<
typeof zComfyWorkflow | typeof zComfyWorkflow1 typeof zComfyWorkflow | typeof zComfyWorkflow1
> >

View File

@@ -214,9 +214,7 @@ export type StringInputSpec = z.infer<typeof zStringInputSpec>
export type ComboInputSpec = z.infer<typeof zComboInputSpec> export type ComboInputSpec = z.infer<typeof zComboInputSpec>
export type ColorInputSpec = z.infer<typeof zColorInputSpec> export type ColorInputSpec = z.infer<typeof zColorInputSpec>
export type FileUploadInputSpec = z.infer<typeof zFileUploadInputSpec> export type FileUploadInputSpec = z.infer<typeof zFileUploadInputSpec>
export type ImageInputSpec = z.infer<typeof zImageInputSpec>
export type ImageCompareInputSpec = z.infer<typeof zImageCompareInputSpec> export type ImageCompareInputSpec = z.infer<typeof zImageCompareInputSpec>
export type MarkdownInputSpec = z.infer<typeof zMarkdownInputSpec>
export type TreeSelectInputSpec = z.infer<typeof zTreeSelectInputSpec> export type TreeSelectInputSpec = z.infer<typeof zTreeSelectInputSpec>
export type MultiSelectInputSpec = z.infer<typeof zMultiSelectInputSpec> export type MultiSelectInputSpec = z.infer<typeof zMultiSelectInputSpec>
export type ChartInputSpec = z.infer<typeof zChartInputSpec> export type ChartInputSpec = z.infer<typeof zChartInputSpec>

View File

@@ -128,30 +128,12 @@ export function isFloatInputSpec(
return inputSpec[0] === 'FLOAT' return inputSpec[0] === 'FLOAT'
} }
export function isBooleanInputSpec(
inputSpec: InputSpec
): inputSpec is BooleanInputSpec {
return inputSpec[0] === 'BOOLEAN'
}
export function isStringInputSpec(
inputSpec: InputSpec
): inputSpec is StringInputSpec {
return inputSpec[0] === 'STRING'
}
export function isComboInputSpecV2( export function isComboInputSpecV2(
inputSpec: InputSpec inputSpec: InputSpec
): inputSpec is ComboInputSpecV2 { ): inputSpec is ComboInputSpecV2 {
return inputSpec[0] === 'COMBO' return inputSpec[0] === 'COMBO'
} }
export function isCustomInputSpec(
inputSpec: InputSpec
): inputSpec is CustomInputSpec {
return typeof inputSpec[0] === 'string' && !excludedLiterals.has(inputSpec[0])
}
export function isComboInputSpec( export function isComboInputSpec(
inputSpec: InputSpec inputSpec: InputSpec
): inputSpec is ComboInputSpec | ComboInputSpecV2 { ): inputSpec is ComboInputSpec | ComboInputSpecV2 {
@@ -247,22 +229,13 @@ export type ComfyOutputTypesSpec = z.infer<typeof zComfyOutputTypesSpec>
export type ComfyNodeDef = z.infer<typeof zComfyNodeDef> export type ComfyNodeDef = z.infer<typeof zComfyNodeDef>
export type RemoteWidgetConfig = z.infer<typeof zRemoteWidgetConfig> export type RemoteWidgetConfig = z.infer<typeof zRemoteWidgetConfig>
// Input specs
export type IntInputOptions = z.infer<typeof zIntInputOptions>
export type FloatInputOptions = z.infer<typeof zFloatInputOptions>
export type BooleanInputOptions = z.infer<typeof zBooleanInputOptions>
export type StringInputOptions = z.infer<typeof zStringInputOptions>
export type ComboInputOptions = z.infer<typeof zComboInputOptions> export type ComboInputOptions = z.infer<typeof zComboInputOptions>
export type BaseInputOptions = z.infer<typeof zBaseInputOptions>
export type NumericInputOptions = z.infer<typeof zNumericInputOptions> export type NumericInputOptions = z.infer<typeof zNumericInputOptions>
export type IntInputSpec = z.infer<typeof zIntInputSpec> export type IntInputSpec = z.infer<typeof zIntInputSpec>
export type FloatInputSpec = z.infer<typeof zFloatInputSpec> export type FloatInputSpec = z.infer<typeof zFloatInputSpec>
export type BooleanInputSpec = z.infer<typeof zBooleanInputSpec>
export type StringInputSpec = z.infer<typeof zStringInputSpec>
export type ComboInputSpec = z.infer<typeof zComboInputSpec> export type ComboInputSpec = z.infer<typeof zComboInputSpec>
export type ComboInputSpecV2 = z.infer<typeof zComboInputSpecV2> export type ComboInputSpecV2 = z.infer<typeof zComboInputSpecV2>
export type CustomInputSpec = z.infer<typeof zCustomInputSpec>
export type InputSpec = z.infer<typeof zInputSpec> export type InputSpec = z.infer<typeof zInputSpec>
export function validateComfyNodeDef( export function validateComfyNodeDef(

View File

@@ -10,8 +10,6 @@ export const apiKeySchema = z.object({
.length(72, t('validation.length', { length: 72 })) .length(72, t('validation.length', { length: 72 }))
}) })
export type ApiKeyData = z.infer<typeof apiKeySchema>
export const signInSchema = z.object({ export const signInSchema = z.object({
email: z email: z
.string() .string()
@@ -42,8 +40,6 @@ export const updatePasswordSchema = passwordSchema.refine(
} }
) )
export type UpdatePasswordData = z.infer<typeof updatePasswordSchema>
export const signUpSchema = passwordSchema export const signUpSchema = passwordSchema
.extend({ .extend({
email: z email: z

View File

@@ -375,17 +375,3 @@ LGraphNode.prototype.addDOMWidget = function <
return widget return widget
} }
/**
* Prunes widgets that are no longer in the graph.
* @param nodes The nodes to prune widgets for.
*/
export const pruneWidgets = (nodes: LGraphNode[]) => {
const nodeSet = new Set(nodes)
const domWidgetStore = useDomWidgetStore()
for (const { widget } of domWidgetStore.widgetStates.values()) {
if (!nodeSet.has(widget.node)) {
domWidgetStore.unregisterWidget(widget.id)
}
}
}

View File

@@ -17,6 +17,7 @@ export function clone<T>(obj: T): T {
} }
/** /**
* @knipIgnoreUnusedButUsedByCustomNodes
* @deprecated Use `applyTextReplacements` from `@/utils/searchAndReplace` instead * @deprecated Use `applyTextReplacements` from `@/utils/searchAndReplace` instead
* There are external callers to this function, so we need to keep it for now * There are external callers to this function, so we need to keep it for now
*/ */
@@ -24,6 +25,7 @@ export function applyTextReplacements(app: ComfyApp, value: string): string {
return _applyTextReplacements(app.graph, value) return _applyTextReplacements(app.graph, value)
} }
/** @knipIgnoreUnusedButUsedByCustomNodes */
export async function addStylesheet( export async function addStylesheet(
urlOrFile: string, urlOrFile: string,
relativeTo?: string relativeTo?: string

View File

@@ -1,7 +1,4 @@
import type { import type { BaseSearchParamsWithoutQuery } from 'algoliasearch/dist/lite/browser'
BaseSearchParamsWithoutQuery,
Hit
} from 'algoliasearch/dist/lite/browser'
import type { components } from '@/types/comfyRegistryTypes' import type { components } from '@/types/comfyRegistryTypes'
@@ -13,15 +10,6 @@ type SafeNestedProperty<
type RegistryNodePack = components['schemas']['Node'] type RegistryNodePack = components['schemas']['Node']
/**
* Result of searching the Algolia index.
* Represents the entire result of a search query.
*/
export type SearchPacksResult = {
nodePacks: Hit<AlgoliaNodePack>[]
querySuggestions: Hit<NodesIndexSuggestion>[]
}
/** /**
* Node pack record after it has been mapped to Algolia index format. * Node pack record after it has been mapped to Algolia index format.
* @see https://github.com/Comfy-Org/comfy-api/blob/main/mapper/algolia.go * @see https://github.com/Comfy-Org/comfy-api/blob/main/mapper/algolia.go

View File

@@ -2,7 +2,6 @@ import type { InjectionKey, Ref } from 'vue'
import type { AlgoliaNodePack } from '@/types/algoliaTypes' import type { AlgoliaNodePack } from '@/types/algoliaTypes'
import type { components } from '@/types/comfyRegistryTypes' import type { components } from '@/types/comfyRegistryTypes'
import type { components as managerComponents } from '@/types/generatedManagerTypes'
export type RegistryPack = components['schemas']['Node'] export type RegistryPack = components['schemas']['Node']
export type MergedNodePack = RegistryPack & AlgoliaNodePack export type MergedNodePack = RegistryPack & AlgoliaNodePack
@@ -10,16 +9,9 @@ export const isMergedNodePack = (
nodePack: RegistryPack | AlgoliaNodePack nodePack: RegistryPack | AlgoliaNodePack
): nodePack is MergedNodePack => 'comfy_nodes' in nodePack ): nodePack is MergedNodePack => 'comfy_nodes' in nodePack
export type PackField = keyof RegistryPack | null
export const IsInstallingKey: InjectionKey<Ref<boolean>> = export const IsInstallingKey: InjectionKey<Ref<boolean>> =
Symbol('isInstalling') Symbol('isInstalling')
export enum ManagerWsQueueStatus {
DONE = 'all-done',
IN_PROGRESS = 'in_progress'
}
export enum ManagerTab { export enum ManagerTab {
All = 'all', All = 'all',
Installed = 'installed', Installed = 'installed',
@@ -34,31 +26,12 @@ export interface TabItem {
icon: string icon: string
} }
export enum ManagerSortField {
Author = 'author',
CreateDate = 'creation_date',
LastUpdateDate = 'last_update',
Name = 'name',
Stars = 'stars',
Size = 'size'
}
export enum PackEnableState {
Enabled,
Disabled,
NotInstalled
}
export type TaskLog = { export type TaskLog = {
taskName: string taskName: string
taskId: string taskId: string
logs: string[] logs: string[]
} }
export interface ManagerQueueOptions {
maxConcurrent?: number
}
export interface UseNodePacksOptions { export interface UseNodePacksOptions {
immediate?: boolean immediate?: boolean
maxConcurrent?: number maxConcurrent?: number
@@ -83,13 +56,3 @@ export interface ManagerState {
searchMode: 'nodes' | 'packs' searchMode: 'nodes' | 'packs'
sortField: string sortField: string
} }
/**
* Types for import failure information API
*/
export type ImportFailInfoBulkRequest =
managerComponents['schemas']['ImportFailInfoBulkRequest']
export type ImportFailInfoBulkResponse =
managerComponents['schemas']['ImportFailInfoBulkResponse']
export type ImportFailInfoItem =
managerComponents['schemas']['ImportFailInfoItem']

View File

@@ -9,9 +9,6 @@ import type { components } from './comfyRegistryTypes'
// Re-export core types from Registry API // Re-export core types from Registry API
export type Node = components['schemas']['Node'] export type Node = components['schemas']['Node']
export type NodeVersion = components['schemas']['NodeVersion']
export type NodeStatus = components['schemas']['NodeStatus']
export type NodeVersionStatus = components['schemas']['NodeVersionStatus']
/** /**
* Conflict types that can be detected in the system * Conflict types that can be detected in the system
@@ -27,22 +24,6 @@ export type ConflictType =
| 'banned' // Banned package | 'banned' // Banned package
| 'pending' // Security verification pending | 'pending' // Security verification pending
/**
* Version comparison operators
* @enum {string}
*/
export type VersionOperator = '>=' | '>' | '<=' | '<' | '==' | '!='
/**
* Version requirement specification
*/
export interface VersionRequirement {
/** @description Comparison operator for version checking */
operator: VersionOperator
/** @description Target version string */
version: string
}
/** /**
* Node Pack requirements from Registry API * Node Pack requirements from Registry API
* Extends Node type with additional installation and compatibility metadata * Extends Node type with additional installation and compatibility metadata

View File

@@ -1,9 +1,3 @@
export enum LinkReleaseTriggerMode {
ALWAYS = 'always',
HOLD_SHIFT = 'hold shift',
NOT_HOLD_SHIFT = 'NOT hold shift'
}
export enum LinkReleaseTriggerAction { export enum LinkReleaseTriggerAction {
CONTEXT_MENU = 'context menu', CONTEXT_MENU = 'context menu',
SEARCH_BOX = 'search box', SEARCH_BOX = 'search box',

View File

@@ -20,15 +20,6 @@ export enum HashFunction {
SHA512 = 'sha512' SHA512 = 'sha512'
} }
export enum AutoLaunch {
// Let server decide whether to auto launch based on the current environment
Auto = 'auto',
// Disable auto launch
Disable = 'disable',
// Enable auto launch
Enable = 'enable'
}
export enum CudaMalloc { export enum CudaMalloc {
// Let server decide whether to use CUDA malloc based on the current environment // Let server decide whether to use CUDA malloc based on the current environment
Auto = 'auto', Auto = 'auto',

View File

@@ -25,13 +25,6 @@ export interface SettingOption {
value?: any value?: any
} }
export interface Setting {
id: keyof Settings
onChange?: (value: any, oldValue?: any) => void
name: string
render: () => HTMLElement
}
export interface SettingParams<TValue = unknown> extends FormItem { export interface SettingParams<TValue = unknown> extends FormItem {
id: keyof Settings id: keyof Settings
defaultValue: any | (() => any) defaultValue: any | (() => any)

View File

@@ -33,10 +33,6 @@ export function appendJsonExt(path: string) {
return path return path
} }
export function trimJsonExt(path?: string) {
return path?.replace(/\.json$/, '')
}
export function highlightQuery(text: string, query: string) { export function highlightQuery(text: string, query: string) {
if (!query) return text if (!query) return text
@@ -80,28 +76,6 @@ export function formatSize(value?: number) {
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}` return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`
} }
/**
* Finds the common directory prefix between two paths
* @example
* findCommonPrefix('a/b/c', 'a/b/d') // returns 'a/b'
* findCommonPrefix('x/y/z', 'a/b/c') // returns ''
* findCommonPrefix('a/b/c', 'a/b/c/d') // returns 'a/b/c'
*/
export function findCommonPrefix(path1: string, path2: string): string {
const parts1 = path1.split('/')
const parts2 = path2.split('/')
const commonParts: string[] = []
for (let i = 0; i < Math.min(parts1.length, parts2.length); i++) {
if (parts1[i] === parts2[i]) {
commonParts.push(parts1[i])
} else {
break
}
}
return commonParts.join('/')
}
/** /**
* Returns various filename components. * Returns various filename components.
* Example: * Example:
@@ -423,26 +397,6 @@ export function compareVersions(
return 0 return 0
} }
/**
* Converts a currency amount to Metronome's integer representation.
* For USD, converts to cents (multiplied by 100).
* For all other currencies (including custom pricing units), returns the amount as is.
* This is specific to Metronome's API requirements.
*
* @param amount - The amount in currency to convert
* @param currency - The currency to convert
* @returns The amount in Metronome's integer format (cents for USD, base units for others)
* @example
* toMetronomeCurrency(1.23, 'usd') // returns 123 (cents)
* toMetronomeCurrency(1000, 'jpy') // returns 1000 (yen)
*/
export function toMetronomeCurrency(amount: number, currency: string): number {
if (currency === 'usd') {
return Math.round(amount * 100)
}
return amount
}
/** /**
* Converts Metronome's integer amount back to a formatted currency string. * Converts Metronome's integer amount back to a formatted currency string.
* For USD, converts from cents to dollars. * For USD, converts from cents to dollars.

View File

@@ -15,7 +15,6 @@ import {
isFloatInputSpec, isFloatInputSpec,
isIntInputSpec isIntInputSpec
} from '@/schemas/nodeDefSchema' } from '@/schemas/nodeDefSchema'
import type { ComfyNodeDefImpl } from '@/stores/nodeDefStore'
import { lcm } from './mathUtil' import { lcm } from './mathUtil'
@@ -139,11 +138,3 @@ export const mergeInputSpec = (
return mergeCommonInputSpec(spec1, spec2) return mergeCommonInputSpec(spec1, spec2)
} }
/**
* Checks if a node definition represents a subgraph node.
* Subgraph nodes are created with category='subgraph' and python_module='nodes'.
*/
export const isSubgraphNode = (nodeDef: ComfyNodeDefImpl): boolean => {
return nodeDef.category === 'subgraph' && nodeDef.python_module === 'nodes'
}

View File

@@ -29,34 +29,6 @@ export function satisfiesVersion(version: string, range: string): boolean {
} }
} }
/**
* Compares two versions and returns the difference type
* @param version1 First version
* @param version2 Second version
* @returns Difference type or null if comparison fails
*/
export function getVersionDifference(
version1: string,
version2: string
): semver.ReleaseType | null {
try {
const clean1 = cleanVersion(version1)
const clean2 = cleanVersion(version2)
return semver.diff(clean1, clean2)
} catch {
return null
}
}
/**
* Checks if a version is valid according to semver
* @param version Version string to validate
* @returns true if version is valid
*/
export function isValidVersion(version: string): boolean {
return semver.valid(version) !== null
}
/** /**
* Checks version compatibility and returns conflict details. * Checks version compatibility and returns conflict details.
* Supports all semver ranges including >=, <=, >, <, ~, ^ operators. * Supports all semver ranges including >=, <=, >, <, ~, ^ operators.

View File

@@ -113,16 +113,7 @@ LiteGraphGlobal {
"Reroute": [Function], "Reroute": [Function],
"SPLINE_LINK": 2, "SPLINE_LINK": 2,
"STRAIGHT_LINK": 0, "STRAIGHT_LINK": 0,
"SlotDirection": { "SlotDirection": {},
"1": "Up",
"2": "Down",
"3": "Left",
"4": "Right",
"Down": 2,
"Left": 3,
"Right": 4,
"Up": 1,
},
"SlotShape": { "SlotShape": {
"1": "Box", "1": "Box",
"3": "Circle", "3": "Circle",

View File

@@ -169,140 +169,3 @@ export const subgraphTest = test.extend<SubgraphFixtures>({
capture.cleanup() capture.cleanup()
} }
}) })
/**
* Fixtures that test edge cases and error conditions.
* These may leave the system in an invalid state and should be used carefully.
*/
export interface EdgeCaseFixtures {
/** Subgraph with circular references (for testing recursion detection) */
circularSubgraph: {
rootGraph: LGraph
subgraphA: Subgraph
subgraphB: Subgraph
nodeA: SubgraphNode
nodeB: SubgraphNode
}
/** Deeply nested subgraphs approaching the theoretical limit */
deeplyNestedSubgraph: ReturnType<typeof createNestedSubgraphs>
/** Subgraph with maximum inputs and outputs */
maxIOSubgraph: Subgraph
}
/**
* Test with edge case fixtures. Use sparingly and with caution.
* These tests may intentionally create invalid states.
*/
export const edgeCaseTest = subgraphTest.extend<EdgeCaseFixtures>({
// @ts-expect-error TODO: Fix after merge - fixture use parameter type
// eslint-disable-next-line no-empty-pattern
circularSubgraph: async ({}, use: (value: unknown) => Promise<void>) => {
const rootGraph = new LGraph()
// Create two subgraphs that will reference each other
const subgraphA = createTestSubgraph({
name: 'Subgraph A',
inputs: [{ name: 'input', type: '*' }],
outputs: [{ name: 'output', type: '*' }]
})
const subgraphB = createTestSubgraph({
name: 'Subgraph B',
inputs: [{ name: 'input', type: '*' }],
outputs: [{ name: 'output', type: '*' }]
})
// Create instances (this doesn't create circular refs by itself)
const nodeA = createTestSubgraphNode(subgraphA, { pos: [100, 100] })
const nodeB = createTestSubgraphNode(subgraphB, { pos: [300, 100] })
// Add nodes to root graph
rootGraph.add(nodeA)
rootGraph.add(nodeB)
await use({
rootGraph,
subgraphA,
subgraphB,
nodeA,
nodeB
})
},
// @ts-expect-error TODO: Fix after merge - fixture use parameter type
// eslint-disable-next-line no-empty-pattern
deeplyNestedSubgraph: async ({}, use: (value: unknown) => Promise<void>) => {
// Create a very deep nesting structure (but not exceeding MAX_NESTED_SUBGRAPHS)
const nested = createNestedSubgraphs({
depth: 50, // Deep but reasonable
nodesPerLevel: 1,
inputsPerSubgraph: 1,
outputsPerSubgraph: 1
})
await use(nested)
},
// @ts-expect-error TODO: Fix after merge - fixture use parameter type
// eslint-disable-next-line no-empty-pattern
maxIOSubgraph: async ({}, use: (value: unknown) => Promise<void>) => {
// Create a subgraph with many inputs and outputs
const inputs = Array.from({ length: 20 }, (_, i) => ({
name: `input_${i}`,
type: i % 2 === 0 ? 'number' : ('string' as const)
}))
const outputs = Array.from({ length: 20 }, (_, i) => ({
name: `output_${i}`,
type: i % 2 === 0 ? 'number' : ('string' as const)
}))
const subgraph = createTestSubgraph({
name: 'Max IO Subgraph',
inputs,
outputs,
nodeCount: 10
})
await use(subgraph)
}
})
/**
* Helper to verify fixture integrity.
* Use this in tests to ensure fixtures are properly set up.
*/
export function verifyFixtureIntegrity<T extends Record<string, unknown>>(
fixture: T,
expectedProperties: (keyof T)[]
): void {
for (const prop of expectedProperties) {
if (!(prop in fixture)) {
throw new Error(`Fixture missing required property: ${String(prop)}`)
}
if (fixture[prop] === undefined || fixture[prop] === null) {
throw new Error(`Fixture property ${String(prop)} is null or undefined`)
}
}
}
/**
* Creates a snapshot-friendly representation of a subgraph for testing.
* Useful for serialization tests and regression detection.
*/
export function createSubgraphSnapshot(subgraph: Subgraph) {
return {
id: subgraph.id,
name: subgraph.name,
inputCount: subgraph.inputs.length,
outputCount: subgraph.outputs.length,
nodeCount: subgraph.nodes.length,
linkCount: subgraph.links.size,
inputs: subgraph.inputs.map((i) => ({ name: i.name, type: i.type })),
outputs: subgraph.outputs.map((o) => ({ name: o.name, type: o.type })),
hasInputNode: !!subgraph.inputNode,
hasOutputNode: !!subgraph.outputNode
}
}

View File

@@ -382,76 +382,6 @@ export function createTestSubgraphData(
} }
} }
/**
* Creates a complex subgraph with multiple nodes and connections.
* Useful for testing realistic scenarios.
* @param nodeCount Number of internal nodes to create
* @returns Complex subgraph data structure
*/
export function createComplexSubgraphData(
nodeCount: number = 5
): ExportedSubgraph {
const nodes = []
const links: Record<
string,
{
id: number
origin_id: number
origin_slot: number
target_id: number
target_slot: number
type: string
}
> = {}
// Create internal nodes
for (let i = 0; i < nodeCount; i++) {
nodes.push({
id: i + 1, // Start from 1 to avoid conflicts with IO nodes
type: 'basic/test',
pos: [100 + i * 150, 200],
size: [120, 60],
inputs: [{ name: 'in', type: '*', link: null }],
outputs: [{ name: 'out', type: '*', links: [] }],
properties: { value: i },
flags: {},
mode: 0
})
}
// Create some internal links
for (let i = 0; i < nodeCount - 1; i++) {
const linkId = i + 1
links[linkId] = {
id: linkId,
origin_id: i + 1,
origin_slot: 0,
target_id: i + 2,
target_slot: 0,
type: '*'
}
}
return createTestSubgraphData({
// @ts-expect-error TODO: Fix after merge - nodes parameter type
nodes,
// @ts-expect-error TODO: Fix after merge - links parameter type
links,
inputs: [
// @ts-expect-error TODO: Fix after merge - input object type
{ name: 'input1', type: 'number', pos: [0, 0] },
// @ts-expect-error TODO: Fix after merge - input object type
{ name: 'input2', type: 'string', pos: [0, 1] }
],
outputs: [
// @ts-expect-error TODO: Fix after merge - output object type
{ name: 'output1', type: 'number', pos: [0, 0] },
// @ts-expect-error TODO: Fix after merge - output object type
{ name: 'output2', type: 'string', pos: [0, 1] }
]
})
}
/** /**
* Creates an event capture system for testing event sequences. * Creates an event capture system for testing event sequences.
* @param eventTarget The event target to monitor * @param eventTarget The event target to monitor
@@ -493,39 +423,5 @@ export function createEventCapture<T = unknown>(
} }
} }
/**
* Utility to log subgraph structure for debugging tests.
* @param subgraph The subgraph to inspect
* @param label Optional label for the log output
*/
export function logSubgraphStructure(
subgraph: Subgraph,
label: string = 'Subgraph'
): void {
console.log(`\n=== ${label} Structure ===`)
console.log(`Name: ${subgraph.name}`)
console.log(`ID: ${subgraph.id}`)
console.log(`Inputs: ${subgraph.inputs.length}`)
console.log(`Outputs: ${subgraph.outputs.length}`)
console.log(`Nodes: ${subgraph.nodes.length}`)
console.log(`Links: ${subgraph.links.size}`)
if (subgraph.inputs.length > 0) {
console.log(
'Input details:',
subgraph.inputs.map((i) => ({ name: i.name, type: i.type }))
)
}
if (subgraph.outputs.length > 0) {
console.log(
'Output details:',
subgraph.outputs.map((o) => ({ name: o.name, type: o.type }))
)
}
console.log('========================\n')
}
// Re-export expect from vitest for convenience // Re-export expect from vitest for convenience
export { expect } from 'vitest' export { expect } from 'vitest'