Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: add federation-link-util library #6452

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open

Conversation

jdolle
Copy link
Collaborator

@jdolle jdolle commented Jan 31, 2025

Background

This arose from the desire to add a directive for collecting metadata.
Currently there is no great way to ensure you are following the @link spec's defined versioning scheme, and it is very difficult to keep differences between versions straight.

Description

This utility can be used to apply changes to a schema, or collect metadata from a schema. It's very flexible.

@jdolle jdolle self-assigned this Jan 31, 2025

This comment was marked as resolved.

const metaSpec = new LinkableSpec('https://specs.graphql-hive.com/metadata', {
// The return value could be used to map sdl, collect information, or create a graphql yoga plugin.
// In this test, it's used to collect metadata information from the schema.
'v0.1': resolveImportName => (typeDefs: DocumentNode) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is the best example of how I intended this API to be used.
The LinkableSpec defines how the linked schemas are implemented.
The Link. resolveImportName function is then passed to the handler to be used to resolve the names of nodes as they are referred to within the subgraph... I chose to only pass resolveImportName to keep the API simpler.
And then the implementation uses that to figure out the names of things it supports.

@jdolle jdolle marked this pull request as ready for review February 3, 2025 01:15
Copy link
Contributor

coderabbitai bot commented Feb 3, 2025

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Launched a new library to enhance GraphQL federation with improved link parsing, version detection, and compatibility checks.
  • Documentation

    • Added comprehensive guides detailing the library’s features, usage examples, and configuration instructions.
  • Tests

    • Introduced extensive test suites to ensure reliable link resolution and version compatibility across federation scenarios.

Walkthrough

The changes introduce a new TypeScript package @graphql-hive/federation-link-utils, enhancing module resolution through a new path mapping in the TypeScript configuration. It includes a README file detailing the library's purpose and usage, a package.json for package metadata, and various source files that implement utilities for managing federated links, including classes for URL handling, version detection, and import resolution. Extensive test suites have been added to validate the functionalities of these new utilities.

Changes

File(s) Change Summary
tsconfig.json Added a new path mapping for module @graphql-hive/federation-link-utils pointing to ./packages/libraries/federation-link-utils/src/index.ts.
.../packages/libraries/federation-link-utils/tsconfig.json Introduced a new TypeScript configuration for the federation-link-utils package, extending a base config with source, output, and compiler options.
.../packages/libraries/federation-link-utils/README.md Added a README documenting the purpose, usage, and features of the federation-link-utils library.
.../packages/libraries/federation-link-utils/package.json Created a new package declaration for @graphql-hive/federation-link-utils (version 0.0.1) with necessary metadata, dependencies, and scripts.
.../packages/libraries/federation-link-utils/src/__tests__/* Added multiple test suites covering functionalities for LinkableSpec, detectLinkedImplementations, FederatedLinkUrl, and FederatedLink (including URL parsing and import name resolution).
.../packages/libraries/federation-link-utils/src/* Added new source files:
- index.ts: Introduces detectLinkedImplementations and re-exports modules.
- link-import.ts: Adds class FederatedLinkImport.
- link-url.ts: Adds class FederatedLinkUrl with parsing and compatibility methods.
- link.ts: Adds function linkFromArgs and class FederatedLink with methods for handling links.
- linkable-spec.ts: Introduces type alias Versioned<T> and class LinkableSpec<T> with version detection.
- version.ts: Exports a constant version value.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant detectLinkedImplementations
    participant FederatedLink
    participant LinkableSpec

    Client->>detectLinkedImplementations: Call with typeDefs, supportedSpecs
    detectLinkedImplementations->>FederatedLink: Extract links via fromTypedefs(typeDefs)
    FederatedLink-->>detectLinkedImplementations: Return list of links
    loop For each spec in supportedSpecs
        detectLinkedImplementations->>LinkableSpec: spec.detectImplementation(links)
        LinkableSpec-->>detectLinkedImplementations: Return implementation (or undefined)
    end
    detectLinkedImplementations-->>Client: Return array of implementations
Loading

Tip

🌐 Web search-backed reviews and chat
  • We have enabled web search-based reviews and chat for all users. This feature allows CodeRabbit to access the latest documentation and information on the web.
  • You can disable this feature by setting web_search: false in the knowledge_base settings.
  • Please share any feedback in the Discord discussion.

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

coderabbitai[bot]

This comment was marked as resolved.

@jdolle jdolle force-pushed the linkable-spec-lib branch from 66dfc13 to bf8c6e8 Compare February 3, 2025 03:36
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (6)
packages/libraries/linkable-specs/src/__tests__/index.spec.ts (3)

1-17: LGTM! Consider adding JSDoc comments.

The test setup provides a clear example of the API usage with proper imports and SDL definition. Consider adding JSDoc comments to document the test's purpose and the demonstrated functionality.

+/**
+ * Tests the integration between LinkableSpec and detectLinkedImplementations
+ * demonstrating how to implement versioned metadata collection from linked schemas.
+ */
 test('LinkableSpec and detectLinkedImplementations can be used to easily implement linked schema functionality', () => {

32-44: Extract repeated directive argument extraction logic.

The pattern for extracting string values from directive arguments is duplicated. Consider extracting this into a helper function to improve maintainability and reduce duplication.

+const getDirectiveStringArg = (directive: any, argName: string) => 
+  (directive.arguments?.find(a => a.name.value === argName)?.value as StringValueNode | undefined)?.value ?? '??';

-metaData['name'] =
-  (
-    meta.arguments?.find(a => a.name.value === 'name')?.value as
-      | StringValueNode
-      | undefined
-  )?.value ?? '??';
-metaData['content'] =
-  (
-    meta.arguments?.find(a => a.name.value === 'content')?.value as
-      | StringValueNode
-      | undefined
-  )?.value ?? '??';
+metaData['name'] = getDirectiveStringArg(meta, 'name');
+metaData['content'] = getDirectiveStringArg(meta, 'content');

-metaData['eg'] =
-  (
-    example.arguments?.find(a => a.name.value === 'eg')?.value as
-      | StringValueNode
-      | undefined
-  )?.value ?? '??';
+metaData['eg'] = getDirectiveStringArg(example, 'eg');

Also applies to: 48-54


65-68: Add TODO comment for v0.2 implementation.

The v0.2 implementation is currently a placeholder. Consider adding a TODO comment to track this for future implementation.

-'v0.2': _resolveImportName => (_typeDefs: DocumentNode) => {
+'v0.2': _resolveImportName => (_typeDefs: DocumentNode) => {
+  // TODO: Implement v0.2 metadata collection logic
   // collect metadata
   return `running on v0.2...`;
 },
packages/libraries/linkable-specs/src/link.ts (3)

5-32: Consider explicit handling of unknown arguments or removing the commented-out log.
Currently, unrecognized arguments trigger the default case (line 23) with a commented-out console.warn. Either remove the commented log or convert it into a meaningful handling or user feedback mechanism to clarify unsupported arguments.

   default: {
-    // console.warn('Unknown argument');
+    console.warn(`Unknown argument: ${arg.name.value}`);
   }

44-66: Ensure the @link directive is relevant to the intended specification.
While filtering directives named 'link' is correct for Apollo’s federated spec, be cautious if multiple specifications or custom 'link' directives exist. Make sure that the directive name matches the intended spec usage or version.


85-119: Replace ts-expect-error with a more robust type definition.
The overload notation for supports() could be handled via a single function signature that performs runtime checks on its parameters. This would eliminate the need for ts-expect-error and improve type clarity.

-  supports(...args: [string] | [number, number] | [FederatedLinkUrl]): boolean {
-    /** @ts-expect-error: ignore tuple error. These are tuples and can be spread. tsc is wrong. */
-    return this.url.supports(...args);
-  }
+  supports(version: string | number | FederatedLinkUrl, minor?: number): boolean {
+    if (typeof version === 'string') {
+      return this.url.supports(version);
+    } else if (typeof version === 'number' && typeof minor === 'number') {
+      return this.url.supports(version, minor);
+    } else {
+      return this.url.supports(version);
+    }
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 66dfc13 and bf8c6e8.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • packages/libraries/linkable-specs/README.md (1 hunks)
  • packages/libraries/linkable-specs/package.json (1 hunks)
  • packages/libraries/linkable-specs/src/__tests__/index.spec.ts (1 hunks)
  • packages/libraries/linkable-specs/src/__tests__/link-url.spec.ts (1 hunks)
  • packages/libraries/linkable-specs/src/__tests__/link.spec.ts (1 hunks)
  • packages/libraries/linkable-specs/src/__tests__/linkable-spec.spec.ts (1 hunks)
  • packages/libraries/linkable-specs/src/index.ts (1 hunks)
  • packages/libraries/linkable-specs/src/link-import.ts (1 hunks)
  • packages/libraries/linkable-specs/src/link-url.ts (1 hunks)
  • packages/libraries/linkable-specs/src/link.ts (1 hunks)
  • packages/libraries/linkable-specs/src/linkable-spec.ts (1 hunks)
  • packages/libraries/linkable-specs/tsconfig.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • packages/libraries/linkable-specs/README.md
  • packages/libraries/linkable-specs/tsconfig.json
  • packages/libraries/linkable-specs/src/tests/linkable-spec.spec.ts
  • packages/libraries/linkable-specs/src/tests/link.spec.ts
  • packages/libraries/linkable-specs/src/index.ts
  • packages/libraries/linkable-specs/src/tests/link-url.spec.ts
  • packages/libraries/linkable-specs/src/link-import.ts
  • packages/libraries/linkable-specs/package.json
  • packages/libraries/linkable-specs/src/link-url.ts
  • packages/libraries/linkable-specs/src/linkable-spec.ts
🧰 Additional context used
📓 Path-based instructions (2)
packages/libraries/linkable-specs/src/__tests__/index.spec.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/linkable-specs/src/link.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

🔇 Additional comments (4)
packages/libraries/linkable-specs/src/__tests__/index.spec.ts (1)

70-78: Consider adding more test cases.

The current test only covers the happy path for v0.1. Consider adding test cases for:

  • v0.2 implementation
  • Error cases (malformed directives, missing required arguments)
  • Edge cases (no directives, multiple directives)

Would you like me to help generate these additional test cases?

packages/libraries/linkable-specs/src/link.ts (3)

1-4: Imports look clean and straightforward.
No issues found in these imports; they accurately capture the dependencies needed to handle ConstArgumentNode and related types.


34-42: Namespace function logic is clear.
The logic for prefixing names with a namespace is succinct and easy to read. No immediate concerns.


68-84: Validate or sanitize user-provided identifiers in toString().
Because toString() directly interpolates url and imports into an SDL string, confirm that upstream code or the parsing logic won’t cause malformed SDL or injection-like concerns if the input is external.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/libraries/linkable-specs/src/link-import.ts (2)

9-11: Add JSDoc documentation for the toString method.

While the implementation is correct, adding documentation would improve clarity about the expected string format.

+  /**
+   * Returns a string representation of the import in GraphQL SDL format.
+   * @returns {string} For imports with alias: { name: "name", as: "alias" }
+   *                   For imports without alias: "name"
+   */
   public toString(): string {

13-47: Add JSDoc documentation and empty string validation.

  1. Add JSDoc documentation for better API understanding.
  2. Add validation for empty strings in the 'name' field.
+  /**
+   * Parses GraphQL ConstValueNode to extract @link import declarations.
+   * @param {ConstValueNode} node - The node containing import declarations
+   * @returns {FederatedLinkImport[]} Array of parsed import declarations
+   * @throws {Error} If the node structure is invalid
+   */
   static fromTypedefs(node: ConstValueNode): FederatedLinkImport[] {

     // ... existing code ...

             name = f.value.value;
+            if (name.trim() === '') {
+              throw new Error('Empty string is not allowed for @link "name" field');
+            }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf8c6e8 and 67f0137.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (1)
  • packages/libraries/linkable-specs/src/link-import.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/libraries/linkable-specs/src/link-import.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

📓 Learnings (1)
📓 Common learnings
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🔇 Additional comments (2)
packages/libraries/linkable-specs/src/link-import.ts (2)

1-7: LGTM! Clean and concise class declaration.

The imports are appropriate, and the constructor uses TypeScript's parameter property syntax effectively.


17-17: Replace 'this' with class name in static context.

Using 'this' in static methods can be confusing as it refers to the class itself.

-          return new this(v.value, null);
+          return new FederatedLinkImport(v.value, null);
-          return new this(name, as);
+          return new FederatedLinkImport(name, as);

Also applies to: 40-40

@jdolle jdolle requested review from kamilkisiela and n1ru4l February 3, 2025 03:48
Copy link
Member

@jasonkuhrt jasonkuhrt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @jdolle my review is accumulating some questions and there's still a ways to go. Going to flush what I have so far in case you could already answer some. It would probably help me better understand what is going on etc. :)

Comment on lines 3 to 5
[Hive](https://the-guild.dev/graphql/hive) is a fully open-source schema registry, analytics,
metrics and gateway for [GraphQL federation](https://the-guild.dev/graphql/hive/federation) and
other GraphQL APIs.
Copy link
Member

@jasonkuhrt jasonkuhrt Feb 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if could make this even more maintainable by avoiding specific feature references, e.g.

... is a delightful MIT licensed platform for developing and operating your GraphQL services from idea to production, solo to enterprise.

And maybe there is/this is a template we can turn into a copy standard? CC @dotansimha @Urigo

packages/libraries/linkable-specs/README.md Outdated Show resolved Hide resolved
coderabbitai[bot]

This comment was marked as resolved.

coderabbitai[bot]

This comment was marked as resolved.

@jasonkuhrt

This comment was marked as resolved.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/libraries/linkable-specs/README.md (2)

9-12: Grammar Correction in Custom Features Description

The sentence “This library can be used to make a custom features for GraphQL schemas backed by Federation's directive.” contains a grammatical issue with the article used before the plural noun “features.” Consider revising to either:

  • “This library can be used to create custom features for GraphQL schemas…”
    or
  • “This library can be used to make a custom feature for GraphQL schemas…”

This change improves clarity and correctness.

🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)


66-68: Clarify the "Yoga Plugin" Recommendation

The note stating “always return a yoga plugin” might be ambiguous to users who are not familiar with the term. Consider providing a brief explanation or a hyperlink to documentation that explains what a yoga plugin is and why it is recommended.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8df0a0 and 36584a1.

📒 Files selected for processing (1)
  • packages/libraries/linkable-specs/README.md (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/libraries/linkable-specs/README.md (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

📓 Learnings (2)
📓 Common learnings
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
packages/libraries/linkable-specs/README.md (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🪛 LanguageTool
packages/libraries/linkable-specs/README.md

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: test / unit
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: SonarCloud
🔇 Additional comments (4)
packages/libraries/linkable-specs/README.md (4)

1-2: Header is Clear and Informative

The header “GraphQL Hive - linkable-specs” clearly identifies the package and purpose.


3-5: Adopt a Consistent Copy Standard for the Introduction

The introductory text provides a good overview; however, consider revising it to follow a consistent copy standard. Previous feedback suggested using a generic template that avoids overly specific feature references. For example, instead of specific claims, a template outlining capabilities in a maintainable way could be beneficial.


13-18: Features Section Looks Good

The bulleted features list is concise and clearly communicates the key capabilities of the package.


19-34: GraphQL Usage Example is Informative

The provided GraphQL schema snippet adequately demonstrates the usage of the @link directive in context. The inclusion of a concrete example helps users understand how to apply the feature.

packages/libraries/linkable-specs/README.md Outdated Show resolved Hide resolved
@jdolle
Copy link
Collaborator Author

jdolle commented Feb 3, 2025

I considered using that package but federation links only have major and minor, have special requirements for v0.x that don't follow semantic versioning, and I didnt want to introduce a dependency for just the one or two relatively simple functions to write. If the link version spec is ever modified to specifically use semantic versions then we can add this package.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/libraries/linkable-specs/README.md (2)

9-12: Grammar Correction Needed in Description.

The sentence "This library can be used to make a custom features for GraphQL schemas backed by Federation's" contains a grammatical error ("a custom features"). Consider rephrasing to "This library can be used to create custom features for GraphQL schemas backed by Federation's" (or another phrasing that fits the intended meaning).

Diff Suggestion:

-This library can be used to make a custom features for GraphQL schemas backed by Federation's
+This library can be used to create custom features for GraphQL schemas backed by Federation's
🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)


65-68: Clarify Recommendation on Consistent Implementation.

The concluding note recommends that all LinkedSpecs always return a yoga plugin. It would be beneficial to provide a brief explanation or example of why this consistency is important and how it benefits the overall implementation.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36584a1 and e93a477.

📒 Files selected for processing (1)
  • packages/libraries/linkable-specs/README.md (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/libraries/linkable-specs/README.md (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

📓 Learnings (2)
📓 Common learnings
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
packages/libraries/linkable-specs/README.md (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🪛 LanguageTool
packages/libraries/linkable-specs/README.md

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: typescript / typecheck
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: test / unit
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: SonarCloud
🔇 Additional comments (6)
packages/libraries/linkable-specs/README.md (6)

1-2: Title and Header Are Clear.

The title "GraphQL Hive - linkable-specs" succinctly informs the reader about the package’s purpose.


3-6: Clear Description Provided.

The introductory paragraph effectively describes GraphQL Hive as an open-source schema registry, analytics, and gateway for GraphQL APIs.


13-18: Features Section is Concise and Informative.

The bullet points clearly list the package’s key capabilities: link version support, import as/namespacing following the link spec, and maintaining minimal dependencies.


19-20: Usage Section Header is Clear.

The dedicated "Usage" section helps users quickly locate practical examples.


21-35: GraphQL Schema Example Effectively Demonstrates Usage.

The provided GraphQL code block clearly illustrates how to define a directive and utilize the @link directive. This practical example aligns well with the package’s objective of managing schemas with custom link features.


36-64: TypeScript Example Illustrates Implementation Correctly.

The TypeScript example demonstrates the instantiation of LinkableSpec and the resolution of directive names using resolveImportName, which is consistent with the automatic namespacing feature noted in the retrieved learnings. Ensure that functions like visit, parse, and detectLinkedImplementations are either included in the context or referenced in documentation for clarity.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (9)
packages/libraries/linkable-specs/README.md (3)

9-11: Grammar Correction in Usage Description

The sentence "This library can be used to make a custom features for GraphQL schemas backed by Federation's" contains a grammatical error due to the use of the singular article "a" before the plural noun "features." Consider revising it to either "make custom features" (if plural is intended) or "make a custom feature" (if singular is intended).

🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)


36-63: TypeScript Example: Usage Clarity and Completeness

The TypeScript sample clearly illustrates how to create and configure a LinkableSpec instance, resolve a base directive name (e.g., @example), and extract metadata via traversing type definitions. As this snippet is intended for documentation purposes, consider adding import statements or comments indicating where functions (such as visit, parse) and types (DocumentNode, StringValueNode) are sourced from. This can help users better adapt the example into a production context.


65-67: Concluding Remarks and Consistency

The concluding section effectively communicates that the LinkableSpec is unopinionated regarding implementation while recommending consistency across all linked specs (e.g., always returning a yoga plugin). For enhanced clarity and in alignment with internal documentation styles, consider reviewing the phrasing (for example, verifying the use of "I.e." or "i.e.," as per your copy standards).

packages/libraries/linkable-specs/src/link-url.ts (4)

1-10: Consider tightening version constraints.

The version regex pattern is quite permissive, allowing major versions up to 99 and minor versions up to 9999. Consider adding numeric range validation to ensure versions stay within reasonable bounds for federation links.

-const VERSION_MATCH = /v(\d{1,2})\.(\d{1,4})/i;
+const VERSION_MATCH = /v(\d{1,2})\.(\d{1,3})/i;

 function parseVersion(version: string | null): [number, number] {
   const versionParts = version?.match(VERSION_MATCH);
   if (versionParts?.length) {
     const [_full, major, minor] = versionParts;
+    const majorNum = Number(major);
+    const minorNum = Number(minor);
+    if (majorNum > 20 || minorNum > 999) {
+      return [-1, -1];
+    }
-    return [Number(major), Number(minor)];
+    return [majorNum, minorNum];
   }
   return [-1, -1];
 }

12-29: Enhance class documentation.

The class documentation could be more detailed about the parameters and their purpose.

 /**
  * A wrapper around the `@link` url -- this parses all necessary data to identify the link
  * and determine which version is most appropriate to use.
+ *
+ * @param identity - The base URL or identifier of the link
+ * @param name - Optional name of the link specification
+ * @param version - Optional version string in the format 'vX.Y'
  */

53-75: Improve error message specificity.

The error message for unsupported version arguments could be more helpful by indicating the expected types.

-      throw new Error(`Unsupported version argument: ${args}.`);
+      throw new Error(
+        `Unsupported version argument: ${args}. Expected a version string (e.g., 'v1.0'), ` +
+        `version numbers (major, minor), or FederatedLinkUrl instance.`
+      );

77-85: Document version compatibility rules.

The version compatibility logic, especially the special handling of v0.x versions, should be documented for clarity.

+  /**
+   * Checks if this version is compatible with the given version.
+   * 
+   * Version compatibility rules:
+   * - For v0.x: Versions must match exactly (no backward compatibility)
+   * - For v1.x and above: Compatible if major versions match and current minor version >= given minor version
+   */
   private isCompatibleVersion(major: number, minor: number): boolean {
packages/libraries/linkable-specs/src/link.ts (2)

42-50: Add JSDoc documentation and input validation.

The utility function would benefit from proper documentation and input validation.

+/**
+ * Prefixes a name with a namespace if provided.
+ * @param namespace - The namespace to prefix the name with
+ * @param name - The name to be namespaced
+ * @returns The namespaced name or the original name if no namespace is provided
+ * @throws {Error} If name is empty or contains invalid characters
+ */
 function namespaced(namespace: string | null, name: string) {
+  if (!name || typeof name !== 'string') {
+    throw new Error('Name must be a non-empty string');
+  }
+
   if (namespace?.length) {
     if (name.startsWith('@')) {
       return `@${namespace}__${name.substring(1)}`;
     }
     return `${namespace}__${name}`;
   }
   return name;
 }

120-130: Simplify resolveImportName logic and improve error handling.

The method contains complex string manipulation and could benefit from clearer logic separation.

 resolveImportName(elementName: string): string {
+  if (!elementName) {
+    throw new Error('Element name must be provided');
+  }
+
+  // Handle default link import case
   if (this.url.name && elementName === `@${this.url.name}`) {
-    // @note: default is a directive... So remove the `@`
-    return this.defaultImport!.substring(1);
+    const defaultImport = this.defaultImport;
+    if (!defaultImport) {
+      throw new Error('Default import not available');
+    }
+    return defaultImport.substring(1);
   }
+
+  // Find explicit import
   const imported = this.imports.find(i => i.name === elementName);
-  let resolvedName = imported?.as ?? imported?.name ?? namespaced(this.namespace, elementName);
-  // Strip the `@` prefix for directives because in all implementations of mapping or visiting a schema,
-  // directive names are not prefixed with `@`. The `@` is only for SDL.
+  const resolvedName = imported?.as ?? imported?.name ?? namespaced(this.namespace, elementName);
+
+  // Remove @ prefix for directives as they're not used in schema visitors
   return resolvedName.startsWith('@') ? resolvedName.substring(1) : resolvedName;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e93a477 and 88ed00d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • packages/libraries/linkable-specs/README.md (1 hunks)
  • packages/libraries/linkable-specs/src/link-url.ts (1 hunks)
  • packages/libraries/linkable-specs/src/link.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
packages/libraries/linkable-specs/README.md (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/linkable-specs/src/link-url.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/linkable-specs/src/link.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

📓 Learnings (2)
📓 Common learnings
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
packages/libraries/linkable-specs/README.md (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🪛 LanguageTool
packages/libraries/linkable-specs/README.md

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)

⏰ Context from checks skipped due to timeout of 90000ms (12)
  • GitHub Check: alpha / npm / snapshot
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: test / unit
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: db-types / diff
  • GitHub Check: test / db-migrations
  • GitHub Check: SonarCloud
🔇 Additional comments (6)
packages/libraries/linkable-specs/README.md (3)

1-8: Introduction and Overview Section Review

The introductory section clearly presents the package and its purpose. The title and accompanying description effectively introduce "GraphQL Hive - linkable-specs" and set the context for the utility library.


13-18: Features List Clarity

The features list is well-structured and clearly highlights the package’s functionalities, including link version support, import as/namespacing based on the link spec, and the minimal dependency on graphql.


19-34: GraphQL Code Snippet Validation

The provided GraphQL code snippet is syntactically correct and effectively demonstrates how to declare a directive and extend the schema using the @link directive. The inclusion of relevant hyperlinks enhances clarity and offers users an avenue for further exploration of the concept.

packages/libraries/linkable-specs/src/link-url.ts (1)

31-33: LGTM!

The toString implementation correctly handles both versioned and unversioned URLs.

packages/libraries/linkable-specs/src/link.ts (2)

5-40: Improve type validation and return type.

The function's type validation and return type could be improved for better type safety and clarity.

Consider these improvements:

  1. Add explicit type validation before using values
  2. Make the return type more explicit
  3. Add input validation for required fields
-function linkFromArgs(args: readonly ConstArgumentNode[]): FederatedLink | undefined {
+function linkFromArgs(args: readonly ConstArgumentNode[]): FederatedLink | null {
   let url: FederatedLinkUrl | undefined,
     imports: FederatedLinkImport[] = [],
     as: string | null = null;
+
+  // Validate required arguments
+  const urlArg = args.find(arg => arg.name.value === 'url');
+  if (!urlArg) {
+    throw new Error('Missing required "url" argument in @link directive');
+  }
+
   for (const arg of args) {
     switch (arg.name.value) {
       case 'url': {
-        if (arg.value.kind === Kind.STRING) {
-          url = FederatedLinkUrl.fromUrl(arg.value.value);
-        } else {
-          console.warn(`Unexpected kind, ${arg.value.kind}, for argument "url" in @link.`);
+        if (arg.value.kind !== Kind.STRING) {
+          throw new Error(`Unexpected kind ${arg.value.kind} for argument "url" in @link directive. Expected ${Kind.STRING}.`);
         }
+        url = FederatedLinkUrl.fromUrl(arg.value.value);
         break;
       }
       // ... similar changes for 'as' argument
     }
   }
   if (url !== undefined) {
     return new FederatedLink(url, as, imports);
   }
-  return;
+  return null;
 }

53-53: Address the TODO comment about import names.

The TODO comment raises an important architectural question about import name handling.

Could you clarify if import names are needed for every feature or just the namespace? This decision could impact the implementation's flexibility and performance.

packages/libraries/linkable-specs/src/link-url.ts Outdated Show resolved Hide resolved
packages/libraries/linkable-specs/src/link.ts Outdated Show resolved Hide resolved
packages/libraries/linkable-specs/src/link.ts Outdated Show resolved Hide resolved
}
`;

const metaSpec = new LinkableSpec('https://specs.graphql-hive.com/metadata', {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC the given URL here is conforming to the URL spec value in @link, and upon being passed, it is used in conjunction with the given SDL to find the @linked instance (instances plural possible?). Is that right?

Something that wasn't clear to me from reading the spec https://specs.apollo.dev/link/v1.0/ was what is name and version when not explicitly specified in the URL?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when not specified, they are null. You can see examples in packages/libraries/federation-link-utils/src/tests/link-url.spec.ts:4

Copy link
Member

@jasonkuhrt jasonkuhrt Feb 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, given a linked schema has 4 versions, but the given url to it doesn't specify when one, after null, what happens then? Like, would an entry of null in the versions record of your class constructor arguments be used? Or...

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (13)
packages/libraries/federation-link-utils/src/link-url.ts (1)

1-10: Consider supporting patch versions or alternative version formats.
This approach correctly extracts major and minor versions using a concise regex. However, if federated links ever adopt full semantic versioning (including patch versions or more digits), you may need to adjust the regex or your approach.

packages/libraries/federation-link-utils/src/link.ts (1)

52-132: Potential improvement: incorporate stronger validations or error handling.
While FederatedLink.fromTypedefs collects links effectively, you might consider validating the resulting links (e.g., ensuring they have valid URLs, checking for conflicts) before returning them. This could prevent issues later in the pipeline.

packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts (1)

5-19: Enhance test coverage with additional test cases.

While the current test verifies basic version compatibility, consider adding test cases for:

  • Edge cases (no matching version)
  • Warning message when version is unsupported
  • Multiple links in the schema
  • Invalid URLs
  • Version compatibility edge cases (v0.x handling)

Example test cases to add:

test('warns when no supporting version is found', () => {
  const spec = new LinkableSpec('https://specs.graphql-hive.com/example', {
    'v1.0': _resolveImportName => 'Version 1.0 used.',
  });
  const sdl = `
    extend schema
      @link(url: "https://specs.graphql-hive.com/example/v2.0")
  `;
  const consoleSpy = jest.spyOn(console, 'warn');
  const links = FederatedLink.fromTypedefs(parse(sdl));
  const specImpl = spec.detectImplementation(links);
  expect(specImpl).toBeUndefined();
  expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Cannot apply @link'));
});

test('handles multiple links in schema', () => {
  const spec = new LinkableSpec('https://specs.graphql-hive.com/example', {
    'v2.0': _resolveImportName => 'Version 2.0 used.',
    'v1.0': _resolveImportName => 'Version 1.0 used.',
  });
  const sdl = `
    extend schema
      @link(url: "https://specs.graphql-hive.com/other/v1.0")
      @link(url: "https://specs.graphql-hive.com/example/v1.1")
  `;
  const links = FederatedLink.fromTypedefs(parse(sdl));
  const specImpl = spec.detectImplementation(links);
  expect(specImpl).toBe('Version 1.0 used.');
});
packages/libraries/federation-link-utils/src/link-import.ts (1)

13-47: Enhance error handling and type safety.

Consider the following improvements:

  1. Add validation for empty name
  2. Use type guards for better type safety
  3. Make error messages more descriptive by including the actual value

Apply this diff to improve the implementation:

 static fromTypedefs(node: ConstValueNode): FederatedLinkImport[] {
   if (node.kind == Kind.LIST) {
     const imports = node.values.map(v => {
       if (v.kind === Kind.STRING) {
+        if (!v.value.trim()) {
+          throw new Error('Empty string is not allowed as @link import name');
+        }
         return new FederatedLinkImport(v.value, null);
       }
       if (v.kind === Kind.OBJECT) {
         let name: string = '';
         let as: string | null = null;

         v.fields.forEach(f => {
           if (f.name.value === 'name') {
             if (f.value.kind !== Kind.STRING) {
               throw new Error(
-                `Expected string value for @link "name" field but got "${f.value.kind}"`,
+                `Expected string value for @link "name" field but got "${f.value.kind}" (${JSON.stringify(f.value)})`,
               );
             }
+            if (!f.value.value.trim()) {
+              throw new Error('Empty string is not allowed as @link import name');
+            }
             name = f.value.value;
           } else if (f.name.value === 'as') {
             if (f.value.kind !== Kind.STRING) {
               throw new Error(
-                `Expected string value for @link "as" field but got "${f.value.kind}"`,
+                `Expected string value for @link "as" field but got "${f.value.kind}" (${JSON.stringify(f.value)})`,
               );
             }
+            if (!f.value.value.trim()) {
+              throw new Error('Empty string is not allowed as @link import alias');
+            }
             as = f.value.value;
           }
         });
+        if (!name) {
+          throw new Error('Missing required "name" field in @link import');
+        }
         return new FederatedLinkImport(name, as);
       }
-      throw new Error(`Unexpected value kind "${v.kind}" in @link import declaration`);
+      throw new Error(`Unexpected value kind "${v.kind}" in @link import declaration (${JSON.stringify(v)})`);
     });
     return imports;
   }
-  throw new Error(`Expected a list of @link imports but got "${node.kind}"`);
+  throw new Error(`Expected a list of @link imports but got "${node.kind}" (${JSON.stringify(node)})`);
 }
packages/libraries/federation-link-utils/src/linkable-spec.ts (3)

15-19: Improve version comparison logic.

The current version comparison assumes a simple major.minor format. Consider:

  1. Adding validation for version format
  2. Handling pre-release versions
  3. Supporting patch versions if needed
 this.sortedVersionKeys = Object.keys(versions).sort((a, b) => {
+  const versionRegex = /^v(\d+)\.(\d+)(?:\.(\d+))?(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
+  const matchA = a.match(versionRegex);
+  const matchB = b.match(versionRegex);
+  if (!matchA || !matchB) {
+    throw new Error(`Invalid version format: ${!matchA ? a : b}`);
+  }
-  const [aMajor, aMinor] = a.split('.').map(Number);
-  const [bMajor, bMinor] = b.split('.').map(Number);
+  const [, aMajor, aMinor] = matchA.map(Number);
+  const [, bMajor, bMinor] = matchB.map(Number);
   return bMajor !== aMajor ? bMajor - aMajor : bMinor - aMinor;
 });

47-50: Fix typo in warning message.

The warning message contains a duplicate word "compatible".

-          `Available versions: ${this.sortedVersionKeys.join(', ')} and any version these are compatible compatible with.`,
+          `Available versions: ${this.sortedVersionKeys.join(', ')} and any version these are compatible with.`,

39-52: Add JSDoc for public method.

The detectImplementation method would benefit from more detailed documentation.

+  /**
+   * Detects and returns an implementation based on the provided links.
+   * 
+   * @param links - Array of FederatedLink instances to search through
+   * @returns The implementation for the highest compatible version found,
+   *          or undefined if no compatible version is found
+   * @throws {Error} If the version format is invalid
+   * @example
+   * ```typescript
+   * const spec = new LinkableSpec('https://example.com', {
+   *   'v2.0': resolveImportName => new Implementation(resolveImportName),
+   * });
+   * const links = FederatedLink.fromTypedefs(parse(sdl));
+   * const impl = spec.detectImplementation(links);
+   * ```
+   */
   public detectImplementation(links: FederatedLink[]): T | undefined {
packages/libraries/federation-link-utils/src/__tests__/link-url.spec.ts (1)

4-25: Improve test descriptions and add error cases.

While the test coverage is good, consider:

  1. Making test descriptions more descriptive
  2. Adding error cases for invalid URLs
  3. Adding test cases for malformed versions
test.each([
  {
    description: 'parses URL with schema name and version in path',
    url: 'https://spec.example.com/a/b/mySchema/v1.0/',
    expected: {
      identity: 'https://spec.example.com/a/b/mySchema',
      name: 'mySchema',
      version: 'v1.0'
    }
  },
  // ... existing test cases ...
])('fromUrl $description', ({ url, expected }) => {
  const spec = FederatedLinkUrl.fromUrl(url);
  expect(spec.identity).toBe(expected.identity);
  expect(spec.name).toBe(expected.name);
  expect(spec.version).toBe(expected.version);
});

test.each([
  ['invalid://url', 'Invalid URL protocol'],
  ['https://spec.example.com/mySchema/invalid-version', 'Invalid version format'],
])('fromUrl throws error for invalid input: %s', (url, expectedError) => {
  expect(() => FederatedLinkUrl.fromUrl(url)).toThrow(expectedError);
});
packages/libraries/federation-link-utils/README.md (2)

9-11: Fix grammar in the introduction.

The article "a" should not be used before the plural noun "features".

-This library can be used to make a custom features for GraphQL schemas backed by Federation's
+This library can be used to make custom features for GraphQL schemas backed by Federation's
🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)


13-67: Consider enhancing the documentation.

While the documentation provides a good overview and usage example, consider adding the following sections to make it more comprehensive:

  • Installation instructions
  • API reference
  • Contributing guidelines
  • License information
packages/libraries/federation-link-utils/src/__tests__/link.spec.ts (2)

12-37: Consider adding negative test cases.

The fromTypedefs test suite only covers successful scenarios. Consider adding test cases for:

  • Invalid SDL syntax
  • Missing or malformed @link directives
  • Empty import arrays

39-71: Add test descriptions for better readability.

The test cases in resolveImportName would benefit from descriptive titles using test.each or separate test blocks for each scenario.

-  test('resolveImportName', () => {
+  describe('resolveImportName', () => {
+    test('resolves aliased imports correctly', () => {
       // Test aliased imports
+    });
+    test('resolves unimported directives with namespace', () => {
       // Test unimported directives
+    });
+    // ... more test cases
   });
packages/libraries/federation-link-utils/src/__tests__/index.spec.ts (1)

31-43: Simplify type assertions using optional chaining.

The nested type assertions and null checks can be simplified using optional chaining.

-metaData['name'] =
-  (
-    meta.arguments?.find(a => a.name.value === 'name')?.value as
-      | StringValueNode
-      | undefined
-  )?.value ?? '??';
+metaData['name'] = (meta.arguments?.find(a => a.name.value === 'name')?.value as StringValueNode)?.value ?? '??';
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc19a7c and 53eb7c5.

📒 Files selected for processing (13)
  • packages/libraries/federation-link-utils/README.md (1 hunks)
  • packages/libraries/federation-link-utils/package.json (1 hunks)
  • packages/libraries/federation-link-utils/src/__tests__/index.spec.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/__tests__/link-url.spec.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/__tests__/link.spec.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/index.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/link-import.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/link-url.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/link.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/linkable-spec.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/version.ts (1 hunks)
  • packages/libraries/federation-link-utils/tsconfig.json (1 hunks)
✅ Files skipped from review due to trivial changes (3)
  • packages/libraries/federation-link-utils/src/version.ts
  • packages/libraries/federation-link-utils/package.json
  • packages/libraries/federation-link-utils/tsconfig.json
🧰 Additional context used
📓 Path-based instructions (10)
packages/libraries/federation-link-utils/src/__tests__/link-url.spec.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/federation-link-utils/src/index.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/federation-link-utils/src/__tests__/index.spec.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/federation-link-utils/src/link-import.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/federation-link-utils/src/linkable-spec.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/federation-link-utils/src/__tests__/link.spec.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/federation-link-utils/README.md (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/federation-link-utils/src/link-url.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/federation-link-utils/src/link.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

📓 Learnings (3)
packages/libraries/federation-link-utils/src/__tests__/index.spec.ts (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
packages/libraries/federation-link-utils/README.md (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🪛 LanguageTool
packages/libraries/federation-link-utils/README.md

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: SonarCloud
🔇 Additional comments (6)
packages/libraries/federation-link-utils/src/link-url.ts (3)

21-29: Constructor usage looks sound.
Storing both the parsed major/minor and the original version string effectively supports flexible version checks while preserving raw data.


35-51: Handle invalid or malformed URLs more gracefully.
Currently, if new URL(urlSource) receives an invalid URL, it will throw an exception. Consider wrapping it in a try/catch and logging or handling the error to avoid unexpected crashes.


53-85: Version compatibility logic is appropriate.
The current rules for version handling (exact match for major=0, otherwise this.minor >= minor) is consistent with the stated requirements. This ensures backward compatibility of minor versions while forcing exact matches for v0.x.

packages/libraries/federation-link-utils/src/link.ts (2)

5-40: Validate unknown argument handling.
In linkFromArgs, unrecognized arguments are silently ignored. Ensure that ignoring these arguments aligns with desired behavior, or add warnings for unexpected parameters if needed.


42-50: Utility function is straightforward.
The namespaced function is straightforward and well-documented. It correctly separates directives from normal types by adjusting the "@" prefix.

packages/libraries/federation-link-utils/src/index.ts (1)

10-20: Function design is clear and modular.
detectLinkedImplementations provides a simple, modular approach to link detection. It neatly delegates link parsing to FederatedLink.fromTypedefs and leverages each supportedSpec to detect potential matches, keeping concerns well-separated.

Comment on lines 4 to 77
test('LinkableSpec and detectLinkedImplementations can be used to easily implement linked schema functionality', () => {
const sdl = `
directive @meta(name: String!, content: String!) on SCHEMA | FIELD
directive @metadata__example(eg: String!) on FIELD
extend schema
@link(url: "https://specs.apollo.dev/federation/v2.3")
@link(url: "https://specs.graphql-hive.com/metadata/v0.1", import: ["@meta"])

type Query {
ping: String @meta(name: "owner", content: "hive-console-team")
pong: String @metadata__example(eg: "1...2...3... Pong")
}
`;

const metaSpec = new LinkableSpec('https://specs.graphql-hive.com/metadata', {
// The return value could be used to map sdl, collect information, or create a graphql yoga plugin.
// In this test, it's used to collect metadata information from the schema.
'v0.1': resolveImportName => (typeDefs: DocumentNode) => {
const collectedMeta: Record<string, Record<string, string>> = {};
const metaName = resolveImportName('@meta');
const exampleName = resolveImportName('@example');
visit(typeDefs, {
FieldDefinition: node => {
let metaData: Record<string, string> = {};
const fieldName = node.name.value;
const meta = node.directives?.find(d => d.name.value === metaName);
if (meta) {
metaData['name'] =
(
meta.arguments?.find(a => a.name.value === 'name')?.value as
| StringValueNode
| undefined
)?.value ?? '??';
metaData['content'] =
(
meta.arguments?.find(a => a.name.value === 'content')?.value as
| StringValueNode
| undefined
)?.value ?? '??';
}

const example = node.directives?.find(d => d.name.value === exampleName);
if (example) {
metaData['eg'] =
(
example.arguments?.find(a => a.name.value === 'eg')?.value as
| StringValueNode
| undefined
)?.value ?? '??';
}
if (Object.keys(metaData).length) {
collectedMeta[fieldName] ??= {};
collectedMeta[fieldName] = Object.assign(collectedMeta[fieldName], metaData);
}
return;
},
});
// collect metadata
return `running on v0.1.\nFound metadata: ${JSON.stringify(collectedMeta)}}`;
},
'v0.2': _resolveImportName => (_typeDefs: DocumentNode) => {
// collect metadata
return `running on v0.2...`;
},
});
const typeDefs = parse(sdl);
const linked = detectLinkedImplementations(typeDefs, [metaSpec]);
expect(linked.map(link => link(typeDefs))).toMatchInlineSnapshot(`
[
running on v0.1.
Found metadata: {"ping":{"name":"owner","content":"hive-console-team"},"pong":{"eg":"1...2...3... Pong"}}},
]
`);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling test cases.

The test suite should include cases for error scenarios:

  • Invalid SDL
  • Unsupported spec versions
  • Missing required directive arguments

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/libraries/federation-link-utils/src/link.ts (3)

5-40: Consider more robust error or fallback handling for missing or invalid arguments.
Currently, if url is not a valid string, the function returns undefined, silently skipping link creation. You might consider either throwing an error or returning a diagnostic result to make the behavior explicit for callers.


42-50: Function name clarity.
The function namespaced is concise and descriptive, but it may be clearer to name it something like addNamespacePrefix to communicate exactly what it does.


52-131: Use a configurable logger or error mechanism instead of console.warn.
Using console.warn (lines 15, 27) directly in a library can be noisy for consumers. A configurable logger or custom error-handling mechanism might give library users more control over log output.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2de886f and a84e735.

📒 Files selected for processing (2)
  • packages/libraries/federation-link-utils/src/index.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/link.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
packages/libraries/federation-link-utils/src/index.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/federation-link-utils/src/link.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

⏰ Context from checks skipped due to timeout of 90000ms (10)
  • GitHub Check: test / unit
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: test / db-migrations
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: SonarCloud
🔇 Additional comments (3)
packages/libraries/federation-link-utils/src/link.ts (1)

1-4: Looks well-structured and provides clear imports.
No issues found in these lines.

packages/libraries/federation-link-utils/src/index.ts (2)

1-9: Import and export declarations look good.
They cleanly expose the library’s capabilities.


10-21: Verify error handling for partial or invalid spec detection.
If a detectImplementation call throws an exception or returns a partial result, it might be beneficial to handle or log that scenario.

Would you like to run a codebase analysis script to identify places where detectImplementation might fail or return unexpected values?

Copy link
Contributor

github-actions bot commented Feb 4, 2025

🐋 This PR was built and pushed to the following Docker images:

Targets: build

Platforms: linux/amd64

Image Tag: b009d102eb95d325839674fa7f154ef0bb3872a0

Docker Bake metadata
{
"app": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/api/health",
          "build-arg:IMAGE_DESCRIPTION": "The app of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/app",
          "build-arg:PORT": "3000",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "build-arg:SERVICE_DIR_NAME": "@hive/app",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/3bnur9x0ke9trkuk2kwnxw42z",
  "containerimage.config.digest": "sha256:0d529c386bc54e4ac355e80d6786ee17b4532b9284dfd1383e5f51f3801f7f9c",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:f39def69bc93ea03ab079052b961bd4d3621a1220b2fef952c241047fbe5c234",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:f39def69bc93ea03ab079052b961bd4d3621a1220b2fef952c241047fbe5c234",
  "image.name": "ghcr.io/graphql-hive/app:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/app:linkable_spec_lib-amd64"
},
"buildx.build.warnings": [
  {
    "vertex": "sha256:de891251bb1d4820b99510bd3289c67536b36a6c33af561986668cb50a73a400",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIwKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 20
        },
        "end": {
          "line": 20
        }
      }
    ]
  },
  {
    "vertex": "sha256:cb204f3d9b40827e6f9e8d9d2647a25f41dafc6b802807b43d51752b96de515c",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDExKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 11
        },
        "end": {
          "line": 11
        }
      }
    ]
  },
  {
    "vertex": "sha256:de891251bb1d4820b99510bd3289c67536b36a6c33af561986668cb50a73a400",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIyKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 22
        },
        "end": {
          "line": 22
        }
      }
    ]
  },
  {
    "vertex": "sha256:cb204f3d9b40827e6f9e8d9d2647a25f41dafc6b802807b43d51752b96de515c",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDEwKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 10
        },
        "end": {
          "line": 10
        }
      }
    ]
  },
  {
    "vertex": "sha256:de891251bb1d4820b99510bd3289c67536b36a6c33af561986668cb50a73a400",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDIxKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 21
        },
        "end": {
          "line": 21
        }
      }
    ]
  },
  {
    "vertex": "sha256:de891251bb1d4820b99510bd3289c67536b36a6c33af561986668cb50a73a400",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRQT1JUJyAobGluZSAyMik=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 22
        },
        "end": {
          "line": 22
        }
      }
    ]
  },
  {
    "vertex": "sha256:cb204f3d9b40827e6f9e8d9d2647a25f41dafc6b802807b43d51752b96de515c",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9USVRMRScgKGxpbmUgMTUp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 15
        },
        "end": {
          "line": 15
        }
      }
    ]
  },
  {
    "vertex": "sha256:cb204f3d9b40827e6f9e8d9d2647a25f41dafc6b802807b43d51752b96de515c",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9ERVNDUklQVElPTicgKGxpbmUgMTcp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 17
        },
        "end": {
          "line": 17
        }
      }
    ]
  },
  {
    "vertex": "sha256:de891251bb1d4820b99510bd3289c67536b36a6c33af561986668cb50a73a400",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAyMSk=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 21
        },
        "end": {
          "line": 21
        }
      }
    ]
  },
  {
    "vertex": "sha256:cb204f3d9b40827e6f9e8d9d2647a25f41dafc6b802807b43d51752b96de515c",
    "level": 1,
    "short": "TGVnYWN5S2V5VmFsdWVGb3JtYXQ6ICJFTlYga2V5PXZhbHVlIiBzaG91bGQgYmUgdXNlZCBpbnN0ZWFkIG9mIGxlZ2FjeSAiRU5WIGtleSB2YWx1ZSIgZm9ybWF0IChsaW5lIDEyKQ==",
    "detail": [
      "TGVnYWN5IGtleS92YWx1ZSBmb3JtYXQgd2l0aCB3aGl0ZXNwYWNlIHNlcGFyYXRvciBzaG91bGQgbm90IGJlIHVzZWQ="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/legacy-key-value-format/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 12
        },
        "end": {
          "line": 12
        }
      }
    ]
  },
  {
    "vertex": "sha256:de891251bb1d4820b99510bd3289c67536b36a6c33af561986668cb50a73a400",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9ERVNDUklQVElPTicgKGxpbmUgMTQp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 14
        },
        "end": {
          "line": 14
        }
      }
    ]
  },
  {
    "vertex": "sha256:cb204f3d9b40827e6f9e8d9d2647a25f41dafc6b802807b43d51752b96de515c",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAxMik=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "migrations.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSBjYS1jZXJ0aWZpY2F0ZXMKCldPUktESVIgL3Vzci9zcmMvYXBwCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpFTlYgRU5WSVJPTk1FTlQgcHJvZHVjdGlvbgpFTlYgTk9ERV9FTlYgcHJvZHVjdGlvbgpFTlYgUkVMRUFTRSAkUkVMRUFTRQoKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmxpY2Vuc2VzPU1JVApMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudGl0bGU9JElNQUdFX1RJVExFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS52ZXJzaW9uPSRSRUxFQVNFCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS5kZXNjcmlwdGlvbj0kSU1BR0VfREVTQ1JJUFRJT04KTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmF1dGhvcnM9IlRoZSBHdWlsZCIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlbmRvcj0iS2FtaWwgS2lzaWVsYSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnVybD0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnNvdXJjZT0iaHR0cHM6Ly9naXRodWIuY29tL2dyYXBocWwtaGl2ZS9wbGF0Zm9ybSIKCkVOVFJZUE9JTlQgWyAiL2VudHJ5cG9pbnQuc2giIF0K",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 12
        },
        "end": {
          "line": 12
        }
      }
    ]
  },
  {
    "vertex": "sha256:de891251bb1d4820b99510bd3289c67536b36a6c33af561986668cb50a73a400",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRJTUFHRV9USVRMRScgKGxpbmUgMTIp",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 12
        },
        "end": {
          "line": 12
        }
      }
    ]
  },
  {
    "vertex": "sha256:de891251bb1d4820b99510bd3289c67536b36a6c33af561986668cb50a73a400",
    "level": 1,
    "short": "VW5kZWZpbmVkVmFyOiBVc2FnZSBvZiB1bmRlZmluZWQgdmFyaWFibGUgJyRSRUxFQVNFJyAobGluZSAxMyk=",
    "detail": [
      "VmFyaWFibGVzIHNob3VsZCBiZSBkZWZpbmVkIGJlZm9yZSB0aGVpciB1c2U="
    ],
    "url": "https://docs.docker.com/go/dockerfile/rule/undefined-var/",
    "sourceInfo": {
      "filename": "services.dockerfile",
      "data": "RlJPTSBub2RlOjIyLjEzLjAtc2xpbQoKUlVOIGFwdC1nZXQgdXBkYXRlICYmIGFwdC1nZXQgaW5zdGFsbCAteSB3Z2V0IGNhLWNlcnRpZmljYXRlcyAmJiBybSAtcmYgL3Zhci9saWIvYXB0L2xpc3RzLyoKCkFSRyBTRVJWSUNFX0RJUl9OQU1FCldPUktESVIgL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FCgpDT1BZIC0tZnJvbT1kaXN0IC4gL3Vzci9zcmMvYXBwLyRTRVJWSUNFX0RJUl9OQU1FLwpDT1BZIC0tZnJvbT1zaGFyZWQgLiAvCgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UubGljZW5zZXM9TUlUCkxBQkVMIG9yZy5vcGVuY29udGFpbmVycy5pbWFnZS50aXRsZT0kSU1BR0VfVElUTEUKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLnZlcnNpb249JFJFTEVBU0UKTEFCRUwgb3JnLm9wZW5jb250YWluZXJzLmltYWdlLmRlc2NyaXB0aW9uPSRJTUFHRV9ERVNDUklQVElPTgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UuYXV0aG9ycz0iVGhlIEd1aWxkIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudmVuZG9yPSJLYW1pbCBLaXNpZWxhIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2UudXJsPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgpMQUJFTCBvcmcub3BlbmNvbnRhaW5lcnMuaW1hZ2Uuc291cmNlPSJodHRwczovL2dpdGh1Yi5jb20vZ3JhcGhxbC1oaXZlL3BsYXRmb3JtIgoKRU5WIEVOVklST05NRU5UIHByb2R1Y3Rpb24KRU5WIFJFTEVBU0UgJFJFTEVBU0UKRU5WIFBPUlQgJFBPUlQKCkhFQUxUSENIRUNLIC0taW50ZXJ2YWw9NXMgXAogIC0tdGltZW91dD01cyBcCiAgLS1zdGFydC1wZXJpb2Q9NXMgXAogIC0tcmV0cmllcz02IFwKICBDTUQgJEhFQUxUSENIRUNLX0NNRAoKRU5UUllQT0lOVCBbICIvZW50cnlwb2ludC5zaCIgXQo=",
      "language": "Dockerfile"
    },
    "range": [
      {
        "start": {
          "line": 13
        },
        "end": {
          "line": 13
        }
      }
    ]
  }
],
"composition-federation-2": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "Federation 2 Composition Service for GraphQL Hive.",
          "build-arg:IMAGE_TITLE": "graphql-hive/composition-federation-2",
          "build-arg:PORT": "3069",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "build-arg:SERVICE_DIR_NAME": "@hive/external-composition",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/k953xvmpspli37zslmbadd91t",
  "containerimage.config.digest": "sha256:310b88248e505fd535c44a56c78d1931fdcab8fadf3f58bdf8f7988502f468f8",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:42b43c5ecca2556853b417ac7f8a9f8485fbfc0ef31b8dab3edcebc4f93ce457",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:42b43c5ecca2556853b417ac7f8a9f8485fbfc0ef31b8dab3edcebc4f93ce457",
  "image.name": "ghcr.io/graphql-hive/composition-federation-2:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/composition-federation-2:linkable_spec_lib-amd64"
},
"emails": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The emails service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/emails",
          "build-arg:PORT": "3006",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "build-arg:SERVICE_DIR_NAME": "@hive/emails",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/48m12qhsg34pf3ebmotmx3i6n",
  "containerimage.config.digest": "sha256:a7d5134aa32f967875c0deb4fd7136bf1dbc54de4e465fa444c3881e8bfda1c0",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:4466de47d110b2441e1f1ffe07efddfa6edde08a583de48ac712cec5c123eaca",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:4466de47d110b2441e1f1ffe07efddfa6edde08a583de48ac712cec5c123eaca",
  "image.name": "ghcr.io/graphql-hive/emails:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/emails:linkable_spec_lib-amd64"
},
"policy": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The policy service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/policy",
          "build-arg:PORT": "3012",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "build-arg:SERVICE_DIR_NAME": "@hive/policy",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/pj704gups8ie1ct14vb85q3jk",
  "containerimage.config.digest": "sha256:f066a4fa649a8a8bf2f59e2f644b018dd6b77a31f5e6d7c1d058f1cf8592bc9b",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:ab1b08baa50f6349a73c594434109ec65ee2c104d96e7c30476530507717e563",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:ab1b08baa50f6349a73c594434109ec65ee2c104d96e7c30476530507717e563",
  "image.name": "ghcr.io/graphql-hive/policy:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/policy:linkable_spec_lib-amd64"
},
"rate-limit": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The rate limit service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/rate-limit",
          "build-arg:PORT": "3009",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "build-arg:SERVICE_DIR_NAME": "@hive/rate-limit",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/nofeomdv89eh50cx6ypzab11k",
  "containerimage.config.digest": "sha256:6313ecb9ad0cd53d0aacba6538ac12499b71d9749649c74a064fe71a430f9c34",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:bf125cbe15afa56cbde94cf40863fbd31297602821c92bd70e192391b0eb759e",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:bf125cbe15afa56cbde94cf40863fbd31297602821c92bd70e192391b0eb759e",
  "image.name": "ghcr.io/graphql-hive/rate-limit:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/rate-limit:linkable_spec_lib-amd64"
},
"schema": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The schema service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/schema",
          "build-arg:PORT": "3002",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "build-arg:SERVICE_DIR_NAME": "@hive/schema",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/naklc23mef7kes1cxiyba07ae",
  "containerimage.config.digest": "sha256:5fb6664d68eee949e1c9ebab868a2681c5bc90161801434d4eceafb25490bc51",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:c39ff270c8e2ae13c6ea0c9f3ca89806383199b0a574a470152fcdad28fe7b4a",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:c39ff270c8e2ae13c6ea0c9f3ca89806383199b0a574a470152fcdad28fe7b4a",
  "image.name": "ghcr.io/graphql-hive/schema:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/schema:linkable_spec_lib-amd64"
},
"server": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The server service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/server",
          "build-arg:PORT": "3001",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "build-arg:SERVICE_DIR_NAME": "@hive/server",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/olfjsnioonch06an59huexxk1",
  "containerimage.config.digest": "sha256:06eadd55703427f92f3803ec9e055b4f3da5bbe66a4a4d24f1a486e2f5c91e2e",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:6beb5978e96779693fcb42ae0186de22c2c0d0dd42ba2fe5b50358cb76656aea",
    "size": 2076,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:6beb5978e96779693fcb42ae0186de22c2c0d0dd42ba2fe5b50358cb76656aea",
  "image.name": "ghcr.io/graphql-hive/server:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/server:linkable_spec_lib-amd64"
},
"storage": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "migrations.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:IMAGE_DESCRIPTION": "The migrations service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/storage",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/yl66fh69xiaq0wabpfflo5ia2",
  "containerimage.config.digest": "sha256:552db8fcf3b0bfec90f1ef8fc3195693535fcc8a4ae3a40a2a7eaa34c2210ea0",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:81ba73802b786cacde9db6df7231afd565f84ea3a91770ec9f2cf27f1f291f31",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:81ba73802b786cacde9db6df7231afd565f84ea3a91770ec9f2cf27f1f291f31",
  "image.name": "ghcr.io/graphql-hive/storage:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/storage:linkable_spec_lib-amd64"
},
"stripe-billing": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The stripe billing service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/stripe-billing",
          "build-arg:PORT": "3010",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "build-arg:SERVICE_DIR_NAME": "@hive/stripe-billing",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/mg1ts04kdfp9m9t58qoud9ys0",
  "containerimage.config.digest": "sha256:f9230c5a6ff5c3ebae214c1ed7233efa4d31837c0c97117651c9c55c04bcf37d",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:4f5a2eddaf110538fa8ee7eb3a78957e85cda7e49449077639549aa63b740cb0",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:4f5a2eddaf110538fa8ee7eb3a78957e85cda7e49449077639549aa63b740cb0",
  "image.name": "ghcr.io/graphql-hive/stripe-billing:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/stripe-billing:linkable_spec_lib-amd64"
},
"tokens": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The tokens service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/tokens",
          "build-arg:PORT": "3003",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "build-arg:SERVICE_DIR_NAME": "@hive/tokens",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/w7rfw80869dt85wb0eju1djdn",
  "containerimage.config.digest": "sha256:0dc2321662aa531e4effcd4fd6008cd7d0fe9495dc4cb70ef04be0757f777e51",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:6bedcf383cf6349fa6c2bd1bceb9ecd10c9bd93c806c3bbd70af0756b6759265",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:6bedcf383cf6349fa6c2bd1bceb9ecd10c9bd93c806c3bbd70af0756b6759265",
  "image.name": "ghcr.io/graphql-hive/tokens:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/tokens:linkable_spec_lib-amd64"
},
"usage": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The usage ingestor service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/usage",
          "build-arg:PORT": "3006",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "build-arg:SERVICE_DIR_NAME": "@hive/usage",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/wmtxj2or59nga8it50o5bhy96",
  "containerimage.config.digest": "sha256:9c5f743d2b291ec2e7afee59cc03c4c4542278f0e35fdd8adbfed8422d5a5160",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:9f7f5a8c0d952708af0dd032ad58cf7c175aade77d096e4410fcbc86a5005040",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:9f7f5a8c0d952708af0dd032ad58cf7c175aade77d096e4410fcbc86a5005040",
  "image.name": "ghcr.io/graphql-hive/usage:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/usage:linkable_spec_lib-amd64"
},
"usage-estimator": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The usage estimator service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/usage-estimator",
          "build-arg:PORT": "3008",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "build-arg:SERVICE_DIR_NAME": "@hive/usage-estimator",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/4l3h2hpwnqvmwi7lktx48p39k",
  "containerimage.config.digest": "sha256:404ebb9b5a6453e4d2ec7650348c4131fff9ba463f9a9e40e8f97ac637f5388a",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:df80fac3f3e35f35bfe0297cb2cd5fc7719a95e8d0bea775098f9f13f8bdca46",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:df80fac3f3e35f35bfe0297cb2cd5fc7719a95e8d0bea775098f9f13f8bdca46",
  "image.name": "ghcr.io/graphql-hive/usage-estimator:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/usage-estimator:linkable_spec_lib-amd64"
},
"usage-ingestor": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The usage ingestor service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/usage-ingestor",
          "build-arg:PORT": "3007",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "build-arg:SERVICE_DIR_NAME": "@hive/usage-ingestor",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/p8oacrfc7qka8d18w330otx0y",
  "containerimage.config.digest": "sha256:1aa9d03b7e45d7970f1ef91edad84367a2f89121e76547edfd51b2552fc25870",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:62638380cf8c544a4a5c7928db49f9ce264dcbf92f8aed29646b1efcaa95fb87",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:62638380cf8c544a4a5c7928db49f9ce264dcbf92f8aed29646b1efcaa95fb87",
  "image.name": "ghcr.io/graphql-hive/usage-ingestor:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/usage-ingestor:linkable_spec_lib-amd64"
},
"webhooks": {
  "buildx.build.provenance": {
    "buildType": "https://mobyproject.org/buildkit@v1",
    "materials": [
      {
        "uri": "pkg:docker/[email protected]?platform=linux%2Famd64",
        "digest": {
          "sha256": "f5a0871ab03b035c58bdb3007c3d177b001c2145c18e81817b71624dcf7d8bff"
        }
      }
    ],
    "invocation": {
      "configSource": {
        "entryPoint": "services.dockerfile"
      },
      "parameters": {
        "frontend": "dockerfile.v0",
        "args": {
          "build-arg:HEALTHCHECK_CMD": "wget --spider -q http://127.0.0.1:${PORT}/_readiness",
          "build-arg:IMAGE_DESCRIPTION": "The webhooks ingestor service of the GraphQL Hive project.",
          "build-arg:IMAGE_TITLE": "graphql-hive/webhooks",
          "build-arg:PORT": "3005",
          "build-arg:RELEASE": "b009d102eb95d325839674fa7f154ef0bb3872a0",
          "build-arg:SERVICE_DIR_NAME": "@hive/webhooks",
          "context:dist": "local:dist",
          "context:shared": "local:shared",
          "frontend.caps": "moby.buildkit.frontend.contexts+forward",
          "local-sessionid:context": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:dockerfile": "q8yd272t0nohoqmvuapwee526",
          "local-sessionid:shared": "q8yd272t0nohoqmvuapwee526"
        },
        "locals": [
          {
            "name": "context"
          },
          {
            "name": "dist"
          },
          {
            "name": "dockerfile"
          },
          {
            "name": "shared"
          }
        ]
      },
      "environment": {
        "platform": "linux/amd64"
      }
    }
  },
  "buildx.build.ref": "builder-af7cf860-98cb-4e2b-abcb-b4a8048bd034/builder-af7cf860-98cb-4e2b-abcb-b4a8048bd0340/pc0b5lowhth8nfsesvisp6glz",
  "containerimage.config.digest": "sha256:867560b99b81ef5adae83d85267bf9b2a81e239ac76e36259c01e70acca197f8",
  "containerimage.descriptor": {
    "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
    "digest": "sha256:ae1becf1b16e86a34b9a84152f45e810da20918dbbaf92354b7df542fe7d54c8",
    "size": 2075,
    "platform": {
      "architecture": "amd64",
      "os": "linux"
    }
  },
  "containerimage.digest": "sha256:ae1becf1b16e86a34b9a84152f45e810da20918dbbaf92354b7df542fe7d54c8",
  "image.name": "ghcr.io/graphql-hive/webhooks:b009d102eb95d325839674fa7f154ef0bb3872a0-amd64,ghcr.io/graphql-hive/webhooks:linkable_spec_lib-amd64"
}
}

@jdolle jdolle enabled auto-merge (squash) February 4, 2025 06:57
@n1ru4l
Copy link
Contributor

n1ru4l commented Feb 4, 2025

Is supporting Federation 1 out of the scope of this?

E.g. we support @tag/schema contracts for Federation 1 and 2. This directive only addresses Federation 2 use-cases though. https://www.apollographql.com/docs/graphos/platform/schema-management/delivery/contracts/create#1-add-tags-to-subgraph-schemas

Thinking about this, I would like to see this library being used on something like packages/services/schema/src/lib/federation-tag-extraction.ts to better understand its real-world usage.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/libraries/federation-link-utils/README.md (2)

9-12: Correct Grammar in the Description

There is a minor grammatical error on line 9: the phrase "make a custom features" should be revised for correctness. Consider rephrasing it to "create custom features" or "make custom features" for clarity.

-This library can be used to make a custom features for GraphQL schemas backed by Federation's
+This library can be used to create custom features for GraphQL schemas backed by Federation's
🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)


66-68: Clarify Final Notes on LinkableSpec Implementation

The concluding explanation regarding LinkableSpec is helpful, but it could benefit from additional context. A brief note or a reference link explaining what it means to "always return a yoga plugin" would add clarity for users who might not be familiar with that convention.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a84e735 and b751f8b.

📒 Files selected for processing (1)
  • packages/libraries/federation-link-utils/README.md (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
packages/libraries/federation-link-utils/README.md (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

📓 Learnings (1)
packages/libraries/federation-link-utils/README.md (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🪛 LanguageTool
packages/libraries/federation-link-utils/README.md

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: test / unit
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: alpha / cli-artifacts
🔇 Additional comments (6)
packages/libraries/federation-link-utils/README.md (6)

1-2: Descriptive Title and Overview

The file title "GraphQL Hive - federation-link-utils" is clear and descriptive. Consider adding a brief tagline that summarizes the library’s purpose to further orient new users.


3-6: Clear Introduction Provided

The introduction effectively explains what GraphQL Hive offers and provides useful links for more information. Please ensure that the external links remain up-to-date as the project evolves.


7-8: Well-Placed Section Separator

The horizontal rule is appropriately used to separate the introductory content from the rest of the document.


13-18: Feature List is Clear and Concise

The features section succinctly lists the library’s core capabilities. In future iterations, you might consider providing additional details or examples for each feature to enhance the user’s understanding.


19-35: Usage Example for GraphQL Schema is Informative

The provided GraphQL schema example clearly demonstrates how to extend a schema using the @link directive. This example is valuable for users looking to grasp the practical application of the library.


36-64: Enhance Code Snippet Clarity with Required Imports

The TypeScript snippet effectively demonstrates how to create a LinkableSpec and utilize detectLinkedImplementations. However, to improve clarity for developers integrating this code, consider explicitly adding the necessary imports (e.g., visit, parse, DocumentNode, and StringValueNode) from the appropriate modules.

+// Ensure to import required dependencies from 'graphql'
+import { DocumentNode, StringValueNode, visit, parse } from 'graphql';

@jdolle
Copy link
Collaborator Author

jdolle commented Feb 4, 2025

Is supporting Federation 1 out of the scope of this?

The @link directive is only in federation 2.
This package is specifically to handle the versioning of @links and the naming of the associated imports per the @link spec. Extracting directives (other than @link) from the schema falls outside the scope of this package.

@jdolle jdolle force-pushed the linkable-spec-lib branch from 142969f to 484e969 Compare February 4, 2025 18:08
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/libraries/federation-link-utils/README.md (1)

66-68: Clarify the “yoga plugin” Recommendation

The final note suggests always returning a “yoga plugin” without any further explanation. To improve clarity for users who might not be familiar with this term, consider adding a brief explanation or a link to relevant documentation.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b751f8b and 484e969.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • packages/libraries/federation-link-utils/README.md (1 hunks)
  • packages/libraries/federation-link-utils/package.json (1 hunks)
  • packages/libraries/federation-link-utils/src/__tests__/index.spec.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/__tests__/link-url.spec.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/__tests__/link.spec.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/__tests__/linkable-spec.spec.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/index.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/link-import.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/link-url.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/link.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/linkable-spec.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/version.ts (1 hunks)
  • packages/libraries/federation-link-utils/tsconfig.json (1 hunks)
  • tsconfig.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (13)
  • packages/libraries/federation-link-utils/src/version.ts
  • tsconfig.json
  • packages/libraries/federation-link-utils/src/index.ts
  • packages/libraries/federation-link-utils/src/tests/link-url.spec.ts
  • packages/libraries/federation-link-utils/tsconfig.json
  • packages/libraries/federation-link-utils/package.json
  • packages/libraries/federation-link-utils/src/tests/linkable-spec.spec.ts
  • packages/libraries/federation-link-utils/src/tests/link.spec.ts
  • packages/libraries/federation-link-utils/src/tests/index.spec.ts
  • packages/libraries/federation-link-utils/src/link-import.ts
  • packages/libraries/federation-link-utils/src/linkable-spec.ts
  • packages/libraries/federation-link-utils/src/link-url.ts
  • packages/libraries/federation-link-utils/src/link.ts
🧰 Additional context used
📓 Path-based instructions (1)
packages/libraries/federation-link-utils/README.md (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

📓 Learnings (1)
packages/libraries/federation-link-utils/README.md (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🪛 LanguageTool
packages/libraries/federation-link-utils/README.md

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)

⏰ Context from checks skipped due to timeout of 90000ms (11)
  • GitHub Check: alpha / npm / snapshot
  • GitHub Check: test / unit
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: db-types / diff
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: alpha / cli-artifacts
  • GitHub Check: SonarCloud
🔇 Additional comments (4)
packages/libraries/federation-link-utils/README.md (4)

1-8: Clear and Informative Introduction

The header and opening paragraphs clearly introduce the library and its context within GraphQL Hive. The title, description, and horizontal rule effectively set the stage for the rest of the document.


13-18: Solid Features Section

The “Features” list is concise and well organized. It clearly shows the key capabilities of the library such as link version support and import namespacing while keeping external dependencies to a minimum.


19-34: Effective GraphQL Usage Example

The GraphQL snippet effectively demonstrates how to use the custom directive with the @link directive. The example is clear and provides a good reference for users.


36-64: Enhance TypeScript Example with Complete Import Guidance

The TypeScript snippet thoroughly illustrates how to create and use a LinkableSpec. However, functions and types such as visit, parse, DocumentNode, and StringValueNode appear in the example without corresponding import statements. Although these omissions might be for brevity, providing clarity on the source of these symbols would help users integrate the snippet more readily.

 // specs.ts
-import { detectLinkedImplementations, LinkableSpec } from '@graphql-hive/federation-link-utils'
+import { detectLinkedImplementations, LinkableSpec } from '@graphql-hive/federation-link-utils'
+import { visit, parse } from 'graphql'; // Consider adding this import if using the GraphQL library
+import type { DocumentNode, StringValueNode } from 'graphql'; // Add proper type imports

Comment on lines +9 to +11
This library can be used to make a custom features for GraphQL schemas backed by Federation's
[`@link`](https://www.apollographql.com/docs/graphos/reference/federation/directives#the-link-directive)
directive.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Fix Grammar in Library Description

There is a grammatical issue with the phrase “a custom features.” It would be clearer to remove the singular article and use a verb that better suits the plural noun. Consider the following adjustment:

-This library can be used to make a custom features for GraphQL schemas backed by Federation's
+This library can be used to create custom features for GraphQL schemas backed by Federation's
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
This library can be used to make a custom features for GraphQL schemas backed by Federation's
[`@link`](https://www.apollographql.com/docs/graphos/reference/federation/directives#the-link-directive)
directive.
This library can be used to create custom features for GraphQL schemas backed by Federation's
[`@link`](https://www.apollographql.com/docs/graphos/reference/federation/directives#the-link-directive)
directive.
🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)

@jdolle
Copy link
Collaborator Author

jdolle commented Feb 4, 2025

I've added a way to support federation 1.
I did this in a way that is optional and maintains the same interface for resolving the element names.
I think this satisfies the case for packages/services/schema/src/lib/federation-tag-extraction.ts.

However, I'd like to work on refactoring the tag extraction logic in a separate PR to avoid this change getting any larger. If we end up being unhappy with this library, we can iterate in that change or revert this change. Does that sound reasonable?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
packages/libraries/federation-link-utils/src/index.ts (1)

12-32: Consider adding JSDoc documentation for the function.

While the function implementation looks good, adding JSDoc documentation would help users understand:

  • The purpose of the function
  • The expected format of input parameters
  • The structure of the return value
  • Example usage
+/**
+ * Detects and returns implementations from a list of supported specifications based on the links defined in the schema.
+ * @param typeDefs - The GraphQL schema as a DocumentNode
+ * @param supportedSpecs - Array of LinkableSpec instances to detect implementations for
+ * @returns Array of detected implementations
+ * @example
+ * ```ts
+ * const typeDefs = parse(sdl);
+ * const specs = [new LinkableSpec(...)];
+ * const implementations = detectLinkedImplementations(typeDefs, specs);
+ * ```
+ */
 export function detectLinkedImplementations<T>(
   typeDefs: DocumentNode,
   supportedSpecs: LinkableSpec<T>[],
 ): T[] {
packages/libraries/federation-link-utils/src/linkable-spec.ts (1)

50-53: Consider using a structured logging solution.

Instead of using console.warn directly, consider using a structured logging solution that can be configured based on the environment and allows for better error tracking.

-      console.warn(
-        `Cannot apply @link due to unsupported version found for "${this.identity}". ` +
-          `Available versions: ${this.sortedVersionKeys.join(', ')} and any version these are compatible compatible with.`,
-      );
+      const message = `Cannot apply @link due to unsupported version found for "${this.identity}". ` +
+        `Available versions: ${this.sortedVersionKeys.join(', ')} and any version these are compatible with.`;
+      this.logger?.warn?.(message) ?? console.warn(message);

Also, there's a typo in the message: "compatible" is repeated twice.

packages/libraries/federation-link-utils/README.md (1)

40-76: Enhance the TypeScript example.

The example code could be improved in several ways:

  1. Add type annotations for the sdl variable
  2. Show error handling for the apply function
  3. Document the expected shape of the return value
 // specs.ts
 import {
   detectLinkedImplementations,
   FEDERATION_V1,
-  LinkableSpec
+  LinkableSpec,
+  DocumentNode
 } from '@graphql-hive/federation-link-utils'

+// Define the shape of your implementation
+type ExampleImpl = Record<string, string>;
+
+// Your SDL as a string
+const sdl = `...`;
+
 const exampleSpec = new LinkableSpec('https://specs.graphql-hive.com/example', {
   [FEDERATION_V1]: resolveImportName => (typeDefs: DocumentNode) => {
     // option to support federation 1 schemas. Be extremely cautious here because versioning
     // cannot be done safely.
   },
   'v0.1': resolveImportName => (typeDefs: DocumentNode) => {
     const examples: Record<string, string> = {}
     // ... rest of the implementation
     return examples
   }
 })
 const typeDefs = parse(sdl)
 const linkedSpecs = detectLinkedImplementations(typeDefs, [exampleSpec])
-const result = linkedSpecs.map(apply => apply(typeDefs))
+const result = linkedSpecs.map(apply => {
+  try {
+    return apply(typeDefs) as ExampleImpl;
+  } catch (error) {
+    console.error('Failed to apply spec:', error);
+    return {};
+  }
+});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 484e969 and 1972b22.

📒 Files selected for processing (5)
  • packages/libraries/federation-link-utils/README.md (1 hunks)
  • packages/libraries/federation-link-utils/src/__tests__/index.spec.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/index.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/link.ts (1 hunks)
  • packages/libraries/federation-link-utils/src/linkable-spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/libraries/federation-link-utils/src/tests/index.spec.ts
  • packages/libraries/federation-link-utils/src/link.ts
🧰 Additional context used
📓 Path-based instructions (3)
packages/libraries/federation-link-utils/src/linkable-spec.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/federation-link-utils/src/index.ts (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

packages/libraries/federation-link-utils/README.md (1)

Pattern packages/libraries/**: Most of the dirs here are NPM packages that provides our customers an integration library, in order to get/fetch data from Hive Console API or Hive CDN.

📓 Learnings (1)
packages/libraries/federation-link-utils/README.md (1)
Learnt from: jdolle
PR: graphql-hive/console#6452
File: packages/libraries/linkable-specs/src/__tests__/index.spec.ts:25-25
Timestamp: 2025-02-03T03:41:50.270Z
Learning: The @graphql-hive/linkable-specs package handles directive namespacing automatically through its resolveImportName function. Users should use base directive names (e.g., '@example') rather than fully qualified names (e.g., '@metadata_example') as the package manages the namespace transformation internally.
🪛 LanguageTool
packages/libraries/federation-link-utils/README.md

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: alpha / publish_docker / Build CLI Docker Image
  • GitHub Check: static-analysis / analyze (typescript)
  • GitHub Check: typescript / typecheck
  • GitHub Check: static-analysis / analyze (javascript)
  • GitHub Check: build / dockerize (linux/arm64)
  • GitHub Check: build / dockerize (linux/amd64)
  • GitHub Check: code-style / eslint-and-prettier
  • GitHub Check: alpha / cli-artifacts
🔇 Additional comments (2)
packages/libraries/federation-link-utils/src/index.ts (1)

5-8: LGTM! Clean and well-organized exports.

The exports are properly organized and provide a clear public API for the library.

packages/libraries/federation-link-utils/README.md (1)

9-11: Fix grammar in library description.

There is a grammatical issue in the description.

-This library can be used to make a custom features for GraphQL schemas backed by Federation's
+This library can be used to create custom features for GraphQL schemas backed by Federation's
🧰 Tools
🪛 LanguageTool

[grammar] ~9-~9: Do not use the singular ‘a’ before the plural noun ‘features’.
Context: ... --- This library can be used to make a custom features for GraphQL schemas backed by Federatio...

(VB_A_JJ_NNS)

Comment on lines +18 to +22
this.sortedVersionKeys = Object.keys(versions).sort((a, b) => {
const [aMajor, aMinor] = a.split('.').map(Number);
const [bMajor, bMinor] = b.split('.').map(Number);
return bMajor !== aMajor ? bMajor - aMajor : bMinor - aMinor;
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Invalid Version Format Handling Required

The current version sorting logic assumes that all version keys strictly follow a valid "major.minor" format. There is no validation (e.g., checking for NaN values or ensuring exactly two numeric parts) to safeguard against invalid version strings, which could lead to unpredictable behavior.

  • Location: packages/libraries/federation-link-utils/src/linkable-spec.ts (inside the constructor, where sortedVersionKeys is initialized)
  • Issue: No validation or error handling is applied when splitting and converting version strings.
  • Recommendation: Add validation logic (or error handling) to check the version string format before performing the sort.
🔗 Analysis chain

Consider handling invalid version formats.

The version sorting logic assumes valid version strings in the format "major.minor". Consider adding validation or error handling for invalid version formats.


I'll run new scripts to inspect the file and look for any error handling or validation related to the version sorting logic. This will help us determine whether we need to validate version formats.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for any non-standard version formats in the codebase
rg -g '*.{ts,js,graphql}' -A 2 '@link\(.*version:' 

Length of output: 52


Script:

#!/bin/bash
# 1. Display context for the version sorting logic to see if any validation exists.
rg -C 10 "sortedVersionKeys" packages/libraries/federation-link-utils/src/linkable-spec.ts

# 2. Check for use of isNaN, which might indicate any runtime validation.
rg -n "isNaN" packages/libraries/federation-link-utils/src/linkable-spec.ts

# 3. Search for any warning or error logging (e.g., console.warn) related to version parsing.
rg -n "console.warn" packages/libraries/federation-link-utils/src/linkable-spec.ts

Length of output: 2350

@jdolle jdolle changed the title feature: add linkable-spec utility lib feature: add federation-link-util library Feb 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

4 participants