UNPKG

libmodpm

Version:

Modrinth package manager library

291 lines 19.6 kB
// SPDX-License-Identifier: GPL-3.0-or-later import { HTTPClient } from "../HTTPClient.js"; /** * Represents an error returned by the registry API. * * @final */ class RegistryError extends Error { /** * Error code. */ code; /** * Creates a new registry error. * * @param description Error description. * @param [code] Error code. */ constructor(description, code) { super(description); this.name = new.target.name; this.code = code; } } /** * Provides methods for interacting with the registry via its HTTP API. * * @final */ export class RegistryClient extends HTTPClient { static RegistryError = RegistryError; /** * Base URL for HTTP requests. */ baseUrl; /** * Creates a new registry client. * * @param userAgent User agent string used when making requests to the registry. * @param [token] API authentication token. * @param [baseUrl=new URL("https://api.modrinth.com/v2/")] API authentication token. Requires the following scopes: * - `PROJECT_READ` * - `VERSION_READ` * * Authentication is only needed for accessing private/draft packages and their versions. */ constructor(userAgent, token, baseUrl = new URL("https://api.modrinth.com/v2/")) { super(userAgent, token); this.baseUrl = baseUrl; } /** * Catches 404 errors and returns null. * * @param error Error to try to catch. * @returns `null` if the error is a 404 error. * @throws {@link RegistryClient.RegistryError} If the error is not a 404 error. */ static catch404(error) { if (error instanceof RegistryClient.RegistryError && error.message === "404") return null; throw error; } /** * Retrieves the package associated with the specified ID. * * @param id Package ID. * @returns `null` if the package is not found. * @throws {@link RegistryClient.RegistryError} If the request fails. * @throws {@link !TypeError} If fetching fails. */ async getPackage(id) { return this.fetch(["project", id]) .then(res => res.json()) .catch(RegistryClient.catch404); } /** * Retrieves the ID of the package associated with the specified slug. * * This method returns the ID even for private/draft packages, without requiring authentication. * * @param slug Package slug. * @returns `null` if the package is not found. * @throws {@link RegistryClient.RegistryError} If the request fails. * @throws {@link !TypeError} If fetching fails. */ async getPackageId(slug) { return this.fetch(["project", slug, "check"]) .then(res => res.json()) .then(json => json.id) .catch(RegistryClient.catch404); } /** * Retrieves packages matching the specified search query and facet filters. * * @param [query] Search query. * @param [facets] Facets to filter by. * @param [sort] Sort order. * @param [offset] Offset into the search. * @param [limit] Maximum number of results to return. * * @see https://docs.modrinth.com/api/operations/searchprojects/ Search projects | Modrinth Documentation */ async search(query, facets, sort, offset, limit = 20) { const queryParams = new URLSearchParams(); if (query !== undefined) queryParams.set("query", query); if (facets !== undefined) queryParams.set("facets", JSON.stringify(facets)); if (sort !== undefined) queryParams.set("sort", sort); if (offset !== undefined) queryParams.set("offset", offset.toString()); queryParams.set("limit", limit.toString()); const body = await this.fetch(["search"], {}, queryParams).then(res => res.text()); return JSON.parse(body, (_, value) => { if (typeof value === "string" && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$/.test(value)) return new Date(value); return value; }); } /** * Retrieves the version associated with the specified version ID. * * @param id Version ID. * @returns `null` if the version is not found. * @throws {@link RegistryClient.RegistryError} If the request fails. * @throws {@link !TypeError} If fetching fails. */ async getVersion(id) { return this.fetch(["version", id]) .then(res => res.json()) .catch(RegistryClient.catch404); } /** * Retrieves the versions associated with the specified package. * * Filters: * - `loaders` — restricts versions to those compatible with the specified loaders. * - `game_versions` — restricts versions to those compatible with the specified game versions. * - `version_type` — restricts versions to those of the specified type (release channel). * * @param pkg Package ID. * @param [filters] Filters to apply. * @returns List of matching versions, sorted in descending order, with the latest version first. * @throws {@link RegistryClient.RegistryError} If the request fails. * @throws {@link !TypeError} If fetching fails. */ async listVersions(pkg, filters = {}) { const params = new URLSearchParams(); if (filters.loaders !== undefined) params.append("loaders", JSON.stringify(filters.loaders)); if (filters.game_versions !== undefined) params.append("game_versions", JSON.stringify(filters.game_versions)); if (filters.version_type !== undefined) params.append("version_type", JSON.stringify(filters.version_type)); return this.fetch(["project", pkg, "version"], {}, params) .then(res => res.json()) .catch(RegistryClient.catch404); } /** * Retrieves the packages associated with the specified IDs. * * @param ids Package IDs. * @throws {@link RegistryClient.RegistryError} If the request fails. * @throws {@link !TypeError} If fetching fails. */ async getPackages(ids) { return this.fetch(["projects"], {}, new URLSearchParams({ ids: JSON.stringify(ids), })).then(res => res.json()); } /** * Retrieves the version associated with the specified file hash. * * @param hash SHA-512 hash of the file, encoded as a hex string. * @returns `null` if the version is not found. * @throws {@link RegistryClient.RegistryError} If the request fails. * @throws {@link !TypeError} If fetching fails. */ async getVersionByHash(hash) { return this.fetch(["version_file", hash], {}, new URLSearchParams({ algorithm: "sha512", })) .then(res => res.json()) .catch(RegistryClient.catch404); } /** * Retrieves the versions associated with the specified hashes. * * @param hashes SHA-512 hashes of the files, encoded as a hex strings. * @throws {@link RegistryClient.RegistryError} If the request fails. * @throws {@link !TypeError} If fetching fails. */ async getVersionsByHashes(hashes) { return this.fetch(["version_files"], { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ hashes, algorithm: "sha512", }), }).then(res => res.json()) .then((json) => Object.values(json)); } /** * Retrieves the versions associated with the specified version IDs. * * @param ids Version IDs. * @throws {@link RegistryClient.RegistryError} If the request fails. * @throws {@link !TypeError} If fetching fails. */ async getVersions(ids) { return this.fetch(["versions"], {}, new URLSearchParams({ ids: JSON.stringify(ids), })).then(res => res.json()); } /** * Retrieves the version associated with the specified package and version number or ID. * * @param pkg Package ID. * @param version Version number or ID. * @returns `null` if the package or version is not found. * @throws {@link RegistryClient.RegistryError} If the request fails. * @throws {@link !TypeError} If fetching fails. */ async getVersionByNumber(pkg, version) { return this.fetch(["project", pkg, "version", version]) .then(res => res.json()) .catch(err => { if (err instanceof RegistryClient.RegistryError && err.message === "404") return null; throw err; }); } /** * Retrieves the latest versions associated with the specified hashes. * * Filters: * - `loaders` — restricts versions to those compatible with the specified loaders. * - `game_versions` — restricts versions to those compatible with the specified game versions. * - `version_type` — restricts versions to those of the specified type (release channel). * * @param hashes SHA-512 hashes of the files, encoded as a hex strings. * @param [filters] Filters to apply. * @throws {@link RegistryClient.RegistryError} If the request fails. * @throws {@link !TypeError} If fetching fails. */ async getLatestVersions(hashes, filters = {}) { return this.fetch(["version_files", "update"], { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ hashes, algorithm: "sha512", loaders: filters.loaders, game_versions: filters.game_versions, version_type: filters.version_type, }), }).then(res => res.json()); } /** * Retrieves all loaders supported by the registry. * * @throws {@link RegistryClient.RegistryError} If the request fails. * @throws {@link !TypeError} If fetching fails. */ async getLoaders() { return this.fetch(["tag", "loader"]) .then(res => res.json()) .then((json) => json.map((l) => l.name)); } async createError(res) { if ((res.headers.get("Content-Type")?.startsWith("application/json") ?? false) && res.body !== null) { const { description, error } = await res.json(); return new RegistryError(description, error); } return new RegistryError(res.status.toString()); } // only adding an overload… sorry @final 😔 async fetch(url, options, query, retries = 3, remainingRetries = retries) { if (Array.isArray(url)) return super.fetch(new URL(url.map(globalThis.encodeURIComponent).join("/"), this.baseUrl), options, query, remainingRetries); return super.fetch(url, options, query, remainingRetries); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVnaXN0cnlDbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcmVnaXN0cnkvUmVnaXN0cnlDbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsNENBQTRDO0FBQzVDLE9BQU8sRUFBQyxVQUFVLEVBQUMsTUFBTSxrQkFBa0IsQ0FBQztBQVE1Qzs7OztHQUlHO0FBQ0gsTUFBTSxhQUFjLFNBQVEsS0FBSztJQUM3Qjs7T0FFRztJQUNhLElBQUksQ0FBVTtJQUU5Qjs7Ozs7T0FLRztJQUNILFlBQW1CLFdBQW1CLEVBQUUsSUFBYTtRQUNqRCxLQUFLLENBQUMsV0FBVyxDQUFDLENBQUM7UUFDbkIsSUFBSSxDQUFDLElBQUksR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQztRQUM1QixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztJQUNyQixDQUFDO0NBQ0o7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxPQUFPLGNBQWUsU0FBUSxVQUF5QjtJQUNsRCxNQUFNLENBQVUsYUFBYSxHQUFHLGFBQWEsQ0FBQztJQUVyRDs7T0FFRztJQUNjLE9BQU8sQ0FBTTtJQUU5Qjs7Ozs7Ozs7OztPQVVHO0lBQ0gsWUFBbUIsU0FBaUIsRUFBRSxLQUFjLEVBQUUsT0FBTyxHQUFHLElBQUksR0FBRyxDQUFDLDhCQUE4QixDQUFDO1FBQ25HLEtBQUssQ0FBQyxTQUFTLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDeEIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7SUFDM0IsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNJLE1BQU0sQ0FBQyxRQUFRLENBQUMsS0FBWTtRQUMvQixJQUFJLEtBQUssWUFBWSxjQUFjLENBQUMsYUFBYSxJQUFJLEtBQUssQ0FBQyxPQUFPLEtBQUssS0FBSztZQUN4RSxPQUFPLElBQUksQ0FBQztRQUNoQixNQUFNLEtBQUssQ0FBQztJQUNoQixDQUFDO0lBRUQ7Ozs7Ozs7T0FPRztJQUNJLEtBQUssQ0FBQyxVQUFVLENBQUMsRUFBVTtRQUM5QixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxTQUFTLEVBQUUsRUFBRSxDQUFDLENBQUM7YUFDN0IsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDO2FBQ3ZCLEtBQUssQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDeEMsQ0FBQztJQUVEOzs7Ozs7Ozs7T0FTRztJQUNJLEtBQUssQ0FBQyxZQUFZLENBQUMsSUFBWTtRQUNsQyxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxTQUFTLEVBQUUsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDO2FBQ3hDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQzthQUN2QixJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO2FBQ3JCLEtBQUssQ0FBQyxjQUFjLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDeEMsQ0FBQztJQUVEOzs7Ozs7Ozs7O09BVUc7SUFDSSxLQUFLLENBQUMsTUFBTSxDQUNmLEtBQWMsRUFDZCxNQUE0RCxFQUM1RCxJQUF5QixFQUN6QixNQUFlLEVBQ2YsUUFBZ0IsRUFBRTtRQUVsQixNQUFNLFdBQVcsR0FBRyxJQUFJLGVBQWUsRUFBRSxDQUFDO1FBQzFDLElBQUksS0FBSyxLQUFLLFNBQVM7WUFBRSxXQUFXLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxLQUFLLENBQUMsQ0FBQztRQUN6RCxJQUFJLE1BQU0sS0FBSyxTQUFTO1lBQUUsV0FBVyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQzVFLElBQUksSUFBSSxLQUFLLFNBQVM7WUFBRSxXQUFXLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsQ0FBQztRQUN0RCxJQUFJLE1BQU0sS0FBSyxTQUFTO1lBQUUsV0FBVyxDQUFDLEdBQUcsQ0FBQyxRQUFRLEVBQUUsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUM7UUFDdkUsV0FBVyxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsS0FBSyxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUM7UUFDM0MsTUFBTSxJQUFJLEdBQUcsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsUUFBUSxDQUFDLEVBQUUsRUFBRSxFQUFFLFdBQVcsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO1FBQ25GLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDLEVBQUUsS0FBSyxFQUFFLEVBQUU7WUFDakMsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksOENBQThDLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQztnQkFDdkYsT0FBTyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztZQUMzQixPQUFPLEtBQUssQ0FBQztRQUNqQixDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFRDs7Ozs7OztPQU9HO0lBQ0ksS0FBSyxDQUFDLFVBQVUsQ0FBQyxFQUFVO1FBQzlCLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLFNBQVMsRUFBRSxFQUFFLENBQUMsQ0FBQzthQUM3QixJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUM7YUFDdkIsS0FBSyxDQUFDLGNBQWMsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUN4QyxDQUFDO0lBRUQ7Ozs7Ozs7Ozs7Ozs7T0FhRztJQUNJLEtBQUssQ0FBQyxZQUFZLENBQ3JCLEdBQVcsRUFDWCxVQUlJLEVBQUU7UUFFTixNQUFNLE1BQU0sR0FBRyxJQUFJLGVBQWUsRUFBRSxDQUFDO1FBQ3JDLElBQUksT0FBTyxDQUFDLE9BQU8sS0FBSyxTQUFTO1lBQzdCLE1BQU0sQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7UUFDOUQsSUFBSSxPQUFPLENBQUMsYUFBYSxLQUFLLFNBQVM7WUFDbkMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxlQUFlLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQztRQUMxRSxJQUFJLE9BQU8sQ0FBQyxZQUFZLEtBQUssU0FBUztZQUNsQyxNQUFNLENBQUMsTUFBTSxDQUFDLGNBQWMsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDO1FBRXhFLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLFNBQVMsRUFBRSxHQUFHLEVBQUUsU0FBUyxDQUFDLEVBQUUsRUFBRSxFQUFFLE1BQU0sQ0FBQzthQUNyRCxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUM7YUFDdkIsS0FBSyxDQUFDLGNBQWMsQ0FBQyxRQUFRLENBQUMsQ0FBQztJQUN4QyxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksS0FBSyxDQUFDLFdBQVcsQ0FBQyxHQUFhO1FBQ2xDLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLFVBQVUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxJQUFJLGVBQWUsQ0FBQztZQUNwRCxHQUFHLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUM7U0FDM0IsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7SUFDaEMsQ0FBQztJQUVEOzs7Ozs7O09BT0c7SUFDSSxLQUFLLENBQUMsZ0JBQWdCLENBQUMsSUFBWTtRQUN0QyxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDLEVBQUUsRUFBRSxFQUFFLElBQUksZUFBZSxDQUFDO1lBQzlELFNBQVMsRUFBRSxRQUFRO1NBQ3RCLENBQUMsQ0FBQzthQUNFLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQzthQUN2QixLQUFLLENBQUMsY0FBYyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQ3hDLENBQUM7SUFFRDs7Ozs7O09BTUc7SUFDSSxLQUFLLENBQUMsbUJBQW1CLENBQUMsTUFBZ0I7UUFDN0MsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsZUFBZSxDQUFDLEVBQUU7WUFDakMsTUFBTSxFQUFFLE1BQU07WUFDZCxPQUFPLEVBQUU7Z0JBQ0wsY0FBYyxFQUFFLGtCQUFrQjthQUNyQztZQUNELElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDO2dCQUNqQixNQUFNO2dCQUNOLFNBQVMsRUFBRSxRQUFRO2FBQ3RCLENBQUM7U0FDTCxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDO2FBQ3JCLElBQUksQ0FBQyxDQUFDLElBQXFDLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUM5RSxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksS0FBSyxDQUFDLFdBQVcsQ0FBQyxHQUFhO1FBQ2xDLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLFVBQVUsQ0FBQyxFQUFFLEVBQUUsRUFBRSxJQUFJLGVBQWUsQ0FBQztZQUNwRCxHQUFHLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUM7U0FDM0IsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7SUFDaEMsQ0FBQztJQUVEOzs7Ozs7OztPQVFHO0lBQ0ksS0FBSyxDQUFDLGtCQUFrQixDQUFDLEdBQVcsRUFBRSxPQUFlO1FBQ3hELE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLFNBQVMsRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLE9BQU8sQ0FBQyxDQUFDO2FBQ2xELElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQzthQUN2QixLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUU7WUFDVCxJQUFJLEdBQUcsWUFBWSxjQUFjLENBQUMsYUFBYSxJQUFJLEdBQUcsQ0FBQyxPQUFPLEtBQUssS0FBSztnQkFDcEUsT0FBTyxJQUFJLENBQUM7WUFDaEIsTUFBTSxHQUFHLENBQUM7UUFDZCxDQUFDLENBQUMsQ0FBQztJQUNYLENBQUM7SUFFRDs7Ozs7Ozs7Ozs7O09BWUc7SUFDSSxLQUFLLENBQUMsaUJBQWlCLENBQUMsTUFBZ0IsRUFBRSxVQUk3QyxFQUFFO1FBQ0YsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsZUFBZSxFQUFFLFFBQVEsQ0FBQyxFQUFFO1lBQzNDLE1BQU0sRUFBRSxNQUFNO1lBQ2QsT0FBTyxFQUFFO2dCQUNMLGNBQWMsRUFBRSxrQkFBa0I7YUFDckM7WUFDRCxJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQztnQkFDakIsTUFBTTtnQkFDTixTQUFTLEVBQUUsUUFBUTtnQkFDbkIsT0FBTyxFQUFFLE9BQU8sQ0FBQyxPQUFPO2dCQUN4QixhQUFhLEVBQUUsT0FBTyxDQUFDLGFBQWE7Z0JBQ3BDLFlBQVksRUFBRSxPQUFPLENBQUMsWUFBWTthQUNyQyxDQUFDO1NBQ0wsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQy9CLENBQUM7SUFFRDs7Ozs7T0FLRztJQUNJLEtBQUssQ0FBQyxVQUFVO1FBQ25CLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssRUFBRSxRQUFRLENBQUMsQ0FBQzthQUMvQixJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUM7YUFDdkIsSUFBSSxDQUFDLENBQUMsSUFBVyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBaUIsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDeEUsQ0FBQztJQUVlLEtBQUssQ0FBQyxXQUFXLENBQUMsR0FBYTtRQUMzQyxJQUNJLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLEVBQUUsVUFBVSxDQUFDLGtCQUFrQixDQUFDLElBQUksS0FBSyxDQUFDO2VBQ3ZFLEdBQUcsQ0FBQyxJQUFJLEtBQUssSUFBSSxFQUN0QixDQUFDO1lBQ0MsTUFBTSxFQUFDLFdBQVcsRUFBRSxLQUFLLEVBQUMsR0FBRyxNQUFNLEdBQUcsQ0FBQyxJQUFJLEVBQTBDLENBQUM7WUFDdEYsT0FBTyxJQUFJLGFBQWEsQ0FBQyxXQUFXLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDakQsQ0FBQztRQUNELE9BQU8sSUFBSSxhQUFhLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxRQUFRLEVBQUUsQ0FBQyxDQUFDO0lBQ3BELENBQUM7SUEwQ0QsMkNBQTJDO0lBQ3hCLEtBQUssQ0FBQyxLQUFLLENBQzFCLEdBQTRCLEVBQzVCLE9BQXFCLEVBQ3JCLEtBQXVCLEVBQ3ZCLE9BQU8sR0FBRyxDQUFDLEVBQ1gsZ0JBQWdCLEdBQUcsT0FBTztRQUUxQixJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDO1lBQ2xCLE9BQU8sS0FBSyxDQUFDLEtBQUssQ0FDZCxJQUFJLEdBQUcsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLEVBQ3ZFLE9BQU8sRUFDUCxLQUFLLEVBQ0wsZ0JBQWdCLENBQ25CLENBQUM7UUFDTixPQUFPLEtBQUssQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztJQUM5RCxDQUFDIn0=