Repository: graphql-hive/console
Pull request #6779: fix(schema): always treat network errors as schema publish rejections
Author: login: n1ru4l
Created: 2025-05-13T10:26:46Z | Merged: 2025-05-14T07:11:53Z

### Background

<!---
Please include a short note explaining the need for this PR / change.
If you are resolving/closing/fixing an issue, please mention it in this section.
--->

### Description

<!---
Please share here a technical description of your changes. This should include what packages/components are effects: CLI, client/agent, services, APIs.
--->

### Checklist

<!---
We are following the OWASP Secure Coding Practices for develpoing Hive. You can find the complete guide here:
https://owasp.org/www-pdf-archive/OWASP_SCP_Quick_Reference_Guide_v2.pdf

Please use this checklist to ensure your PR quality before proceeding.
You may remove unnecessary checks from this list, if it's not relevant to your changes.
--->

- [x] Input validation
- [x] Output encoding
- [x] Authentication management
- [ ] Session management
- [x] Access control
- [ ] Cryptographic practices
- [ ] Error handling and logging
- [ ] Data protection
- [ ] Communication security
- [ ] System configuration
- [ ] Database security
- [ ] File management
- [x] Memory management
- [x] Testing



Diff:
diff --git a/deployment/services/schema.ts b/deployment/services/schema.ts
index 4f86e8b56f4..7b3bfe69e9f 100644
--- a/deployment/services/schema.ts
+++ b/deployment/services/schema.ts
@@ -42,7 +42,6 @@ export function deploySchema({
         SCHEMA_CACHE_POLL_INTERVAL_MS: '150',
         SCHEMA_CACHE_TTL_MS: '65000' /* 65s */,
         SCHEMA_CACHE_SUCCESS_TTL_MS: String(hourInMS * 2),
-        SCHEMA_COMPOSITION_TIMEOUT_MS: String(minuteInMS),
         OPENTELEMETRY_COLLECTOR_ENDPOINT:
           observability.enabled && observability.tracingEndpoint
             ? observability.tracingEndpoint
diff --git a/integration-tests/package.json b/integration-tests/package.json
index f01f905cf9c..5495d61c230 100644
--- a/integration-tests/package.json
+++ b/integration-tests/package.json
@@ -21,7 +21,7 @@
     "@hive/schema": "workspace:*",
     "@hive/server": "workspace:*",
     "@hive/storage": "workspace:*",
-    "@theguild/federation-composition": "0.18.1",
+    "@theguild/federation-composition": "0.18.3",
     "@trpc/client": "10.45.2",
     "@trpc/server": "10.45.2",
     "@types/async-retry": "1.4.8",
diff --git a/integration-tests/tests/api/schema/external-composition.spec.ts b/integration-tests/tests/api/schema/external-composition.spec.ts
index a68d2a74628..435b084c5f8 100644
--- a/integration-tests/tests/api/schema/external-composition.spec.ts
+++ b/integration-tests/tests/api/schema/external-composition.spec.ts
@@ -367,7 +367,7 @@ test.concurrent('a timeout error should be visible to the user', async ({ expect
         total: 1,
         nodes: [
           {
-            message: expect.stringMatching(/timeout/i),
+            message: expect.stringMatching(/The schema composition timed out. Please try again./i),
           },
         ],
       },
diff --git a/packages/libraries/cli/package.json b/packages/libraries/cli/package.json
index c3a2c1667ef..379feda59d0 100644
--- a/packages/libraries/cli/package.json
+++ b/packages/libraries/cli/package.json
@@ -59,7 +59,7 @@
     "@oclif/core": "^3.26.6",
     "@oclif/plugin-help": "6.0.22",
     "@oclif/plugin-update": "4.2.13",
-    "@theguild/federation-composition": "0.18.1",
+    "@theguild/federation-composition": "0.18.3",
     "colors": "1.4.0",
     "env-ci": "7.3.0",
     "graphql": "^16.8.1",
diff --git a/packages/services/api/package.json b/packages/services/api/package.json
index bb9186e15ba..78939d89ee3 100644
--- a/packages/services/api/package.json
+++ b/packages/services/api/package.json
@@ -37,7 +37,7 @@
     "@sentry/node": "7.120.2",
     "@sentry/types": "7.120.2",
     "@slack/web-api": "7.8.0",
-    "@theguild/federation-composition": "0.18.1",
+    "@theguild/federation-composition": "0.18.3",
     "@trpc/client": "10.45.2",
     "@trpc/server": "10.45.2",
     "@types/bcryptjs": "2.4.6",
diff --git a/packages/services/api/src/modules/schema/providers/models/composite.ts b/packages/services/api/src/modules/schema/providers/models/composite.ts
index bfcafb5cced..a85c55e87fa 100644
--- a/packages/services/api/src/modules/schema/providers/models/composite.ts
+++ b/packages/services/api/src/modules/schema/providers/models/composite.ts
@@ -456,6 +456,21 @@ export class CompositeModel {
         })) ?? null,
     });
 
+    if (
+      compositionCheck.status === 'failed' &&
+      (compositionCheck.reason.includesNetworkError || compositionCheck.reason.includesException)
+    ) {
+      return {
+        conclusion: SchemaPublishConclusion.Reject,
+        reasons: [
+          {
+            code: PublishFailureReasonCode.CompositionFailure,
+            compositionErrors: compositionCheck.reason.errorsBySource.composition,
+          },
+        ],
+      };
+    }
+
     if (
       compositionCheck.status === 'failed' &&
       compositionCheck.reason.errorsBySource.graphql.length > 0 &&
diff --git a/packages/services/api/src/modules/schema/providers/orchestrators/federation.ts b/packages/services/api/src/modules/schema/providers/orchestrators/federation.ts
index 6768057ac45..8e8542e2baa 100644
--- a/packages/services/api/src/modules/schema/providers/orchestrators/federation.ts
+++ b/packages/services/api/src/modules/schema/providers/orchestrators/federation.ts
@@ -3,7 +3,13 @@ import { abortSignalAny } from '@graphql-hive/signal';
 import type { ContractsInputType, SchemaBuilderApi } from '@hive/schema';
 import { traceFn } from '@hive/service-common';
 import { createTRPCProxyClient, httpLink } from '@trpc/client';
-import { Orchestrator, Project, ProjectType, SchemaObject } from '../../../../shared/entities';
+import {
+  ComposeAndValidateResult,
+  Orchestrator,
+  Project,
+  ProjectType,
+  SchemaObject,
+} from '../../../../shared/entities';
 import { Logger } from '../../../shared/providers/logger';
 import type { SchemaServiceConfig } from './tokens';
 import { SCHEMA_SERVICE_CONFIG } from './tokens';
@@ -121,8 +127,29 @@ export class FederationOrchestrator implements Orchestrator {
           signal: abortSignalAny([this.incomingRequestAbortSignal, timeoutAbortSignal]),
         },
       );
-
       return result;
+    } catch (err) {
+      // In case of a timeout error we return something the user can process
+      if (timeoutAbortSignal.reason) {
+        return {
+          contracts: null,
+          metadataAttributes: null,
+          schemaMetadata: null,
+          sdl: null,
+          supergraph: null,
+          tags: null,
+          includesNetworkError: true,
+          includesException: false,
+          errors: [
+            {
+              message: 'The schema composition timed out. Please try again.',
+              source: 'composition',
+            },
+          ],
+        } satisfies ComposeAndValidateResult;
+      }
+
+      throw err;
     } finally {
       timeoutAbortSignal.removeEventListener('abort', onTimeout);
       this.incomingRequestAbortSignal.removeEventListener('abort', onIncomingRequestAbort);
diff --git a/packages/services/api/src/modules/schema/providers/orchestrators/single.ts b/packages/services/api/src/modules/schema/providers/orchestrators/single.ts
index 1d0a2da2765..adf9a53b63a 100644
--- a/packages/services/api/src/modules/schema/providers/orchestrators/single.ts
+++ b/packages/services/api/src/modules/schema/providers/orchestrators/single.ts
@@ -3,7 +3,12 @@ import { abortSignalAny } from '@graphql-hive/signal';
 import type { SchemaBuilderApi } from '@hive/schema';
 import { traceFn } from '@hive/service-common';
 import { createTRPCProxyClient, httpLink } from '@trpc/client';
-import { Orchestrator, ProjectType, SchemaObject } from '../../../../shared/entities';
+import {
+  ComposeAndValidateResult,
+  Orchestrator,
+  ProjectType,
+  SchemaObject,
+} from '../../../../shared/entities';
 import { Logger } from '../../../shared/providers/logger';
 import type { SchemaServiceConfig } from './tokens';
 import { SCHEMA_SERVICE_CONFIG } from './tokens';
@@ -90,6 +95,27 @@ export class SingleOrchestrator implements Orchestrator {
         },
       );
       return result;
+    } catch (err) {
+      // In case of a timeout error we return something the user can process
+      if (timeoutAbortSignal.reason) {
+        return {
+          contracts: null,
+          metadataAttributes: null,
+          schemaMetadata: null,
+          sdl: null,
+          supergraph: null,
+          tags: null,
+          includesNetworkError: true,
+          includesException: false,
+          errors: [
+            {
+              message: 'The schema composition timed out. Please try again.',
+              source: 'composition',
+            },
+          ],
+        } satisfies ComposeAndValidateResult;
+      }
+      throw err;
     } finally {
       timeoutAbortSignal.removeEventListener('abort', onTimeout);
       this.incomingRequestAbortSignal.removeEventListener('abort', onIncomingRequestAbort);
diff --git a/packages/services/api/src/modules/schema/providers/orchestrators/stitching.ts b/packages/services/api/src/modules/schema/providers/orchestrators/stitching.ts
index f500acea3dc..942e899a55f 100644
--- a/packages/services/api/src/modules/schema/providers/orchestrators/stitching.ts
+++ b/packages/services/api/src/modules/schema/providers/orchestrators/stitching.ts
@@ -3,7 +3,12 @@ import { abortSignalAny } from '@graphql-hive/signal';
 import type { SchemaBuilderApi } from '@hive/schema';
 import { traceFn } from '@hive/service-common';
 import { createTRPCProxyClient, httpLink } from '@trpc/client';
-import { Orchestrator, ProjectType, SchemaObject } from '../../../../shared/entities';
+import {
+  ComposeAndValidateResult,
+  Orchestrator,
+  ProjectType,
+  SchemaObject,
+} from '../../../../shared/entities';
 import { Logger } from '../../../shared/providers/logger';
 import type { SchemaServiceConfig } from './tokens';
 import { SCHEMA_SERVICE_CONFIG } from './tokens';
@@ -84,6 +89,27 @@ export class StitchingOrchestrator implements Orchestrator {
         },
       );
       return result;
+    } catch (err) {
+      // In case of a timeout error we return something the user can process
+      if (timeoutAbortSignal.reason) {
+        return {
+          contracts: null,
+          metadataAttributes: null,
+          schemaMetadata: null,
+          sdl: null,
+          supergraph: null,
+          tags: null,
+          includesNetworkError: true,
+          includesException: false,
+          errors: [
+            {
+              message: 'The schema composition timed out. Please try again.',
+              source: 'composition',
+            },
+          ],
+        } satisfies ComposeAndValidateResult;
+      }
+      throw err;
     } finally {
       timeoutAbortSignal.removeEventListener('abort', onTimeout);
       this.incomingRequestAbortSignal.removeEventListener('abort', onIncomingRequestAbort);
diff --git a/packages/services/api/src/modules/schema/providers/registry-checks.ts b/packages/services/api/src/modules/schema/providers/registry-checks.ts
index dcf66150511..3f72118e734 100644
--- a/packages/services/api/src/modules/schema/providers/registry-checks.ts
+++ b/packages/services/api/src/modules/schema/providers/registry-checks.ts
@@ -283,6 +283,8 @@ export class RegistryChecks {
           // Federation 1 apparently has SDL and validation errors at the same time.
           fullSchemaSdl: result.sdl,
           contracts: result.contracts?.map(mapContract) ?? null,
+          includesNetworkError: result.includesNetworkError ?? false,
+          includesException: result.includesException ?? false,
         },
       } satisfies CheckResult;
     }
diff --git a/packages/services/api/src/shared/entities.ts b/packages/services/api/src/shared/entities.ts
index 34cf515e581..3383fea61dd 100644
--- a/packages/services/api/src/shared/entities.ts
+++ b/packages/services/api/src/shared/entities.ts
@@ -394,6 +394,8 @@ export interface ComposeAndValidateResult {
     Array<{ name: string; content: string; source: string | null }>
   > | null;
   metadataAttributes: null | Record<string, string[]>;
+  includesNetworkError?: boolean;
+  includesException?: boolean;
 }
 
 export interface Orchestrator {
diff --git a/packages/services/schema/.env.template b/packages/services/schema/.env.template
index eae780bcfcb..22cadc68254 100644
--- a/packages/services/schema/.env.template
+++ b/packages/services/schema/.env.template
@@ -4,5 +4,6 @@ REDIS_PASSWORD=""
 ENCRYPTION_SECRET="97e4094d2463e71a981913cca4e56788"
 SCHEMA_CACHE_TTL_MS=5000
 SCHEMA_CACHE_SUCCESS_TTL_MS=5000
-SCHEMA_COMPOSITION_TIMEOUT_MS=25000
-OPENTELEMETRY_COLLECTOR_ENDPOINT="<sync>"
\ No newline at end of file
+SCHEMA_COMPOSITION_TIMEOUT_MS=30000
+SCHEMA_EXTERNAL_COMPOSITION_TIMEOUT_MS=9000
+OPENTELEMETRY_COLLECTOR_ENDPOINT="<sync>"
diff --git a/packages/services/schema/package.json b/packages/services/schema/package.json
index c14440f3942..f14987a37e1 100644
--- a/packages/services/schema/package.json
+++ b/packages/services/schema/package.json
@@ -15,7 +15,7 @@
     "@graphql-tools/stitching-directives": "3.1.31",
     "@hive/service-common": "workspace:*",
     "@sentry/node": "7.120.2",
-    "@theguild/federation-composition": "0.18.1",
+    "@theguild/federation-composition": "0.18.3",
     "@trpc/server": "10.45.2",
     "@types/async-retry": "1.4.8",
     "@types/ioredis-mock": "8.2.5",
diff --git a/packages/services/schema/src/api.ts b/packages/services/schema/src/api.ts
index c25865eb361..9eaaa5f3c25 100644
--- a/packages/services/schema/src/api.ts
+++ b/packages/services/schema/src/api.ts
@@ -28,7 +28,7 @@ const t = initTRPC.context<Context>().create();
 
 const procedure = t.procedure.use(handleTRPCError);
 
-const EXTERNAL_VALIDATION = z
+const ExternalValidationModel = z
   .object({
     endpoint: z.string().url().min(1),
     encryptedSecret: z.string().min(1),
@@ -72,7 +72,7 @@ export const schemaBuilderApiRouter = t.router({
               })
               .required(),
           ),
-          external: EXTERNAL_VALIDATION,
+          external: ExternalValidationModel,
           native: z.boolean().optional(),
           contracts: ContractsInputModel.nullable().optional(),
         }),
@@ -100,7 +100,6 @@ export const schemaBuilderApiRouter = t.router({
                 data: {
                   type: 'federation',
                   args,
-                  requestTimeoutMs: ctx.cache.timeoutMs,
                 },
                 abortSignal,
                 requestId: ctx.req.id,
@@ -159,24 +158,28 @@ export const schemaBuilderApiRouter = t.router({
 
         assertAllCasesExhausted(input);
       } catch (error) {
+        ctx.req.log.error('Composition timed out. (error=%o)', error);
+
         // Treat timeouts caused by external composition as "expected errors"
-        if (ctx.cache.isTimeoutError(error) && input.type === 'federation' && input.external) {
+        if (ctx.cache.isTimeoutError(error)) {
           return {
             errors: [
               {
-                message: error.message,
-                source: 'graphql',
+                message: 'The composition timed out. Please try again later.',
+                source: 'composition',
               },
             ],
             sdl: null,
             supergraph: null,
-            includesNetworkError: true,
+            includesNetworkError: false,
+            includesException: true,
             contracts: null,
             tags: null,
             schemaMetadata: null,
             metadataAttributes: null,
           } satisfies CompositionResponse;
         }
+
         throw error;
       }
 
@@ -205,4 +208,5 @@ export type CompositionResponse = {
   metadataAttributes: Record<string, string[]> | null;
   tags: Array<string> | null;
   includesNetworkError?: boolean;
+  includesException?: boolean;
 };
diff --git a/packages/services/schema/src/composition-worker.ts b/packages/services/schema/src/composition-worker.ts
index 415a60b719b..503cb5aba74 100644
--- a/packages/services/schema/src/composition-worker.ts
+++ b/packages/services/schema/src/composition-worker.ts
@@ -40,7 +40,7 @@ export function createCompositionWorker(args: {
           const composeFederation = createComposeFederation({
             decrypt,
             logger: baseLogger.child({ reqId: message.data.args.requestId }) as FastifyBaseLogger,
-            requestTimeoutMs: message.data.requestTimeoutMs,
+            requestTimeoutMs: args.env.timings.schemaExternalCompositionTimeout,
           });
           const composed = await composeFederation(message.data.args);
 
@@ -128,7 +128,6 @@ export type CompositionEvent = {
     | {
         type: 'federation';
         args: ComposeFederationArgs;
-        requestTimeoutMs: number;
       }
     | {
         type: 'single';
diff --git a/packages/services/schema/src/environment.ts b/packages/services/schema/src/environment.ts
index 87f78663006..dd55a9109be 100644
--- a/packages/services/schema/src/environment.ts
+++ b/packages/services/schema/src/environment.ts
@@ -43,7 +43,8 @@ const RequestBrokerModel = zod.union([
 const TimingsModel = zod.object({
   SCHEMA_CACHE_TTL_MS: NumberFromString().default(30000),
   SCHEMA_CACHE_SUCCESS_TTL_MS: zod.optional(NumberFromString()),
-  SCHEMA_COMPOSITION_TIMEOUT_MS: NumberFromString(25000).default(25000),
+  SCHEMA_COMPOSITION_TIMEOUT_MS: NumberFromString(30_000).default(30_000),
+  SCHEMA_EXTERNAL_COMPOSITION_TIMEOUT_MS: NumberFromString(9_000).default(9_000),
   SCHEMA_CACHE_POLL_INTERVAL_MS: NumberFromString(100).default(150),
 });
 
@@ -175,7 +176,13 @@ export const env = {
       timings.SCHEMA_CACHE_SUCCESS_TTL_MS ||
       timings.SCHEMA_CACHE_TTL_MS /* Fallback to cacheTTL if not set */,
     cachePollInterval: timings.SCHEMA_CACHE_POLL_INTERVAL_MS,
+    /** timeout of the composition in worker */
     schemaCompositionTimeout: timings.SCHEMA_COMPOSITION_TIMEOUT_MS,
+    /**
+     * Timeout of calls to the external composition endpoint within worker
+     * NOTE: This value should always be lower than the schemaCompositionTimeout
+     */
+    schemaExternalCompositionTimeout: timings.SCHEMA_EXTERNAL_COMPOSITION_TIMEOUT_MS,
   },
   requestBroker:
     requestBroker.REQUEST_BROKER === '1'
diff --git a/packages/services/schema/src/lib/compose.ts b/packages/services/schema/src/lib/compose.ts
index 1d5e410da63..489fc9f17e9 100644
--- a/packages/services/schema/src/lib/compose.ts
+++ b/packages/services/schema/src/lib/compose.ts
@@ -25,14 +25,13 @@ interface BrokerPayload {
   body: string;
 }
 
-const EXTERNAL_COMPOSITION_RESULT = z.union([
+const ExternalCompositionResultModel = z.union([
   z.object({
     type: z.literal('success'),
     result: z.object({
       supergraph: z.string(),
       sdl: z.string(),
     }),
-    includesNetworkError: z.boolean().default(false),
   }),
   z.object({
     type: z.literal('failure'),
@@ -46,11 +45,15 @@ const EXTERNAL_COMPOSITION_RESULT = z.union([
         }),
       ),
     }),
-    includesNetworkError: z.boolean().default(false),
   }),
 ]);
 
-export type ComposerMethodResult = z.TypeOf<typeof EXTERNAL_COMPOSITION_RESULT>;
+export type ComposerMethodResult = z.TypeOf<typeof ExternalCompositionResultModel> & {
+  /** The result contains a network error */
+  includesNetworkError: boolean;
+  /** The result contains an internal unexpected exception */
+  includesException: boolean;
+};
 
 export function composeFederationV1(
   subgraphs: Array<{
@@ -69,6 +72,7 @@ export function composeFederationV1(
         sdl: result.schema ? printSchema(result.schema) : undefined,
       },
       includesNetworkError: false,
+      includesException: false,
     };
   }
 
@@ -79,6 +83,7 @@ export function composeFederationV1(
       sdl: printSchema(result.schema),
     },
     includesNetworkError: false,
+    includesException: false,
   };
 }
 
@@ -94,10 +99,15 @@ export function composeFederationV2(
 ): ComposerMethodResult & {
   includesException?: boolean;
 } {
+  logger?.debug(
+    'Compose subgraphs using native federation v2. (subgraphs=%o)',
+    subgraphs.map(s => s.name),
+  );
   try {
     const result = nativeComposeServices(subgraphs);
 
     if (nativeCompositionHasErrors(result)) {
+      logger?.debug('Native composition failed with composition errors.');
       return {
         type: 'failure',
         result: {
@@ -105,9 +115,12 @@ export function composeFederationV2(
           sdl: undefined,
         },
         includesNetworkError: false,
+        includesException: false,
       } as const;
     }
 
+    logger?.debug('Native composition succeeded.');
+
     return {
       type: 'success',
       result: {
@@ -115,8 +128,10 @@ export function composeFederationV2(
         sdl: print(transformSupergraphToPublicSchema(parse(result.supergraphSdl))),
       },
       includesNetworkError: false,
+      includesException: false,
     } as const;
   } catch (error) {
+    logger?.error('Unexpected error during composition.');
     logger?.error(error);
     Sentry.captureException(error);
 
@@ -191,7 +206,12 @@ export async function composeExternalFederation(args: {
     : callExternalService(request, args.logger, args.requestTimeoutMs));
 
   args.logger?.debug('Got response from external composition service, trying to safe parse');
-  const parseResult = EXTERNAL_COMPOSITION_RESULT.safeParse(externalResponse);
+
+  if (externalResponse.type === 'error') {
+    return externalResponse.data;
+  }
+
+  const parseResult = ExternalCompositionResultModel.safeParse(externalResponse.data);
 
   if (!parseResult.success) {
     args.logger?.error(
@@ -205,12 +225,13 @@ export async function composeExternalFederation(args: {
         sdl: null,
         errors: [
           {
-            message: 'External composition failure: invalid shape of data',
+            message: 'External composition failure: invalid shape of data returned from service',
             source: 'composition',
           },
         ],
       },
       includesNetworkError: false,
+      includesException: true,
     };
   }
 
@@ -226,10 +247,15 @@ export async function composeExternalFederation(args: {
         sdl: print(transformSupergraphToPublicSchema(parse(parseResult.data.result.supergraph))),
       },
       includesNetworkError: false,
+      includesException: false,
     };
   }
 
-  return parseResult.data;
+  return {
+    ...parseResult.data,
+    includesNetworkError: false,
+    includesException: false,
+  };
 }
 
 async function checkExternalCompositionCompatibility(
@@ -300,7 +326,12 @@ async function callExternalService(
   input: { url: string; headers: Record<string, string>; body: string },
   logger: ServiceLogger | undefined,
   timeoutMs: number,
-) {
+): Promise<
+  /** external service got called and response body was delivered */
+  | { type: 'success'; data: unknown }
+  /** calling external service failed; return an error */
+  | { type: 'error'; data: Extract<ComposerMethodResult, { type: 'failure' }> }
+> {
   const tracer = trace.getTracer('external-composition');
   const parsedUrl = new URL(input.url);
   const span = tracer.startSpan('External Composition', {
@@ -315,24 +346,25 @@ async function callExternalService(
   });
 
   try {
-    logger?.debug('Calling external composition service (url=%s)', input.url);
+    logger?.debug(
+      'Calling external composition service (url=%s, timeout=%s)',
+      input.url,
+      timeoutMs,
+    );
+
     const response = await got(input.url, {
       method: 'POST',
       headers: input.headers,
       body: input.body,
       responseType: 'text',
       retry: {
-        limit: 5,
+        limit: 3,
         methods: ['POST', ...(got.defaults.options.retry.methods ?? [])],
         statusCodes: [404].concat(got.defaults.options.retry.statusCodes ?? []),
         backoffLimit: 500,
       },
       timeout: {
         request: timeoutMs,
-        // connecting should be quick
-        lookup: 10_000,
-        connect: 10_000,
-        secureConnect: 10_000,
       },
     });
 
@@ -340,32 +372,48 @@ async function callExternalService(
     span.setAttribute('http.response.status_code', response.statusCode);
     span.end();
 
-    return JSON.parse(response.body) as unknown;
+    return { type: 'success', data: JSON.parse(response.body) as unknown };
   } catch (error) {
     if (error instanceof RequestError) {
       if (!error.response) {
-        span.setAttribute('error.message', error.message);
+        let message = error.message;
+
+        if (!message && error.cause instanceof AggregateError) {
+          message = error.cause.message;
+
+          if (!message) {
+            message = error.cause.errors[0].message;
+          }
+        }
+
+        span.setAttribute('error.code', error.code);
         span.setAttribute('error.type', error.name);
+        span.setAttribute('error.message', message);
 
         logger?.error(
-          'Network error during external composition without response. (errorName=%s, errorMessage=%s)',
+          'Network error during external composition without response. (errorName=%s, code=%s, message=%s)',
           error.name,
-          error.message,
+          error.code,
+          message,
         );
 
         return {
-          type: 'failure',
-          result: {
-            sdl: null,
-            supergraph: null,
-            errors: [
-              {
-                message: `A network error occurred during external composition: "${error.message}"`,
-                source: 'graphql',
-              },
-            ],
+          type: 'error',
+          data: {
+            type: 'failure',
+            result: {
+              sdl: null,
+              supergraph: null,
+              errors: [
+                {
+                  message: `A network error occurred during external composition: "${error.code} ${message}"`,
+                  source: 'composition',
+                },
+              ],
+            },
+            includesNetworkError: true,
+            includesException: false,
           },
-          includesNetworkError: true,
         };
       }
 
@@ -382,16 +430,20 @@ async function callExternalService(
 
           if (translatedMessage) {
             return {
-              type: 'failure',
-              result: {
-                errors: [
-                  {
-                    message: `External composition failure: ${translatedMessage}`,
-                    source: 'graphql',
-                  },
-                ],
+              type: 'error',
+              data: {
+                type: 'failure',
+                result: {
+                  errors: [
+                    {
+                      message: `External composition failure: ${translatedMessage}`,
+                      source: 'composition',
+                    },
+                  ],
+                },
+                includesNetworkError: true,
+                includesException: false,
               },
-              includesNetworkError: true,
             };
           }
         }
@@ -409,16 +461,20 @@ async function callExternalService(
         span.setAttribute('error.type', error.name);
 
         return {
-          type: 'failure',
-          result: {
-            errors: [
-              {
-                message: `External composition network failure: ${error.message}`,
-                source: 'graphql',
-              },
-            ],
+          type: 'error',
+          data: {
+            type: 'failure',
+            result: {
+              errors: [
+                {
+                  message: `External composition network failure: ${error.message}`,
+                  source: 'composition',
+                },
+              ],
+            },
+            includesNetworkError: true,
+            includesException: false,
           },
-          includesNetworkError: true,
         };
       }
     }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d26622d8eb4..9c150c1091e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -321,8 +321,8 @@ importers:
         specifier: workspace:*
         version: link:../packages/services/storage
       '@theguild/federation-composition':
-        specifier: 0.18.1
-        version: 0.18.1(graphql@16.9.0)
+        specifier: 0.18.3
+        version: 0.18.3(graphql@16.9.0)
       '@trpc/client':
         specifier: 10.45.2
         version: 10.45.2(@trpc/server@10.45.2)
@@ -452,8 +452,8 @@ importers:
         specifier: 4.2.13
         version: 4.2.13
       '@theguild/federation-composition':
-        specifier: 0.18.1
-        version: 0.18.1(graphql@16.9.0)
+        specifier: 0.18.3
+        version: 0.18.3(graphql@16.9.0)
       colors:
         specifier: 1.4.0
         version: 1.4.0
@@ -757,8 +757,8 @@ importers:
         specifier: 7.8.0
         version: 7.8.0
       '@theguild/federation-composition':
-        specifier: 0.18.1
-        version: 0.18.1(graphql@16.9.0)
+        specifier: 0.18.3
+        version: 0.18.3(graphql@16.9.0)
       '@trpc/client':
         specifier: 10.45.2
         version: 10.45.2(@trpc/server@10.45.2)
@@ -1154,8 +1154,8 @@ importers:
         specifier: 7.120.2
         version: 7.120.2
       '@theguild/federation-composition':
-        specifier: 0.18.1
-        version: 0.18.1(graphql@16.9.0)
+        specifier: 0.18.3
+        version: 0.18.3(graphql@16.9.0)
       '@trpc/server':
         specifier: 10.45.2
         version: 10.45.2
@@ -3557,6 +3557,7 @@ packages:
 
   '@fastify/vite@6.0.7':
     resolution: {integrity: sha512-+dRo9KUkvmbqdmBskG02SwigWl06Mwkw8SBDK1zTNH6vd4DyXbRvI7RmJEmBkLouSU81KTzy1+OzwHSffqSD6w==}
+    bundledDependencies: []
 
   '@floating-ui/core@1.2.6':
     resolution: {integrity: sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg==}
@@ -7602,8 +7603,8 @@ packages:
       eslint: ^8 || ^9
       typescript: ^5
 
-  '@theguild/federation-composition@0.18.1':
-    resolution: {integrity: sha512-qEEWJpFiQ5FTeEeTPmFIEG6mHr3KHB8sXnJTphDo1FGCEMSSz15ifVEJF3skECJHN8HZThXxp8dnVp97zjzWpg==}
+  '@theguild/federation-composition@0.18.3':
+    resolution: {integrity: sha512-UGa3R3JMDHzoKi6k32kq/bB8SbZrkkxLrfinuEPcsv0sU8r1CW+4jwCBbQGC5vnoU9asg+MuCjMYpLZSOJS/dw==}
     engines: {node: '>=18'}
     peerDependencies:
       graphql: ^16.0.0
@@ -16489,8 +16490,8 @@ snapshots:
     dependencies:
       '@aws-crypto/sha256-browser': 3.0.0
       '@aws-crypto/sha256-js': 3.0.0
-      '@aws-sdk/client-sso-oidc': 3.596.0
-      '@aws-sdk/client-sts': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)
+      '@aws-sdk/client-sso-oidc': 3.596.0(@aws-sdk/client-sts@3.596.0)
+      '@aws-sdk/client-sts': 3.596.0
       '@aws-sdk/core': 3.592.0
       '@aws-sdk/credential-provider-node': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)(@aws-sdk/client-sts@3.596.0)
       '@aws-sdk/middleware-host-header': 3.577.0
@@ -16597,11 +16598,11 @@ snapshots:
     transitivePeerDependencies:
       - aws-crt
 
-  '@aws-sdk/client-sso-oidc@3.596.0':
+  '@aws-sdk/client-sso-oidc@3.596.0(@aws-sdk/client-sts@3.596.0)':
     dependencies:
       '@aws-crypto/sha256-browser': 3.0.0
       '@aws-crypto/sha256-js': 3.0.0
-      '@aws-sdk/client-sts': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)
+      '@aws-sdk/client-sts': 3.596.0
       '@aws-sdk/core': 3.592.0
       '@aws-sdk/credential-provider-node': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)(@aws-sdk/client-sts@3.596.0)
       '@aws-sdk/middleware-host-header': 3.577.0
@@ -16640,6 +16641,7 @@ snapshots:
       '@smithy/util-utf8': 3.0.0
       tslib: 2.8.1
     transitivePeerDependencies:
+      - '@aws-sdk/client-sts'
       - aws-crt
 
   '@aws-sdk/client-sso-oidc@3.723.0(@aws-sdk/client-sts@3.723.0)':
@@ -16773,11 +16775,11 @@ snapshots:
     transitivePeerDependencies:
       - aws-crt
 
-  '@aws-sdk/client-sts@3.596.0(@aws-sdk/client-sso-oidc@3.596.0)':
+  '@aws-sdk/client-sts@3.596.0':
     dependencies:
       '@aws-crypto/sha256-browser': 3.0.0
       '@aws-crypto/sha256-js': 3.0.0
-      '@aws-sdk/client-sso-oidc': 3.596.0
+      '@aws-sdk/client-sso-oidc': 3.596.0(@aws-sdk/client-sts@3.596.0)
       '@aws-sdk/core': 3.592.0
       '@aws-sdk/credential-provider-node': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)(@aws-sdk/client-sts@3.596.0)
       '@aws-sdk/middleware-host-header': 3.577.0
@@ -16816,7 +16818,6 @@ snapshots:
       '@smithy/util-utf8': 3.0.0
       tslib: 2.8.1
     transitivePeerDependencies:
-      - '@aws-sdk/client-sso-oidc'
       - aws-crt
 
   '@aws-sdk/client-sts@3.723.0':
@@ -16930,7 +16931,7 @@ snapshots:
 
   '@aws-sdk/credential-provider-ini@3.596.0(@aws-sdk/client-sso-oidc@3.596.0)(@aws-sdk/client-sts@3.596.0)':
     dependencies:
-      '@aws-sdk/client-sts': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)
+      '@aws-sdk/client-sts': 3.596.0
       '@aws-sdk/credential-provider-env': 3.587.0
       '@aws-sdk/credential-provider-http': 3.596.0
       '@aws-sdk/credential-provider-process': 3.587.0
@@ -17049,7 +17050,7 @@ snapshots:
 
   '@aws-sdk/credential-provider-web-identity@3.587.0(@aws-sdk/client-sts@3.596.0)':
     dependencies:
-      '@aws-sdk/client-sts': 3.596.0(@aws-sdk/client-sso-oidc@3.596.0)
+      '@aws-sdk/client-sts': 3.596.0
       '@aws-sdk/types': 3.577.0
       '@smithy/property-provider': 3.1.11
       '@smithy/types': 3.7.2
@@ -17224,7 +17225,7 @@ snapshots:
 
   '@aws-sdk/token-providers@3.587.0(@aws-sdk/client-sso-oidc@3.596.0)':
     dependencies:
-      '@aws-sdk/client-sso-oidc': 3.596.0
+      '@aws-sdk/client-sso-oidc': 3.596.0(@aws-sdk/client-sts@3.596.0)
       '@aws-sdk/types': 3.577.0
       '@smithy/property-provider': 3.1.11
       '@smithy/shared-ini-file-loader': 3.1.12
@@ -24218,7 +24219,7 @@ snapshots:
       - eslint-import-resolver-webpack
       - supports-color
 
-  '@theguild/federation-composition@0.18.1(graphql@16.9.0)':
+  '@theguild/federation-composition@0.18.3(graphql@16.9.0)':
     dependencies:
       constant-case: 3.0.4
       debug: 4.4.0(supports-color@8.1.1)


Reviews:
user: login: n1ru4l
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T10:29:17Z
user: login: n1ru4l
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T10:29:59Z
user: login: n1ru4l
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T10:30:47Z
user: login: n1ru4l
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T10:30:51Z
user: login: n1ru4l
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T10:34:44Z
user: login: n1ru4l
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T10:38:53Z
user: login: kamilkisiela
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T10:40:03Z
user: login: n1ru4l
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T10:40:04Z
user: login: kamilkisiela
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T15:01:42Z
user: login: kamilkisiela
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-13T15:01:55Z
user: login: jdolle
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-13T15:35:19Z
user: login: jdolle
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-13T15:37:19Z
user: login: n1ru4l
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T07:11:43Z

Review comments:
diff_hunk: @@ -100,7 +100,6 @@ export const schemaBuilderApiRouter = t.router({
                 data: {
                   type: 'federation',
                   args,
-                  requestTimeoutMs: ctx.cache.timeoutMs,
path: packages/services/schema/src/api.ts
user: login: n1ru4l
body: This is now defined via a environment variable
created_at: 2025-05-13T10:29:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -160,23 +159,25 @@ export const schemaBuilderApiRouter = t.router({
         assertAllCasesExhausted(input);
       } catch (error) {
         // Treat timeouts caused by external composition as "expected errors"
-        if (ctx.cache.isTimeoutError(error) && input.type === 'federation' && input.external) {
+        if (ctx.cache.isTimeoutError(error)) {
path: packages/services/schema/src/api.ts
user: login: n1ru4l
body: It does not make sense to only apply this for federation and external composition. If there is a timeout error it is an exception we pass on to the end user.
created_at: 2025-05-13T10:29:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -25,14 +25,13 @@ interface BrokerPayload {
   body: string;
 }
 
-const EXTERNAL_COMPOSITION_RESULT = z.union([
+const ExternalCompositionResultModel = z.union([
   z.object({
     type: z.literal('success'),
     result: z.object({
       supergraph: z.string(),
       sdl: z.string(),
     }),
-    includesNetworkError: z.boolean().default(false),
path: packages/services/schema/src/lib/compose.ts
user: login: n1ru4l
body: We never want to receive this from the external composition service. It is state that only exists on our end.
created_at: 2025-05-13T10:30:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -46,11 +45,15 @@ const EXTERNAL_COMPOSITION_RESULT = z.union([
         }),
       ),
     }),
-    includesNetworkError: z.boolean().default(false),
path: packages/services/schema/src/lib/compose.ts
user: login: n1ru4l
body: We never want to receive this from the external composition service. It is state that only exists on our end.
created_at: 2025-05-13T10:30:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -315,57 +338,78 @@ async function callExternalService(
   });
 
   try {
-    logger?.debug('Calling external composition service (url=%s)', input.url);
+    logger?.debug(
+      'Calling external composition service (url=%s, timeout=%s)',
+      input.url,
+      timeoutMs,
+    );
+
     const response = await got(input.url, {
       method: 'POST',
       headers: input.headers,
       body: input.body,
       responseType: 'text',
       retry: {
-        limit: 5,
+        limit: 3,
         methods: ['POST', ...(got.defaults.options.retry.methods ?? [])],
         statusCodes: [404].concat(got.defaults.options.retry.statusCodes ?? []),
         backoffLimit: 500,
       },
       timeout: {
         request: timeoutMs,
         // connecting should be quick
-        lookup: 10_000,
-        connect: 10_000,
-        secureConnect: 10_000,
+        lookup: 5_000,
+        connect: 5_000,
+        secureConnect: 5_000,
path: packages/services/schema/src/lib/compose.ts
user: login: n1ru4l
body: We have
`SCHEMA_COMPOSITION_TIMEOUT_MS` - Timeout for the whole composition process (main process kills worker)
`SCHEMA_EXTERNAL_COMPOSITION_TIMEOUT_MS` - Timeout for a external composition call

`SCHEMA_COMPOSITION_TIMEOUT_MS` must be higher than `SCHEMA_EXTERNAL_COMPOSITION_TIMEOUT_MS * EXTERNAL_COMPOSITION_RETRY_COUNT`.

```
EXTERNAL_COMPOSITION_RETRY_COUNT -> 9_000
EXTERNAL_COMPOSITION_RETRY_COUNT -> 3
TOTAL = 27_000
```

That means `SCHEMA_COMPOSITION_TIMEOUT_MS` set to `30_000` is okay. That way we get the actual HTTP/Socket error we can show to the end user (for their own external composition service). Instead of a generic "schema composition timed out" or "unexpected error".

created_at: 2025-05-13T10:34:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -160,23 +159,25 @@ export const schemaBuilderApiRouter = t.router({
         assertAllCasesExhausted(input);
       } catch (error) {
         // Treat timeouts caused by external composition as "expected errors"
-        if (ctx.cache.isTimeoutError(error) && input.type === 'federation' && input.external) {
+        if (ctx.cache.isTimeoutError(error)) {
           return {
             errors: [
               {
-                message: error.message,
-                source: 'graphql',
path: packages/services/schema/src/api.ts
user: login: n1ru4l
body: No idea why this was 'graphql' before. With the new guard for rejecting within `packages/services/api/src/modules/schema/providers/models/composite.ts` it makes more sense to have this be `composite`.
created_at: 2025-05-13T10:38:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -160,23 +159,25 @@ export const schemaBuilderApiRouter = t.router({
         assertAllCasesExhausted(input);
       } catch (error) {
         // Treat timeouts caused by external composition as "expected errors"
-        if (ctx.cache.isTimeoutError(error) && input.type === 'federation' && input.external) {
+        if (ctx.cache.isTimeoutError(error)) {
           return {
             errors: [
               {
-                message: error.message,
-                source: 'graphql',
+                message: 'The composition timed out. Please try again later.',
+                source: 'composition',
               },
             ],
             sdl: null,
             supergraph: null,
-            includesNetworkError: true,
+            includesNetworkError: false,
+            includesException: true,
path: packages/services/schema/src/api.ts
user: login: n1ru4l
body: This is not a network error but an exception because this basically means that we either
1. have an infinite loop in our composition logic
2. have external composition timeouts configured so they do not time out before external composition calls time out
created_at: 2025-05-13T10:40:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -117,7 +117,8 @@
       "cookie@<0.7.0": "0.7.2",
       "tar-fs": "2.1.2",
       "ip": "npm:neoip@2.1.0",
-      "miniflare@3>undici": "5.28.5"
+      "miniflare@3>undici": "5.28.5",
+      "@theguild/federation-composition": "0.18.3-alpha-20250513135301-b361eeefafe4ce751993d558e3fa77d6100efad2"
path: package.json
user: login: kamilkisiela
body: you know
created_at: 2025-05-13T15:01:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -315,57 +346,74 @@ async function callExternalService(
   });
 
   try {
-    logger?.debug('Calling external composition service (url=%s)', input.url);
+    logger?.debug(
+      'Calling external composition service (url=%s, timeout=%s)',
+      input.url,
+      timeoutMs,
+    );
+
     const response = await got(input.url, {
       method: 'POST',
       headers: input.headers,
       body: input.body,
       responseType: 'text',
       retry: {
-        limit: 5,
+        limit: 3,
         methods: ['POST', ...(got.defaults.options.retry.methods ?? [])],
         statusCodes: [404].concat(got.defaults.options.retry.statusCodes ?? []),
         backoffLimit: 500,
       },
       timeout: {
         request: timeoutMs,
-        // connecting should be quick
-        lookup: 10_000,
-        connect: 10_000,
-        secureConnect: 10_000,
       },
     });
 
     span.setAttribute('http.response.body.size', response.rawBody.length);
     span.setAttribute('http.response.status_code', response.statusCode);
     span.end();
 
-    return JSON.parse(response.body) as unknown;
+    return { type: 'success', data: JSON.parse(response.body) as unknown };
   } catch (error) {
     if (error instanceof RequestError) {
       if (!error.response) {
-        span.setAttribute('error.message', error.message);
+        let message = error.message;
+
+        if (!message && error.cause instanceof AggregateError) {
+          message = error.cause.message;
path: packages/services/schema/src/lib/compose.ts
user: login: jdolle
body: Should we collect the `error.errors` messages instead? For an aggregate error, there may be multiple messages that would be helpful.
created_at: 2025-05-13T15:35:18Z
author_association: COLLABORATOR
diff_hunk: @@ -315,57 +346,74 @@ async function callExternalService(
   });
 
   try {
-    logger?.debug('Calling external composition service (url=%s)', input.url);
+    logger?.debug(
+      'Calling external composition service (url=%s, timeout=%s)',
+      input.url,
+      timeoutMs,
+    );
+
     const response = await got(input.url, {
       method: 'POST',
       headers: input.headers,
       body: input.body,
       responseType: 'text',
       retry: {
-        limit: 5,
+        limit: 3,
         methods: ['POST', ...(got.defaults.options.retry.methods ?? [])],
         statusCodes: [404].concat(got.defaults.options.retry.statusCodes ?? []),
         backoffLimit: 500,
       },
       timeout: {
         request: timeoutMs,
-        // connecting should be quick
-        lookup: 10_000,
-        connect: 10_000,
-        secureConnect: 10_000,
       },
     });
 
     span.setAttribute('http.response.body.size', response.rawBody.length);
     span.setAttribute('http.response.status_code', response.statusCode);
     span.end();
 
-    return JSON.parse(response.body) as unknown;
+    return { type: 'success', data: JSON.parse(response.body) as unknown };
   } catch (error) {
     if (error instanceof RequestError) {
       if (!error.response) {
-        span.setAttribute('error.message', error.message);
+        let message = error.message;
+
+        if (!message && error.cause instanceof AggregateError) {
+          message = error.cause.message;
path: packages/services/schema/src/lib/compose.ts
user: login: n1ru4l
body: Usually the error messages are the same for each attempt. I already have some notes to further improve the whole flow. We can do this in a follow up pr.
created_at: 2025-05-14T07:11:43Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-05-13T10:26:52Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: skip review by coderabbit.ai -->

> [!IMPORTANT]
> ## Review skipped
> 
> Auto reviews are disabled on this repository.
> 
> Please check the settings in the CodeRabbit UI or the `.coderabbit.yaml` file in this repository. To trigger a single review, invoke the `@coderabbitai review` command.
> 
> You can disable this status message by setting the `reviews.review_status` to `false` in the CodeRabbit configuration file.

<!-- end of auto-generated comment: skip review by coderabbit.ai -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=graphql-hive/console&utm_content=6779):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `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 explain this code block.`
  -	`@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 explain its main purpose.`
  - `@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.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

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 generate sequence diagram` to generate a sequence diagram of the changes in this 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](https://docs.coderabbit.ai/guides/configure-coderabbit) 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](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-13T10:30:34Z
body: <!--- storybook-deployment --->
### 📚 Storybook Deployment

The latest changes are available as preview in: [https://pr-6779.hive-storybook.pages.dev](https://pr-6779.hive-storybook.pages.dev)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-13T10:30:36Z
body: <!-- changesetsSnapshotPrCommentKey -->
### 🚀 Snapshot Release (`alpha`)

The latest changes of this PR are available as `alpha` on npm (based on the declared `changesets`):
| Package | Version | Info |
|------|---------|----|
| `@graphql-hive/apollo` | `0.39.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3` | [npm ↗︎](https://www.npmjs.com/package/@graphql-hive/apollo/v/0.39.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3) [unpkg ↗︎](https://unpkg.com/browse/@graphql-hive/apollo@0.39.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3/) |
| `@graphql-hive/cli` | `0.50.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3` | [npm ↗︎](https://www.npmjs.com/package/@graphql-hive/cli/v/0.50.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3) [unpkg ↗︎](https://unpkg.com/browse/@graphql-hive/cli@0.50.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3/) |
| `@graphql-hive/core` | `0.11.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3` | [npm ↗︎](https://www.npmjs.com/package/@graphql-hive/core/v/0.11.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3) [unpkg ↗︎](https://unpkg.com/browse/@graphql-hive/core@0.11.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3/) |
| `@graphql-hive/envelop` | `0.36.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3` | [npm ↗︎](https://www.npmjs.com/package/@graphql-hive/envelop/v/0.36.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3) [unpkg ↗︎](https://unpkg.com/browse/@graphql-hive/envelop@0.36.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3/) |
| `@graphql-hive/yoga` | `0.42.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3` | [npm ↗︎](https://www.npmjs.com/package/@graphql-hive/yoga/v/0.42.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3) [unpkg ↗︎](https://unpkg.com/browse/@graphql-hive/yoga@0.42.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3/) |
| `hive` | `7.0.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3` | [npm ↗︎](https://www.npmjs.com/package/hive/v/7.0.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3) [unpkg ↗︎](https://unpkg.com/browse/hive@7.0.0-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3/) |
| `hive-apollo-router-plugin` | `2.1.1-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3` | [npm ↗︎](https://www.npmjs.com/package/hive-apollo-router-plugin/v/2.1.1-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3) [unpkg ↗︎](https://unpkg.com/browse/hive-apollo-router-plugin@2.1.1-alpha-20250513153326-559fabbde4352bf00527ef49601530b08b4241a3/) |
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-13T10:33:19Z
body: <!--- website_deployment --->
### 💻 Website Preview

The latest changes are available as preview in: [https://pr-6779.hive-landing-page.pages.dev](https://pr-6779.hive-landing-page.pages.dev)
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-13T10:38:29Z
body: 🐋 This PR was built and pushed to the following [Docker images](https://github.com/graphql-hive?ecosystem=container&tab=packages&visibility=public):

**Targets**: `build`

**Platforms**: `linux/amd64`

**Image Tag**: `559fabbde4352bf00527ef49601530b08b4241a3`

<details>
  <summary>Docker Bake metadata</summary>
  
  ```json
  {
  "app": {
    "buildx.build.provenance": {
      "buildType": "https://mobyproject.org/buildkit@v1",
      "materials": [
        {
          "uri": "pkg:docker/node@22.13.0-slim?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": "559fabbde4352bf00527ef49601530b08b4241a3",
            "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": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:dockerfile": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:shared": "b5rih7mflua9pqi0xkfndudn4"
          },
          "locals": [
            {
              "name": "context"
            },
            {
              "name": "dist"
            },
            {
              "name": "dockerfile"
            },
            {
              "name": "shared"
            }
          ]
        },
        "environment": {
          "platform": "linux/amd64"
        }
      }
    },
    "buildx.build.ref": "builder-853c2f66-76b4-4a18-a017-81144649f699/builder-853c2f66-76b4-4a18-a017-81144649f6990/xuiiyxbzl0tdlgskdcad71gfy",
    "containerimage.config.digest": "sha256:8694e14904326f2c3e575b118c16baa5dd6e3ea5a24ca3e49cf439aeeb47ceae",
    "containerimage.descriptor": {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "digest": "sha256:2c4bfdb01aa52c2ee7cb2f2060a17aeb24fd8c66e611db719c2eb3fa28041e8a",
      "size": 2075,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    },
    "containerimage.digest": "sha256:2c4bfdb01aa52c2ee7cb2f2060a17aeb24fd8c66e611db719c2eb3fa28041e8a",
    "image.name": "ghcr.io/graphql-hive/app:559fabbde4352bf00527ef49601530b08b4241a3-amd64,ghcr.io/graphql-hive/app:fix_external_composition_timeouts-amd64"
  },
  "buildx.build.warnings": [
    {
      "vertex": "sha256:064b3fd71482370072bd9d0797aab759f402c95a4c6ac215149c3b6334ac63da",
      "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
          }
        }
      ]
    },
    {
      "vertex": "sha256:064b3fd71482370072bd9d0797aab759f402c95a4c6ac215149c3b6334ac63da",
      "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:064b3fd71482370072bd9d0797aab759f402c95a4c6ac215149c3b6334ac63da",
      "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:7f8cbb6e85ac41d1ab38a211c5e01e43ebf30b43cb9fd90a5f053ebd4eb29421",
      "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:064b3fd71482370072bd9d0797aab759f402c95a4c6ac215149c3b6334ac63da",
      "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:7f8cbb6e85ac41d1ab38a211c5e01e43ebf30b43cb9fd90a5f053ebd4eb29421",
      "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:064b3fd71482370072bd9d0797aab759f402c95a4c6ac215149c3b6334ac63da",
      "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:7f8cbb6e85ac41d1ab38a211c5e01e43ebf30b43cb9fd90a5f053ebd4eb29421",
      "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:7f8cbb6e85ac41d1ab38a211c5e01e43ebf30b43cb9fd90a5f053ebd4eb29421",
      "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:7f8cbb6e85ac41d1ab38a211c5e01e43ebf30b43cb9fd90a5f053ebd4eb29421",
      "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:064b3fd71482370072bd9d0797aab759f402c95a4c6ac215149c3b6334ac63da",
      "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:064b3fd71482370072bd9d0797aab759f402c95a4c6ac215149c3b6334ac63da",
      "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:064b3fd71482370072bd9d0797aab759f402c95a4c6ac215149c3b6334ac63da",
      "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:7f8cbb6e85ac41d1ab38a211c5e01e43ebf30b43cb9fd90a5f053ebd4eb29421",
      "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
          }
        }
      ]
    }
  ],
  "commerce": {
    "buildx.build.provenance": {
      "buildType": "https://mobyproject.org/buildkit@v1",
      "materials": [
        {
          "uri": "pkg:docker/node@22.13.0-slim?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 commerce service of the GraphQL Hive project.",
            "build-arg:IMAGE_TITLE": "graphql-hive/commerce",
            "build-arg:PORT": "3010",
            "build-arg:RELEASE": "559fabbde4352bf00527ef49601530b08b4241a3",
            "build-arg:SERVICE_DIR_NAME": "@hive/commerce",
            "context:dist": "local:dist",
            "context:shared": "local:shared",
            "frontend.caps": "moby.buildkit.frontend.contexts+forward",
            "local-sessionid:context": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:dockerfile": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:shared": "b5rih7mflua9pqi0xkfndudn4"
          },
          "locals": [
            {
              "name": "context"
            },
            {
              "name": "dist"
            },
            {
              "name": "dockerfile"
            },
            {
              "name": "shared"
            }
          ]
        },
        "environment": {
          "platform": "linux/amd64"
        }
      }
    },
    "buildx.build.ref": "builder-853c2f66-76b4-4a18-a017-81144649f699/builder-853c2f66-76b4-4a18-a017-81144649f6990/ps3pzjzzepb36sonyiq8j10ew",
    "containerimage.config.digest": "sha256:c342ff1740cd7a2aa44665fe9be8a27b9bf978b858e9afe61522089427b72395",
    "containerimage.descriptor": {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "digest": "sha256:5eeef3efbd3d72a34c07cd3a6c664afe107ed0de496f9ca7e168903ed6e9055f",
      "size": 2075,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    },
    "containerimage.digest": "sha256:5eeef3efbd3d72a34c07cd3a6c664afe107ed0de496f9ca7e168903ed6e9055f",
    "image.name": "ghcr.io/graphql-hive/commerce:559fabbde4352bf00527ef49601530b08b4241a3-amd64,ghcr.io/graphql-hive/commerce:fix_external_composition_timeouts-amd64"
  },
  "composition-federation-2": {
    "buildx.build.provenance": {
      "buildType": "https://mobyproject.org/buildkit@v1",
      "materials": [
        {
          "uri": "pkg:docker/node@22.13.0-slim?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": "559fabbde4352bf00527ef49601530b08b4241a3",
            "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": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:dockerfile": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:shared": "b5rih7mflua9pqi0xkfndudn4"
          },
          "locals": [
            {
              "name": "context"
            },
            {
              "name": "dist"
            },
            {
              "name": "dockerfile"
            },
            {
              "name": "shared"
            }
          ]
        },
        "environment": {
          "platform": "linux/amd64"
        }
      }
    },
    "buildx.build.ref": "builder-853c2f66-76b4-4a18-a017-81144649f699/builder-853c2f66-76b4-4a18-a017-81144649f6990/z9lmcaf989byl2e99t4pwi44g",
    "containerimage.config.digest": "sha256:eae15119ea9ec51f983a0d39f8674ce3cd764f76e81ed5c66363d9520ed2e8fc",
    "containerimage.descriptor": {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "digest": "sha256:4cfcebf51c659c0cdea6997620a620915b90d34f3a6a30d8509cf477816f44ec",
      "size": 2075,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    },
    "containerimage.digest": "sha256:4cfcebf51c659c0cdea6997620a620915b90d34f3a6a30d8509cf477816f44ec",
    "image.name": "ghcr.io/graphql-hive/composition-federation-2:559fabbde4352bf00527ef49601530b08b4241a3-amd64,ghcr.io/graphql-hive/composition-federation-2:fix_external_composition_timeouts-amd64"
  },
  "emails": {
    "buildx.build.provenance": {
      "buildType": "https://mobyproject.org/buildkit@v1",
      "materials": [
        {
          "uri": "pkg:docker/node@22.13.0-slim?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": "559fabbde4352bf00527ef49601530b08b4241a3",
            "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": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:dockerfile": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:shared": "b5rih7mflua9pqi0xkfndudn4"
          },
          "locals": [
            {
              "name": "context"
            },
            {
              "name": "dist"
            },
            {
              "name": "dockerfile"
            },
            {
              "name": "shared"
            }
          ]
        },
        "environment": {
          "platform": "linux/amd64"
        }
      }
    },
    "buildx.build.ref": "builder-853c2f66-76b4-4a18-a017-81144649f699/builder-853c2f66-76b4-4a18-a017-81144649f6990/hiy1r9fo7igks9hko0rlolcgy",
    "containerimage.config.digest": "sha256:91d1cd368d36b717b3b4d7f305fcde01d3652910ba5c07cb1a73771a8abc5660",
    "containerimage.descriptor": {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "digest": "sha256:5e739a2ae055fcdf83eb22d86f0b975841e120b70237f28545b96deff0ba4e34",
      "size": 2075,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    },
    "containerimage.digest": "sha256:5e739a2ae055fcdf83eb22d86f0b975841e120b70237f28545b96deff0ba4e34",
    "image.name": "ghcr.io/graphql-hive/emails:559fabbde4352bf00527ef49601530b08b4241a3-amd64,ghcr.io/graphql-hive/emails:fix_external_composition_timeouts-amd64"
  },
  "policy": {
    "buildx.build.provenance": {
      "buildType": "https://mobyproject.org/buildkit@v1",
      "materials": [
        {
          "uri": "pkg:docker/node@22.13.0-slim?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": "559fabbde4352bf00527ef49601530b08b4241a3",
            "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": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:dockerfile": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:shared": "b5rih7mflua9pqi0xkfndudn4"
          },
          "locals": [
            {
              "name": "context"
            },
            {
              "name": "dist"
            },
            {
              "name": "dockerfile"
            },
            {
              "name": "shared"
            }
          ]
        },
        "environment": {
          "platform": "linux/amd64"
        }
      }
    },
    "buildx.build.ref": "builder-853c2f66-76b4-4a18-a017-81144649f699/builder-853c2f66-76b4-4a18-a017-81144649f6990/zc9yz4o9q0s8arzoonafeav2w",
    "containerimage.config.digest": "sha256:da276ce0aa900f898de9ed6e0e2a08a98a184aef15e26f553f8620f24c690625",
    "containerimage.descriptor": {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "digest": "sha256:e7b101303783d23c47c44f633053a0e4401fcf1685d77412da3537caeb7ca4da",
      "size": 2075,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    },
    "containerimage.digest": "sha256:e7b101303783d23c47c44f633053a0e4401fcf1685d77412da3537caeb7ca4da",
    "image.name": "ghcr.io/graphql-hive/policy:559fabbde4352bf00527ef49601530b08b4241a3-amd64,ghcr.io/graphql-hive/policy:fix_external_composition_timeouts-amd64"
  },
  "schema": {
    "buildx.build.provenance": {
      "buildType": "https://mobyproject.org/buildkit@v1",
      "materials": [
        {
          "uri": "pkg:docker/node@22.13.0-slim?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": "559fabbde4352bf00527ef49601530b08b4241a3",
            "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": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:dockerfile": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:shared": "b5rih7mflua9pqi0xkfndudn4"
          },
          "locals": [
            {
              "name": "context"
            },
            {
              "name": "dist"
            },
            {
              "name": "dockerfile"
            },
            {
              "name": "shared"
            }
          ]
        },
        "environment": {
          "platform": "linux/amd64"
        }
      }
    },
    "buildx.build.ref": "builder-853c2f66-76b4-4a18-a017-81144649f699/builder-853c2f66-76b4-4a18-a017-81144649f6990/x306g31qk062a0vc7333hwlpq",
    "containerimage.config.digest": "sha256:243523be7d277e7ea1cc981ea01619f6c7131bd70d55ba10d4b767e604d5b223",
    "containerimage.descriptor": {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "digest": "sha256:8dd24f6f0f9b75f6ce1d51d2d406078c84ae811f3936a63d2ce92503c35ab446",
      "size": 2075,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    },
    "containerimage.digest": "sha256:8dd24f6f0f9b75f6ce1d51d2d406078c84ae811f3936a63d2ce92503c35ab446",
    "image.name": "ghcr.io/graphql-hive/schema:559fabbde4352bf00527ef49601530b08b4241a3-amd64,ghcr.io/graphql-hive/schema:fix_external_composition_timeouts-amd64"
  },
  "server": {
    "buildx.build.provenance": {
      "buildType": "https://mobyproject.org/buildkit@v1",
      "materials": [
        {
          "uri": "pkg:docker/node@22.13.0-slim?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": "559fabbde4352bf00527ef49601530b08b4241a3",
            "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": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:dockerfile": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:shared": "b5rih7mflua9pqi0xkfndudn4"
          },
          "locals": [
            {
              "name": "context"
            },
            {
              "name": "dist"
            },
            {
              "name": "dockerfile"
            },
            {
              "name": "shared"
            }
          ]
        },
        "environment": {
          "platform": "linux/amd64"
        }
      }
    },
    "buildx.build.ref": "builder-853c2f66-76b4-4a18-a017-81144649f699/builder-853c2f66-76b4-4a18-a017-81144649f6990/59b2axwnvib2mve48fpr8kma5",
    "containerimage.config.digest": "sha256:32bc2e7ee6b055b9591aed2e52d41b2cd1be8335e1286f147bb779a518b0f406",
    "containerimage.descriptor": {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "digest": "sha256:343b17f09a398a59cfe000c01a836b3a4c0767f5c1b029d11a4e9d93d0a71eb9",
      "size": 2076,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    },
    "containerimage.digest": "sha256:343b17f09a398a59cfe000c01a836b3a4c0767f5c1b029d11a4e9d93d0a71eb9",
    "image.name": "ghcr.io/graphql-hive/server:559fabbde4352bf00527ef49601530b08b4241a3-amd64,ghcr.io/graphql-hive/server:fix_external_composition_timeouts-amd64"
  },
  "storage": {
    "buildx.build.provenance": {
      "buildType": "https://mobyproject.org/buildkit@v1",
      "materials": [
        {
          "uri": "pkg:docker/node@22.13.0-slim?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": "559fabbde4352bf00527ef49601530b08b4241a3",
            "context:dist": "local:dist",
            "context:shared": "local:shared",
            "frontend.caps": "moby.buildkit.frontend.contexts+forward",
            "local-sessionid:context": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:dockerfile": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:shared": "b5rih7mflua9pqi0xkfndudn4"
          },
          "locals": [
            {
              "name": "context"
            },
            {
              "name": "dist"
            },
            {
              "name": "dockerfile"
            },
            {
              "name": "shared"
            }
          ]
        },
        "environment": {
          "platform": "linux/amd64"
        }
      }
    },
    "buildx.build.ref": "builder-853c2f66-76b4-4a18-a017-81144649f699/builder-853c2f66-76b4-4a18-a017-81144649f6990/dtthvel2rid1dgcjf1s7ykk7z",
    "containerimage.config.digest": "sha256:43bef30cccd7aa8d1c8e4c79a369dc7234523c05b1297e7280721606f6c23be9",
    "containerimage.descriptor": {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "digest": "sha256:7a728bc1855b4f1aa520d50e8dd1fbe9f2f4f125c47ab0d5768a85449d21b448",
      "size": 2075,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    },
    "containerimage.digest": "sha256:7a728bc1855b4f1aa520d50e8dd1fbe9f2f4f125c47ab0d5768a85449d21b448",
    "image.name": "ghcr.io/graphql-hive/storage:559fabbde4352bf00527ef49601530b08b4241a3-amd64,ghcr.io/graphql-hive/storage:fix_external_composition_timeouts-amd64"
  },
  "tokens": {
    "buildx.build.provenance": {
      "buildType": "https://mobyproject.org/buildkit@v1",
      "materials": [
        {
          "uri": "pkg:docker/node@22.13.0-slim?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": "559fabbde4352bf00527ef49601530b08b4241a3",
            "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": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:dockerfile": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:shared": "b5rih7mflua9pqi0xkfndudn4"
          },
          "locals": [
            {
              "name": "context"
            },
            {
              "name": "dist"
            },
            {
              "name": "dockerfile"
            },
            {
              "name": "shared"
            }
          ]
        },
        "environment": {
          "platform": "linux/amd64"
        }
      }
    },
    "buildx.build.ref": "builder-853c2f66-76b4-4a18-a017-81144649f699/builder-853c2f66-76b4-4a18-a017-81144649f6990/i0u2iplera3zjivlaqhj72oax",
    "containerimage.config.digest": "sha256:c94721c07c14099793036d9e2a348d5e014a9e417588a6fbe8b58a1dc7c4b396",
    "containerimage.descriptor": {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "digest": "sha256:5d835a40a57d23ce29c61bb370e32e2b3c4d790cc2d4d3abc4cfbbdc08c3e8a2",
      "size": 2075,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    },
    "containerimage.digest": "sha256:5d835a40a57d23ce29c61bb370e32e2b3c4d790cc2d4d3abc4cfbbdc08c3e8a2",
    "image.name": "ghcr.io/graphql-hive/tokens:559fabbde4352bf00527ef49601530b08b4241a3-amd64,ghcr.io/graphql-hive/tokens:fix_external_composition_timeouts-amd64"
  },
  "usage": {
    "buildx.build.provenance": {
      "buildType": "https://mobyproject.org/buildkit@v1",
      "materials": [
        {
          "uri": "pkg:docker/node@22.13.0-slim?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": "559fabbde4352bf00527ef49601530b08b4241a3",
            "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": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:dockerfile": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:shared": "b5rih7mflua9pqi0xkfndudn4"
          },
          "locals": [
            {
              "name": "context"
            },
            {
              "name": "dist"
            },
            {
              "name": "dockerfile"
            },
            {
              "name": "shared"
            }
          ]
        },
        "environment": {
          "platform": "linux/amd64"
        }
      }
    },
    "buildx.build.ref": "builder-853c2f66-76b4-4a18-a017-81144649f699/builder-853c2f66-76b4-4a18-a017-81144649f6990/ucx9v80nstf9nbdn7yco9nilk",
    "containerimage.config.digest": "sha256:35475902efb68f7b6b9e918e5eff76175df4011bcca8508031d88a1992a7fa69",
    "containerimage.descriptor": {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "digest": "sha256:1048d2aae251cb3a76cedc332fc430d0d06334e28e2dca0809556942dc29e1a2",
      "size": 2075,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    },
    "containerimage.digest": "sha256:1048d2aae251cb3a76cedc332fc430d0d06334e28e2dca0809556942dc29e1a2",
    "image.name": "ghcr.io/graphql-hive/usage:559fabbde4352bf00527ef49601530b08b4241a3-amd64,ghcr.io/graphql-hive/usage:fix_external_composition_timeouts-amd64"
  },
  "usage-ingestor": {
    "buildx.build.provenance": {
      "buildType": "https://mobyproject.org/buildkit@v1",
      "materials": [
        {
          "uri": "pkg:docker/node@22.13.0-slim?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": "559fabbde4352bf00527ef49601530b08b4241a3",
            "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": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:dockerfile": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:shared": "b5rih7mflua9pqi0xkfndudn4"
          },
          "locals": [
            {
              "name": "context"
            },
            {
              "name": "dist"
            },
            {
              "name": "dockerfile"
            },
            {
              "name": "shared"
            }
          ]
        },
        "environment": {
          "platform": "linux/amd64"
        }
      }
    },
    "buildx.build.ref": "builder-853c2f66-76b4-4a18-a017-81144649f699/builder-853c2f66-76b4-4a18-a017-81144649f6990/n7ntee36cv5crnbb3amqx1vcf",
    "containerimage.config.digest": "sha256:4f14fa16f615e8b4736d80c4fe650138e9ec1ede1f119fd714a5061fed80c0b6",
    "containerimage.descriptor": {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "digest": "sha256:1e98d1bfb4aeaf5c15337500910216e337366830361a8be8a73e82979884ed59",
      "size": 2075,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    },
    "containerimage.digest": "sha256:1e98d1bfb4aeaf5c15337500910216e337366830361a8be8a73e82979884ed59",
    "image.name": "ghcr.io/graphql-hive/usage-ingestor:559fabbde4352bf00527ef49601530b08b4241a3-amd64,ghcr.io/graphql-hive/usage-ingestor:fix_external_composition_timeouts-amd64"
  },
  "webhooks": {
    "buildx.build.provenance": {
      "buildType": "https://mobyproject.org/buildkit@v1",
      "materials": [
        {
          "uri": "pkg:docker/node@22.13.0-slim?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": "559fabbde4352bf00527ef49601530b08b4241a3",
            "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": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:dockerfile": "b5rih7mflua9pqi0xkfndudn4",
            "local-sessionid:shared": "b5rih7mflua9pqi0xkfndudn4"
          },
          "locals": [
            {
              "name": "context"
            },
            {
              "name": "dist"
            },
            {
              "name": "dockerfile"
            },
            {
              "name": "shared"
            }
          ]
        },
        "environment": {
          "platform": "linux/amd64"
        }
      }
    },
    "buildx.build.ref": "builder-853c2f66-76b4-4a18-a017-81144649f699/builder-853c2f66-76b4-4a18-a017-81144649f6990/16f2en63h2spx1rjw90wxzngq",
    "containerimage.config.digest": "sha256:e3d9fe0b294ceaa7feacf86a5dc1d579d71aace67e8dd34030034a54853984c3",
    "containerimage.descriptor": {
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "digest": "sha256:5f31f4efbebbc34c363ccddf47d47999225b202e58f6dcbebdf4c65b73e0a6ec",
      "size": 2075,
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    },
    "containerimage.digest": "sha256:5f31f4efbebbc34c363ccddf47d47999225b202e58f6dcbebdf4c65b73e0a6ec",
    "image.name": "ghcr.io/graphql-hive/webhooks:559fabbde4352bf00527ef49601530b08b4241a3-amd64,ghcr.io/graphql-hive/webhooks:fix_external_composition_timeouts-amd64"
  }
}
  ```
</details>
<!-- Sticky Pull Request Commentpr -->
author_association: CONTRIBUTOR