"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RegistryResponseError = void 0;
const core_loggers_1 = require("@pnpm/core-loggers");
const error_1 = __importStar(require("@pnpm/error"));
const retry = __importStar(require("@zkochan/retry"));
const url = require("url");
// https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
const semvarRegex = new RegExp(/(.*)(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/);
class RegistryResponseError extends error_1.FetchError {
    constructor(request, response, pkgName) {
        let hint;
        if (response.status === 404) {
            hint = `${pkgName} is not in the npm registry, or you have no permission to fetch it.`;
            const matched = pkgName.match(semvarRegex);
            if (matched) {
                hint += ` Did you mean ${matched[1]}?`;
            }
        }
        super(request, response, hint);
        this.pkgName = pkgName;
    }
}
exports.RegistryResponseError = RegistryResponseError;
async function fromRegistry(fetch, retryOpts, pkgName, registry, authHeaderValue) {
    const uri = toUri(pkgName, registry);
    const op = retry.operation(retryOpts);
    return new Promise((resolve, reject) => op.attempt(async (attempt) => {
        const response = await fetch(uri, { authHeaderValue, retry: retryOpts });
        if (response.status > 400) {
            const request = {
                authHeaderValue,
                url: uri,
            };
            reject(new RegistryResponseError(request, response, pkgName));
            return;
        }
        // Here we only retry broken JSON responses.
        // Other HTTP issues are retried by the @pnpm/fetch library
        try {
            resolve(await response.json());
        }
        catch (error) {
            const timeout = op.retry(new error_1.default('BROKEN_METADATA_JSON', error.message));
            if (timeout === false) {
                reject(op.mainError());
                return;
            }
            core_loggers_1.requestRetryLogger.debug({
                attempt,
                error,
                maxRetries: retryOpts.retries,
                method: 'GET',
                timeout,
                url: uri,
            });
        }
    }));
}
exports.default = fromRegistry;
function toUri(pkgName, registry) {
    let encodedName;
    if (pkgName[0] === '@') {
        encodedName = `@${encodeURIComponent(pkgName.substr(1))}`;
    }
    else {
        encodedName = encodeURIComponent(pkgName);
    }
    return url.resolve(registry, encodedName);
}
//# sourceMappingURL=fetch.js.map