"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.s3Mount = s3Mount;
exports.gitMount = gitMount;
exports.gcsMount = gcsMount;
exports.mountConfig = mountConfig;
exports.validateMountConfigProxyConfig = validateMountConfigProxyConfig;
function requireNonEmptyString(value, field) {
    if (typeof value !== "string" || value.trim() === "") {
        throw new Error(`${field} must be a non-empty string`);
    }
    return value.trim();
}
function copyMountSecret(secret, field) {
    if (secret === null || typeof secret !== "object" || Array.isArray(secret)) {
        throw new Error(`${field} must be a sandbox secret`);
    }
    const candidate = secret;
    if (candidate.type !== "workspace_secret" && candidate.type !== "opaque") {
        throw new Error(`${field} must use workspace_secret or opaque`);
    }
    if (typeof candidate.value !== "string" || candidate.value.trim() === "") {
        throw new Error(`${field}.value must be a non-empty string`);
    }
    return {
        type: candidate.type,
        value: candidate.value,
    };
}
function requireGitRemoteUrl(remoteUrl) {
    if (typeof remoteUrl !== "string" || remoteUrl === "") {
        throw new Error("remoteUrl must be a non-empty string");
    }
    if (remoteUrl.trim() !== remoteUrl ||
        /\s/u.test(remoteUrl) ||
        remoteUrl.includes(String.fromCharCode(0))) {
        throw new Error("remoteUrl must not contain whitespace or NUL bytes");
    }
    let parsed;
    try {
        parsed = new URL(remoteUrl);
    }
    catch {
        throw new Error("remoteUrl must be an absolute HTTPS URL");
    }
    if (parsed.protocol !== "https:" || parsed.host === "") {
        throw new Error("remoteUrl must be an absolute HTTPS URL");
    }
    if (parsed.username !== "" || parsed.password !== "") {
        throw new Error("remoteUrl must not include embedded credentials");
    }
    if (parsed.pathname === "" || parsed.pathname === "/") {
        throw new Error("remoteUrl must include a repository path");
    }
    if (parsed.search !== "" || parsed.hash !== "") {
        throw new Error("remoteUrl must not include query or fragment");
    }
    return remoteUrl;
}
function copyGitRef(ref) {
    if (ref === null || typeof ref !== "object" || Array.isArray(ref)) {
        throw new Error("ref must be an object");
    }
    if (ref.type !== "branch" && ref.type !== "tag") {
        throw new Error("ref.type must be branch or tag");
    }
    return {
        type: ref.type,
        name: requireNonEmptyString(ref.name, "ref.name"),
    };
}
function s3Mount({ id, mountPath, bucket, region = "us-east-1", prefix, endpointUrl = "https://s3.amazonaws.com", pathStyle = false, readOnly, cache, }) {
    const mount = {
        id: requireNonEmptyString(id, "id"),
        type: "s3",
        mount_path: requireNonEmptyString(mountPath, "mountPath"),
        s3: {
            endpoint_url: requireNonEmptyString(endpointUrl, "endpointUrl"),
            region: requireNonEmptyString(region, "region"),
            bucket: requireNonEmptyString(bucket, "bucket"),
            path_style: pathStyle,
        },
    };
    if (prefix !== undefined) {
        mount.s3.prefix = requireNonEmptyString(prefix, "prefix");
    }
    if (readOnly !== undefined) {
        mount.read_only = readOnly;
    }
    if (cache !== undefined) {
        mount.cache = { ...cache };
    }
    return mount;
}
function gitMount({ id, mountPath, remoteUrl, ref, refreshIntervalSeconds, }) {
    const mount = {
        id: requireNonEmptyString(id, "id"),
        type: "git",
        mount_path: requireNonEmptyString(mountPath, "mountPath"),
        git: {
            remote_url: requireGitRemoteUrl(remoteUrl),
        },
    };
    if (ref !== undefined) {
        mount.git.ref = copyGitRef(ref);
    }
    if (refreshIntervalSeconds !== undefined) {
        if (refreshIntervalSeconds < 1) {
            throw new Error("refreshIntervalSeconds must be at least 1");
        }
        mount.git.refresh_interval_seconds = refreshIntervalSeconds;
    }
    return mount;
}
function gcsMount({ id, mountPath, bucket, prefix, readOnly, cache, }) {
    const mount = {
        id: requireNonEmptyString(id, "id"),
        type: "gcs",
        mount_path: requireNonEmptyString(mountPath, "mountPath"),
        gcs: {
            bucket: requireNonEmptyString(bucket, "bucket"),
        },
    };
    if (prefix !== undefined) {
        mount.gcs.prefix = requireNonEmptyString(prefix, "prefix");
    }
    if (readOnly !== undefined) {
        mount.read_only = readOnly;
    }
    if (cache !== undefined) {
        mount.cache = { ...cache };
    }
    return mount;
}
function normalizeMounts(mounts) {
    if (!Array.isArray(mounts) || mounts.length === 0) {
        throw new Error("mounts must be a non-empty array of mount objects");
    }
    return mounts.map((mount) => {
        if (mount === null || typeof mount !== "object" || Array.isArray(mount)) {
            throw new Error("mounts must be a non-empty array of mount objects");
        }
        if (mount.type !== "s3" && mount.type !== "gcs" && mount.type !== "git") {
            throw new Error("mountConfig only supports s3, gcs, and git mounts");
        }
        rejectProviderCredentialsInMount(mount);
        return mount;
    });
}
const FORBIDDEN_MOUNT_CREDENTIAL_FIELDS = new Set([
    "access_key_id",
    "secret_access_key",
    "session_token",
    "service_account_json",
    "access_token",
    "refresh_token",
    "credentials",
    "credential",
]);
function rejectProviderCredentialsInMount(value) {
    if (Array.isArray(value)) {
        for (const child of value) {
            rejectProviderCredentialsInMount(child);
        }
        return;
    }
    if (value !== null && typeof value === "object") {
        for (const [key, child] of Object.entries(value)) {
            if (FORBIDDEN_MOUNT_CREDENTIAL_FIELDS.has(key)) {
                throw new Error("provider credentials must be supplied in mountConfig.auth, not individual mount specs");
            }
            rejectProviderCredentialsInMount(child);
        }
    }
}
function normalizeMountAuth(auth) {
    if (!Array.isArray(auth)) {
        throw new Error("auth must be an array of provider auth blocks");
    }
    const byProvider = {};
    for (const block of auth) {
        if (block === null || typeof block !== "object" || Array.isArray(block)) {
            throw new Error("auth must be an array of provider auth blocks");
        }
        const provider = block.type;
        if (provider !== "aws" && provider !== "gcp") {
            throw new Error("mountConfig auth only supports aws and gcp blocks");
        }
        if (byProvider[provider] !== undefined) {
            throw new Error(`duplicate ${provider} auth rule in mountConfig`);
        }
        if (provider === "aws") {
            const aws = block.aws;
            if (aws === undefined) {
                throw new Error("aws mount auth must include an aws block");
            }
            byProvider.aws = {
                access_key_id: copyMountSecret(aws.access_key_id, "accessKeyId"),
                secret_access_key: copyMountSecret(aws.secret_access_key, "secretAccessKey"),
            };
        }
        else {
            const gcp = block.gcp;
            if (gcp === undefined) {
                throw new Error("gcp mount auth must include a gcp block");
            }
            byProvider.gcp = {
                service_account_json: copyMountSecret(gcp.service_account_json, "serviceAccountJson"),
            };
        }
    }
    return byProvider;
}
function mountConfig({ auth = [], mounts, }) {
    const normalizedMounts = normalizeMounts(mounts);
    const authByProvider = normalizeMountAuth(auth);
    const mountProviders = new Set(normalizedMounts.map((mount) => mount.type));
    if (mountProviders.has("s3") && authByProvider.aws === undefined) {
        throw new Error("s3 mounts require aws auth in mountConfig");
    }
    if (mountProviders.has("gcs") && authByProvider.gcp === undefined) {
        throw new Error("gcs mounts require gcp auth in mountConfig");
    }
    if (authByProvider.aws !== undefined && !mountProviders.has("s3")) {
        throw new Error("aws auth requires at least one s3 mount in mountConfig");
    }
    if (authByProvider.gcp !== undefined && !mountProviders.has("gcs")) {
        throw new Error("gcp auth requires at least one gcs mount in mountConfig");
    }
    return {
        auth: authByProvider,
        mounts: normalizedMounts,
    };
}
function validateMountConfigProxyConfig(mountConfig, proxyConfig) {
    if (proxyConfig === undefined) {
        return;
    }
    const rules = proxyConfig.rules ?? [];
    if (!Array.isArray(rules)) {
        throw new Error("proxyConfig rules must be an array");
    }
    for (const rule of rules) {
        if (rule === null || typeof rule !== "object" || Array.isArray(rule)) {
            continue;
        }
        const ruleType = rule.type;
        if ((ruleType === "aws" || ruleType === "gcp") &&
            mountConfig.auth[ruleType] !== undefined) {
            throw new Error(`${ruleType} auth cannot be provided in both mountConfig and proxyConfig`);
        }
    }
}
