
      import { Temporal } from "@js-temporal/polyfill";
      import { URLPattern } from "urlpattern-polyfill";
      globalThis.addEventListener = () => {};
    
import { CryptographicKey, Object as Object$1, deno_default } from "./lookup-Csp1y88W.js";
import { isActor } from "./actor-BV5H5O8U.js";
import { getDocumentLoader } from "@fedify/vocab-runtime";
import { getLogger } from "@logtape/logtape";
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";

//#region src/sig/key.ts
/**
* Checks if the given key is valid and supported.  No-op if the key is valid,
* otherwise throws an error.
* @param key The key to check.
* @param type Which type of key to check.  If not specified, the key can be
*             either public or private.
* @throws {TypeError} If the key is invalid or unsupported.
*/
function validateCryptoKey(key, type) {
	if (type != null && key.type !== type) throw new TypeError(`The key is not a ${type} key.`);
	if (!key.extractable) throw new TypeError("The key is not extractable.");
	if (key.algorithm.name !== "RSASSA-PKCS1-v1_5" && key.algorithm.name !== "Ed25519") throw new TypeError("Currently only RSASSA-PKCS1-v1_5 and Ed25519 keys are supported.  More algorithms will be added in the future!");
	if (key.algorithm.name === "RSASSA-PKCS1-v1_5") {
		const algorithm = key.algorithm;
		if (algorithm.hash.name !== "SHA-256") throw new TypeError("For compatibility with the existing Fediverse software (e.g., Mastodon), hash algorithm for RSASSA-PKCS1-v1_5 keys must be SHA-256.");
	}
}
/**
* Generates a key pair which is appropriate for Fedify.
* @param algorithm The algorithm to use.  Currently only RSASSA-PKCS1-v1_5 and
*                  Ed25519 are supported.
* @returns The generated key pair.
* @throws {TypeError} If the algorithm is unsupported.
*/
function generateCryptoKeyPair(algorithm) {
	if (algorithm == null) getLogger([
		"fedify",
		"sig",
		"key"
	]).warn("No algorithm specified.  Using RSASSA-PKCS1-v1_5 by default, but it is recommended to specify the algorithm explicitly as the parameter will be required in the future.");
	if (algorithm == null || algorithm === "RSASSA-PKCS1-v1_5") return crypto.subtle.generateKey({
		name: "RSASSA-PKCS1-v1_5",
		modulusLength: 4096,
		publicExponent: new Uint8Array([
			1,
			0,
			1
		]),
		hash: "SHA-256"
	}, true, ["sign", "verify"]);
	else if (algorithm === "Ed25519") return crypto.subtle.generateKey("Ed25519", true, ["sign", "verify"]);
	throw new TypeError("Unsupported algorithm: " + algorithm);
}
/**
* Exports a key in JWK format.
* @param key The key to export.  Either public or private key.
* @returns The exported key in JWK format.  The key is suitable for
*          serialization and storage.
* @throws {TypeError} If the key is invalid or unsupported.
*/
async function exportJwk(key) {
	validateCryptoKey(key);
	const jwk = await crypto.subtle.exportKey("jwk", key);
	if (jwk.crv === "Ed25519") jwk.alg = "Ed25519";
	return jwk;
}
/**
* Imports a key from JWK format.
* @param jwk The key in JWK format.
* @param type Which type of key to import, either `"public"` or `"private"`.
* @returns The imported key.
* @throws {TypeError} If the key is invalid or unsupported.
*/
async function importJwk(jwk, type) {
	let key;
	if (jwk.kty === "RSA" && jwk.alg === "RS256") key = await crypto.subtle.importKey("jwk", jwk, {
		name: "RSASSA-PKCS1-v1_5",
		hash: "SHA-256"
	}, true, type === "public" ? ["verify"] : ["sign"]);
	else if (jwk.kty === "OKP" && jwk.crv === "Ed25519") {
		if (navigator?.userAgent === "Cloudflare-Workers") {
			jwk = { ...jwk };
			delete jwk.alg;
		}
		key = await crypto.subtle.importKey("jwk", jwk, "Ed25519", true, type === "public" ? ["verify"] : ["sign"]);
	} else throw new TypeError("Unsupported JWK format.");
	validateCryptoKey(key, type);
	return key;
}
/**
* Fetches a {@link CryptographicKey} or {@link Multikey} from the given URL.
* If the given URL contains an {@link Actor} object, it tries to find
* the corresponding key in the `publicKey` or `assertionMethod` property.
* @template T The type of the key to fetch.  Either {@link CryptographicKey}
*              or {@link Multikey}.
* @param keyId The URL of the key.
* @param cls The class of the key to fetch.  Either {@link CryptographicKey}
*            or {@link Multikey}.
* @param options Options for fetching the key.  See {@link FetchKeyOptions}.
* @returns The fetched key or `null` if the key is not found.
* @since 1.3.0
*/
function fetchKey(keyId, cls, options = {}) {
	const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
	const tracer = tracerProvider.getTracer(deno_default.name, deno_default.version);
	keyId = typeof keyId === "string" ? new URL(keyId) : keyId;
	return tracer.startActiveSpan("activitypub.fetch_key", {
		kind: SpanKind.CLIENT,
		attributes: {
			"http.method": "GET",
			"url.full": keyId.href,
			"url.scheme": keyId.protocol.replace(/:$/, ""),
			"url.domain": keyId.hostname,
			"url.path": keyId.pathname,
			"url.query": keyId.search.replace(/^\?/, ""),
			"url.fragment": keyId.hash.replace(/^#/, "")
		}
	}, async (span) => {
		try {
			const result = await fetchKeyInternal(keyId, cls, options);
			span.setAttribute("activitypub.actor.key.cached", result.cached);
			return result;
		} catch (e) {
			span.setStatus({
				code: SpanStatusCode.ERROR,
				message: String(e)
			});
			throw e;
		} finally {
			span.end();
		}
	});
}
async function fetchKeyInternal(keyId, cls, { documentLoader, contextLoader, keyCache, tracerProvider } = {}) {
	const logger = getLogger([
		"fedify",
		"sig",
		"key"
	]);
	const cacheKey = typeof keyId === "string" ? new URL(keyId) : keyId;
	keyId = typeof keyId === "string" ? keyId : keyId.href;
	if (keyCache != null) {
		const cachedKey = await keyCache.get(cacheKey);
		if (cachedKey instanceof cls && cachedKey.publicKey != null) {
			logger.debug("Key {keyId} found in cache.", { keyId });
			return {
				key: cachedKey,
				cached: true
			};
		} else if (cachedKey === null) {
			logger.debug("Entry {keyId} found in cache, but it is unavailable.", { keyId });
			return {
				key: null,
				cached: true
			};
		}
	}
	logger.debug("Fetching key {keyId} to verify signature...", { keyId });
	let document;
	try {
		const remoteDocument = await (documentLoader ?? getDocumentLoader())(keyId);
		document = remoteDocument.document;
	} catch (_) {
		logger.debug("Failed to fetch key {keyId}.", { keyId });
		await keyCache?.set(cacheKey, null);
		return {
			key: null,
			cached: false
		};
	}
	let object;
	try {
		object = await Object$1.fromJsonLd(document, {
			documentLoader,
			contextLoader,
			tracerProvider
		});
	} catch (e) {
		if (!(e instanceof TypeError)) throw e;
		try {
			object = await cls.fromJsonLd(document, {
				documentLoader,
				contextLoader,
				tracerProvider
			});
		} catch (e$1) {
			if (e$1 instanceof TypeError) {
				logger.debug("Failed to verify; key {keyId} returned an invalid object.", { keyId });
				await keyCache?.set(cacheKey, null);
				return {
					key: null,
					cached: false
				};
			}
			throw e$1;
		}
	}
	let key = null;
	if (object instanceof cls) key = object;
	else if (isActor(object)) {
		const keys = cls === CryptographicKey ? object.getPublicKeys({
			documentLoader,
			contextLoader,
			tracerProvider
		}) : object.getAssertionMethods({
			documentLoader,
			contextLoader,
			tracerProvider
		});
		let length = 0;
		let lastKey = null;
		for await (const k of keys) {
			length++;
			lastKey = k;
			if (k.id?.href === keyId) {
				key = k;
				break;
			}
		}
		const keyIdUrl = new URL(keyId);
		if (key == null && keyIdUrl.hash === "" && length === 1) key = lastKey;
		if (key == null) {
			logger.debug("Failed to verify; object {keyId} returned an {actorType}, but has no key matching {keyId}.", {
				keyId,
				actorType: object.constructor.name
			});
			await keyCache?.set(cacheKey, null);
			return {
				key: null,
				cached: false
			};
		}
	} else {
		logger.debug("Failed to verify; key {keyId} returned an invalid object.", { keyId });
		await keyCache?.set(cacheKey, null);
		return {
			key: null,
			cached: false
		};
	}
	if (key.publicKey == null) {
		logger.debug("Failed to verify; key {keyId} has no publicKeyPem field.", { keyId });
		await keyCache?.set(cacheKey, null);
		return {
			key: null,
			cached: false
		};
	}
	if (keyCache != null) {
		await keyCache.set(cacheKey, key);
		logger.debug("Key {keyId} cached.", { keyId });
	}
	return {
		key,
		cached: false
	};
}

//#endregion
export { exportJwk, fetchKey, generateCryptoKeyPair, importJwk, validateCryptoKey };