@xmcl/curseforge
Version:
An implementation of curseforge (official) API in https://docs.curseforge.com/
8 lines (7 loc) • 29.1 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../index.ts"],
"sourcesContent": ["/* eslint-disable n/no-unsupported-features/node-builtins */\n/**\n * @module @xmcl/curseforge\n */\n\nexport interface ModAsset {\n id: number\n modId: number\n title: string\n description: string\n thumbnailUrl: string\n url: string\n}\n\nexport const enum ModStatus {\n New = 1,\n ChangesRequired = 2,\n UnderSoftReview = 3,\n Approved = 4,\n Rejected = 5,\n ChangesMade = 6,\n Inactive = 7,\n Abandoned = 8,\n Deleted = 9,\n UnderReview = 10,\n}\nexport const enum FileReleaseType {\n Release = 1,\n Beta = 2,\n Alpha = 3,\n}\n\nexport const enum FileModLoaderType {\n Any = 0,\n Forge = 1,\n Cauldron = 2,\n LiteLoader = 3,\n Fabric = 4,\n Quilt = 5,\n NeoForge = 6,\n}\nexport interface FileIndex {\n gameVersion: string\n fileId: number\n filename: string\n releaseType: FileReleaseType\n\n gameVersionTypeId: number | null\n modLoader: FileModLoaderType\n}\n\nexport interface Mod {\n /**\n * The addon id. You can use this in many functions required the `addonID`\n */\n id: number\n /**\n * Game id. Minecraft is 432.\n */\n gameId: number\n /**\n * The display name of the addon\n */\n name: string\n /**\n * The mod slug that would appear in the URL\n */\n slug: string\n /** Relevant links for the mod such as Issue tracker and Wiki */\n links: {\n websiteUrl: string\n wikiUrl: string\n issuesUrl: string\n sourceUrl: string\n }\n /**\n * One line summery\n */\n summary: string\n /**\n * Current mod status\n */\n status: ModStatus\n /**\n * Number of downloads for the mod\n */\n downloadCount: number\n /**\n * Whether the mod is included in the featured mods list\n */\n isFeatured: boolean\n /**\n * The main category of the mod as it was chosen by the mod author\n */\n primaryCategoryId: number\n /**\n * List of categories that this mod is related to\n */\n categories: ModCategory[]\n /**\n * The class id this mod belongs to\n */\n classId: number | null\n /**\n * The list of authors\n */\n authors: Author[]\n\n logo: ModAsset\n\n screenshots: ModAsset[]\n /**\n * The id of the main file of the mod\n */\n mainFileId: number\n latestFiles: File[]\n /**\n * List of file related details for the latest files of the mod\n */\n latestFilesIndexes: FileIndex[]\n /**\n * The creation date of the mod\n */\n dateCreated: string\n\n dateModified: string\n dateReleased: string\n\n /**\n * Is mod allowed to be distributed\n */\n allowModDistribution: boolean | null\n /**\n * The mod popularity rank for the game\n */\n gamePopularityRank: number\n /**\n * Is the mod available for search. This can be false when a mod is experimental, in a deleted state or has only alpha files\n */\n isAvailable: boolean\n\n /**\n * The default download file id\n */\n defaultFileId: number\n /**\n * The mod's thumbs up count\n */\n thumbsUpCount: number\n}\n\nexport interface GameVersionLatestFile {\n gameVersion: string\n projectFileId: number\n projectFileName: string\n fileType: number\n}\n\nexport interface CategorySection {\n id: number\n gameId: number\n name: string\n packageType: number\n path: string\n initialInclusionPattern: string\n extraIncludePattern?: any\n gameCategoryId: number\n}\nexport const enum HashAlgo {\n Sha1 = 1,\n Md5 = 2,\n}\nexport interface FileHash {\n algo: HashAlgo\n value: string\n}\n\nexport const enum FileStatus {\n Processing = 1,\n ChangesRequired = 2,\n UnderReview = 3,\n Approved = 4,\n Rejected = 5,\n MalwareDetected = 6,\n Deleted = 7,\n Archived = 8,\n Testing = 9,\n Released = 10,\n ReadyForReview = 11,\n Deprecated = 12,\n Baking = 13,\n AwaitingPublishing = 14,\n FailedPublishing = 15,\n}\n\nexport const enum FileRelationType {\n EmbeddedLibrary = 1,\n OptionalDependency = 2,\n RequiredDependency = 3,\n Tool = 4,\n Incompatible = 5,\n Include = 6,\n}\n\nexport interface FileDependency {\n modId: number\n relationType: FileRelationType\n}\n\nexport interface File {\n /**\n * The fileID\n */\n id: number\n /**\n * The game id related to the mod that this file belongs to\n */\n gameId: number\n /**\n * The projectId (addonId)\n */\n modId: number\n /**\n * Whether the file is available to download\n */\n isAvailable: boolean\n /**\n * Display name\n */\n displayName: string\n /**\n * File name. Might be the same with `displayName`\n */\n fileName: string\n /**\n * Release or type.\n * - `1` is the release\n * - `2` beta\n * - `3` alpha\n */\n releaseType: number\n\n fileStatus: FileStatus\n\n hashes: FileHash[]\n\n fileFingerprint: number\n\n /**\n * The date of this file uploaded\n */\n fileDate: string\n /**\n * # bytes of this file.\n */\n fileLength: number\n\n /**\n * Number of downloads for the mod\n */\n downloadCount: number\n\n /**\n * Url to download\n */\n downloadUrl?: string\n /**\n * Game version string array, like `[\"1.12.2\"]`\n */\n gameVersions: string[]\n /**\n * Metadata used for sorting by game versions\n */\n isAlternate: boolean\n alternateFileId: number\n dependencies: FileDependency[]\n /**\n * What files inside?\n */\n modules: Module[]\n sortableGameVersions?: SortableGameVersion[]\n}\n\nexport interface SortableGameVersion {\n gameVersionPadded: string\n gameVersion: string\n gameVersionReleaseDate: string\n gameVersionName: string\n}\n\n/**\n * Represent a file in a `File`.\n */\nexport interface Module {\n /**\n * Actually the file name, not the folder\n */\n name: string\n /**\n * A number represent fingerprint\n */\n fingerprint: number\n type: number\n}\n\n/**\n * The author info\n */\nexport interface Author {\n /**\n * The project id of this query\n */\n projectId: number\n projectTitleId?: any\n projectTitleTitle?: any\n\n /**\n * Display name of the author\n */\n name: string\n /**\n * The full url of author homepage in curseforge\n */\n url: string\n /**\n * The id of this author\n */\n id: number\n userId: number\n twitchId: number\n}\n\nexport interface ModCategory {\n /**\n * The category id\n */\n id: number\n gameId: number\n name: string\n slug: string\n url: string\n iconUrl: string\n dateModified: string\n /**\n * A top level category for other categories\n */\n isClass: boolean | null\n /**\n * The class id of the category, meaning - the class of which this category is under\n */\n classId: number | null\n /**\n * The parent category for this category\n */\n parentCategoryId: number | null\n /**\n * The display index for this category\n */\n displayIndex: number | null\n}\n\n/**\n * The search options of the search API.\n *\n * @see {@link searchMods}\n */\nexport interface SearchOptions {\n /**\n * The category section id, which is also a category id.\n * You can fetch if from `getCategories`.\n *\n * To get available categories, you can:\n *\n * ```ts\n * const cat = await getCategories();\n * const sectionIds = cat\n * .filter(c => c.gameId === 432) // 432 is minecraft game id\n * .filter(c => c.rootGameCategoryId === null).map(c => c.id);\n * // the sectionIds is all normal sections here\n * ```\n *\n * @see {@link getCategories}\n */\n classId?: number\n /**\n * This is actually the sub category id of the `sectionId`. All the numbers for this should also be fetch by `getCategories`.\n *\n * To get available values, you can:\n *\n * ```ts\n * const cat = await getCategories();\n * const sectionId = 6; // the mods\n * const categoryIds = cat\n * .filter(c => c.gameId === 432) // 432 is minecraft game id\n * .filter(c => c.rootGameCategoryId === sectionId) // only under the section id\n * .map(c => c.id);\n * // Use categoryIds' id to search under the corresponding section id.\n * ```\n *\n * @see {@link getCategories}\n */\n categoryId?: number\n /**\n * The game id. The Minecraft is 432.\n *\n * @default 432\n */\n gameId?: number\n /**\n * The game version. For Minecraft, it should looks like 1.12.2.\n */\n gameVersion?: string\n /**\n * The index of the addon, NOT the page!\n *\n * When your page size is 25, if you want to get next page contents, you should have index = 25 to get 2nd page content.\n *\n * @default 0\n */\n index?: number\n /**\n * Filter by ModsSearchSortField enumeration\n */\n sortField?: ModsSearchSortField\n /**\n * 'asc' if sort is in ascending order, 'desc' if sort is in descending order\n */\n sortOrder?: 'asc' | 'desc'\n /**\n * Filter only mods associated to a given modloader (Forge, Fabric ...). Must be coupled with gameVersion.\n */\n modLoaderType?: FileModLoaderType\n\n modLoaderTypes?: string[]\n /**\n * Filter only mods that contain files tagged with versions of the given gameVersionTypeId\n */\n gameVersionTypeId?: number\n /**\n * Filter by slug (coupled with classId will result in a unique result).\n */\n slug?: string\n /**\n * The page size, or the number of the addons in a page.\n *\n * @default 25\n */\n pageSize?: number\n /**\n * The keyword of search. If this is absent, it just list out the available addons by `sectionId` and `categoryId`.\n */\n searchFilter?: string\n}\n\nexport const enum ModsSearchSortField {\n Featured = 1,\n Popularity = 2,\n LastUpdated = 3,\n Name = 4,\n Author = 5,\n TotalDownloads = 6,\n Category = 7,\n GameVersion = 8,\n}\n\n/**\n * The options to query\n */\nexport interface QueryOption {\n /**\n * Additional header\n */\n headers?: Record<string, any>\n /**\n * override the http client\n */\n client?: (url: string, options: QueryOption, body?: object, text?: boolean) => Promise<object | string>\n}\n\nexport interface GetModFilesOptions {\n modId: number\n gameVersion?: string\n modLoaderType?: FileModLoaderType\n /**\n * Filter only files that are tagged with versions of the given gameVersionTypeId\n */\n gameVersionTypeId?: number\n index?: number\n pageSize?: number\n}\n\nexport interface Pagination {\n /**\n * A zero based index of the first item that is included in the response\n */\n index: number\n /**\n * The requested number of items to be included in the response\n */\n pageSize: number\n /**\n * The actual number of items that were included in the response\n */\n resultCount: number\n /**\n * The total number of items available by the fetch\n */\n totalCount: number\n}\n\nexport interface CurseforgeClientOptions {\n /**\n * Extra headers\n */\n headers?: Record<string, string>\n /**\n * The base url, the default is `https://api.curseforge.com`\n */\n baseUrl?: string\n /**\n * The fetch function to use. The default is `fetch`\n */\n fetch?: typeof fetch\n}\n\nexport interface FingerprintMatch {\n /**\n * The mod id\n */\n id: number\n file: File\n latestFiles: File[]\n}\nexport interface FingerprintsMatchesResult {\n data: {\n isCacheBuilt: boolean\n exactMatches: FingerprintMatch[]\n exactFingerprints: number[]\n partialMatches: FingerprintMatch[]\n partialFingerprints: object\n unmatchedFingerprints: number[]\n }\n}\n\nexport interface FingerprintFuzzyMatch {\n id: number\n file: File\n latestFiles: File[]\n fingerprints: number[]\n}\n\nexport interface FingerprintFuzzyMatchResult {\n data: {\n fuzzyMatches: FingerprintFuzzyMatch[]\n }\n}\n\nexport class CurseforgeApiError extends Error {\n constructor(readonly url: string, readonly status: number, readonly body: string) {\n super(`Fail to fetch curseforge api ${url}. Status=${status}. ${body}`)\n this.name = 'CurseforgeApiError'\n }\n}\n\n/**\n * Reference the https://docs.curseforge.com/#curseforge-core-api-mods\n */\nexport class CurseforgeV1Client {\n headers: Record<string, string>\n private fetch: typeof fetch\n private baseUrl: string\n\n constructor(private apiKey: string, options?: CurseforgeClientOptions) {\n this.headers = {\n 'x-api-key': this.apiKey,\n ...options?.headers,\n }\n this.baseUrl = options?.baseUrl || 'https://api.curseforge.com'\n this.fetch = options?.fetch || ((...args) => fetch(...args))\n }\n\n /**\n * @see https://docs.curseforge.com/#get-categories\n */\n async getCategories(signal?: AbortSignal) {\n const url = new URL(this.baseUrl + '/v1/categories')\n url.searchParams.append('gameId', '432')\n const response = await this.fetch(url, {\n headers: {\n ...this.headers,\n accept: 'application/json',\n },\n signal,\n })\n if (response.status !== 200) {\n throw new CurseforgeApiError(url.toString(), response.status, await response.text())\n }\n const categories = await response.json() as { data: ModCategory[] }\n return categories.data\n }\n\n /**\n * Get the mod by mod Id.\n * @see https://docs.curseforge.com/#get-mod\n * @param modId The id of mod\n * @param options The query options\n */\n async getMod(modId: number, signal?: AbortSignal) {\n const url = new URL(this.baseUrl + `/v1/mods/${modId}`)\n const response = await this.fetch(url, {\n headers: {\n accept: 'application/json',\n ...this.headers,\n },\n signal,\n })\n if (response.status !== 200) {\n throw new CurseforgeApiError(url.toString(), response.status, await response.text())\n }\n const result = await response.json() as { data: Mod }\n return result.data\n }\n\n /**\n * @see https://docs.curseforge.com/#get-mod-description\n */\n async getModDescription(modId: number, signal?: AbortSignal) {\n const url = new URL(this.baseUrl + `/v1/mods/${modId}/description`)\n const response = await this.fetch(url, {\n headers: {\n ...this.headers,\n accept: 'application/json',\n },\n signal,\n })\n if (response.status !== 200) {\n throw new CurseforgeApiError(url.toString(), response.status, await response.text())\n }\n const result = await response.json() as { data: string }\n return result.data\n }\n\n /**\n * @see https://docs.curseforge.com/#get-mod-files\n */\n async getModFiles(options: GetModFilesOptions, signal?: AbortSignal) {\n const url = new URL(this.baseUrl + `/v1/mods/${options.modId}/files`)\n url.searchParams.append('gameVersion', options.gameVersion ?? '')\n if (options.modLoaderType !== undefined) {\n url.searchParams.append('modLoaderType', options.modLoaderType?.toString() ?? '')\n }\n url.searchParams.append('gameVersionTypeId', options.gameVersionTypeId?.toString() ?? '')\n url.searchParams.append('index', options.index?.toString() ?? '')\n url.searchParams.append('pageSize', options.pageSize?.toString() ?? '')\n const response = await this.fetch(url, {\n headers: {\n ...this.headers,\n accept: 'application/json',\n },\n signal,\n })\n if (response.status !== 200) {\n throw new CurseforgeApiError(url.toString(), response.status, await response.text())\n }\n const result = await response.json() as { data: File[]; pagination: Pagination }\n return result\n }\n\n /**\n * @see https://docs.curseforge.com/#curseforge-core-api-files\n */\n async getModFile(modId: number, fileId: number, signal?: AbortSignal) {\n const url = new URL(this.baseUrl + `/v1/mods/${modId}/files/${fileId}`)\n const response = await this.fetch(url, {\n headers: {\n ...this.headers,\n accept: 'application/json',\n },\n signal,\n })\n if (response.status !== 200) {\n throw new CurseforgeApiError(url.toString(), response.status, await response.text())\n }\n const result = await response.json() as { data: File }\n return result.data\n }\n\n /**\n * @see https://docs.curseforge.com/#get-mods\n */\n async getMods(modIds: number[], signal?: AbortSignal) {\n const url = new URL(this.baseUrl + '/v1/mods')\n const response = await this.fetch(url, {\n method: 'POST',\n body: JSON.stringify({ modIds }),\n headers: {\n ...this.headers,\n 'content-type': 'application/json',\n accept: 'application/json',\n },\n signal,\n })\n if (response.status !== 200) {\n throw new CurseforgeApiError(url.toString(), response.status, await response.text())\n }\n const result = await response.json() as { data: Mod[] }\n return result.data\n }\n\n /**\n * @see https://docs.curseforge.com/#get-files\n */\n async getFiles(fileIds: number[], signal?: AbortSignal) {\n const url = new URL(this.baseUrl + '/v1/mods/files')\n const response = await this.fetch(url, {\n method: 'POST',\n body: JSON.stringify({ fileIds }),\n headers: {\n ...this.headers,\n 'content-type': 'application/json',\n accept: 'application/json',\n },\n signal,\n })\n if (response.status !== 200) {\n throw new CurseforgeApiError(url.toString(), response.status, await response.text())\n }\n const result = await response.json() as { data: File[] }\n return result.data\n }\n\n /**\n * @see https://docs.curseforge.com/#search-mods\n */\n async searchMods(options: SearchOptions, signal?: AbortSignal) {\n const url = new URL(this.baseUrl + '/v1/mods/search')\n url.searchParams.append('gameId', '432')\n if (options.classId) { url.searchParams.append('classId', options.classId.toString()) }\n if (options.categoryId) { url.searchParams.append('categoryId', options.categoryId.toString()) }\n if (options.gameVersion) { url.searchParams.append('gameVersion', options.gameVersion) }\n if (options.searchFilter) { url.searchParams.append('searchFilter', options.searchFilter) }\n url.searchParams.append('sortField', options.sortField?.toString() ?? ModsSearchSortField.Popularity.toString())\n url.searchParams.append('sortOrder', options.sortOrder ?? 'desc')\n if (options.modLoaderType) { url.searchParams.append('modLoaderType', options.modLoaderType.toString()) }\n if (options.modLoaderTypes) { url.searchParams.append('modLoaderTypes', '[' + options.modLoaderTypes.join(',') + ']') }\n if (options.gameVersionTypeId) { url.searchParams.append('gameVersionTypeId', options.gameVersionTypeId.toString()) }\n url.searchParams.append('index', options.index?.toString() ?? '0')\n url.searchParams.append('pageSize', options.pageSize?.toString() ?? '25')\n if (options.slug) { url.searchParams.append('slug', options.slug) }\n const response = await this.fetch(url, {\n headers: {\n ...this.headers,\n accept: 'application/json',\n },\n signal,\n })\n if (response.status !== 200) {\n throw new CurseforgeApiError(url.toString(), response.status, await response.text())\n }\n const result = await response.json() as { data: Mod[]; pagination: Pagination }\n return result\n }\n\n /**\n * https://docs.curseforge.com/#get-mod-file-changelog\n */\n async getModFileChangelog(modId: number, fileId: number, signal?: AbortSignal) {\n const url = new URL(this.baseUrl + `/v1/mods/${modId}/files/${fileId}/changelog`)\n const response = await this.fetch(url, {\n headers: {\n ...this.headers,\n accept: 'application/json',\n },\n signal,\n })\n if (response.status !== 200) {\n throw new CurseforgeApiError(url.toString(), response.status, await response.text())\n }\n const result = await response.json() as { data: string }\n return result.data\n }\n\n async getFingerprintsMatchesByGameId(gameId: number, fingerprints: number[], signal?: AbortSignal) {\n const url = new URL(this.baseUrl + `/v1/fingerprints/${gameId}`)\n const response = await this.fetch(url, {\n method: 'POST',\n body: JSON.stringify({ fingerprints }),\n headers: {\n ...this.headers,\n 'content-type': 'application/json',\n accept: 'application/json',\n },\n signal,\n })\n if (response.status !== 200) {\n throw new CurseforgeApiError(url.toString(), response.status, await response.text())\n }\n const result = await response.json() as FingerprintsMatchesResult\n return result.data\n }\n\n async getFingerprintsFuzzyMatchesByGameId(gameId: number, fingerprints: number[], signal?: AbortSignal) {\n const url = new URL(this.baseUrl + `/v1/fingerprints/fuzzy/${gameId}`)\n const response = await this.fetch(url, {\n method: 'POST',\n body: JSON.stringify({ fingerprints }),\n headers: {\n ...this.headers,\n 'content-type': 'application/json',\n accept: 'application/json',\n },\n signal,\n })\n if (response.status !== 200) {\n throw new CurseforgeApiError(url.toString(), response.status, await response.text())\n }\n const result = await response.json() as FingerprintFuzzyMatchResult\n return result.data\n }\n}\n"],
"mappings": "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",
"names": ["ModStatus", "FileReleaseType", "FileModLoaderType", "HashAlgo", "FileStatus", "FileRelationType", "ModsSearchSortField"]
}