mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-28 18:22:40 +00:00
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:
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -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
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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']
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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'
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
Reference in New Issue
Block a user