Files
ai-video-fullstack/frontend/src/components/workflow/useNodeSpecs.ts
T

62 lines
1.5 KiB
TypeScript
Raw Normal View History

"use client";
/**
* 拉取并缓存 /api/node-types 节点目录(每会话一次,模块级缓存)。
* 对齐 dograh 的 useNodeSpecs:规格随后端 + 刷新生效。
*/
import { useEffect, useRef, useState } from "react";
import { nodeTypesApi, type NodeTypesResponse } from "@/lib/api";
import {
toRuntimeSpec,
type NodeSpecMap,
type RuntimeNodeSpec,
} from "./specs";
let _cache: NodeTypesResponse | null = null;
type State = {
specs: RuntimeNodeSpec[];
byType: NodeSpecMap;
loading: boolean;
error: string | null;
};
function build(data: NodeTypesResponse | null): {
specs: RuntimeNodeSpec[];
byType: NodeSpecMap;
} {
const specs = (data?.nodeTypes ?? []).map(toRuntimeSpec);
const byType: NodeSpecMap = {};
for (const spec of specs) byType[spec.type] = spec;
return { specs, byType };
}
export function useNodeSpecs(): State {
const fetched = useRef(false);
const [state, setState] = useState<State>(() => {
const { specs, byType } = build(_cache);
return { specs, byType, loading: !_cache, error: null };
});
useEffect(() => {
if (_cache || fetched.current) return;
fetched.current = true;
nodeTypesApi
.list()
.then((data) => {
_cache = data;
const { specs, byType } = build(data);
setState({ specs, byType, loading: false, error: null });
})
.catch((err: unknown) => {
const message = err instanceof Error ? err.message : String(err);
setState((s) => ({ ...s, loading: false, error: message }));
});
}, []);
return state;
}