mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-05-22 13:32:11 +00:00
fix(website): query every raw-id alias when pack slugs collide
Addresses Oracle review feedback: when two raw upstream ids slugify to the same URL slug (e.g. ComfyUI-QwenVL + ComfyUI_QwenVL both -> comfyui-qwenvl) the previous merge kept only the first rawId and used only that single alias to fetch registry metadata. If that one alias missed but its twin would have resolved, the merged pack lost banner/icon/license info. Now NodePack carries rawIds: string[] holding every raw alias seen for the slug. parseCloudNodes flattens all aliases into a single registry batch and pickRegistryPack walks the alias list in insertion order to find the first non-null hit.
This commit is contained in:
@@ -345,6 +345,53 @@ describe('fetchCloudNodesForBuild', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('queries every raw-id alias when packs collide on the same slug and picks the first hit', async () => {
|
||||
fetchRegistryPacksMock.mockResolvedValue(
|
||||
new Map<string, unknown>([
|
||||
['ComfyUI-QwenVL', null],
|
||||
[
|
||||
'ComfyUI_QwenVL',
|
||||
{
|
||||
id: 'ComfyUI_QwenVL',
|
||||
name: 'ComfyUI QwenVL',
|
||||
repository: 'https://github.com/example/ComfyUI_QwenVL'
|
||||
}
|
||||
]
|
||||
])
|
||||
)
|
||||
|
||||
const fetchImpl = vi.fn(async () =>
|
||||
response({
|
||||
QwenDash: validNode({
|
||||
name: 'QwenDash',
|
||||
python_module: 'custom_nodes.ComfyUI-QwenVL.nodes'
|
||||
}),
|
||||
QwenUnder: validNode({
|
||||
name: 'QwenUnder',
|
||||
python_module: 'custom_nodes.ComfyUI_QwenVL.nodes'
|
||||
})
|
||||
})
|
||||
)
|
||||
const outcome = await fetchCloudNodesForBuild({
|
||||
apiKey: KEY,
|
||||
baseUrl: BASE_URL,
|
||||
fetchImpl: fetchImpl as typeof fetch
|
||||
})
|
||||
|
||||
expect(outcome.status).toBe('fresh')
|
||||
if (outcome.status !== 'fresh') return
|
||||
expect(outcome.snapshot.packs).toHaveLength(1)
|
||||
expect(outcome.snapshot.packs[0]?.id).toBe('comfyui-qwenvl')
|
||||
expect(outcome.snapshot.packs[0]?.registryId).toBe('ComfyUI_QwenVL')
|
||||
expect(outcome.snapshot.packs[0]?.repoUrl).toBe(
|
||||
'https://github.com/example/ComfyUI_QwenVL'
|
||||
)
|
||||
expect(fetchRegistryPacksMock).toHaveBeenCalledWith(
|
||||
['ComfyUI-QwenVL', 'ComfyUI_QwenVL'],
|
||||
expect.anything()
|
||||
)
|
||||
})
|
||||
|
||||
it('normalizes pack ids when reading a fallback snapshot', async () => {
|
||||
const snapshotUrl = withSnapshotDir({
|
||||
fetchedAt: '2026-04-01T00:00:00.000Z',
|
||||
|
||||
@@ -238,12 +238,12 @@ async function parseCloudNodes(
|
||||
)
|
||||
const grouped = groupNodesByPack(sanitizedDefs)
|
||||
|
||||
const allAliases = grouped.flatMap((pack) => pack.rawIds)
|
||||
let registryMap = new Map<string, RegistryPack | null>()
|
||||
try {
|
||||
registryMap = await fetchRegistryPacks(
|
||||
grouped.map((pack) => pack.rawId),
|
||||
{ fetchImpl: options.fetchImpl }
|
||||
)
|
||||
registryMap = await fetchRegistryPacks(allAliases, {
|
||||
fetchImpl: options.fetchImpl
|
||||
})
|
||||
} catch {
|
||||
registryMap = new Map()
|
||||
}
|
||||
@@ -253,13 +253,24 @@ async function parseCloudNodes(
|
||||
pack.id,
|
||||
pack.displayName,
|
||||
pack.nodes,
|
||||
registryMap.get(pack.rawId)
|
||||
pickRegistryPack(registryMap, pack.rawIds)
|
||||
)
|
||||
)
|
||||
|
||||
return { kind: 'ok', packs, droppedNodes }
|
||||
}
|
||||
|
||||
function pickRegistryPack(
|
||||
registryMap: Map<string, RegistryPack | null>,
|
||||
aliases: readonly string[]
|
||||
): RegistryPack | null | undefined {
|
||||
for (const alias of aliases) {
|
||||
const hit = registryMap.get(alias)
|
||||
if (hit) return hit
|
||||
}
|
||||
return registryMap.get(aliases[0])
|
||||
}
|
||||
|
||||
function safeExternalUrl(value: string | undefined): string | undefined {
|
||||
if (!value) return undefined
|
||||
try {
|
||||
|
||||
@@ -92,6 +92,17 @@ describe('groupNodesByPack', () => {
|
||||
'QwenA',
|
||||
'QwenB'
|
||||
])
|
||||
expect(grouped[0].rawIds).toEqual(['ComfyUI-QwenVL', 'ComfyUI_QwenVL'])
|
||||
})
|
||||
|
||||
it('does not record duplicate aliases when the same raw id appears twice', () => {
|
||||
const grouped = groupNodesByPack({
|
||||
QwenA: makeNodeDef('QwenA', 'custom_nodes.ComfyUI-QwenVL.nodes'),
|
||||
QwenB: makeNodeDef('QwenB', 'custom_nodes.ComfyUI-QwenVL.nodes')
|
||||
})
|
||||
|
||||
expect(grouped).toHaveLength(1)
|
||||
expect(grouped[0].rawIds).toEqual(['ComfyUI-QwenVL'])
|
||||
})
|
||||
|
||||
it('strips version suffix before slugifying', () => {
|
||||
|
||||
@@ -10,6 +10,7 @@ export interface PackedNode {
|
||||
export interface NodePack {
|
||||
id: string
|
||||
rawId: string
|
||||
rawIds: string[]
|
||||
displayName: string
|
||||
nodes: PackedNode[]
|
||||
}
|
||||
@@ -40,12 +41,16 @@ export function groupNodesByPack(
|
||||
|
||||
if (existing) {
|
||||
existing.nodes.push(node)
|
||||
if (!existing.rawIds.includes(rawId)) {
|
||||
existing.rawIds.push(rawId)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
byPackId.set(slug, {
|
||||
id: slug,
|
||||
rawId,
|
||||
rawIds: [rawId],
|
||||
displayName: source.displayText,
|
||||
nodes: [node]
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user