UNPKG

@shulkers/api-fetcher

Version:

A comprehensive TypeScript API wrapper for Minecraft plugin repositories (Spiget, Modrinth, Hangar)

1,430 lines (1,405 loc) 54.8 kB
var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true, configurable: true, set: (newValue) => all[name] = () => newValue }); }; // ../../node_modules/ky/distribution/errors/HTTPError.js class HTTPError extends Error { response; request; options; constructor(response, request, options) { const code = response.status || response.status === 0 ? response.status : ""; const title = response.statusText || ""; const status = `${code} ${title}`.trim(); const reason = status ? `status code ${status}` : "an unknown error"; super(`Request failed with ${reason}: ${request.method} ${request.url}`); this.name = "HTTPError"; this.response = response; this.request = request; this.options = options; } } // ../../node_modules/ky/distribution/errors/TimeoutError.js class TimeoutError extends Error { request; constructor(request) { super(`Request timed out: ${request.method} ${request.url}`); this.name = "TimeoutError"; this.request = request; } } // ../../node_modules/ky/distribution/core/constants.js var supportsRequestStreams = (() => { let duplexAccessed = false; let hasContentType = false; const supportsReadableStream = typeof globalThis.ReadableStream === "function"; const supportsRequest = typeof globalThis.Request === "function"; if (supportsReadableStream && supportsRequest) { try { hasContentType = new globalThis.Request("https://empty.invalid", { body: new globalThis.ReadableStream, method: "POST", get duplex() { duplexAccessed = true; return "half"; } }).headers.has("Content-Type"); } catch (error) { if (error instanceof Error && error.message === "unsupported BodyInit type") { return false; } throw error; } } return duplexAccessed && !hasContentType; })(); var supportsAbortController = typeof globalThis.AbortController === "function"; var supportsResponseStreams = typeof globalThis.ReadableStream === "function"; var supportsFormData = typeof globalThis.FormData === "function"; var requestMethods = ["get", "post", "put", "patch", "head", "delete"]; var validate = () => { return; }; validate(); var responseTypes = { json: "application/json", text: "text/*", formData: "multipart/form-data", arrayBuffer: "*/*", blob: "*/*" }; var maxSafeTimeout = 2147483647; var usualFormBoundarySize = new TextEncoder().encode("------WebKitFormBoundaryaxpyiPgbbPti10Rw").length; var stop = Symbol("stop"); var kyOptionKeys = { json: true, parseJson: true, stringifyJson: true, searchParams: true, prefixUrl: true, retry: true, timeout: true, hooks: true, throwHttpErrors: true, onDownloadProgress: true, onUploadProgress: true, fetch: true }; var requestOptionsRegistry = { method: true, headers: true, body: true, mode: true, credentials: true, cache: true, redirect: true, referrer: true, referrerPolicy: true, integrity: true, keepalive: true, signal: true, window: true, dispatcher: true, duplex: true, priority: true }; // ../../node_modules/ky/distribution/utils/body.js var getBodySize = (body) => { if (!body) { return 0; } if (body instanceof FormData) { let size = 0; for (const [key, value] of body) { size += usualFormBoundarySize; size += new TextEncoder().encode(`Content-Disposition: form-data; name="${key}"`).length; size += typeof value === "string" ? new TextEncoder().encode(value).length : value.size; } return size; } if (body instanceof Blob) { return body.size; } if (body instanceof ArrayBuffer) { return body.byteLength; } if (typeof body === "string") { return new TextEncoder().encode(body).length; } if (body instanceof URLSearchParams) { return new TextEncoder().encode(body.toString()).length; } if ("byteLength" in body) { return body.byteLength; } if (typeof body === "object" && body !== null) { try { const jsonString = JSON.stringify(body); return new TextEncoder().encode(jsonString).length; } catch { return 0; } } return 0; }; var streamResponse = (response, onDownloadProgress) => { const totalBytes = Number(response.headers.get("content-length")) || 0; let transferredBytes = 0; if (response.status === 204) { if (onDownloadProgress) { onDownloadProgress({ percent: 1, totalBytes, transferredBytes }, new Uint8Array); } return new Response(null, { status: response.status, statusText: response.statusText, headers: response.headers }); } return new Response(new ReadableStream({ async start(controller) { const reader = response.body.getReader(); if (onDownloadProgress) { onDownloadProgress({ percent: 0, transferredBytes: 0, totalBytes }, new Uint8Array); } async function read() { const { done, value } = await reader.read(); if (done) { controller.close(); return; } if (onDownloadProgress) { transferredBytes += value.byteLength; const percent = totalBytes === 0 ? 0 : transferredBytes / totalBytes; onDownloadProgress({ percent, transferredBytes, totalBytes }, value); } controller.enqueue(value); await read(); } await read(); } }), { status: response.status, statusText: response.statusText, headers: response.headers }); }; var streamRequest = (request, onUploadProgress) => { const totalBytes = getBodySize(request.body); let transferredBytes = 0; return new Request(request, { duplex: "half", body: new ReadableStream({ async start(controller) { const reader = request.body instanceof ReadableStream ? request.body.getReader() : new Response("").body.getReader(); async function read() { const { done, value } = await reader.read(); if (done) { if (onUploadProgress) { onUploadProgress({ percent: 1, transferredBytes, totalBytes: Math.max(totalBytes, transferredBytes) }, new Uint8Array); } controller.close(); return; } transferredBytes += value.byteLength; let percent = totalBytes === 0 ? 0 : transferredBytes / totalBytes; if (totalBytes < transferredBytes || percent === 1) { percent = 0.99; } if (onUploadProgress) { onUploadProgress({ percent: Number(percent.toFixed(2)), transferredBytes, totalBytes }, value); } controller.enqueue(value); await read(); } await read(); } }) }); }; // ../../node_modules/ky/distribution/utils/is.js var isObject = (value) => value !== null && typeof value === "object"; // ../../node_modules/ky/distribution/utils/merge.js var validateAndMerge = (...sources) => { for (const source of sources) { if ((!isObject(source) || Array.isArray(source)) && source !== undefined) { throw new TypeError("The `options` argument must be an object"); } } return deepMerge({}, ...sources); }; var mergeHeaders = (source1 = {}, source2 = {}) => { const result = new globalThis.Headers(source1); const isHeadersInstance = source2 instanceof globalThis.Headers; const source = new globalThis.Headers(source2); for (const [key, value] of source.entries()) { if (isHeadersInstance && value === "undefined" || value === undefined) { result.delete(key); } else { result.set(key, value); } } return result; }; function newHookValue(original, incoming, property) { return Object.hasOwn(incoming, property) && incoming[property] === undefined ? [] : deepMerge(original[property] ?? [], incoming[property] ?? []); } var mergeHooks = (original = {}, incoming = {}) => ({ beforeRequest: newHookValue(original, incoming, "beforeRequest"), beforeRetry: newHookValue(original, incoming, "beforeRetry"), afterResponse: newHookValue(original, incoming, "afterResponse"), beforeError: newHookValue(original, incoming, "beforeError") }); var deepMerge = (...sources) => { let returnValue = {}; let headers = {}; let hooks = {}; for (const source of sources) { if (Array.isArray(source)) { if (!Array.isArray(returnValue)) { returnValue = []; } returnValue = [...returnValue, ...source]; } else if (isObject(source)) { for (let [key, value] of Object.entries(source)) { if (isObject(value) && key in returnValue) { value = deepMerge(returnValue[key], value); } returnValue = { ...returnValue, [key]: value }; } if (isObject(source.hooks)) { hooks = mergeHooks(hooks, source.hooks); returnValue.hooks = hooks; } if (isObject(source.headers)) { headers = mergeHeaders(headers, source.headers); returnValue.headers = headers; } } } return returnValue; }; // ../../node_modules/ky/distribution/utils/normalize.js var normalizeRequestMethod = (input) => requestMethods.includes(input) ? input.toUpperCase() : input; var retryMethods = ["get", "put", "head", "delete", "options", "trace"]; var retryStatusCodes = [408, 413, 429, 500, 502, 503, 504]; var retryAfterStatusCodes = [413, 429, 503]; var defaultRetryOptions = { limit: 2, methods: retryMethods, statusCodes: retryStatusCodes, afterStatusCodes: retryAfterStatusCodes, maxRetryAfter: Number.POSITIVE_INFINITY, backoffLimit: Number.POSITIVE_INFINITY, delay: (attemptCount) => 0.3 * 2 ** (attemptCount - 1) * 1000 }; var normalizeRetryOptions = (retry = {}) => { if (typeof retry === "number") { return { ...defaultRetryOptions, limit: retry }; } if (retry.methods && !Array.isArray(retry.methods)) { throw new Error("retry.methods must be an array"); } if (retry.statusCodes && !Array.isArray(retry.statusCodes)) { throw new Error("retry.statusCodes must be an array"); } return { ...defaultRetryOptions, ...retry }; }; // ../../node_modules/ky/distribution/utils/timeout.js async function timeout(request, init, abortController, options) { return new Promise((resolve, reject) => { const timeoutId = setTimeout(() => { if (abortController) { abortController.abort(); } reject(new TimeoutError(request)); }, options.timeout); options.fetch(request, init).then(resolve).catch(reject).then(() => { clearTimeout(timeoutId); }); }); } // ../../node_modules/ky/distribution/utils/delay.js async function delay(ms, { signal }) { return new Promise((resolve, reject) => { if (signal) { signal.throwIfAborted(); signal.addEventListener("abort", abortHandler, { once: true }); } function abortHandler() { clearTimeout(timeoutId); reject(signal.reason); } const timeoutId = setTimeout(() => { signal?.removeEventListener("abort", abortHandler); resolve(); }, ms); }); } // ../../node_modules/ky/distribution/utils/options.js var findUnknownOptions = (request, options) => { const unknownOptions = {}; for (const key in options) { if (!(key in requestOptionsRegistry) && !(key in kyOptionKeys) && !(key in request)) { unknownOptions[key] = options[key]; } } return unknownOptions; }; // ../../node_modules/ky/distribution/core/Ky.js class Ky { static create(input, options) { const ky = new Ky(input, options); const function_ = async () => { if (typeof ky._options.timeout === "number" && ky._options.timeout > maxSafeTimeout) { throw new RangeError(`The \`timeout\` option cannot be greater than ${maxSafeTimeout}`); } await Promise.resolve(); let response = await ky._fetch(); for (const hook of ky._options.hooks.afterResponse) { const modifiedResponse = await hook(ky.request, ky._options, ky._decorateResponse(response.clone())); if (modifiedResponse instanceof globalThis.Response) { response = modifiedResponse; } } ky._decorateResponse(response); if (!response.ok && ky._options.throwHttpErrors) { let error = new HTTPError(response, ky.request, ky._options); for (const hook of ky._options.hooks.beforeError) { error = await hook(error); } throw error; } if (ky._options.onDownloadProgress) { if (typeof ky._options.onDownloadProgress !== "function") { throw new TypeError("The `onDownloadProgress` option must be a function"); } if (!supportsResponseStreams) { throw new Error("Streams are not supported in your environment. `ReadableStream` is missing."); } return streamResponse(response.clone(), ky._options.onDownloadProgress); } return response; }; const isRetriableMethod = ky._options.retry.methods.includes(ky.request.method.toLowerCase()); const result = (isRetriableMethod ? ky._retry(function_) : function_()).finally(async () => { if (!ky.request.bodyUsed) { await ky.request.body?.cancel(); } }); for (const [type, mimeType] of Object.entries(responseTypes)) { result[type] = async () => { ky.request.headers.set("accept", ky.request.headers.get("accept") || mimeType); const response = await result; if (type === "json") { if (response.status === 204) { return ""; } const arrayBuffer = await response.clone().arrayBuffer(); const responseSize = arrayBuffer.byteLength; if (responseSize === 0) { return ""; } if (options.parseJson) { return options.parseJson(await response.text()); } } return response[type](); }; } return result; } request; abortController; _retryCount = 0; _input; _options; constructor(input, options = {}) { this._input = input; this._options = { ...options, headers: mergeHeaders(this._input.headers, options.headers), hooks: mergeHooks({ beforeRequest: [], beforeRetry: [], beforeError: [], afterResponse: [] }, options.hooks), method: normalizeRequestMethod(options.method ?? this._input.method ?? "GET"), prefixUrl: String(options.prefixUrl || ""), retry: normalizeRetryOptions(options.retry), throwHttpErrors: options.throwHttpErrors !== false, timeout: options.timeout ?? 1e4, fetch: options.fetch ?? globalThis.fetch.bind(globalThis) }; if (typeof this._input !== "string" && !(this._input instanceof URL || this._input instanceof globalThis.Request)) { throw new TypeError("`input` must be a string, URL, or Request"); } if (this._options.prefixUrl && typeof this._input === "string") { if (this._input.startsWith("/")) { throw new Error("`input` must not begin with a slash when using `prefixUrl`"); } if (!this._options.prefixUrl.endsWith("/")) { this._options.prefixUrl += "/"; } this._input = this._options.prefixUrl + this._input; } if (supportsAbortController) { const originalSignal = this._options.signal ?? this._input.signal; this.abortController = new globalThis.AbortController; this._options.signal = originalSignal ? AbortSignal.any([originalSignal, this.abortController.signal]) : this.abortController.signal; } if (supportsRequestStreams) { this._options.duplex = "half"; } if (this._options.json !== undefined) { this._options.body = this._options.stringifyJson?.(this._options.json) ?? JSON.stringify(this._options.json); this._options.headers.set("content-type", this._options.headers.get("content-type") ?? "application/json"); } this.request = new globalThis.Request(this._input, this._options); if (this._options.searchParams) { const textSearchParams = typeof this._options.searchParams === "string" ? this._options.searchParams.replace(/^\?/, "") : new URLSearchParams(this._options.searchParams).toString(); const searchParams = "?" + textSearchParams; const url = this.request.url.replace(/(?:\?.*?)?(?=#|$)/, searchParams); if ((supportsFormData && this._options.body instanceof globalThis.FormData || this._options.body instanceof URLSearchParams) && !(this._options.headers && this._options.headers["content-type"])) { this.request.headers.delete("content-type"); } this.request = new globalThis.Request(new globalThis.Request(url, { ...this.request }), this._options); } if (this._options.onUploadProgress) { if (typeof this._options.onUploadProgress !== "function") { throw new TypeError("The `onUploadProgress` option must be a function"); } if (!supportsRequestStreams) { throw new Error("Request streams are not supported in your environment. The `duplex` option for `Request` is not available."); } const originalBody = this.request.body; if (originalBody) { this.request = streamRequest(this.request, this._options.onUploadProgress); } } } _calculateRetryDelay(error) { this._retryCount++; if (this._retryCount > this._options.retry.limit || error instanceof TimeoutError) { throw error; } if (error instanceof HTTPError) { if (!this._options.retry.statusCodes.includes(error.response.status)) { throw error; } const retryAfter = error.response.headers.get("Retry-After") ?? error.response.headers.get("RateLimit-Reset") ?? error.response.headers.get("X-RateLimit-Reset") ?? error.response.headers.get("X-Rate-Limit-Reset"); if (retryAfter && this._options.retry.afterStatusCodes.includes(error.response.status)) { let after = Number(retryAfter) * 1000; if (Number.isNaN(after)) { after = Date.parse(retryAfter) - Date.now(); } else if (after >= Date.parse("2024-01-01")) { after -= Date.now(); } const max = this._options.retry.maxRetryAfter ?? after; return after < max ? after : max; } if (error.response.status === 413) { throw error; } } const retryDelay = this._options.retry.delay(this._retryCount); return Math.min(this._options.retry.backoffLimit, retryDelay); } _decorateResponse(response) { if (this._options.parseJson) { response.json = async () => this._options.parseJson(await response.text()); } return response; } async _retry(function_) { try { return await function_(); } catch (error) { const ms = Math.min(this._calculateRetryDelay(error), maxSafeTimeout); if (this._retryCount < 1) { throw error; } await delay(ms, { signal: this._options.signal }); for (const hook of this._options.hooks.beforeRetry) { const hookResult = await hook({ request: this.request, options: this._options, error, retryCount: this._retryCount }); if (hookResult === stop) { return; } } return this._retry(function_); } } async _fetch() { for (const hook of this._options.hooks.beforeRequest) { const result = await hook(this.request, this._options); if (result instanceof Request) { this.request = result; break; } if (result instanceof Response) { return result; } } const nonRequestOptions = findUnknownOptions(this.request, this._options); const mainRequest = this.request; this.request = mainRequest.clone(); if (this._options.timeout === false) { return this._options.fetch(mainRequest, nonRequestOptions); } return timeout(mainRequest, nonRequestOptions, this.abortController, this._options); } } // ../../node_modules/ky/distribution/index.js /*! MIT License © Sindre Sorhus */ var createInstance = (defaults) => { const ky = (input, options) => Ky.create(input, validateAndMerge(defaults, options)); for (const method of requestMethods) { ky[method] = (input, options) => Ky.create(input, validateAndMerge(defaults, options, { method })); } ky.create = (newDefaults) => createInstance(validateAndMerge(newDefaults)); ky.extend = (newDefaults) => { if (typeof newDefaults === "function") { newDefaults = newDefaults(defaults ?? {}); } return createInstance(validateAndMerge(defaults, newDefaults)); }; ky.stop = stop; return ky; }; var ky = createInstance(); var distribution_default = ky; // src/utils/baseClient.ts class BaseAPIClient { baseUrl; userAgent; constructor(baseUrl, userAgent) { this.baseUrl = baseUrl; this.userAgent = userAgent || "shulkers/1.0.0"; } buildSearchParams(options) { const params = {}; for (const [key, value] of Object.entries(options)) { if (value !== undefined) { if (Array.isArray(value)) { params[key] = JSON.stringify(value); } else { params[key] = value; } } } return params; } getHeaders() { return { "User-Agent": this.userAgent }; } async makeRequest(endpoint, searchParams) { try { const response = await distribution_default.get(`${this.baseUrl}${endpoint}`, { searchParams: searchParams ? this.buildSearchParams(searchParams) : undefined, headers: this.getHeaders() }); return response.json(); } catch (error) { throw this.handleError(error, "API_REQUEST_FAILED", `Failed to fetch ${endpoint}`); } } async makePostRequest(endpoint, body, searchParams) { try { const response = await distribution_default.post(`${this.baseUrl}${endpoint}`, { json: body, searchParams: searchParams ? this.buildSearchParams(searchParams) : undefined, headers: this.getHeaders() }); return response.json(); } catch (error) { throw this.handleError(error, "API_REQUEST_FAILED", `Failed to post to ${endpoint}`); } } async makeRawRequest(url) { try { return await distribution_default.get(url, { headers: this.getHeaders() }); } catch (error) { throw this.handleError(error, "DOWNLOAD_FAILED", `Failed to download from ${url}`); } } } // src/types/spigetType.ts var exports_spigetType = {}; __export(exports_spigetType, { SpigetVersionMethod: () => SpigetVersionMethod, SpigetSearchField: () => SpigetSearchField, SpigetErrorCode: () => SpigetErrorCode, SpigetError: () => SpigetError }); var SpigetSearchField; ((SpigetSearchField2) => { SpigetSearchField2["NAME"] = "name"; SpigetSearchField2["TAG"] = "tag"; })(SpigetSearchField ||= {}); var SpigetVersionMethod; ((SpigetVersionMethod2) => { SpigetVersionMethod2["ANY"] = "any"; SpigetVersionMethod2["ALL"] = "all"; })(SpigetVersionMethod ||= {}); var SpigetErrorCode; ((SpigetErrorCode2) => { SpigetErrorCode2["EXTERNAL_FILE_DOWNLOAD"] = "EXTERNAL_FILE_DOWNLOAD"; SpigetErrorCode2["RESOURCE_NOT_FOUND"] = "RESOURCE_NOT_FOUND"; SpigetErrorCode2["INVALID_SEARCH_PARAMETERS"] = "INVALID_SEARCH_PARAMETERS"; SpigetErrorCode2["API_REQUEST_FAILED"] = "API_REQUEST_FAILED"; SpigetErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR"; SpigetErrorCode2["INVALID_RESPONSE"] = "INVALID_RESPONSE"; SpigetErrorCode2["RATE_LIMIT_EXCEEDED"] = "RATE_LIMIT_EXCEEDED"; SpigetErrorCode2["UNAUTHORIZED"] = "UNAUTHORIZED"; SpigetErrorCode2["INVALID_VERSION"] = "INVALID_VERSION"; SpigetErrorCode2["DOWNLOAD_FAILED"] = "DOWNLOAD_FAILED"; })(SpigetErrorCode ||= {}); class SpigetError extends Error { code; details; constructor(code, message, details) { super(message); this.code = code; this.details = details; this.name = "SpigetError"; } } // src/api/spiget.ts class SpigetAPI extends BaseAPIClient { constructor(options = {}) { super(options.baseUrl ?? "https://api.spiget.org/v2", options.userAgent); } async searchResources(query, { field, options = {} }) { return this.makeRequest(`/search/resources/${query}`, { field, ...options }); } async getStatus() { return this.makeRequest("/status"); } async getResources(options = {}) { return this.makeRequest("/resources", options); } async getPremiumResources(options = {}) { return this.makeRequest("/resources/premium", options); } async getFreeResources(options = {}) { return this.makeRequest("/resources/free", options); } async getNewResources(options = {}) { return this.makeRequest("/resources/new", options); } async getResourcesForVersion(version, method = "any" /* ANY */, options = {}) { const data = await this.makeRequest(`/resources/for/${version}`, { method, ...options }); return data.match; } async getResourcesForVersionDetailed(version, method = "any" /* ANY */, options = {}) { return this.makeRequest(`/resources/for/${version}`, { method, ...options }); } async getResource(resourceId) { return this.makeRequest(`/resources/${resourceId}`); } async getResourceAuthor(resourceId) { return this.makeRequest(`/resources/${resourceId}/author`); } async downloadResource(resourceId) { const resource = await this.getResource(resourceId); if (resource.external) { throw new SpigetError("EXTERNAL_FILE_DOWNLOAD" /* EXTERNAL_FILE_DOWNLOAD */, `Cannot download external resource ${resourceId}. Use externalUrl instead.`, { resourceId, externalUrl: resource.file.externalUrl }); } return this.makeRawRequest(`${this.baseUrl}/resources/${resourceId}/download`); } async getResourceVersions(resourceId, options = {}) { return this.makeRequest(`/resources/${resourceId}/versions`, options); } async getResourceVersion(resourceId, versionId) { return this.makeRequest(`/resources/${resourceId}/versions/${versionId}`); } async getResourceLatestVersion(resourceId) { return this.makeRequest(`/resources/${resourceId}/versions/latest`); } async downloadResourceVersion(resourceId, versionId) { const resource = await this.getResource(resourceId); if (resource.external) { throw new SpigetError("EXTERNAL_FILE_DOWNLOAD" /* EXTERNAL_FILE_DOWNLOAD */, `Cannot download external resource ${resourceId}. Use externalUrl instead.`, { resourceId, versionId, externalUrl: resource.file.externalUrl }); } return this.makeRawRequest(`${this.baseUrl}/resources/${resourceId}/versions/${versionId}/download`); } async getResourceUpdates(resourceId, options = {}) { return this.makeRequest(`/resources/${resourceId}/updates`, options); } async getResourceLatestUpdate(resourceId) { return this.makeRequest(`/resources/${resourceId}/updates/latest`); } async getResourceReviews(resourceId, options = {}) { return this.makeRequest(`/resources/${resourceId}/reviews`, options); } async getAuthors(options = {}) { return this.makeRequest("/authors", options); } async getAuthor(authorId) { return this.makeRequest(`/authors/${authorId}`); } async getAuthorResources(authorId, options = {}) { return this.makeRequest(`/authors/${authorId}/resources`, options); } async getAuthorReviews(authorId, options = {}) { return this.makeRequest(`/authors/${authorId}/reviews`, options); } async getCategories(options = {}) { return this.makeRequest("/categories", options); } async getCategory(categoryId) { return this.makeRequest(`/categories/${categoryId}`); } async getCategoryResources(categoryId, options = {}) { return this.makeRequest(`/categories/${categoryId}/resources`, options); } async searchAuthors(query, field = "name", options = {}) { return this.makeRequest(`/search/authors/${query}`, { field, ...options }); } handleError(error, defaultCode, defaultMessage) { if (error.response) { const status = error.response.status; if (status === 404) { throw new SpigetError("RESOURCE_NOT_FOUND" /* RESOURCE_NOT_FOUND */, "Resource not found", { status, spigetError: error }); } else if (status === 401) { throw new SpigetError("UNAUTHORIZED" /* UNAUTHORIZED */, "Unauthorized access", { status, spigetError: error }); } else if (status === 429) { throw new SpigetError("RATE_LIMIT_EXCEEDED" /* RATE_LIMIT_EXCEEDED */, "Rate limit exceeded", { status, spigetError: error }); } else if (status >= 400 && status < 500) { throw new SpigetError("INVALID_SEARCH_PARAMETERS" /* INVALID_SEARCH_PARAMETERS */, "Invalid request parameters", { status, spigetError: error }); } else if (status >= 500) { throw new SpigetError("API_REQUEST_FAILED" /* API_REQUEST_FAILED */, "Server error", { status, spigetError: error }); } } if (error.name === "NetworkError" || error.code === "ECONNRESET" || error.code === "ENOTFOUND") { throw new SpigetError("NETWORK_ERROR" /* NETWORK_ERROR */, "Network error occurred", { spigetError: error }); } throw new SpigetError(defaultCode, defaultMessage, { spigetError: error }); } } // src/types/modrinthType.ts var exports_modrinthType = {}; __export(exports_modrinthType, { ModrinthVersionType: () => ModrinthVersionType, ModrinthVersionStatus: () => ModrinthVersionStatus, ModrinthSortIndex: () => ModrinthSortIndex, ModrinthSideType: () => ModrinthSideType, ModrinthProjectType: () => ModrinthProjectType, ModrinthProjectStatus: () => ModrinthProjectStatus, ModrinthGameVersionType: () => ModrinthGameVersionType, ModrinthFacetType: () => ModrinthFacetType, ModrinthFacetOperation: () => ModrinthFacetOperation, ModrinthFacetGroup: () => ModrinthFacetGroup, ModrinthFacetBuilder: () => ModrinthFacetBuilder, ModrinthFacet: () => ModrinthFacet, ModrinthErrorCode: () => ModrinthErrorCode, ModrinthError: () => ModrinthError, ModrinthDependencyType: () => ModrinthDependencyType }); var ModrinthFacetType; ((ModrinthFacetType2) => { ModrinthFacetType2["PROJECT_TYPE"] = "project_type"; ModrinthFacetType2["CATEGORIES"] = "categories"; ModrinthFacetType2["VERSIONS"] = "versions"; ModrinthFacetType2["CLIENT_SIDE"] = "client_side"; ModrinthFacetType2["SERVER_SIDE"] = "server_side"; ModrinthFacetType2["OPEN_SOURCE"] = "open_source"; ModrinthFacetType2["TITLE"] = "title"; ModrinthFacetType2["AUTHOR"] = "author"; ModrinthFacetType2["FOLLOWS"] = "follows"; ModrinthFacetType2["PROJECT_ID"] = "project_id"; ModrinthFacetType2["LICENSE"] = "license"; ModrinthFacetType2["DOWNLOADS"] = "downloads"; ModrinthFacetType2["COLOR"] = "color"; ModrinthFacetType2["CREATED_TIMESTAMP"] = "created_timestamp"; ModrinthFacetType2["MODIFIED_TIMESTAMP"] = "modified_timestamp"; })(ModrinthFacetType ||= {}); var ModrinthFacetOperation; ((ModrinthFacetOperation2) => { ModrinthFacetOperation2["EQUAL"] = ":"; ModrinthFacetOperation2["NOT_EQUAL"] = "!="; ModrinthFacetOperation2["GREATER_THAN"] = ">"; ModrinthFacetOperation2["GREATER_THAN_OR_EQUAL"] = ">="; ModrinthFacetOperation2["LESS_THAN"] = "<"; ModrinthFacetOperation2["LESS_THAN_OR_EQUAL"] = "<="; })(ModrinthFacetOperation ||= {}); class ModrinthFacet { type; operation; value; constructor(type, operation, value) { this.type = type; this.operation = operation; this.value = value; } toString() { return `${this.type}${this.operation}${this.value}`; } static projectType(value) { return new ModrinthFacet("project_type" /* PROJECT_TYPE */, ":" /* EQUAL */, value); } static categories(value) { return new ModrinthFacet("categories" /* CATEGORIES */, ":" /* EQUAL */, value); } static versions(value) { return new ModrinthFacet("versions" /* VERSIONS */, ":" /* EQUAL */, value); } static clientSide(value) { return new ModrinthFacet("client_side" /* CLIENT_SIDE */, ":" /* EQUAL */, value); } static serverSide(value) { return new ModrinthFacet("server_side" /* SERVER_SIDE */, ":" /* EQUAL */, value); } static openSource(value) { return new ModrinthFacet("open_source" /* OPEN_SOURCE */, ":" /* EQUAL */, value); } static title(value) { return new ModrinthFacet("title" /* TITLE */, ":" /* EQUAL */, value); } static author(value) { return new ModrinthFacet("author" /* AUTHOR */, ":" /* EQUAL */, value); } static follows(operation, value) { return new ModrinthFacet("follows" /* FOLLOWS */, operation, value); } static projectId(value) { return new ModrinthFacet("project_id" /* PROJECT_ID */, ":" /* EQUAL */, value); } static license(value) { return new ModrinthFacet("license" /* LICENSE */, ":" /* EQUAL */, value); } static downloads(operation, value) { return new ModrinthFacet("downloads" /* DOWNLOADS */, operation, value); } static color(value) { return new ModrinthFacet("color" /* COLOR */, ":" /* EQUAL */, value); } static createdTimestamp(operation, value) { return new ModrinthFacet("created_timestamp" /* CREATED_TIMESTAMP */, operation, value); } static modifiedTimestamp(operation, value) { return new ModrinthFacet("modified_timestamp" /* MODIFIED_TIMESTAMP */, operation, value); } } class ModrinthFacetGroup { facets = []; constructor(facets = []) { this.facets = [...facets]; } addFacet(facet) { this.facets.push(facet); return this; } addFacets(facets) { this.facets.push(...facets); return this; } removeFacet(facet) { const index = this.facets.indexOf(facet); if (index > -1) { this.facets.splice(index, 1); } return this; } clear() { this.facets = []; return this; } getFacets() { return [...this.facets]; } isEmpty() { return this.facets.length === 0; } size() { return this.facets.length; } toArray() { return this.facets.map((facet) => facet.toString()); } toString() { return JSON.stringify(this.toArray()); } static projectTypes(types) { const group = new ModrinthFacetGroup; types.forEach((type) => group.addFacet(ModrinthFacet.projectType(type))); return group; } static categories(categories) { const group = new ModrinthFacetGroup; categories.forEach((category) => group.addFacet(ModrinthFacet.categories(category))); return group; } static versions(versions) { const group = new ModrinthFacetGroup; versions.forEach((version) => group.addFacet(ModrinthFacet.versions(version))); return group; } } class ModrinthFacetBuilder { groups = []; constructor() {} addGroup(group) { if (!group.isEmpty()) { this.groups.push(group); } return this; } addGroups(groups) { groups.forEach((group) => this.addGroup(group)); return this; } addFacet(facet) { const group = new ModrinthFacetGroup([facet]); this.groups.push(group); return this; } addFacets(facets) { facets.forEach((facet) => this.addFacet(facet)); return this; } removeGroup(group) { const index = this.groups.indexOf(group); if (index > -1) { this.groups.splice(index, 1); } return this; } clear() { this.groups = []; return this; } getGroups() { return [...this.groups]; } isEmpty() { return this.groups.length === 0; } size() { return this.groups.length; } build() { if (this.groups.length === 0) { return ""; } const facetArray = this.groups.map((group) => group.toArray()); return JSON.stringify(facetArray); } } var ModrinthErrorCode; ((ModrinthErrorCode2) => { ModrinthErrorCode2["RESOURCE_NOT_FOUND"] = "RESOURCE_NOT_FOUND"; ModrinthErrorCode2["INVALID_SEARCH_PARAMETERS"] = "INVALID_SEARCH_PARAMETERS"; ModrinthErrorCode2["API_REQUEST_FAILED"] = "API_REQUEST_FAILED"; ModrinthErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR"; ModrinthErrorCode2["INVALID_RESPONSE"] = "INVALID_RESPONSE"; ModrinthErrorCode2["RATE_LIMIT_EXCEEDED"] = "RATE_LIMIT_EXCEEDED"; ModrinthErrorCode2["UNAUTHORIZED"] = "UNAUTHORIZED"; ModrinthErrorCode2["INVALID_VERSION"] = "INVALID_VERSION"; ModrinthErrorCode2["DOWNLOAD_FAILED"] = "DOWNLOAD_FAILED"; ModrinthErrorCode2["EXTERNAL_FILE_DOWNLOAD"] = "EXTERNAL_FILE_DOWNLOAD"; })(ModrinthErrorCode ||= {}); class ModrinthError extends Error { code; details; constructor(code, message, details) { super(message); this.code = code; this.details = details; this.name = "ModrinthError"; } } var ModrinthProjectType; ((ModrinthProjectType2) => { ModrinthProjectType2["MOD"] = "mod"; ModrinthProjectType2["MODPACK"] = "modpack"; ModrinthProjectType2["RESOURCEPACK"] = "resourcepack"; ModrinthProjectType2["SHADER"] = "shader"; })(ModrinthProjectType ||= {}); var ModrinthVersionType; ((ModrinthVersionType2) => { ModrinthVersionType2["RELEASE"] = "release"; ModrinthVersionType2["BETA"] = "beta"; ModrinthVersionType2["ALPHA"] = "alpha"; })(ModrinthVersionType ||= {}); var ModrinthSideType; ((ModrinthSideType2) => { ModrinthSideType2["REQUIRED"] = "required"; ModrinthSideType2["OPTIONAL"] = "optional"; ModrinthSideType2["UNSUPPORTED"] = "unsupported"; ModrinthSideType2["UNKNOWN"] = "unknown"; })(ModrinthSideType ||= {}); var ModrinthDependencyType; ((ModrinthDependencyType2) => { ModrinthDependencyType2["REQUIRED"] = "required"; ModrinthDependencyType2["OPTIONAL"] = "optional"; ModrinthDependencyType2["INCOMPATIBLE"] = "incompatible"; ModrinthDependencyType2["EMBEDDED"] = "embedded"; })(ModrinthDependencyType ||= {}); var ModrinthProjectStatus; ((ModrinthProjectStatus2) => { ModrinthProjectStatus2["APPROVED"] = "approved"; ModrinthProjectStatus2["ARCHIVED"] = "archived"; ModrinthProjectStatus2["REJECTED"] = "rejected"; ModrinthProjectStatus2["DRAFT"] = "draft"; ModrinthProjectStatus2["UNLISTED"] = "unlisted"; ModrinthProjectStatus2["PROCESSING"] = "processing"; ModrinthProjectStatus2["WITHHELD"] = "withheld"; ModrinthProjectStatus2["SCHEDULED"] = "scheduled"; ModrinthProjectStatus2["PRIVATE"] = "private"; ModrinthProjectStatus2["UNKNOWN"] = "unknown"; })(ModrinthProjectStatus ||= {}); var ModrinthVersionStatus; ((ModrinthVersionStatus2) => { ModrinthVersionStatus2["LISTED"] = "listed"; ModrinthVersionStatus2["ARCHIVED"] = "archived"; ModrinthVersionStatus2["DRAFT"] = "draft"; ModrinthVersionStatus2["UNLISTED"] = "unlisted"; ModrinthVersionStatus2["SCHEDULED"] = "scheduled"; ModrinthVersionStatus2["UNKNOWN"] = "unknown"; })(ModrinthVersionStatus ||= {}); var ModrinthGameVersionType; ((ModrinthGameVersionType2) => { ModrinthGameVersionType2["RELEASE"] = "release"; ModrinthGameVersionType2["SNAPSHOT"] = "snapshot"; ModrinthGameVersionType2["ALPHA"] = "alpha"; ModrinthGameVersionType2["BETA"] = "beta"; })(ModrinthGameVersionType ||= {}); var ModrinthSortIndex; ((ModrinthSortIndex2) => { ModrinthSortIndex2["RELEVANCE"] = "relevance"; ModrinthSortIndex2["DOWNLOADS"] = "downloads"; ModrinthSortIndex2["FOLLOWS"] = "follows"; ModrinthSortIndex2["NEWEST"] = "newest"; ModrinthSortIndex2["UPDATED"] = "updated"; })(ModrinthSortIndex ||= {}); // src/api/modrinth.ts class ModrinthAPI extends BaseAPIClient { constructor(options = {}) { super(options.baseUrl || "https://api.modrinth.com/v2", options.userAgent || "shulkers/1.0.0"); } async searchProjects(options = {}) { const { query, facets, index = "relevance" /* RELEVANCE */, offset = 0, limit = 10 } = options; const params = { index, offset, limit }; if (query) params.query = query; if (facets) params.facets = this.processFacets(facets); return this.makeRequest("/search", params); } processFacets(facets) { if (typeof facets === "string") return facets; if (facets instanceof ModrinthFacetBuilder) return facets.build(); if (facets instanceof ModrinthFacetGroup) return JSON.stringify([facets.toArray()]); if (Array.isArray(facets)) return JSON.stringify([facets.map((f) => f.toString())]); return ""; } async getProject(id) { return this.makeRequest(`/project/${id}`); } async getProjects(ids) { return this.makeRequest("/projects", { ids }); } async getRandomProjects(count = 10) { if (count < 0 || count > 100) { throw new ModrinthError("INVALID_SEARCH_PARAMETERS" /* INVALID_SEARCH_PARAMETERS */, "Count must be between 0 and 100"); } return this.makeRequest("/projects_random", { count }); } async checkProjectValidity(id) { return this.makeRequest(`/project/${id}/check`); } async getProjectDependencies(id) { return this.makeRequest(`/project/${id}/dependencies`); } async getProjectTeamMembers(id) { return this.makeRequest(`/project/${id}/members`); } async getProjectVersions(id, options = {}) { return this.makeRequest(`/project/${id}/version`, options); } async getVersion(id) { return this.makeRequest(`/version/${id}`); } async getVersions(ids) { return this.makeRequest("/versions", { ids }); } async getVersionFromProject(projectId, versionId) { return this.makeRequest(`/project/${projectId}/version/${versionId}`); } async getVersionFromHash(hash, algorithm = "sha1", multiple = false) { return this.makeRequest(`/version_file/${hash}`, { algorithm, multiple }); } async getVersionsFromHashes(hashList) { return this.makePostRequest("/version_files", hashList); } async getLatestVersionFromHash(hash, algorithm = "sha1", updateInfo) { return this.makePostRequest(`/version_file/${hash}/update`, updateInfo, { algorithm }); } async getLatestVersionsFromHashes(updateInfo) { return this.makePostRequest("/version_files/update", updateInfo); } async getUser(id) { return this.makeRequest(`/user/${id}`); } async getUsers(ids) { return this.makeRequest("/users", { ids }); } async getUserProjects(id) { return this.makeRequest(`/user/${id}/projects`); } async getTeams(teamIds) { return this.makeRequest("/teams", { ids: teamIds }); } async getTeamMembers(teamId) { return this.makeRequest(`/team/${teamId}/members`); } async getCategories() { return this.makeRequest("/tag/category"); } async getLoaders() { return this.makeRequest("/tag/loader"); } async getGameVersions() { return this.makeRequest("/tag/game_version"); } async getProjectTypes() { return this.makeRequest("/tag/project_type"); } async getSideTypes() { return this.makeRequest("/tag/side_type"); } async getReportTypes() { return this.makeRequest("/tag/report_type"); } async getForgeUpdates(id) { return this.makeRequest(`/updates/${id}/forge_updates.json`); } async getStatistics() { return this.makeRequest("/statistics"); } async downloadFile(url) { return this.makeRawRequest(url); } async downloadVersionFile(versionId) { const version = await this.getVersion(versionId); const primaryFile = version.files.find((file) => file.primary) || version.files[0]; if (!primaryFile) { throw new ModrinthError("RESOURCE_NOT_FOUND" /* RESOURCE_NOT_FOUND */, `No files found for version ${versionId}`); } return this.downloadFile(primaryFile.url); } async downloadVersionFileByName(versionId, filename) { const version = await this.getVersion(versionId); const file = version.files.find((f) => f.filename === filename); if (!file) { throw new ModrinthError("RESOURCE_NOT_FOUND" /* RESOURCE_NOT_FOUND */, `File ${filename} not found in version ${versionId}`); } return this.downloadFile(file.url); } async downloadLatestProjectFile(projectId, options = {}) { const versions = await this.getProjectVersions(projectId, options); if (versions.length === 0) { throw new ModrinthError("RESOURCE_NOT_FOUND" /* RESOURCE_NOT_FOUND */, `No versions found for project ${projectId}`); } return this.downloadVersionFile(versions[0].id); } async getFileContent(url) { const response = await this.downloadFile(url); return response.arrayBuffer(); } async getFileBlob(url) { const response = await this.downloadFile(url); return response.blob(); } handleError(error, defaultCode, defaultMessage) { if (error.response) { const status = error.response.status; if (status === 404) { throw new ModrinthError("RESOURCE_NOT_FOUND" /* RESOURCE_NOT_FOUND */, "Resource not found", { status, modrinthError: error }); } else if (status === 401) { throw new ModrinthError("UNAUTHORIZED" /* UNAUTHORIZED */, "Unauthorized access", { status, modrinthError: error }); } else if (status === 429) { throw new ModrinthError("RATE_LIMIT_EXCEEDED" /* RATE_LIMIT_EXCEEDED */, "Rate limit exceeded", { status, modrinthError: error }); } else if (status >= 400 && status < 500) { throw new ModrinthError("INVALID_SEARCH_PARAMETERS" /* INVALID_SEARCH_PARAMETERS */, "Invalid request parameters", { status, modrinthError: error }); } else if (status >= 500) { throw new ModrinthError("API_REQUEST_FAILED" /* API_REQUEST_FAILED */, "Server error", { status, modrinthError: error }); } } if (error.name === "NetworkError" || error.code === "ECONNRESET" || error.code === "ENOTFOUND") { throw new ModrinthError("NETWORK_ERROR" /* NETWORK_ERROR */, "Network error occurred", { modrinthError: error }); } throw new ModrinthError(defaultCode, defaultMessage, { modrinthError: error }); } } // src/types/hangarType.ts var exports_hangarType = {}; __export(exports_hangarType, { HangarVisibility: () => HangarVisibility, HangarVersionChannel: () => HangarVersionChannel, HangarProjectSort: () => HangarProjectSort, HangarPlatform: () => HangarPlatform, HangarErrorCode: () => HangarErrorCode, HangarError: () => HangarError, HangarCategory: () => HangarCategory }); var HangarCategory; ((HangarCategory2) => { HangarCategory2["ADMIN_TOOLS"] = "admin_tools"; HangarCategory2["CHAT"] = "chat"; HangarCategory2["DEV_TOOLS"] = "dev_tools"; HangarCategory2["ECONOMY"] = "economy"; HangarCategory2["GAMEPLAY"] = "gameplay"; HangarCategory2["GAMES"] = "games"; HangarCategory2["PROTECTION"] = "protection"; HangarCategory2["ROLE_PLAYING"] = "role_playing"; HangarCategory2["WORLD_MANAGEMENT"] = "world_management"; HangarCategory2["MISC"] = "misc"; })(HangarCategory ||= {}); var HangarPlatform; ((HangarPlatform2) => { HangarPlatform2["PAPER"] = "PAPER"; HangarPlatform2["VELOCITY"] = "VELOCITY"; HangarPlatform2["WATERFALL"] = "WATERFALL"; })(HangarPlatform ||= {}); var HangarVisibility; ((HangarVisibility2) => { HangarVisibility2["PUBLIC"] = "public"; HangarVisibility2["UNLISTED"] = "unlisted"; HangarVisibility2["PRIVATE"] = "private"; })(HangarVisibility ||= {}); var HangarProjectSort; ((HangarProjectSort2) => { HangarProjectSort2["VIEWS"] = "views"; HangarProjectSort2["DOWNLOADS"] = "downloads"; HangarProjectSort2["NEWEST"] = "newest"; HangarProjectSort2["STARS"] = "stars"; HangarProjectSort2["UPDATED"] = "updated"; HangarProjectSort2["RECENT_DOWNLOADS"] = "recent_downloads"; HangarProjectSort2["RECENT_VIEWS"] = "recent_views"; })(HangarProjectSort ||= {}); var HangarVersionChannel; ((HangarVersionChannel2) => { HangarVersionChannel2["RELEASE"] = "Release"; HangarVersionChannel2["SNAPSHOT"] = "Snapshot"; HangarVersionChannel2["ALPHA"] = "Alpha"; HangarVersionChannel2["BETA"] = "Beta"; })(HangarVersionChannel ||= {}); var HangarErrorCode; ((HangarErrorCode2) => { HangarErrorCode2["API_REQUEST_FAILED"] = "API_REQUEST_FAILED"; HangarErrorCode2["RESOURCE_NOT_FOUND"] = "RESOURCE_NOT_FOUND"; HangarErrorCode2["UNAUTHORIZED"] = "UNAUTHORIZED"; HangarErrorCode2["RATE_LIMIT_EXCEEDED"] = "RATE_LIMIT_EXCEEDED"; HangarErrorCode2["INVALID_SEARCH_PARAMETERS"] = "INVALID_SEARCH_PARAMETERS"; HangarErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR"; HangarErrorCode2["DOWNLOAD_FAILED"] = "DOWNLOAD_FAILED"; HangarErrorCode2["VALIDATION_ERROR"] = "VALIDATION_ERROR"; })(HangarErrorCode ||= {}); class HangarError extends Error { code; context; constructor(code, message, context) { super(message); this.name = "HangarError"; this.code = code; this.context = context; if (Error.captureStackTrace) { Error.captureStackTrace(this, HangarError); } } toJSON() { return { name: this.name, code: this.code, message: this.message, context: this.context }; } } // src/api/hangar.ts class HangarAPI extends BaseAPIClient { constructor(options = {}) { super(options.baseUrl || "https://hangar.papermc.io/api/v1", options.userAgent || "shulkers/1.0.0"); } async searchProjects(options = {}) { const params = {}; if (options.q) params.q = options.q; if (options.owner) params.owner = options.owner; if (options.sort) params.sort = options.sort; if (options.limit !== undefined) params.limit = Math.min(options.limit, 25); if (options.offset !== undefined) params.offset = options.offset; if (options.category) { if (Array.isArray(options.category)) { params.category = options.category; } else { params.category = [options.category]; } } if (options.platform) { if (Array.isArray(options.platform)) { params.platform = options.platform; } else { params.platform = [options.platform]; } } return this.makeRequest("/projects", params); } async getProject(slugOrId) { return this.makeRequest(`/projects/${slugOrId}`); } async getUser(username) { retu