var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
    if (kind === "m") throw new TypeError("Private method is not writable");
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
    return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
    if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
    if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
    return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _PortableLoader_instances, _PortableLoader_options, _PortableLoader_fetchOngoing, _PortableLoader_lockfile, _PortableLoader_fetchModules, _PortableLoader_fetchRedirects, _PortableLoader_resolveJsrSpecifier, _PortableLoader_resolveRemote, _PortableLoader_loadRemote, _PortableLoader_fetch, _PortableLoader_loadLocal;
import { fromFileUrl } from "../../../../@std/path/1.0.8/mod.js";
import { mapContentType, mediaTypeToLoader, parseJsrSpecifier, parseNpmSpecifier, } from "./shared.js";
import { instantiate } from "./wasm/loader.generated.js";
const JSR_URL = Deno.env.get("JSR_URL") ?? "https://jsr.io";
async function readLockfile(path) {
    try {
        const data = await Deno.readTextFile(path);
        const instance = instantiate();
        return new instance.WasmLockfile(path, data);
    }
    catch (err) {
        if (err instanceof Deno.errors.NotFound) {
            return null;
        }
        throw err;
    }
}
export class PortableLoader {
    constructor(options) {
        _PortableLoader_instances.add(this);
        _PortableLoader_options.set(this, void 0);
        _PortableLoader_fetchOngoing.set(this, new Map());
        _PortableLoader_lockfile.set(this, void 0);
        _PortableLoader_fetchModules.set(this, new Map());
        _PortableLoader_fetchRedirects.set(this, new Map());
        __classPrivateFieldSet(this, _PortableLoader_options, options, "f");
    }
    [(_PortableLoader_options = new WeakMap(), _PortableLoader_fetchOngoing = new WeakMap(), _PortableLoader_lockfile = new WeakMap(), _PortableLoader_fetchModules = new WeakMap(), _PortableLoader_fetchRedirects = new WeakMap(), _PortableLoader_instances = new WeakSet(), Symbol.dispose)]() {
        if (__classPrivateFieldGet(this, _PortableLoader_lockfile, "f") != null && "free" in __classPrivateFieldGet(this, _PortableLoader_lockfile, "f")) {
            __classPrivateFieldGet(this, _PortableLoader_lockfile, "f").free();
        }
    }
    async resolve(specifier) {
        switch (specifier.protocol) {
            case "file:": {
                return { kind: "esm", specifier };
            }
            case "http:":
            case "https:":
            case "data:": {
                const module = await __classPrivateFieldGet(this, _PortableLoader_instances, "m", _PortableLoader_loadRemote).call(this, specifier.href);
                return { kind: "esm", specifier: new URL(module.specifier) };
            }
            case "npm:": {
                const npmSpecifier = parseNpmSpecifier(specifier);
                return {
                    kind: "npm",
                    packageId: "",
                    packageName: npmSpecifier.name,
                    path: npmSpecifier.path ?? "",
                };
            }
            case "node:": {
                return { kind: "node", path: specifier.pathname };
            }
            case "jsr:": {
                const resolvedSpecifier = await __classPrivateFieldGet(this, _PortableLoader_instances, "m", _PortableLoader_resolveJsrSpecifier).call(this, specifier);
                return { kind: "esm", specifier: resolvedSpecifier };
            }
            default:
                throw new Error(`Unsupported scheme: '${specifier.protocol}'`);
        }
    }
    async loadEsm(url) {
        let module;
        switch (url.protocol) {
            case "file:": {
                module = await __classPrivateFieldGet(this, _PortableLoader_instances, "m", _PortableLoader_loadLocal).call(this, url);
                break;
            }
            case "http:":
            case "https:":
            case "data:": {
                module = await __classPrivateFieldGet(this, _PortableLoader_instances, "m", _PortableLoader_loadRemote).call(this, url.href);
                break;
            }
            default:
                throw new Error("[unreachable] unsupported esm scheme " + url.protocol);
        }
        const loader = mediaTypeToLoader(module.mediaType);
        if (loader === null)
            return undefined;
        const res = { contents: module.data, loader };
        if (url.protocol === "file:") {
            res.watchFiles = [fromFileUrl(module.specifier)];
        }
        return res;
    }
}
_PortableLoader_resolveJsrSpecifier = async function _PortableLoader_resolveJsrSpecifier(specifier) {
    // parse the JSR specifier.
    const jsrSpecifier = parseJsrSpecifier(specifier);
    // Attempt to load the lockfile.
    if (__classPrivateFieldGet(this, _PortableLoader_lockfile, "f") === undefined) {
        __classPrivateFieldSet(this, _PortableLoader_lockfile, typeof __classPrivateFieldGet(this, _PortableLoader_options, "f").lock === "string"
            ? readLockfile(__classPrivateFieldGet(this, _PortableLoader_options, "f").lock)
            : null, "f");
    }
    if (__classPrivateFieldGet(this, _PortableLoader_lockfile, "f") instanceof Promise) {
        __classPrivateFieldSet(this, _PortableLoader_lockfile, await __classPrivateFieldGet(this, _PortableLoader_lockfile, "f"), "f");
    }
    if (__classPrivateFieldGet(this, _PortableLoader_lockfile, "f") === null) {
        throw new Error("jsr: specifiers are not supported in the portable loader without a lockfile");
    }
    const lockfile = __classPrivateFieldGet(this, _PortableLoader_lockfile, "f");
    // Look up the package + constraint in the lockfile.
    const id = `jsr:${jsrSpecifier.name}${jsrSpecifier.version ? `@${jsrSpecifier.version}` : ""}`;
    const resolvedVersion = lockfile.package_version(id);
    if (!resolvedVersion) {
        throw new Error(`Specifier not found in lockfile: ${id}`);
    }
    // Load the JSR manifest to find the export path.
    const manifestUrl = new URL(`./${jsrSpecifier.name}/${resolvedVersion}_meta.json`, JSR_URL);
    const manifest = await __classPrivateFieldGet(this, _PortableLoader_instances, "m", _PortableLoader_loadRemote).call(this, manifestUrl.href);
    if (manifest.mediaType !== "Json") {
        throw new Error(`Expected JSON media type for JSR manifest, got: ${manifest.mediaType}`);
    }
    const manifestData = new TextDecoder().decode(manifest.data);
    const manifestJson = JSON.parse(manifestData);
    // Look up the export path in the manifest.
    const exportEntry = `.${jsrSpecifier.path ?? ""}`;
    const exportPath = manifestJson.exports[exportEntry];
    if (!exportPath) {
        throw new Error(`Package 'jsr:${jsrSpecifier.name}@${resolvedVersion}' has no export named '${exportEntry}'`);
    }
    // Return the resolved URL.
    return new URL(`./${jsrSpecifier.name}/${resolvedVersion}/${exportPath}`, JSR_URL);
}, _PortableLoader_resolveRemote = function _PortableLoader_resolveRemote(specifier) {
    return __classPrivateFieldGet(this, _PortableLoader_fetchRedirects, "f").get(specifier) ?? specifier;
}, _PortableLoader_loadRemote = async function _PortableLoader_loadRemote(specifier) {
    for (let i = 0; i < 10; i++) {
        specifier = __classPrivateFieldGet(this, _PortableLoader_instances, "m", _PortableLoader_resolveRemote).call(this, specifier);
        const module = __classPrivateFieldGet(this, _PortableLoader_fetchModules, "f").get(specifier);
        if (module)
            return module;
        let promise = __classPrivateFieldGet(this, _PortableLoader_fetchOngoing, "f").get(specifier);
        if (!promise) {
            promise = __classPrivateFieldGet(this, _PortableLoader_instances, "m", _PortableLoader_fetch).call(this, specifier);
            __classPrivateFieldGet(this, _PortableLoader_fetchOngoing, "f").set(specifier, promise);
        }
        await promise;
    }
    throw new Error("Too many redirects. Last one: " + specifier);
}, _PortableLoader_fetch = async function _PortableLoader_fetch(specifier) {
    const resp = await fetch(specifier, {
        redirect: "manual",
    });
    if (resp.status < 200 && resp.status >= 400) {
        throw new Error(`Encountered status code ${resp.status} while fetching ${specifier}.`);
    }
    if (resp.status >= 300 && resp.status < 400) {
        await resp.body?.cancel();
        const location = resp.headers.get("location");
        if (!location) {
            throw new Error(`Redirected without location header while fetching ${specifier}.`);
        }
        const url = new URL(location, specifier);
        if (url.protocol !== "https:" && url.protocol !== "http:") {
            throw new Error(`Redirected to unsupported protocol '${url.protocol}' while fetching ${specifier}.`);
        }
        __classPrivateFieldGet(this, _PortableLoader_fetchRedirects, "f").set(specifier, url.href);
        return;
    }
    const contentType = resp.headers.get("content-type");
    const mediaType = mapContentType(new URL(specifier), contentType);
    const data = new Uint8Array(await resp.arrayBuffer());
    __classPrivateFieldGet(this, _PortableLoader_fetchModules, "f").set(specifier, {
        specifier,
        mediaType,
        data,
    });
}, _PortableLoader_loadLocal = async function _PortableLoader_loadLocal(specifier) {
    const path = fromFileUrl(specifier);
    const mediaType = mapContentType(specifier, null);
    const data = await Deno.readFile(path);
    return { specifier: specifier.href, mediaType, data };
};
//# sourceMappingURL=loader_portable.js.map