{"version":3,"file":"docker-hub-utils.esm.js","sources":["../src/types/DockerHubRepo.ts","../src/utils/log.ts","../src/utils/constants.ts","../src/services/DockerHubAPI.ts"],"sourcesContent":["/**\n * This is a direct representation of what we get back from the `/repositories`\n * API call.\n */\nexport interface DockerHubAPIRepo {\n readonly can_edit: boolean\n readonly description: string\n readonly is_automated: boolean\n readonly is_migrated: boolean\n readonly is_private: boolean\n readonly last_updated: string\n readonly name: string\n readonly namespace: string\n readonly pull_count: number\n readonly repository_type: string\n readonly star_count: number\n readonly status: number\n readonly user: string\n}\n\n/**\n * Union type representing the architecture defined in part of an OCI image's\n * manifest list.\n *\n * As specified in the Docker Manifest spec, any valid GOARCH values are valid\n * image architecture values, and vice versa:\n * > The platform object describes the platform which the image in the manifest\n * > runs on. A full list of valid operating system and architecture values are\n * > listed in the Go language documentation for $GOOS and $GOARCH\n * @see https://docs.docker.com/registry/spec/manifest-v2-2/#manifest-list-field-descriptions\n */\nexport enum Architecture {\n i386 = '386',\n amd64 = 'amd64',\n arm = 'arm',\n arm64 = 'arm64',\n mips = 'mips',\n mips64 = 'mips64',\n mips64le = 'mips64le',\n mipsle = 'mipsle',\n ppc64 = 'ppc64',\n ppc64le = 'ppc64le',\n s390x = 's390x',\n wasm = 'wasm',\n}\n\n/**\n * Union type representing the OS defined in part of an OCI image's\n * manifest list.\n * See the docs for the `Architecture` type above for more info.\n */\nexport enum OS {\n aix = 'aix',\n android = 'android',\n darwin = 'darwin',\n dragonfly = 'dragonfly',\n freebsd = 'freebsd',\n illumos = 'illumos',\n js = 'js',\n linux = 'linux',\n netbsd = 'netbsd',\n openbsd = 'openbsd',\n plan9 = 'plan9',\n solaris = 'solaris',\n windows = 'windows',\n}\n\nexport enum ManifestMediaType {\n Manifest = 'application/vnd.docker.distribution.manifest.v2+json',\n ManifestList = 'application/vnd.docker.distribution.manifest.list.v2+json',\n}\n\n/**\n * Yes, there's *way* more information contained in the manifest / \"fat\"\n * manifestList than just architectures, but I find this to be the most\n * relevant section for my projects. PR's welcome.\n */\nexport interface DockerManifest {\n readonly digest: string\n readonly mediaType: ManifestMediaType\n readonly platform: Array<{\n architecture: Architecture\n os: OS\n }>\n readonly schemaVersion: 1 | 2 | number\n}\n\nexport interface DockerManifestList {\n readonly manifests: DockerManifest[]\n readonly mediaType: ManifestMediaType\n readonly schemaVersion: 1 | 2 | any\n}\n\nexport interface DockerHubRepo {\n // ========================\n // Main fields of interest\n // ========================\n readonly description: string | null | undefined\n readonly lastUpdated: string\n readonly name: string\n readonly pullCount: number\n readonly starCount: number\n readonly user: string\n\n // Manifest type *may* be nested within this interface, but is usually\n // fetched and returned separately.\n readonly manifestList?: DockerManifestList\n readonly tags?: Tag[]\n\n // =============================================\n // Other stuff that comes down through the API,\n // that some may find useful\n // =============================================\n readonly canEdit?: boolean\n readonly isAutomated?: boolean\n readonly isMigrated?: boolean\n readonly isPrivate?: boolean\n readonly namespace?: string\n readonly repositoryType?: string\n readonly status?: number\n}\n\nexport interface Tag {\n creator: number\n fullSize: number\n id: number\n images: TagElement[]\n lastUpdated: string\n lastUpdater: number\n lastUpdaterUsername: string\n name: string\n repository: number\n v2: boolean\n}\n\nexport interface TagElement {\n architecture: Architecture\n digest: string\n features: string\n os: OS\n size: number\n}\n","import pino from 'pino'\n\nexport default pino({ base: null, useLevelLabels: true })\n","export const DOCKER_CLOUD_URL = 'https://cloud.docker.com/repository/docker/'\nexport const DOCKER_HUB_API_ROOT = 'https://hub.docker.com/v2/'\nexport const DOCKER_HUB_API_AUTH_URL = 'https://auth.docker.io/token'\n","import axios from 'axios'\nimport camelcaseKeys from 'camelcase-keys'\nimport { DateTime } from 'luxon'\nimport R from 'ramda'\nimport log from '../utils/log'\n\nimport {\n DockerHubAPIRepo,\n DockerHubRepo,\n DockerManifestList,\n Tag,\n} from '../types/DockerHubRepo'\nimport {\n DOCKER_HUB_API_AUTH_URL,\n DOCKER_HUB_API_ROOT,\n} from '../utils/constants'\n\n/**\n * Currently only supports fetching the manifest for the `latest` tag; in\n * reality, we can pass any valid content digest[1] to retrieve the manifest(s)\n * for that image.\n *\n * [1]: https://github.com/opencontainers/distribution-spec/blob/master/spec.md#content-digests\n */\nconst createManifestListURL = ({ repo }: { repo: DockerHubRepo }): string =>\n `https://registry-1.docker.io/v2/${repo.user}/${repo.name}/manifests/latest`\n\nconst createUserReposListURL = (user: string): string =>\n `${DOCKER_HUB_API_ROOT}repositories/${user}`\n\n/**\n * The OCI distribution spec requires a unique token for each repo manifest queried.\n */\nexport const fetchDockerHubToken = async (\n repo: DockerHubRepo,\n): Promise => {\n const { name, user } = repo\n const tokenRequest = await axios.get(DOCKER_HUB_API_AUTH_URL, {\n params: {\n scope: `repository:${user}/${name}:pull`,\n service: 'registry.docker.io',\n },\n })\n\n const token: string | undefined = R.path(['data', 'token'], tokenRequest)\n if (!token) {\n throw new Error('Unable to retrieve auth token from registry.')\n }\n return token\n}\n\n/**\n * Pure function that massages the Docker Hub API response into the\n * format we want to return. e.g., only extracting certain fields;\n * converting snake_case to camelCase, etc.\n */\nexport const extractRepositoryDetails = (\n repos: DockerHubAPIRepo[],\n lastUpdatedSince?: DateTime,\n): DockerHubRepo[] => {\n if (!repos || R.isEmpty(repos)) {\n return []\n }\n\n const parsedRepos: DockerHubRepo[] = (camelcaseKeys(\n repos,\n ) as unknown) as DockerHubRepo[]\n\n if (R.isNil(lastUpdatedSince)) {\n return parsedRepos\n }\n\n return parsedRepos.filter(\n repo => DateTime.fromISO(repo.lastUpdated) < lastUpdatedSince,\n )\n}\n\n/**\n * Query a single repository given a repo name and username.\n *\n * @param user The DockerHub username or org name to query for.\n * @param name The DockerHub repo name -- restrict to this single repo.\n */\nexport const queryRepo = async ({\n name,\n user,\n}: {\n name: string\n user: string\n}): Promise => {\n const repoResult = await axios.request({\n url: `${DOCKER_HUB_API_ROOT}repositories/${user}/${name}/`,\n })\n const repo: DockerHubRepo | undefined = R.prop('data', repoResult)\n if (repoResult.status !== 200 || !repo || R.isEmpty(repo)) {\n return\n }\n return (camelcaseKeys(repo) as unknown) as DockerHubRepo\n}\n\n/**\n * Top-level function for querying repositories.\n *\n * @TODO Rename to just `queryRepos`.\n *\n * @param user The DockerHub username or org name to query for.\n * @param numRepos The number of repos to query (max 100).\n * @param lastUpdatedSince Filter by the DateTime at which a repo was last updated.\n */\nexport const queryTopRepos = async ({\n lastUpdatedSince,\n numRepos = 100,\n user,\n}: {\n lastUpdatedSince?: DateTime\n numRepos?: number\n user: string\n}): Promise => {\n if (numRepos > 100) {\n throw new RangeError('Number of repos to query cannot exceed 100.')\n }\n\n const listReposURL = createUserReposListURL(user)\n const repoResults = await axios.get(listReposURL, {\n params: { page: 1, page_size: numRepos },\n })\n const repos: DockerHubAPIRepo[] = R.path(\n ['data', 'results'],\n repoResults,\n ) as DockerHubAPIRepo[]\n\n return extractRepositoryDetails(repos, lastUpdatedSince)\n}\n\n/**\n * Query image tags.\n */\nexport const queryTags = async (\n repo: DockerHubRepo,\n): Promise => {\n const repoUrl = createUserReposListURL(repo.user)\n const tagsUrl = `${repoUrl}/${repo.name}/tags?page_size=100`\n const tagsResults = await axios.get(tagsUrl)\n const tags = R.path(['data', 'results'], tagsResults)\n if (!tags || R.isEmpty(tags)) {\n return\n }\n // @ts-ignore\n return camelcaseKeys(tags)\n}\n\n/**\n * Queries the Docker Hub API to retrieve a \"fat manifest\", an object of\n * `Content-Type` `application/vnd.docker.distribution.manifest.list.v2+json/`.\n * Read up on the Manifest v2, Schema 2 Spec in more detail:\n * @see https://github.com/docker/distribution/blob/master/docs/spec/manifest-v2-2.md\n * Or the shiny new OCI distribution spec which builds on it:\n * @see https://github.com/opencontainers/distribution-spec/blob/f67bc11ba3a083a9c62f8fa53ad14c5bcf2116af/spec.md\n */\nexport const fetchManifestList = async (\n repo: DockerHubRepo,\n): Promise => {\n // Docker Hub requires a unique token for each repo manifest queried.\n const token = await fetchDockerHubToken(repo)\n\n const manifestListURL = createManifestListURL({ repo })\n const manifestListResponse = await axios.get(manifestListURL, {\n headers: {\n Accept: 'application/vnd.docker.distribution.manifest.list.v2+json',\n Authorization: `Bearer ${token}`,\n },\n })\n // For now, just ignore legacy V1 schema manifests. They have an entirely\n // different response shape and it's not worth mucking up the schema to\n // support a legacy format.\n if (manifestListResponse.data.schemaVersion === 1) {\n log.info('Schema version 1 is unsupported.', repo.name)\n return\n }\n\n return R.path(['data'], manifestListResponse)\n}\n"],"names":["Architecture","OS","ManifestMediaType","pino","base","useLevelLabels","DOCKER_HUB_API_ROOT","DOCKER_HUB_API_AUTH_URL","createManifestListURL","repo","user","name","createUserReposListURL","fetchDockerHubToken","axios","get","params","scope","service","tokenRequest","token","R","path","Error","extractRepositoryDetails","repos","lastUpdatedSince","isEmpty","parsedRepos","camelcaseKeys","isNil","filter","DateTime","fromISO","lastUpdated","queryRepo","request","url","repoResult","prop","status","queryTopRepos","numRepos","RangeError","listReposURL","page","page_size","repoResults","queryTags","repoUrl","tagsUrl","tagsResults","tags","fetchManifestList","manifestListURL","headers","Accept","Authorization","manifestListResponse","data","schemaVersion","log","info"],"mappings":";;;;;;AAoBA;;;;;;;;;;;IAWYA;;AAAZ,WAAYA;AACVA,EAAAA,oBAAA,QAAA;AACAA,EAAAA,qBAAA,UAAA;AACAA,EAAAA,mBAAA,QAAA;AACAA,EAAAA,qBAAA,UAAA;AACAA,EAAAA,oBAAA,SAAA;AACAA,EAAAA,sBAAA,WAAA;AACAA,EAAAA,wBAAA,aAAA;AACAA,EAAAA,sBAAA,WAAA;AACAA,EAAAA,qBAAA,UAAA;AACAA,EAAAA,uBAAA,YAAA;AACAA,EAAAA,qBAAA,UAAA;AACAA,EAAAA,oBAAA,SAAA;AACD,CAbD,EAAYA,YAAY,KAAZA,YAAY,KAAA,CAAxB;AAeA;;;;;;;AAKA,IAAYC,EAAZ;;AAAA,WAAYA;AACVA,EAAAA,SAAA,QAAA;AACAA,EAAAA,aAAA,YAAA;AACAA,EAAAA,YAAA,WAAA;AACAA,EAAAA,eAAA,cAAA;AACAA,EAAAA,aAAA,YAAA;AACAA,EAAAA,aAAA,YAAA;AACAA,EAAAA,QAAA,OAAA;AACAA,EAAAA,WAAA,UAAA;AACAA,EAAAA,YAAA,WAAA;AACAA,EAAAA,aAAA,YAAA;AACAA,EAAAA,WAAA,UAAA;AACAA,EAAAA,aAAA,YAAA;AACAA,EAAAA,aAAA,YAAA;AACD,CAdD,EAAYA,EAAE,KAAFA,EAAE,KAAA,CAAd;;IAgBYC;;AAAZ,WAAYA;AACVA,EAAAA,6BAAA,yDAAA;AACAA,EAAAA,iCAAA,8DAAA;AACD,CAHD,EAAYA,iBAAiB,KAAjBA,iBAAiB,KAAA,CAA7B;;ACjEA,UAAA;AAAeC,IAAI,CAAC;AAAEC,EAAAA,IAAI,EAAE,IAAR;AAAcC,EAAAA,cAAc,EAAE;AAA9B,CAAD,CAAnB;;ICDaC,mBAAmB,GAAG,4BAA5B;AACP,IAAaC,uBAAuB,GAAG,8BAAhC;;ACeP;;;;;;;;AAOA,IAAMC,qBAAqB,GAAG,SAAxBA,qBAAwB;AAAA,MAAGC,IAAH,QAAGA,IAAH;AAAA,8CACOA,IAAI,CAACC,IADZ,SACoBD,IAAI,CAACE,IADzB;AAAA,CAA9B;;AAGA,IAAMC,sBAAsB,GAAG,SAAzBA,sBAAyB,CAACF,IAAD;AAAA,SAC1BJ,mBAD0B,qBACSI,IADT;AAAA,CAA/B;AAGA;;;;;AAGA,IAAaG,mBAAmB,YAAnBA,mBAAmB,CAC9BJ,IAD8B;AAAA;QAGtBE,OAAeF,KAAfE;QAAMD,OAASD,KAATC;2BACaI,KAAK,CAACC,GAAN,CAAUR,uBAAV,EAAmC;AAC5DS,MAAAA,MAAM,EAAE;AACNC,QAAAA,KAAK,kBAAgBP,IAAhB,SAAwBC,IAAxB,UADC;AAENO,QAAAA,OAAO,EAAE;AAFH;AADoD,KAAnC,kBAArBC;AAON,UAAMC,KAAK,GAAuBC,CAAC,CAACC,IAAF,CAAO,CAAC,MAAD,EAAS,OAAT,CAAP,EAA0BH,YAA1B,CAAlC;;AACA,UAAI,CAACC,KAAL,EAAY;AACV,cAAM,IAAIG,KAAJ,CAAU,8CAAV,CAAN;AACD;;AACD,aAAOH,KAAP;;AACD,GAhB+B;AAAA;AAAA;AAAA,CAAzB;AAkBP;;;;;;AAKA,IAAaI,wBAAwB,GAAG,SAA3BA,wBAA2B,CACtCC,KADsC,EAEtCC,gBAFsC;AAItC,MAAI,CAACD,KAAD,IAAUJ,CAAC,CAACM,OAAF,CAAUF,KAAV,CAAd,EAAgC;AAC9B,WAAO,EAAP;AACD;;AAED,MAAMG,WAAW,GAAqBC,aAAa,CACjDJ,KADiD,CAAnD;;AAIA,MAAIJ,CAAC,CAACS,KAAF,CAAQJ,gBAAR,CAAJ,EAA+B;AAC7B,WAAOE,WAAP;AACD;;AAED,SAAOA,WAAW,CAACG,MAAZ,CACL,UAAAtB,IAAI;AAAA,WAAIuB,QAAQ,CAACC,OAAT,CAAiBxB,IAAI,CAACyB,WAAtB,IAAqCR,gBAAzC;AAAA,GADC,CAAP;AAGD,CAnBM;AAqBP;;;;;;;AAMA,IAAaS,SAAS,YAATA,SAAS;AAAA,MACpBxB,IADoB,SACpBA,IADoB;AAAA,MAEpBD,IAFoB,SAEpBA,IAFoB;;AAAA;2BAOKI,KAAK,CAACsB,OAAN,CAAc;AACrCC,MAAAA,GAAG,EAAK/B,mBAAL,qBAAwCI,IAAxC,SAAgDC,IAAhD;AADkC,KAAd,kBAAnB2B;AAGN,UAAM7B,IAAI,GAA8BY,CAAC,CAACkB,IAAF,CAAO,MAAP,EAAeD,UAAf,CAAxC;;AACA,UAAIA,UAAU,CAACE,MAAX,KAAsB,GAAtB,IAA6B,CAAC/B,IAA9B,IAAsCY,CAAC,CAACM,OAAF,CAAUlB,IAAV,CAA1C,EAA2D;AACzD;AACD;;AACD,aAAQoB,aAAa,CAACpB,IAAD,CAArB;;AACD,GAfqB;AAAA;AAAA;AAAA,CAAf;AAiBP;;;;;;;;;;AASA,IAAagC,aAAa,YAAbA,aAAa;AAAA,MACxBf,gBADwB,SACxBA,gBADwB;AAAA,6BAExBgB,QAFwB;AAAA,MAExBA,QAFwB,+BAEb,GAFa;AAAA,MAGxBhC,IAHwB,SAGxBA,IAHwB;;AAAA;AASxB,QAAIgC,QAAQ,GAAG,GAAf,EAAoB;AAClB,YAAM,IAAIC,UAAJ,CAAe,6CAAf,CAAN;AACD;;AAED,QAAMC,YAAY,GAAGhC,sBAAsB,CAACF,IAAD,CAA3C;2BAC0BI,KAAK,CAACC,GAAN,CAAU6B,YAAV,EAAwB;AAChD5B,MAAAA,MAAM,EAAE;AAAE6B,QAAAA,IAAI,EAAE,CAAR;AAAWC,QAAAA,SAAS,EAAEJ;AAAtB;AADwC,KAAxB,kBAApBK;AAGN,UAAMtB,KAAK,GAAuBJ,CAAC,CAACC,IAAF,CAChC,CAAC,MAAD,EAAS,SAAT,CADgC,EAEhCyB,WAFgC,CAAlC;AAKA,aAAOvB,wBAAwB,CAACC,KAAD,EAAQC,gBAAR,CAA/B;;AACD,GAvByB;AAAA;AAAA;AAAA,CAAnB;AAyBP;;;;AAGA,IAAasB,SAAS,YAATA,SAAS,CACpBvC,IADoB;AAAA;AAGpB,QAAMwC,OAAO,GAAGrC,sBAAsB,CAACH,IAAI,CAACC,IAAN,CAAtC;AACA,QAAMwC,OAAO,GAAMD,OAAN,SAAiBxC,IAAI,CAACE,IAAtB,wBAAb;2BAC0BG,KAAK,CAACC,GAAN,CAAUmC,OAAV,kBAApBC;AACN,UAAMC,IAAI,GAAG/B,CAAC,CAACC,IAAF,CAAO,CAAC,MAAD,EAAS,SAAT,CAAP,EAA4B6B,WAA5B,CAAb;;AACA,UAAI,CAACC,IAAD,IAAS/B,CAAC,CAACM,OAAF,CAAUyB,IAAV,CAAb,EAA8B;AAC5B;AACD;;;AAED,aAAOvB,aAAa,CAACuB,IAAD,CAApB;;AACD,GAZqB;AAAA;AAAA;AAAA,CAAf;AAcP;;;;;;;;;AAQA,IAAaC,iBAAiB,YAAjBA,iBAAiB,CAC5B5C,IAD4B;AAAA;AAG5B;2BACoBI,mBAAmB,CAACJ,IAAD,kBAAjCW;AAEN,UAAMkC,eAAe,GAAG9C,qBAAqB,CAAC;AAAEC,QAAAA,IAAI,EAAJA;AAAF,OAAD,CAA7C;6BACmCK,KAAK,CAACC,GAAN,CAAUuC,eAAV,EAA2B;AAC5DC,QAAAA,OAAO,EAAE;AACPC,UAAAA,MAAM,EAAE,2DADD;AAEPC,UAAAA,aAAa,cAAYrC;AAFlB;AADmD,OAA3B,kBAA7BsC;AAMN;AACA;AACA;AACA,YAAIA,oBAAoB,CAACC,IAArB,CAA0BC,aAA1B,KAA4C,CAAhD,EAAmD;AACjDC,UAAAA,GAAG,CAACC,IAAJ,CAAS,kCAAT,EAA6CrD,IAAI,CAACE,IAAlD;AACA;AACD;;AAED,eAAOU,CAAC,CAACC,IAAF,CAAO,CAAC,MAAD,CAAP,EAAiBoC,oBAAjB,CAAP;;;AACD,GAtB6B;AAAA;AAAA;AAAA,CAAvB;;;;"}