import { __decorateMetadata } from "../../../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorateMetadata.mjs";
import { __decorate } from "../../../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorate.mjs";
import { ServiceDecorator } from "../../decorators/service/ServiceDecorator.mjs";
import { parseDidCommProtocolUri, parseMessageType, supportsIncomingDidCommProtocolUri, supportsIncomingMessageType } from "../../util/messageType.mjs";
import { DidCommOutboundMessageContext } from "../../models/DidCommOutboundMessageContext.mjs";
import "../../models/index.mjs";
import { DidCommDidExchangeState } from "../connections/models/DidCommDidExchangeState.mjs";
import { DidCommHandshakeProtocol } from "../connections/models/DidCommHandshakeProtocol.mjs";
import { of } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/observable/of.mjs";
import { EmptyError } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/util/EmptyError.mjs";
import { firstValueFrom } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/firstValueFrom.mjs";
import { timeout } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/operators/timeout.mjs";
import { map } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/operators/map.mjs";
import { catchError } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/operators/catchError.mjs";
import { first } from "../../../../../node_modules/.pnpm/rxjs@7.8.2/node_modules/rxjs/dist/esm5/internal/operators/first.mjs";
import { DidCommEventTypes } from "../../DidCommEvents.mjs";
import { DidCommOutOfBandState } from "./domain/DidCommOutOfBandState.mjs";
import { __decorateParam } from "../../../../../_virtual/_@oxc-project_runtime@0.94.0/helpers/decorateParam.mjs";
import { DidCommDocumentService } from "../../services/DidCommDocumentService.mjs";
import "../../services/index.mjs";
import { OutOfBandDidCommService } from "./domain/OutOfBandDidCommService.mjs";
import { DidCommOutOfBandEventTypes } from "./domain/DidCommOutOfBandEvents.mjs";
import { DidCommOutOfBandRole } from "./domain/DidCommOutOfBandRole.mjs";
import { outOfBandServiceToInlineKeysNumAlgo2Did } from "./helpers.mjs";
import { DidCommInvitationType, DidCommOutOfBandInvitation } from "./messages/DidCommOutOfBandInvitation.mjs";
import "./messages/index.mjs";
import { DidCommOutOfBandRecord } from "./repository/DidCommOutOfBandRecord.mjs";
import { DidCommOutOfBandRepository } from "./repository/DidCommOutOfBandRepository.mjs";
import "./repository/index.mjs";
import { DidCommOutOfBandService } from "./DidCommOutOfBandService.mjs";
import { DidCommOutOfBandRecordMetadataKeys } from "./repository/outOfBandRecordMetadataTypes.mjs";
import { DidCommMessageSender } from "../../DidCommMessageSender.mjs";
import { DidCommRoutingService } from "../routing/services/DidCommRoutingService.mjs";
import { DidCommConnectionsApi } from "../connections/DidCommConnectionsApi.mjs";
import { DidCommMessageHandlerRegistry } from "../../DidCommMessageHandlerRegistry.mjs";
import "../connections/index.mjs";
import { convertToNewInvitation, convertToOldInvitation } from "./converters.mjs";
import { parseInvitationShortUrl } from "../../util/parseInvitation.mjs";
import { AgentContext, CredoError, DidKey, EventEmitter, InjectionSymbols, JsonEncoder, JsonTransformer, Kms, filterContextCorrelationId, inject, injectable } from "@credo-ts/core";

//#region ../didcomm/src/modules/oob/DidCommOutOfBandApi.ts
var _ref, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8;
const didCommProfiles = ["didcomm/aip1", "didcomm/aip2;env=rfc19"];
let DidCommOutOfBandApi = class DidCommOutOfBandApi$1 {
	constructor(messageHandlerRegistry, didCommDocumentService, outOfBandService, routingService, connectionsApi, messageSender, eventEmitter, logger, agentContext) {
		this.messageHandlerRegistry = messageHandlerRegistry;
		this.didCommDocumentService = didCommDocumentService;
		this.agentContext = agentContext;
		this.logger = logger;
		this.outOfBandService = outOfBandService;
		this.routingService = routingService;
		this.connectionsApi = connectionsApi;
		this.messageSender = messageSender;
		this.eventEmitter = eventEmitter;
	}
	/**
	* Creates an outbound out-of-band record containing out-of-band invitation message defined in
	* Aries RFC 0434: Out-of-Band Protocol 1.1.
	*
	* It automatically adds all supported handshake protocols by agent to `handshake_protocols`. You
	* can modify this by setting `handshakeProtocols` in `config` parameter. If you want to create
	* invitation without handshake, you can set `handshake` to `false`.
	*
	* If `config` parameter contains `messages` it adds them to `requests~attach` attribute.
	*
	* Agent role: sender (inviter)
	*
	* @param config configuration of how out-of-band invitation should be created
	* @returns out-of-band record
	*/
	async createInvitation(config = {}) {
		const multiUseInvitation = config.multiUseInvitation ?? false;
		const handshake = config.handshake ?? true;
		const customHandshakeProtocols = config.handshakeProtocols;
		const autoAcceptConnection = config.autoAcceptConnection ?? this.connectionsApi.config.autoAcceptConnections;
		const messages = config.messages && config.messages.length > 0 ? config.messages : void 0;
		const label = config.label;
		const imageUrl = config.imageUrl;
		const appendedAttachments = config.appendedAttachments && config.appendedAttachments.length > 0 ? config.appendedAttachments : void 0;
		if (!handshake && !messages) throw new CredoError("One or both of handshake_protocols and requests~attach MUST be included in the message.");
		if (!handshake && customHandshakeProtocols) throw new CredoError(`Attribute 'handshake' can not be 'false' when 'handshakeProtocols' is defined.`);
		if (messages && multiUseInvitation) throw new CredoError("Attribute 'multiUseInvitation' can not be 'true' when 'messages' is defined.");
		let handshakeProtocols;
		if (handshake) {
			if (customHandshakeProtocols) this.assertHandshakeProtocolsSupported(customHandshakeProtocols);
			handshakeProtocols = this.getSupportedHandshakeProtocols(customHandshakeProtocols).map((p) => p.parsedProtocolUri.protocolUri);
		}
		let mediatorId = void 0;
		let services;
		if (config.routing && config.invitationDid) throw new CredoError("Both 'routing' and 'invitationDid' cannot be provided at the same time.");
		const invitationInlineServiceKeys = [];
		if (config.invitationDid) services = [config.invitationDid];
		else {
			const routing = config.routing ?? await this.routingService.getRouting(this.agentContext, {});
			mediatorId = routing?.mediatorId;
			services = routing.endpoints.map((endpoint, index) => {
				invitationInlineServiceKeys.push({
					kmsKeyId: routing.recipientKey.keyId,
					recipientKeyFingerprint: routing.recipientKey.fingerprint
				});
				return new OutOfBandDidCommService({
					id: `#inline-${index}`,
					serviceEndpoint: endpoint,
					recipientKeys: [routing.recipientKey].map((key) => new DidKey(key).did),
					routingKeys: routing.routingKeys.map((key) => new DidKey(key).did)
				});
			});
		}
		const outOfBandInvitation = new DidCommOutOfBandInvitation({
			label,
			goal: config.goal,
			goalCode: config.goalCode,
			imageUrl,
			accept: didCommProfiles,
			services,
			handshakeProtocols,
			appendedAttachments
		});
		if (messages) for (const message of messages) {
			if (message.service) message.service = void 0;
			outOfBandInvitation.addRequest(message);
		}
		const recipientKeyFingerprints = await this.resolveInvitationRecipientKeyFingerprints(outOfBandInvitation);
		const outOfBandRecord = new DidCommOutOfBandRecord({
			mediatorId,
			role: DidCommOutOfBandRole.Sender,
			state: DidCommOutOfBandState.AwaitResponse,
			alias: config.alias,
			outOfBandInvitation,
			reusable: multiUseInvitation,
			autoAcceptConnection,
			invitationInlineServiceKeys,
			tags: { recipientKeyFingerprints }
		});
		await this.outOfBandService.save(this.agentContext, outOfBandRecord);
		this.outOfBandService.emitStateChangedEvent(this.agentContext, outOfBandRecord, null);
		return outOfBandRecord;
	}
	/**
	* Creates an outbound out-of-band record in the same way how `createInvitation` method does it,
	* but it also converts out-of-band invitation message to an "legacy" invitation message defined
	* in RFC 0160: Connection Protocol and returns it together with out-of-band record.
	*
	* Agent role: sender (inviter)
	*
	* @param config configuration of how a connection invitation should be created
	* @returns out-of-band record and connection invitation
	*/
	async createLegacyInvitation(config = {}) {
		const outOfBandRecord = await this.createInvitation({
			...config,
			handshakeProtocols: [DidCommHandshakeProtocol.Connections]
		});
		outOfBandRecord.metadata.set(DidCommOutOfBandRecordMetadataKeys.LegacyInvitation, { legacyInvitationType: DidCommInvitationType.Connection });
		await this.agentContext.dependencyManager.resolve(DidCommOutOfBandRepository).update(this.agentContext, outOfBandRecord);
		return {
			outOfBandRecord,
			invitation: convertToOldInvitation(outOfBandRecord.outOfBandInvitation)
		};
	}
	async createLegacyConnectionlessInvitation(config) {
		const outOfBandRecord = await this.createInvitation({
			messages: [config.message],
			routing: config.routing
		});
		outOfBandRecord.metadata.set(DidCommOutOfBandRecordMetadataKeys.LegacyInvitation, { legacyInvitationType: DidCommInvitationType.Connectionless });
		await this.agentContext.dependencyManager.resolve(DidCommOutOfBandRepository).update(this.agentContext, outOfBandRecord);
		const resolvedService = await this.outOfBandService.getResolvedServiceForOutOfBandServices(this.agentContext, outOfBandRecord.outOfBandInvitation.getServices());
		config.message.service = ServiceDecorator.fromResolvedDidCommService(resolvedService);
		return {
			message: config.message,
			invitationUrl: `${config.domain}?d_m=${JsonEncoder.toBase64URL(JsonTransformer.toJSON(config.message))}`,
			outOfBandRecord
		};
	}
	/**
	* Parses URL, decodes invitation and calls `receiveMessage` with parsed invitation message.
	*
	* Agent role: receiver (invitee)
	*
	* @param invitationUrl url containing a base64 encoded invitation to receive
	* @param config configuration of how out-of-band invitation should be processed
	* @returns out-of-band record and connection record if one has been created
	*/
	async receiveInvitationFromUrl(invitationUrl, config) {
		const message = await this.parseInvitation(invitationUrl);
		return this.receiveInvitation(message, config);
	}
	/**
	* Parses URL containing encoded invitation and returns invitation message.
	*
	* Will fetch the url if the url does not contain a base64 encoded invitation.
	*
	* @param invitationUrl URL containing encoded invitation
	*
	* @returns DidCommOutOfBandInvitation
	*/
	async parseInvitation(invitationUrl) {
		return parseInvitationShortUrl(invitationUrl, this.agentContext.config.agentDependencies);
	}
	/**
	* Creates inbound out-of-band record and assigns out-of-band invitation message to it if the
	* message is valid. It automatically passes out-of-band invitation for further processing to
	* `acceptInvitation` method. If you don't want to do that you can set `autoAcceptInvitation`
	* attribute in `config` parameter to `false` and accept the message later by calling
	* `acceptInvitation`.
	*
	* It supports both OOB (Aries RFC 0434: Out-of-Band Protocol 1.1) and Connection Invitation
	* (0160: Connection Protocol).
	*
	* Agent role: receiver (invitee)
	*
	* @param invitation either DidCommOutOfBandInvitation or DidCommConnectionInvitationMessage
	* @param config config for handling of invitation
	*
	* @returns out-of-band record and connection record if one has been created.
	*/
	async receiveInvitation(invitation, config) {
		return this._receiveInvitation(invitation, config);
	}
	/**
	* Creates inbound out-of-band record from an implicit invitation, given as a public DID the agent
	* should be capable of resolving. It automatically passes out-of-band invitation for further
	* processing to `acceptInvitation` method. If you don't want to do that you can set
	* `autoAcceptInvitation` attribute in `config` parameter to `false` and accept the message later by
	* calling `acceptInvitation`.
	*
	* It supports both OOB (Aries RFC 0434: Out-of-Band Protocol 1.1) and Connection Invitation
	* (0160: Connection Protocol). Handshake protocol to be used depends on handshakeProtocols
	* (DID Exchange by default)
	*
	* Agent role: receiver (invitee)
	*
	* @param config config for creating and handling invitation
	*
	* @returns out-of-band record and connection record if one has been created.
	*/
	async receiveImplicitInvitation(config) {
		const handshakeProtocols = this.getSupportedHandshakeProtocols(config.handshakeProtocols ?? [DidCommHandshakeProtocol.DidExchange]).map((p) => p.parsedProtocolUri.protocolUri);
		const invitation = new DidCommOutOfBandInvitation({
			id: config.did,
			label: config.alias ?? "",
			services: [config.did],
			handshakeProtocols
		});
		return this._receiveInvitation(invitation, {
			...config,
			isImplicit: true
		});
	}
	/**
	* Internal receive invitation method, for both explicit and implicit OOB invitations
	*/
	async _receiveInvitation(invitation, config) {
		const outOfBandInvitation = invitation instanceof DidCommOutOfBandInvitation ? invitation : convertToNewInvitation(invitation);
		const { handshakeProtocols } = outOfBandInvitation;
		const { routing } = config;
		const autoAcceptInvitation = config.autoAcceptInvitation ?? true;
		const autoAcceptConnection = config.autoAcceptConnection ?? true;
		const reuseConnection = config.reuseConnection ?? false;
		const label = config.label;
		const alias = config.alias;
		const imageUrl = config.imageUrl;
		const messages = outOfBandInvitation.getRequests();
		const isConnectionless = handshakeProtocols === void 0 || handshakeProtocols.length === 0;
		if ((!handshakeProtocols || handshakeProtocols.length === 0) && (!messages || messages?.length === 0)) throw new CredoError("One or both of handshake_protocols and requests~attach MUST be included in the message.");
		if (!config.isImplicit) {
			if ((await this.outOfBandService.findAllByQuery(this.agentContext, {
				invitationId: outOfBandInvitation.id,
				role: DidCommOutOfBandRole.Receiver
			})).length > 0) throw new CredoError(`An out of band record with invitation ${outOfBandInvitation.id} has already been received. Invitations should have a unique id.`);
		}
		const recipientKeyFingerprints = await this.resolveInvitationRecipientKeyFingerprints(outOfBandInvitation);
		const outOfBandRecord = new DidCommOutOfBandRecord({
			role: DidCommOutOfBandRole.Receiver,
			state: DidCommOutOfBandState.Initial,
			outOfBandInvitation,
			autoAcceptConnection,
			tags: { recipientKeyFingerprints },
			mediatorId: routing?.mediatorId
		});
		if (routing && (isConnectionless || !autoAcceptInvitation)) {
			this.logger.debug("Storing routing for out of band invitation.");
			outOfBandRecord.metadata.set(DidCommOutOfBandRecordMetadataKeys.RecipientRouting, {
				recipientKeyFingerprint: routing.recipientKey.fingerprint,
				recipientKeyId: routing.recipientKey.keyId,
				routingKeyFingerprints: routing.routingKeys.map((key) => key.fingerprint),
				endpoints: routing.endpoints,
				mediatorId: routing.mediatorId
			});
			outOfBandRecord.setTags({ recipientRoutingKeyFingerprint: routing.recipientKey.fingerprint });
		}
		if (outOfBandInvitation.invitationType && outOfBandInvitation.invitationType !== DidCommInvitationType.OutOfBand) outOfBandRecord.metadata.set(DidCommOutOfBandRecordMetadataKeys.LegacyInvitation, { legacyInvitationType: outOfBandInvitation.invitationType });
		await this.outOfBandService.save(this.agentContext, outOfBandRecord);
		this.outOfBandService.emitStateChangedEvent(this.agentContext, outOfBandRecord, null);
		if (autoAcceptInvitation) return await this.acceptInvitation(outOfBandRecord.id, {
			label,
			alias,
			imageUrl,
			autoAcceptConnection,
			reuseConnection,
			routing,
			timeoutMs: config.acceptInvitationTimeoutMs,
			ourDid: config.ourDid
		});
		return { outOfBandRecord };
	}
	/**
	* Creates a connection if the out-of-band invitation message contains `handshake_protocols`
	* attribute, except for the case when connection already exists and `reuseConnection` is enabled.
	*
	* It passes first supported message from `requests~attach` attribute to the agent, except for the
	* case reuse of connection is applied when it just sends `handshake-reuse` message to existing
	* connection.
	*
	* Agent role: receiver (invitee)
	*
	* @param outOfBandId
	* @param config
	* @returns out-of-band record and connection record if one has been created.
	*/
	async acceptInvitation(outOfBandId, config) {
		const outOfBandRecord = await this.outOfBandService.getById(this.agentContext, outOfBandId);
		const { outOfBandInvitation } = outOfBandRecord;
		const { label, alias, imageUrl, autoAcceptConnection, reuseConnection, ourDid } = config;
		const services = outOfBandInvitation.getServices();
		const messages = outOfBandInvitation.getRequests();
		const timeoutMs = config.timeoutMs ?? 2e4;
		let routing = config.routing;
		const recipientRouting = outOfBandRecord.metadata.get(DidCommOutOfBandRecordMetadataKeys.RecipientRouting);
		if (!routing && recipientRouting) {
			const recipientPublicJwk = Kms.PublicJwk.fromFingerprint(recipientRouting.recipientKeyFingerprint);
			recipientPublicJwk.keyId = recipientRouting.recipientKeyId ?? recipientPublicJwk.legacyKeyId;
			routing = {
				recipientKey: recipientPublicJwk,
				routingKeys: recipientRouting.routingKeyFingerprints.map((fingerprint) => Kms.PublicJwk.fromFingerprint(fingerprint)),
				endpoints: recipientRouting.endpoints,
				mediatorId: recipientRouting.mediatorId
			};
		}
		const { handshakeProtocols } = outOfBandInvitation;
		const existingConnection = await this.findExistingConnection(outOfBandInvitation);
		await this.outOfBandService.updateState(this.agentContext, outOfBandRecord, DidCommOutOfBandState.PrepareResponse);
		if (handshakeProtocols && handshakeProtocols.length > 0) {
			this.logger.debug("Out of band message contains handshake protocols.");
			let connectionRecord = void 0;
			if (existingConnection && reuseConnection) {
				this.logger.debug(`Connection already exists and reuse is enabled. Reusing an existing connection with ID ${existingConnection.id}.`);
				if (!messages || messages?.length === 0) {
					this.logger.debug("Out of band message does not contain any request messages.");
					if (await this.handleHandshakeReuse(outOfBandRecord, existingConnection)) {
						this.logger.debug(`Handshake reuse successful. Reusing existing connection ${existingConnection.id}.`);
						connectionRecord = existingConnection;
					} else this.logger.debug(`Handshake reuse failed. Not using existing connection ${existingConnection.id}.`);
				} else {
					this.logger.debug(`Reusing existing connection ${existingConnection.id}.`);
					connectionRecord = existingConnection;
				}
			}
			if (!connectionRecord) {
				this.logger.debug("Connection does not exist or reuse is disabled. Creating a new connection.");
				const firstSupportedProtocol = this.getFirstSupportedProtocol(handshakeProtocols);
				connectionRecord = await this.connectionsApi.acceptOutOfBandInvitation(outOfBandRecord, {
					label,
					alias,
					imageUrl,
					autoAcceptConnection,
					protocol: firstSupportedProtocol.handshakeProtocol,
					routing,
					ourDid
				});
			}
			if (messages && messages.length > 0) {
				this.logger.debug("Out of band message contains request messages.");
				if (connectionRecord.isReady) await this.emitWithConnection(outOfBandRecord, connectionRecord, messages);
				else this.connectionsApi.returnWhenIsConnected(connectionRecord.id, { timeoutMs }).then((connectionRecord$1) => this.emitWithConnection(outOfBandRecord, connectionRecord$1, messages)).catch((error) => {
					if (error instanceof EmptyError) this.logger.warn(`Agent unsubscribed before connection got into ${DidCommDidExchangeState.Completed} state`, error);
					else this.logger.error("Promise waiting for the connection to be complete failed.", error);
				});
			}
			return {
				outOfBandRecord,
				connectionRecord
			};
		}
		if (messages) {
			this.logger.debug("Out of band message contains only request messages.");
			if (existingConnection && reuseConnection) {
				this.logger.debug("Connection already exists.", { connectionId: existingConnection.id });
				await this.emitWithConnection(outOfBandRecord, existingConnection, messages);
			} else await this.emitWithServices(outOfBandRecord, services, messages);
		}
		return { outOfBandRecord };
	}
	async findByReceivedInvitationId(receivedInvitationId) {
		return this.outOfBandService.findByReceivedInvitationId(this.agentContext, receivedInvitationId);
	}
	async findByCreatedInvitationId(createdInvitationId) {
		return this.outOfBandService.findByCreatedInvitationId(this.agentContext, createdInvitationId);
	}
	/**
	* Retrieve all out of bands records
	*
	* @returns List containing all  out of band records
	*/
	getAll() {
		return this.outOfBandService.getAll(this.agentContext);
	}
	/**
	* Retrieve all out of bands records by specified query param
	*
	* @returns List containing all out of band records matching specified query params
	*/
	findAllByQuery(query, queryOptions) {
		return this.outOfBandService.findAllByQuery(this.agentContext, query, queryOptions);
	}
	/**
	* Retrieve a out of band record by id
	*
	* @param outOfBandId The  out of band record id
	* @throws {RecordNotFoundError} If no record is found
	* @return The out of band record
	*
	*/
	getById(outOfBandId) {
		return this.outOfBandService.getById(this.agentContext, outOfBandId);
	}
	/**
	* Find an out of band record by id
	*
	* @param outOfBandId the  out of band record id
	* @returns The out of band record or null if not found
	*/
	findById(outOfBandId) {
		return this.outOfBandService.findById(this.agentContext, outOfBandId);
	}
	/**
	* Delete an out of band record by id
	*
	* @param outOfBandId the out of band record id
	*/
	async deleteById(outOfBandId) {
		const outOfBandRecord = await this.getById(outOfBandId);
		const relatedConnections = await this.connectionsApi.findAllByOutOfBandId(outOfBandId);
		if (outOfBandRecord.mediatorId && outOfBandRecord.outOfBandInvitation.getDidServices().length === 0 && (relatedConnections.length === 0 || outOfBandRecord.reusable)) {
			const recipientKeys = outOfBandRecord.getTags().recipientKeyFingerprints.map((item) => Kms.PublicJwk.fromFingerprint(item));
			await this.routingService.removeRouting(this.agentContext, {
				recipientKeys,
				mediatorId: outOfBandRecord.mediatorId
			});
		}
		return this.outOfBandService.deleteById(this.agentContext, outOfBandId);
	}
	assertHandshakeProtocolsSupported(handshakeProtocols) {
		if (!this.areHandshakeProtocolsSupported(handshakeProtocols)) throw new CredoError(`Handshake protocols [${handshakeProtocols}] are not supported. Supported protocols are [${this.getSupportedHandshakeProtocols()}]`);
	}
	areHandshakeProtocolsSupported(handshakeProtocols) {
		return this.getSupportedHandshakeProtocols(handshakeProtocols).length === handshakeProtocols.length;
	}
	getSupportedHandshakeProtocols(limitToHandshakeProtocols) {
		const parsedHandshakeProtocolUris = (limitToHandshakeProtocols ?? Object.values(DidCommHandshakeProtocol)).map((h) => ({
			handshakeProtocol: h,
			parsedProtocolUri: parseDidCommProtocolUri(h.replace(".x", ".0"))
		}));
		const supportedHandshakeProtocols = this.messageHandlerRegistry.filterSupportedProtocolsByProtocolUris(parsedHandshakeProtocolUris.map((p) => p.parsedProtocolUri));
		if (supportedHandshakeProtocols.length === 0) throw new CredoError("There is no handshake protocol supported. Agent can not create a connection.");
		return parsedHandshakeProtocolUris.map((p) => {
			const found = supportedHandshakeProtocols.find((s) => supportsIncomingDidCommProtocolUri(s, p.parsedProtocolUri));
			return found ? {
				...p,
				parsedProtocolUri: found
			} : null;
		}).filter((p) => p !== null);
	}
	/**
	* Get the first supported protocol based on the handshake protocols provided in the out of band
	* invitation.
	*
	* Returns an enum value from {@link DidCommHandshakeProtocol} or throw an error if no protocol is supported.
	* Minor versions are ignored when selecting a supported protocols, so if the `outOfBandInvitationSupportedProtocolsWithMinorVersion`
	* value is `https://didcomm.org/didexchange/1.0` and the agent supports `https://didcomm.org/didexchange/1.1`
	* this will be fine, and the returned value will be {@link DidCommHandshakeProtocol.DidExchange}.
	*/
	getFirstSupportedProtocol(protocolUris) {
		const supportedProtocols = this.getSupportedHandshakeProtocols();
		const parsedProtocolUris = protocolUris.map(parseDidCommProtocolUri);
		const firstSupportedProtocol = supportedProtocols.find((supportedProtocol) => parsedProtocolUris.find((parsedProtocol) => supportsIncomingDidCommProtocolUri(supportedProtocol.parsedProtocolUri, parsedProtocol)));
		if (!firstSupportedProtocol) throw new CredoError(`Handshake protocols [${protocolUris}] are not supported. Supported protocols are [${supportedProtocols.map((p) => p.handshakeProtocol)}]`);
		return firstSupportedProtocol;
	}
	async findExistingConnection(outOfBandInvitation) {
		this.logger.debug("Searching for an existing connection for out-of-band invitation.", { outOfBandInvitation });
		const invitationDids = [...outOfBandInvitation.invitationDids, ...outOfBandInvitation.getInlineServices().map(outOfBandServiceToInlineKeysNumAlgo2Did)];
		for (const invitationDid of invitationDids) {
			const connections = await this.connectionsApi.findByInvitationDid(invitationDid);
			this.logger.debug(`Retrieved ${connections.length} connections for invitation did ${invitationDid}`);
			if (connections.length === 1) {
				const [firstConnection] = connections;
				return firstConnection;
			}
			if (connections.length > 1) {
				this.logger.warn(`There is more than one connection created from invitationDid ${invitationDid}. Taking the first one.`);
				const [firstConnection] = connections;
				return firstConnection;
			}
			return null;
		}
	}
	async emitWithConnection(outOfBandRecord, connectionRecord, messages) {
		const supportedMessageTypes = this.messageHandlerRegistry.supportedMessageTypes;
		const plaintextMessage = messages.find((message) => {
			const parsedMessageType = parseMessageType(message["@type"]);
			return supportedMessageTypes.find((type) => supportsIncomingMessageType(parsedMessageType, type));
		});
		if (!plaintextMessage) throw new CredoError("There is no message in requests~attach supported by agent.");
		this.ensureParentThreadId(outOfBandRecord, plaintextMessage);
		this.logger.debug(`Message with type ${plaintextMessage["@type"]} can be processed.`);
		this.eventEmitter.emit(this.agentContext, {
			type: DidCommEventTypes.DidCommMessageReceived,
			payload: {
				message: plaintextMessage,
				connection: connectionRecord,
				contextCorrelationId: this.agentContext.contextCorrelationId
			}
		});
	}
	async emitWithServices(outOfBandRecord, services, messages) {
		if (!services || services.length === 0) throw new CredoError("There are no services. We can not emit messages");
		const supportedMessageTypes = this.messageHandlerRegistry.supportedMessageTypes;
		const plaintextMessage = messages.find((message) => {
			const parsedMessageType = parseMessageType(message["@type"]);
			return supportedMessageTypes.find((type) => supportsIncomingMessageType(parsedMessageType, type));
		});
		if (!plaintextMessage) throw new CredoError("There is no message in requests~attach supported by agent.");
		this.ensureParentThreadId(outOfBandRecord, plaintextMessage);
		this.logger.debug(`Message with type ${plaintextMessage["@type"]} can be processed.`);
		this.eventEmitter.emit(this.agentContext, {
			type: DidCommEventTypes.DidCommMessageReceived,
			payload: {
				message: plaintextMessage,
				contextCorrelationId: this.agentContext.contextCorrelationId
			}
		});
	}
	ensureParentThreadId(outOfBandRecord, plaintextMessage) {
		const legacyInvitationMetadata = outOfBandRecord.metadata.get(DidCommOutOfBandRecordMetadataKeys.LegacyInvitation);
		if (plaintextMessage["~thread"]?.pthid && plaintextMessage["~thread"].pthid !== outOfBandRecord.outOfBandInvitation.id) throw new CredoError(`Out of band invitation requests~attach message contains parent thread id ${plaintextMessage["~thread"].pthid} that does not match the invitation id ${outOfBandRecord.outOfBandInvitation.id}`);
		if (legacyInvitationMetadata?.legacyInvitationType === DidCommInvitationType.Connectionless) return;
		if (!plaintextMessage["~thread"]) plaintextMessage["~thread"] = {};
		plaintextMessage["~thread"].pthid = outOfBandRecord.outOfBandInvitation.id;
	}
	async handleHandshakeReuse(outOfBandRecord, connectionRecord) {
		const reuseMessage = await this.outOfBandService.createHandShakeReuse(this.agentContext, outOfBandRecord, connectionRecord);
		const reuseAcceptedEventPromise = firstValueFrom(this.eventEmitter.observable(DidCommOutOfBandEventTypes.HandshakeReused).pipe(filterContextCorrelationId(this.agentContext.contextCorrelationId), first((event) => event.payload.reuseThreadId === reuseMessage.threadId && event.payload.outOfBandRecord.id === outOfBandRecord.id && event.payload.connectionRecord.id === connectionRecord.id), map(() => true), timeout({
			first: 15e3,
			meta: "OutOfBandApi.handleHandshakeReuse"
		}), catchError(() => of(false))));
		const outboundMessageContext = new DidCommOutboundMessageContext(reuseMessage, {
			agentContext: this.agentContext,
			connection: connectionRecord
		});
		await this.messageSender.sendMessage(outboundMessageContext);
		return reuseAcceptedEventPromise;
	}
	async resolveInvitationRecipientKeyFingerprints(outOfBandInvitation) {
		const recipientKeyFingerprints = [];
		for (const service of outOfBandInvitation.getServices()) if (typeof service === "string") {
			this.logger.debug(`Resolving services for did ${service}.`);
			const resolvedDidCommServices = await this.didCommDocumentService.resolveServicesFromDid(this.agentContext, service);
			recipientKeyFingerprints.push(...resolvedDidCommServices.reduce((aggr, { recipientKeys }) => [...aggr, ...recipientKeys], []).map((key) => key.fingerprint));
		} else recipientKeyFingerprints.push(...service.recipientKeys.map((didKey) => DidKey.fromDid(didKey).publicJwk.fingerprint));
		return recipientKeyFingerprints;
	}
};
DidCommOutOfBandApi = __decorate([
	injectable(),
	__decorateParam(7, inject(InjectionSymbols.Logger)),
	__decorateMetadata("design:paramtypes", [
		typeof (_ref = typeof DidCommMessageHandlerRegistry !== "undefined" && DidCommMessageHandlerRegistry) === "function" ? _ref : Object,
		typeof (_ref2 = typeof DidCommDocumentService !== "undefined" && DidCommDocumentService) === "function" ? _ref2 : Object,
		typeof (_ref3 = typeof DidCommOutOfBandService !== "undefined" && DidCommOutOfBandService) === "function" ? _ref3 : Object,
		typeof (_ref4 = typeof DidCommRoutingService !== "undefined" && DidCommRoutingService) === "function" ? _ref4 : Object,
		typeof (_ref5 = typeof DidCommConnectionsApi !== "undefined" && DidCommConnectionsApi) === "function" ? _ref5 : Object,
		typeof (_ref6 = typeof DidCommMessageSender !== "undefined" && DidCommMessageSender) === "function" ? _ref6 : Object,
		typeof (_ref7 = typeof EventEmitter !== "undefined" && EventEmitter) === "function" ? _ref7 : Object,
		Object,
		typeof (_ref8 = typeof AgentContext !== "undefined" && AgentContext) === "function" ? _ref8 : Object
	])
], DidCommOutOfBandApi);

//#endregion
export { DidCommOutOfBandApi };
//# sourceMappingURL=DidCommOutOfBandApi.mjs.map