From fde6ab886dee1e9a7787531b077018c196292f80 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 8 Sep 2026 23:33:32 +0000 Subject: [PATCH] fix(registry): match scoped definition names on Windows loadDefinition derives the expected name from relative(managerDir, filePath), which returns platform separators. On Windows a scoped definition yields "@apollo\client" and never matches the "@apollo/client" inside the file, so listDefinitions() threw for every scoped package and no registry command ran at all. Reported by @TeeJS in #133, who reproduced it on main with their own definitions removed and deliberately left it unfixed to keep that PR to one concern. Normalising unconditionally rather than branching on sep keeps a single code path on every platform, so Linux CI exercises the same comparison Windows does instead of leaving the Windows branch untested. The regression test reproduces the failure on Linux: a literal backslash in the filename is one filename here rather than a separator, but the string reaching the comparison is byte-identical to what Windows produces. Without the fix it fails with the exact reported error, "Definition name \"@apollo/client\" doesn't match filename \"@apollo\\client.yaml\"". No changeset: @neuledge/registry is private. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01NBQQpA86yYzwJUiVz8ph2R --- packages/registry/src/definition.test.ts | 25 ++++++++++++++++++++++++ packages/registry/src/definition.ts | 9 ++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/packages/registry/src/definition.test.ts b/packages/registry/src/definition.test.ts index 9f53de0..3738b5b 100644 --- a/packages/registry/src/definition.test.ts +++ b/packages/registry/src/definition.test.ts @@ -171,6 +171,31 @@ versions: expect(def.versions[0].tag_pattern).toBe("@trpc/server@{version}"); }); + it("accepts a scoped path that uses backslash separators", () => { + // On Windows relative() returns "@apollo\\client", which never matched the + // "@apollo/client" in the file, so listDefinitions() threw for every scoped + // definition. Reproduced here by putting a literal backslash in the filename: + // on Linux that is one filename rather than a separator, but the string + // reaching the comparison is byte-identical to what Windows produces. + const yaml = ` +name: "@apollo/client" +description: "Apollo Client" +source: + type: git + url: https://github.com/apollographql/apollo-client + docs_path: docs +`; + const npmDir = join(tempDir, "npm"); + mkdirSync(npmDir, { recursive: true }); + const filePath = join(npmDir, "@apollo\\client.yaml"); + writeFileSync(filePath, yaml); + + const def = loadDefinition(filePath, npmDir); + + expect(def.name).toBe("@apollo/client"); + expect(def.registry).toBe("npm"); + }); + it("throws when scoped name doesn't match path", () => { const yaml = ` name: "@trpc/client" diff --git a/packages/registry/src/definition.ts b/packages/registry/src/definition.ts index 9381e23..ffd6e8b 100644 --- a/packages/registry/src/definition.ts +++ b/packages/registry/src/definition.ts @@ -167,8 +167,15 @@ export function loadDefinition( // Derive expected name from relative path within managerDir // e.g., npm/@trpc/server.yaml → @trpc/server // e.g., npm/next.yaml → next + // + // relative() returns platform separators, so on Windows a scoped definition + // yields "@apollo\\client" and never matches the "@apollo/client" in the file. + // Normalising unconditionally rather than only when sep is "\\" keeps one code + // path on every platform, so Linux CI exercises the same comparison Windows does. const expectedName = managerDir - ? relative(managerDir, filePath).replace(/\.yaml$/, "") + ? relative(managerDir, filePath) + .replace(/\.yaml$/, "") + .replaceAll("\\", "/") : basename(filePath, ".yaml"); // Allow filesystem-safe encoding: colons in names are replaced with underscores