function extractConnectionMetadata(
  fragment: Fragment,
): ReaderPaginationMetadata | void {
  const fields = [];
  let connectionField = null;
  let path = null;
  GraphQLIRVisitor.visit(fragment, {
    LinkedField: {
      enter(field) {
        fields.push(field);
        if (
          (field.handles &&
            field.handles.some(handle => handle.name === 'connection')) ||
          field.directives.some(directive => directive.name === 'connection')
        ) {
          // Disallow multiple @connections
          if (connectionField != null) {
            throw createUserError(
              `Invalid use of @refetchable with @connection in fragment '${
                fragment.name
              }', at most once @connection can appear in a refetchable fragment.`,
              [field.loc],
            );
          }
          // Disallow connections within plurals
          const pluralOnPath = fields.find(
            pathField => getNullableType(pathField.type) instanceof GraphQLList,
          );
          if (pluralOnPath) {
            throw createUserError(
              `Invalid use of @refetchable with @connection in fragment '${
                fragment.name
              }', refetchable connections cannot appear inside plural fields.`,
              [field.loc, pluralOnPath.loc],
            );
          }
          connectionField = field;
          path = fields.map(pathField => pathField.alias ?? pathField.name);
        }
      },
      leave() {
        fields.pop();
      },
    },
  });
  if (connectionField == null || path == null) {
    return;
  }
  // Validate arguments: if either of before/last appear they must both appear
  // and use variables (not scalar values)
  let backward = null;
  const before = findArgument(connectionField, 'before');
  const last = findArgument(connectionField, 'last');
  if (before || last) {
    if (
      !before ||
      !last ||
      before.value.kind !== 'Variable' ||
      last.value.kind !== 'Variable'
    ) {
      throw createUserError(
        `Invalid use of @refetchable with @connection in fragment '${
          fragment.name
        }', refetchable connections must use variables for the before and last arguments.`,
        [
          connectionField.loc,
          before && before.value.kind !== 'Variable' ? before.value.loc : null,
          last && last.value.kind !== 'Variable' ? last.value.loc : null,
        ].filter(Boolean),
      );
    }
    backward = {
      count: last.value.variableName,
      cursor: before.value.variableName,
    };
  }
  // Validate arguments: if either of after/first appear they must both appear
  // and use variables (not scalar values)
  let forward = null;
  const after = findArgument(connectionField, 'after');
  const first = findArgument(connectionField, 'first');
  if (after || first) {
    if (
      !after ||
      !first ||
      after.value.kind !== 'Variable' ||
      first.value.kind !== 'Variable'
    ) {
      throw createUserError(
        `Invalid use of @refetchable with @connection in fragment '${
          fragment.name
        }', refetchable connections must use variables for the after and first arguments.`,
        [
          connectionField.loc,
          after && after.value.kind !== 'Variable' ? after.value.loc : null,
          first && first.value.kind !== 'Variable' ? first.value.loc : null,
        ].filter(Boolean),
      );
    }
    forward = {
      count: first.value.variableName,
      cursor: after.value.variableName,
    };
  }
  return {forward, backward, path};
}
function extractConnectionMetadata(
  fragment: Fragment,
): ReaderPaginationMetadata | void {
  const fields = [];
  let connectionField = null;
  let path = null;
  GraphQLIRVisitor.visit(fragment, {
    LinkedField: {
      enter(field) {
        fields.push(field);
        if (
          (field.handles &&
            field.handles.some(handle => handle.name === 'connection')) ||
          field.directives.some(directive => directive.name === 'connection')
        ) {
          // Disallow multiple @connections
          if (connectionField != null) {
            throw createUserError(
              `Invalid use of @refetchable with @connection in fragment '${
                fragment.name
              }', at most once @connection can appear in a refetchable fragment.`,
              [field.loc],
            );
          }
          // Disallow connections within plurals
          const pluralOnPath = fields.find(
            pathField => getNullableType(pathField.type) instanceof GraphQLList,
          );
          if (pluralOnPath) {
            throw createUserError(
              `Invalid use of @refetchable with @connection in fragment '${
                fragment.name
              }', refetchable connections cannot appear inside plural fields.`,
              [field.loc, pluralOnPath.loc],
            );
          }
          connectionField = field;
          path = fields.map(pathField => pathField.alias ?? pathField.name);
        }
      },
      leave() {
        fields.pop();
      },
    },
  });
  if (connectionField == null || path == null) {
    return;
  }
  // Validate arguments: if either of before/last appear they must both appear
  // and use variables (not scalar values)
  let backward = null;
  const before = findArgument(connectionField, 'before');
  const last = findArgument(connectionField, 'last');
  if (before || last) {
    if (
      !before ||
      !last ||
      before.value.kind !== 'Variable' ||
      last.value.kind !== 'Variable'
    ) {
      throw createUserError(
        `Invalid use of @refetchable with @connection in fragment '${
          fragment.name
        }', refetchable connections must use variables for the before and last arguments.`,
        [
          connectionField.loc,
          before && before.value.kind !== 'Variable' ? before.value.loc : null,
          last && last.value.kind !== 'Variable' ? last.value.loc : null,
        ].filter(Boolean),
      );
    }
    backward = {
      count: last.value.variableName,
      cursor: before.value.variableName,
    };
  }
  // Validate arguments: if either of after/first appear they must both appear
  // and use variables (not scalar values)
  let forward = null;
  const after = findArgument(connectionField, 'after');
  const first = findArgument(connectionField, 'first');
  if (after || first) {
    if (
      !after ||
      !first ||
      after.value.kind !== 'Variable' ||
      first.value.kind !== 'Variable'
    ) {
      throw createUserError(
        `Invalid use of @refetchable with @connection in fragment '${
          fragment.name
        }', refetchable connections must use variables for the after and first arguments.`,
        [
          connectionField.loc,
          after && after.value.kind !== 'Variable' ? after.value.loc : null,
          first && first.value.kind !== 'Variable' ? first.value.loc : null,
        ].filter(Boolean),
      );
    }
    forward = {
      count: first.value.variableName,
      cursor: after.value.variableName,
    };
  }
  return {forward, backward, path};
}
function extractConnectionMetadata ( fragment : Fragment , ) : ReaderPaginationMetadata | void { const fields = [ ] ; let connectionField = null ; let path = null ; GraphQLIRVisitor . visit ( fragment , { LinkedField : { enter ( field ) { fields . push ( field ) ; if ( ( field . handles && field . handles . some ( handle => handle . name === 'connection' ) ) || field . directives . some ( directive => directive . name === 'connection' ) ) { // Disallow multiple @connections if ( connectionField != null ) { throw createUserError ( ` ${ fragment . name } ` , [ field . loc ] , ) ; } // Disallow connections within plurals const pluralOnPath = fields . find ( pathField => getNullableType ( pathField . type ) instanceof GraphQLList , ) ; if ( pluralOnPath ) { throw createUserError ( ` ${ fragment . name } ` , [ field . loc , pluralOnPath . loc ] , ) ; } connectionField = field ; path = fields . map ( pathField => pathField . alias ? ? pathField . name ) ; } } , leave ( ) { fields . pop ( ) ; } , } , } ) ; if ( connectionField == null || path == null ) { return ; } // Validate arguments: if either of before/last appear they must both appear // and use variables (not scalar values) let backward = null ; const before = findArgument ( connectionField , 'before' ) ; const last = findArgument ( connectionField , 'last' ) ; if ( before || last ) { if ( ! before || ! last || before . value . kind !== 'Variable' || last . value . kind !== 'Variable' ) { throw createUserError ( ` ${ fragment . name } ` , [ connectionField . loc , before && before . value . kind !== 'Variable' ? before . value . loc : null , last && last . value . kind !== 'Variable' ? last . value . loc : null , ] . filter ( Boolean ) , ) ; } backward = { count : last . value . variableName , cursor : before . value . variableName , } ; } // Validate arguments: if either of after/first appear they must both appear // and use variables (not scalar values) let forward = null ; const after = findArgument ( connectionField , 'after' ) ; const first = findArgument ( connectionField , 'first' ) ; if ( after || first ) { if ( ! after || ! first || after . value . kind !== 'Variable' || first . value . kind !== 'Variable' ) { throw createUserError ( ` ${ fragment . name } ` , [ connectionField . loc , after && after . value . kind !== 'Variable' ? after . value . loc : null , first && first . value . kind !== 'Variable' ? first . value . loc : null , ] . filter ( Boolean ) , ) ; } forward = { count : first . value . variableName , cursor : after . value . variableName , } ; } return { forward , backward , path } ; }
Validate that any @connection usage is valid for refetching:
- Variables are used for both the "count" and "cursor" arguments
(after/first or before/last)
- Exactly one connection
- Has a stable path to the connection data

Returns connection metadata to add to the transformed fragment or undefined
if there is no connection.
Validate that any @connection usage is valid for refetching : - Variables are used for both the count and cursor arguments ( after / first or before / last ) - Exactly one connection - Has a stable path to the connection data
Extract connection metadata from a fragment