import { logger } from '../../../logger/index.ts'; import { isNotNullOrUndefined } from '../../../util/array.ts'; import type { Http } from '../../../util/http/index.ts'; import { MaybeTimestamp } from '../../../util/timestamp.ts'; import type { Release, ReleaseResult } from '../types.ts'; import { type File, PagedResponse } from './schema/prefix-dev.ts'; const MAX_PREFIX_DEV_GRAPHQL_PAGE = 100; const query = ` query search($channel: String!, $package: String!, $page: Int = 0) { package(channelName: $channel, name: $package) { variants(limit: 500, page: $page) { pages page { createdAt version yankedReason urls { url kind } } } } } `; export async function getReleases( http: Http, channel: string, packageName: string, ): Promise { logger.debug( { channel, packageName }, 'lookup package from prefix.dev graphql API', ); const files = await getPagedResponse(http, query, { channel, package: packageName, }); if (!files.length) { return null; } let homepage: string | undefined = undefined; let sourceUrl: string | undefined = undefined; const releases: Record = {}; for (const file of files) { const version = file.version; homepage ??= file.urls.HOME; sourceUrl ??= file.urls.DEV; releases[version] ??= { version }; // we assume all packages are roughly released on the same time releases[version].releaseTimestamp = releases[version].releaseTimestamp ?? MaybeTimestamp.parse(file.createdAt); // if the version has not been marked as deprecated, check other releases packages of the same version releases[version].isDeprecated ??= isNotNullOrUndefined(file.yankedReason); } return { homepage, sourceUrl, releases: Object.values(releases), }; } async function getPagedResponse( http: Http, query: string, data: any, ): Promise { const result: File[] = []; for (let page = 0; page <= MAX_PREFIX_DEV_GRAPHQL_PAGE; page++) { const res = await http.postJson( 'https://prefix.dev/api/graphql', { body: { operationName: 'search', query, variables: { ...data, page, }, }, }, PagedResponse, ); const currentPage = res.body.data.package?.variants; if (!currentPage) { break; } result.push(...currentPage.page); if (page >= currentPage.pages - 1) { break; } } return result; }