import axios from 'axios';
import camelcaseKeys from 'camelcase-keys';
import { DateTime } from 'luxon';
import R from 'ramda';
import pino from 'pino';

/**
 * Union type representing the architecture defined in part of an OCI image's
 * manifest list.
 *
 * As specified in the Docker Manifest spec, any valid GOARCH values are valid
 * image architecture values, and vice versa:
 * > The platform object describes the platform which the image in the manifest
 * > runs on. A full list of valid operating system and architecture values are
 * > listed in the Go language documentation for $GOOS and $GOARCH
 * @see https://docs.docker.com/registry/spec/manifest-v2-2/#manifest-list-field-descriptions
 */
var Architecture;

(function (Architecture) {
  Architecture["i386"] = "386";
  Architecture["amd64"] = "amd64";
  Architecture["arm"] = "arm";
  Architecture["arm64"] = "arm64";
  Architecture["mips"] = "mips";
  Architecture["mips64"] = "mips64";
  Architecture["mips64le"] = "mips64le";
  Architecture["mipsle"] = "mipsle";
  Architecture["ppc64"] = "ppc64";
  Architecture["ppc64le"] = "ppc64le";
  Architecture["s390x"] = "s390x";
  Architecture["wasm"] = "wasm";
})(Architecture || (Architecture = {}));
/**
 * Union type representing the OS defined in part of an OCI image's
 * manifest list.
 * See the docs for the `Architecture` type above for more info.
 */


var OS;

(function (OS) {
  OS["aix"] = "aix";
  OS["android"] = "android";
  OS["darwin"] = "darwin";
  OS["dragonfly"] = "dragonfly";
  OS["freebsd"] = "freebsd";
  OS["illumos"] = "illumos";
  OS["js"] = "js";
  OS["linux"] = "linux";
  OS["netbsd"] = "netbsd";
  OS["openbsd"] = "openbsd";
  OS["plan9"] = "plan9";
  OS["solaris"] = "solaris";
  OS["windows"] = "windows";
})(OS || (OS = {}));

var ManifestMediaType;

(function (ManifestMediaType) {
  ManifestMediaType["Manifest"] = "application/vnd.docker.distribution.manifest.v2+json";
  ManifestMediaType["ManifestList"] = "application/vnd.docker.distribution.manifest.list.v2+json";
})(ManifestMediaType || (ManifestMediaType = {}));

var log = /*#__PURE__*/
pino({
  base: null,
  useLevelLabels: true
});

var DOCKER_HUB_API_ROOT = 'https://hub.docker.com/v2/';
var DOCKER_HUB_API_AUTH_URL = 'https://auth.docker.io/token';

/**
 * Currently only supports fetching the manifest for the `latest` tag; in
 * reality, we can pass any valid content digest[1] to retrieve the manifest(s)
 * for that image.
 *
 * [1]: https://github.com/opencontainers/distribution-spec/blob/master/spec.md#content-digests
 */

var createManifestListURL = function createManifestListURL(_ref) {
  var repo = _ref.repo;
  return "https://registry-1.docker.io/v2/" + repo.user + "/" + repo.name + "/manifests/latest";
};

var createUserReposListURL = function createUserReposListURL(user) {
  return DOCKER_HUB_API_ROOT + "repositories/" + user;
};
/**
 * The OCI distribution spec requires a unique token for each repo manifest queried.
 */


var fetchDockerHubToken = function fetchDockerHubToken(repo) {
  try {
    var name = repo.name,
        user = repo.user;
    return Promise.resolve(axios.get(DOCKER_HUB_API_AUTH_URL, {
      params: {
        scope: "repository:" + user + "/" + name + ":pull",
        service: 'registry.docker.io'
      }
    })).then(function (tokenRequest) {
      var token = R.path(['data', 'token'], tokenRequest);

      if (!token) {
        throw new Error('Unable to retrieve auth token from registry.');
      }

      return token;
    });
  } catch (e) {
    return Promise.reject(e);
  }
};
/**
 * Pure function that massages the Docker Hub API response into the
 * format we want to return. e.g., only extracting certain fields;
 * converting snake_case to camelCase, etc.
 */

var extractRepositoryDetails = function extractRepositoryDetails(repos, lastUpdatedSince) {
  if (!repos || R.isEmpty(repos)) {
    return [];
  }

  var parsedRepos = camelcaseKeys(repos);

  if (R.isNil(lastUpdatedSince)) {
    return parsedRepos;
  }

  return parsedRepos.filter(function (repo) {
    return DateTime.fromISO(repo.lastUpdated) < lastUpdatedSince;
  });
};
/**
 * Query a single repository given a repo name and username.
 *
 * @param user The DockerHub username or org name to query for.
 * @param name The DockerHub repo name -- restrict to this single repo.
 */

var queryRepo = function queryRepo(_ref2) {
  var name = _ref2.name,
      user = _ref2.user;

  try {
    return Promise.resolve(axios.request({
      url: DOCKER_HUB_API_ROOT + "repositories/" + user + "/" + name + "/"
    })).then(function (repoResult) {
      var repo = R.prop('data', repoResult);

      if (repoResult.status !== 200 || !repo || R.isEmpty(repo)) {
        return;
      }

      return camelcaseKeys(repo);
    });
  } catch (e) {
    return Promise.reject(e);
  }
};
/**
 * Top-level function for querying repositories.
 *
 * @TODO Rename to just `queryRepos`.
 *
 * @param user The DockerHub username or org name to query for.
 * @param numRepos The number of repos to query (max 100).
 * @param lastUpdatedSince Filter by the DateTime at which a repo was last updated.
 */

var queryTopRepos = function queryTopRepos(_ref3) {
  var lastUpdatedSince = _ref3.lastUpdatedSince,
      _ref3$numRepos = _ref3.numRepos,
      numRepos = _ref3$numRepos === void 0 ? 100 : _ref3$numRepos,
      user = _ref3.user;

  try {
    if (numRepos > 100) {
      throw new RangeError('Number of repos to query cannot exceed 100.');
    }

    var listReposURL = createUserReposListURL(user);
    return Promise.resolve(axios.get(listReposURL, {
      params: {
        page: 1,
        page_size: numRepos
      }
    })).then(function (repoResults) {
      var repos = R.path(['data', 'results'], repoResults);
      return extractRepositoryDetails(repos, lastUpdatedSince);
    });
  } catch (e) {
    return Promise.reject(e);
  }
};
/**
 * Query image tags.
 */

var queryTags = function queryTags(repo) {
  try {
    var repoUrl = createUserReposListURL(repo.user);
    var tagsUrl = repoUrl + "/" + repo.name + "/tags?page_size=100";
    return Promise.resolve(axios.get(tagsUrl)).then(function (tagsResults) {
      var tags = R.path(['data', 'results'], tagsResults);

      if (!tags || R.isEmpty(tags)) {
        return;
      } // @ts-ignore


      return camelcaseKeys(tags);
    });
  } catch (e) {
    return Promise.reject(e);
  }
};
/**
 * Queries the Docker Hub API to retrieve a "fat manifest", an object of
 * `Content-Type` `application/vnd.docker.distribution.manifest.list.v2+json/`.
 * Read up on the Manifest v2, Schema 2 Spec in more detail:
 * @see https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-2.md
 * Or the shiny new OCI distribution spec which builds on it:
 * @see https://github.com/opencontainers/distribution-spec/blob/f67bc11ba3a083a9c62f8fa53ad14c5bcf2116af/spec.md
 */

var fetchManifestList = function fetchManifestList(repo) {
  try {
    // Docker Hub requires a unique token for each repo manifest queried.
    return Promise.resolve(fetchDockerHubToken(repo)).then(function (token) {
      var manifestListURL = createManifestListURL({
        repo: repo
      });
      return Promise.resolve(axios.get(manifestListURL, {
        headers: {
          Accept: 'application/vnd.docker.distribution.manifest.list.v2+json',
          Authorization: "Bearer " + token
        }
      })).then(function (manifestListResponse) {
        // For now, just ignore legacy V1 schema manifests. They have an entirely
        // different response shape and it's not worth mucking up the schema to
        // support a legacy format.
        if (manifestListResponse.data.schemaVersion === 1) {
          log.info('Schema version 1 is unsupported.', repo.name);
          return;
        }

        return R.path(['data'], manifestListResponse);
      });
    });
  } catch (e) {
    return Promise.reject(e);
  }
};

export { Architecture, DOCKER_HUB_API_AUTH_URL, DOCKER_HUB_API_ROOT, ManifestMediaType, extractRepositoryDetails, fetchDockerHubToken, fetchManifestList, queryRepo, queryTags, queryTopRepos };
//# sourceMappingURL=docker-hub-utils.esm.js.map
