diff --git a/.changeset/famous-paws-sell.md b/.changeset/famous-paws-sell.md new file mode 100644 index 000000000..fedb8e0ca --- /dev/null +++ b/.changeset/famous-paws-sell.md @@ -0,0 +1,5 @@ +--- +'hive': patch +--- + +Add schema linting support for type extensions diff --git a/packages/services/policy/__tests__/policy.spec.ts b/packages/services/policy/__tests__/policy.spec.ts index 6329cb32d..e8ee63c88 100644 --- a/packages/services/policy/__tests__/policy.spec.ts +++ b/packages/services/policy/__tests__/policy.spec.ts @@ -210,6 +210,20 @@ describe('policy checks', () => { ); }); + it('should support type extensions', async () => { + const result = await schemaPolicyApiRouter + .createCaller({ req: { log: console } as any }) + .checkPolicy({ + source: `type Query extend type Query { foo: Foo } type Foo { id: ID! }`, + schema: `type Query extend type Query { foo: Foo } type Foo { id: ID! }`, + target: '1', + policy: { + 'no-unreachable-types': [2], + }, + }); + expect(result).toHaveLength(0); + }); + /** To ensure existing policies dont break during upgrades */ it.each(policies)('should support existing policies', async policy => { await expect( diff --git a/patches/@graphql-eslint__eslint-plugin@3.20.1.patch b/patches/@graphql-eslint__eslint-plugin@3.20.1.patch index 3a2801072..d74644340 100644 --- a/patches/@graphql-eslint__eslint-plugin@3.20.1.patch +++ b/patches/@graphql-eslint__eslint-plugin@3.20.1.patch @@ -120,6 +120,40 @@ index d952cee1e10976459e7c5836b86ba6540c45fdb6..d314997bfd26477c7d823cad397eb5d6 return { [ruleId]: { meta: { +diff --git a/esm/rules/no-unreachable-types.js b/esm/rules/no-unreachable-types.js +index 17e9394c80ebb2257ec6145421fafa227872d0ca..f39ad76d7948ccc4e35f6b7d4adb26dee9e4a71b 100644 +--- a/esm/rules/no-unreachable-types.js ++++ b/esm/rules/no-unreachable-types.js +@@ -51,8 +51,15 @@ function getReachableTypes(schema) { + for (const { astNode } of [...objects, ...interfaces]) { + visit(astNode, visitor); + } +- } else if (type == null ? void 0 : type.astNode) { +- visit(type.astNode, visitor); ++ } else { ++ if (type == null ? void 0 : type.astNode) { ++ visit(type.astNode, visitor); ++ } ++ if (type == null ? void 0 : type.extensionASTNodes) { ++ for (const ext of type.extensionASTNodes) { ++ visit(ext, visitor); ++ } ++ } + } + }; + const visitor = { +@@ -74,6 +81,11 @@ function getReachableTypes(schema) { + if (type == null ? void 0 : type.astNode) { + visit(type.astNode, visitor); + } ++ if (type == null ? void 0 : type.extensionASTNodes) { ++ for (const ext of type.extensionASTNodes) { ++ visit(ext, visitor); ++ } ++ } + } + for (const node of schema.getDirectives()) { + if (node.locations.some((location) => RequestDirectiveLocations.has(location))) { diff --git a/esm/rules/relay-edge-types.js b/esm/rules/relay-edge-types.js index 7a7eb179786103d9d72a4a84fb4bf811d11a4286..6fb12a5f56b71f56c9a14018d0494e9c7584f935 100644 --- a/esm/rules/relay-edge-types.js diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b4e79667..1e4d4692c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,7 +56,7 @@ patchedDependencies: hash: 2280c6d4f2e9268fc118d06dc95deea7e9b58200010db1b332004627d6793a9f path: patches/@graphql-codegen__schema-ast.patch '@graphql-eslint/eslint-plugin@3.20.1': - hash: 230c54b62a1b41bce5584acfdebdeadd5631d8cce18b137ecd712b9ebbe2244b + hash: 09fda5b278e2d5231c4881c9d877a8b38b813c049a8e6651f7dfb9df1da7f170 path: patches/@graphql-eslint__eslint-plugin@3.20.1.patch '@oclif/core@3.26.6': hash: 7432c7b46bd5e3276faff9af4fc733575417c17a0ec31df3c7a229f766e3cffc @@ -137,7 +137,7 @@ importers: version: 3.0.1(graphql@16.9.0) '@graphql-eslint/eslint-plugin': specifier: 3.20.1 - version: 3.20.1(patch_hash=230c54b62a1b41bce5584acfdebdeadd5631d8cce18b137ecd712b9ebbe2244b)(@babel/core@7.28.5)(@types/node@24.12.2)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0) + version: 3.20.1(patch_hash=09fda5b278e2d5231c4881c9d877a8b38b813c049a8e6651f7dfb9df1da7f170)(@babel/core@7.28.5)(@types/node@24.12.2)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0) '@graphql-inspector/cli': specifier: 6.0.6 version: 6.0.6(@types/node@24.12.2)(encoding@0.1.13)(graphql@16.9.0) @@ -1450,7 +1450,7 @@ importers: devDependencies: '@graphql-eslint/eslint-plugin': specifier: 3.20.1 - version: 3.20.1(patch_hash=230c54b62a1b41bce5584acfdebdeadd5631d8cce18b137ecd712b9ebbe2244b)(@babel/core@7.28.5)(@types/node@25.5.0)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0) + version: 3.20.1(patch_hash=09fda5b278e2d5231c4881c9d877a8b38b813c049a8e6651f7dfb9df1da7f170)(@babel/core@7.28.5)(@types/node@25.5.0)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0) '@hive/service-common': specifier: workspace:* version: link:../service-common @@ -22374,7 +22374,7 @@ snapshots: parse-filepath: 1.0.2 tslib: 2.6.3 - '@graphql-eslint/eslint-plugin@3.20.1(patch_hash=230c54b62a1b41bce5584acfdebdeadd5631d8cce18b137ecd712b9ebbe2244b)(@babel/core@7.28.5)(@types/node@24.12.2)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0)': + '@graphql-eslint/eslint-plugin@3.20.1(patch_hash=09fda5b278e2d5231c4881c9d877a8b38b813c049a8e6651f7dfb9df1da7f170)(@babel/core@7.28.5)(@types/node@24.12.2)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0)': dependencies: '@babel/code-frame': 7.29.0 '@graphql-tools/code-file-loader': 7.3.23(@babel/core@7.28.5)(graphql@16.9.0) @@ -22397,7 +22397,7 @@ snapshots: - supports-color - utf-8-validate - '@graphql-eslint/eslint-plugin@3.20.1(patch_hash=230c54b62a1b41bce5584acfdebdeadd5631d8cce18b137ecd712b9ebbe2244b)(@babel/core@7.28.5)(@types/node@25.5.0)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0)': + '@graphql-eslint/eslint-plugin@3.20.1(patch_hash=09fda5b278e2d5231c4881c9d877a8b38b813c049a8e6651f7dfb9df1da7f170)(@babel/core@7.28.5)(@types/node@25.5.0)(cosmiconfig-toml-loader@1.0.0)(encoding@0.1.13)(graphql@16.9.0)': dependencies: '@babel/code-frame': 7.29.0 '@graphql-tools/code-file-loader': 7.3.23(@babel/core@7.28.5)(graphql@16.9.0)